c++: requires-expressions and partial instantiation [PR96410]
This patch makes tsubst_requires_expr avoid substituting into a requires-expression when partially instantiating a generic lambda. This is necessary in general to ensure that we always check requirements in lexical order (as in the first testcase below). A mechanism similar to PACK_EXPANSION_EXTRA_ARGS is added to remember template arguments and defer substitution of requires-expressions. Incidentally, this change also fixes the two mentioned PRs -- the problem there is that tsubst_requires_expr was performing semantic checks on template trees, and some of the checks are not prepared to handle such trees. With this patch, tsubst_requires_expr no longer does any semantic checking at all when processing_template_decl. gcc/cp/ChangeLog: PR c++/96409 PR c++/96410 * constraint.cc (tsubst_requires_expr): Use REQUIRES_EXPR_PARMS and REQUIRES_EXPR_REQS. Use REQUIRES_EXPR_EXTRA_ARGS, add_extra_args and build_extra_args to defer substitution until we have all the template arguments. (finish_requires_expr): Adjust the call to build_min so that REQUIRES_EXPR_EXTRA_ARGS gets set to NULL_TREE. * cp-tree.def (REQUIRES_EXPR): Give it a third operand. * cp-tree.h (REQUIRES_EXPR_PARMS, REQUIRES_EXPR_REQS, REQUIRES_EXPR_EXTRA_ARGS): Define. (add_extra_args, build_extra_args): Declare. gcc/testsuite/ChangeLog: PR c++/96409 PR c++/96410 * g++.dg/cpp2a/concepts-lambda13.C: New test. * g++.dg/cpp2a/concepts-lambda14.C: New test.
This commit is contained in:
parent
9fcedcc391
commit
b28b621ac6
5 changed files with 80 additions and 9 deletions
|
@ -2175,7 +2175,19 @@ tsubst_requires_expr (tree t, tree args,
|
|||
/* A requires-expression is an unevaluated context. */
|
||||
cp_unevaluated u;
|
||||
|
||||
tree parms = TREE_OPERAND (t, 0);
|
||||
args = add_extra_args (REQUIRES_EXPR_EXTRA_ARGS (t), args);
|
||||
if (processing_template_decl)
|
||||
{
|
||||
/* We're partially instantiating a generic lambda. Substituting into
|
||||
this requires-expression now may cause its requirements to get
|
||||
checked out of order, so instead just remember the template
|
||||
arguments and wait until we can substitute them all at once. */
|
||||
t = copy_node (t);
|
||||
REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain);
|
||||
return t;
|
||||
}
|
||||
|
||||
tree parms = REQUIRES_EXPR_PARMS (t);
|
||||
if (parms)
|
||||
{
|
||||
parms = tsubst_constraint_variables (parms, args, info);
|
||||
|
@ -2183,14 +2195,11 @@ tsubst_requires_expr (tree t, tree args,
|
|||
return boolean_false_node;
|
||||
}
|
||||
|
||||
tree reqs = TREE_OPERAND (t, 1);
|
||||
tree reqs = REQUIRES_EXPR_REQS (t);
|
||||
reqs = tsubst_requirement_body (reqs, args, info);
|
||||
if (reqs == error_mark_node)
|
||||
return boolean_false_node;
|
||||
|
||||
if (processing_template_decl)
|
||||
return finish_requires_expr (cp_expr_location (t), parms, reqs);
|
||||
|
||||
return boolean_true_node;
|
||||
}
|
||||
|
||||
|
@ -2933,7 +2942,7 @@ finish_requires_expr (location_t loc, tree parms, tree reqs)
|
|||
}
|
||||
|
||||
/* Build the node. */
|
||||
tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs);
|
||||
tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs, NULL_TREE);
|
||||
TREE_SIDE_EFFECTS (r) = false;
|
||||
TREE_CONSTANT (r) = true;
|
||||
SET_EXPR_LOCATION (r, loc);
|
||||
|
|
|
@ -524,11 +524,13 @@ DEFTREECODE (CONSTRAINT_INFO, "constraint_info", tcc_exceptional, 0)
|
|||
of the wildcard. */
|
||||
DEFTREECODE (WILDCARD_DECL, "wildcard_decl", tcc_declaration, 0)
|
||||
|
||||
/* A requires-expr is a binary expression. The first operand is
|
||||
/* A requires-expr has three operands. The first operand is
|
||||
its parameter list (possibly NULL). The second is a list of
|
||||
requirements, which are denoted by the _REQ* tree codes
|
||||
below. */
|
||||
DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_expression, 2)
|
||||
below. The third is a TREE_VEC of template arguments to
|
||||
be applied when substituting into the parameter list and
|
||||
requirements, set by tsubst_requires_expr for partial instantiations. */
|
||||
DEFTREECODE (REQUIRES_EXPR, "requires_expr", tcc_expression, 3)
|
||||
|
||||
/* A requirement for an expression. */
|
||||
DEFTREECODE (SIMPLE_REQ, "simple_req", tcc_expression, 1)
|
||||
|
|
|
@ -1618,6 +1618,21 @@ check_constraint_info (tree t)
|
|||
#define CONSTRAINED_PARM_PROTOTYPE(NODE) \
|
||||
DECL_INITIAL (TYPE_DECL_CHECK (NODE))
|
||||
|
||||
/* The list of local parameters introduced by this requires-expression,
|
||||
in the form of a chain of PARM_DECLs. */
|
||||
#define REQUIRES_EXPR_PARMS(NODE) \
|
||||
TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 0)
|
||||
|
||||
/* A TREE_LIST of the requirements for this requires-expression.
|
||||
The requirements are stored in lexical order within the TREE_VALUE
|
||||
of each TREE_LIST node. The TREE_PURPOSE of each node is unused. */
|
||||
#define REQUIRES_EXPR_REQS(NODE) \
|
||||
TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 1)
|
||||
|
||||
/* Like PACK_EXPANSION_EXTRA_ARGS, for requires-expressions. */
|
||||
#define REQUIRES_EXPR_EXTRA_ARGS(NODE) \
|
||||
TREE_OPERAND (TREE_CHECK (NODE, REQUIRES_EXPR), 2)
|
||||
|
||||
enum cp_tree_node_structure_enum {
|
||||
TS_CP_GENERIC,
|
||||
TS_CP_IDENTIFIER,
|
||||
|
@ -7013,6 +7028,8 @@ extern bool template_guide_p (const_tree);
|
|||
extern bool builtin_guide_p (const_tree);
|
||||
extern void store_explicit_specifier (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);
|
||||
|
||||
/* in rtti.c */
|
||||
/* A vector of all tinfo decls that haven't been emitted yet. */
|
||||
|
|
18
gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
Normal file
18
gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C
Normal file
|
@ -0,0 +1,18 @@
|
|||
// { dg-do compile { target c++20 } }
|
||||
|
||||
template<typename T>
|
||||
struct S {
|
||||
using type = T::type; // { dg-bogus "" }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
auto f() {
|
||||
return [] <typename U> (U) {
|
||||
// Verify that partial instantiation of this generic lambda doesn't cause
|
||||
// these requirements to get checked out of order.
|
||||
static_assert(!requires { typename U::type; typename S<T>::type; });
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
int a = f<int>()(0);
|
25
gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
Normal file
25
gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C
Normal file
|
@ -0,0 +1,25 @@
|
|||
// PR c++/96410
|
||||
// { dg-do compile { target c++20 } }
|
||||
|
||||
struct S { using blah = void; };
|
||||
|
||||
template <typename T> constexpr bool trait = !__is_same(T, S);
|
||||
template <typename T> concept C = trait<T>;
|
||||
|
||||
template<typename T>
|
||||
void foo() noexcept(!__is_same(T, void)) { }
|
||||
|
||||
template<typename U>
|
||||
auto f() {
|
||||
return []<typename T>(T, bool a = requires { C<T>; }){
|
||||
static_assert(requires { requires C<U> && (C<T> || C<T>); }); // { dg-error "assert" }
|
||||
static_assert(requires { C<T>; });
|
||||
static_assert(requires { { foo<T>() } noexcept -> C; });
|
||||
static_assert(!requires { typename T::blah; }); // { dg-error "assert" }
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
auto g = f<int>(); // { dg-bogus "" }
|
||||
int n = g(0); // { dg-bogus "" }
|
||||
int m = g(S{}); // { dg-message "required from here" }
|
Loading…
Add table
Reference in a new issue