aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2020-03-28 08:56:59 -0400
committerPatrick Palka <ppalka@redhat.com>2020-03-28 08:56:59 -0400
commita7ea3d2ced786c4544fa625f34f515d89ed074fe (patch)
tree5d72578ca8e5a5be0fbb57cbd820e4bb2f1a175b /gcc
parentcd68edf894d6b72e5bc37ac205deef9d237ab70b (diff)
downloadgcc-a7ea3d2ced786c4544fa625f34f515d89ed074fe.zip
gcc-a7ea3d2ced786c4544fa625f34f515d89ed074fe.tar.gz
gcc-a7ea3d2ced786c4544fa625f34f515d89ed074fe.tar.bz2
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) <case INTEGER_CST>: Remove this case so that we diagnose INTEGER_CST expressions of non-bool type via the default case. * cp-gimplify.c (cp_genericize_r) <case REQUIRES_EXPR>: 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) <case REQUIRES_EXPR>: 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.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog18
-rw-r--r--gcc/cp/constraint.cc10
-rw-r--r--gcc/cp/cp-gimplify.c7
-rw-r--r--gcc/cp/parser.c14
-rw-r--r--gcc/cp/pt.c2
-rw-r--r--gcc/cp/semantics.c6
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/g++.dg/concepts/diagnostic7.C11
-rw-r--r--gcc/testsuite/g++.dg/concepts/pr94252.C27
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-requires18.C2
10 files changed, 91 insertions, 12 deletions
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 <ppalka@redhat.com>
+ 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) <case INTEGER_CST>: Remove this case so
+ that we diagnose INTEGER_CST expressions of non-bool type via the
+ default case.
+ * cp-gimplify.c (cp_genericize_r) <case REQUIRES_EXPR>: 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) <case REQUIRES_EXPR>: 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 <ppalka@redhat.com>
+ 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<typename A, typename B>
+ concept same_as = __is_same(A, B);
+
+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 }
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<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>;
+});
+
+template<typename T>
+ concept c = requires (T t) { requires (T)5; }; // { dg-error "has type .int." }
+
+int
+foo()
+{
+ requires { requires c<int>; };
+ requires { { 5 } -> same_as<bool>; };
+ requires { requires !requires { { 5 } -> same_as<bool>; }; };
+ 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<typename T>
concept integer = __is_same_as(T, int);
template<typename T>
-concept subst = requires (T x) { requires true; };
+concept subst = requires (T x) { requires true; }; // { dg-error "parameter type .void." }
template<typename T>
concept c1 = requires { requires integer<T> || subst<T&>; }; // { dg-message "in requirements" }