Commit 10acaf4d by Jason Merrill Committed by Jason Merrill

PR c++/90538 - multiple expansions of capture packs

Previously, with init-capture the type of the closure field was a
DECLTYPE_TYPE of the initializer.  But since each time we tsubst a lambda we
get a different lambda, that meant that if the initializer is a lambda, we'd
end up with different closure types in the field and initializer after
substitution (PR 87322).  We dealt with this by remembering the lambda
instantiation within each pack expansion element, using
local_specialization_stack to separate the elements.  But that broke this
testcase, because it lost lambda capture proxies that also use
local_specializations.

So, this patch removes the local_specializations changes from that patch and
fixes 87322 differently, by giving init-capture fields 'auto' type and doing
deduction later.  There's a bit of a kludge to get the right number of
fields by pretending that 'auto...' uses the parameter packs from the
initializer, but it does the trick.

	* cp-tree.h (DECLTYPE_FOR_INIT_CAPTURE): Remove.
	* lambda.c (add_capture): Copy parameter packs from init.
	(lambda_capture_field_type): Always use auto for init-capture.
	* pt.c (uses_parameter_packs): Return tree.
	(tsubst) [DECLTYPE_TYPE]: Remove init-capture handling.
	(gen_elem_of_pack_expansion_instantiation): Don't push
	local_specialization_stack.
	(prepend_one_capture): New.
	(tsubst_lambda_expr): Use it.  Don't touch local_specializations.
	(do_auto_deduction): Avoid redundant error.

From-SVN: r273944
parent fc79fc49
2019-07-31 Jason Merrill <jason@redhat.com> 2019-07-31 Jason Merrill <jason@redhat.com>
PR c++/90538 - multiple expansions of capture packs
* cp-tree.h (DECLTYPE_FOR_INIT_CAPTURE): Remove.
* lambda.c (add_capture): Copy parameter packs from init.
(lambda_capture_field_type): Always use auto for init-capture.
* pt.c (uses_parameter_packs): Return tree.
(tsubst) [DECLTYPE_TYPE]: Remove init-capture handling.
(gen_elem_of_pack_expansion_instantiation): Don't push
local_specialization_stack.
(prepend_one_capture): New.
(tsubst_lambda_expr): Use it. Don't touch local_specializations.
(do_auto_deduction): Avoid redundant error.
Fix copy_node of TEMPLATE_INFO. Fix copy_node of TEMPLATE_INFO.
* cp-tree.h (struct tree_template_info): Use tree_base instead of * cp-tree.h (struct tree_template_info): Use tree_base instead of
tree_common. Add tmpl and args fields. tree_common. Add tmpl and args fields.
......
...@@ -423,7 +423,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; ...@@ -423,7 +423,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
LAMBDA_EXPR_MUTABLE_P (in LAMBDA_EXPR) LAMBDA_EXPR_MUTABLE_P (in LAMBDA_EXPR)
DECL_FINAL_P (in FUNCTION_DECL) DECL_FINAL_P (in FUNCTION_DECL)
QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF) QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF)
DECLTYPE_FOR_INIT_CAPTURE (in DECLTYPE_TYPE)
CONSTRUCTOR_IS_DEPENDENT (in CONSTRUCTOR) CONSTRUCTOR_IS_DEPENDENT (in CONSTRUCTOR)
TINFO_USED_TEMPLATE_ID (in TEMPLATE_INFO) TINFO_USED_TEMPLATE_ID (in TEMPLATE_INFO)
PACK_EXPANSION_SIZEOF_P (in *_PACK_EXPANSION) PACK_EXPANSION_SIZEOF_P (in *_PACK_EXPANSION)
...@@ -4484,12 +4483,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) ...@@ -4484,12 +4483,10 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
(DECLTYPE_TYPE_CHECK (NODE))->type_common.string_flag (DECLTYPE_TYPE_CHECK (NODE))->type_common.string_flag
/* These flags indicate that we want different semantics from normal /* These flags indicate that we want different semantics from normal
decltype: lambda capture just drops references, init capture decltype: lambda capture just drops references,
uses auto semantics, lambda proxies look through implicit dereference. */ lambda proxies look through implicit dereference. */
#define DECLTYPE_FOR_LAMBDA_CAPTURE(NODE) \ #define DECLTYPE_FOR_LAMBDA_CAPTURE(NODE) \
TREE_LANG_FLAG_0 (DECLTYPE_TYPE_CHECK (NODE)) TREE_LANG_FLAG_0 (DECLTYPE_TYPE_CHECK (NODE))
#define DECLTYPE_FOR_INIT_CAPTURE(NODE) \
TREE_LANG_FLAG_1 (DECLTYPE_TYPE_CHECK (NODE))
#define DECLTYPE_FOR_LAMBDA_PROXY(NODE) \ #define DECLTYPE_FOR_LAMBDA_PROXY(NODE) \
TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE)) TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
#define DECLTYPE_FOR_REF_CAPTURE(NODE) \ #define DECLTYPE_FOR_REF_CAPTURE(NODE) \
...@@ -6794,7 +6791,7 @@ extern bool maybe_instantiate_noexcept (tree, tsubst_flags_t = tf_warning_or_er ...@@ -6794,7 +6791,7 @@ extern bool maybe_instantiate_noexcept (tree, tsubst_flags_t = tf_warning_or_er
extern tree instantiate_decl (tree, bool, bool); extern tree instantiate_decl (tree, bool, bool);
extern int comp_template_parms (const_tree, const_tree); extern int comp_template_parms (const_tree, const_tree);
extern bool builtin_pack_fn_p (tree); extern bool builtin_pack_fn_p (tree);
extern bool uses_parameter_packs (tree); extern tree uses_parameter_packs (tree);
extern bool template_parameter_pack_p (const_tree); extern bool template_parameter_pack_p (const_tree);
extern bool function_parameter_pack_p (const_tree); extern bool function_parameter_pack_p (const_tree);
extern bool function_parameter_expanded_from_pack_p (tree, tree); extern bool function_parameter_expanded_from_pack_p (tree, tree);
......
...@@ -213,16 +213,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, ...@@ -213,16 +213,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
tree type; tree type;
bool is_this = is_this_parameter (tree_strip_nop_conversions (expr)); bool is_this = is_this_parameter (tree_strip_nop_conversions (expr));
if (!is_this && type_dependent_expression_p (expr)) if (!is_this && explicit_init_p)
{
type = cxx_make_type (DECLTYPE_TYPE);
DECLTYPE_TYPE_EXPR (type) = expr;
DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true;
DECLTYPE_FOR_INIT_CAPTURE (type) = explicit_init_p;
DECLTYPE_FOR_REF_CAPTURE (type) = by_reference_p;
SET_TYPE_STRUCTURAL_EQUALITY (type);
}
else if (!is_this && explicit_init_p)
{ {
tree auto_node = make_auto (); tree auto_node = make_auto ();
...@@ -233,6 +224,14 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, ...@@ -233,6 +224,14 @@ lambda_capture_field_type (tree expr, bool explicit_init_p,
type = build_reference_type (type); type = build_reference_type (type);
type = do_auto_deduction (type, expr, auto_node); type = do_auto_deduction (type, expr, auto_node);
} }
else if (!is_this && type_dependent_expression_p (expr))
{
type = cxx_make_type (DECLTYPE_TYPE);
DECLTYPE_TYPE_EXPR (type) = expr;
DECLTYPE_FOR_LAMBDA_CAPTURE (type) = true;
DECLTYPE_FOR_REF_CAPTURE (type) = by_reference_p;
SET_TYPE_STRUCTURAL_EQUALITY (type);
}
else else
{ {
type = non_reference (unlowered_expr_type (expr)); type = non_reference (unlowered_expr_type (expr));
...@@ -594,7 +593,16 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p, ...@@ -594,7 +593,16 @@ add_capture (tree lambda, tree id, tree orig_init, bool by_reference_p,
name = get_identifier (buf); name = get_identifier (buf);
if (variadic) if (variadic)
type = make_pack_expansion (type); {
type = make_pack_expansion (type);
if (explicit_init_p)
/* With an explicit initializer 'type' is auto, which isn't really a
parameter pack in this context. We will want as many fields as we
have elements in the expansion of the initializer, so use its packs
instead. */
PACK_EXPANSION_PARAMETER_PACKS (type)
= uses_parameter_packs (initializer);
}
/* Make member variable. */ /* Make member variable. */
member = build_decl (input_location, FIELD_DECL, name, type); member = build_decl (input_location, FIELD_DECL, name, type);
......
...@@ -3872,7 +3872,7 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data) ...@@ -3872,7 +3872,7 @@ find_parameter_packs_r (tree *tp, int *walk_subtrees, void* data)
} }
/* Determines if the expression or type T uses any parameter packs. */ /* Determines if the expression or type T uses any parameter packs. */
bool tree
uses_parameter_packs (tree t) uses_parameter_packs (tree t)
{ {
tree parameter_packs = NULL_TREE; tree parameter_packs = NULL_TREE;
...@@ -3882,7 +3882,7 @@ uses_parameter_packs (tree t) ...@@ -3882,7 +3882,7 @@ uses_parameter_packs (tree t)
ppd.type_pack_expansion_p = false; ppd.type_pack_expansion_p = false;
cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited); cp_walk_tree (&t, &find_parameter_packs_r, &ppd, ppd.visited);
delete ppd.visited; delete ppd.visited;
return parameter_packs != NULL_TREE; return parameter_packs;
} }
/* Turn ARG, which may be an expression, type, or a TREE_LIST /* Turn ARG, which may be an expression, type, or a TREE_LIST
...@@ -11764,10 +11764,6 @@ gen_elem_of_pack_expansion_instantiation (tree pattern, ...@@ -11764,10 +11764,6 @@ gen_elem_of_pack_expansion_instantiation (tree pattern,
ARGUMENT_PACK_SELECT_INDEX (aps) = index; ARGUMENT_PACK_SELECT_INDEX (aps) = index;
} }
// Any local specialization bindings arising from this substitution
// cannot be reused for a different INDEX.
local_specialization_stack lss (lss_copy);
/* Substitute into the PATTERN with the (possibly altered) /* Substitute into the PATTERN with the (possibly altered)
arguments. */ arguments. */
if (pattern == in_decl) if (pattern == in_decl)
...@@ -15139,24 +15135,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) ...@@ -15139,24 +15135,12 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
/*function_p*/false, /*function_p*/false,
/*integral_constant_expression*/false); /*integral_constant_expression*/false);
if (DECLTYPE_FOR_INIT_CAPTURE (t))
{
if (type == NULL_TREE)
{
if (complain & tf_error)
error ("empty initializer in lambda init-capture");
type = error_mark_node;
}
else if (TREE_CODE (type) == TREE_LIST)
type = build_x_compound_expr_from_list (type, ELK_INIT, complain);
}
--cp_unevaluated_operand; --cp_unevaluated_operand;
--c_inhibit_evaluation_warnings; --c_inhibit_evaluation_warnings;
if (DECLTYPE_FOR_LAMBDA_CAPTURE (t)) if (DECLTYPE_FOR_LAMBDA_CAPTURE (t))
type = lambda_capture_field_type (type, type = lambda_capture_field_type (type,
DECLTYPE_FOR_INIT_CAPTURE (t), false /*explicit_init*/,
DECLTYPE_FOR_REF_CAPTURE (t)); DECLTYPE_FOR_REF_CAPTURE (t));
else if (DECLTYPE_FOR_LAMBDA_PROXY (t)) else if (DECLTYPE_FOR_LAMBDA_PROXY (t))
type = lambda_proxy_type (type); type = lambda_proxy_type (type);
...@@ -18023,6 +18007,33 @@ tsubst_non_call_postfix_expression (tree t, tree args, ...@@ -18023,6 +18007,33 @@ tsubst_non_call_postfix_expression (tree t, tree args,
return t; return t;
} }
/* Subroutine of tsubst_lambda_expr: add the FIELD/INIT capture pair to the
LAMBDA_EXPR_CAPTURE_LIST passed in LIST. Do deduction for a previously
dependent init-capture. */
static void
prepend_one_capture (tree field, tree init, tree &list,
tsubst_flags_t complain)
{
if (tree auto_node = type_uses_auto (TREE_TYPE (field)))
{
tree type = NULL_TREE;
if (!init)
{
if (complain & tf_error)
error ("empty initializer in lambda init-capture");
init = error_mark_node;
}
else if (TREE_CODE (init) == TREE_LIST)
init = build_x_compound_expr_from_list (init, ELK_INIT, complain);
if (!type)
type = do_auto_deduction (TREE_TYPE (field), init, auto_node, complain);
TREE_TYPE (field) = type;
cp_apply_type_quals_to_decl (cp_type_quals (type), field);
}
list = tree_cons (field, init, list);
}
/* T is a LAMBDA_EXPR. Generate a new LAMBDA_EXPR for the current /* T is a LAMBDA_EXPR. Generate a new LAMBDA_EXPR for the current
instantiation context. Instantiating a pack expansion containing a lambda instantiation context. Instantiating a pack expansion containing a lambda
might result in multiple lambdas all based on the same lambda in the might result in multiple lambdas all based on the same lambda in the
...@@ -18034,17 +18045,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) ...@@ -18034,17 +18045,8 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
tree oldfn = lambda_function (t); tree oldfn = lambda_function (t);
in_decl = oldfn; in_decl = oldfn;
/* If we have already specialized this lambda expr, reuse it. See
PR c++/87322. */
if (local_specializations)
if (tree r = retrieve_local_specialization (t))
return r;
tree r = build_lambda_expr (); tree r = build_lambda_expr ();
if (local_specializations)
register_local_specialization (r, t);
LAMBDA_EXPR_LOCATION (r) LAMBDA_EXPR_LOCATION (r)
= LAMBDA_EXPR_LOCATION (t); = LAMBDA_EXPR_LOCATION (t);
LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
...@@ -18097,15 +18099,15 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) ...@@ -18097,15 +18099,15 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
gcc_assert (TREE_CODE (init) == TREE_VEC gcc_assert (TREE_CODE (init) == TREE_VEC
&& TREE_VEC_LENGTH (init) == len); && TREE_VEC_LENGTH (init) == len);
for (int i = 0; i < len; ++i) for (int i = 0; i < len; ++i)
LAMBDA_EXPR_CAPTURE_LIST (r) prepend_one_capture (TREE_VEC_ELT (field, i),
= tree_cons (TREE_VEC_ELT (field, i), TREE_VEC_ELT (init, i),
TREE_VEC_ELT (init, i), LAMBDA_EXPR_CAPTURE_LIST (r),
LAMBDA_EXPR_CAPTURE_LIST (r)); complain);
} }
else else
{ {
LAMBDA_EXPR_CAPTURE_LIST (r) prepend_one_capture (field, init, LAMBDA_EXPR_CAPTURE_LIST (r),
= tree_cons (field, init, LAMBDA_EXPR_CAPTURE_LIST (r)); complain);
if (id_equal (DECL_NAME (field), "__this")) if (id_equal (DECL_NAME (field), "__this"))
LAMBDA_EXPR_THIS_CAPTURE (r) = field; LAMBDA_EXPR_THIS_CAPTURE (r) = field;
...@@ -27602,6 +27604,9 @@ do_auto_deduction (tree type, tree init, tree auto_node, ...@@ -27602,6 +27604,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,
} }
else else
{ {
if (error_operand_p (init))
return error_mark_node;
tree parms = build_tree_list (NULL_TREE, type); tree parms = build_tree_list (NULL_TREE, type);
tree tparms; tree tparms;
......
// PR c++/90538
// { dg-do compile { target c++11 } }
template <class... Ts>
void f(Ts... ts)
{
[=]{
f(ts...);
f(ts...);
}();
}
void g()
{
f(1);
}
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
int main() int main()
{ {
auto a; // { dg-error "no initializer" } auto a; // { dg-error "no initializer" }
for(auto i: a) // { dg-error "deduce" } for(auto i: a)
; ;
} }
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
template < class T = int > void f (T) template < class T = int > void f (T)
{ {
auto g = [&a = f] () {}; // { dg-error "invalid initialization" } auto g = [&a = f] () {}; // { dg-error "auto" }
} }
int main () int main ()
......
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