summaryrefslogtreecommitdiff
path: root/libstdc++-v3
diff options
context:
space:
mode:
authorJonathan Wakely <jwakely@redhat.com>2022-03-24 20:37:13 +0000
committerJonathan Wakely <jwakely@redhat.com>2022-03-27 18:50:31 +0100
commitb78e0ce28b32a1b89886219c557506aeae6caffc (patch)
tree8c5b093cdb25333a6b26608b418a38dd8033b82f /libstdc++-v3
parentd2906412ada87a4bdd6410060bc18a2c53c419b7 (diff)
downloadgcc-b78e0ce28b32a1b89886219c557506aeae6caffc.tar.gz
libstdc++: Define std::expected for C++23 (P0323R12)
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.
Diffstat (limited to 'libstdc++-v3')
-rw-r--r--libstdc++-v3/doc/doxygen/user.cfg.in1
-rw-r--r--libstdc++-v3/include/Makefile.am1
-rw-r--r--libstdc++-v3/include/Makefile.in1
-rw-r--r--libstdc++-v3/include/precompiled/stdc++.h1
-rw-r--r--libstdc++-v3/include/std/expected1240
-rw-r--r--libstdc++-v3/include/std/version1
-rw-r--r--libstdc++-v3/libsupc++/exception2
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/assign.cc321
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/cons.cc175
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc67
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/observers.cc209
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/requirements.cc129
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/swap.cc57
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/synopsis.cc21
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/unexpected.cc80
-rw-r--r--libstdc++-v3/testsuite/20_util/expected/version.cc10
16 files changed, 2315 insertions, 1 deletions
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