diff options
author | Patrick Palka <ppalka@redhat.com> | 2020-09-17 09:16:02 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2020-09-17 09:16:02 -0400 |
commit | b28b621ac67beee81a47adb50b954bcf751570fd (patch) | |
tree | 8fa20ad6c88d0c2bf9f95239a059df843e92bad2 /gcc | |
parent | 9fcedcc39153cb3cfa08ebab20aef6cdfb9ed609 (diff) | |
download | gcc-b28b621ac67beee81a47adb50b954bcf751570fd.zip gcc-b28b621ac67beee81a47adb50b954bcf751570fd.tar.gz gcc-b28b621ac67beee81a47adb50b954bcf751570fd.tar.bz2 |
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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/constraint.cc | 21 | ||||
-rw-r--r-- | gcc/cp/cp-tree.def | 8 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 17 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C | 18 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C | 25 |
5 files changed, 80 insertions, 9 deletions
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index ad9d470..0aab307 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -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); diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 31be2cf..6eabe0d6 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -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) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 5b72734..08976d8 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -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. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C new file mode 100644 index 0000000..d4bed30 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda13.C @@ -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); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C new file mode 100644 index 0000000..bdc893d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda14.C @@ -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" } |