From 7de37c97b4031ba61c867cf6fadf63916c666894 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 12 Nov 2018 23:34:59 -0500 Subject: [PATCH] Implement P0780R2, pack expansion in lambda init-capture. 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 | 8 ++ gcc/cp/parser.c | 13 +++ gcc/cp/pt.c | 93 ++++++++++++++++--- .../g++.dg/cpp2a/lambda-pack-init1.C | 17 ++++ 4 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-pack-init1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 066d293e531..2f15c08b3e4 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,13 @@ 2018-11-12 Jason Merrill + 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. 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* 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 +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 +} -- 2.30.2