re PR c++/67411 (internal compiler error: in tsubst_copy, at cp/pt.c:13473)
authorJason Merrill <jason@redhat.com>
Sun, 20 Dec 2015 18:38:37 +0000 (13:38 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Sun, 20 Dec 2015 18:38:37 +0000 (13:38 -0500)
PR c++/67411

* lambda.c (generic_lambda_fn_p): Split out from...
(maybe_add_lambda_conv_op): ...here.
* semantics.c (process_outer_var_ref): Don't defer maybe-constant
variables in a generic lambda.
* pt.c (instantiate_non_dependent_or_null): New.
* init.c (constant_value_1): Use it.
* cp-tree.h: Declare it and generic_lambda_fn_p.

From-SVN: r231863

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/init.c
gcc/cp/lambda.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C [new file with mode: 0644]

index 40ae390507d9adb788bf88de16c953cba5749a94..e7adad382282b12d6752bef32d03e677b1371529 100644 (file)
@@ -1,5 +1,14 @@
 2015-12-20  Jason Merrill  <jason@redhat.com>
 
+       PR c++/67411
+       * lambda.c (generic_lambda_fn_p): Split out from...
+       (maybe_add_lambda_conv_op): ...here.
+       * semantics.c (process_outer_var_ref): Don't defer maybe-constant
+       variables in a generic lambda.
+       * pt.c (instantiate_non_dependent_or_null): New.
+       * init.c (constant_value_1): Use it.
+       * cp-tree.h: Declare it and generic_lambda_fn_p.
+
        PR c++/67411
        * decl2.c (decl_maybe_constant_var_p): A proxy isn't constant.
 
index 058324f32a585b3acfbc6e27256be3a1ec243732..f0f7e36cbb4d905a15e6f0a7c35ad24c3df32967 100644 (file)
@@ -6158,6 +6158,7 @@ extern bool reregister_specialization             (tree, tree, tree);
 extern tree instantiate_non_dependent_expr     (tree);
 extern tree instantiate_non_dependent_expr_sfinae (tree, tsubst_flags_t);
 extern tree instantiate_non_dependent_expr_internal (tree, tsubst_flags_t);
+extern tree instantiate_non_dependent_or_null   (tree);
 extern bool variable_template_specialization_p  (tree);
 extern bool alias_type_or_template_p            (tree);
 extern bool alias_template_specialization_p     (const_tree);
@@ -6473,6 +6474,7 @@ extern tree maybe_resolve_dummy                   (tree, bool);
 extern tree current_nonlambda_function         (void);
 extern tree nonlambda_method_basetype          (void);
 extern tree current_nonlambda_scope            (void);
+extern bool generic_lambda_fn_p                        (tree);
 extern void maybe_add_lambda_conv_op            (tree);
 extern bool is_lambda_ignored_entity            (tree);
 
index 1d5cc65ef2d195d525bb575c43193927f325a6c6..09c1183157dd77fbe515357ce47e45543c20d80d 100644 (file)
@@ -2080,6 +2080,8 @@ constant_value_1 (tree decl, bool strict_p, bool return_aggregate_cst_ok_p)
          && TREE_CODE (init) == TREE_LIST
          && TREE_CHAIN (init) == NULL_TREE)
        init = TREE_VALUE (init);
+      /* Instantiate a non-dependent initializer.  */
+      init = instantiate_non_dependent_or_null (init);
       if (!init
          || !TREE_TYPE (init)
          || !TREE_CONSTANT (init)
index 8d1ee14ca39cca73b2f559b38a7a1efa637d6184..d50e48d71c378849a888548601dddd23e231047a 100644 (file)
@@ -851,6 +851,16 @@ prepare_op_call (tree fn, int nargs)
   return t;
 }
 
+/* Return true iff CALLOP is the op() for a generic lambda.  */
+
+bool
+generic_lambda_fn_p (tree callop)
+{
+  return (LAMBDA_FUNCTION_P (callop)
+         && DECL_TEMPLATE_INFO (callop)
+         && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (callop)));
+}
+
 /* If the closure TYPE has a static op(), also add a conversion to function
    pointer.  */
 
@@ -867,9 +877,7 @@ maybe_add_lambda_conv_op (tree type)
   if (processing_template_decl)
     return;
 
-  bool const generic_lambda_p
-    = (DECL_TEMPLATE_INFO (callop)
-    && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (callop)) == callop);
+  bool const generic_lambda_p = generic_lambda_fn_p (callop);
 
   if (!generic_lambda_p && DECL_INITIAL (callop) == NULL_TREE)
     {
index da57fb2a45c6e4e2a5ac91ebb77d1389434dbc52..209e65f23b6ccd99fbd0a50014910898bd295f9a 100644 (file)
@@ -5661,6 +5661,28 @@ instantiate_non_dependent_expr (tree expr)
   return instantiate_non_dependent_expr_sfinae (expr, tf_error);
 }
 
+/* Like instantiate_non_dependent_expr, but return NULL_TREE rather than
+   an uninstantiated expression.  */
+
+tree
+instantiate_non_dependent_or_null (tree expr)
+{
+  if (expr == NULL_TREE)
+    return NULL_TREE;
+  if (processing_template_decl)
+    {
+      if (instantiation_dependent_expression_p (expr)
+         || !potential_constant_expression (expr))
+       expr = NULL_TREE;
+      else
+       {
+         processing_template_decl_sentinel s;
+         expr = instantiate_non_dependent_expr_internal (expr, tf_error);
+       }
+    }
+  return expr;
+}
+
 /* True iff T is a specialization of a variable template.  */
 
 bool
index b8f4e8f7ce73081c52bbf6ca6236cb974bddbc74..ab9989a5a122b907a3fda7218c6dc8dd7d06adeb 100644 (file)
@@ -3231,27 +3231,7 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
   if (!mark_used (decl, complain) && !(complain & tf_error))
     return error_mark_node;
 
-  /* 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))
-       {
-         tree t = maybe_constant_value (convert_from_reference (decl));
-         if (TREE_CONSTANT (t))
-           return t;
-       }
-    }
-
+  bool saw_generic_lambda = false;
   if (parsing_nsdmi ())
     containing_function = NULL_TREE;
   else
@@ -3265,6 +3245,9 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
        tree closure = DECL_CONTEXT (containing_function);
        lambda_expr = CLASSTYPE_LAMBDA_EXPR (closure);
 
+       if (generic_lambda_fn_p (containing_function))
+         saw_generic_lambda = true;
+
        if (TYPE_CLASS_SCOPE_P (closure))
          /* A lambda in an NSDMI (c++/64496).  */
          break;
@@ -3281,6 +3264,35 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain)
          = decl_function_context (containing_function);
       }
 
+  /* 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 && !saw_generic_lambda)
+       /* In a non-generic lambda within a template, wait until instantiation
+          time to decide whether to capture.  For a generic lambda, we can't
+          wait until we instantiate the op() because the closure class is
+          already defined at that point.  FIXME to get the semantics exactly
+          right we need to partially-instantiate the lambda body so the only
+          dependencies left are on the generic parameters themselves.  This
+          probably means moving away from our current model of lambdas in
+          templates (instantiating the closure type) to one based on creating
+          the closure type when instantiating the lambda context.  That is
+          probably also the way to handle lambdas within pack expansions.  */
+       return decl;
+      else if (decl_constant_var_p (decl))
+       {
+         tree t = maybe_constant_value (convert_from_reference (decl));
+         if (TREE_CONSTANT (t))
+           return t;
+       }
+    }
+
   if (lambda_expr && VAR_P (decl)
       && DECL_ANON_UNION_VAR_P (decl))
     {
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const2.C
new file mode 100644 (file)
index 0000000..9a00e22
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/67411
+// { dg-do compile { target c++14 } }
+
+template <class T>
+void f()
+{
+  int i = 42;
+  [=] {
+    const int x = i;
+    [&](auto) {
+      [=] { return x; }();
+    }(1);
+  }();
+}
+
+int main()
+{
+  f<int>();
+}