[PATCH] libstdc++: Implement C++26 P3748R0 - Inspecting exception_ptr should be constexpr
Jonathan Wakely
jwakely@redhat.com
Fri Jul 11 11:16:17 GMT 2025
On Fri, 11 Jul 2025 at 12:14, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> 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
This would need to be adjusted for my suggestions, of course.
Maybe to just #ifdef __glibcxx_exception_ptr_cast
> > 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