On Tue, Nov 10, 2020 at 02:30:30PM -0500, Jason Merrill via Gcc-patches wrote: > On 11/10/20 2:28 PM, Marek Polacek wrote: > > On Tue, Nov 10, 2020 at 02:15:56PM -0500, Jason Merrill wrote: > > > On 11/10/20 1:59 PM, Marek Polacek wrote: > > > > On Tue, Nov 10, 2020 at 11:32:04AM -0500, Jason Merrill wrote: > > > > > On 11/9/20 7:21 PM, Marek Polacek wrote: > > > > > > Currently, when a static_assert fails, we only say "static > > > > > > assertion failed". > > > > > > It would be more useful if we could also print the expression that > > > > > > evaluated to false; this is especially useful when the condition > > > > > > uses > > > > > > template parameters. Consider the motivating example, in which we > > > > > > have > > > > > > this line: > > > > > > > > > > > > static_assert(is_same<X, Y>::value); > > > > > > > > > > > > if this fails, the user has to play dirty games to get the compiler > > > > > > to > > > > > > print the template arguments. With this patch, we say: > > > > > > > > > > > > static assertion failed due to requirement 'is_same<int*, > > > > > > int>::value' > > > > > > > > > > I'd rather avoid the word "requirement" here, to avoid confusion with > > > > > Concepts. > > > > > > > > > > Maybe have the usual failed error, and if we're showing the > > > > > expression, have > > > > > a second inform to say e.g. "%qE evaluates to false"? > > > > > > > > Works for me. > > > > > > > > > Also, if we've narrowed the problem down to a particular > > > > > subexpression, > > > > > let's only print that one. > > > > > > > > Done. > > > > > > > > > > which I think is much better. However, always printing the > > > > > > condition that > > > > > > evaluated to 'false' wouldn't be very useful: e.g. noexcept(fn) is > > > > > > always parsed to true/false, so we would say "static assertion > > > > > > failed due > > > > > > to requirement 'false'" which doesn't help. So I wound up only > > > > > > printing > > > > > > the condition when it was instantiation-dependent, that is, we > > > > > > called > > > > > > finish_static_assert from tsubst_expr. > > > > > > > > > > > > Moreover, this patch also improves the diagnostic when the condition > > > > > > consists of a logical AND. Say you have something like this: > > > > > > > > > > > > static_assert(fn1() && fn2() && fn3() && fn4() && fn5()); > > > > > > > > > > > > where fn4() evaluates to false and the other ones to true. > > > > > > Highlighting > > > > > > the whole thing is not that helpful because it won't say which > > > > > > clause > > > > > > evaluated to false. With the find_failing_clause tweak in this > > > > > > patch > > > > > > we emit: > > > > > > > > > > > > error: static assertion failed > > > > > > 6 | static_assert(fn1() && fn2() && fn3() && fn4() && fn5()); > > > > > > | ~~~^~ > > > > > > > > > > > > so you know right away what's going on. Unfortunately, when you > > > > > > combine > > > > > > both things, that is, have an instantiation-dependent expr and && in > > > > > > a static_assert, we can't yet quite point to the clause that > > > > > > failed. It > > > > > > is because when we tsubstitute something like is_same<X, Y>::value, > > > > > > we > > > > > > generate a VAR_DECL that doesn't have any location. It would be > > > > > > awesome > > > > > > if we could wrap it with a location wrapper, but I didn't see > > > > > > anything > > > > > > obvious. > > > > > > > > > > Hmm, I vaguely remember that at first we were using location wrappers > > > > > less > > > > > in templates, but I thought that was fixed. I don't see anything > > > > > setting > > > > > suppress_location_wrappers, and it looks like tsubst_copy_and_build > > > > > should > > > > > preserve a location wrapper. > > > > > > > > The problem here is that tsubst_qualified_id produces a VAR_DECL and > > > > for those > > > > CAN_HAVE_LOCATION_P is false. > > > > > > Ah, then perhaps tsubst_qualified_id should call maybe_wrap_with_location > > > to > > > preserve the location of the SCOPE_REF. > > > > The SCOPE_REF's location is fine, we just throw it away and return a > > VAR_DECL. > > Yes, I'm saying that tsubst_qualified_id should extract the location from > the SCOPE_REF and pass it to maybe_wrap_with_location to give the VAR_DECL a > location wrapper.
Nevermind, I wasn't checking the return value of maybe_wrap_with_location... Maybe we should childproof it by marking it with WARN_UNUSED_RESULT. Anyway, this patch does the tsubst_qualified_id tweak. With that, the static_assert diagnostic with && is pretty spot on! Relatedly, don't create useless location wrappers for temporary variables. Since they are compiler-generated and ignored, nobody should be interested in their location in the source file. Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- Retain the location when tsubstituting a qualified-id so that our static_assert diagnostic can benefit. Don't create useless location wrappers for temporary variables. gcc/ChangeLog: PR c++/97518 * tree.c (maybe_wrap_with_location): Don't add a location wrapper around an artificial and ignored decl. gcc/cp/ChangeLog: PR c++/97518 * pt.c (tsubst_qualified_id): Use EXPR_LOCATION of the qualified-id. Use it to maybe_wrap_with_location the final expression. gcc/testsuite/ChangeLog: PR c++/97518 * g++.dg/diagnostic/static_assert3.C: New test. --- gcc/cp/pt.c | 5 +-- .../g++.dg/diagnostic/static_assert3.C | 36 +++++++++++++++++++ gcc/tree.c | 4 +++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/diagnostic/static_assert3.C diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6ba114c9da3..c592461c474 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -16214,7 +16214,7 @@ tsubst_qualified_id (tree qualified_id, tree args, tree name; bool is_template; tree template_args; - location_t loc = UNKNOWN_LOCATION; + location_t loc = EXPR_LOCATION (qualified_id); gcc_assert (TREE_CODE (qualified_id) == SCOPE_REF); @@ -16223,7 +16223,6 @@ tsubst_qualified_id (tree qualified_id, tree args, if (TREE_CODE (name) == TEMPLATE_ID_EXPR) { is_template = true; - loc = EXPR_LOCATION (name); template_args = TREE_OPERAND (name, 1); if (template_args) template_args = tsubst_template_args (template_args, args, @@ -16351,6 +16350,8 @@ tsubst_qualified_id (tree qualified_id, tree args, if (REF_PARENTHESIZED_P (qualified_id)) expr = force_paren_expr (expr); + expr = maybe_wrap_with_location (expr, loc); + return expr; } diff --git a/gcc/testsuite/g++.dg/diagnostic/static_assert3.C b/gcc/testsuite/g++.dg/diagnostic/static_assert3.C new file mode 100644 index 00000000000..5d363884508 --- /dev/null +++ b/gcc/testsuite/g++.dg/diagnostic/static_assert3.C @@ -0,0 +1,36 @@ +// PR c++/97518 +// { dg-do compile { target c++17 } } +// { dg-options "-fdiagnostics-show-caret" } + +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, typename U> +void f(T, U) +{ + static_assert(is_same<T, T>::value && is_same<T, U>::value); // { dg-error "56:static assertion failed" } +/* { dg-begin-multiline-output "" } + static_assert(is_same<T, T>::value && is_same<T, U>::value); + ^~~~~ + { dg-end-multiline-output "" } */ +// { dg-message ".is_same<int, double>::value. evaluates to false" "" { target *-*-* } .-5 } + static_assert(is_same<U, T>::value && is_same<U, U>::value); // { dg-error "32:static assertion failed" } +/* { dg-begin-multiline-output "" } + static_assert(is_same<U, T>::value && is_same<U, U>::value); + ^~~~~ + { dg-end-multiline-output "" } */ +// { dg-message ".is_same<double, int>::value. evaluates to false" "" { target *-*-* } .-5 } + static_assert(is_same<U, U>::value + && is_same<U, T>::value // { dg-error "35:static assertion failed" } + && is_same<T, T>::value); +/* { dg-begin-multiline-output "" } + && is_same<U, T>::value + ^~~~~ + { dg-end-multiline-output "" } */ +// { dg-message ".is_same<double, int>::value. evaluates to false" "" { target *-*-* } .-6 } +} + +void g() +{ + f(0, 1.3); +} diff --git a/gcc/tree.c b/gcc/tree.c index 663f3ecfd12..34cc4116d66 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -15041,6 +15041,10 @@ maybe_wrap_with_location (tree expr, location_t loc) if (EXCEPTIONAL_CLASS_P (expr)) return expr; + /* Compiler-generated temporary variables don't need a wrapper. */ + if (DECL_P (expr) && DECL_ARTIFICIAL (expr) && DECL_IGNORED_P (expr)) + return expr; + /* If any auto_suppress_location_wrappers are active, don't create wrappers. */ if (suppress_location_wrappers > 0) base-commit: 9179d9da39cf6e72ab870647db893ef69316b103 -- 2.28.0