diff mbox series

[C++] Implement P0780R2, pack expansion in lambda init-capture.

Message ID 20181113043434.8860-1-jason@redhat.com
State New
Headers show
Series [C++] Implement P0780R2, pack expansion in lambda init-capture. | expand

Commit Message

Jason Merrill Nov. 13, 2018, 4:34 a.m. UTC
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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	* 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.
---
 gcc/cp/parser.c                               | 13 +++
 gcc/cp/pt.c                                   | 93 ++++++++++++++++---
 .../g++.dg/cpp2a/lambda-pack-init1.C          | 17 ++++
 gcc/cp/ChangeLog                              | 10 ++
 4 files changed, 122 insertions(+), 11 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C


base-commit: 4daed3b3d5561c30ad430f35a260fbcdf06e397d
diff mbox series

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 465ab8fdbae..0428f6dda90 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -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
 	{
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 4cb8238ba12..0c33c8e1527 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -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
index 00000000000..89c63532831
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C
@@ -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
+}
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 066d293e531..864d47cf17a 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,13 @@ 
+2018-11-09  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.
+
 2018-11-12  Jason Merrill  <jason@redhat.com>
 
 	* cp-tree.h (struct cp_evaluated): New.