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

            Bug ID: 120300
           Summary: -Wmissing-noreturn is handled inconsistently for
                    in-class and out-of-class definitions
           Product: gcc
           Version: 16.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: leekillough at gmail dot com
  Target Milestone: ---

This occurs with every version tested from 3.x - trunk.

Out-of-class virtual function definitions which never return are incorrectly
warned about missing noreturn.

In-class non-virtual function definitions which never return are incorrectly
not warned about missing noreturn.

In-class final virtual function definitions which never return are incorrectly
not warned about missing noreturn.

struct C1 { virtual void f()                        { throw 1; } };
struct C2 { virtual void f(); };       void C2::f() { throw 1; }
struct C3 {         void f()                        { throw 1; } };
struct C4 {         void f(); };       void C4::f() { throw 1; }
struct C5 : C1 {    void f() final                  { throw 1; } };
struct C6 : C2 {    void f() final; }; void C6::f() { throw 1; }

> $ g++ -c -Wmissing-noreturn test.cc
> test.cc: In member function ‘virtual void C2::f()’:
> test.cc:2:45: warning: function might be candidate for attribute ‘noreturn’ 
> [-Wsuggest-attribute=noreturn]
>     2 | struct C2 { virtual void f(); };       void C2::f() { throw 1; }
>       |                                             ^~
> test.cc: In member function ‘void C4::f()’:
> test.cc:4:45: warning: function might be candidate for attribute ‘noreturn’ 
> [-Wsuggest-attribute=noreturn]
>     4 | struct C4 {         void f(); };       void C4::f() { throw 1; }
>       |                                             ^~
> test.cc: In member function ‘virtual void C6::f()’:
> test.cc:6:45: warning: function might be candidate for attribute ‘noreturn’ 
> [-Wsuggest-attribute=noreturn]
>     6 | struct C6 : C2 {    void f() final; }; void C6::f() { throw 1; }

C1::f(), which is virtual and has an in-class definition, is correctly not
warned about missing noreturn.

C2::f(), which is virtual and has an out-of-class definition, is wrongly warned
about missing noreturn.

C3::f(), which is non-virtual and has an in-class definition, is wrongly not
warned about missing noreturn.

C4::f(), which is non-virtual and has an out-of-class definition, is correctly
warned about missing noreturn.

C5::f(), which is virtual and final and has an in-class definition, is wrongly
not warned about missing noreturn.

C6::f(), which is virtual and final and has an out-of-class definition, is
correctly warned about missing noreturn.

[[noreturn]] and __attribute__((noreturn)) are function attributes, not type
attributes, and are not inherited or enforced on virtual function overrides.

Historically, __attribute__((noreturn)) might have behaved slightly differently
w.r.t. inheritance, but ever since C++11 [[noreturn]], it should have the
latter behavior.

If preserving the historic behavior of __attribute__((noreturn)) separately
from [[noreturn]] is required, then the -Wmissing-noreturn warning should be
separated from -Wsuggest-attribute=noreturn so that -Wmissing-noreturn enforces
[[noreturn]] semantics, and -Wsuggest-attribute=noreturn enforces
__attribute__((noreturn)) semantics, c.f. bug 57166.

A virtual function call using dynamic dispatch cannot consider the noreturn
attribute, because it can be overriden by a function which does not have the
noreturn attribute.

struct C { [[noreturn]] virtual void f() { throw 1; } };
void func(C& c) { c.f(); }

Here, GCC has the correct behavior, because the virtual call c.f() cannot
consider the noreturn attribute:

> $ g++ -c -Wmissing-noreturn test.cc
> $

If a virtual function is de-virtualized by calling it through a class type in
which it is marked "final", or it is called non-virtually such as c.C::f(),
then the noreturn attribute can be considered, and a function calling a
noreturn virtual function non-virtually can be warned if is missing noreturn.

Here, GCC has the correct behavior, because a non-virtual call to a virtual
function, or a virtual call to a final virtual function, can consider the
noreturn attribute as non-overridable, and thus warn if the caller is missing
noreturn:

struct C { [[noreturn]] virtual void f() { throw 1; } };
void func(C& c) { c.C::f(); }

> $ g++ -c -Wmissing-noreturn test.cc
> test.cc: In function ‘void func(C&)’:
> test.cc:2:6: warning: function might be candidate for attribute ‘noreturn’ 
> [-Wsuggest-attribute=noreturn]
>     2 | void func(C& c) { c.C::f(); }
>       |      ^~~~

struct C { [[noreturn]] virtual void f() final { throw 1; } };
void func(C& c) { c.f(); }

> $ g++ -c -Wmissing-noreturn test.cc
> test.cc: In function ‘void func(C&)’:
> test.cc:2:6: warning: function might be candidate for attribute ‘noreturn’ 
> [-Wsuggest-attribute=noreturn]
>     2 | void func(C& c) { c.f(); }
>       |      ^~~~

Key points:

A non-final virtual member function definition, whether it is in-class or
out-of-class, should not be warned about missing noreturn, because it can be
called virtually and overriden by a function which returns, so there are paths
of calling such a function which do in fact return, and so the missing noreturn
attribute is only useful if all calls to the virtual function are non-virtual,
which defeats the point of declaring it virtual.

A non-member function definition, non-virtual member function definition, or a
final virtual member function definition, should be warned as missing noreturn
iff all of its execution paths throw exceptions or call noreturn functions
non-virtually.

The handling of -Wmissing-noreturn should behave the same whether a member
function is defined in-class or out-of-class.

Related Bug 61379 should be closed as #NOTABUG.

Reply via email to