C++ PATCH for more variadic capture issues

Message ID CADzB+2ndbLzkF1+zrQeB5Sysc3dDE77xQktNOton5gpW-n7_KQ@mail.gmail.com
State New
Headers show
Series
  • C++ PATCH for more variadic capture issues
Related show

Commit Message

Jason Merrill Feb. 13, 2018, 2:21 p.m.
This patch fixes two issues:

1) We were failing to capture a parameter pack used by a pack
expansion which needs to use PACK_EXPANSION_EXTRA_ARGS, because in
that case we defer actual substitution until we have all the arguments
we need.  But by that time we're inside an instantiation of the lambda
op(), and it's too late to capture anything.  So we need to force
lambda capture while we're packing up the arguments we'll need for
that later substitution.

2) While looking at that, I noticed that although we recently started
to support implicit capture within a pack expansion, we didn't support
explicit capture.

Tested x86_64-pc-linux-gnu, applying to trunk.  After this patch, the
Boost.Hana testsuite also passes again.
commit 6383f88febfbe13aef71a9da2f678ecae344d1f2
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Feb 12 10:26:53 2018 -0500

            Fix more variadic capture issues.
    
            * pt.c (find_parameter_packs_r): Also look at explicit captures.
            (check_for_bare_parameter_packs): Check current_class_type for
            lambda context.
            (extract_locals_r): Handle seeing a full instantiation of a pack.
            (tsubst_pack_expansion): Likewise.  Force lambda capture.
            * parser.c (cp_parser_lambda_introducer): Don't
            check_for_bare_parameter_packs.

Patch

diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 9a05e4fc812..81c6f0128e6 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -10412,8 +10412,6 @@  cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
 	      cp_lexer_consume_token (parser->lexer);
 	      capture_init_expr = make_pack_expansion (capture_init_expr);
 	    }
-	  else
-	    check_for_bare_parameter_packs (capture_init_expr);
 	}
 
       if (LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda_expr) != CPLD_NONE
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a83b7073d20..02d448e99b6 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -3587,7 +3587,6 @@  find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
         }
       break;
 
-      /* Look through a lambda capture proxy to the field pack.  */
     case VAR_DECL:
       if (DECL_PACK_P (t))
         {
@@ -3707,6 +3706,12 @@  find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
 
     case LAMBDA_EXPR:
       {
+	/* Look at explicit captures.  */
+	for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t);
+	     cap; cap = TREE_CHAIN (cap))
+	  cp_walk_tree (&TREE_VALUE (cap), &find_parameter_packs_r, ppd,
+			ppd->visited);
+	/* Since we defer implicit capture, look in the body as well.  */
 	tree fn = lambda_function (t);
 	cp_walk_tree (&DECL_SAVED_TREE (fn), &find_parameter_packs_r, ppd,
 		      ppd->visited);
@@ -3907,7 +3912,7 @@  check_for_bare_parameter_packs (tree t)
     return false;
 
   /* A lambda might use a parameter pack from the containing context.  */
-  if (current_function_decl && LAMBDA_FUNCTION_P (current_function_decl))
+  if (current_class_type && LAMBDA_TYPE_P (current_class_type))
     return false;
 
   if (TREE_CODE (t) == TYPE_DECL)
@@ -11410,30 +11415,72 @@  tsubst_binary_right_fold (tree t, tree args, tsubst_flags_t complain,
 /* Walk through the pattern of a pack expansion, adding everything in
    local_specializations to a list.  */
 
+struct el_data
+{
+  tree extra;
+  tsubst_flags_t complain;
+};
 static tree
-extract_locals_r (tree *tp, int */*walk_subtrees*/, void *data)
+extract_locals_r (tree *tp, int */*walk_subtrees*/, void *data_)
 {
-  tree *extra = reinterpret_cast<tree*>(data);
+  el_data &data = *reinterpret_cast<el_data*>(data_);
+  tree *extra = &data.extra;
+  tsubst_flags_t complain = data.complain;
   if (tree spec = retrieve_local_specialization (*tp))
     {
       if (TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)
 	{
-	  /* Pull out the actual PARM_DECL for the partial instantiation.  */
+	  /* Maybe pull out the PARM_DECL for a partial instantiation.  */
 	  tree args = ARGUMENT_PACK_ARGS (spec);
-	  gcc_assert (TREE_VEC_LENGTH (args) == 1);
-	  tree arg = TREE_VEC_ELT (args, 0);
-	  spec = PACK_EXPANSION_PATTERN (arg);
+	  if (TREE_VEC_LENGTH (args) == 1)
+	    {
+	      tree elt = TREE_VEC_ELT (args, 0);
+	      if (PACK_EXPANSION_P (elt))
+		elt = PACK_EXPANSION_PATTERN (elt);
+	      if (DECL_PACK_P (elt))
+		spec = elt;
+	    }
+	  if (TREE_CODE (spec) == NONTYPE_ARGUMENT_PACK)
+	    {
+	      /* Handle lambda capture here, since we aren't doing any
+		 substitution now, and so tsubst_copy won't call
+		 process_outer_var_ref.  */
+	      tree args = ARGUMENT_PACK_ARGS (spec);
+	      int len = TREE_VEC_LENGTH (args);
+	      for (int i = 0; i < len; ++i)
+		{
+		  tree arg = TREE_VEC_ELT (args, i);
+		  tree carg = arg;
+		  if (outer_automatic_var_p (arg))
+		    carg = process_outer_var_ref (arg, complain);
+		  if (carg != arg)
+		    {
+		      /* Make a new NONTYPE_ARGUMENT_PACK of the capture
+			 proxies.  */
+		      if (i == 0)
+			{
+			  spec = copy_node (spec);
+			  args = copy_node (args);
+			  SET_ARGUMENT_PACK_ARGS (spec, args);
+			  register_local_specialization (spec, *tp);
+			}
+		      TREE_VEC_ELT (args, i) = carg;
+		    }
+		}
+	    }
 	}
+      if (outer_automatic_var_p (spec))
+	spec = process_outer_var_ref (spec, complain);
       *extra = tree_cons (*tp, spec, *extra);
     }
   return NULL_TREE;
 }
 static tree
-extract_local_specs (tree pattern)
+extract_local_specs (tree pattern, tsubst_flags_t complain)
 {
-  tree extra = NULL_TREE;
-  cp_walk_tree_without_duplicates (&pattern, extract_locals_r, &extra);
-  return extra;
+  el_data data = { NULL_TREE, complain };
+  cp_walk_tree_without_duplicates (&pattern, extract_locals_r, &data);
+  return data.extra;
 }
 
 /* Substitute ARGS into T, which is an pack expansion
@@ -11468,8 +11515,10 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
 	     extract_local_specs; map from the general template to our local
 	     context.  */
 	  tree gen = TREE_PURPOSE (elt);
-	  tree partial = TREE_VALUE (elt);
-	  tree inst = retrieve_local_specialization (partial);
+	  tree inst = TREE_VALUE (elt);
+	  if (DECL_PACK_P (inst))
+	    inst = retrieve_local_specialization (inst);
+	  /* else inst is already a full instantiation of the pack.  */
 	  register_local_specialization (inst, gen);
 	}
       gcc_assert (!TREE_PURPOSE (extra));
@@ -11651,7 +11700,7 @@  tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
       t = make_pack_expansion (pattern, complain);
       tree extra = args;
       if (local_specializations)
-	if (tree locals = extract_local_specs (pattern))
+	if (tree locals = extract_local_specs (pattern, complain))
 	  extra = tree_cons (NULL_TREE, extra, locals);
       PACK_EXPANSION_EXTRA_ARGS (t) = extra;
       return t;
diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C
index 5196a18b5f1..97f64cd761a 100644
--- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C
+++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-variadic5.C
@@ -11,6 +11,7 @@  template<class... T>
 void print_all(const T&... t)
 {
   accept_all([&]()->int { print(t); return 0; }...);
+  accept_all([&t]()->int { print(t); return 0; }...);
 }
 
 int main()
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic12.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic12.C
new file mode 100644
index 00000000000..811332040fa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-variadic12.C
@@ -0,0 +1,16 @@ 
+// { dg-do compile { target c++14 } }
+
+template < typename... T > void sink(T ...){}
+
+template < typename... T >
+auto f(T... t){
+  [=](auto ... j){
+    sink((t + j)...);
+  }(t...);
+}
+
+int main(){
+  f(0);
+  f();
+  f(0.1,0.2);
+}