Commit 7de37c97 by Jason Merrill Committed by Jason Merrill

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
parent 16e723e6
2018-11-12 Jason Merrill <jason@redhat.com> 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. * cp-tree.h (struct cp_evaluated): New.
* init.c (get_nsdmi): Use it. * init.c (get_nsdmi): Use it.
* parser.c (cp_parser_enclosed_template_argument_list): Use it. * parser.c (cp_parser_enclosed_template_argument_list): Use it.
......
...@@ -10395,6 +10395,17 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) ...@@ -10395,6 +10395,17 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
continue; 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. */ /* Remember whether we want to capture as a reference or not. */
if (cp_lexer_next_token_is (parser->lexer, CPP_AND)) if (cp_lexer_next_token_is (parser->lexer, CPP_AND))
{ {
...@@ -10438,6 +10449,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) ...@@ -10438,6 +10449,8 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
error ("empty initializer for lambda init-capture"); error ("empty initializer for lambda init-capture");
capture_init_expr = error_mark_node; capture_init_expr = error_mark_node;
} }
if (init_pack_expansion)
capture_init_expr = make_pack_expansion (capture_init_expr);
} }
else else
{ {
...@@ -12151,7 +12151,7 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain, ...@@ -12151,7 +12151,7 @@ tsubst_pack_expansion (tree t, tree args, tsubst_flags_t complain,
where it isn't expected). */ where it isn't expected). */
unsubstituted_fn_pack = true; 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); arg_pack = retrieve_local_specialization (parm_pack);
if (argument_pack_element_is_expansion_p (arg_pack, 0)) if (argument_pack_element_is_expansion_p (arg_pack, 0))
...@@ -16769,6 +16769,55 @@ tsubst_decomp_names (tree decl, tree pattern_decl, tree args, ...@@ -16769,6 +16769,55 @@ tsubst_decomp_names (tree decl, tree pattern_decl, tree args,
return decl; 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 /* Like tsubst_copy for expressions, etc. but also does semantic
processing. */ processing. */
...@@ -16854,18 +16903,21 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, ...@@ -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 /* We're in tsubst_lambda_expr, we've already inserted a new
capture proxy, so look it up and register it. */ capture proxy, so look it up and register it. */
tree inst; 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 inst = (retrieve_local_specialization
(DECL_CAPTURED_VARIABLE (decl))); (DECL_CAPTURED_VARIABLE (decl)));
gcc_assert (TREE_CODE (inst) == NONTYPE_ARGUMENT_PACK); gcc_assert (TREE_CODE (inst) == NONTYPE_ARGUMENT_PACK);
} }
else else
{ inst = lookup_init_capture_pack (decl);
inst = lookup_name_real (DECL_NAME (decl), 0, 0,
/*block_p=*/true, 0, LOOKUP_HIDDEN);
gcc_assert (inst != decl && is_capture_proxy (inst));
}
register_local_specialization (inst, decl); register_local_specialization (inst, decl);
break; break;
} }
...@@ -17812,13 +17864,22 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) ...@@ -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 gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
&& LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
vec<tree,va_gc>* field_packs = NULL;
for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap; for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (t); cap;
cap = TREE_CHAIN (cap)) cap = TREE_CHAIN (cap))
{ {
tree field = TREE_PURPOSE (cap); tree ofield = TREE_PURPOSE (cap);
if (PACK_EXPANSION_P (field)) if (PACK_EXPANSION_P (ofield))
field = PACK_EXPANSION_PATTERN (field); ofield = PACK_EXPANSION_PATTERN (ofield);
field = tsubst_decl (field, args, complain); 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) if (field == error_mark_node)
return 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) ...@@ -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); 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); register_parameter_specializations (oldfn, fn);
if (oldtmpl) if (oldtmpl)
......
// { 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
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment