]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR c++/90538 - multiple expansions of capture packs
authorJason Merrill <jason@redhat.com>
Mon, 12 Aug 2019 17:46:25 +0000 (13:46 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Mon, 12 Aug 2019 17:46:25 +0000 (13:46 -0400)
Previously, with init-capture the type of the closure field was a
DECLTYPE_TYPE of the initializer.  But since each time we tsubst a lambda we
get a different lambda, that meant that if the initializer is a lambda, we'd
end up with different closure types in the field and initializer after
substitution (PR 87322).  We dealt with this by remembering the lambda
instantiation within each pack expansion element, using
local_specialization_stack to separate the elements.  But that broke this
testcase, because it lost lambda capture proxies that also use
local_specializations.

So, this patch removes the local_specializations changes from that patch and
fixes 87322 differently, by giving init-capture fields 'auto' type and doing
deduction later.  There's a bit of a kludge to get the right number of
fields by pretending that 'auto...' uses the parameter packs from the
initializer, but it does the trick.

* cp-tree.h (DECLTYPE_FOR_INIT_CAPTURE): Remove.
* lambda.c (add_capture): Copy parameter packs from init.
(lambda_capture_field_type): Always use auto for init-capture.
* pt.c (uses_parameter_packs): Return tree.
(tsubst) [DECLTYPE_TYPE]: Remove init-capture handling.
(gen_elem_of_pack_expansion_instantiation): Don't push
local_specialization_stack.
(prepend_one_capture): New.
(tsubst_lambda_expr): Use it.  Don't touch local_specializations.
(do_auto_deduction): Avoid redundant error.

From-SVN: r274315

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/lambda.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/range-for19.C
gcc/testsuite/g++.dg/cpp1y/lambda-init16.C

index c977850d067c1becf222f37baada39f37f5ab20b..9a34a4f128e26166c37e4d0c5826b65f033fa232 100644 (file)
@@ -1,3 +1,17 @@
+2019-08-12  Jason Merrill  <jason@redhat.com>
+
+       PR c++/90538 - multiple expansions of capture packs
+       * cp-tree.h (DECLTYPE_FOR_INIT_CAPTURE): Remove.
+       * lambda.c (add_capture): Copy parameter packs from init.
+       (lambda_capture_field_type): Always use auto for init-capture.
+       * pt.c (uses_parameter_packs): Return tree.
+       (tsubst) [DECLTYPE_TYPE]: Remove init-capture handling.
+       (gen_elem_of_pack_expansion_instantiation): Don't push
+       local_specialization_stack.
+       (prepend_one_capture): New.
+       (tsubst_lambda_expr): Use it.  Don't touch local_specializations.
+       (do_auto_deduction): Avoid redundant error.
+
 2019-08-12  Release Manager
 
        * GCC 9.2.0 released.
index ff4ce068a83601bb083f4b3e848cea6569d48b0a..e15a07392ca0175606a6bc283755c21ee661e213 100644 (file)
@@ -423,7 +423,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       LAMBDA_EXPR_MUTABLE_P (in LAMBDA_EXPR)
       DECL_FINAL_P (in FUNCTION_DECL)
       QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF)
-      DECLTYPE_FOR_INIT_CAPTURE (in DECLTYPE_TYPE)
       CONSTRUCTOR_IS_DEPENDENT (in CONSTRUCTOR)
       TINFO_USED_TEMPLATE_ID (in TEMPLATE_INFO)
       PACK_EXPANSION_SIZEOF_P (in *_PACK_EXPANSION)
@@ -4471,12 +4470,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
   (DECLTYPE_TYPE_CHECK (NODE))->type_common.string_flag
 
 /* These flags indicate that we want different semantics from normal
-   decltype: lambda capture just drops references, init capture
-   uses auto semantics, lambda proxies look through implicit dereference.  */
+   decltype: lambda capture just drops references,
+   lambda proxies look through implicit dereference.  */
 #define DECLTYPE_FOR_LAMBDA_CAPTURE(NODE) \
   TREE_LANG_FLAG_0 (DECLTYPE_TYPE_CHECK (NODE))
-#define DECLTYPE_FOR_INIT_CAPTURE(NODE) \
-  TREE_LANG_FLAG_1 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_LAMBDA_PROXY(NODE) \
   TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_REF_CAPTURE(NODE) \
@@ -6779,7 +6776,7 @@ extern bool maybe_instantiate_noexcept            (tree, tsubst_flags_t = tf_warning_or_er
 extern tree instantiate_decl                   (tree, bool, bool);
 extern int comp_template_parms                 (const_tree, const_tree);
 extern bool builtin_pack_fn_p                  (tree);
-extern bool uses_parameter_packs                (tree);
+extern tree uses_parameter_packs                (tree);
 extern bool template_parameter_pack_p           (const_tree);
 extern bool function_parameter_pack_p          (const_tree);
 extern bool function_parameter_expanded_from_pack_p (tree, tree);
index 93664181b47b949d6cc5f66e198b091b5c3d499d..edfd2f75853c7b8d2b8c9abcda5305318ba1ca3c 100644 (file)
@@ -220,16 +220,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
   tree type;
   bool is_this = is_this_parameter (tree_strip_nop_conversions (expr));
 
-  if (!is_this && type_dependent_expression_p (expr))
-    {
-      type = cxx_make_type (DECLTYPE_TYPE);
-      DECLTYPE_TYPE_EXPR (type) = expr;
-      DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true;
-      DECLTYPE_FOR_INIT_CAPTURE (type) = explicit_init_p;
-      DECLTYPE_FOR_REF_CAPTURE (type) = by_reference_p;
-      SET_TYPE_STRUCTURAL_EQUALITY (type);
-    }
-  else if (!is_this && explicit_init_p)
+  if (!is_this && explicit_init_p)
     {
       tree auto_node = make_auto ();
       
@@ -240,6 +231,14 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
        type = build_reference_type (type);
       type = do_auto_deduction (type, expr, auto_node);
     }
+  else if (!is_this && type_dependent_expression_p (expr))
+    {
+      type = cxx_make_type (DECLTYPE_TYPE);
+      DECLTYPE_TYPE_EXPR (type) = expr;
+      DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true;
+      DECLTYPE_FOR_REF_CAPTURE (type) = by_reference_p;
+      SET_TYPE_STRUCTURAL_EQUALITY (type);
+    }
   else
     {
       type = non_reference (unlowered_expr_type (expr));
@@ -602,7 +601,16 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
   name = get_identifier (buf);
 
   if (variadic)
-    type = make_pack_expansion (type);
+    {
+      type = make_pack_expansion (type);
+      if (explicit_init_p)
+       /* With an explicit initializer 'type' is auto, which isn't really a
+          parameter pack in this context.  We will want as many fields as we
+          have elements in the expansion of the initializer, so use its packs
+          instead.  */
+       PACK_EXPANSION_PARAMETER_PACKS (type)
+         = uses_parameter_packs (initializer);
+    }
 
   /* Make member variable.  */
   member = build_decl (input_location, FIELD_DECL, name, type);
index a2a986b1db1beb1db9b8b01e4281fcaea1ea3df1..dd2dd2940bfe546bc55e83f9f98430c41c1bed31 100644 (file)
@@ -3875,7 +3875,7 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
 }
 
 /* Determines if the expression or type T uses any parameter packs.  */
-bool
+tree
 uses_parameter_packs (tree t)
 {
   tree parameter_packs = NULL_TREE;
@@ -3885,7 +3885,7 @@ uses_parameter_packs (tree t)
   ppd.type_pack_expansion_p = false;
   cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited);
   delete ppd.visited;
-  return parameter_packs != NULL_TREE;
+  return parameter_packs;
 }
 
 /* Turn ARG, which may be an expression, type, or a TREE_LIST
@@ -11757,10 +11757,6 @@ gen_elem_of_pack_expansion_instantiation (tree pattern,
       ARGUMENT_PACK_SELECT_INDEX (aps) = index;
     }
 
-  // Any local specialization bindings arising from this substitution
-  // cannot be reused for a different INDEX.
-  local_specialization_stack lss (lss_copy);
-
   /* Substitute into the PATTERN with the (possibly altered)
      arguments.  */
   if (pattern == in_decl)
@@ -15130,24 +15126,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
                                      /*function_p*/false,
                                      /*integral_constant_expression*/false);
 
-       if (DECLTYPE_FOR_INIT_CAPTURE (t))
-         {
-           if (type == NULL_TREE)
-             {
-               if (complain & tf_error)
-                 error ("empty initializer in lambda init-capture");
-               type = error_mark_node;
-             }
-           else if (TREE_CODE (type) == TREE_LIST)
-             type = build_x_compound_expr_from_list (type, ELK_INIT, complain);
-         }
-
        --cp_unevaluated_operand;
        --c_inhibit_evaluation_warnings;
 
        if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))
          type = lambda_capture_field_type (type,
-                                           DECLTYPE_FOR_INIT_CAPTURE (t),
+                                           false /*explicit_init*/,
                                            DECLTYPE_FOR_REF_CAPTURE (t));
        else if (DECLTYPE_FOR_LAMBDA_PROXY (t))
          type = lambda_proxy_type (type);
@@ -18014,6 +17998,33 @@ tsubst_non_call_postfix_expression (tree t, tree args,
   return t;
 }
 
+/* Subroutine of tsubst_lambda_expr: add the FIELD/INIT capture pair to the
+   LAMBDA_EXPR_CAPTURE_LIST passed in LIST.  Do deduction for a previously
+   dependent init-capture.  */
+
+static void
+prepend_one_capture (tree field, tree init, tree &list,
+                    tsubst_flags_t complain)
+{
+  if (tree auto_node = type_uses_auto (TREE_TYPE (field)))
+    {
+      tree type = NULL_TREE;
+      if (!init)
+       {
+         if (complain & tf_error)
+           error ("empty initializer in lambda init-capture");
+         init = error_mark_node;
+       }
+      else if (TREE_CODE (init) == TREE_LIST)
+       init = build_x_compound_expr_from_list (init, ELK_INIT, complain);
+      if (!type)
+       type = do_auto_deduction (TREE_TYPE (field), init, auto_node, complain);
+      TREE_TYPE (field) = type;
+      cp_apply_type_quals_to_decl (cp_type_quals (type), field);
+    }
+  list = tree_cons (field, init, list);
+}
+
 /* T is a LAMBDA_EXPR.  Generate a new LAMBDA_EXPR for the current
    instantiation context.  Instantiating a pack expansion containing a lambda
    might result in multiple lambdas all based on the same lambda in the
@@ -18025,17 +18036,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   tree oldfn = lambda_function (t);
   in_decl = oldfn;
 
-  /* If we have already specialized this lambda expr, reuse it.  See
-     PR c++/87322.  */
-  if (local_specializations)
-    if (tree r = retrieve_local_specialization (t))
-      return r;
-
   tree r = build_lambda_expr ();
 
-  if (local_specializations)
-    register_local_specialization (r, t);
-
   LAMBDA_EXPR_LOCATION (r)
     = LAMBDA_EXPR_LOCATION (t);
   LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
@@ -18088,15 +18090,15 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
          gcc_assert (TREE_CODE (init) == TREE_VEC
                      && TREE_VEC_LENGTH (init) == len);
          for (int i = 0; i < len; ++i)
-           LAMBDA_EXPR_CAPTURE_LIST (r)
-             = tree_cons (TREE_VEC_ELT (field, i),
-                          TREE_VEC_ELT (init, i),
-                          LAMBDA_EXPR_CAPTURE_LIST (r));
+           prepend_one_capture (TREE_VEC_ELT (field, i),
+                                TREE_VEC_ELT (init, i),
+                                LAMBDA_EXPR_CAPTURE_LIST (r),
+                                complain);
        }
       else
        {
-         LAMBDA_EXPR_CAPTURE_LIST (r)
-           = tree_cons (field, init, LAMBDA_EXPR_CAPTURE_LIST (r));
+         prepend_one_capture (field, init, LAMBDA_EXPR_CAPTURE_LIST (r),
+                              complain);
 
          if (id_equal (DECL_NAME (field), "__this"))
            LAMBDA_EXPR_THIS_CAPTURE (r) = field;
@@ -27615,6 +27617,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,
     }
   else
     {
+      if (error_operand_p (init))
+       return error_mark_node;
+
       tree parms = build_tree_list (NULL_TREE, type);
       tree tparms;
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic9.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic9.C
new file mode 100644 (file)
index 0000000..8c5085c
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/90538
+// { dg-do compile { target c++11 } }
+
+template <class... Ts>
+void f(Ts... ts)
+{
+  [=]{
+    f(ts...);
+    f(ts...);
+  }();
+}
+
+void g()
+{
+  f(1);
+}
index e3f446f3700a90aa633cafc2367bab89eb11f56c..6afaee577f2326e08c7c3a75833fb9b5a3003c0b 100644 (file)
@@ -5,6 +5,6 @@
 int main()
 {
   auto a;        // { dg-error "no initializer" }
-  for(auto i: a) // { dg-error "deduce" }
+  for(auto i: a)
     ;
 }
index 0a40ebbee0fbf17c31261354de2ea991eb154be0..6b4ed347eeea113284aca978004ac7e62723f109 100644 (file)
@@ -3,7 +3,7 @@
 
 template < class T = int > void f (T)
 {
-  auto g = [&a = f] () {};  // { dg-error "invalid initialization" }
+  auto g = [&a = f] () {};  // { dg-error "auto" }
 }
 
 int main ()