8sa1-gcc/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
Patrick Palka 123b3e03c9 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.
2021-04-08 13:07:43 -04:00

62 lines
1.4 KiB
C

// 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" }
}