In passing, I've cleaned up some things:
* use iloc_sentinel when appropriate,
* it's nicer to call contextual_conv_bool instead of the rather verbose
perform_implicit_conversion_flags,
* no need to check for INTEGER_CST before calling integer_zerop.
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
gcc/cp/ChangeLog:
PR c++/97518
* cp-tree.h (finish_static_assert): Adjust declaration.
* parser.c (cp_parser_static_assert): Pass false to
finish_static_assert.
* pt.c (tsubst_expr): Pass true to finish_static_assert.
* semantics.c (find_failing_clause_r): New function.
(find_failing_clause): New function.
(finish_static_assert): Add a bool parameter. Use
iloc_sentinel. Call contextual_conv_bool instead of
perform_implicit_conversion_flags. Don't check for INTEGER_CST before
calling integer_zerop. Call find_failing_clause and maybe use its
location. Print the original condition if SHOW_EXPR_P.
gcc/testsuite/ChangeLog:
PR c++/97518
* g++.dg/diagnostic/pr87386.C: Adjust expected output.
* g++.dg/diagnostic/static_assert1.C: New test.
* g++.dg/diagnostic/static_assert2.C: New test.
libcc1/ChangeLog:
PR c++/97518
* libcp1plugin.cc (plugin_add_static_assert): Pass false to
finish_static_assert.
libstdc++-v3/ChangeLog:
* testsuite/20_util/scoped_allocator/69293_neg.cc: Adjust dg-error.
* testsuite/20_util/uses_allocator/69293_neg.cc: Likewise.
* testsuite/20_util/uses_allocator/cons_neg.cc: Likewise.
* testsuite/26_numerics/random/pr60037-neg.cc: Likewise.
---
gcc/cp/cp-tree.h | 2 +-
gcc/cp/parser.c | 3 +-
gcc/cp/pt.c | 6 +-
gcc/cp/semantics.c | 79 ++++++++++++++++---
gcc/testsuite/g++.dg/diagnostic/pr87386.C | 2 +-
.../g++.dg/diagnostic/static_assert1.C | 20 +++++
.../g++.dg/diagnostic/static_assert2.C | 68 ++++++++++++++++
libcc1/libcp1plugin.cc | 2 +-
.../20_util/scoped_allocator/69293_neg.cc | 2 +-
.../20_util/uses_allocator/69293_neg.cc | 2 +-
.../20_util/uses_allocator/cons_neg.cc | 2 +-
.../26_numerics/random/pr60037-neg.cc | 4 +-
12 files changed, 168 insertions(+), 24 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/diagnostic/static_assert1.C
create mode 100644 gcc/testsuite/g++.dg/diagnostic/static_assert2.C
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index b98d47a702f..230a1525c63 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7234,7 +7234,7 @@ extern bool cxx_omp_create_clause_info (tree,
tree, bool, bool,
bool, bool);
extern tree baselink_for_fns (tree);
extern void finish_static_assert (tree, tree, location_t,
- bool);
+ bool, bool);
extern tree finish_decltype_type (tree, bool, tsubst_flags_t);
extern tree finish_trait_expr (location_t, enum
cp_trait_kind, tree, tree);
extern tree build_lambda_expr (void);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 4c819ea1c5d..1ef14279ff7 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -14886,7 +14886,8 @@ cp_parser_static_assert(cp_parser *parser, bool
member_p)
/* Complete the static assertion, which may mean either processing
the static assert now or saving it for template instantiation. */
- finish_static_assert (condition, message, assert_loc, member_p);
+ finish_static_assert (condition, message, assert_loc, member_p,
+ /*show_expr_p=*/false);
}
/* Parse the expression in decltype ( expression ). */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a2655a0ff52..6ba114c9da3 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -18492,8 +18492,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl,
tree condition;
++c_inhibit_evaluation_warnings;
- condition =
- tsubst_expr (STATIC_ASSERT_CONDITION (t),
+ condition =
+ tsubst_expr (STATIC_ASSERT_CONDITION (t),
args,
complain, in_decl,
/*integral_constant_expression_p=*/true);
@@ -18502,7 +18502,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t
complain, tree in_decl,
finish_static_assert (condition,
STATIC_ASSERT_MESSAGE (t),
STATIC_ASSERT_SOURCE_LOCATION (t),
- /*member_p=*/false);
+ /*member_p=*/false, /*show_expr_p=*/true);
}
break;
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index a550db69488..1cc5118748f 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -9823,13 +9823,53 @@ init_cp_semantics (void)
{
}
+
+/* If we have a condition in conjunctive normal form (CNF), find the first
+ failing clause. In other words, given an expression like
+
+ true && true && false && true && false
+
+ return the first 'false'. EXPR is the expression. */
+
+static tree
+find_failing_clause_r (tree expr)
+{
+ if (TREE_CODE (expr) == TRUTH_ANDIF_EXPR)
+ {
+ /* First check the left side... */
+ tree e = find_failing_clause_r (TREE_OPERAND (expr, 0));
+ if (e == NULL_TREE)
+ /* ...if we didn't find a false clause, check the right side. */
+ e = find_failing_clause_r (TREE_OPERAND (expr, 1));
+ return e;
+ }
+ tree e = contextual_conv_bool (expr, tf_none);
+ e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true);
+ if (integer_zerop (e))
+ /* This is the failing clause. */
+ return expr;
+ return NULL_TREE;
+}
+
+/* Wrapper for find_failing_clause_r. */
+
+static tree
+find_failing_clause (tree expr)
+{
+ if (TREE_CODE (expr) != TRUTH_ANDIF_EXPR)
+ return NULL_TREE;
+ return find_failing_clause_r (expr);
+}
+
/* Build a STATIC_ASSERT for a static assertion with the condition
CONDITION and the message text MESSAGE. LOCATION is the location
of the static assertion in the source code. When MEMBER_P, this
- static assertion is a member of a class. */
+ static assertion is a member of a class. If SHOW_EXPR_P is true,
+ print the condition (because it was instantiation-dependent). */
+
void
finish_static_assert (tree condition, tree message, location_t location,
- bool member_p)
+ bool member_p, bool show_expr_p)
{
tsubst_flags_t complain = tf_warning_or_error;
@@ -9867,8 +9907,7 @@ finish_static_assert (tree condition, tree message, location_t location,
tree orig_condition = condition;
/* Fold the expression and convert it to a boolean value. */
- condition = perform_implicit_conversion_flags (boolean_type_node, condition,
- complain, LOOKUP_NORMAL);
+ condition = contextual_conv_bool (condition, complain);
condition = fold_non_dependent_expr (condition, complain,
/*manifestly_const_eval=*/true);
@@ -9877,21 +9916,38 @@ finish_static_assert (tree condition, tree message, location_t location,
;
else
{
- location_t saved_loc = input_location;
+ iloc_sentinel ils (location);
- input_location = location;
- if (TREE_CODE (condition) == INTEGER_CST
- && integer_zerop (condition))
+ if (integer_zerop (condition))
{
int sz = TREE_INT_CST_LOW (TYPE_SIZE_UNIT
(TREE_TYPE (TREE_TYPE (message))));
int len = TREE_STRING_LENGTH (message) / sz - 1;
+
+ /* See if we can find which clause was failing (for logical AND). */
+ tree bad = find_failing_clause (orig_condition);
+ /* If not, or its location is unusable, fall back to the previous
+ location. */
+ location_t cloc = location;
+ if (cp_expr_location (bad) != UNKNOWN_LOCATION)
+ cloc = cp_expr_location (bad);
+
/* Report the error. */
if (len == 0)
- error ("static assertion failed");
+ {
+ if (show_expr_p)
+ error_at (cloc, "static assertion failed due to "
+ "requirement %qE", orig_condition);
+ else
+ error_at (cloc, "static assertion failed");
+ }
+ else if (show_expr_p)
+ error_at (cloc, "static assertion failed due to "
+ "requirement %qE: %s", orig_condition,
+ TREE_STRING_POINTER (message));
else
- error ("static assertion failed: %s",
- TREE_STRING_POINTER (message));
+ error_at (cloc, "static assertion failed: %s",
+ TREE_STRING_POINTER (message));
/* Actually explain the failure if this is a concept check or a
requires-expression. */
@@ -9905,7 +9961,6 @@ finish_static_assert (tree condition, tree message,
location_t location,
if (require_rvalue_constant_expression (condition))
cxx_constant_value (condition);
}
- input_location = saved_loc;
}
}
diff --git a/gcc/testsuite/g++.dg/diagnostic/pr87386.C
b/gcc/testsuite/g++.dg/diagnostic/pr87386.C
index 85726af9f01..679a5177f64 100644
--- a/gcc/testsuite/g++.dg/diagnostic/pr87386.C
+++ b/gcc/testsuite/g++.dg/diagnostic/pr87386.C
@@ -14,5 +14,5 @@ static_assert (foo::test<int>::value, "foo"); // {
dg-error "static assertion f
static_assert (foo::test<int>::value && true, "bar"); // { dg-error "static
assertion failed: bar" }
/* { dg-begin-multiline-output "" }
static_assert (foo::test<int>::value && true, "bar");
- ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
+ ~~~~~~~~~~~~~~~~^~~~~
{ dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/g++.dg/diagnostic/static_assert1.C
b/gcc/testsuite/g++.dg/diagnostic/static_assert1.C
new file mode 100644
index 00000000000..5cfb66a5ad7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/static_assert1.C
@@ -0,0 +1,20 @@
+// PR c++/97518
+// { dg-do compile { target c++17 } }
+
+template <typename T, typename U> struct is_same { static constexpr bool value
= false; };
+template <typename T> struct is_same<T, T> { static constexpr bool value =
true; };
+
+template <typename T> using some_metafunction_t = T;
+
+template <typename T>
+void foo(T ) {
+ using X = T*;
+ using Y = some_metafunction_t<T>;
+
+ static_assert(is_same<X, Y>::value); // { dg-error {static assertion failed due
to requirement .is_same<int\*, int>::value.} }
+ static_assert(is_same<X, Y>::value, "foo"); // { dg-error {static assertion
failed due to requirement .is_same<int\*, int>::value.: foo} }
+}
+
+void bar() {
+ foo(0);
+}
diff --git a/gcc/testsuite/g++.dg/diagnostic/static_assert2.C
b/gcc/testsuite/g++.dg/diagnostic/static_assert2.C
new file mode 100644
index 00000000000..542697f99de
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/static_assert2.C
@@ -0,0 +1,68 @@
+// PR c++/97518
+// { dg-do compile { target c++11 } }
+// { dg-options "-fdiagnostics-show-caret" }
+
+constexpr bool yes () { return true; }
+constexpr bool no () { return false; }
+constexpr bool yay = true;
+constexpr bool nay = false;
+
+void
+bar ()
+{
+ static_assert (true && true && no(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (true && true && no(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert (yay && nay, ""); // { dg-error "static assertion failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (yay && nay, "");
+ ^~~
+ { dg-end-multiline-output "" } */
+ static_assert (yes() && no(), ""); // { dg-error "static assertion failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (yes() && no(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert (no() && yes(), ""); // { dg-error "static assertion failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (no() && yes(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert (no() && no() && yes(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (no() && no() && yes(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert (yes() && yes() && yes () && no() && yes(), ""); // { dg-error
"static assertion failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (yes() && yes() && yes () && no() && yes(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert (yes() && yes() && yes () && (no() && yes()), ""); // { dg-error
"static assertion failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert (yes() && yes() && yes () && (no() && yes()), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert ((yes() && no()) && no(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert ((yes() && no()) && no(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert ((yes() && no()) && no(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert ((yes() && no()) && no(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+ static_assert ((no() || no()) && yes(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert ((no() || no()) && yes(), "");
+ ~~~~~~^~~~~~~~
+ { dg-end-multiline-output "" } */
+ static_assert ((yes() || no()) && no(), ""); // { dg-error "static assertion
failed" }
+/* { dg-begin-multiline-output "" }
+ static_assert ((yes() || no()) && no(), "");
+ ~~^~
+ { dg-end-multiline-output "" } */
+}
diff --git a/libcc1/libcp1plugin.cc b/libcc1/libcp1plugin.cc
index bab2751a5ce..67a235f9095 100644
--- a/libcc1/libcp1plugin.cc
+++ b/libcc1/libcp1plugin.cc
@@ -3642,7 +3642,7 @@ plugin_add_static_assert (cc1_plugin::connection *self,
bool member_p = at_class_scope_p ();
- finish_static_assert (condition, message, loc, member_p);
+ finish_static_assert (condition, message, loc, member_p, false);
return 1;
}
diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
index 6f3cd0a2521..d28a4330504 100644
--- a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc
@@ -46,7 +46,7 @@ test01()
scoped_alloc sa;
auto p = sa.allocate(1);
sa.construct(p); // this is required to be ill-formed
- // { dg-error "failed: .* uses_allocator is true" "" { target *-*-* } 0 }
+ // { dg-error "failed due to requirement \[^\n\r\]*: .* uses_allocator is true"
"" { target *-*-* } 0 }
}
// Needed because of PR c++/92193
diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
b/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
index 423ecc4d90e..b6d925edcd3 100644
--- a/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/uses_allocator/69293_neg.cc
@@ -44,6 +44,6 @@ test01()
{
alloc_type a;
std::tuple<X> t(std::allocator_arg, a); // this is required to be ill-formed
- // { dg-error "failed: .* uses_allocator is true" "" { target *-*-* } 0 }
+ // { dg-error "failed due to requirement \[^\n\r\]*: .* uses_allocator is true"
"" { target *-*-* } 0 }
// { dg-error "no matching function for call" "" { target c++2a } 0 }
}
diff --git a/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
b/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
index c3ff05aa72a..20190feaa0b 100644
--- a/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/uses_allocator/cons_neg.cc
@@ -43,5 +43,5 @@ void test01()
tuple<Type> t(allocator_arg, a, 1);
}
-// { dg-error "failed: .* uses_allocator is true" "" { target *-*-* } 0 }
+// { dg-error "failed due to requirement \[^\n\r\]*: .* uses_allocator is true"
"" { target *-*-* } 0 }
// { dg-error "no matching function for call" "" { target c++2a } 0 }
diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
index ba252ef34fe..e2066cd59ee 100644
--- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
+++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc
@@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long>
aurng(urng);
auto x = std::generate_canonical<std::size_t,
std::numeric_limits<std::size_t>::digits>(urng);
-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 167 }
+// { dg-error "static assertion failed due to requirement \[^\n\r\]*: template argument must
be a floating point type" "" { target *-*-* } 167 }
-// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3346 }
+// { dg-error "static assertion failed due to requirement \[^\n\r\]*: template argument must
be a floating point type" "" { target *-*-* } 3346 }
base-commit: 04126e46eb2d829d7b4149d394b667e878912cc8