From a7ea3d2ced786c4544fa625f34f515d89ed074fe Mon Sep 17 00:00:00 2001 From: Patrick Palka Date: Sat, 28 Mar 2020 08:56:59 -0400 Subject: c++: requires-expression outside of a template is misevaluated [PR94252] This PR shows that a REQUIRES_EXPR outside of a template can sometimes be misevaluated. This happens because the evaluation routine tsubst_requires_expr (and diagnose_requires_expr) assumes the REQUIRES_EXPR's subtrees are templated trees and that therefore it's safe to call tsubst_expr on them. But this assumption isn't valid when we've parsed a REQUIRES_EXPR outside of a template context. In order to make this assumption valid here, this patch sets processing_template_decl to non-zero before parsing the body of a REQUIRES_EXPR so that its subtrees are indeed always templated trees. gcc/cp/ChangeLog: PR c++/94252 * constraint.cc (tsubst_compound_requirement): Always suppress errors from type_deducible_p and expression_convertible_p, as they're not substitution errors. (diagnose_atomic_constraint) : Remove this case so that we diagnose INTEGER_CST expressions of non-bool type via the default case. * cp-gimplify.c (cp_genericize_r) : New case. * parser.c (cp_parser_requires_expression): Always parse the requirement body as if we're processing a template, by temporarily incrementing processing_template_decl. Afterwards, if we're not actually in a template context, perform semantic processing to diagnose any invalid types and expressions. * pt.c (tsubst_copy_and_build) : Remove dead code. * semantics.c (finish_static_assert): Explain an assertion failure when the condition is a REQUIRES_EXPR like we do when it is a concept check. gcc/testsuite/ChangeLog: PR c++/94252 * g++.dg/concepts/diagnostic7.C: New test. * g++.dg/concepts/pr94252.C: New test. * g++.dg/cpp2a/concepts-requires18.C: Adjust to expect an additional diagnostic. --- gcc/cp/ChangeLog | 18 ++++++++++++++++ gcc/cp/constraint.cc | 10 ++++----- gcc/cp/cp-gimplify.c | 7 ++++++ gcc/cp/parser.c | 14 +++++++++++- gcc/cp/pt.c | 2 -- gcc/cp/semantics.c | 6 ++++-- gcc/testsuite/ChangeLog | 6 ++++++ gcc/testsuite/g++.dg/concepts/diagnostic7.C | 11 ++++++++++ gcc/testsuite/g++.dg/concepts/pr94252.C | 27 ++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C | 2 +- 10 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic7.C create mode 100644 gcc/testsuite/g++.dg/concepts/pr94252.C (limited to 'gcc') diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index be67685..a197c7d 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,23 @@ 2020-03-28 Patrick Palka + PR c++/94252 + * constraint.cc (tsubst_compound_requirement): Always suppress errors + from type_deducible_p and expression_convertible_p, as they're not + substitution errors. + (diagnose_atomic_constraint) : Remove this case so + that we diagnose INTEGER_CST expressions of non-bool type via the + default case. + * cp-gimplify.c (cp_genericize_r) : New case. + * parser.c (cp_parser_requires_expression): Always parse the requirement + body as if we're processing a template, by temporarily incrementing + processing_template_decl. Afterwards, if we're not actually in a + template context, perform semantic processing to diagnose any invalid + types and expressions. + * pt.c (tsubst_copy_and_build) : Remove dead code. + * semantics.c (finish_static_assert): Explain an assertion failure + when the condition is a REQUIRES_EXPR like we do when it is a concept + check. + * constraint.cc (diagnose_compound_requirement): When diagnosing a compound requirement, maybe replay the satisfaction failure, subject to the current diagnosis depth. diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 571c7cb..9c21ce8 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1981,15 +1981,17 @@ tsubst_compound_requirement (tree t, tree args, subst_info info) if (type == error_mark_node) return error_mark_node; + subst_info quiet (tf_none, info.in_decl); + /* Check expression against the result type. */ if (type) { if (tree placeholder = type_uses_auto (type)) { - if (!type_deducible_p (expr, type, placeholder, args, info)) + if (!type_deducible_p (expr, type, placeholder, args, quiet)) return error_mark_node; } - else if (!expression_convertible_p (expr, type, info)) + else if (!expression_convertible_p (expr, type, quiet)) return error_mark_node; } @@ -3443,10 +3445,6 @@ diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info) case REQUIRES_EXPR: diagnose_requires_expr (expr, map, info.in_decl); break; - case INTEGER_CST: - /* This must be either 0 or false. */ - inform (loc, "%qE is never satisfied", expr); - break; default: tree a = copy_node (t); ATOMIC_CONSTR_MAP (a) = map; diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c index d003f5b..3999695 100644 --- a/gcc/cp/cp-gimplify.c +++ b/gcc/cp/cp-gimplify.c @@ -1747,6 +1747,13 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data) TARGET_EXPR_NO_ELIDE (stmt) = 1; break; + case REQUIRES_EXPR: + /* Emit the value of the requires-expression. */ + *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt), + boolean_type_node); + *walk_subtrees = 0; + break; + case TEMPLATE_ID_EXPR: gcc_assert (concept_check_p (stmt)); /* Emit the value of the concept check. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 3ca8eb9..bf7387d 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -27762,7 +27762,9 @@ cp_parser_requires_expression (cp_parser *parser) parms = NULL_TREE; /* Parse the requirement body. */ + ++processing_template_decl; reqs = cp_parser_requirement_body (parser); + --processing_template_decl; if (reqs == error_mark_node) return error_mark_node; } @@ -27771,7 +27773,17 @@ cp_parser_requires_expression (cp_parser *parser) the parm chain. */ grokparms (parms, &parms); loc = make_location (loc, loc, parser->lexer); - return finish_requires_expr (loc, parms, reqs); + tree expr = finish_requires_expr (loc, parms, reqs); + if (!processing_template_decl) + { + /* Perform semantic processing now to diagnose any invalid types and + expressions. */ + int saved_errorcount = errorcount; + tsubst_requires_expr (expr, NULL_TREE, tf_warning_or_error, NULL_TREE); + if (errorcount > saved_errorcount) + return error_mark_node; + } + return expr; } /* Parse a parameterized requirement. diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 15b5439..bd30c96 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -20319,8 +20319,6 @@ tsubst_copy_and_build (tree t, case REQUIRES_EXPR: { tree r = tsubst_requires_expr (t, args, tf_none, in_decl); - if (r == error_mark_node && (complain & tf_error)) - tsubst_requires_expr (t, args, complain, in_decl); RETURN (r); } diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 2721a55..38637bd 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -9687,8 +9687,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/ChangeLog b/gcc/testsuite/ChangeLog index e2e00f4..8c8eee0 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,11 @@ 2020-03-28 Patrick Palka + PR c++/94252 + * g++.dg/concepts/diagnostic7.C: New test. + * g++.dg/concepts/pr94252.C: New test. + * g++.dg/cpp2a/concepts-requires18.C: Adjust to expect an additional + diagnostic. + * g++.dg/concepts/diagnostic1.C: Pass -fconcepts-diagnostics-depth=2. * g++.dg/concepts/diagnostic5.C: Adjust expected diagnostics. * g++.dg/cpp2a/concepts-iconv1.C: Pass -fconcepts-diagnostics-depth=2. diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic7.C b/gcc/testsuite/g++.dg/concepts/diagnostic7.C new file mode 100644 index 0000000..3761b2b --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/diagnostic7.C @@ -0,0 +1,11 @@ +// { dg-do compile { target c++2a } } + +template + concept same_as = __is_same(A, B); + +void f(); + +static_assert(requires { { f() } noexcept -> same_as; }); +// { dg-error "static assertion failed" "" { target *-*-* } .-1 } +// { dg-message "not .noexcept." "" { target *-*-* } .-2 } +// { dg-message "return-type-requirement" "" { target *-*-* } .-3 } diff --git a/gcc/testsuite/g++.dg/concepts/pr94252.C b/gcc/testsuite/g++.dg/concepts/pr94252.C new file mode 100644 index 0000000..56ce5f8 --- /dev/null +++ b/gcc/testsuite/g++.dg/concepts/pr94252.C @@ -0,0 +1,27 @@ +// PR c++/94252 +// { dg-do compile { target c++2a } } + +auto f = []{ return 0; }; +static_assert(requires { f(); }); +static_assert(requires { requires requires { f(); }; }); + +template + 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; +}); + +template + concept c = requires (T t) { requires (T)5; }; // { dg-error "has type .int." } + +int +foo() +{ + requires { requires c; }; + requires { { 5 } -> same_as; }; + requires { requires !requires { { 5 } -> same_as; }; }; + return requires { requires 5; }; // { dg-error "has type .int." } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C index c76b12c..c977045 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C @@ -4,7 +4,7 @@ template concept integer = __is_same_as(T, int); template -concept subst = requires (T x) { requires true; }; +concept subst = requires (T x) { requires true; }; // { dg-error "parameter type .void." } template concept c1 = requires { requires integer || subst; }; // { dg-message "in requirements" } -- cgit v1.1