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.