[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