My change to avoid building a dummy variable in a nested function turns out to have been too aggressive; in this case, we have a reference to a local const variable in the type of an array that we're capturing. It seems safe enough to make dummy copies of const and static variables when instantiating a lambda operator, so let's do that.

Tested x86_64-pc-linux-gnu, applying to trunk and 4.9.
commit 7638a1fc9626c71c8665263dba6703b007293f92
Author: Jason Merrill <ja...@redhat.com>
Date:   Wed Apr 30 13:58:09 2014 -0400

    	PR c++/60992
    	* lambda.c (lambda_capture_field_type): Wrap anything dependent
    	other than 'this'.
    	(add_capture): Check for VLA before calling it.
    	* semantics.c (is_this_parameter): Accept any 'this' parameter, not
    	just the current one.  Make non-static.
    	* cp-tree.h: Declare it.
    	* pt.c (tsubst_copy) [VAR_DECL]: Also build a new VAR_DECL if
    	the operand was static or constant.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 55ecc4e..34d3d20 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5773,6 +5773,7 @@ extern bool is_sub_constant_expr (tree);
 extern bool reduced_constant_expression_p (tree);
 extern void explain_invalid_constexpr_fn (tree);
 extern vec<tree> cx_error_context (void);
+extern bool is_this_parameter (tree);
 
 enum {
   BCS_NO_SCOPE = 1,
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 0b8b46a..5ba6f14 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -216,8 +216,8 @@ lambda_capture_field_type (tree expr, bool explicit_init_p)
     }
   else
     type = non_reference (unlowered_expr_type (expr));
-  if (!type || WILDCARD_TYPE_P (type) || type_uses_auto (type)
-      || DECL_PACK_P (expr))
+  if (type_dependent_expression_p (expr)
+      && !is_this_parameter (tree_strip_nop_conversions (expr)))
     {
       type = cxx_make_type (DECLTYPE_TYPE);
       DECLTYPE_TYPE_EXPR (type) = expr;
@@ -455,7 +455,7 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
   if (TREE_CODE (initializer) == TREE_LIST)
     initializer = build_x_compound_expr_from_list (initializer, ELK_INIT,
 						   tf_warning_or_error);
-  type = lambda_capture_field_type (initializer, explicit_init_p);
+  type = TREE_TYPE (initializer);
   if (array_of_runtime_bound_p (type))
     {
       vla = true;
@@ -482,15 +482,19 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
 		"variable size", TREE_TYPE (type));
       type = error_mark_node;
     }
-  else if (by_reference_p)
+  else
     {
-      type = build_reference_type (type);
-      if (!real_lvalue_p (initializer))
-	error ("cannot capture %qE by reference", initializer);
+      type = lambda_capture_field_type (initializer, explicit_init_p);
+      if (by_reference_p)
+	{
+	  type = build_reference_type (type);
+	  if (!real_lvalue_p (initializer))
+	    error ("cannot capture %qE by reference", initializer);
+	}
+      else
+	/* Capture by copy requires a complete type.  */
+	type = complete_type (type);
     }
-  else
-    /* Capture by copy requires a complete type.  */
-    type = complete_type (type);
 
   /* Add __ to the beginning of the field name so that user code
      won't find the field with name lookup.  We can't just leave the name
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 48cc2a9..1584eb9 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12629,13 +12629,17 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		}
 	      else
 		{
-		  /* This can happen for a variable used in a late-specified
-		     return type of a local lambda.  Just make a dummy decl
-		     since it's only used for its type.  */
-		  if (cp_unevaluated_operand)
-		    return tsubst_decl (t, args, complain);
-		  gcc_assert (errorcount || sorrycount);
-		  return error_mark_node;
+		  /* This can happen for a variable used in a
+		     late-specified return type of a local lambda, or for a
+		     local static or constant.  Building a new VAR_DECL
+		     should be OK in all those cases.  */
+		  r = tsubst_decl (t, args, complain);
+		  if (decl_constant_var_p (r))
+		    /* A use of a local constant must decay to its value.  */
+		    return integral_constant_value (r);
+		  gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
+			      || errorcount || sorrycount);
+		  return r;
 		}
 	    }
 	}
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 3f8ca44..4afb821 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8155,10 +8155,11 @@ maybe_initialize_constexpr_call_table (void)
 
 /* Return true if T designates the implied `this' parameter.  */
 
-static inline bool
+bool
 is_this_parameter (tree t)
 {
-  return t == current_class_ptr;
+  return (TREE_CODE (t) == PARM_DECL
+	  && DECL_NAME (t) == this_identifier);
 }
 
 /* We have an expression tree T that represents a call, either CALL_EXPR
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C
new file mode 100644
index 0000000..a1ffadd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C
@@ -0,0 +1,38 @@
+// PR c++/60992
+// { dg-do compile { target c++11 } }
+
+struct ScopeGuardGenerator { };
+
+struct FF
+{
+  template < class F, class ... Ts >
+  void
+  operator () (F & ...)
+  {
+    const int n = sizeof ... (Ts) + 1;
+    void *mutexes[n];
+    auto _on_scope_exit_var_0 =
+      ScopeGuardGenerator () + [&mutexes] { };
+  }
+};
+
+template < class F >
+int operator+ (ScopeGuardGenerator, F) { return 1; }
+
+struct D
+{
+  template < class T0, class T1, class T2, class ... T >
+  void
+  operator () (T0, T1, const T2 & t2, T & ... t)
+  {
+    base (t2, t ...);
+  }
+  FF base;
+};
+
+D run_with_locks;
+
+void Fn ()
+{
+  run_with_locks ([] { }, 0, 0);
+}
commit 5d59882ea8c256f956a2db6fbeaf256ff8a96585
Author: Jason Merrill <ja...@redhat.com>
Date:   Wed Apr 30 13:58:09 2014 -0400

    	PR c++/60992
    	* lambda.c (lambda_capture_field_type): Wrap anything dependent
    	other than 'this' or a VLA.
    	(is_this): New.
    	* pt.c (tsubst_copy) [VAR_DECL]: Also build a new VAR_DECL if
    	the operand was static or constant.

diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 0b8b46a..3280644 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -201,6 +201,13 @@ lambda_function (tree lambda)
   return lambda;
 }
 
+static inline bool
+is_this (tree t)
+{
+  return (TREE_CODE (t) == PARM_DECL
+	  && DECL_NAME (t) == this_identifier);
+}
+
 /* Returns the type to use for the FIELD_DECL corresponding to the
    capture of EXPR.
    The caller should add REFERENCE_TYPE for capture by reference.  */
@@ -216,8 +223,9 @@ lambda_capture_field_type (tree expr, bool explicit_init_p)
     }
   else
     type = non_reference (unlowered_expr_type (expr));
-  if (!type || WILDCARD_TYPE_P (type) || type_uses_auto (type)
-      || DECL_PACK_P (expr))
+  if (type_dependent_expression_p (expr)
+      && !is_this (tree_strip_nop_conversions (expr))
+      && !array_of_runtime_bound_p (type))
     {
       type = cxx_make_type (DECLTYPE_TYPE);
       DECLTYPE_TYPE_EXPR (type) = expr;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 318c325..18389e0 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12638,13 +12638,17 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		}
 	      else
 		{
-		  /* This can happen for a variable used in a late-specified
-		     return type of a local lambda.  Just make a dummy decl
-		     since it's only used for its type.  */
-		  if (cp_unevaluated_operand)
-		    return tsubst_decl (t, args, complain);
-		  gcc_assert (errorcount || sorrycount);
-		  return error_mark_node;
+		  /* This can happen for a variable used in a
+		     late-specified return type of a local lambda, or for a
+		     local static or constant.  Building a new VAR_DECL
+		     should be OK in all those cases.  */
+		  r = tsubst_decl (t, args, complain);
+		  if (decl_constant_var_p (r))
+		    /* A use of a local constant must decay to its value.  */
+		    return integral_constant_value (r);
+		  gcc_assert (cp_unevaluated_operand || TREE_STATIC (r)
+			      || errorcount || sorrycount);
+		  return r;
 		}
 	    }
 	}
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C
new file mode 100644
index 0000000..a1ffadd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-const3.C
@@ -0,0 +1,38 @@
+// PR c++/60992
+// { dg-do compile { target c++11 } }
+
+struct ScopeGuardGenerator { };
+
+struct FF
+{
+  template < class F, class ... Ts >
+  void
+  operator () (F & ...)
+  {
+    const int n = sizeof ... (Ts) + 1;
+    void *mutexes[n];
+    auto _on_scope_exit_var_0 =
+      ScopeGuardGenerator () + [&mutexes] { };
+  }
+};
+
+template < class F >
+int operator+ (ScopeGuardGenerator, F) { return 1; }
+
+struct D
+{
+  template < class T0, class T1, class T2, class ... T >
+  void
+  operator () (T0, T1, const T2 & t2, T & ... t)
+  {
+    base (t2, t ...);
+  }
+  FF base;
+};
+
+D run_with_locks;
+
+void Fn ()
+{
+  run_with_locks ([] { }, 0, 0);
+}

Reply via email to