[committed] libstdc++: Define std::expected for C++23 (P0323R12)

Message ID 20220327175819.1422085-1-jwakely@redhat.com
State Committed
Headers
Series [committed] libstdc++: Define std::expected for C++23 (P0323R12) |

Commit Message

Jonathan Wakely March 27, 2022, 5:58 p.m. UTC
  Tested powerpc64le-linux, pushed to trunk.

It's late in stage 4 to be adding new features, but this is C++23-only
so entirely experimental, and not defined for the std-gnu++17 default
(or even -std=gnu++20). It does mean that the old (very old)
std::unexpected function is no longer available in C++23 mode, but it's
not even been part of the standard since C++17, and carries the
deprecated attribute in libstdc++, so I don't see any problem with
making the change for C++23. We might even want to consider removing it
for C++20 so there's a "gap" between the old meaning of the name as a
function, and the new meaning as a class template.

-- >8 --

Because this adds a new class template called std::unexpected, we have
to stop declaring the std::unexpected() function (which was deprecated
in C++11 and removed in C++17).

libstdc++-v3/ChangeLog:

	* doc/doxygen/user.cfg.in: Add new header.
	* include/Makefile.am: Likewise.
	* include/Makefile.in: Regenerate.
	* include/precompiled/stdc++.h: Add new header.
	* include/std/version (__cpp_lib_expected): Define.
	* libsupc++/exception [__cplusplus > 202002] (unexpected)
	(unexpected_handler, set_unexpected): Do not declare for C++23.
	* include/std/expected: New file.
	* testsuite/20_util/expected/assign.cc: New test.
	* testsuite/20_util/expected/cons.cc: New test.
	* testsuite/20_util/expected/illformed_neg.cc: New test.
	* testsuite/20_util/expected/observers.cc: New test.
	* testsuite/20_util/expected/requirements.cc: New test.
	* testsuite/20_util/expected/swap.cc: New test.
	* testsuite/20_util/expected/synopsis.cc: New test.
	* testsuite/20_util/expected/unexpected.cc: New test.
	* testsuite/20_util/expected/version.cc: New test.
---
 libstdc++-v3/doc/doxygen/user.cfg.in          |    1 +
 libstdc++-v3/include/Makefile.am              |    1 +
 libstdc++-v3/include/Makefile.in              |    1 +
 libstdc++-v3/include/precompiled/stdc++.h     |    1 +
 libstdc++-v3/include/std/expected             | 1240 +++++++++++++++++
 libstdc++-v3/include/std/version              |    1 +
 libstdc++-v3/libsupc++/exception              |    2 +-
 .../testsuite/20_util/expected/assign.cc      |  321 +++++
 .../testsuite/20_util/expected/cons.cc        |  175 +++
 .../20_util/expected/illformed_neg.cc         |   67 +
 .../testsuite/20_util/expected/observers.cc   |  209 +++
 .../20_util/expected/requirements.cc          |  129 ++
 .../testsuite/20_util/expected/swap.cc        |   57 +
 .../testsuite/20_util/expected/synopsis.cc    |   21 +
 .../testsuite/20_util/expected/unexpected.cc  |   80 ++
 .../testsuite/20_util/expected/version.cc     |   10 +
 16 files changed, 2315 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/include/std/expected
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/assign.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/cons.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/observers.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/requirements.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/swap.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/synopsis.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/unexpected.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/version.cc
  

Patch

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 2f15f2c1b82..85955f88390 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -858,6 +858,7 @@  INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/concepts \
                          include/condition_variable \
                          include/deque \
+                         include/expected \
                          include/filesystem \
                          include/forward_list \
                          include/fstream \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 43f7f9f240d..77eea7d61e8 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -42,6 +42,7 @@  std_headers = \
 	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
+	${std_srcdir}/expected \
 	${std_srcdir}/filesystem \
 	${std_srcdir}/forward_list \
 	${std_srcdir}/fstream \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 88391a44d33..01bf3e0eb32 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -400,6 +400,7 @@  std_headers = \
 	${std_srcdir}/coroutine \
 	${std_srcdir}/deque \
 	${std_srcdir}/execution \
+	${std_srcdir}/expected \
 	${std_srcdir}/filesystem \
 	${std_srcdir}/forward_list \
 	${std_srcdir}/fstream \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index dc94a9c471f..6d6d2ad7c4c 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -153,5 +153,6 @@ 
 #endif
 
 #if __cplusplus > 202002L
+#include <expected>
 #include <spanstream>
 #endif
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
new file mode 100644
index 00000000000..39d07cda4a9
--- /dev/null
+++ b/libstdc++-v3/include/std/expected
@@ -0,0 +1,1240 @@ 
+// <expected> -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/expected
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_EXPECTED
+#define _GLIBCXX_EXPECTED
+
+#pragma GCC system_header
+
+#if __cplusplus > 202002L && __cpp_concepts >= 202002L
+
+#include <initializer_list>
+#include <bits/exception.h>	// exception
+#include <bits/stl_construct.h>	// construct_at
+#include <bits/utility.h>	// in_place_t
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  /**
+   * @defgroup expected_values Expected values
+   * @addtogroup utilities
+   * @since C++23
+   * @{
+   */
+
+#define __cpp_lib_expected 202202L
+
+  /// Discriminated union that holds an expected value or an error value.
+  /**
+   * @since C++23
+   */
+  template<typename _Tp, typename _Er>
+    class expected;
+
+  /// Wrapper type used to pass an error value to a `std::expected`.
+  /**
+   * @since C++23
+   */
+  template<typename _Er>
+    class unexpected;
+
+  /// Exception thrown by std::expected when the value() is not present.
+  /**
+   * @since C++23
+   */
+  template<typename _Er>
+    class bad_expected_access;
+
+  template<>
+    class bad_expected_access<void> : public exception
+    {
+    protected:
+      bad_expected_access() noexcept { }
+      bad_expected_access(const bad_expected_access&) = default;
+      bad_expected_access(bad_expected_access&&) = default;
+      bad_expected_access& operator=(const bad_expected_access&) = default;
+      bad_expected_access& operator=(bad_expected_access&&) = default;
+      ~bad_expected_access() = default;
+
+    public:
+
+      [[nodiscard]]
+      const char*
+      what() const noexcept override
+      { return "bad access to std::expected without expected value"; }
+    };
+
+  template<typename _Er>
+    class bad_expected_access : public bad_expected_access<void> {
+    public:
+      explicit
+      bad_expected_access(_Er __e) : _M_val(__e) { }
+
+      // XXX const char* what() const noexcept override;
+
+      [[nodiscard]]
+      _Er&
+      error() & noexcept
+      { return _M_val; }
+
+      [[nodiscard]]
+      const _Er&
+      error() const & noexcept
+      { return _M_val; }
+
+      [[nodiscard]]
+      _Er&&
+      error() && noexcept
+      { return std::move(_M_val); }
+
+      [[nodiscard]]
+      const _Er&&
+      error() const && noexcept
+      { return std::move(_M_val); }
+
+    private:
+      _Er _M_val;
+    };
+
+  /// Tag type for constructing unexpected values in a std::expected
+  /**
+   * @since C++23
+   */
+  struct unexpect_t
+  {
+    explicit unexpect_t() = default;
+  };
+
+  /// Tag for constructing unexpected values in a std::expected
+  /**
+   * @since C++23
+   */
+  inline constexpr unexpect_t unexpect{};
+
+/// @cond undoc
+namespace __expected
+{
+  template<typename _Tp>
+    constexpr bool __is_expected = false;
+  template<typename _Tp, typename _Er>
+    constexpr bool __is_expected<expected<_Tp, _Er>> = true;
+
+  template<typename _Tp>
+    constexpr bool __is_unexpected = false;
+  template<typename _Tp>
+    constexpr bool __is_unexpected<unexpected<_Tp>> = true;
+
+  template<typename _Er>
+    concept __can_be_unexpected
+      = is_object_v<_Er> && (!is_array_v<_Er>)
+	  && (!__expected::__is_unexpected<_Er>)
+	  && (!is_const_v<_Er>) && (!is_volatile_v<_Er>);
+}
+/// @endcond
+
+  template<typename _Er>
+    class unexpected
+    {
+      static_assert( __expected::__can_be_unexpected<_Er> );
+
+    public:
+      constexpr unexpected(const unexpected&) = default;
+      constexpr unexpected(unexpected&&) = default;
+
+      template<typename _Err = _Er>
+	requires (!is_same_v<remove_cvref_t<_Err>, unexpected>)
+	  && (!is_same_v<remove_cvref_t<_Err>, in_place_t>)
+	  && is_constructible_v<_Er, _Err>
+	constexpr explicit
+	unexpected(_Err&& __e)
+	noexcept(is_nothrow_constructible_v<_Er, _Err>)
+	: _M_val(std::forward<_Err>(__e))
+	{ }
+
+      template<typename... _Args>
+	requires is_constructible_v<_Er, _Args...>
+	constexpr explicit
+	unexpected(in_place_t, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, _Args...>)
+	: _M_val(std::forward<_Args>(__args)...)
+	{ }
+
+      template<typename _Up, typename... _Args>
+	requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
+	constexpr explicit
+	unexpected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
+					    _Args...>)
+	: _M_val(__il, std::forward<_Args>(__args)...)
+	{ }
+
+      constexpr unexpected& operator=(const unexpected&) = default;
+      constexpr unexpected& operator=(unexpected&&) = default;
+
+
+      [[nodiscard]]
+      constexpr const _Er&
+      error() const & noexcept { return _M_val; }
+
+      [[nodiscard]]
+      constexpr _Er&
+      error() & noexcept { return _M_val; }
+
+      [[nodiscard]]
+      constexpr const _Er&&
+      error() const && noexcept { return std::move(_M_val); }
+
+      [[nodiscard]]
+      constexpr _Er&&
+      error() && noexcept { return std::move(_M_val); }
+
+      constexpr void
+      swap(unexpected& __other) noexcept(is_nothrow_swappable_v<_Er>)
+      {
+	static_assert( is_swappable_v<_Er> );
+	using std::swap;
+	swap(_M_val, __other._M_val);
+      }
+
+      template<typename _Err>
+	[[nodiscard]]
+	friend constexpr bool
+	operator==(const unexpected& __x, const unexpected<_Err>& __y)
+	{ return __x._M_val == __y.error(); }
+
+      friend constexpr void
+      swap(unexpected& __x, unexpected& __y)
+      noexcept(noexcept(__x.swap(__y)))
+      requires requires {__x.swap(__y);}
+      { __x.swap(__y); }
+
+    private:
+      _Er _M_val;
+    };
+
+  template<typename _Er> unexpected(_Er) -> unexpected<_Er>;
+
+/// @cond undoc
+namespace __expected
+{
+  template<typename _Tp>
+    struct _Guard
+    {
+      static_assert( is_nothrow_move_constructible_v<_Tp> );
+
+      constexpr explicit
+      _Guard(_Tp& __x)
+      : _M_guarded(__builtin_addressof(__x)), _M_tmp(std::move(__x)) // nothrow
+      { std::destroy_at(_M_guarded); }
+
+      constexpr
+      ~_Guard()
+      {
+	if (_M_guarded) [[unlikely]]
+	  std::construct_at(_M_guarded, std::move(_M_tmp));
+      }
+
+      _Guard(const _Guard&) = delete;
+      _Guard& operator=(const _Guard&) = delete;
+
+      constexpr _Tp&&
+      release() noexcept
+      {
+	_M_guarded = nullptr;
+	return std::move(_M_tmp);
+      }
+
+    private:
+      _Tp* _M_guarded;
+      _Tp _M_tmp;
+    };
+
+  // reinit-expected helper from [expected.object.assign]
+  template<typename _Tp, typename _Up, typename _Vp>
+    constexpr void
+    __reinit(_Tp* __newval, _Up* __oldval, _Vp&& __arg)
+    noexcept(is_nothrow_constructible_v<_Tp, _Vp>)
+    {
+      if constexpr (is_nothrow_constructible_v<_Tp, _Vp>)
+	{
+	  std::destroy_at(__oldval);
+	  std::construct_at(__newval, std::forward<_Vp>(__arg));
+	}
+      else if constexpr (is_nothrow_move_constructible_v<_Tp>)
+	{
+	  _Tp __tmp(std::forward<_Vp>(__arg)); // might throw
+	  std::destroy_at(__oldval);
+	  std::construct_at(__newval, std::move(__tmp));
+	}
+      else
+	{
+	  _Guard<_Up> __guard(*__oldval);
+	  std::construct_at(__newval, std::forward<_Vp>(__arg)); // might throw
+	  __guard.release();
+	}
+    }
+}
+/// @endcond
+
+  template<typename _Tp, typename _Er>
+    class expected
+    {
+      static_assert( ! is_reference_v<_Tp> );
+      static_assert( ! is_function_v<_Tp> );
+      static_assert( ! is_same_v<remove_cv_t<_Tp>, in_place_t> );
+      static_assert( ! is_same_v<remove_cv_t<_Tp>, unexpect_t> );
+      static_assert( ! __expected::__is_unexpected<remove_cv_t<_Tp>> );
+      static_assert( __expected::__can_be_unexpected<_Er> );
+
+      template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
+	static constexpr bool __cons_from_expected
+	  = __or_v<is_constructible<_Tp, expected<_Up, _Err>&>,
+		   is_constructible<_Tp, expected<_Up, _Err>>,
+		   is_constructible<_Tp, const expected<_Up, _Err>&>,
+		   is_constructible<_Tp, const expected<_Up, _Err>>,
+		   is_convertible<expected<_Up, _Err>&, _Tp>,
+		   is_convertible<expected<_Up, _Err>, _Tp>,
+		   is_convertible<const expected<_Up, _Err>&, _Tp>,
+		   is_convertible<const expected<_Up, _Err>, _Tp>,
+		   is_constructible<_Unex, expected<_Up, _Err>&>,
+		   is_constructible<_Unex, expected<_Up, _Err>>,
+		   is_constructible<_Unex, const expected<_Up, _Err>&>,
+		   is_constructible<_Unex, const expected<_Up, _Err>>
+		  >;
+
+      template<typename _Up, typename _Err>
+	constexpr static bool __explicit_conv
+	  = __or_v<__not_<is_convertible<_Up, _Tp>>,
+		   __not_<is_convertible<_Err, _Er>>
+		  >;
+
+    public:
+      using value_type = _Tp;
+      using error_type = _Er;
+      using unexpected_type = unexpected<_Er>;
+
+      template<typename _Up>
+	using rebind = expected<_Up, error_type>;
+
+      constexpr
+      expected()
+      noexcept(is_nothrow_default_constructible_v<_Tp>)
+      requires is_default_constructible_v<_Tp>
+      : _M_val(), _M_has_value(true)
+      { }
+
+      expected(const expected&) = default;
+
+      constexpr
+      expected(const expected& __x)
+      noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
+		       is_nothrow_copy_constructible<_Er>>)
+      requires is_copy_constructible_v<_Tp> && is_copy_constructible_v<_Er>
+      && (!is_trivially_copy_constructible_v<_Tp>
+	  || !is_trivially_copy_constructible_v<_Er>)
+      : _M_invalid(), _M_has_value(__x._M_has_value)
+      {
+	if (_M_has_value)
+	  std::construct_at(__builtin_addressof(_M_val), __x._M_val);
+	else
+	  std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
+      }
+
+      expected(expected&&) = default;
+
+      constexpr
+      expected(expected&& __x)
+      noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
+		       is_nothrow_move_constructible<_Er>>)
+      requires is_move_constructible_v<_Tp> && is_move_constructible_v<_Er>
+      && (!is_trivially_move_constructible_v<_Tp>
+	  || !is_trivially_move_constructible_v<_Er>)
+      : _M_invalid(), _M_has_value(__x._M_has_value)
+      {
+	if (_M_has_value)
+	  std::construct_at(__builtin_addressof(_M_val),
+			    std::move(__x)._M_val);
+	else
+	  std::construct_at(__builtin_addressof(_M_unex),
+			    std::move(__x)._M_unex);
+      }
+
+      template<typename _Up, typename _Gr>
+	requires is_constructible_v<_Tp, const _Up&>
+	      && is_constructible_v<_Er, const _Gr&>
+	      && (!__cons_from_expected<_Up, _Gr>)
+	constexpr explicit(__explicit_conv<const _Up&, const _Gr&>)
+	expected(const expected<_Up, _Gr>& __x)
+	noexcept(__and_v<is_nothrow_constructible<_Tp, const _Up&>,
+			 is_nothrow_constructible<_Er, const _Gr&>>)
+	: _M_invalid(), _M_has_value(__x._M_has_value)
+	{
+	  if (_M_has_value)
+	    std::construct_at(__builtin_addressof(_M_val), __x._M_val);
+	  else
+	    std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
+	}
+
+      template<typename _Up, typename _Gr>
+	requires is_constructible_v<_Tp, _Up>
+	      && is_constructible_v<_Er, _Gr>
+	      && (!__cons_from_expected<_Up, _Gr>)
+	constexpr explicit(__explicit_conv<_Up, _Gr>)
+	expected(expected<_Up, _Gr>&& __x)
+	noexcept(__and_v<is_nothrow_constructible<_Tp, _Up>,
+			 is_nothrow_constructible<_Er, _Gr>>)
+	: _M_invalid(), _M_has_value(__x._M_has_value)
+	{
+	  if (_M_has_value)
+	    std::construct_at(__builtin_addressof(_M_val),
+			      std::move(__x)._M_val);
+	  else
+	    std::construct_at(__builtin_addressof(_M_unex),
+			      std::move(__x)._M_unex);
+	}
+
+      template<typename _Up = _Tp>
+	requires (!is_same_v<remove_cvref_t<_Up>, expected>)
+	  && (!is_same_v<remove_cvref_t<_Up>, in_place_t>)
+	  && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
+	  && is_constructible_v<_Tp, _Up>
+	constexpr explicit(!is_convertible_v<_Up, _Tp>)
+	expected(_Up&& __v)
+	noexcept(is_nothrow_constructible_v<_Tp, _Up>)
+	: _M_val(std::forward<_Up>(__v)), _M_has_value(true)
+	{ }
+
+      template<typename _Gr = _Er>
+	requires is_constructible_v<_Er, const _Gr&>
+	constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
+	expected(const unexpected<_Gr>& __u)
+	noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
+	: _M_unex(__u.error()), _M_has_value(false)
+	{ }
+
+      template<typename _Gr = _Er>
+	requires is_constructible_v<_Er, _Gr>
+	constexpr explicit(!is_convertible_v<_Gr, _Er>)
+	expected(unexpected<_Gr>&& __u)
+	noexcept(is_nothrow_constructible_v<_Er, _Gr>)
+	: _M_unex(std::move(__u).error()), _M_has_value(false)
+	{ }
+
+      template<typename... _Args>
+	requires is_constructible_v<_Tp, _Args...>
+	constexpr explicit
+	expected(in_place_t, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
+	: _M_val(std::forward<_Args>(__args)...), _M_has_value(true)
+	{ }
+
+      template<typename _Up, typename... _Args>
+	requires is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
+	constexpr explicit
+	expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
+					    _Args...>)
+	: _M_val(__il, std::forward<_Args>(__args)...), _M_has_value(true)
+	{ }
+
+      template<typename... _Args>
+	requires is_constructible_v<_Er, _Args...>
+	constexpr explicit
+	expected(unexpect_t, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, _Args...>)
+	: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
+	{ }
+
+      template<typename _Up, typename... _Args>
+	requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
+	constexpr explicit
+	expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
+					    _Args...>)
+	: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
+	{ }
+
+      constexpr ~expected() = default;
+
+      constexpr ~expected()
+      requires (!is_trivially_destructible_v<_Tp>)
+	    || (!is_trivially_destructible_v<_Er>)
+      {
+	if (_M_has_value)
+	  std::destroy_at(__builtin_addressof(_M_val));
+	else
+	  std::destroy_at(__builtin_addressof(_M_unex));
+      }
+
+      // assignment
+
+      expected& operator=(const expected&) = delete;
+
+      constexpr expected&
+      operator=(const expected& __x)
+      noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
+		       is_nothrow_copy_constructible<_Er>,
+		       is_nothrow_copy_assignable<_Tp>,
+		       is_nothrow_copy_assignable<_Er>>)
+      requires is_copy_assignable_v<_Tp> && is_copy_constructible_v<_Tp>
+	    && is_copy_assignable_v<_Er> && is_copy_constructible_v<_Er>
+	    && (is_nothrow_move_constructible_v<_Tp>
+		|| is_nothrow_move_constructible_v<_Er>)
+      {
+	if (__x._M_has_value)
+	  this->_M_assign_val(__x._M_val);
+	else
+	  this->_M_assign_unex(__x._M_unex);
+	return *this;
+      }
+
+      constexpr expected&
+      operator=(expected&& __x)
+      noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
+		       is_nothrow_move_constructible<_Er>,
+		       is_nothrow_move_assignable<_Tp>,
+		       is_nothrow_move_assignable<_Er>>)
+      requires is_move_assignable_v<_Tp> && is_move_constructible_v<_Tp>
+	    && is_move_assignable_v<_Er> && is_move_constructible_v<_Er>
+	    && (is_nothrow_move_constructible_v<_Tp>
+		|| is_nothrow_move_constructible_v<_Er>)
+      {
+	if (__x._M_has_value)
+	  _M_assign_val(std::move(__x._M_val));
+	else
+	  _M_assign_unex(std::move(__x._M_unex));
+	return *this;
+      }
+
+      template<typename _Up = _Tp>
+	requires (!is_same_v<expected, remove_cvref_t<_Up>>)
+	      && (!__expected::__is_unexpected<remove_cvref_t<_Up>>)
+	      && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up>
+	      && (is_nothrow_constructible_v<_Tp, _Up>
+		  || is_nothrow_move_constructible_v<_Tp>
+		  || is_nothrow_move_constructible_v<_Er>)
+	constexpr expected&
+	operator=(_Up&& __v)
+	{
+	  _M_assign_val(std::forward<_Up>(__v));
+	  return *this;
+	}
+
+      template<typename _Gr>
+	requires is_constructible_v<_Er, const _Gr&>
+	      && is_assignable_v<_Er&, const _Gr&>
+	      && (is_nothrow_constructible_v<_Er, const _Gr&>
+		  || is_nothrow_move_constructible_v<_Tp>
+		  || is_nothrow_move_constructible_v<_Er>)
+	constexpr expected&
+	operator=(const unexpected<_Gr>& __e)
+	{
+	  _M_assign_unex(__e.error());
+	  return *this;
+	}
+
+      template<typename _Gr>
+	requires is_constructible_v<_Er, _Gr>
+	      && is_assignable_v<_Er&, _Gr>
+	      && (is_nothrow_constructible_v<_Er, _Gr>
+		  || is_nothrow_move_constructible_v<_Tp>
+		  || is_nothrow_move_constructible_v<_Er>)
+	constexpr expected&
+	operator=(unexpected<_Gr>&& __e)
+	{
+	  _M_assign_unex(std::move(__e).error());
+	  return *this;
+	}
+
+      // modifiers
+
+      template<typename... _Args>
+	requires is_nothrow_constructible_v<_Tp, _Args...>
+	constexpr _Tp&
+	emplace(_Args&&... __args) noexcept
+	{
+	  if (_M_has_value)
+	    std::destroy_at(__builtin_addressof(_M_val));
+	  else
+	    {
+	      std::destroy_at(__builtin_addressof(_M_unex));
+	      _M_has_value = true;
+	    }
+	  std::construct_at(__builtin_addressof(_M_val),
+			    std::forward<_Args>(__args)...);
+	  return _M_val;
+	}
+
+      template<typename _Up, typename... _Args>
+	requires is_nothrow_constructible_v<_Tp, initializer_list<_Up>&,
+					    _Args...>
+	constexpr _Tp&
+	emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept
+	{
+	  if (_M_has_value)
+	    std::destroy_at(__builtin_addressof(_M_val));
+	  else
+	    {
+	      std::destroy_at(__builtin_addressof(_M_unex));
+	      _M_has_value = true;
+	    }
+	  std::construct_at(__builtin_addressof(_M_val),
+			    __il, std::forward<_Args>(__args)...);
+	  return _M_val;
+	}
+
+      // swap
+      constexpr void
+      swap(expected& __x)
+      noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
+		       is_nothrow_move_constructible<_Er>,
+		       is_nothrow_swappable<_Tp&>,
+		       is_nothrow_swappable<_Er&>>)
+      requires is_swappable_v<_Tp> && is_swappable_v<_Er>
+	    && is_move_constructible_v<_Tp>
+	    && is_move_constructible_v<_Er>
+	    && (is_nothrow_move_constructible_v<_Tp>
+		|| is_nothrow_move_constructible_v<_Er>)
+      {
+	if (_M_has_value)
+	  {
+	    if (__x._M_has_value)
+	      {
+		using std::swap;
+		swap(_M_val, __x._M_val);
+	      }
+	    else
+	      this->_M_swap_val_unex(__x);
+	  }
+	else
+	  {
+	    if (__x._M_has_value)
+	      __x._M_swap_val_unex(*this);
+	    else
+	      {
+		using std::swap;
+		swap(_M_unex, __x._M_unex);
+	      }
+	  }
+      }
+
+      // observers
+
+      [[nodiscard]]
+      constexpr const _Tp*
+      operator->() const noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return __builtin_addressof(_M_val);
+      }
+
+      [[nodiscard]]
+      constexpr _Tp*
+      operator->() noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return __builtin_addressof(_M_val);
+      }
+
+      [[nodiscard]]
+      constexpr const _Tp&
+      operator*() const & noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return _M_val;
+      }
+
+      [[nodiscard]]
+      constexpr _Tp&
+      operator*() & noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return _M_val;
+      }
+
+      [[nodiscard]]
+      constexpr const _Tp&&
+      operator*() const && noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return std::move(_M_val);
+      }
+
+      [[nodiscard]]
+      constexpr _Tp&&
+      operator*() && noexcept
+      {
+	__glibcxx_assert(_M_has_value);
+	return std::move(_M_val);
+      }
+
+      [[nodiscard]]
+      constexpr explicit
+      operator bool() const noexcept { return _M_has_value; }
+
+      [[nodiscard]]
+      constexpr bool has_value() const noexcept { return _M_has_value; }
+
+      constexpr const _Tp&
+      value() const &
+      {
+	if (_M_has_value) [[likely]]
+	  return _M_val;
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
+      }
+
+      constexpr _Tp&
+      value() &
+      {
+	if (_M_has_value) [[likely]]
+	  return _M_val;
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
+      }
+
+      constexpr const _Tp&&
+      value() const &&
+      {
+	if (_M_has_value) [[likely]]
+	  return std::move(_M_val);
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
+				  std::move(_M_unex)));
+      }
+
+      constexpr _Tp&&
+      value() &&
+      {
+	if (_M_has_value) [[likely]]
+	  return std::move(_M_val);
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(
+				  std::move(_M_unex)));
+      }
+
+      constexpr const _Er&
+      error() const & noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return _M_unex;
+      }
+
+      constexpr _Er&
+      error() & noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return _M_unex;
+      }
+
+      constexpr const _Er&&
+      error() const && noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return std::move(_M_unex);
+      }
+
+      constexpr _Er&&
+      error() && noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return std::move(_M_unex);
+      }
+
+      template<typename _Up>
+	constexpr _Tp
+	value_or(_Up&& __v) const &
+	noexcept(__and_v<is_nothrow_copy_constructible<_Tp>,
+			 is_nothrow_convertible<_Up, _Tp>>)
+	{
+	  static_assert( is_copy_constructible_v<_Tp> );
+	  static_assert( is_convertible_v<_Up, _Tp> );
+
+	  if (_M_has_value)
+	    return _M_val;
+	  return static_cast<_Tp>(std::forward<_Up>(__v));
+	}
+
+      template<typename _Up>
+	constexpr _Tp
+	value_or(_Up&& __v) &&
+	noexcept(__and_v<is_nothrow_move_constructible<_Tp>,
+			 is_nothrow_convertible<_Up, _Tp>>)
+	{
+	  static_assert( is_move_constructible_v<_Tp> );
+	  static_assert( is_convertible_v<_Up, _Tp> );
+
+	  if (_M_has_value)
+	    return std::move(_M_val);
+	  return static_cast<_Tp>(std::forward<_Up>(__v));
+	}
+
+      // equality operators
+
+      template<typename _Up, typename _Er2>
+	requires (!is_void_v<_Up>)
+	friend constexpr bool
+	operator==(const expected& __x, const expected<_Up, _Er2>& __y)
+	noexcept(noexcept(bool(*__x == *__y))
+		  && noexcept(bool(__x.error() == __y.error())))
+	{
+	  if (__x.has_value())
+	    return __y.has_value() && bool(*__x == *__y);
+	  else
+	    return !__y.has_value() && bool(__x.error() == __y.error());
+	}
+
+      template<typename _Up>
+	friend constexpr bool
+	operator==(const expected& __x, const _Up& __v)
+	noexcept(noexcept(bool(*__x == __v)))
+	{ return __x.has_value() && bool(*__x == __v); }
+
+      template<typename _Er2>
+	friend constexpr bool
+	operator==(const expected& __x, const unexpected<_Er2>& __e)
+	noexcept(noexcept(bool(__x.error() == __e.error())))
+	{ return !__x.has_value() && bool(__x.error() == __e.error()); }
+
+      friend constexpr void
+      swap(expected& __x, expected& __y)
+      noexcept(noexcept(__x.swap(__y)))
+      requires requires {__x.swap(__y);}
+      { __x.swap(__y); }
+
+    private:
+      template<typename, typename> friend class expected;
+
+      template<typename _Vp>
+	constexpr void
+	_M_assign_val(_Vp&& __v)
+	{
+	  if (_M_has_value)
+	    _M_val = std::forward<_Vp>(__v);
+	  else
+	    {
+	      __expected::__reinit(__builtin_addressof(_M_val),
+				   __builtin_addressof(_M_unex),
+				   std::forward<_Vp>(__v));
+	      _M_has_value = true;
+	    }
+	}
+
+      template<typename _Vp>
+	constexpr void
+	_M_assign_unex(_Vp&& __v)
+	{
+	  if (_M_has_value)
+	    {
+	      __expected::__reinit(__builtin_addressof(_M_unex),
+				   __builtin_addressof(_M_val),
+				   std::forward<_Vp>(__v));
+	      _M_has_value = false;
+	    }
+	  else
+	    _M_unex = std::forward<_Vp>(__v);
+	}
+
+      // Swap two expected objects when only one has a value.
+      // Precondition: this->_M_has_value && !__rhs._M_has_value
+      constexpr void
+      _M_swap_val_unex(expected& __rhs)
+      noexcept(__and_v<is_nothrow_move_constructible<_Er>,
+		       is_nothrow_move_constructible<_Tp>>)
+      {
+	if constexpr (is_nothrow_move_constructible_v<_Er>)
+	  {
+	    __expected::_Guard<_Er> __guard(__rhs._M_unex);
+	    std::construct_at(__builtin_addressof(__rhs._M_val),
+			      std::move(_M_val)); // might throw
+	    __rhs._M_has_value = true;
+	    std::destroy_at(__builtin_addressof(_M_val));
+	    std::construct_at(__builtin_addressof(_M_unex),
+			      __guard.release());
+	    _M_has_value = false;
+	  }
+	else
+	  {
+	    __expected::_Guard<_Tp> __guard(__rhs._M_val);
+	    std::construct_at(__builtin_addressof(_M_unex),
+			      std::move(__rhs._M_unex)); // might throw
+	    _M_has_value = false;
+	    std::destroy_at(__builtin_addressof(__rhs._M_unex));
+	    std::construct_at(__builtin_addressof(__rhs._M_val),
+			      __guard.release());
+	    __rhs._M_has_value = true;
+	  }
+      }
+
+      union {
+	struct { } _M_invalid;
+	_Tp _M_val;
+	_Er _M_unex;
+      };
+
+      bool _M_has_value;
+    };
+
+  // Partial specialization for std::expected<cv void, E>
+  template<typename _Tp, typename _Er> requires is_void_v<_Tp>
+    class expected<_Tp, _Er>
+    {
+      static_assert( __expected::__can_be_unexpected<_Er> );
+
+      template<typename _Up, typename _Err, typename _Unex = unexpected<_Er>>
+	static constexpr bool __cons_from_expected
+	  = __or_v<is_constructible<_Unex, expected<_Up, _Err>&>,
+		   is_constructible<_Unex, expected<_Up, _Err>>,
+		   is_constructible<_Unex, const expected<_Up, _Err>&>,
+		   is_constructible<_Unex, const expected<_Up, _Err>>
+		  >;
+
+    public:
+      using value_type = _Tp;
+      using error_type = _Er;
+      using unexpected_type = unexpected<_Er>;
+
+      template<typename _Up>
+	using rebind = expected<_Up, error_type>;
+
+      constexpr
+      expected() noexcept
+      : _M_void(), _M_has_value(true)
+      { }
+
+      expected(const expected&) = default;
+
+      constexpr
+      expected(const expected& __x)
+      noexcept(is_nothrow_copy_constructible_v<_Er>)
+      requires is_copy_constructible_v<_Er>
+	    && (!is_trivially_copy_constructible_v<_Er>)
+      : _M_void(), _M_has_value(__x._M_has_value)
+      {
+	if (!_M_has_value)
+	  std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
+      }
+
+      expected(expected&&) = default;
+
+      constexpr
+      expected(expected&& __x)
+      noexcept(is_nothrow_move_constructible_v<_Er>)
+      requires is_move_constructible_v<_Er>
+	    && (!is_trivially_move_constructible_v<_Er>)
+      : _M_void(), _M_has_value(__x._M_has_value)
+      {
+	if (!_M_has_value)
+	  std::construct_at(__builtin_addressof(_M_unex),
+			    std::move(__x)._M_unex);
+      }
+
+      template<typename _Up, typename _Gr>
+	requires is_void_v<_Up>
+	      && is_constructible_v<_Er, const _Gr&>
+	      && (!__cons_from_expected<_Up, _Gr>)
+	constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
+	expected(const expected<_Up, _Gr>& __x)
+	noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
+	: _M_void(), _M_has_value(__x._M_has_value)
+	{
+	  if (!_M_has_value)
+	    std::construct_at(__builtin_addressof(_M_unex), __x._M_unex);
+	}
+
+      template<typename _Up, typename _Gr>
+	requires is_void_v<_Tp>
+	      && is_constructible_v<_Er, const _Gr&>
+	      && (!__cons_from_expected<_Up, _Gr>)
+	constexpr explicit(!is_convertible_v<_Gr, _Er>)
+	expected(expected<_Up, _Gr>&& __x)
+	noexcept(is_nothrow_constructible_v<_Er, _Gr>)
+	: _M_void(), _M_has_value(__x._M_has_value)
+	{
+	  if (!_M_has_value)
+	    std::construct_at(__builtin_addressof(_M_unex),
+			      std::move(__x)._M_unex);
+	}
+
+      template<typename _Gr = _Er>
+	requires is_constructible_v<_Er, const _Gr&>
+	constexpr explicit(!is_convertible_v<const _Gr&, _Er>)
+	expected(const unexpected<_Gr>& __u)
+	noexcept(is_nothrow_constructible_v<_Er, const _Gr&>)
+	: _M_unex(__u.error()), _M_has_value(false)
+	{ }
+
+      template<typename _Gr = _Er>
+	requires is_constructible_v<_Er, _Gr>
+	constexpr explicit(!is_convertible_v<_Gr, _Er>)
+	expected(unexpected<_Gr>&& __u)
+	noexcept(is_nothrow_constructible_v<_Er, _Gr>)
+	: _M_unex(std::move(__u).error()), _M_has_value(false)
+	{ }
+
+      template<typename... _Args>
+	constexpr explicit
+	expected(in_place_t) noexcept
+	: expected()
+	{ }
+
+      template<typename... _Args>
+	requires is_constructible_v<_Er, _Args...>
+	constexpr explicit
+	expected(unexpect_t, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, _Args...>)
+	: _M_unex(std::forward<_Args>(__args)...), _M_has_value(false)
+	{ }
+
+      template<typename _Up, typename... _Args>
+	requires is_constructible_v<_Er, initializer_list<_Up>&, _Args...>
+	constexpr explicit
+	expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args)
+	noexcept(is_nothrow_constructible_v<_Er, initializer_list<_Up>&,
+					    _Args...>)
+	: _M_unex(__il, std::forward<_Args>(__args)...), _M_has_value(false)
+	{ }
+
+      constexpr ~expected() = default;
+
+      constexpr ~expected() requires (!is_trivially_destructible_v<_Er>)
+      {
+	if (!_M_has_value)
+	  std::destroy_at(__builtin_addressof(_M_unex));
+      }
+
+      // assignment
+
+      expected& operator=(const expected&) = delete;
+
+      constexpr expected&
+      operator=(const expected& __x)
+      noexcept(__and_v<is_nothrow_copy_constructible<_Er>,
+		       is_nothrow_copy_assignable<_Er>>)
+      requires is_copy_constructible_v<_Er>
+	    && is_copy_assignable_v<_Er>
+      {
+	if (__x._M_has_value)
+	  emplace();
+	else
+	  _M_assign_unex(__x._M_unex);
+	return *this;
+      }
+
+      constexpr expected&
+      operator=(expected&& __x)
+      noexcept(__and_v<is_nothrow_move_constructible<_Er>,
+		       is_nothrow_move_assignable<_Er>>)
+      requires is_move_constructible_v<_Er>
+	    && is_move_assignable_v<_Er>
+      {
+	if (__x._M_has_value)
+	  emplace();
+	else
+	  _M_assign_unex(std::move(__x._M_unex));
+	return *this;
+      }
+
+      template<typename _Gr>
+	requires is_constructible_v<_Er, const _Gr&>
+	      && is_assignable_v<_Er&, const _Gr&>
+	constexpr expected&
+	operator=(const unexpected<_Gr>& __e)
+	{
+	  _M_assign_unex(__e.error());
+	  return *this;
+	}
+
+      template<typename _Gr>
+	requires is_constructible_v<_Er, _Gr>
+	      && is_assignable_v<_Er&, _Gr>
+	constexpr expected&
+	operator=(unexpected<_Gr>&& __e)
+	{
+	  _M_assign_unex(std::move(__e.error()));
+	  return *this;
+	}
+
+      // modifiers
+
+      constexpr void
+      emplace() noexcept
+      {
+	if (!_M_has_value)
+	  {
+	    std::destroy_at(__builtin_addressof(_M_unex));
+	    _M_has_value = true;
+	  }
+      }
+
+      // swap
+      constexpr void
+      swap(expected& __x)
+      noexcept(__and_v<is_nothrow_swappable<_Er&>,
+		       is_nothrow_move_constructible<_Er>>)
+      requires is_swappable_v<_Er> && is_move_constructible_v<_Er>
+      {
+	if (_M_has_value)
+	  {
+	    if (!__x._M_has_value)
+	      {
+		std::construct_at(__builtin_addressof(_M_unex),
+				  std::move(__x._M_unex)); // might throw
+		std::destroy_at(__builtin_addressof(__x._M_unex));
+		__x._M_has_value = true;
+	      }
+	  }
+	else
+	  {
+	    if (__x._M_has_value)
+	      {
+		std::construct_at(__builtin_addressof(__x._M_unex),
+				  std::move(_M_unex)); // might throw
+		std::destroy_at(__builtin_addressof(_M_unex));
+		_M_has_value = true;
+	      }
+	    else
+	      {
+		using std::swap;
+		swap(_M_unex, __x._M_unex);
+	      }
+	  }
+      }
+
+      // observers
+
+      [[nodiscard]]
+      constexpr explicit
+      operator bool() const noexcept { return _M_has_value; }
+
+      [[nodiscard]]
+      constexpr bool has_value() const noexcept { return _M_has_value; }
+
+      constexpr void
+      operator*() const noexcept { __glibcxx_assert(_M_has_value); }
+
+      constexpr void
+      value() const&
+      {
+	if (_M_has_value) [[likely]]
+	  return;
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(_M_unex));
+      }
+
+      constexpr void
+      value() &&
+      {
+	if (_M_has_value) [[likely]]
+	  return;
+	_GLIBCXX_THROW_OR_ABORT(bad_expected_access<_Er>(std::move(_M_unex)));
+      }
+
+      constexpr const _Er&
+      error() const & noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return _M_unex;
+      }
+
+      constexpr _Er&
+      error() & noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return _M_unex;
+      }
+
+      constexpr const _Er&&
+      error() const && noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return std::move(_M_unex);
+      }
+
+      constexpr _Er&&
+      error() && noexcept
+      {
+	__glibcxx_assert(!_M_has_value);
+	return std::move(_M_unex);
+      }
+
+      // equality operators
+
+      template<typename _Up, typename _Er2>
+	requires is_void_v<_Up>
+	friend constexpr bool
+	operator==(const expected& __x, const expected<_Up, _Er2>& __y)
+	noexcept(noexcept(bool(__x.error() == __y.error())))
+	{
+	  if (__x.has_value())
+	    return __y.has_value();
+	  else
+	    return !__y.has_value() && bool(__x.error() == __y.error());
+	}
+
+      template<typename _Er2>
+	friend constexpr bool
+	operator==(const expected& __x, const unexpected<_Er2>& __e)
+	noexcept(noexcept(bool(__x.error() == __e.error())))
+	{ return !__x.has_value() && bool(__x.error() == __e.error()); }
+
+      friend constexpr void
+      swap(expected& __x, expected& __y)
+      noexcept(noexcept(__x.swap(__y)))
+      requires requires { __x.swap(__y); }
+      { __x.swap(__y); }
+
+    private:
+      template<typename, typename> friend class expected;
+
+      template<typename _Vp>
+	constexpr void
+	_M_assign_unex(_Vp&& __v)
+	{
+	  if (_M_has_value)
+	    {
+	      std::construct_at(__builtin_addressof(_M_unex),
+				std::forward<_Vp>(__v));
+	      _M_has_value = false;
+	    }
+	  else
+	    _M_unex = std::forward<_Vp>(__v);
+	}
+
+
+      union {
+	struct { } _M_void;
+	_Er _M_unex;
+      };
+
+      bool _M_has_value;
+    };
+  /// @}
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif // C++23
+#endif // _GLIBCXX_EXPECTED
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 535f095108a..7dbac23f22d 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -306,6 +306,7 @@ 
 
 #if _GLIBCXX_HOSTED
 #define __cpp_lib_adaptor_iterator_pair_constructor 202106L
+#define __cpp_lib_expected 202202L
 #define __cpp_lib_invoke_r 202106L
 #define __cpp_lib_ios_noreplace 202200L
 #if __cpp_lib_concepts
diff --git a/libstdc++-v3/libsupc++/exception b/libstdc++-v3/libsupc++/exception
index 43f1cf71262..ae2b0dd7f78 100644
--- a/libstdc++-v3/libsupc++/exception
+++ b/libstdc++-v3/libsupc++/exception
@@ -79,7 +79,7 @@  namespace std
    *  abandoned for any reason.  It can also be called by the user.  */
   void terminate() _GLIBCXX_USE_NOEXCEPT __attribute__ ((__noreturn__));
 
-#if __cplusplus < 201703L || _GLIBCXX_USE_DEPRECATED
+#if __cplusplus < 201703L || (__cplusplus <= 202002L && _GLIBCXX_USE_DEPRECATED)
   /// If you write a replacement %unexpected handler, it must be of this type.
   typedef void (*_GLIBCXX11_DEPRECATED unexpected_handler) ();
 
diff --git a/libstdc++-v3/testsuite/20_util/expected/assign.cc b/libstdc++-v3/testsuite/20_util/expected/assign.cc
new file mode 100644
index 00000000000..bbf5b900f4c
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/assign.cc
@@ -0,0 +1,321 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <type_traits>
+#include <testsuite_hooks.h>
+
+int dtor_count;
+constexpr void reset_dtor_count()
+{
+  if (!std::is_constant_evaluated())
+    dtor_count = 0;
+}
+constexpr void inc_dtor_count()
+{
+  if (!std::is_constant_evaluated())
+    ++dtor_count;
+}
+constexpr bool check_dtor_count(int c)
+{
+  if (std::is_constant_evaluated())
+    return true;
+  return dtor_count == c;
+}
+
+struct X
+{
+  constexpr X(int i, int j = 0) noexcept : n(i+j) { }
+  constexpr X(std::initializer_list<int> l, void*) noexcept : n(l.size()) { }
+
+  constexpr X(const X&) = default;
+  constexpr X(X&& x) noexcept : n(x.n) { x.n = -1; }
+
+  constexpr X& operator=(const X&) = default;
+  constexpr X& operator=(X&& x) noexcept { n = x.n; x.n = -1; return *this; }
+
+  constexpr ~X()
+  {
+    inc_dtor_count();
+  }
+
+  constexpr bool operator==(const X&) const = default;
+  constexpr bool operator==(int i) const { return n == i; }
+
+  int n;
+};
+
+constexpr bool
+test_copy(bool = true)
+{
+  reset_dtor_count();
+
+  std::expected<int, int> e1(1), e2(2), e3(std::unexpect, 3);
+
+  e1 = e1;
+  e1 = e2; // T = T
+  VERIFY( e1.value() == e2.value() );
+  e1 = e3; // T = E
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = e3; // E = E
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = e2; // E = T
+  VERIFY( e1.value() == e2.value() );
+
+  e1 = std::move(e1);
+  e1 = std::move(e2); // T = T
+  VERIFY( e1.value() == e2.value() );
+  e1 = std::move(e3); // T = E
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = std::move(e3); // E = E
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = std::move(e2); // E = T
+  VERIFY( e1.value() == e2.value() );
+
+  std::expected<X, X> x1(1), x2(2), x3(std::unexpect, 3);
+
+  x1 = x1;
+
+  x1 = x2; // T = T
+  VERIFY( check_dtor_count(0) );
+  VERIFY( x1.value() == x2.value() );
+  x1 = x3; // T = E
+  VERIFY( check_dtor_count(1) );
+  VERIFY( ! x1.has_value() );
+  x1 = x3; // E = E
+  VERIFY( check_dtor_count(1) );
+  VERIFY( ! x1.has_value() );
+  x1 = x2; // E = T
+  VERIFY( check_dtor_count(2) );
+  VERIFY( x1.value() == x2.value() );
+
+  reset_dtor_count();
+
+  x1 = std::move(x1);
+  VERIFY( x1.value() == -1 );
+
+  x1 = std::move(x2); // T = T
+  VERIFY( check_dtor_count(0) );
+  VERIFY( x1.value() == 2 );
+  VERIFY( x2.value() == -1 );
+  x1 = std::move(x3); // T = E
+  VERIFY( check_dtor_count(1) );
+  VERIFY( ! x1.has_value() );
+  VERIFY( x1.error() == 3 );
+  VERIFY( x3.error() == -1 );
+  x3.error().n = 33;
+  x1 = std::move(x3); // E = E
+  VERIFY( check_dtor_count(1) );
+  VERIFY( ! x1.has_value() );
+  VERIFY( x1.error() == 33 );
+  VERIFY( x3.error() == -1 );
+  x2.value().n = 22;
+  x1 = std::move(x2); // E = T
+  VERIFY( check_dtor_count(2) );
+  VERIFY( x1.value() == 22 );
+  VERIFY( x2.value() == -1 );
+
+  std::expected<void, int> ev1, ev2, ev3(std::unexpect, 3);
+
+  ev1 = ev2; // T = T
+  VERIFY( ev1.has_value() );
+  ev1 = ev3; // T = E
+  VERIFY( ! ev1.has_value() );
+  VERIFY( ev1.error() == ev3.error() );
+  ev1 = ev3; // E = E
+  VERIFY( ! ev1.has_value() );
+  VERIFY( ev1.error() == ev3.error() );
+  ev1 = ev2; // E = T
+  VERIFY( ev1.has_value() );
+
+  reset_dtor_count();
+  std::expected<void, X> xv1, xv2, xv3(std::unexpect, 3);
+
+  xv1 = std::move(xv2); // T = T
+  VERIFY( check_dtor_count(0) );
+  VERIFY( xv1.has_value() );
+  xv1 = std::move(xv3); // T = E
+  VERIFY( check_dtor_count(0) );
+  VERIFY( ! xv1.has_value() );
+  VERIFY( xv1.error() == 3 );
+  VERIFY( xv3.error() == -1 );
+  xv3.error().n = 33;
+  xv1 = std::move(xv3); // E = E
+  VERIFY( check_dtor_count(0) );
+  VERIFY( xv1.error() == 33 );
+  VERIFY( xv3.error() == -1 );
+  xv1 = std::move(xv2); // E = T
+  VERIFY( check_dtor_count(1) );
+  VERIFY( xv1.has_value() );
+
+  return true;
+}
+
+constexpr bool
+test_converting(bool = true)
+{
+  std::expected<int, int> e1(1);
+  std::expected<unsigned, long> e2(2U), e3(std::unexpect, 3L);
+  e1 = e2;
+  VERIFY( e1.value() == e2.value() );
+  e1 = e3;
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = e2;
+  VERIFY( e1.value() == e2.value() );
+
+  e1 = std::move(e3);
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == e3.error() );
+  e1 = std::move(e2);
+  VERIFY( e1.value() == e2.value() );
+
+  std::expected<void, int> ev4;
+  std::expected<const void, long> ev5(std::unexpect, 5);
+  ev4 = ev5;
+  VERIFY( ! ev4.has_value() );
+  VERIFY( ev4.error() == 5 );
+  ev4 = std::expected<volatile void, unsigned>();
+  VERIFY( ev4.has_value() );
+  ev4 = std::move(ev5);
+  VERIFY( ! ev4.has_value() );
+  VERIFY( ev4.error() == 5 );
+
+  return true;
+}
+
+constexpr bool
+test_unexpected(bool = true)
+{
+  reset_dtor_count();
+
+  std::expected<X, int> e1(0);
+
+  e1 = std::unexpected<int>(5);
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == 5 );
+  VERIFY( check_dtor_count(1) );
+
+  e1 = std::unexpected<int>(6);
+  VERIFY( check_dtor_count(1) );
+
+  std::expected<int, X> e2;
+
+  std::unexpected<X> x(std::in_place, 1, 2);
+  e2 = x;
+  VERIFY( check_dtor_count(1) );
+
+  e2 = 1;
+  VERIFY( e2.value() == 1 );
+  VERIFY( check_dtor_count(2) );
+
+  return true;
+}
+
+constexpr bool
+test_emplace(bool = true)
+{
+  reset_dtor_count();
+
+  std::expected<int, int> e1(1);
+  e1.emplace(2);
+  VERIFY( e1.value() == 2 );
+
+  std::expected<void, int> ev2;
+  ev2.emplace();
+  VERIFY( ev2.has_value() );
+
+  std::expected<X, int> e3(std::in_place, 0, 0);
+
+  e3.emplace({1,2,3}, nullptr);
+  VERIFY( e3.value() == 3 );
+  VERIFY( check_dtor_count(1) );
+
+  e3.emplace(2, 2);
+  VERIFY( e3.value() == 4 );
+  VERIFY( check_dtor_count(2) );
+
+  std::expected<int, X> ev4(std::unexpect, 4);
+
+  ev4.emplace(5);
+  VERIFY( ev4.value() == 5 );
+  VERIFY( check_dtor_count(3) );
+
+  ev4.emplace(6);
+  VERIFY( ev4.value() == 6 );
+  VERIFY( check_dtor_count(3) );
+
+  return true;
+}
+
+void
+test_exception_safety()
+{
+  struct CopyThrows
+  {
+    CopyThrows(int i) noexcept : x(i) { }
+    CopyThrows(const CopyThrows&) { throw 1; }
+    CopyThrows(CopyThrows&&) = default;
+    CopyThrows& operator=(const CopyThrows&) = default;
+    CopyThrows& operator=(CopyThrows&&) = default;
+    int x;
+
+    bool operator==(int i) const { return x == i; }
+  };
+
+  struct MoveThrows
+  {
+    MoveThrows(int i) noexcept : x(i) { }
+    MoveThrows(const MoveThrows&) = default;
+    MoveThrows(MoveThrows&&) { throw 1L; }
+    MoveThrows& operator=(const MoveThrows&) = default;
+    MoveThrows& operator=(MoveThrows&&) = default;
+    int x;
+
+    bool operator==(int i) const { return x == i; }
+  };
+
+  std::expected<CopyThrows, MoveThrows> c(std::unexpect, 1);
+
+  // operator=(U&&)
+  try {
+    CopyThrows x(2);
+    c = x;
+    VERIFY( false );
+  } catch (int) {
+    VERIFY( ! c.has_value() );
+    VERIFY( c.error() == 1 );
+  }
+
+  c = CopyThrows(2);
+
+  try {
+    c = std::unexpected<MoveThrows>(3);
+    VERIFY( false );
+  } catch (long) {
+    VERIFY( c.value() == 2 );
+  }
+}
+
+int main(int argc, char**)
+{
+  bool non_constant = argc == 1; // force non-constant evaluation
+
+  static_assert( test_copy() );
+  test_copy(non_constant);
+  static_assert( test_converting() );
+  test_converting(non_constant);
+  static_assert( test_unexpected() );
+  test_unexpected(non_constant);
+  static_assert( test_emplace() );
+  test_emplace(non_constant);
+
+  test_exception_safety();
+
+  // Ensure the non-constexpr tests actually ran:
+  VERIFY( dtor_count != 0 );
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/cons.cc b/libstdc++-v3/testsuite/20_util/expected/cons.cc
new file mode 100644
index 00000000000..1fe5b7bf4d1
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/cons.cc
@@ -0,0 +1,175 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_default()
+{
+  std::expected<int, int> e;
+  VERIFY( e.has_value() );
+  VERIFY( *e == 0 );
+
+  std::expected<void, int> ev;
+  VERIFY( ev.has_value() );
+  VERIFY( (ev.value(), true) );
+
+  return true;
+}
+
+constexpr bool
+test_val()
+{
+  std::expected<int, int> e1(1);
+  VERIFY( e1.has_value() );
+  VERIFY( *e1 == 1 );
+
+  std::expected<int, int> e2(std::in_place, 2);
+  VERIFY( e2.has_value() );
+  VERIFY( *e2 == 2 );
+
+  struct X
+  {
+    constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
+    int n;
+  };
+
+  std::expected<X, int> e3(X{{1, 2, 3}, nullptr});
+  VERIFY( e3.has_value() );
+  VERIFY( e3->n == 3 );
+
+  std::expected<X, int> e4(std::in_place, {1, 2, 3, 4}, nullptr);
+  VERIFY( e4.has_value() );
+  VERIFY( e4->n == 4 );
+
+  std::expected<void, int> ev(std::in_place);
+  VERIFY( ev.has_value() );
+  VERIFY( (ev.value(), true) );
+
+  return true;
+}
+
+constexpr bool
+test_err()
+{
+  std::expected<int, int> e1(std::unexpected<int>(1));
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == 1 );
+
+  const std::unexpected<int> u2(2);
+  std::expected<int, int> e2(u2);
+  VERIFY( ! e2.has_value() );
+  VERIFY( e2.error() == 2 );
+
+  std::expected<int, int> e3(std::unexpect, 3);
+  VERIFY( ! e3.has_value() );
+  VERIFY( e3.error() == 3 );
+
+  struct X
+  {
+    constexpr X(int i, int j) : n(i+j) { }
+    constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
+    int n;
+  };
+
+  std::expected<int, X> e4(std::unexpect, 1, 3);
+  VERIFY( ! e4.has_value() );
+  VERIFY( e4.error().n == 4 );
+
+  std::expected<int, X> e5(std::unexpect, {1, 2, 3, 4, 5}, nullptr);
+  VERIFY( ! e5.has_value() );
+  VERIFY( e5.error().n == 5 );
+
+  std::expected<const void, int> ev1(std::unexpected<int>(1));
+  VERIFY( ! ev1.has_value() );
+  VERIFY( ev1.error() == 1 );
+
+  std::expected<volatile void, int> ev2(u2);
+  VERIFY( ! ev2.has_value() );
+  VERIFY( ev2.error() == 2 );
+
+  std::expected<const volatile void, int> ev3(std::unexpect, 3);
+  VERIFY( ! ev3.has_value() );
+  VERIFY( ev3.error() == 3 );
+
+  std::expected<void, X> ev4(std::unexpect, 1, 3);
+  VERIFY( ! ev4.has_value() );
+  VERIFY( ev4.error().n == 4 );
+
+  std::expected<void, X> ev5(std::unexpect, {1, 2, 3, 4, 5}, nullptr);
+  VERIFY( ! ev5.has_value() );
+  VERIFY( ev5.error().n == 5 );
+
+  return true;
+}
+
+constexpr bool
+test_copy()
+{
+  std::expected<int, int> e1(1);
+  std::expected<int, int> e2(e1);
+  VERIFY( e2.value() == 1 );
+  std::expected<int, int> e3(std::move(e2));
+  VERIFY( e2.value() == 1 );
+  VERIFY( e3.value() == 1 );
+  std::expected<short, short> e4(e1);
+  VERIFY( e4.value() == 1 );
+  std::expected<short, short> e5(std::move(e4));
+  VERIFY( e4.value() == 1 );
+  VERIFY( e5.value() == 1 );
+
+  std::expected<int, int> u1(std::unexpect, 2);
+  std::expected<int, int> u2(u1);
+  VERIFY( ! u2.has_value() );
+  VERIFY( u2.error() == 2 );
+  std::expected<int, int> u3(std::move(u2));
+  VERIFY( ! u3.has_value() );
+  VERIFY( u3.error() == 2 );
+  std::expected<short, short> u4(u1);
+  VERIFY( ! u4.has_value() );
+  VERIFY( u4.error() == 2 );
+  std::expected<short, short> u5(std::move(u4));
+  VERIFY( ! u5.has_value() );
+  VERIFY( u5.error() == 2 );
+
+  std::expected<void, int> ev1;
+  std::expected<void, int> ev2(ev1);
+  VERIFY( ev2.has_value() );
+  std::expected<void, int> ev3(std::move(ev2));
+  VERIFY( ev2.has_value() );
+  VERIFY( ev3.has_value() );
+  std::expected<volatile void, short> ev4(ev1);
+  VERIFY( ev4.has_value() );
+  std::expected<const void, short> ev5(std::move(ev4));
+  VERIFY( ev4.has_value() );
+  VERIFY( ev5.has_value() );
+
+  std::expected<void, int> uv1(std::unexpect, 2);
+  std::expected<void, int> uv2(uv1);
+  VERIFY( ! uv2.has_value() );
+  VERIFY( uv2.error() == 2 );
+  std::expected<void, int> uv3(std::move(uv2));
+  VERIFY( ! uv3.has_value() );
+  VERIFY( uv3.error() == 2 );
+  std::expected<const void, short> uv4(uv1);
+  VERIFY( ! uv4.has_value() );
+  VERIFY( uv4.error() == 2 );
+  std::expected<volatile void, short> uv5(std::move(uv4));
+  VERIFY( ! uv5.has_value() );
+  VERIFY( uv5.error() == 2 );
+
+  return true;
+}
+
+int main()
+{
+  test_default();
+  static_assert( test_default() );
+  test_val();
+  static_assert( test_val() );
+  test_err();
+  static_assert( test_err() );
+  test_copy();
+  static_assert( test_copy() );
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
new file mode 100644
index 00000000000..921306bc667
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc
@@ -0,0 +1,67 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <expected>
+
+void
+test_unexpected()
+{
+  int i[2]{};
+
+  // std::unexpected<E> is ill-formed if E is a non-object type,
+
+  std::unexpected<int&> ref(i[0]); // { dg-error "here" }
+  std::unexpected<void()> func(test_unexpected); // { dg-error "here" }
+  // { dg-error "no matching function for call to" "" { target *-*-* } 0 }
+  // { dg-error "invalidly declared function type" "" { target *-*-* } 0 }
+
+  // an array type,
+  std::unexpected<int[2]> array(i); // { dg-error "here" }
+
+  // a specialization of std::unexpected,
+  std::unexpected<int> u(1);
+  std::unexpected<std::unexpected<int>> nested(u); // { dg-error "here" }
+						   //
+  // or a cv-qualified type.
+  std::unexpected<const int> c_int(1); // { dg-error "here" }
+  std::unexpected<volatile int> v_int(1); // { dg-error "here" }
+}
+
+void
+test_expected_value()
+{
+  // std::expected<T, E> is ill-formed if T is a reference type,
+  std::expected<int&, int> ref(std::unexpect); // { dg-error "here" }
+  //  { dg-error "reference type" "" { target *-*-* } 0 }
+
+  // a function type,
+  std::expected<void(), int> func(std::unexpect); // { dg-error "here" }
+  //  { dg-error "returning a function" "" { target *-*-* } 0 }
+						 //
+  // possibly cv-qualified types in_place_t,
+  std::expected<std::in_place_t, int> tag(std::unexpect); // { dg-error "here" }
+  std::expected<const std::in_place_t, int> ctag(std::unexpect); // { dg-error "here" }
+  // unexpect_t,
+  std::expected<std::unexpect_t, int> utag(std::unexpect); // { dg-error "here" }
+  std::expected<const std::unexpect_t, int> cutag(std::unexpect); // { dg-error "here" }
+  // or a specialization of unexpected.
+  std::expected<std::unexpected<int>, int> unex(std::in_place, 1); // { dg-error "here" }
+  std::expected<const std::unexpected<int>, int> cunex(std::in_place, 1); // { dg-error "here" }
+}
+
+void
+test_expected_error()
+{
+
+  // std::expected<T, E> is ill-formed if std::unexpected<E> would be
+  // ill-formed. Test the same types as in test_unexpected().
+
+  std::expected<int, int&> ref; // { dg-error "here" }
+  std::expected<int, void()> func; // { dg-error "here" }
+  std::expected<int, int[2]> array; // { dg-error "here" }
+  std::expected<int, std::unexpected<int>> nested; // { dg-error "here" }
+  std::expected<int, const int> c_int; // { dg-error "here" }
+  std::expected<int, volatile int> v_int; // { dg-error "here" }
+}
+
+// { dg-prune-output "static assertion failed" }
diff --git a/libstdc++-v3/testsuite/20_util/expected/observers.cc b/libstdc++-v3/testsuite/20_util/expected/observers.cc
new file mode 100644
index 00000000000..e76ca378026
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/observers.cc
@@ -0,0 +1,209 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <expected>
+#include <testsuite_hooks.h>
+
+struct X
+{
+  constexpr int f() & { return 1; }
+  constexpr int f() const & { return 2; }
+  constexpr int f() && { return 3; }
+  constexpr int f() const && { return 4; }
+};
+
+constexpr bool
+test_arrow()
+{
+  std::expected<X, int> e1;
+  VERIFY( e1->f() == 1 );
+  const auto& e2 = e1;
+  VERIFY( e2->f() == 2 );
+
+  return true;
+}
+
+constexpr bool
+test_star()
+{
+  std::expected<X, int> e1;
+  VERIFY( (*e1).f() == 1 );
+  VERIFY( std::move(*e1).f() == 3 );
+  const auto& e2 = e1;
+  VERIFY( (*e2).f() == 2 );
+  VERIFY( std::move(*e2).f() == 4 );
+
+  std::expected<void, int> v;
+  *v;
+
+  return true;
+}
+
+constexpr bool
+test_has_value()
+{
+  std::expected<int, int> e;
+  VERIFY( e.has_value() );
+  VERIFY( e );
+  e = std::unexpected(1);
+  VERIFY( ! e.has_value() );
+  VERIFY( ! e );
+
+  std::expected<void, int> v;
+  VERIFY( v.has_value() );
+  VERIFY( v );
+  v = std::unexpected(1);
+  VERIFY( ! v.has_value() );
+  VERIFY( ! v );
+
+  return true;
+}
+
+constexpr bool
+test_value()
+{
+  std::expected<X, int> e1;
+
+  VERIFY( e1.value().f() == 1 );
+  VERIFY( std::move(e1).value().f() == 3 );
+  const auto& e2 = e1;
+  VERIFY( e2.value().f() == 2 );
+  VERIFY( std::move(e2).value().f() == 4 );
+
+  std::expected<void, int> v1;
+  v1.value();
+  std::move(v1).value();
+
+  return true;
+}
+
+void
+test_value_throw()
+{
+  std::expected<int, int> e1 = std::unexpected(9);
+
+  try {
+    e1.value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 9 );
+  }
+  try {
+    std::move(e1).value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 9 );
+  }
+
+  const auto& e2 = e1;
+  try {
+    e2.value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 9 );
+  }
+  try {
+    std::move(e2).value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 9 );
+  }
+
+  std::expected<void, int> v1 = std::unexpected(8);
+  try {
+    v1.value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 8 );
+  }
+  try {
+    std::move(v1).value();
+    VERIFY( false );
+  } catch (const std::bad_expected_access<int>& e) {
+    VERIFY( e.error() == 8 );
+  }
+}
+
+constexpr bool
+test_error()
+{
+  std::expected<int, X> e1(std::unexpect);
+
+  VERIFY( e1.error().f() == 1 );
+  VERIFY( std::move(e1).error().f() == 3 );
+  const auto& e2 = e1;
+  VERIFY( e2.error().f() == 2 );
+  VERIFY( std::move(e2).error().f() == 4 );
+
+  std::expected<void, X> v1(std::unexpect);
+
+  VERIFY( v1.error().f() == 1 );
+  VERIFY( std::move(v1).error().f() == 3 );
+  const auto& v2 = v1;
+  VERIFY( v2.error().f() == 2 );
+  VERIFY( std::move(v2).error().f() == 4 );
+
+  return true;
+}
+
+constexpr bool
+test_value_or()
+{
+  struct Movable
+  {
+    constexpr Movable(int i) : x(i) { }
+    constexpr Movable(const Movable&) = default;
+    constexpr Movable(Movable&& m) : x(m.x) { m.x = -1; }
+    int x;
+
+    constexpr bool operator==(int i) const { return x == i; }
+  };
+
+  std::expected<Movable, int> e1(1);
+
+  Movable m2(2);
+  VERIFY( e1.value_or(2) == 1 );
+  VERIFY( e1.value_or(m2) == 1 );
+  VERIFY( e1.value_or(std::move(m2)) == 1 );
+  VERIFY( m2 == 2 );
+
+  VERIFY( std::move(e1).value_or(m2) == 1 );
+  VERIFY( *e1 == -1 ); // moved
+  VERIFY( m2 == 2 );
+
+  e1 = std::unexpected(3);
+  VERIFY( e1.value_or(m2) == 2 );
+  VERIFY( m2 == 2 );
+  VERIFY( std::move(e1).value_or(m2) == 2 );
+  VERIFY( m2 == 2 );
+
+  VERIFY( e1.value_or(std::move(m2)) == 2 );
+  VERIFY( m2 == -1 );
+
+  m2.x = 4;
+  VERIFY( std::move(e1).value_or(std::move(m2)) == 4 );
+  VERIFY( m2 == -1 );
+
+  VERIFY( e1.value_or(5) == 5 );
+  VERIFY( std::move(e1).value_or(6) == 6 );
+
+  return true;
+}
+
+int main()
+{
+  static_assert( test_arrow() );
+  test_arrow();
+  static_assert( test_star() );
+  test_star();
+  static_assert( test_has_value() );
+  test_has_value();
+  static_assert( test_value() );
+  test_value();
+  test_value_throw();
+  static_assert( test_error() );
+  test_error();
+  static_assert( test_value_or() );
+  test_value_or();
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/requirements.cc b/libstdc++-v3/testsuite/20_util/expected/requirements.cc
new file mode 100644
index 00000000000..485aa338679
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/requirements.cc
@@ -0,0 +1,129 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <expected>
+#include <type_traits>
+
+// Default construction
+
+template<typename T, typename E>
+  constexpr bool default_constructible
+    = std::is_default_constructible_v<std::expected<T, E>>;
+
+struct A { A(int); };
+
+static_assert( default_constructible< int,  int > );
+static_assert( default_constructible< A,    int > == false );
+static_assert( default_constructible< int,  A   > );
+static_assert( default_constructible< A,    A   > == false );
+static_assert( default_constructible< int,  A   > );
+static_assert( default_constructible< void, int > );
+
+// Destruction
+
+template<typename T, typename E>
+  constexpr bool trivially_destructible
+    = std::is_trivially_destructible_v<std::expected<T, E>>;
+
+struct B { ~B(); };
+
+static_assert( trivially_destructible< int,  int > );
+static_assert( trivially_destructible< B,    int > == false );
+static_assert( trivially_destructible< int,  B   > == false );
+static_assert( trivially_destructible< B,    B   > == false );
+static_assert( trivially_destructible< void, int > );
+static_assert( trivially_destructible< void, B   > == false );
+
+enum Result { No, Yes, NoThrow, Trivial };
+
+// Copy construction
+
+template<typename T, typename E>
+  constexpr Result copy_constructible
+    = std::is_trivially_copy_constructible_v<std::expected<T, E>> ? Trivial
+    : std::is_copy_constructible_v<std::expected<T, E>> ? Yes
+    : No;
+
+struct C { C(const C&); };
+struct D { D(D&&); };
+
+static_assert( copy_constructible< int,  int > == Trivial );
+static_assert( copy_constructible< C,    C   > == Yes );
+static_assert( copy_constructible< C,    int > == Yes );
+static_assert( copy_constructible< int,  C   > == Yes );
+static_assert( copy_constructible< int,  D   > == No );
+static_assert( copy_constructible< D,    int > == No );
+static_assert( copy_constructible< D,    D   > == No );
+static_assert( copy_constructible< void, int > == Trivial );
+static_assert( copy_constructible< void, C   > == Yes );
+static_assert( copy_constructible< void, D   > == No );
+
+// Move construction
+
+template<typename T, typename E>
+  constexpr Result move_constructible
+    = std::is_trivially_move_constructible_v<std::expected<T, E>> ? Trivial
+    : std::is_nothrow_move_constructible_v<std::expected<T, E>> ? NoThrow
+    : std::is_move_constructible_v<std::expected<T, E>> ? Yes
+    : No;
+
+struct E { E(E&&) noexcept; };
+
+static_assert( move_constructible< int,  int > == Trivial );
+static_assert( move_constructible< C,    C   > == Yes );
+static_assert( move_constructible< C,    int > == Yes );
+static_assert( move_constructible< int,  C   > == Yes );
+static_assert( move_constructible< D,    D   > == Yes );
+static_assert( move_constructible< D,    int > == Yes );
+static_assert( move_constructible< int,  D   > == Yes );
+static_assert( move_constructible< E,    E   > == NoThrow );
+static_assert( move_constructible< E,    int > == NoThrow );
+static_assert( move_constructible< int,  E   > == NoThrow );
+static_assert( move_constructible< void, int > == Trivial );
+static_assert( move_constructible< void, C   > == Yes );
+static_assert( move_constructible< void, D   > == Yes );
+static_assert( move_constructible< void, E   > == NoThrow );
+
+// Copy assignment
+
+template<typename T, typename E>
+  constexpr bool copy_assignable
+    = std::is_copy_assignable_v<std::expected<T, E>>;
+
+struct F { F(F&&); F& operator=(const F&); }; // not copy-constructible
+struct G { G(const G&); G(G&&); G& operator=(const G&); }; // throwing move
+
+static_assert( copy_assignable< int,  int > );
+static_assert( copy_assignable< F,    int > == false );
+static_assert( copy_assignable< int,  F   > == false );
+static_assert( copy_assignable< F,    F   > == false );
+static_assert( copy_assignable< G,    int > );
+static_assert( copy_assignable< int,  G   > );
+static_assert( copy_assignable< G,    G   > == false );
+static_assert( copy_assignable< void, int > );
+static_assert( copy_assignable< void, F > == false );
+static_assert( copy_assignable< void, G > );
+
+// Move assignment
+
+template<typename T, typename E>
+  constexpr bool move_assignable
+    = std::is_move_assignable_v<std::expected<T, E>>;
+
+static_assert( move_assignable< int,  int > );
+static_assert( move_assignable< F,    int > );
+static_assert( move_assignable< int,  F   > );
+static_assert( move_assignable< F,    F   > == false );
+static_assert( move_assignable< G,    int > );
+static_assert( move_assignable< int,  G   > );
+static_assert( move_assignable< G,    G   > == false );
+static_assert( move_assignable< void, int > );
+static_assert( move_assignable< void, F > );
+static_assert( move_assignable< void, G > );
+
+// QoI properties
+static_assert( sizeof(std::expected<char, unsigned char>) == 2 );
+static_assert( sizeof(std::expected<void, char>) == 2 );
+static_assert( sizeof(std::expected<void*, char>) == 2 * __alignof(void*) );
+static_assert( alignof(std::expected<void, char>) == 1 );
+static_assert( alignof(std::expected<void*, char>) == alignof(void*) );
diff --git a/libstdc++-v3/testsuite/20_util/expected/swap.cc b/libstdc++-v3/testsuite/20_util/expected/swap.cc
new file mode 100644
index 00000000000..1b3b8c5f4e8
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/swap.cc
@@ -0,0 +1,57 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_swap()
+{
+  std::expected<int, int> e1(1), e2(2);
+  std::expected<int, int> e3(std::unexpect, 3), e4(std::unexpect, 4);
+
+  swap(e1, e2);
+  VERIFY( e1.value() == 2 );
+  VERIFY( e2.value() == 1 );
+  swap(e1, e3);
+  VERIFY( ! e1.has_value() );
+  VERIFY( e1.error() == 3 );
+  VERIFY( e3.value() == 2 );
+  swap(e1, e3);
+  VERIFY( ! e3.has_value() );
+  VERIFY( e1.value() == 2 );
+  VERIFY( e3.error() == 3 );
+  swap(e3, e4);
+  VERIFY( ! e3.has_value() );
+  VERIFY( ! e4.has_value() );
+  VERIFY( e3.error() == 4 );
+  VERIFY( e4.error() == 3 );
+
+  std::expected<int, int> v1(1), v2(2);
+  std::expected<int, int> v3(std::unexpect, 3), v4(std::unexpect, 4);
+
+  swap(v1, v2);
+  VERIFY( v1.value() == 2 );
+  VERIFY( v2.value() == 1 );
+  swap(v1, v3);
+  VERIFY( ! v1.has_value() );
+  VERIFY( v1.error() == 3 );
+  VERIFY( v3.value() == 2 );
+  swap(v1, v3);
+  VERIFY( ! v3.has_value() );
+  VERIFY( v1.value() == 2 );
+  VERIFY( v3.error() == 3 );
+  swap(v3, v4);
+  VERIFY( ! v3.has_value() );
+  VERIFY( ! v4.has_value() );
+  VERIFY( v3.error() == 4 );
+  VERIFY( v4.error() == 3 );
+
+  return true;
+}
+
+int main()
+{
+  static_assert( test_swap() );
+  test_swap();
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/synopsis.cc b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc
new file mode 100644
index 00000000000..304bae93ebd
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/synopsis.cc
@@ -0,0 +1,21 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+
+#include <expected>
+
+#ifndef __cpp_lib_expected
+# error "Feature-test macro for expected missing in <expected>"
+#elif __cpp_lib_expected != 202202L
+# error "Feature-test macro for expected has wrong value in <expected>"
+#endif
+
+namespace std
+{
+  template<class E> class unexpected;
+  template<class E> class bad_expected_access;
+  template<> class bad_expected_access<void>;
+  struct unexpect_t;
+  extern inline const unexpect_t unexpect;
+  template<class T, class E> class expected;
+  template<class T, class E> requires is_void_v<T> class expected<T, E>;
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/unexpected.cc b/libstdc++-v3/testsuite/20_util/expected/unexpected.cc
new file mode 100644
index 00000000000..d4cbeadf674
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/unexpected.cc
@@ -0,0 +1,80 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <expected>
+#include <testsuite_hooks.h>
+
+static_assert( sizeof(std::unexpected<char>) == 1 );
+
+constexpr bool
+test()
+{
+  std::unexpected<int> u1(1);
+  VERIFY( u1.error() == 1 );
+
+  std::unexpected<int> u2(std::in_place, 2);
+  VERIFY( u2.error() == 2 );
+
+  struct X
+  {
+    constexpr X(int i, int j) : n(i+j) { }
+    constexpr X(std::initializer_list<int> l, void*) : n(l.size()) { }
+
+    constexpr X(const X&) = default;
+    constexpr X(X&& x) :n(x.n) { x.n = -1; }
+
+    constexpr X& operator=(const X&) = default;
+    constexpr X& operator=(X&& x) { n = x.n; x.n = -1; return *this; }
+
+    constexpr bool operator==(const X&) const = default;
+    constexpr bool operator==(int i) const { return n == i; }
+
+    int n;
+  };
+
+  std::unexpected<X> u3(std::in_place, 2, 1);
+  VERIFY( u3.error() == 3 );
+
+  std::unexpected<X> u4(std::in_place, {1,2,3,4}, nullptr);
+  VERIFY( u4.error() == 4 );
+
+  std::unexpected<X> u5(u4);
+  VERIFY( u5.error() == 4 );
+  VERIFY( u4.error() == 4 );
+
+  std::unexpected<X> u6(std::move(u4));
+  VERIFY( u6.error() == 4 );
+  VERIFY( u4.error() == -1 );
+
+  u6 = u3;
+  VERIFY( u6.error() == 3 );
+  VERIFY( u3.error() == 3 );
+
+  u5 = std::move(u3);
+  VERIFY( u5.error() == 3 );
+  VERIFY( u3.error() == -1 );
+
+  u5.swap(u3);
+  VERIFY( u3.error() == 3 );
+  VERIFY( u5.error() == -1 );
+
+  swap(u5, u3);
+  VERIFY( u5.error() == 3 );
+  VERIFY( u3.error() == -1 );
+
+  VERIFY( u1 == u1 );
+  VERIFY( u1 != u2 );
+  VERIFY( u3 == u4 );
+
+  // CTAD
+  std::unexpected u7(1L);
+  static_assert( std::is_same_v<decltype(u7), std::unexpected<long>> );
+
+  return true;
+}
+
+int main()
+{
+  static_assert( test() );
+  test();
+}
diff --git a/libstdc++-v3/testsuite/20_util/expected/version.cc b/libstdc++-v3/testsuite/20_util/expected/version.cc
new file mode 100644
index 00000000000..78dc61ef55d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/version.cc
@@ -0,0 +1,10 @@ 
+// { dg-options "-std=gnu++23" }
+// { dg-do preprocess { target c++23 } }
+
+#include <version>
+
+#ifndef __cpp_lib_expected
+# error "Feature-test macro for expected missing in <version>"
+#elif __cpp_lib_expected != 202202L
+# error "Feature-test macro for expected has wrong value in <version>"
+#endif