[PATCH] libstdc++: Implement C++26 P3748R0 - Inspecting exception_ptr should be constexpr
Jonathan Wakely
jwakely@redhat.com
Fri Jul 11 11:14:56 GMT 2025
On Fri, 11 Jul 2025 at 12:05, Jakub Jelinek <jakub@redhat.com> wrote:
>
> Hi!
>
> The following patch makes std::exception_ptr_cast constexpr.
> The paper suggests using dynamic_cast, but that does only work for
> polymorphic exceptions, doesn't work if they are scalar or non-polymorphic
> classes.
>
> Furthermore, the patch adds some static_asserts for
> "Mandates: E is a cv-unqualified complete object type. E is not an array type.
> E is not a pointer or pointer-to-member type."
>
> Tested on x86_64-linux, ok for trunk if it passes full bootstrap/regtest?
>
> 2025-07-11 Jakub Jelinek <jakub@redhat.com>
>
> * libsupc++/exception_ptr.h: Implement C++26 P3748R0 - Inspecting
> exception_ptr should be constexpr.
> (std::exception_ptr_cast): Make constexpr, remove inline keyword. Add
> static_asserts for Mandates. For if consteval use std::rethrow_exception,
> catch it and return its address or nullptr.
> * testsuite/18_support/exception_ptr/exception_ptr_cast.cc (E::~E): Add
> constexpr.
> (G::G): Likewise.
> (test01): Likewise. Return bool and take bool argument, throw if the
> argument is true. Add static_assert(test01(false)).
> (main): Call test01(true) in try.
>
> --- libstdc++-v3/libsupc++/exception_ptr.h.jj 2025-07-11 00:40:08.861188412 +0200
> +++ libstdc++-v3/libsupc++/exception_ptr.h 2025-07-11 11:36:01.564267603 +0200
> @@ -83,7 +83,7 @@ namespace std _GLIBCXX_VISIBILITY(defaul
>
> #if __cpp_lib_exception_ptr_cast >= 202506L
> template<typename _Ex>
> - const _Ex* exception_ptr_cast(const exception_ptr&) noexcept;
> + constexpr const _Ex* exception_ptr_cast(const exception_ptr&) noexcept;
> template<typename _Ex>
> void exception_ptr_cast(const exception_ptr&&) = delete;
> #endif
> @@ -138,7 +138,8 @@ namespace std _GLIBCXX_VISIBILITY(defaul
> _GLIBCXX_USE_NOEXCEPT;
> #if __cpp_lib_exception_ptr_cast >= 202506L
> template<typename _Ex>
> - friend const _Ex* std::exception_ptr_cast(const exception_ptr&) noexcept;
> + friend constexpr const _Ex* std::exception_ptr_cast(const exception_ptr&)
> + noexcept;
> #endif
>
> const void* _M_exception_ptr_cast(const type_info&) const
> @@ -352,11 +353,33 @@ namespace std _GLIBCXX_VISIBILITY(defaul
> #if __cpp_lib_exception_ptr_cast >= 202506L
> template<typename _Ex>
> [[__gnu__::__always_inline__]]
> - inline const _Ex* exception_ptr_cast(const exception_ptr& __p) noexcept
> + constexpr const _Ex* exception_ptr_cast(const exception_ptr& __p) noexcept
> {
> + static_assert(!std::is_const_v<_Ex>);
> + static_assert(!std::is_reference_v<_Ex>);
> + static_assert(std::is_object_v<_Ex>);
> + static_assert(!std::is_array_v<_Ex>);
> + static_assert(!std::is_pointer_v<_Ex>);
> + static_assert(!std::is_member_pointer_v<_Ex>);
> #ifdef __cpp_rtti
> - const type_info &__id = typeid(const _Ex&);
> - return static_cast<const _Ex*>(__p._M_exception_ptr_cast(__id));
> + if consteval {
> + if (__p._M_exception_object)
> + try
This won't compile with -fno-exceptions, so maybe we only want it to
be usable in constexpr when __cpp_exceptions is defined?
And so maybe the feature test macro should be defined something like:
ftms = {
name = exception_ptr_cast;
values = {
v = 202506;
cxxmin = 26;
extra_cond = "__cpp_exceptions";
};
values = {
v = 1;
cxxmin = 26;
no_stdname = yes;
};
};
This would give us __glibcxx_exception_ptr_cast for C++26 with or
without exceptions, but would only define __cpp_lib_exception_ptr_cast
when it's fully supported.
Alternatively:
ftms = {
name = exception_ptr_cast;
values = {
v = 202506;
cxxmin = 26;
extra_cond = "__cpp_exceptions";
};
values = {
v = 202500;
cxxmin = 26;
};
};
So we would still define __cpp_lib_exception_ptr_cast for
-fno-exceptions, but with the value 202500 to indicate that it's not
100% conforming.
> + {
> + std::rethrow_exception(__p);
> + }
> + catch (const _Ex& __exc)
> + {
> + return &__exc;
> + }
> + catch (...)
> + {
> + }
> + return nullptr;
> + } else {
> + const type_info &__id = typeid(const _Ex&);
> + return static_cast<const _Ex*>(__p._M_exception_ptr_cast(__id));
> + }
> #else
> return nullptr;
> #endif
> --- libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc.jj 2025-06-26 16:31:04.958266404 +0200
> +++ libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc 2025-07-11 11:39:39.275381406 +0200
> @@ -30,13 +30,13 @@ struct A { int a; };
> struct B : A {};
> struct C : B {};
> struct D {};
> -struct E : virtual C { int e; virtual ~E () {} };
> +struct E : virtual C { int e; constexpr virtual ~E () {} };
> struct F : virtual E, virtual C { int f; };
> struct G : virtual F, virtual C, virtual E {
> - G () : g (4) { a = 1; e = 2; f = 3; } int g;
> + constexpr G () : g (4) { a = 1; e = 2; f = 3; } int g;
> };
>
> -void test01()
> +constexpr bool test01(bool x)
> {
> auto a = std::make_exception_ptr(C{ 42 });
> auto b = std::exception_ptr_cast<C>(a);
> @@ -73,9 +73,20 @@ void test01()
> auto n = std::exception_ptr_cast<G>(a);
> VERIFY( n == nullptr );
> }
> + if (x)
> + throw 1;
> + return true;
> }
>
> +static_assert(test01(false));
> +
> int main()
> {
> - test01();
> + try
> + {
> + test01(true);
> + }
> + catch (...)
> + {
> + }
> }
>
> Jakub
>
More information about the Libstdc++
mailing list