diff mbox

C++ PATCH for c++/63207 (ICE with lambda and const var)

Message ID 543752BC.70305@redhat.com
State New
Headers show

Commit Message

Jason Merrill Oct. 10, 2014, 3:30 a.m. UTC
When we see a reference to an outer const variable that might be usable 
in a constant-expression, we wait until instantiation time to see.  If 
it turns out not to be usable, we need to do the same lambda magic that 
we would have done in finish_id_expression.

Tested x86_64-pc-linux-gnu, applying to trunk.
diff mbox

Patch

commit 4bfc903761da7471f584443538718bbe6d529d73
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Fri Oct 10 03:28:18 2014 +0000

    	PR c++/63207
    	* semantics.c (outer_var_p): Non-static.
    	(process_outer_var_ref): Split out from finish_id_expression.
    	* pt.c (tsubst_copy_and_build): Call them.
    	* cp-tree.h: Declare them.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@216056 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 3787c4a..6d720c1 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5879,6 +5879,8 @@  extern void finish_template_decl		(tree);
 extern tree finish_template_type		(tree, tree, int);
 extern tree finish_base_specifier		(tree, tree, bool);
 extern void finish_member_declaration		(tree);
+extern bool outer_automatic_var_p		(tree);
+extern tree process_outer_var_ref		(tree, tsubst_flags_t);
 extern tree finish_id_expression		(tree, tree, tree,
 						 cp_id_kind *,
 						 bool, bool, bool *,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 85af59d..f2c21ee 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -15460,6 +15460,7 @@  tsubst_copy_and_build (tree t,
     case PARM_DECL:
       {
 	tree r = tsubst_copy (t, args, complain, in_decl);
+	/* ??? We're doing a subset of finish_id_expression here.  */
 	if (VAR_P (r)
 	    && !processing_template_decl
 	    && !cp_unevaluated_operand
@@ -15471,6 +15472,8 @@  tsubst_copy_and_build (tree t,
 		 a call to its wrapper.  */
 	      r = build_cxx_call (wrap, 0, NULL, tf_warning_or_error);
 	  }
+	else if (outer_automatic_var_p (r))
+	  r = process_outer_var_ref (r, complain);
 
 	if (TREE_CODE (TREE_TYPE (t)) != REFERENCE_TYPE)
 	  /* If the original type was a reference, we'll be wrapped in
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 3ca91d8..ab8c82a 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -3067,13 +3067,105 @@  outer_var_p (tree decl)
 
 /* As above, but also checks that DECL is automatic.  */
 
-static bool
+bool
 outer_automatic_var_p (tree decl)
 {
   return (outer_var_p (decl)
 	  && !TREE_STATIC (decl));
 }
 
+/* DECL satisfies outer_automatic_var_p.  Possibly complain about it or
+   rewrite it for lambda capture.  */
+
+tree
+process_outer_var_ref (tree decl, tsubst_flags_t complain)
+{
+  if (cp_unevaluated_operand)
+    /* It's not a use (3.2) if we're in an unevaluated context.  */
+    return decl;
+
+  tree context = DECL_CONTEXT (decl);
+  tree containing_function = current_function_decl;
+  tree lambda_stack = NULL_TREE;
+  tree lambda_expr = NULL_TREE;
+  tree initializer = convert_from_reference (decl);
+
+  /* Mark it as used now even if the use is ill-formed.  */
+  mark_used (decl);
+
+  /* Core issue 696: "[At the July 2009 meeting] the CWG expressed
+     support for an approach in which a reference to a local
+     [constant] automatic variable in a nested class or lambda body
+     would enter the expression as an rvalue, which would reduce
+     the complexity of the problem"
+
+     FIXME update for final resolution of core issue 696.  */
+  if (decl_maybe_constant_var_p (decl))
+    {
+      if (processing_template_decl)
+	/* In a template, the constant value may not be in a usable
+	   form, so wait until instantiation time.  */
+	return decl;
+      else if (decl_constant_var_p (decl))
+	return integral_constant_value (decl);
+    }
+
+  if (parsing_nsdmi ())
+    containing_function = NULL_TREE;
+  else
+    /* If we are in a lambda function, we can move out until we hit
+       1. the context,
+       2. a non-lambda function, or
+       3. a non-default capturing lambda function.  */
+    while (context != containing_function
+	   && LAMBDA_FUNCTION_P (containing_function))
+      {
+	lambda_expr = CLASSTYPE_LAMBDA_EXPR
+	  (DECL_CONTEXT (containing_function));
+
+	if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr)
+	    == CPLD_NONE)
+	  break;
+
+	lambda_stack = tree_cons (NULL_TREE,
+				  lambda_expr,
+				  lambda_stack);
+
+	containing_function
+	  = decl_function_context (containing_function);
+      }
+
+  if (lambda_expr && TREE_CODE (decl) == VAR_DECL
+      && DECL_ANON_UNION_VAR_P (decl))
+    {
+      if (complain & tf_error)
+	error ("cannot capture member %qD of anonymous union", decl);
+      return error_mark_node;
+    }
+  if (context == containing_function)
+    {
+      decl = add_default_capture (lambda_stack,
+				  /*id=*/DECL_NAME (decl),
+				  initializer);
+    }
+  else if (lambda_expr)
+    {
+      if (complain & tf_error)
+	error ("%qD is not captured", decl);
+      return error_mark_node;
+    }
+  else
+    {
+      if (complain & tf_error)
+	error (VAR_P (decl)
+	       ? G_("use of local variable with automatic storage from containing function")
+	       : G_("use of parameter from containing function"));
+      inform (input_location, "%q+#D declared here", decl);
+      return error_mark_node;
+    }
+  return decl;
+}
+
 /* ID_EXPRESSION is a representation of parsed, but unprocessed,
    id-expression.  (See cp_parser_id_expression for details.)  SCOPE,
    if non-NULL, is the type or namespace used to explicitly qualify
@@ -3179,90 +3271,8 @@  finish_id_expression (tree id_expression,
 
       /* Disallow uses of local variables from containing functions, except
 	 within lambda-expressions.  */
-      if (!outer_var_p (decl))
-	/* OK */;
-      else if (TREE_STATIC (decl)
-	       /* It's not a use (3.2) if we're in an unevaluated context.  */
-	       || cp_unevaluated_operand)
-	/* OK */;
-      else
-	{
-	  tree context = DECL_CONTEXT (decl);
-	  tree containing_function = current_function_decl;
-	  tree lambda_stack = NULL_TREE;
-	  tree lambda_expr = NULL_TREE;
-	  tree initializer = convert_from_reference (decl);
-
-	  /* Mark it as used now even if the use is ill-formed.  */
-	  mark_used (decl);
-
-	  /* Core issue 696: "[At the July 2009 meeting] the CWG expressed
-	     support for an approach in which a reference to a local
-	     [constant] automatic variable in a nested class or lambda body
-	     would enter the expression as an rvalue, which would reduce
-	     the complexity of the problem"
-
-	     FIXME update for final resolution of core issue 696.  */
-	  if (decl_maybe_constant_var_p (decl))
-	    {
-	      if (processing_template_decl)
-		/* In a template, the constant value may not be in a usable
-		   form, so wait until instantiation time.  */
-		return decl;
-	      else if (decl_constant_var_p (decl))
-		return integral_constant_value (decl);
-	    }
-
-	  if (parsing_nsdmi ())
-	    containing_function = NULL_TREE;
-	  /* If we are in a lambda function, we can move out until we hit
-	     1. the context,
-	     2. a non-lambda function, or
-	     3. a non-default capturing lambda function.  */
-	  else while (context != containing_function
-		      && LAMBDA_FUNCTION_P (containing_function))
-	    {
-	      lambda_expr = CLASSTYPE_LAMBDA_EXPR
-		(DECL_CONTEXT (containing_function));
-
-	      if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr)
-		  == CPLD_NONE)
-		break;
-
-	      lambda_stack = tree_cons (NULL_TREE,
-					lambda_expr,
-					lambda_stack);
-
-	      containing_function
-		= decl_function_context (containing_function);
-	    }
-
-	  if (lambda_expr && TREE_CODE (decl) == VAR_DECL
-	      && DECL_ANON_UNION_VAR_P (decl))
-	    {
-	      error ("cannot capture member %qD of anonymous union", decl);
-	      return error_mark_node;
-	    }
-	  if (context == containing_function)
-	    {
-	      decl = add_default_capture (lambda_stack,
-					  /*id=*/DECL_NAME (decl),
-					  initializer);
-	    }
-	  else if (lambda_expr)
-	    {
-	      error ("%qD is not captured", decl);
-	      return error_mark_node;
-	    }
-	  else
-	    {
-	      error (VAR_P (decl)
-		     ? G_("use of local variable with automatic storage from containing function")
-		     : G_("use of parameter from containing function"));
-	      inform (input_location, "%q+#D declared here", decl);
-	      return error_mark_node;
-	    }
-	}
+      if (outer_automatic_var_p (decl))
+	decl = process_outer_var_ref (decl, tf_warning_or_error);
 
       /* Also disallow uses of function parameters outside the function
 	 body, except inside an unevaluated context (i.e. decltype).  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C
new file mode 100644
index 0000000..02ad602
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const4.C
@@ -0,0 +1,21 @@ 
+// PR c++/63207
+// { dg-do run { target c++11 } }
+
+template <typename T>
+struct Base {
+  T value;
+};
+
+template <typename T>
+struct Test : Base<T> {
+  T test() {
+    const int x = this->value;
+    return ([&]{ return x; })();
+  }
+};
+
+int main()  {
+  Test<int> t;
+  t.value = 0;
+  return t.test();
+}