PR c++/86969 - ICE with constexpr if and recursive generic lambdas.
authorJason Merrill <jason@redhat.com>
Wed, 27 Feb 2019 21:54:25 +0000 (16:54 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Wed, 27 Feb 2019 21:54:25 +0000 (16:54 -0500)
Here, the problem was that extract_local_specs wasn't seeing that we use
'self' inside the lambda in the else of the inner constexpr if, because we
don't walk into lambda bodies and we didn't capture it in the lambda because
'self' is still dependent.  Marek recently changed process_outer_var_ref to
do more implicit capture in templates; this example shows that we should
always capture non-packs, so that we can continue to not walk into lambda
bodies.  We do walk into lambda bodies for pack expansions, so we can delay
deciding whether we're capturing a single element or the entire pack.

Immediately capturing a VLA means we need to create a dependent VLA capture
type, and not in the context of the lambda op(), since trying to look up the
instantiation of the op() while we're substituting into the capture list
would crash.  So I force TYPE_CONTEXT and the binding level out to the
enclosing function before pushtag, avoid adding a TAG_DEFN, and instead
force the type to be complete in tsubst_lambda_expr.

* semantics.c (process_outer_var_ref): Do capture dependent vars.
* class.c (finish_struct): Only add TAG_DEFN if T is in
current_function_decl.
* lambda.c (vla_capture_type): Force the capture type out into the
lambda's enclosing function.
(add_capture): Pass in the lambda.
* pt.c (tsubst_lambda_expr): complete_type a VLA capture type.

From-SVN: r269265

gcc/cp/ChangeLog
gcc/cp/class.c
gcc/cp/lambda.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/testsuite/g++.dg/cpp1z/constexpr-if27.C [new file with mode: 0644]

index a2d16e15b4a77fa3c79ed708888d34ff2538909b..e4d876b4c0bad9911a3e27283cb292ee529ff100 100644 (file)
@@ -1,3 +1,14 @@
+2019-02-27  Jason Merrill  <jason@redhat.com>
+
+       PR c++/86969 - ICE with constexpr if and recursive generic lambdas.
+       * semantics.c (process_outer_var_ref): Do capture dependent vars.
+       * class.c (finish_struct): Only add TAG_DEFN if T is in
+       current_function_decl.
+       * lambda.c (vla_capture_type): Force the capture type out into the
+       lambda's enclosing function.
+       (add_capture): Pass in the lambda.
+       * pt.c (tsubst_lambda_expr): complete_type a VLA capture type.
+
 2019-02-27  Marek Polacek  <polacek@redhat.com>
 
        PR c++/89511 - ICE with using-declaration and unscoped enumerator.
index f44acfd62b5eaaaf230173d1fc020360a78bb748..830ede56af8b5e5e8d65af4cd4f3bd4151e3d680 100644 (file)
@@ -7246,6 +7246,7 @@ finish_struct (tree t, tree attributes)
     error ("trying to finish struct, but kicked out due to previous parse errors");
 
   if (processing_template_decl && at_function_scope_p ()
+      && TYPE_CONTEXT (t) == current_function_decl
       /* Lambdas are defined by the LAMBDA_EXPR.  */
       && !LAMBDA_TYPE_P (t))
     add_stmt (build_min (TAG_DEFN, t));
index d178f15a4da4acc288da73444338e46d2add3505..c25df2fbc0ee63ccfddffa6f68c20f3ddaccede7 100644 (file)
@@ -479,9 +479,31 @@ static GTY(()) tree max_id;
    an array of runtime length.  */
 
 static tree
-vla_capture_type (tree array_type)
+vla_capture_type (tree array_type, tree lambda)
 {
-  tree type = xref_tag (record_type, make_anon_name (), ts_current, false);
+  tree closure = LAMBDA_EXPR_CLOSURE (lambda);
+  tree type = make_class_type (RECORD_TYPE);
+  cp_binding_level *slev = current_binding_level;
+  if (closure)
+    {
+      /* If we're already inside the lambda body, force the capture type out
+        into the enclosing context, so we don't crash trying to instantiate
+        the capture field in tsubst_lambda_expr.  We won't have a TAG_DEFN
+        from finish_struct in the enclosing context, which we work around in
+        tsubst_lambda_expr.  */
+      TYPE_CONTEXT (type) = TYPE_CONTEXT (closure);
+      cp_binding_level *b = current_binding_level;
+      for (;; b = b->level_chain)
+       if (b->this_entity == closure)
+         {
+           while (b->this_entity == closure)
+             b = b->level_chain;
+           break;
+         }
+      current_binding_level = b;
+    }
+  type = pushtag (make_anon_name (), type, ts_current);
+  current_binding_level = slev;
   xref_basetypes (type, NULL_TREE);
   type = begin_class_definition (type);
   if (!ptr_id)
@@ -541,7 +563,7 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
       initializer = build_constructor_va (init_list_type_node, 2,
                                          NULL_TREE, build_address (elt),
                                          NULL_TREE, array_type_nelts (type));
-      type = vla_capture_type (type);
+      type = vla_capture_type (type, lambda);
     }
   else if (!dependent_type_p (type)
           && variably_modified_type_p (type, NULL_TREE))
index d678e27807810a2353a6f3ebdbc60ff79a953025..673ea8e2258136722b09651d9b65879fde0f081e 100644 (file)
@@ -17989,6 +17989,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
       if (PACK_EXPANSION_P (ofield))
        ofield = PACK_EXPANSION_PATTERN (ofield);
       tree field = tsubst_decl (ofield, args, complain);
+      if (DECL_VLA_CAPTURE_P (ofield))
+       /* The type of a VLA capture might not have a TAG_DEFN in the enclosing
+          context, so complete it here.  */
+       complete_type (TREE_TYPE (field));
 
       if (DECL_PACK_P (ofield) && !DECL_NORMAL_CAPTURE_P (ofield))
        {
index da814bdd655769b10755471929329d7a64e72aa0..d1a378acd98965fe3612540f1757af0ae4d79c23 100644 (file)
@@ -3469,10 +3469,12 @@ process_outer_var_ref (tree decl, tsubst_flags_t complain, bool odr_use)
        = decl_function_context (containing_function);
     }
 
-  /* In a lambda within a template, wait until instantiation
-     time to implicitly capture a dependent type.  */
+  /* In a lambda within a template, wait until instantiation time to implicitly
+     capture a parameter pack.  We want to wait because we don't know if we're
+     capturing the whole pack or a single element, and it's OK to wait because
+     find_parameter_packs_r walks into the lambda body.  */
   if (context == containing_function
-      && dependent_type_p (TREE_TYPE (decl)))
+      && DECL_PACK_P (decl))
     return decl;
 
   if (lambda_expr && VAR_P (decl)
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-if27.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-if27.C
new file mode 100644 (file)
index 0000000..a1a6e0b
--- /dev/null
@@ -0,0 +1,22 @@
+// PR c++/86969
+// { dg-do compile { target c++17 } }
+
+auto compose = [](auto... fs) {
+    if constexpr (sizeof...(fs) == 0) {
+        return [](auto x) { return x; };
+    } else {
+        auto fn = [](auto self, auto f, auto... fs) {
+            if constexpr (sizeof...(fs) == 0) return f;
+            else return [=](auto x) { 
+                return f(self(self, fs...)(x));
+            };
+        };
+        return fn(fn, fs...);
+    }
+};
+
+static_assert(compose(
+        [](auto x) { return x * 3; },
+        [](auto x) { return x + 1; },
+        [](auto x) { return x / 2; }
+    )(6) == 12);