This PR reports that the requires-expression in auto f = [] { }; static_assert(requires { f(); });
erroneously evaluates to false. The way we end up evaluating to false goes as follows. During the parsing of the requires-expression, we call finish_call_expr from cp_parser_requires_expression with the arguments fn = VIEW_CONVERT_EXPR<struct ._anon_0>(f); args = {} which does the full processing of the call (since we're not inside a template) and returns <lambda()>::operator() (&f); Later, when evaluating the requires-expression, we call finish_call_expr again, this time from tsubst_expr from satisfy_atom, with the arguments fn = operator() args = { &f } (which, as expected, correspond to the CALL_EXPR returned by finish_call_expr the first time). But this time, finish_call_expr returns error_mark_node because it doesn't expect to see an explicit 'this' argument in the args array, treating it instead as a user-written argument which causes the only candidate function to be discarded. This causes the requires-expression to evaluate to false. In short, it seems finish_call_expr is not idempotent on certain inputs when !processing_template_decl. Assuming this idempotency issue is not specific to finish_call_expr, it seems that the safest thing to do is to avoid doing full semantic processing twice when parsing and evaluating a requires-expression that lives outside of a template definition. This patch achieves this by temporarily setting processing_template_decl to non-zero when parsing the body of a requires-expression. This way, full semantic processing will always be done only during evaluation and not during parsing. Testing is in progress. Does this look like the right solution? gcc/cp/ChangeLog: PR c++/94252 * parser.c (cp_parser_requires_expression): Always parse the requirement body as if we're processing a template, by temporarily setting processing_template_decl to non-zero. * semantics.c (finish_static_assert): Also explain an assertion failure when the condition is a REQUIRES_EXPR. gcc/testsuite/ChangeLog: PR c++/94252 * g++.dg/concepts/diagnostic7.C: New test. * g++.dg/concepts/pr94252.C: New test. --- gcc/cp/parser.c | 7 ++++++- gcc/cp/semantics.c | 6 ++++-- gcc/testsuite/g++.dg/concepts/diagnostic7.C | 12 ++++++++++++ gcc/testsuite/g++.dg/concepts/pr94252.C | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic7.C create mode 100644 gcc/testsuite/g++.dg/concepts/pr94252.C diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 03ecd7838f6..14a22b85318 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -27689,7 +27689,12 @@ cp_parser_requires_expression (cp_parser *parser) else parms = NULL_TREE; - /* Parse the requirement body. */ + /* Always parse the requirement body as if we're inside a template so that + we always do full semantic processing only during evaluation of this + requires-expression and not also now during parsing. */ + processing_template_decl_sentinel ptds; + if (!processing_template_decl) + processing_template_decl = 1; reqs = cp_parser_requirement_body (parser); if (reqs == error_mark_node) return error_mark_node; diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index bcb2e72fbb5..e998b373af4 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -9691,8 +9691,10 @@ finish_static_assert (tree condition, tree message, location_t location, error ("static assertion failed: %s", TREE_STRING_POINTER (message)); - /* Actually explain the failure if this is a concept check. */ - if (concept_check_p (orig_condition)) + /* Actually explain the failure if this is a concept check or a + requires-expression. */ + if (concept_check_p (orig_condition) + || TREE_CODE (orig_condition) == REQUIRES_EXPR) diagnose_constraints (location, orig_condition, NULL_TREE); } else if (condition && condition != error_mark_node) diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic7.C b/gcc/testsuite/g++.dg/concepts/diagnostic7.C new file mode 100644 index 00000000000..f78e9bb8240 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic7.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++2a } } + +template<typename A, typename B> + concept same_as = __is_same(A, B); // { dg-message ".void. is not the same as .int." } + +void f(); + +static_assert(requires { { f() } noexcept -> same_as<int>; }); +// { dg-error "static assertion failed" "" { target *-*-* } .-1 } +// { dg-message "not .noexcept." "" { target *-*-* } .-2 } +// { dg-message "return-type-requirement" "" { target *-*-* } .-3 } +// { dg-error "does not satisfy placeholder constraints" "" { target *-*-* } .-4 } diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C b/gcc/testsuite/g++.dg/concepts/pr94252.C new file mode 100644 index 00000000000..7b93997363a --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/pr94252.C @@ -0,0 +1,14 @@ +// PR c++/94252 +// { dg-do compile { target c++2a } } + +auto f = []{ return 0; }; +static_assert(requires { f(); }); + +template<typename A, typename B> + concept same_as = __is_same(A, B); + +struct S { int f(int) noexcept; }; +static_assert(requires(S o, int i) { + o.f(i); + { o.f(i) } noexcept -> same_as<int>; +}); -- 2.26.0.rc1.11.g30e9940356