c++: Don't substitute into constraints on lambdas [PR99874]
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 need to be able to get at all in-scope template parameters and corresponding template arguments during constraint checking of a lambda's op(). And this information is not easily available when 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 (analogous to the TI_TEMPLATE and TI_ARGS of a TEMPLATE_INFO, respectively). LAMBDA_EXPR_REGENERATING_TARGS is not strictly necessary -- in an earlier prototype, I walked LAMBDA_EXPR_EXTRA_SCOPE to build up this set of outer template arguments on demand, but it seems 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". 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 set LAMBDA_EXPR_INSTANTIATED. Set LAMBDA_EXPR_REGENERATED_FROM and LAMBDA_EXPR_REGENERATING_TARGS. 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.
This commit is contained in:
parent
05708d6eef
commit
123b3e03c9
@ -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 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 (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. */
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
42
gcc/cp/pt.c
42
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;
|
||||
@ -14065,10 +14064,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,
|
||||
@ -14354,13 +14350,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
|
||||
@ -14376,7 +14383,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;
|
||||
|
||||
@ -14396,7 +14403,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))
|
||||
@ -19271,7 +19278,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);
|
||||
@ -19413,17 +19422,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;
|
||||
|
||||
|
||||
61
gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
Normal file
61
gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
Normal file
@ -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" }
|
||||
}
|
||||
14
gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
Normal file
14
gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
Normal file
@ -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>();
|
||||
Loading…
Reference in New Issue
Block a user