Implement P0780R2, pack expansion in lambda init-capture.
authorJason Merrill <jason@redhat.com>
Tue, 13 Nov 2018 04:34:59 +0000 (23:34 -0500)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 13 Nov 2018 04:34:59 +0000 (23:34 -0500)
Mostly this was straightforward; the tricky bit was finding, in the
instantiation, the set of capture proxies built when instantiating the
init-capture.  The comment in lookup_init_capture_pack goes into detail.

* parser.c (cp_parser_lambda_introducer): Parse pack init-capture.
* pt.c (tsubst_pack_expansion): Handle init-capture packs.
(lookup_init_capture_pack): New.
(tsubst_expr) [DECL_EXPR]: Use it.
(tsubst_lambda_expr): Remember field pack expansions for
init-captures.

From-SVN: r266052

gcc/cp/ChangeLog
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C [new file with mode: 0644]

index 066d293e531ef329f226b8af34c7fba3ca538f6c..2f15c08b3e4fe390ce34c922c46207ea3451ae49 100644 (file)
@@ -1,5 +1,13 @@
 2018-11-12  Jason Merrill  <jason@redhat.com>
 
+       Implement P0780R2, pack expansion in lambda init-capture.
+       * parser.c (cp_parser_lambda_introducer): Parse pack init-capture.
+       * pt.c (tsubst_pack_expansion): Handle init-capture packs.
+       (lookup_init_capture_pack): New.
+       (tsubst_expr) [DECL_EXPR]: Use it.
+       (tsubst_lambda_expr): Remember field pack expansions for
+       init-captures.
+
        * cp-tree.h (struct cp_evaluated): New.
        * init.c (get_nsdmi): Use it.
        * parser.c (cp_parser_enclosed_template_argument_list): Use it.
index 465ab8fdbaed0efbaa68a8583dac908ce6c72f9e..0428f6dda906126f0c989ac376779ffd0dedd536 100644 (file)
@@ -10395,6 +10395,17 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
          continue;
        }
 
+      bool init_pack_expansion = false;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+       {
+         location_t loc = cp_lexer_peek_token (parser->lexer)->location;
+         if (cxx_dialect < cxx2a)
+           pedwarn (loc, 0, "pack init-capture only available with "
+                            "-std=c++2a or -std=gnu++2a");
+         cp_lexer_consume_token (parser->lexer);
+         init_pack_expansion = true;
+       }
+
       /* Remember whether we want to capture as a reference or not.  */
       if (cp_lexer_next_token_is (parser->lexer, CPP_AND))
        {
@@ -10438,6 +10449,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
              error ("empty initializer for lambda init-capture");
              capture_init_expr = error_mark_node;
            }
+         if (init_pack_expansion)
+           capture_init_expr = make_pack_expansion (capture_init_expr);
        }
       else
        {
index 4cb8238ba121f8461bf511fb57844eee90cea1cf..0c33c8e1527085ed300d1c259f5820ca43e8d597 100644 (file)
@@ -12151,7 +12151,7 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
               where it isn't expected).  */
            unsubstituted_fn_pack = true;
        }
-      else if (is_normal_capture_proxy (parm_pack))
+      else if (is_capture_proxy (parm_pack))
        {
          arg_pack = retrieve_local_specialization (parm_pack);
          if (argument_pack_element_is_expansion_p (arg_pack, 0))
@@ -16769,6 +16769,55 @@ tsubst_decomp_names (tree decl, tree pattern_decl, tree args,
   return decl;
 }
 
+/* Return the proper local_specialization for init-capture pack DECL.  */
+
+static tree
+lookup_init_capture_pack (tree decl)
+{
+  /* We handle normal pack captures by forwarding to the specialization of the
+     captured parameter.  We can't do that for pack init-captures; we need them
+     to have their own local_specialization.  We created the individual
+     VAR_DECLs (if any) under build_capture_proxy, and we need to collect them
+     when we process the DECL_EXPR for the pack init-capture in the template.
+     So, how do we find them?  We don't know the capture proxy pack when
+     building the individual resulting proxies, and we don't know the
+     individual proxies when instantiating the pack.  What we have in common is
+     the FIELD_DECL.
+
+     So...when we instantiate the FIELD_DECL, we stick the result in
+     local_specializations.  Then at the DECL_EXPR we look up that result, see
+     how many elements it has, synthesize the names, and look them up.  */
+
+  tree cname = DECL_NAME (decl);
+  tree val = DECL_VALUE_EXPR (decl);
+  tree field = TREE_OPERAND (val, 1);
+  gcc_assert (TREE_CODE (field) == FIELD_DECL);
+  tree fpack = retrieve_local_specialization (field);
+  if (fpack == error_mark_node)
+    return error_mark_node;
+
+  int len = 1;
+  tree vec = NULL_TREE;
+  tree r = NULL_TREE;
+  if (TREE_CODE (fpack) == TREE_VEC)
+    {
+      len = TREE_VEC_LENGTH (fpack);
+      vec = make_tree_vec (len);
+      r = make_node (NONTYPE_ARGUMENT_PACK);
+      SET_ARGUMENT_PACK_ARGS (r, vec);
+    }
+  for (int i = 0; i < len; ++i)
+    {
+      tree ename = vec ? make_ith_pack_parameter_name (cname, i) : cname;
+      tree elt = lookup_name_real (ename, 0, 0, true, 0, LOOKUP_NORMAL);
+      if (vec)
+       TREE_VEC_ELT (vec, i) = elt;
+      else
+       r = elt;
+    }
+  return r;
+}
+
 /* Like tsubst_copy for expressions, etc. but also does semantic
    processing.  */
 
@@ -16854,18 +16903,21 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl,
            /* We're in tsubst_lambda_expr, we've already inserted a new
               capture proxy, so look it up and register it.  */
            tree inst;
-           if (DECL_PACK_P (decl))
+           if (!DECL_PACK_P (decl))
+             {
+               inst = lookup_name_real (DECL_NAME (decl), 0, 0,
+                                        /*block_p=*/true, 0, LOOKUP_HIDDEN);
+               gcc_assert (inst != decl && is_capture_proxy (inst));
+             }
+           else if (is_normal_capture_proxy (decl))
              {
                inst = (retrieve_local_specialization
                        (DECL_CAPTURED_VARIABLE (decl)));
                gcc_assert (TREE_CODE (inst) == NONTYPE_ARGUMENT_PACK);
              }
            else
-             {
-               inst = lookup_name_real (DECL_NAME (decl), 0, 0,
-                                        /*block_p=*/true, 0, LOOKUP_HIDDEN);
-               gcc_assert (inst != decl && is_capture_proxy (inst));
-             }
+             inst = lookup_init_capture_pack (decl);
+
            register_local_specialization (inst, decl);
            break;
          }
@@ -17812,13 +17864,22 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
              && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
 
+  vec<tree,va_gc>* field_packs = NULL;
+
   for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap;
        cap = TREE_CHAIN (cap))
     {
-      tree field = TREE_PURPOSE (cap);
-      if (PACK_EXPANSION_P (field))
-       field = PACK_EXPANSION_PATTERN (field);
-      field = tsubst_decl (field, args, complain);
+      tree ofield = TREE_PURPOSE (cap);
+      if (PACK_EXPANSION_P (ofield))
+       ofield = PACK_EXPANSION_PATTERN (ofield);
+      tree field = tsubst_decl (ofield, args, complain);
+
+      if (DECL_PACK_P (ofield) && !DECL_NORMAL_CAPTURE_P (ofield))
+       {
+         /* Remember these for when we've pushed local_specializations.  */
+         vec_safe_push (field_packs, ofield);
+         vec_safe_push (field_packs, field);
+       }
 
       if (field == error_mark_node)
        return error_mark_node;
@@ -17908,6 +17969,16 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 
       tree body = start_lambda_function (fn, r);
 
+      /* Now record them for lookup_init_capture_pack.  */
+      int fplen = vec_safe_length (field_packs);
+      for (int i = 0; i < fplen; )
+       {
+         tree pack = (*field_packs)[i++];
+         tree inst = (*field_packs)[i++];
+         register_local_specialization (inst, pack);
+       }
+      release_tree_vector (field_packs);
+
       register_parameter_specializations (oldfn, fn);
 
       if (oldtmpl)
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C b/gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C
new file mode 100644 (file)
index 0000000..89c6353
--- /dev/null
@@ -0,0 +1,17 @@
+// { dg-do compile { target c++2a } }
+
+void bar();
+void bar(int);
+
+template <typename... Args>
+void foo(Args... args) {
+  [...xs=args]{
+    bar(xs...); // xs is an init-capture pack
+  };
+}
+
+int main()
+{
+  foo();  // OK: xs contains zero init-captures
+  foo(1); // OK: xs contains one init-capture
+}