https://gcc.gnu.org/bugzilla/show_bug.cgi?id=122795

            Bug ID: 122795
           Summary: Continuing to produce compile errors after a failed
                    static_assert
           Product: gcc
           Version: 15.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: barry.revzin at gmail dot com
  Target Milestone: ---

Consider the following situation:

template <class T>
concept Requirements = requires (T t) {
    t.foo();
    t.bar();
};

template <class T>
auto do_stuff(T t) -> void {
    static_assert(Requirements<T>);
    t.foo();
    t.bar();
}

auto main() -> int {
    do_stuff(42);
}

Here, I require all calls to do_stuff to satisfy some Requirements, which I
helpfully static_assert so that users can get good diagnostics on misuse. Which
does happen. 

But then, after the nice diagnostics on static_assert, gcc happily keeps on
rolling to try to compile everything else in this function template, and then
emits errors for all of the other uses. Which... of course they fail, because T
didn't satisfy Requirements. All of those failures are pure noise. For
instance:

<source>: In instantiation of 'void do_stuff(T) [with T = int]':
required from here
<source>:24:13:   
   24 |     do_stuff(42);
      |     ~~~~~~~~^~~~
<source>:10:19: error: static assertion failed
   10 |     static_assert(Requirements<T>);
      |                   ^~~~~~~~~~~~~~~
  • constraints not satisfied
    • required by the constraints of 'template<class T> concept Requirements'
      <source>:2:9:   
          2 | concept Requirements = requires (T t) {
            |         ^~~~~~~~~~~~
    • in requirements with 'T t' [with T = int]
      <source>:2:24:   
          2 | concept Requirements = requires (T t) {
            |                        ^~~~~~~~~~~~~~~~
          3 |     t.foo();
            |     ~~~~~~~~            
          4 |     t.bar();
            |     ~~~~~~~~            
          5 | };
            | ~                       
    • the required expression 't.foo()' is invalid
      <source>:3:10:
          3 |     t.foo();
            |     ~~~~~^~
    • the required expression 't.bar()' is invalid
      <source>:4:10:
          4 |     t.bar();
            |     ~~~~~^~
    • set '-fconcepts-diagnostics-depth=' to at least 2 for more detail
<source>:11:7: error: request for member 'foo' in 't', which is of non-class
type 'int'
   11 |     t.foo();
      |     ~~^~~
<source>:12:7: error: request for member 'bar' in 't', which is of non-class
type 'int'
   12 |     t.bar();
      |     ~~^~~
Compiler returned: 1

This particular example isn't so bad, since the errors for the failed cases are
pretty concise, but could easily get lines and lines of additional error
messages here too. It gets so bad that I've taken to rewriting a lot of code to
have this shape instead:

template <class T>
auto do_stuff(T t) -> void {
    if constexpr (Requirements<T>) {
        t.foo();
        t.bar();
    } else {
        static_assert(Requirements<T>);
    }
}

This ensures that if T doesn't satisfy Requirements, that I only get exactly
one set of errors. That makes for a much better user experience. But it makes
for a much worse coding experience — I have to repeat the assertion criteria
twice (otherwise the static_assert won't provide useful information) and then
the whole body of my function is indented a second time. 

Is it possible to either provide a flag like
-fstop-at-first-static-assert-failure-in-scope or add an attribute like

template <class T>
auto do_stuff(T t) -> void {
    [[gnu::plz_stop]] static_assert(Requirements<T>);
    t.foo();
    t.bar();
}

Or just some mechanism to ensure that the straightforward code also produces
the best diagnostics? 

(Note that the fatal-errors flag doesn't help here, I might still want to see
all the diagnostics if I get them from other sources, I just don't want to see
the extra diagnostics that I get from this scope that I already know that I
will get because of the static_assert failure).

Reply via email to