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.