[PATCH 2/2] libstdc++: Return optional<const _Ex&> from exception_ptr_cast.

Jonathan Wakely jwakely@redhat.com
Fri Jul 3 11:48:37 GMT 2026


On Thu, 02 Jul 2026 at 14:22 +0200, Tomasz Kamiński wrote:
>This implements the remaining part of P3981R2 Better return types
>in std::inplace_vector and std::exception_ptr_cast.
>
>For the following functions that are defined out of line in
><optional> header, that header must be included before their
>use:
>* value - throw bad_optional_access, and would cause cyclic include
>  of <exception>,
>* transform, and_then - couldn't be used with function that
>  returns value (wrapped) in optional, as they would instantiate
>  primary specialization
>* or_else - reintroduce dependency of bits/invoke.h
>* begin/end - dependency or normal_iterator from bits/stl_iterator.h.
>
>The value_or was also defined out of line for consistency, I think
>it would be confisuing if calling value/transform requires <optional>
>include, but not value_or.
>
>This patch also introduce _S_from_ptr function to optional<T&>, that
>allows it to be constructed from pointer, without need to check for
>null. This function is public, as I believe it will be usefull in more
>places.
>
>libstdc++-v3/ChangeLog:
>
>	* include/bits/optional_ref.h (optional<_Tp&>::_S_from_ptr):
>	Define.
>	* include/bits/version.def (exception_ptr_cast): Bump value
>	to 202603.
>	* include/bits/version.h: Regenerate.
>	* libsupc++/exception_ptr.h (exception_ptr_cast)
>	[__cpp_lib_exception_ptr_cast >= 202603L]: Change return
>	type to optional<const _Ex&>.
>	* testsuite/18_support/exception_ptr/exception_ptr_cast.cc:
>	Modify to handle change in the return type, and add test
>	for type convertible to optional to reference to that value.
>---
>Testing on x86_64-linux. All test passed locally.
>OK for trunk when exteded suite happens?
>
> libstdc++-v3/include/bits/optional_ref.h      |  8 ++++
> libstdc++-v3/include/bits/version.def         |  4 +-
> libstdc++-v3/include/bits/version.h           |  4 +-
> libstdc++-v3/libsupc++/exception_ptr.h        | 21 +++++----
> .../exception_ptr/exception_ptr_cast.cc       | 47 ++++++++++++++-----
> 5 files changed, 60 insertions(+), 24 deletions(-)
>
>diff --git a/libstdc++-v3/include/bits/optional_ref.h b/libstdc++-v3/include/bits/optional_ref.h
>index e018a4199e9..bc01d271a9a 100644
>--- a/libstdc++-v3/include/bits/optional_ref.h
>+++ b/libstdc++-v3/include/bits/optional_ref.h
>@@ -132,6 +132,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     public:
>       using value_type = _Tp;
>
>+      constexpr static optional
>+      _S_from_ptr(_Tp* __ptr)

I don't love this being a public member, but OK for trunk.


>+      {
>+	optional __res;
>+	__res._M_val = __ptr;
>+	return __res;
>+      }
>+
>       // Constructors.
>       constexpr optional() noexcept = default;
>       constexpr optional(nullopt_t) noexcept : optional() {}
>diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
>index cb48380e2fa..f47df2a0fb8 100644
>--- a/libstdc++-v3/include/bits/version.def
>+++ b/libstdc++-v3/include/bits/version.def
>@@ -2339,9 +2339,11 @@ ftms = {
> };
>
> ftms = {
>+  // 202506 P2927R3 Observing exceptions stored in exception_ptr
>+  // 202603 P3981R2 Better return types in std::inplace_vector and std::exception_ptr_cast
>   name = exception_ptr_cast;
>   values = {
>-    v = 202506;
>+    v = 202603;
>     cxxmin = 26;
>   };
> };
>diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
>index c20c3d18535..05ded7cd7bd 100644
>--- a/libstdc++-v3/include/bits/version.h
>+++ b/libstdc++-v3/include/bits/version.h
>@@ -2588,9 +2588,9 @@
>
> #if !defined(__cpp_lib_exception_ptr_cast)
> # if (__cplusplus >  202302L)
>-#  define __glibcxx_exception_ptr_cast 202506L
>+#  define __glibcxx_exception_ptr_cast 202603L
> #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_exception_ptr_cast)
>-#   define __cpp_lib_exception_ptr_cast 202506L
>+#   define __cpp_lib_exception_ptr_cast 202603L
> #  endif
> # endif
> #endif /* !defined(__cpp_lib_exception_ptr_cast) */
>diff --git a/libstdc++-v3/libsupc++/exception_ptr.h b/libstdc++-v3/libsupc++/exception_ptr.h
>index 61ab0203fa0..81efe49be1f 100644
>--- a/libstdc++-v3/libsupc++/exception_ptr.h
>+++ b/libstdc++-v3/libsupc++/exception_ptr.h
>@@ -41,6 +41,10 @@
> # include <bits/move.h>
> #endif
>
>+#if __cpp_lib_exception_ptr_cast >= 202603L
>+# include <bits/optional_ref.h>
>+#endif
>+
> #ifdef _GLIBCXX_EH_PTR_RELOPS_COMPAT
> # define _GLIBCXX_EH_PTR_USED __attribute__((__used__))
> #else
>@@ -81,9 +85,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
>   /// Throw the object pointed to by the exception_ptr.
>   void rethrow_exception(exception_ptr) __attribute__ ((__noreturn__));
>
>-#if __cpp_lib_exception_ptr_cast >= 202506L
>+#if __cpp_lib_exception_ptr_cast >= 202603L
>   template<typename _Ex>
>-    constexpr const _Ex* exception_ptr_cast(const exception_ptr&) noexcept;
>+    constexpr optional<const _Ex&> exception_ptr_cast(const exception_ptr&) noexcept;
>   template<typename _Ex>
>     void exception_ptr_cast(const exception_ptr&&) = delete;
> #endif
>@@ -139,7 +143,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
> 	_GLIBCXX_USE_NOEXCEPT;
> #if __cpp_lib_exception_ptr_cast >= 202506L
>       template<typename _Ex>
>-	friend constexpr const _Ex*
>+	friend constexpr optional<const _Ex&>
> 	std::exception_ptr_cast(const exception_ptr&) noexcept;
> #endif
>
>@@ -352,10 +356,10 @@ namespace std _GLIBCXX_VISIBILITY(default)
>       return exception_ptr();
>     }
>
>-#if __cpp_lib_exception_ptr_cast >= 202506L
>+#if __cpp_lib_exception_ptr_cast >= 202603L
>   template<typename _Ex>
>     [[__gnu__::__always_inline__]]
>-    constexpr const _Ex*
>+    constexpr optional<const _Ex&>
>     exception_ptr_cast(const exception_ptr& __p) noexcept
>     {
>       static_assert(!std::is_const_v<_Ex>);
>@@ -369,7 +373,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
>       // For runtime calls with -frtti enabled we can avoid try-catch overhead.
>       if ! consteval {
> 	const type_info &__id = typeid(const _Ex&);
>-	return static_cast<const _Ex*>(__p._M_exception_ptr_cast(__id));
>+	const _Ex* __exp = static_cast<const _Ex*>(__p._M_exception_ptr_cast(__id));
>+	return optional<const _Ex&>::_S_from_ptr(__exp);
>       }
> #endif
>
>@@ -381,14 +386,14 @@ namespace std _GLIBCXX_VISIBILITY(default)
> 	  }
> 	catch (const _Ex& __exc)
> 	  {
>-	    return &__exc;
>+	    return optional<const _Ex&>(std::in_place, __exc);
> 	  }
> 	catch (...)
> 	  {
> 	  }
> #endif
>
>-      return nullptr;
>+      return std::nullopt;
>     }
> #endif
>
>diff --git a/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc b/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc
>index 3c40bef36a4..1b980a32362 100644
>--- a/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc
>+++ b/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc
>@@ -22,8 +22,8 @@
> #include <exception>
> #include <testsuite_hooks.h>
>
>-#if __cpp_lib_exception_ptr_cast != 202506L
>-# error "__cpp_lib_exception_ptr_cast != 202506"
>+#if __cpp_lib_exception_ptr_cast != 202603L
>+# error "__cpp_lib_exception_ptr_cast != 202606"
> #endif
>
> struct A { int a; };
>@@ -36,22 +36,33 @@ struct G : virtual F, virtual C, virtual E {
>   constexpr G () : g (4) { a = 1; e = 2; f = 3; } int g;
> };
>
>+// Convertible to optional<const Y&>
>+struct Y
>+{
>+  constexpr operator std::optional<const Y&>() const
>+  { return std::nullopt; }
>+};
>+
> constexpr bool test01(bool x)
> {
>   auto a = std::make_exception_ptr(C{ 42 });
>   auto b = std::exception_ptr_cast<C>(a);
>-  VERIFY( b != nullptr );
>+  auto bp = &*b;
>+  VERIFY( b );
>   VERIFY( b->a == 42 );
>   auto c = std::exception_ptr_cast<B>(a);
>-  VERIFY( c == static_cast<const B*>(b) );
>+  VERIFY( c );
>+  VERIFY( &*c == static_cast<const B*>(bp) );
>   auto d = std::exception_ptr_cast<A>(a);
>-  VERIFY( d == static_cast<const A*>(b) );
>+  VERIFY( d );
>+  VERIFY( &*d == static_cast<const A*>(bp) );
>   auto e = std::exception_ptr_cast<D>(a);
>-  VERIFY( e == nullptr );
>+  VERIFY( !e );
>   auto f = std::make_exception_ptr(42L);
>   auto g = std::exception_ptr_cast<long>(f);
>-  VERIFY( g != nullptr );
>+  VERIFY( g );
>   VERIFY( *g == 42L );
>+
>   try
>     {
>       throw G ();
>@@ -64,19 +75,29 @@ constexpr bool test01(bool x)
>       auto h = std::current_exception();
> #endif
>       auto i = std::exception_ptr_cast<G>(h);
>-      VERIFY( i != nullptr );
>+      VERIFY( i );
>       VERIFY( i->a == 1 && i->e == 2 && i->f == 3 && i->g == 4 );
>+      auto ip = &*i;
>       auto j = std::exception_ptr_cast<A>(h);
>-      VERIFY( j == static_cast<const A*>(i) );
>+      VERIFY( j );
>+      VERIFY( &*j == static_cast<const A*>(ip) );
>       auto k = std::exception_ptr_cast<C>(h);
>-      VERIFY( k == static_cast<const C*>(i) );
>+      VERIFY( k );
>+      VERIFY( &*k == static_cast<const C*>(ip) );
>       auto l = std::exception_ptr_cast<E>(h);
>-      VERIFY( l == static_cast<const E*>(i) );
>+      VERIFY( l );
>+      VERIFY( &*l == static_cast<const E*>(ip) );
>       auto m = std::exception_ptr_cast<F>(h);
>-      VERIFY( m == static_cast<const F*>(i) );
>+      VERIFY( m );
>+      VERIFY( &*m == static_cast<const F*>(ip) );
>       auto n = std::exception_ptr_cast<G>(a);
>-      VERIFY( n == nullptr );
>+      VERIFY( !n );
>     }
>+
>+  auto o = std::make_exception_ptr(Y{});
>+  auto p = std::exception_ptr_cast<Y>(o);
>+  VERIFY( p );
>+
>   if (x)
>     throw 1;
>   return true;
>-- 
>2.54.0
>
>



More information about the Libstdc++ mailing list