https://gcc.gnu.org/g:f8c32184b85ce71c154fa96cd5ca700aab902656

commit r16-6547-gf8c32184b85ce71c154fa96cd5ca700aab902656
Author: Jakub Jelinek <[email protected]>
Date:   Wed Jan 7 15:00:50 2026 +0100

    libstdc++: Use gnu_inline attribute on constexpr exception methods 
[PR123183]
    
    As mentioned in
    https://gcc.gnu.org/pipermail/gcc-patches/2026-January/704712.html
    in the gnu::constexpr_only thread, gnu::gnu_inline attribute actually
    seems to work for most of what we need for C++26 constexpr exceptions
    (i.e. when we want out of line bodies for C++ < 26 and need to use
    constexpr for C++26, yet don't want for reasons mentioned in those
    two PRs the bodies of those constexpr methods to be emitted inline).
    Unfortunately clang++ doesn't handle it 100% properly and requires
    the redundant inline keyword to make it work (even when the methods
    are constexpr and thus implicilty inline), g++ doesn't require that,
    so the patch adds also the redundant inline keywords and not just
    the [[__gnu__::__gnu_inline__]] attribute.
    This way if something wants to inline those functions it can, but
    if their address is taken, we just rely on libstdc++.{so,a} to provide
    those (which it does as before because those TUs are compiled with
    older -std= modes).
    The earlier r16-6477-gd5743234731 commit made sure gnu::gnu_inline
    constexpr virtual methods can be key methods, so vtables and rtti can
    be emitted only in the TU defining non-gnu_inline versions of those.
    
    2026-01-07  Jakub Jelinek  <[email protected]>
    
            PR libstdc++/123183
            PR libstdc++/123326
            * libsupc++/exception (std::bad_exception::~bad_exception(),
            std::bad_exception::what()): Add inline keyword and
            [[__gnu__::__gnu_inline__]] attribute to the C++26 constexpr
            exceptions definitions.
            * libsupc++/exception.h (std::exception::~exception(),
            std::exception::what()): Likewise.
            * libsupc++/exception_ptr.h 
(std::exception_ptr::exception_ptr(void*)):
            Likewise.
            * libsupc++/nested_exception.h
            (std::nested_exception::~nested_exception()): Likewise.
            * libsupc++/typeinfo (std::bad_cast::~bad_cast(),
            std::bad_cast::what(), std::bad_typeid::~bad_typeid(),
            std::bad_typeid::what()): Likewise.
            * include/bits/new_except.h (std::bad_alloc::~bad_alloc(),
            std::bad_alloc::what(),
            std::bad_array_new_length::~bad_array_new_length(),
            std::bad_array_new_length::what()): Likewise.
            * include/bits/stdexcept_except.h
            (std::logic_error::logic_error(const string&),
            std::logic_error::logic_error(const char*),
            std::logic_error::~logic_error(), std::logic_error::what(),
            std::domain_error::domain_error(const string&),
            std::domain_error::domain_error(const char*),
            std::invalid_argument::invalid_argument(const string&),
            std::invalid_argument::invalid_argument(const char*),
            std::length_error::length_error(const string&),
            std::length_error::length_error(const char*),
            std::out_of_range::out_of_range(const string&),
            std::out_of_range::out_of_range(const char*),
            std::runtime_error::runtime_error(const string&),
            std::runtime_error::runtime_error(const char*),
            std::runtime_error::~runtime_error(), std::runtime_error::what(),
            std::overflow_error::overflow_error(const string&),
            std::overflow_error::overflow_error(const char*),
            std::overflow_error::~overflow_error(),
            std::underflow_error::underflow_error(const string&),
            std::underflow_error::underflow_error(const char*),
            std::underflow_error::~underflow_error()): Likewise.
            (std::domain_error::~domain_error(),
            std::invalid_argument::~invalid_argument(),
            std::length_error::~length_error(),
            std::out_of_range::~out_of_range()): Likewise.  Also change
            _GLIBCXX_NOTHROW to noexcept on those definitions.

Diff:
---
 libstdc++-v3/include/bits/new_except.h       | 12 ++--
 libstdc++-v3/include/bits/stdexcept_except.h | 88 ++++++++++++++++++++--------
 libstdc++-v3/libsupc++/exception             |  7 ++-
 libstdc++-v3/libsupc++/exception.h           |  6 +-
 libstdc++-v3/libsupc++/exception_ptr.h       |  3 +-
 libstdc++-v3/libsupc++/nested_exception.h    |  3 +-
 libstdc++-v3/libsupc++/typeinfo              | 12 ++--
 7 files changed, 91 insertions(+), 40 deletions(-)

diff --git a/libstdc++-v3/include/bits/new_except.h 
b/libstdc++-v3/include/bits/new_except.h
index 18a2d1f82fed..e4f21fdd11e9 100644
--- a/libstdc++-v3/include/bits/new_except.h
+++ b/libstdc++-v3/include/bits/new_except.h
@@ -62,9 +62,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~bad_alloc() noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~bad_alloc() noexcept {}
 
-    constexpr virtual const char* what() const noexcept
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char* what() const noexcept
     {
       return "std::bad_alloc";
     }
@@ -85,9 +87,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _GLIBCXX26_CONSTEXPR bad_array_new_length() throw() { }
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~bad_array_new_length() noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~bad_array_new_length() noexcept {}
 
-    constexpr virtual const char* what() const noexcept
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char* what() const noexcept
     {
       return "std::bad_array_new_length";
     }
diff --git a/libstdc++-v3/include/bits/stdexcept_except.h 
b/libstdc++-v3/include/bits/stdexcept_except.h
index a784d40fdf2c..6a43991c2240 100644
--- a/libstdc++-v3/include/bits/stdexcept_except.h
+++ b/libstdc++-v3/include/bits/stdexcept_except.h
@@ -258,11 +258,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit
     logic_error(const string& __arg) _GLIBCXX_TXN_SAFE
     : _M_msg(__arg) {}
 
-    constexpr explicit
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit
     logic_error(const char* __arg) _GLIBCXX_TXN_SAFE
     : _M_msg(__arg) {}
 
@@ -271,9 +273,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr logic_error(const logic_error&) noexcept = default;
     constexpr logic_error& operator=(const logic_error&) noexcept = default;
 
-    constexpr virtual ~logic_error() _GLIBCXX_TXN_SAFE_DYN noexcept { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~logic_error() _GLIBCXX_TXN_SAFE_DYN noexcept { }
 
-    constexpr virtual const char*
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char*
     what() const _GLIBCXX_TXN_SAFE_DYN noexcept
     {
       return _M_msg.c_str();
@@ -319,15 +323,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit domain_error(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit domain_error(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
-    constexpr explicit domain_error(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit domain_error(const char* __arg) _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
     constexpr domain_error(const domain_error&) = default;
     constexpr domain_error& operator=(const domain_error&) = default;
     constexpr domain_error(domain_error&&) = default;
     constexpr domain_error& operator=(domain_error&&) = default;
-    constexpr virtual ~domain_error() _GLIBCXX_NOTHROW { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~domain_error() noexcept { }
 #else
     explicit domain_error(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
@@ -346,15 +354,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit invalid_argument(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit invalid_argument(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
-    constexpr explicit invalid_argument(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit invalid_argument(const char* __arg)
+      _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
     constexpr invalid_argument(const invalid_argument&) = default;
     constexpr invalid_argument& operator=(const invalid_argument&) = default;
     constexpr invalid_argument(invalid_argument&&) = default;
     constexpr invalid_argument& operator=(invalid_argument&&) = default;
-    constexpr virtual ~invalid_argument() _GLIBCXX_NOTHROW { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~invalid_argument() noexcept { }
 #else
     explicit invalid_argument(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
@@ -374,15 +387,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit length_error(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit length_error(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
-    constexpr explicit length_error(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit length_error(const char* __arg) _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
     constexpr length_error(const length_error&) = default;
     constexpr length_error& operator=(const length_error&) = default;
     constexpr length_error(length_error&&) = default;
     constexpr length_error& operator=(length_error&&) = default;
-    constexpr virtual ~length_error() _GLIBCXX_NOTHROW { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~length_error() noexcept { }
 #else
     explicit length_error(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
@@ -402,15 +419,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit out_of_range(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit out_of_range(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
-    constexpr explicit out_of_range(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit out_of_range(const char* __arg) _GLIBCXX_TXN_SAFE
     : logic_error(__arg) { }
     constexpr out_of_range(const out_of_range&) = default;
     constexpr out_of_range& operator=(const out_of_range&) = default;
     constexpr out_of_range(out_of_range&&) = default;
     constexpr out_of_range& operator=(out_of_range&&) = default;
-    constexpr virtual ~out_of_range() _GLIBCXX_NOTHROW { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~out_of_range() noexcept { }
 #else
     explicit out_of_range(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
@@ -435,11 +456,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit
     runtime_error(const string& __arg) _GLIBCXX_TXN_SAFE
     : _M_msg(__arg) {}
 
-    constexpr explicit
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit
     runtime_error(const char* __arg) _GLIBCXX_TXN_SAFE
     : _M_msg(__arg) {}
 
@@ -448,9 +471,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr runtime_error(const runtime_error&) noexcept = default;
     runtime_error& operator=(const runtime_error&) noexcept = default;
 
-    constexpr virtual ~runtime_error() _GLIBCXX_TXN_SAFE_DYN noexcept { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~runtime_error() _GLIBCXX_TXN_SAFE_DYN noexcept
+    {}
 
-    constexpr virtual const char*
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char*
     what() const _GLIBCXX_TXN_SAFE_DYN noexcept
     {
       return _M_msg.c_str();
@@ -495,15 +521,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit overflow_error(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit overflow_error(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : runtime_error(__arg) { }
-    constexpr explicit overflow_error(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit overflow_error(const char* __arg)
+      _GLIBCXX_TXN_SAFE
     : runtime_error(__arg) { }
     constexpr overflow_error(const overflow_error&) = default;
     constexpr overflow_error& operator=(const overflow_error&) = default;
     constexpr overflow_error(overflow_error&&) = default;
     constexpr overflow_error& operator=(overflow_error&&) = default;
-    constexpr virtual ~overflow_error() noexcept { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~overflow_error() noexcept { }
 #else
     explicit overflow_error(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
@@ -522,15 +553,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
   public:
 #if __cpp_lib_constexpr_exceptions >= 202502L
-    constexpr explicit underflow_error(const string& __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit underflow_error(const string& __arg)
+      _GLIBCXX_TXN_SAFE
     : runtime_error(__arg) { }
-    constexpr explicit underflow_error(const char* __arg) _GLIBCXX_TXN_SAFE
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline explicit underflow_error(const char* __arg)
+      _GLIBCXX_TXN_SAFE
     : runtime_error(__arg) { }
     constexpr underflow_error(const underflow_error&) = default;
     constexpr underflow_error& operator=(const underflow_error&) = default;
     constexpr underflow_error(underflow_error&&) = default;
     constexpr underflow_error& operator=(underflow_error&&) = default;
-    constexpr virtual ~underflow_error() noexcept { }
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~underflow_error() noexcept { }
 #else
     explicit underflow_error(const string& __arg) _GLIBCXX_TXN_SAFE;
 #if __cplusplus >= 201103L
diff --git a/libstdc++-v3/libsupc++/exception b/libstdc++-v3/libsupc++/exception
index 5b402a3209cf..0ae01be25dd5 100644
--- a/libstdc++-v3/libsupc++/exception
+++ b/libstdc++-v3/libsupc++/exception
@@ -61,9 +61,12 @@ namespace std _GLIBCXX_VISIBILITY(default)
     _GLIBCXX26_CONSTEXPR bad_exception() _GLIBCXX_USE_NOEXCEPT { }
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~bad_exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
 
-    constexpr virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN noexcept
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char* what() const _GLIBCXX_TXN_SAFE_DYN
+      noexcept
     {
       return "std::bad_exception";
     }
diff --git a/libstdc++-v3/libsupc++/exception.h 
b/libstdc++-v3/libsupc++/exception.h
index 30928eef7e8f..09a76ed62d8e 100644
--- a/libstdc++-v3/libsupc++/exception.h
+++ b/libstdc++-v3/libsupc++/exception.h
@@ -63,7 +63,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
   public:
     _GLIBCXX26_CONSTEXPR exception() _GLIBCXX_NOTHROW { }
 #if __cplusplus >= 202400L
-    constexpr virtual ~exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~exception() _GLIBCXX_TXN_SAFE_DYN noexcept {}
 #else
     virtual ~exception() _GLIBCXX_TXN_SAFE_DYN _GLIBCXX_NOTHROW;
 #endif
@@ -77,7 +78,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
     /** Returns a C-style character string describing the general cause
      *  of the current error.  */
 #if __cplusplus >= 202400L
-    constexpr virtual const char*
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char*
     what() const _GLIBCXX_TXN_SAFE_DYN noexcept { return "std::exception"; }
 #else
     virtual const char*
diff --git a/libstdc++-v3/libsupc++/exception_ptr.h 
b/libstdc++-v3/libsupc++/exception_ptr.h
index d31475a9eb62..b7a2304e121c 100644
--- a/libstdc++-v3/libsupc++/exception_ptr.h
+++ b/libstdc++-v3/libsupc++/exception_ptr.h
@@ -107,7 +107,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
       void* _M_exception_object;
 
 #if __cplusplus >= 202400L
-      constexpr explicit exception_ptr(void* __e) noexcept
+      [[__gnu__::__gnu_inline__]]
+      constexpr inline explicit exception_ptr(void* __e) noexcept
       : _M_exception_object(__e)
       {
        if (_M_exception_object)
diff --git a/libstdc++-v3/libsupc++/nested_exception.h 
b/libstdc++-v3/libsupc++/nested_exception.h
index f4915e0fff3f..132f51dde60a 100644
--- a/libstdc++-v3/libsupc++/nested_exception.h
+++ b/libstdc++-v3/libsupc++/nested_exception.h
@@ -72,7 +72,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
     nested_exception& operator=(const nested_exception&) noexcept = default;
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~nested_exception() noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~nested_exception() noexcept {}
 #else
     virtual ~nested_exception() noexcept;
 #endif
diff --git a/libstdc++-v3/libsupc++/typeinfo b/libstdc++-v3/libsupc++/typeinfo
index 11de3ca4fa68..57f5f2d4c1df 100644
--- a/libstdc++-v3/libsupc++/typeinfo
+++ b/libstdc++-v3/libsupc++/typeinfo
@@ -227,9 +227,11 @@ namespace std
     _GLIBCXX26_CONSTEXPR bad_cast() _GLIBCXX_USE_NOEXCEPT { }
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~bad_cast() noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~bad_cast() noexcept {}
 
-    constexpr virtual const char* what() const noexcept
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char* what() const noexcept
     {
       return "std::bad_cast";
     }
@@ -253,9 +255,11 @@ namespace std
     _GLIBCXX26_CONSTEXPR bad_typeid () _GLIBCXX_USE_NOEXCEPT { }
 
 #if __cplusplus >= 202400L
-    constexpr virtual ~bad_typeid() noexcept {}
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual ~bad_typeid() noexcept {}
 
-    constexpr virtual const char* what() const noexcept
+    [[__gnu__::__gnu_inline__]]
+    constexpr inline virtual const char* what() const noexcept
     {
       return "std::bad_typeid";
     }

Reply via email to