Message ID | 20210407161006.2727403-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
On 4/7/21 12:10 PM, Patrick Palka wrote: > We currently substitute through a lambda's constraints whenever we > regenerate it via tsubst_lambda_expr. This is the wrong approach > because it can lead to hard errors due to constraints being evaluated > out of order (as in the testcase concepts-lambda17.C below), and because > it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS > mechanism for delaying substitution into requires-expressions, which is > the cause of this PR. > > But in order to avoid substituting through a lambda's constraints during > regeneration, we we need to be able to get at all in-scope template > parameters and the corresponding template arguments during constraint > checking of a lambda's op(). And this information is not easily > available where we need it, it seems. > > To that end, the approach that this patch takes is to add two new fields > to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM > (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS. > The former allows us to obtain the complete set of template parameters > that are in-scope for a lambda's op(), and the latter gives us all outer > template arguments that were used to regenerate the lambda. I'm a little surprised you didn't use a TEMPLATE_INFO for these, but this way is fine too. > LAMBDA_EXPR_REGENERATING_TARGS is not strictly necessary -- in an > earlier version of the patch, I walked LAMBDA_EXPR_EXTRA_SCOPE to build > up this set of outer template arguments on demand, but it's cleaner to > do it this way. (We'd need to walk LAMBDA_EXPR_EXTRA_SCOPE and not > DECL/TYPE_CONTEXT because the latter skips over variable template > scopes.) > > This patch also renames the predicate instantiated_lambda_fn_p to > regenerated_lambda_fn_p, for sake of consistency with the rest of the > patch which uses "regenerated" instead of "instantiated". > > Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on > cmcstl2 and range-v3. Does this look OK for trunk? OK. > gcc/cp/ChangeLog: > > PR c++/99874 > * constraint.cc (get_normalized_constraints_from_decl): Handle > regenerated lambdas. > (satisfy_declaration_constraints): Likewise. Check for > dependent args later. > * cp-tree.h (LAMBDA_EXPR_INSTANTIATED): Replace with ... > (LAMBDA_EXPR_REGENERATED_FROM): ... this. > (LAMBDA_EXPR_REGENERATING_TARGS): New. > (tree_lambda_expr::regenerated_from): New data member. > (tree_lambda_expr::regenerating_targs): New data member. > (add_to_template_args): Declare. > (regenerated_lambda_fn_p): Likewise. > (most_general_lambda): Likewise. > * lambda.c (build_lambda_expr): Set LAMBDA_EXPR_REGENERATED_FROM > and LAMBDA_EXPR_REGENERATING_TARGS. > * pt.c (add_to_template_args): No longer static. > (tsubst_function_decl): Unconditionally propagate constraints on > the substituted function decl. > (instantiated_lambda_fn_p): Rename to ... > (regenerated_lambda_fn_p): ... this. Check > LAMBDA_EXPR_REGENERATED_FROM instead of > LAMBDA_EXPR_INSTANTIATED. > (most_general_lambda): Define. > (enclosing_instantiation_of): Adjust after renaming > instantiated_lambda_fn_p. > (tsubst_lambda_expr): Don't substitute or set constraints on > the regenerated lambda. > > gcc/testsuite/ChangeLog: > > PR c++/99874 > * g++.dg/cpp2a/concepts-lambda16.C: New test. > * g++.dg/cpp2a/concepts-lambda17.C: New test. > --- > gcc/cp/constraint.cc | 43 +++++++++++-- > gcc/cp/cp-tree.h | 20 ++++-- > gcc/cp/lambda.c | 2 + > gcc/cp/pt.c | 42 ++++++------- > .../g++.dg/cpp2a/concepts-lambda16.C | 61 +++++++++++++++++++ > .../g++.dg/cpp2a/concepts-lambda17.C | 14 +++++ > 6 files changed, 150 insertions(+), 32 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C > create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 5cf43bd6c18..bd526f669ab 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -886,6 +886,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false) > it has the correct template information attached. */ > d = strip_inheriting_ctors (d); > > + if (regenerated_lambda_fn_p (d)) > + { > + /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain > + all in-scope template parameters, but the lambda from which it was > + ultimately regenerated does, so use that instead. */ > + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)); > + lambda = most_general_lambda (lambda); > + d = lambda_function (lambda); > + } > + > if (TREE_CODE (d) == TEMPLATE_DECL) > { > tmpl = d; > @@ -3174,13 +3184,27 @@ satisfy_declaration_constraints (tree t, sat_info info) > args = TI_ARGS (ti); > if (inh_ctor_targs) > args = add_outermost_template_args (args, inh_ctor_targs); > + } > > - /* If any arguments depend on template parameters, we can't > - check constraints. Pretend they're satisfied for now. */ > - if (uses_template_parms (args)) > - return boolean_true_node; > + if (regenerated_lambda_fn_p (t)) > + { > + /* The DECL_TI_ARGS of a regenerated lambda contains only the innermost > + set of template arguments. Augment this with the outer template > + arguments that were used to regenerate the lambda. */ > + gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); > + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); > + tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); > + if (args) > + args = add_to_template_args (outer_args, args); > + else > + args = outer_args; > } > > + /* If any arguments depend on template parameters, we can't > + check constraints. Pretend they're satisfied for now. */ > + if (uses_template_parms (args)) > + return boolean_true_node; > + > /* Get the normalized constraints. */ > tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); > > @@ -3227,7 +3251,16 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > > gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); > > - args = add_outermost_template_args (t, args); > + if (regenerated_lambda_fn_p (t)) > + { > + /* As in the two-parameter version of this function. */ > + gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); > + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); > + tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); > + args = add_to_template_args (outer_args, args); > + } > + else > + args = add_outermost_template_args (t, args); > > /* If any arguments depend on template parameters, we can't > check constraints. Pretend they're satisfied for now. */ > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index a5d9d7ac625..bf9d5add0cf 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -490,7 +490,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; > DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE) > CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR) > OVL_NESTED_P (in OVERLOAD) > - LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR) > DECL_MODULE_EXPORT_P (in _DECL) > 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs) > TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, > @@ -1434,10 +1433,6 @@ enum cp_lambda_default_capture_mode_type { > #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \ > TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE)) > > -/* True iff this LAMBDA_EXPR was generated in tsubst_lambda_expr. */ > -#define LAMBDA_EXPR_INSTANTIATED(NODE) \ > - TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE)) > - > /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit > capture. */ > #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \ > @@ -1461,6 +1456,16 @@ enum cp_lambda_default_capture_mode_type { > #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \ > (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies) > > +/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE > + (if NODE was not regenerated via tsubst_lambda_expr). */ > +#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \ > + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from) > + > +/* The full set of template arguments used to regenerate NODE, or NULL_TREE > + (if NODE was not regenerated via tsubst_lambda_expr). */ > +#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \ > + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs) > + > /* The closure type of the lambda, which is also the type of the > LAMBDA_EXPR. */ > #define LAMBDA_EXPR_CLOSURE(NODE) \ > @@ -1472,6 +1477,8 @@ struct GTY (()) tree_lambda_expr > tree capture_list; > tree this_capture; > tree extra_scope; > + tree regenerated_from; > + tree regenerating_targs; > vec<tree, va_gc> *pending_proxies; > location_t locus; > enum cp_lambda_default_capture_mode_type default_capture_mode : 8; > @@ -7247,6 +7254,7 @@ extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec); > extern void add_mergeable_specialization (bool is_decl, bool is_alias, > spec_entry *, > tree outer, unsigned); > +extern tree add_to_template_args (tree, tree); > extern tree add_outermost_template_args (tree, tree); > extern tree add_extra_args (tree, tree); > extern tree build_extra_args (tree, tree, tsubst_flags_t); > @@ -7557,6 +7565,8 @@ extern void record_null_lambda_scope (tree); > extern void finish_lambda_scope (void); > extern tree start_lambda_function (tree fn, tree lambda_expr); > extern void finish_lambda_function (tree body); > +extern bool regenerated_lambda_fn_p (tree); > +extern tree most_general_lambda (tree); > > /* in tree.c */ > extern int cp_tree_operand_length (const_tree); > diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c > index b0fd6ecc57e..c0a5ffb427e 100644 > --- a/gcc/cp/lambda.c > +++ b/gcc/cp/lambda.c > @@ -41,6 +41,8 @@ build_lambda_expr (void) > LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE; > LAMBDA_EXPR_CAPTURE_LIST (lambda) = NULL_TREE; > LAMBDA_EXPR_THIS_CAPTURE (lambda) = NULL_TREE; > + LAMBDA_EXPR_REGENERATED_FROM (lambda) = NULL_TREE; > + LAMBDA_EXPR_REGENERATING_TARGS (lambda) = NULL_TREE; > LAMBDA_EXPR_PENDING_PROXIES (lambda) = NULL; > LAMBDA_EXPR_MUTABLE_P (lambda) = false; > return lambda; > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index a08d08d2834..7917a280804 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -151,7 +151,6 @@ static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t, > static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t, > bool, bool); > static void tsubst_enum (tree, tree, tree); > -static tree add_to_template_args (tree, tree); > static bool check_instantiated_args (tree, tree, tsubst_flags_t); > static int check_non_deducible_conversion (tree, tree, int, int, > struct conversion **, bool); > @@ -553,7 +552,7 @@ maybe_end_member_template_processing (void) > /* Return a new template argument vector which contains all of ARGS, > but has as its innermost set of arguments the EXTRA_ARGS. */ > > -static tree > +tree > add_to_template_args (tree args, tree extra_args) > { > tree new_args; > @@ -14058,10 +14057,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, > don't substitute through the constraints; that's only done when > they are checked. */ > if (tree ci = get_constraints (t)) > - /* Unless we're regenerating a lambda, in which case we'll set the > - lambda's constraints in tsubst_lambda_expr. */ > - if (!lambda_fntype) > - set_constraints (r, ci); > + set_constraints (r, ci); > > if (DECL_FRIEND_CONTEXT (t)) > SET_DECL_FRIEND_CONTEXT (r, > @@ -14347,13 +14343,24 @@ lambda_fn_in_template_p (tree fn) > which the above is true. */ > > bool > -instantiated_lambda_fn_p (tree fn) > +regenerated_lambda_fn_p (tree fn) > { > if (!fn || !LAMBDA_FUNCTION_P (fn)) > return false; > tree closure = DECL_CONTEXT (fn); > tree lam = CLASSTYPE_LAMBDA_EXPR (closure); > - return LAMBDA_EXPR_INSTANTIATED (lam); > + return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE; > +} > + > +/* Return the LAMBDA_EXPR from which T was ultimately regenerated. > + If T is not a regenerated LAMBDA_EXPR, return T. */ > + > +tree > +most_general_lambda (tree t) > +{ > + while (LAMBDA_EXPR_REGENERATED_FROM (t)) > + t = LAMBDA_EXPR_REGENERATED_FROM (t); > + return t; > } > > /* We're instantiating a variable from template function TCTX. Return the > @@ -14369,7 +14376,7 @@ enclosing_instantiation_of (tree otctx) > int lambda_count = 0; > > for (; tctx && (lambda_fn_in_template_p (tctx) > - || instantiated_lambda_fn_p (tctx)); > + || regenerated_lambda_fn_p (tctx)); > tctx = decl_function_context (tctx)) > ++lambda_count; > > @@ -14389,7 +14396,7 @@ enclosing_instantiation_of (tree otctx) > { > tree ofn = fn; > int flambda_count = 0; > - for (; fn && instantiated_lambda_fn_p (fn); > + for (; fn && regenerated_lambda_fn_p (fn); > fn = decl_function_context (fn)) > ++flambda_count; > if ((fn && DECL_TEMPLATE_INFO (fn)) > @@ -19264,7 +19271,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) > LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) > = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); > LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); > - LAMBDA_EXPR_INSTANTIATED (r) = true; > + LAMBDA_EXPR_REGENERATED_FROM (r) = t; > + LAMBDA_EXPR_REGENERATING_TARGS (r) > + = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args); > > gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE > && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); > @@ -19406,17 +19415,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) > finish_member_declaration (fn); > } > > - if (tree ci = get_constraints (oldfn)) > - { > - /* Substitute into the lambda's constraints. */ > - if (oldtmpl) > - ++processing_template_decl; > - ci = tsubst_constraint_info (ci, args, complain, in_decl); > - if (oldtmpl) > - --processing_template_decl; > - set_constraints (fn, ci); > - } > - > /* Let finish_function set this. */ > DECL_DECLARED_CONSTEXPR_P (fn) = false; > > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C > new file mode 100644 > index 00000000000..01fda6efb38 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C > @@ -0,0 +1,61 @@ > +// PR c++/99874 > +// { dg-do compile { target c++20 } } > + > +template <class T> > +struct A { > + static inline auto a = [] <class U> (U) { > + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; > + }; > + > + template <class W> > + static inline auto b = [] <class U> (U) { > + return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { }; > + }; > + > + static auto f() { > + return [] <class U> (U) { > + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; > + }; > + } > + > + template <class W> > + static auto g() { > + return [] <class U> (U) { > + return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { }; > + }; > + } > +}; > + > +template <class T> > +auto a = [] <class U> (U) { > + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; > +}; > + > +template <class T> > +auto b = [] <class U> (U) { > + return [] <class V> (V) { > + return [] { > + return [] () requires requires (T t, U u, V v) { t + u + v; } { }; > + }; > + }; > +}; > + > +int main() { > + A<int>::a(0)(0); > + A<int>::a(0)(nullptr); // { dg-error "no match" } > + > + A<int>::b<int>(0)(0); > + A<int>::b<int>(0)(nullptr); // { dg-error "no match" } > + > + A<int>::f()(0)(0); > + A<int>::f()(0)(nullptr); // { dg-error "no match" } > + > + A<int>::g<int>()(0)(0); > + A<int>::g<int>()(0)(nullptr); // { dg-error "no match" } > + > + a<int>(0)(0); > + a<int>(0)(nullptr); // { dg-error "no match" } > + > + b<int>(0)(0)(); > + b<int>(0)(nullptr)()(); // { dg-error "no match" } > +} > diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C > new file mode 100644 > index 00000000000..32ae1e12174 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C > @@ -0,0 +1,14 @@ > +// { dg-do compile { target c++20 } } > + > +template<class T> > +struct A { static const bool value = T::value; }; > + > +template<class T> > +void f() { > + // Verify we don't substitute into a lambda's constraints when > + // regenerating it, which would lead to a hard error here. > + [] () requires (T::value && A<T>::value) || true { }(); > + [] <class U> (U) requires (U::value && A<T>::value) || true { }(0); > +} > + > +template void f<int>(); >
On Thu, 8 Apr 2021, Jason Merrill wrote: > On 4/7/21 12:10 PM, Patrick Palka wrote: > > We currently substitute through a lambda's constraints whenever we > > regenerate it via tsubst_lambda_expr. This is the wrong approach > > because it can lead to hard errors due to constraints being evaluated > > out of order (as in the testcase concepts-lambda17.C below), and because > > it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS > > mechanism for delaying substitution into requires-expressions, which is > > the cause of this PR. > > > > But in order to avoid substituting through a lambda's constraints during > > regeneration, we we need to be able to get at all in-scope template > > parameters and the corresponding template arguments during constraint > > checking of a lambda's op(). And this information is not easily > > available where we need it, it seems. > > > > To that end, the approach that this patch takes is to add two new fields > > to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM > > (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS. > > The former allows us to obtain the complete set of template parameters > > that are in-scope for a lambda's op(), and the latter gives us all outer > > template arguments that were used to regenerate the lambda. > > I'm a little surprised you didn't use a TEMPLATE_INFO for these, but this way > is fine too. Using a TEMPLATE_INFO here works nicely here, I hadn't considered it. Like so? Tested on x86_64-pc-linux-gnu. -- >8 -- Subject: [PATCH] c++: Use a TEMPLATE_INFO to hold regenerated lambda info A TEMPLATE_INFO is a natural fit for what LAMBDA_EXPR_REGENERATED_FROM and LAMBDA_EXPR_REGENERATING_TARGS hold, so let's use it instead. gcc/cp/ChangeLog: * cp-tree.h (LAMBDA_EXPR_REGENERATED_FROM) (LAMBDA_EXPR_REGENERATING_TARGS): Replace these with ... (LAMBDA_EXPR_REGEN_INFO): ... this. (tree_lambda_expr::regenerated_from) (tree_lambda_expr::regenerating_targs): Replace these with ... (tree_lambda_expr::regen_info): ... this. * constraint.cc (satisfy_declaration_constraints): Adjust accordingly. * lambda.c (build_lambda_expr): Likewise. * pt.c (regenerated_lambda_fn_p): Likewise. (most_general_lambda): Likewise. (tsubst_lambda_expr): Likewise. --- gcc/cp/constraint.cc | 4 ++-- gcc/cp/cp-tree.h | 18 +++++++----------- gcc/cp/lambda.c | 3 +-- gcc/cp/pt.c | 15 +++++++++------ 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 0a9d1bfea0f..0ddb2990dd9 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -3193,7 +3193,7 @@ satisfy_declaration_constraints (tree t, sat_info info) arguments that were used to regenerate the lambda. */ gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); - tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); + tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda)); if (args) args = add_to_template_args (outer_args, args); else @@ -3256,7 +3256,7 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) /* As in the two-parameter version of this function. */ gcc_assert (TMPL_ARGS_DEPTH (args) == 1); tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); - tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); + tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda)); args = add_to_template_args (outer_args, args); } else diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index bf9d5add0cf..e42b82ae5a4 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1456,15 +1456,12 @@ enum cp_lambda_default_capture_mode_type { #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \ (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies) -/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE - (if NODE was not regenerated via tsubst_lambda_expr). */ -#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \ - (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from) - -/* The full set of template arguments used to regenerate NODE, or NULL_TREE - (if NODE was not regenerated via tsubst_lambda_expr). */ -#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \ - (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs) +/* If NODE was regenerated via tsubst_lambda_expr, this is a TEMPLATE_INFO + whose TI_TEMPLATE is the immediate LAMBDA_EXPR from which NODE was + regenerated, and TI_ARGS is the full set of template arguments used + to regenerate NODE from the most general lambda. */ +#define LAMBDA_EXPR_REGEN_INFO(NODE) \ + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regen_info) /* The closure type of the lambda, which is also the type of the LAMBDA_EXPR. */ @@ -1477,8 +1474,7 @@ struct GTY (()) tree_lambda_expr tree capture_list; tree this_capture; tree extra_scope; - tree regenerated_from; - tree regenerating_targs; + tree regen_info; vec<tree, va_gc> *pending_proxies; location_t locus; enum cp_lambda_default_capture_mode_type default_capture_mode : 8; diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index c0a5ffb427e..16e2b4c18b4 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -41,8 +41,7 @@ build_lambda_expr (void) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE; LAMBDA_EXPR_CAPTURE_LIST (lambda) = NULL_TREE; LAMBDA_EXPR_THIS_CAPTURE (lambda) = NULL_TREE; - LAMBDA_EXPR_REGENERATED_FROM (lambda) = NULL_TREE; - LAMBDA_EXPR_REGENERATING_TARGS (lambda) = NULL_TREE; + LAMBDA_EXPR_REGEN_INFO (lambda) = NULL_TREE; LAMBDA_EXPR_PENDING_PROXIES (lambda) = NULL; LAMBDA_EXPR_MUTABLE_P (lambda) = false; return lambda; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index daf1b5aeb32..01c807be1bb 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -14356,7 +14356,7 @@ regenerated_lambda_fn_p (tree fn) return false; tree closure = DECL_CONTEXT (fn); tree lam = CLASSTYPE_LAMBDA_EXPR (closure); - return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE; + return LAMBDA_EXPR_REGEN_INFO (lam) != NULL_TREE; } /* Return the LAMBDA_EXPR from which T was ultimately regenerated. @@ -14365,8 +14365,8 @@ regenerated_lambda_fn_p (tree fn) tree most_general_lambda (tree t) { - while (LAMBDA_EXPR_REGENERATED_FROM (t)) - t = LAMBDA_EXPR_REGENERATED_FROM (t); + while (tree ti = LAMBDA_EXPR_REGEN_INFO (t)) + t = TI_TEMPLATE (ti); return t; } @@ -19278,9 +19278,12 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); - LAMBDA_EXPR_REGENERATED_FROM (r) = t; - LAMBDA_EXPR_REGENERATING_TARGS (r) - = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args); + if (tree ti = LAMBDA_EXPR_REGEN_INFO (t)) + LAMBDA_EXPR_REGEN_INFO (r) + = build_template_info (t, add_to_template_args (TI_ARGS (ti), args)); + else + LAMBDA_EXPR_REGEN_INFO (r) + = build_template_info (t, args); gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); -- 2.31.1.189.g2e36527f23
On 4/8/21 5:01 PM, Patrick Palka wrote: > On Thu, 8 Apr 2021, Jason Merrill wrote: > >> On 4/7/21 12:10 PM, Patrick Palka wrote: >>> We currently substitute through a lambda's constraints whenever we >>> regenerate it via tsubst_lambda_expr. This is the wrong approach >>> because it can lead to hard errors due to constraints being evaluated >>> out of order (as in the testcase concepts-lambda17.C below), and because >>> it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS >>> mechanism for delaying substitution into requires-expressions, which is >>> the cause of this PR. >>> >>> But in order to avoid substituting through a lambda's constraints during >>> regeneration, we we need to be able to get at all in-scope template >>> parameters and the corresponding template arguments during constraint >>> checking of a lambda's op(). And this information is not easily >>> available where we need it, it seems. >>> >>> To that end, the approach that this patch takes is to add two new fields >>> to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM >>> (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS. >>> The former allows us to obtain the complete set of template parameters >>> that are in-scope for a lambda's op(), and the latter gives us all outer >>> template arguments that were used to regenerate the lambda. >> >> I'm a little surprised you didn't use a TEMPLATE_INFO for these, but this way >> is fine too. > > Using a TEMPLATE_INFO here works nicely here, I hadn't considered it. > Like so? Tested on x86_64-pc-linux-gnu. OK. > -- >8 -- > > Subject: [PATCH] c++: Use a TEMPLATE_INFO to hold regenerated lambda info > > A TEMPLATE_INFO is a natural fit for what LAMBDA_EXPR_REGENERATED_FROM > and LAMBDA_EXPR_REGENERATING_TARGS hold, so let's use it instead. > > gcc/cp/ChangeLog: > > * cp-tree.h (LAMBDA_EXPR_REGENERATED_FROM) > (LAMBDA_EXPR_REGENERATING_TARGS): Replace these with ... > (LAMBDA_EXPR_REGEN_INFO): ... this. > (tree_lambda_expr::regenerated_from) > (tree_lambda_expr::regenerating_targs): Replace these with ... > (tree_lambda_expr::regen_info): ... this. > * constraint.cc (satisfy_declaration_constraints): Adjust > accordingly. > * lambda.c (build_lambda_expr): Likewise. > * pt.c (regenerated_lambda_fn_p): Likewise. > (most_general_lambda): Likewise. > (tsubst_lambda_expr): Likewise. > --- > gcc/cp/constraint.cc | 4 ++-- > gcc/cp/cp-tree.h | 18 +++++++----------- > gcc/cp/lambda.c | 3 +-- > gcc/cp/pt.c | 15 +++++++++------ > 4 files changed, 19 insertions(+), 21 deletions(-) > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc > index 0a9d1bfea0f..0ddb2990dd9 100644 > --- a/gcc/cp/constraint.cc > +++ b/gcc/cp/constraint.cc > @@ -3193,7 +3193,7 @@ satisfy_declaration_constraints (tree t, sat_info info) > arguments that were used to regenerate the lambda. */ > gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); > tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); > - tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); > + tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda)); > if (args) > args = add_to_template_args (outer_args, args); > else > @@ -3256,7 +3256,7 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) > /* As in the two-parameter version of this function. */ > gcc_assert (TMPL_ARGS_DEPTH (args) == 1); > tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); > - tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); > + tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda)); > args = add_to_template_args (outer_args, args); > } > else > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index bf9d5add0cf..e42b82ae5a4 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -1456,15 +1456,12 @@ enum cp_lambda_default_capture_mode_type { > #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \ > (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies) > > -/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE > - (if NODE was not regenerated via tsubst_lambda_expr). */ > -#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \ > - (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from) > - > -/* The full set of template arguments used to regenerate NODE, or NULL_TREE > - (if NODE was not regenerated via tsubst_lambda_expr). */ > -#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \ > - (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs) > +/* If NODE was regenerated via tsubst_lambda_expr, this is a TEMPLATE_INFO > + whose TI_TEMPLATE is the immediate LAMBDA_EXPR from which NODE was > + regenerated, and TI_ARGS is the full set of template arguments used > + to regenerate NODE from the most general lambda. */ > +#define LAMBDA_EXPR_REGEN_INFO(NODE) \ > + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regen_info) > > /* The closure type of the lambda, which is also the type of the > LAMBDA_EXPR. */ > @@ -1477,8 +1474,7 @@ struct GTY (()) tree_lambda_expr > tree capture_list; > tree this_capture; > tree extra_scope; > - tree regenerated_from; > - tree regenerating_targs; > + tree regen_info; > vec<tree, va_gc> *pending_proxies; > location_t locus; > enum cp_lambda_default_capture_mode_type default_capture_mode : 8; > diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c > index c0a5ffb427e..16e2b4c18b4 100644 > --- a/gcc/cp/lambda.c > +++ b/gcc/cp/lambda.c > @@ -41,8 +41,7 @@ build_lambda_expr (void) > LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE; > LAMBDA_EXPR_CAPTURE_LIST (lambda) = NULL_TREE; > LAMBDA_EXPR_THIS_CAPTURE (lambda) = NULL_TREE; > - LAMBDA_EXPR_REGENERATED_FROM (lambda) = NULL_TREE; > - LAMBDA_EXPR_REGENERATING_TARGS (lambda) = NULL_TREE; > + LAMBDA_EXPR_REGEN_INFO (lambda) = NULL_TREE; > LAMBDA_EXPR_PENDING_PROXIES (lambda) = NULL; > LAMBDA_EXPR_MUTABLE_P (lambda) = false; > return lambda; > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > index daf1b5aeb32..01c807be1bb 100644 > --- a/gcc/cp/pt.c > +++ b/gcc/cp/pt.c > @@ -14356,7 +14356,7 @@ regenerated_lambda_fn_p (tree fn) > return false; > tree closure = DECL_CONTEXT (fn); > tree lam = CLASSTYPE_LAMBDA_EXPR (closure); > - return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE; > + return LAMBDA_EXPR_REGEN_INFO (lam) != NULL_TREE; > } > > /* Return the LAMBDA_EXPR from which T was ultimately regenerated. > @@ -14365,8 +14365,8 @@ regenerated_lambda_fn_p (tree fn) > tree > most_general_lambda (tree t) > { > - while (LAMBDA_EXPR_REGENERATED_FROM (t)) > - t = LAMBDA_EXPR_REGENERATED_FROM (t); > + while (tree ti = LAMBDA_EXPR_REGEN_INFO (t)) > + t = TI_TEMPLATE (ti); > return t; > } > > @@ -19278,9 +19278,12 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) > LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) > = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); > LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); > - LAMBDA_EXPR_REGENERATED_FROM (r) = t; > - LAMBDA_EXPR_REGENERATING_TARGS (r) > - = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args); > + if (tree ti = LAMBDA_EXPR_REGEN_INFO (t)) > + LAMBDA_EXPR_REGEN_INFO (r) > + = build_template_info (t, add_to_template_args (TI_ARGS (ti), args)); > + else > + LAMBDA_EXPR_REGEN_INFO (r) > + = build_template_info (t, args); > > gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE > && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); >
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 5cf43bd6c18..bd526f669ab 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -886,6 +886,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false) it has the correct template information attached. */ d = strip_inheriting_ctors (d); + if (regenerated_lambda_fn_p (d)) + { + /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain + all in-scope template parameters, but the lambda from which it was + ultimately regenerated does, so use that instead. */ + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d)); + lambda = most_general_lambda (lambda); + d = lambda_function (lambda); + } + if (TREE_CODE (d) == TEMPLATE_DECL) { tmpl = d; @@ -3174,13 +3184,27 @@ satisfy_declaration_constraints (tree t, sat_info info) args = TI_ARGS (ti); if (inh_ctor_targs) args = add_outermost_template_args (args, inh_ctor_targs); + } - /* If any arguments depend on template parameters, we can't - check constraints. Pretend they're satisfied for now. */ - if (uses_template_parms (args)) - return boolean_true_node; + if (regenerated_lambda_fn_p (t)) + { + /* The DECL_TI_ARGS of a regenerated lambda contains only the innermost + set of template arguments. Augment this with the outer template + arguments that were used to regenerate the lambda. */ + gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); + tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); + if (args) + args = add_to_template_args (outer_args, args); + else + args = outer_args; } + /* If any arguments depend on template parameters, we can't + check constraints. Pretend they're satisfied for now. */ + if (uses_template_parms (args)) + return boolean_true_node; + /* Get the normalized constraints. */ tree norm = get_normalized_constraints_from_decl (t, info.noisy ()); @@ -3227,7 +3251,16 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) gcc_assert (TREE_CODE (t) == TEMPLATE_DECL); - args = add_outermost_template_args (t, args); + if (regenerated_lambda_fn_p (t)) + { + /* As in the two-parameter version of this function. */ + gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1); + tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t)); + tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda); + args = add_to_template_args (outer_args, args); + } + else + args = add_outermost_template_args (t, args); /* If any arguments depend on template parameters, we can't check constraints. Pretend they're satisfied for now. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index a5d9d7ac625..bf9d5add0cf 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -490,7 +490,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE) CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR) OVL_NESTED_P (in OVERLOAD) - LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR) DECL_MODULE_EXPORT_P (in _DECL) 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs) TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, @@ -1434,10 +1433,6 @@ enum cp_lambda_default_capture_mode_type { #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \ TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE)) -/* True iff this LAMBDA_EXPR was generated in tsubst_lambda_expr. */ -#define LAMBDA_EXPR_INSTANTIATED(NODE) \ - TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE)) - /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit capture. */ #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \ @@ -1461,6 +1456,16 @@ enum cp_lambda_default_capture_mode_type { #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \ (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies) +/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE + (if NODE was not regenerated via tsubst_lambda_expr). */ +#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \ + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from) + +/* The full set of template arguments used to regenerate NODE, or NULL_TREE + (if NODE was not regenerated via tsubst_lambda_expr). */ +#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \ + (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs) + /* The closure type of the lambda, which is also the type of the LAMBDA_EXPR. */ #define LAMBDA_EXPR_CLOSURE(NODE) \ @@ -1472,6 +1477,8 @@ struct GTY (()) tree_lambda_expr tree capture_list; tree this_capture; tree extra_scope; + tree regenerated_from; + tree regenerating_targs; vec<tree, va_gc> *pending_proxies; location_t locus; enum cp_lambda_default_capture_mode_type default_capture_mode : 8; @@ -7247,6 +7254,7 @@ extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec); extern void add_mergeable_specialization (bool is_decl, bool is_alias, spec_entry *, tree outer, unsigned); +extern tree add_to_template_args (tree, tree); extern tree add_outermost_template_args (tree, tree); extern tree add_extra_args (tree, tree); extern tree build_extra_args (tree, tree, tsubst_flags_t); @@ -7557,6 +7565,8 @@ extern void record_null_lambda_scope (tree); extern void finish_lambda_scope (void); extern tree start_lambda_function (tree fn, tree lambda_expr); extern void finish_lambda_function (tree body); +extern bool regenerated_lambda_fn_p (tree); +extern tree most_general_lambda (tree); /* in tree.c */ extern int cp_tree_operand_length (const_tree); diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index b0fd6ecc57e..c0a5ffb427e 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -41,6 +41,8 @@ build_lambda_expr (void) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE; LAMBDA_EXPR_CAPTURE_LIST (lambda) = NULL_TREE; LAMBDA_EXPR_THIS_CAPTURE (lambda) = NULL_TREE; + LAMBDA_EXPR_REGENERATED_FROM (lambda) = NULL_TREE; + LAMBDA_EXPR_REGENERATING_TARGS (lambda) = NULL_TREE; LAMBDA_EXPR_PENDING_PROXIES (lambda) = NULL; LAMBDA_EXPR_MUTABLE_P (lambda) = false; return lambda; diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index a08d08d2834..7917a280804 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -151,7 +151,6 @@ static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t, static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t, bool, bool); static void tsubst_enum (tree, tree, tree); -static tree add_to_template_args (tree, tree); static bool check_instantiated_args (tree, tree, tsubst_flags_t); static int check_non_deducible_conversion (tree, tree, int, int, struct conversion **, bool); @@ -553,7 +552,7 @@ maybe_end_member_template_processing (void) /* Return a new template argument vector which contains all of ARGS, but has as its innermost set of arguments the EXTRA_ARGS. */ -static tree +tree add_to_template_args (tree args, tree extra_args) { tree new_args; @@ -14058,10 +14057,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, don't substitute through the constraints; that's only done when they are checked. */ if (tree ci = get_constraints (t)) - /* Unless we're regenerating a lambda, in which case we'll set the - lambda's constraints in tsubst_lambda_expr. */ - if (!lambda_fntype) - set_constraints (r, ci); + set_constraints (r, ci); if (DECL_FRIEND_CONTEXT (t)) SET_DECL_FRIEND_CONTEXT (r, @@ -14347,13 +14343,24 @@ lambda_fn_in_template_p (tree fn) which the above is true. */ bool -instantiated_lambda_fn_p (tree fn) +regenerated_lambda_fn_p (tree fn) { if (!fn || !LAMBDA_FUNCTION_P (fn)) return false; tree closure = DECL_CONTEXT (fn); tree lam = CLASSTYPE_LAMBDA_EXPR (closure); - return LAMBDA_EXPR_INSTANTIATED (lam); + return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE; +} + +/* Return the LAMBDA_EXPR from which T was ultimately regenerated. + If T is not a regenerated LAMBDA_EXPR, return T. */ + +tree +most_general_lambda (tree t) +{ + while (LAMBDA_EXPR_REGENERATED_FROM (t)) + t = LAMBDA_EXPR_REGENERATED_FROM (t); + return t; } /* We're instantiating a variable from template function TCTX. Return the @@ -14369,7 +14376,7 @@ enclosing_instantiation_of (tree otctx) int lambda_count = 0; for (; tctx && (lambda_fn_in_template_p (tctx) - || instantiated_lambda_fn_p (tctx)); + || regenerated_lambda_fn_p (tctx)); tctx = decl_function_context (tctx)) ++lambda_count; @@ -14389,7 +14396,7 @@ enclosing_instantiation_of (tree otctx) { tree ofn = fn; int flambda_count = 0; - for (; fn && instantiated_lambda_fn_p (fn); + for (; fn && regenerated_lambda_fn_p (fn); fn = decl_function_context (fn)) ++flambda_count; if ((fn && DECL_TEMPLATE_INFO (fn)) @@ -19264,7 +19271,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r) = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t); LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t); - LAMBDA_EXPR_INSTANTIATED (r) = true; + LAMBDA_EXPR_REGENERATED_FROM (r) = t; + LAMBDA_EXPR_REGENERATING_TARGS (r) + = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args); gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL); @@ -19406,17 +19415,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) finish_member_declaration (fn); } - if (tree ci = get_constraints (oldfn)) - { - /* Substitute into the lambda's constraints. */ - if (oldtmpl) - ++processing_template_decl; - ci = tsubst_constraint_info (ci, args, complain, in_decl); - if (oldtmpl) - --processing_template_decl; - set_constraints (fn, ci); - } - /* Let finish_function set this. */ DECL_DECLARED_CONSTEXPR_P (fn) = false; diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C new file mode 100644 index 00000000000..01fda6efb38 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C @@ -0,0 +1,61 @@ +// PR c++/99874 +// { dg-do compile { target c++20 } } + +template <class T> +struct A { + static inline auto a = [] <class U> (U) { + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; + }; + + template <class W> + static inline auto b = [] <class U> (U) { + return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { }; + }; + + static auto f() { + return [] <class U> (U) { + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; + }; + } + + template <class W> + static auto g() { + return [] <class U> (U) { + return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { }; + }; + } +}; + +template <class T> +auto a = [] <class U> (U) { + return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { }; +}; + +template <class T> +auto b = [] <class U> (U) { + return [] <class V> (V) { + return [] { + return [] () requires requires (T t, U u, V v) { t + u + v; } { }; + }; + }; +}; + +int main() { + A<int>::a(0)(0); + A<int>::a(0)(nullptr); // { dg-error "no match" } + + A<int>::b<int>(0)(0); + A<int>::b<int>(0)(nullptr); // { dg-error "no match" } + + A<int>::f()(0)(0); + A<int>::f()(0)(nullptr); // { dg-error "no match" } + + A<int>::g<int>()(0)(0); + A<int>::g<int>()(0)(nullptr); // { dg-error "no match" } + + a<int>(0)(0); + a<int>(0)(nullptr); // { dg-error "no match" } + + b<int>(0)(0)(); + b<int>(0)(nullptr)()(); // { dg-error "no match" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C new file mode 100644 index 00000000000..32ae1e12174 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++20 } } + +template<class T> +struct A { static const bool value = T::value; }; + +template<class T> +void f() { + // Verify we don't substitute into a lambda's constraints when + // regenerating it, which would lead to a hard error here. + [] () requires (T::value && A<T>::value) || true { }(); + [] <class U> (U) requires (U::value && A<T>::value) || true { }(0); +} + +template void f<int>();