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 9c08c0e46a2..36322812310 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 33d715edaec..df698d51dfe 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -9827,13 +9827,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; @@ -9871,8 +9911,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); @@ -9881,21 +9920,32 @@ 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"); + error_at (cloc, "static assertion failed"); else - error ("static assertion failed: %s", - TREE_STRING_POINTER (message)); + error_at (cloc, "static assertion failed: %s", + TREE_STRING_POINTER (message)); + if (show_expr_p) + inform (cloc, "%qE evaluates to false", + /* Nobody wants to see the artificial (bool) cast. */ + (bad ? tree_strip_nop_conversions (bad) : orig_condition)); /* Actually explain the failure if this is a concept check or a requires-expression. */ @@ -9909,7 +9959,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::value, "foo"); // { dg-error "static assertion f static_assert (foo::test::value && true, "bar"); // { dg-error "static assertion failed: bar" } /* { dg-begin-multiline-output "" } static_assert (foo::test::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..fecf3cc51a0 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/static_assert1.C @@ -0,0 +1,30 @@ +// PR c++/97518 +// { dg-do compile { target c++17 } } + +template struct is_same { static constexpr bool value = false; }; +template struct is_same { static constexpr bool value = true; }; + +template using some_metafunction_t = T; + +template +void foo(T ) { + using X = T*; + using Y = some_metafunction_t; + + static_assert(is_same::value); // { dg-error "static assertion failed" } + // { dg-message {.is_same::value. evaluates to false} "" { target *-*-* } .-1 } + static_assert(is_same::value, "foo"); // { dg-error "static assertion failed: foo" } + // { dg-message {.is_same::value. evaluates to false} "" { target *-*-* } .-1 } + static_assert(is_same::value && is_same::value); // { dg-error "static assertion failed" } + // { dg-message {.is_same::value. evaluates to false} "" { target *-*-* } .-1 } + static_assert(is_same::value && is_same::value); // { dg-error "static assertion failed" } + // { dg-message {.is_same::value. evaluates to false} "" { target *-*-* } .-1 } + static_assert(is_same::value + && is_same::value + && is_same::value); // { dg-error "static assertion failed" } + // { dg-message {.is_same::value. evaluates to false} "" { target *-*-* } .-1 } +} + +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; }