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 <ja...@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. 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); +}