From fafd5d018e82160e7ff2118043a3f522bba62b33 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 29 Mar 2022 16:59:27 +0200 Subject: [PATCH] Implement P0323: expected Fixes #2529 --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/expected | 1020 +++++++++ stl/inc/header-units.json | 1 + stl/inc/yvals_core.h | 12 +- tests/std/test.lst | 1 + tests/std/tests/P0323R12_expected/env.lst | 4 + tests/std/tests/P0323R12_expected/test.cpp | 1865 +++++++++++++++++ .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../test.compile.pass.cpp | 14 + 11 files changed, 2918 insertions(+), 3 deletions(-) create mode 100644 stl/inc/expected create mode 100644 tests/std/tests/P0323R12_expected/env.lst create mode 100644 tests/std/tests/P0323R12_expected/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index fae0ffdb05d..37644ec956c 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -130,6 +130,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/deque ${CMAKE_CURRENT_LIST_DIR}/inc/exception ${CMAKE_CURRENT_LIST_DIR}/inc/execution + ${CMAKE_CURRENT_LIST_DIR}/inc/expected ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/coroutine ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/deque ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/filesystem diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 8de1be161e3..a2e1ce8fc7e 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -88,6 +88,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/expected b/stl/inc/expected new file mode 100644 index 00000000000..f5ac4ed450f --- /dev/null +++ b/stl/inc/expected @@ -0,0 +1,1020 @@ +// expected standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _EXPECTED_ +#define _EXPECTED_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX23 && defined(__cpp_lib_concepts) +#pragma message("The contents of are available only with C++23 or later.") +#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv +#include +#include +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +// [expected.un.object] +// clang-format off +template + requires (is_object_v<_Err> && !is_array_v<_Err> && !is_volatile_v<_Err> && !is_const_v<_Err>) +class unexpected { + // clang-format on + static_assert(!_Is_specialization_v<_Err, unexpected>, "E must not be a specialization of unexpected"); + + template + friend class expected; + +public: + // [expected.un.ctor] + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit unexpected(in_place_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) // strengthened + : _Unexpected(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Err, initializer_list<_Uty>&, _Args...> + constexpr explicit unexpected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) // strengthened + : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + + template + requires (!is_same_v, unexpected> && !is_same_v, in_place_t> // + && is_constructible_v<_Err, _UError>) + constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened + : _Unexpected(_STD forward<_UError>(_Unex)) {} + // clang-format on + + // [expected.un.observe] + // TRANSITION, P2549 +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + // [expected.un.swap] + constexpr void swap(unexpected& _Other) noexcept(is_nothrow_swappable_v<_Err>) requires is_swappable_v<_Err> { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + + friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept( + is_nothrow_swappable_v<_Err>) requires is_swappable_v<_Err> { + _Left.swap(_Right); + } + + // [expected.un.eq] + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const unexpected& _Left, const unexpected<_UErr>& _Right) noexcept( // + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + return _Left._Unexpected == _Right.error(); + } + +private: + _Err _Unexpected; +}; + +template +class bad_expected_access; + +template <> +class bad_expected_access : public exception { +public: + _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { + return "Bad expected access"; + } + +#if !_HAS_EXCEPTIONS +protected: + void _Doraise() const override { // perform class-specific exception handling + _RAISE(*this); + } +#endif // !_HAS_EXCEPTIONS +}; + +template +class bad_expected_access : public bad_expected_access { +public: + __CLR_OR_THIS_CALL explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) + : _Unexpected(_STD move(_Unex)) {} + +// TRANSITION, P2549 +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + +private: + _Err _Unexpected; +}; + +struct unexpect_t { + explicit unexpect_t() = default; +}; + +inline constexpr unexpect_t unexpect{}; + +template +class expected { + template + friend class expected; + +public: + using value_type = _Ty; + using error_type = _Err; + using unexpected_type = unexpected<_Err>; + + template + using rebind = expected<_Uty, error_type>; + + // [expected.object.ctor] + // clang-format off + constexpr expected() requires is_default_constructible_v<_Ty> : _Has_value(true), _Value() {}; + + constexpr expected(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err>) // + requires (!is_trivially_copy_constructible_v<_Ty> && !is_trivially_copy_constructible_v<_Err> // + && is_copy_constructible_v<_Ty> && is_copy_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + // clang-format on + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _Other._Value); + } else { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + // clang-format off + expected(const expected& _Other) requires + is_trivially_copy_constructible_v<_Ty> && is_trivially_copy_constructible_v<_Err> = default; + + constexpr expected(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err>) // + requires (!is_trivially_move_constructible_v<_Ty> && !is_trivially_move_constructible_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + // clang-format on + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _STD move(_Other._Value)); + } else { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + } + }; + + // clang-format off + expected(expected&&) requires + is_trivially_move_constructible_v<_Ty> && is_trivially_move_constructible_v<_Err> = default; + // clang-format on + + template + static constexpr bool _Is_not_convertible = !is_constructible_v<_Ty, expected<_Uty, _UErr>&> // + && !is_constructible_v<_Ty, expected<_Uty, _UErr>> // + && !is_constructible_v<_Ty, const expected<_Uty, _UErr>&> // + && !is_constructible_v<_Ty, const expected<_Uty, _UErr>> // + && !is_convertible_v&, _Ty> // + && !is_convertible_v&&, _Ty> // + && !is_convertible_v&, _Ty> // + && !is_convertible_v&&, _Ty> // + && !is_constructible_v, expected<_Uty, _UErr>&> // + && !is_constructible_v, expected<_Uty, _UErr>> // + && !is_constructible_v, const expected<_Uty, _UErr>&> // + && !is_constructible_v, const expected<_Uty, _UErr>>; + + template + requires is_constructible_v<_Ty, const _Uty&> && is_constructible_v<_Err, const _UErr&> // + && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected<_Uty, _UErr>& _Other) noexcept(is_nothrow_constructible_v<_Ty, const _Uty&>&& + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _Other._Value); + } else { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + template + requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v<_Uty, _Ty> || !is_convertible_v<_UErr, _Err>) + expected(expected<_Uty, _UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _STD forward<_Uty>(_Other._Value)); + } else { + _STD construct_at(_STD addressof(_Unexpected), _STD forward<_UErr>(_Other._Unexpected)); + } + } + + // clang-format off + template + requires (!is_same_v, in_place_t> && !is_same_v, expected> // + && !_Is_specialization_v, unexpected> && is_constructible_v<_Ty, _Uty>) + constexpr explicit(!is_convertible_v<_Uty, _Ty>) + expected(_Uty&& _Other) noexcept(is_nothrow_constructible_v<_Ty, _Uty>) /* strengthened */ + : _Has_value(true), _Value(_STD forward<_Uty>(_Other)) {} + // clang-format on + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr expected(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(false), _Unexpected(_Other._Unexpected) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + + template + requires is_constructible_v<_Ty, _Args...> + constexpr explicit expected(in_place_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Ty, _Args...>) /* strengthened */ + : _Has_value(true), _Value(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Ty, initializer_list<_Uty>&,_Args... > + constexpr explicit expected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Ty, initializer_list<_Uty>&, _Args...>) /* strengthened */ + : _Has_value(true), _Value(_Ilist, _STD forward<_Args>(_Vals)...) {} + // clang-format on + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Err, initializer_list<_Uty>&, _Args... > + constexpr explicit expected(unexpect_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + // clang-format on + + // [expected.object.dtor] + constexpr ~expected() noexcept { + if (_Has_value) { + _Value.~_Ty(); + } else { + _Unexpected.~_Err(); + } + } + + // clang-format off + ~expected() requires is_trivially_destructible_v<_Ty> && is_trivially_destructible_v<_Err> = default; + // clang-format on + + + // [expected.object.assign] + template + static constexpr void _Reinit_expected(_First& _New_val, _Second& _Old_val, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_First, _Args...> || is_nothrow_move_constructible_v<_First>) // strengthened + { + if constexpr (is_nothrow_constructible_v<_First, _Args...>) { + _STD destroy_at(_STD addressof(_Old_val)); + _STD construct_at(_STD addressof(_New_val), _STD forward<_Args>(_Vals)...); + } else if constexpr (is_nothrow_move_constructible_v<_First>) { + _First _Tmp(_STD forward<_Args>(_Vals)...); + _STD destroy_at(_STD addressof(_Old_val)); + _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); + } else { + _Second _Tmp(_STD move(_Old_val)); + _STD destroy_at(addressof(_Old_val)); + try { + _STD construct_at(addressof(_New_val), _STD forward<_Args>(_Vals)...); + } catch (...) { + _STD construct_at(addressof(_Old_val), _STD move(_Tmp)); + throw; + } + } + } + + constexpr expected& operator=(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err> // + && is_nothrow_copy_assignable_v<_Ty>&& is_nothrow_copy_assignable_v<_Err>) // strengthened + requires is_copy_assignable_v<_Ty> && is_copy_constructible_v<_Ty> && is_copy_assignable_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Value = _Other._Value; + } else if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _Other._Unexpected); + } else if (_Other._Has_value) { + _Reinit_expected(_Value, _Unexpected, _Other._Value); + } else { + _Unexpected = _Other._Unexpected; + } + + _Has_value = _Other._Has_value; + return *this; + } + + constexpr expected& operator=(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err> // + && is_nothrow_move_assignable_v<_Ty>&& is_nothrow_move_assignable_v<_Err>) // strengthened + requires is_move_assignable_v<_Ty> && is_move_constructible_v<_Ty> // + && is_move_assignable_v<_Err> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Value = _STD move(_Other._Value); + } else if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); + } else if (_Other._Has_value) { + _Reinit_expected(_Value, _Unexpected, _STD move(_Other._Value)); + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + _Has_value = _Other._Has_value; + return *this; + } + + // clang-format off + template + requires (!is_same_v, expected> && !_Is_specialization_v, unexpected> // + && is_constructible_v<_Ty, _Uty> && is_assignable_v<_Ty&, _Uty> && (is_nothrow_constructible_v<_Ty, _Uty> // + || is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>)) + constexpr expected& operator=(_Uty&& _Other) noexcept( + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_assignable_v<_Ty&, _Uty>) { // strengthened + // clang-format on + if (_Has_value) { + _Value = _STD forward<_Uty>(_Other); + } else { + _Reinit_expected(_Value, _Unexpected, _STD forward<_Uty>(_Other)); + } + + _Has_value = true; + return *this; + } + + // clang-format off + template + requires (is_constructible_v<_Err, const _UErr&>&& is_assignable_v<_Err&, const _UErr&> // + && (is_nothrow_constructible_v<_Err, _UErr> || is_nothrow_move_constructible_v<_Ty> // + || is_nothrow_move_constructible_v<_Err>) ) + constexpr expected& operator=(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>&& is_nothrow_assignable_v<_Err&, const _UErr&>) { // strengthened + // clang-format on + if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _Other._Unexpected); + } else { + _Unexpected = _Other._Unexpected; + } + + _Has_value = false; + return *this; + } + + // clang-format off + template + requires (is_constructible_v<_Err, _UErr>&& is_assignable_v<_Err&, _UErr> + && (is_nothrow_constructible_v<_Err, _UErr> || is_nothrow_move_constructible_v<_Ty> // + || is_nothrow_move_constructible_v<_Err>) ) + constexpr expected& operator=(unexpected<_UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Err, _UErr>&& is_nothrow_assignable_v<_Err&, _UErr>) { // strengthened + // clang-format on + if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + _Has_value = false; + return *this; + } + + template + requires is_nothrow_constructible_v<_Ty, _Args...> + constexpr _Ty& emplace(_Args&&... _Vals) noexcept { + if (_Has_value) { + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } + } else { + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } + _Has_value = true; + } + + return *_STD construct_at(_STD addressof(_Value), _STD forward<_Args>(_Vals)...); + } + + // clang-format off + template + requires is_nothrow_constructible_v<_Ty, initializer_list<_Uty>&, _Args...> + constexpr _Ty& emplace(initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept { + // clang-format on + if (_Has_value) { + _Value.~_Ty(); + } else { + _Unexpected.~_Err(); + _Has_value = true; + } + + return *_STD construct_at(_STD addressof(_Value), _Ilist, _STD forward<_Args>(_Vals)...); + } + + // [expected.object.swap] + constexpr void swap(expected& _Other) noexcept(is_nothrow_move_constructible_v<_Ty>&& is_nothrow_swappable_v<_Ty>&& + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Ty> && is_swappable_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Swap_adl(_Value, _Other._Value); + } else if (_Has_value) { + if constexpr (is_nothrow_move_constructible_v<_Ty> && is_nothrow_move_constructible_v<_Err>) { + _Err _Tmp(_STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Value.~_Ty(); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); + } else if constexpr (is_nothrow_move_constructible_v<_Err>) { + _Err _Tmp(_STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + try { + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Value.~_Ty(); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); + } catch (...) { + _STD construct_at(_STD addressof(_Other._Unexpected), _STD move(_Tmp)); + throw; + } + } else { + _Ty _Tmp(_STD move(_Value)); + _Value.~_Ty(); + try { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); + } catch (...) { + _STD construct_at(_STD addressof(_Value), _STD move(_Tmp)); + throw; + } + } + + _Has_value = false; + _Other._Has_value = true; + } else if (_Other._Has_value) { + _Other.swap(*this); + } else { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + } + + friend constexpr void swap(expected& _Lhs, expected& _Rhs) noexcept(is_nothrow_move_constructible_v<_Ty>&& + is_nothrow_swappable_v<_Ty>&& is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Ty> && is_swappable_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + _Lhs.swap(_Rhs); + } + + // [expected.object.observe] + _NODISCARD constexpr const _Ty* operator->() const noexcept { + return _STD addressof(_Value); + } + _NODISCARD constexpr _Ty* operator->() noexcept { + return _STD addressof(_Value); + } + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) operator*(this expected&& _Self) { + return _STD forward(_Self)._Value; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Ty& operator*() const& noexcept { + return _Value; + } + _NODISCARD constexpr _Ty& operator*() & noexcept { + return _Value; + } + _NODISCARD constexpr const _Ty&& operator*() const&& noexcept { + return _STD move(_Value); + } + _NODISCARD constexpr _Ty&& operator*() && noexcept { + return _STD move(_Value); + } +#endif // !__cpp_explicit_this_parameter + + _NODISCARD constexpr explicit operator bool() const noexcept { + return _Has_value; + } + _NODISCARD constexpr bool has_value() const noexcept { + return _Has_value; + } + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) value(this expected&& _Self) { + if (_Has_value) { + return _STD forward(_Self)._Value; + } + _Throw_bad_expected_access(); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Ty& value() const& { + if (_Has_value) { + return _Value; + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr _Ty& value() & { + if (_Has_value) { + return _Value; + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr const _Ty&& value() const&& { + if (_Has_value) { + return _STD move(_Value); + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr _Ty&& value() && { + if (_Has_value) { + return _STD move(_Value); + } + _Throw_bad_expected_access(); + } +#endif // !__cpp_explicit_this_parameter + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + template _Uty> + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened + requires is_copy_constructible_v<_Ty> { + if (_Has_value) { + return _Value; + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + template _Uty> + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened + requires is_move_constructible_v<_Ty> { + if (_Has_value) { + return _STD move(_Value); + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + + // [expected.object.eq] +#ifdef __clang__ // TRANSITION, LLVM-XXXXX + template _UErr> +#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv + template <_Weakly_equality_comparable_with<_Ty> _Uty, _Weakly_equality_comparable_with<_Err> _UErr> +#endif // !__clang__ + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Value == *_Right)) && noexcept( + _Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value != _Right.has_value()) { + return false; + } else if (_Left._Has_value) { + return _Left._Value == *_Right; + } else { + return _Left._Unexpected == _Right.error(); + } + } + +#ifdef __clang__ // TRANSITION, LLVM-XXXXX + template +#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv + template <_Weakly_equality_comparable_with<_Ty> _Uty> +#endif // !__clang__ + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened + if (!_Left._Has_value) { + return false; + } else { + return _Left._Value == _Right; + } + } + + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return _Left._Unexpected == _Right.error(); + } + } + +private: +#ifdef __cpp_explicit_this_parameter + [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { + _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + [[noreturn]] inline void _Throw_bad_expected_access() const& { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] inline void _Throw_bad_expected_access() & { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() const&& { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() && { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } +#endif // !__cpp_explicit_this_parameter + + bool _Has_value; + union { + _Ty _Value; + _Err _Unexpected; + }; +}; + +template + requires is_void_v<_Ty> +class expected<_Ty, _Err> { +public: + using value_type = _Ty; + using error_type = _Err; + using unexpected_type = unexpected<_Err>; + + template + using rebind = expected<_Uty, error_type>; + + // [expected.void.ctor] + constexpr expected() noexcept : _Has_value(true) {} + + // clang-format off + constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) /* strengthened */ + requires (!is_trivially_copy_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + expected(const expected& ) requires is_trivially_copy_constructible_v<_Err> = default; + + constexpr expected(expected&& _Other) noexcept(is_nothrow_move_constructible_v<_Err>) /* strengthened */ + requires (!is_trivially_move_constructible_v<_Err> && is_move_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + } + } + + expected(expected&&) requires is_trivially_move_constructible_v<_Err> = default; + // clang-format on + + template + static constexpr bool _Is_not_convertible = !is_constructible_v, expected<_Uty, _UErr>&> // + && !is_constructible_v, expected<_Uty, _UErr>> // + && !is_constructible_v, const expected<_Uty, _UErr>&> // + && !is_constructible_v, const expected<_Uty, _UErr>>; + + template + requires is_void_v<_Uty> && is_constructible_v<_Err, const _UErr&> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v) expected(const expected<_Uty, _UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _Unexpected = _Other._Unexpected; + } + } + + template + requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v<_UErr, _Err>) + expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _Unexpected = _STD forward<_UErr>(_Other._Unexpected); + } + } + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr expected(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(false), _Unexpected(_Other._Unexpected) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + + constexpr explicit expected(in_place_t) noexcept : _Has_value(true) {} + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + + + // [expected.void.dtor] + constexpr ~expected() noexcept { + if (!_Has_value) { + _Unexpected.~_Err(); + } + } + + // clang-format off + ~expected() requires is_trivially_destructible_v<_Err> = default; + // clang-format on + + // [expected.void.assign] + constexpr expected& operator=(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Err>&& is_nothrow_copy_assignable_v<_Err>) // strengthened + requires is_copy_assignable_v<_Err> && is_copy_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing to do + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + _Has_value = false; + } else if (_Other._Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } else { + _Unexpected = _Other._Unexpected; + } + + return *this; + } + + constexpr expected& operator=(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_move_assignable_v<_Err>) // strengthened + requires is_move_assignable_v<_Err> && is_move_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing to do + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Has_value = false; + } else if (_Other._Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + return *this; + } + + template + requires is_constructible_v<_Err, const _UErr&> && is_assignable_v<_Err&, const _UErr&> + constexpr expected& operator=(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>&& is_nothrow_assignable_v<_Err&, const _UErr&>) { // strengthened + if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + _Has_value = false; + } else { + _Unexpected = _Other._Unexpected; + } + + return *this; + } + + template + requires is_constructible_v<_Err, _UErr> && is_assignable_v<_Err&, _UErr> + constexpr expected& operator=(unexpected<_UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Err, _UErr>&& is_nothrow_assignable_v<_Err&, _UErr>) { // strengthened + if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Has_value = false; + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + return *this; + } + + constexpr void emplace() noexcept { + if (!_Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } + } + + // [expected.void.swap] + constexpr void swap(expected& _Other) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Err> && is_move_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _Has_value = false; + _Other._Has_value = true; + } else if (_Other._Has_value) { + _STD construct_at(_STD addressof(_Other._Unexpected), _STD move(_Unexpected)); + _Unexpected.~_Err(); + _Has_value = true; + _Other._Has_value = false; + } else { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + } + + friend constexpr void swap(expected& _Left, expected& _Right) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Err> && is_move_constructible_v<_Err> { + if (_Left._Has_value && _Right._Has_value) { + // nothing + } else if (_Left._Has_value) { + _STD construct_at(_STD addressof(_Left._Unexpected), _STD move(_Right._Unexpected)); + _Right._Unexpected.~_Err(); + _Left._Has_value = false; + _Right._Has_value = true; + } else if (_Right._Has_value) { + _STD construct_at(_STD addressof(_Right._Unexpected), _STD move(_Left._Unexpected)); + _Left._Unexpected.~_Err(); + _Left._Has_value = true; + _Right._Has_value = false; + } else { + _Swap_adl(_Left._Unexpected, _Right._Unexpected); + } + } + + // [expected.void.observe] + constexpr explicit operator bool() const noexcept { + return _Has_value; + } + constexpr bool has_value() const noexcept { + return _Has_value; + } + + constexpr void operator*() const noexcept {} +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr void value(this expected&& _Self) { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr void value() const& { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() & { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() const&& { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() && { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } +#endif // !__cpp_explicit_this_parameter + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + // [expected.void.eq] + template _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value != _Right.has_value()) { + return false; + } else { + return _Left._Has_value || static_cast(_Left._Unexpected == _Right.error()); + } + } + + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return static_cast(_Left._Unexpected == _Right.error()); + } + } + +private: +#ifdef __cpp_explicit_this_parameter + [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { + _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + [[noreturn]] inline void _Throw_bad_expected_access() const& { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] inline void _Throw_bad_expected_access() & { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() const&& { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() && { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } +#endif // !__cpp_explicit_this_parameter + + bool _Has_value; + union { + _Err _Unexpected; + }; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX23 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _EXPECTED_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index c323fb152da..8d40a1b4ebc 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -54,6 +54,7 @@ "deque", "exception", "execution", + "expected", "filesystem", "format", "forward_list", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 08fd1007998..a12ee701de6 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -281,6 +281,7 @@ // _HAS_CXX23 directly controls: // P0288R9 move_only_function +// P0323R12 // P0401R6 Providing Size Feedback In The Allocator Interface // P0448R4 // P0627R6 unreachable() @@ -1372,9 +1373,14 @@ #define __cpp_lib_associative_heterogeneous_erasure 202110L #define __cpp_lib_byteswap 202110L -#define __cpp_lib_invoke_r 202106L -#define __cpp_lib_is_scoped_enum 202011L -#define __cpp_lib_move_only_function 202110L + +#ifdef __cpp_lib_concepts +#define __cpp_lib_expected 202202L +#endif // __cpp_lib_concepts + +#define __cpp_lib_invoke_r 202106L +#define __cpp_lib_is_scoped_enum 202011L +#define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L diff --git a/tests/std/test.lst b/tests/std/test.lst index 2a8cf5e9ac2..5c8106e83cf 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -254,6 +254,7 @@ tests\P0220R1_searchers tests\P0220R1_string_view tests\P0288R9_move_only_function tests\P0295R0_gcd_lcm +tests\P0323R12_expected tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_clocks diff --git a/tests/std/tests/P0323R12_expected/env.lst b/tests/std/tests/P0323R12_expected/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P0323R12_expected/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp new file mode 100644 index 00000000000..fbaee08dbb2 --- /dev/null +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -0,0 +1,1865 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +enum class IsDefaultConstructible : bool { No, Yes }; +enum class IsTriviallyCopyConstructible : bool { No, Yes }; +enum class IsTriviallyMoveConstructible : bool { No, Yes }; +enum class IsTriviallyDestructible : bool { No, Yes }; + +enum class IsNothrowConstructible : bool { No, Yes }; +enum class IsNothrowCopyConstructible : bool { No, Yes }; +enum class IsNothrowMoveConstructible : bool { No, Yes }; +enum class IsNothrowCopyAssignable : bool { No, Yes }; +enum class IsNothrowMoveAssignable : bool { No, Yes }; +enum class IsNothrowConvertible : bool { No, Yes }; +enum class IsNothrowComparable : bool { No, Yes }; +enum class IsNothrowSwappable : bool { No, Yes }; + +enum class IsExplicitConstructible : bool { No, Yes }; + +struct convertible { + constexpr convertible() = default; + constexpr convertible(const int val) noexcept : _val(val) {} + + [[nodiscard]] constexpr bool operator==(const int other) const noexcept { + return other == _val; + } + + int _val = 0; +}; + +namespace test_unexpected { + template + constexpr void test() { + constexpr bool copy_construction_is_noexcept = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + constexpr bool move_construction_is_noexcept = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool compare_is_noexcept = nothrowComparable == IsNothrowComparable::Yes; + + struct test_error { + constexpr test_error(const int& val) noexcept(copy_construction_is_noexcept) : _val(val) {} + constexpr test_error(int&& val) noexcept(move_construction_is_noexcept) : _val(val) {} + + constexpr test_error(initializer_list, const int& val) noexcept(copy_construction_is_noexcept) + : _val(val) {} + constexpr test_error(initializer_list, int&& val) noexcept(move_construction_is_noexcept) + : _val(val) {} + + constexpr test_error(const convertible& other) noexcept(copy_construction_is_noexcept) : _val(other._val) {} + constexpr test_error(convertible&& other) noexcept(move_construction_is_noexcept) : _val(other._val) {} + + [[nodiscard]] constexpr bool operator==(const test_error& right) const noexcept(compare_is_noexcept) { + return _val == right._val; + } + [[nodiscard]] constexpr bool operator==(const convertible& right) const noexcept(compare_is_noexcept) { + return _val == right._val; + } + + int _val = 0; + }; + using Unexpect = std::unexpected; + + // [expected.un.ctor] + const int& input = 1; + Unexpect in_place_lvalue_constructed{in_place, input}; + static_assert(noexcept(Unexpect{in_place, input}) == copy_construction_is_noexcept); + assert(in_place_lvalue_constructed == Unexpect{test_error{1}}); + + Unexpect in_place_rvalue_constructed{in_place, 42}; + static_assert(noexcept(Unexpect{in_place, 42}) == move_construction_is_noexcept); + assert(in_place_rvalue_constructed == Unexpect{test_error{42}}); + + Unexpect in_place_ilist_lvalue_constructed{in_place, {2}, input}; + static_assert(noexcept(Unexpect{in_place, {2}, input}) == copy_construction_is_noexcept); + assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}}); + + Unexpect in_place_ilist_rvalue_constructed{in_place, {2}, 1337}; + static_assert(noexcept(Unexpect{in_place, {2}, 1337}) == move_construction_is_noexcept); + assert(in_place_ilist_rvalue_constructed == Unexpect{test_error{1337}}); + + Unexpect base_error_constructed{test_error{3}}; + static_assert(noexcept(Unexpect{test_error{3}}) == move_construction_is_noexcept); + + Unexpect conversion_error_constructed{convertible{4}}; + static_assert(noexcept(Unexpect{convertible{4}}) == move_construction_is_noexcept); + + // [expected.un.eq] + assert(in_place_lvalue_constructed == in_place_lvalue_constructed); + assert(in_place_lvalue_constructed != in_place_rvalue_constructed); + static_assert(noexcept(in_place_lvalue_constructed == in_place_lvalue_constructed) == compare_is_noexcept); + static_assert(noexcept(in_place_lvalue_constructed != in_place_lvalue_constructed) == compare_is_noexcept); + + const auto converted = std::unexpected{convertible{3}}; + assert(base_error_constructed == converted); + assert(conversion_error_constructed != converted); + static_assert(noexcept(base_error_constructed == converted) == compare_is_noexcept); + static_assert(noexcept(conversion_error_constructed != converted) == compare_is_noexcept); + + // [expected.un.swap] + in_place_lvalue_constructed.swap(in_place_rvalue_constructed); + assert(in_place_lvalue_constructed == Unexpect{test_error{42}}); + assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}}); + + swap(base_error_constructed, conversion_error_constructed); + assert(base_error_constructed == Unexpect{test_error{4}}); + assert(conversion_error_constructed == Unexpect{test_error{3}}); + + // [expected.un.observe] + auto&& lvalue_error = base_error_constructed.error(); + assert(lvalue_error == test_error{4}); + static_assert(is_same_v); + + auto&& rvalue_error = move(conversion_error_constructed).error(); + assert(rvalue_error == test_error{3}); + static_assert(is_same_v); + + auto&& const_lvalue_error = as_const(in_place_lvalue_constructed).error(); + assert(const_lvalue_error == test_error{42}); + static_assert(is_same_v); + + auto&& const_rvalue_error = move(as_const(in_place_ilist_lvalue_constructed)).error(); + assert(const_rvalue_error == test_error{1}); + static_assert(is_same_v); + } + + constexpr bool test_all() { + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + + return true; + } +} // namespace test_unexpected + +namespace test_unexpect { + auto copy = unexpect; + static_assert(is_same_v); + static_assert(is_trivial_v); + static_assert(is_empty_v); +} // namespace test_unexpect + +namespace test_expected { + constexpr void test_aliases() { + struct value_tag {}; + struct error_tag {}; + + { + using Expected = expected; + static_assert(same_as); + static_assert(same_as); + static_assert(same_as>); + + static_assert(same_as, expected>); + } + + { + using Expected = expected; + static_assert(same_as); + static_assert(same_as); + static_assert(same_as>); + + static_assert(same_as, expected>); + } + } + + template + constexpr void test_default_constructors() { + constexpr bool should_be_defaultable = defaultConstructible == IsDefaultConstructible::Yes; + + struct payload_default_constructor { + constexpr payload_default_constructor() requires(should_be_defaultable) : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + static_assert(is_default_constructible_v> == should_be_defaultable); + // we only care about payload type + static_assert(is_default_constructible_v>); + static_assert(is_default_constructible_v>); + + if constexpr (should_be_defaultable) { + const expected defaulted; + assert(defaulted.value() == 42); + } + } + + template + constexpr void test_copy_constructors() { + constexpr bool should_be_trivial = triviallyCopyConstructible == IsTriviallyCopyConstructible::Yes; + constexpr bool should_be_noexcept = + should_be_trivial || nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + + struct payload_copy_constructor { + payload_copy_constructor() = default; + payload_copy_constructor& operator=(const payload_copy_constructor&) = delete; + constexpr payload_copy_constructor(const payload_copy_constructor&) requires should_be_trivial = default; + constexpr payload_copy_constructor(const payload_copy_constructor&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // Check payload type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + assert(from_value.value() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == 0); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + + { // Check error type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + assert(from_value.value() == 0); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + + { // Check void payload + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + } + + template + constexpr void test_move_constructors() { + constexpr bool should_be_trivial = triviallyMoveConstructible == IsTriviallyMoveConstructible::Yes; + constexpr bool should_be_noexcept = + should_be_trivial || nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + + struct payload_move_constructor { + payload_move_constructor() = default; + payload_move_constructor(const payload_move_constructor&) = default; + payload_move_constructor& operator=(payload_move_constructor&&) = delete; + constexpr payload_move_constructor(payload_move_constructor&&) requires should_be_trivial = default; + constexpr payload_move_constructor(payload_move_constructor&&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // Check payload type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + assert(from_value.value() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == 0); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + + { // Check error type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + assert(from_value.value() == 0); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + + { // Check void payload + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + } + + template + struct payload_destructor { + constexpr payload_destructor(bool& destructor_called) : _destructor_called(destructor_called) {} + bool& _destructor_called; + }; + template <> // TRANSITION, LLVM-46269 + struct payload_destructor { + constexpr payload_destructor(bool& destructor_called) : _destructor_called(destructor_called) {} + payload_destructor(const payload_destructor&) = default; + constexpr ~payload_destructor() { + _destructor_called = true; + }; + + bool& _destructor_called; + }; + template + constexpr void test_destructors() { + constexpr bool is_trivial = triviallyDestructible == IsTriviallyDestructible::Yes; + bool destructor_called = false; + { // Check payload + using Expected = expected, int>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected val{in_place, destructor_called}; + } + assert(destructor_called == !is_trivial); + destructor_called = false; + + { // Check error + using Expected = expected>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected err{unexpect, destructor_called}; + } + assert(destructor_called == !is_trivial); + destructor_called = false; + + { // Check void error + using Expected = expected>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected err{unexpect, destructor_called}; + } + assert(destructor_called == !is_trivial); + } + + constexpr void test_special_members() { + test_default_constructors(); + test_default_constructors(); + + test_copy_constructors(); + test_copy_constructors(); + test_copy_constructors(); + test_copy_constructors(); + + test_move_constructors(); + test_move_constructors(); + test_move_constructors(); + test_move_constructors(); + + test_destructors(); + test_destructors(); + } + + template + constexpr void test_constructors() noexcept { + constexpr bool should_be_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; + constexpr bool should_be_explicit = explicitConstructible == IsExplicitConstructible::Yes; + + struct payload_constructors { + payload_constructors() = default; + // Note clang does not accept local variables in explicit + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(const convertible&) noexcept(should_be_noexcept) + : _val(3) {} + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(convertible&&) noexcept(should_be_noexcept) + : _val(42) {} + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(initializer_list&, convertible) noexcept(should_be_noexcept) + : _val(1337) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // constructing from convertible payload + using Input = convertible; + using Expected = expected; + static_assert(is_convertible_v != should_be_explicit); + static_assert(is_convertible_v != should_be_explicit); + + const Input const_input_value{in_place}; + const Expected copy_constructed_value{const_input_value}; + assert(copy_constructed_value); + assert(copy_constructed_value.value() == 3); + static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept); + + const Expected move_constructed_value{Input{}}; + assert(move_constructed_value); + assert(move_constructed_value.value() == 42); + static_assert(noexcept(Expected{Input{}}) == should_be_noexcept); + } + + { // converting from different expected + using Input = expected; + using Expected = expected; + static_assert(is_convertible_v != should_be_explicit); + static_assert(is_convertible_v != should_be_explicit); + + const Input const_input_value{in_place}; + const Expected copy_constructed_value{const_input_value}; + assert(copy_constructed_value); + assert(copy_constructed_value.value() == 3); + static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept); + + const Expected move_constructed_value{Input{in_place}}; + assert(move_constructed_value); + assert(move_constructed_value.value() == 42); + static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept); + + const Input const_input_error{unexpect}; + const Expected copy_constructed_error{const_input_error}; + assert(!copy_constructed_error); + assert(copy_constructed_error.error() == 3); + static_assert(noexcept(Expected{const_input_error}) == should_be_noexcept); + + const Expected move_constructed_error{Input{unexpect}}; + assert(!move_constructed_error); + assert(move_constructed_error.error() == 42); + static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept); + } + + { // converting from unexpected + using Input = std::unexpected; + using Expected = expected; + + const Input const_input{in_place}; + const Expected copy_constructed{const_input}; + assert(!copy_constructed); + assert(copy_constructed.error() == 3); + static_assert(noexcept(Expected{const_input}) == should_be_noexcept); + + const Expected move_constructed{Input{in_place}}; + assert(!move_constructed); + assert(move_constructed.error() == 42); + static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept); + } + + { // in place payload + using Expected = expected; + const Expected default_constructed{in_place}; + assert(default_constructed); + assert(default_constructed.value() == 0); + static_assert(noexcept(Expected{in_place})); + + const Expected value_constructed{in_place, convertible{}}; + assert(value_constructed); + assert(value_constructed.value() == 42); + static_assert(noexcept(Expected{in_place, convertible{}}) == should_be_noexcept); + + const Expected ilist_value_constructed{in_place, {1}, convertible{}}; + assert(ilist_value_constructed); + assert(ilist_value_constructed.value() == 1337); + static_assert(noexcept(Expected{in_place, {1}, convertible{}}) == should_be_noexcept); + } + + { // in place error + using Expected = expected; + const Expected default_constructed{unexpect}; + assert(!default_constructed); + assert(default_constructed.error() == 0); + static_assert(noexcept(Expected{unexpect})); + + const Expected value_constructed{unexpect, convertible{}}; + assert(!value_constructed); + assert(value_constructed.error() == 42); + static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept); + + const Expected ilist_value_constructed{unexpect, {1}, convertible{}}; + assert(!ilist_value_constructed); + assert(ilist_value_constructed.error() == 1337); + static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept); + } + } + + constexpr void test_constructors() noexcept { + test_constructors(); + test_constructors(); + test_constructors(); + test_constructors(); + } + + template + constexpr void test_assignment() noexcept { + constexpr bool nothrow_copy_constructible = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool nothrow_copy_assignable = nothrowCopyAssignable == IsNothrowCopyAssignable::Yes; + constexpr bool nothrow_move_assignable = nothrowMoveAssignable == IsNothrowMoveAssignable::Yes; + + struct payload_assign { + payload_assign() = default; + constexpr payload_assign(const int val) noexcept : _val(val) {} + constexpr payload_assign(const payload_assign& other) noexcept(nothrow_copy_constructible) + : _val(other._val) {} + constexpr payload_assign(payload_assign&& other) noexcept(nothrow_move_constructible) : _val(other._val) {} + constexpr payload_assign& operator=(const payload_assign& other) noexcept(nothrow_copy_assignable) { + _val = other._val; + return *this; + } + constexpr payload_assign& operator=(payload_assign&& other) noexcept(nothrow_move_assignable) { + _val = other._val; + return *this; + } + + constexpr payload_assign(const convertible& other) noexcept(nothrow_copy_constructible) + : _val(other._val) {} + constexpr payload_assign(convertible&& other) noexcept(nothrow_move_constructible) : _val(other._val) {} + constexpr payload_assign& operator=(const convertible& other) noexcept(nothrow_copy_assignable) { + _val = other._val; + return *this; + } + constexpr payload_assign& operator=(convertible&& other) noexcept(nothrow_move_assignable) { + _val = other._val; + return *this; + } + + [[nodiscard]] constexpr bool operator==(const int other) const noexcept { + return other == _val; + } + int _val = 0; + }; + + { // assign same expected as const ref check payload + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place, 42}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign same expected as const ref check error + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place, 42}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + { // assign same expected as const ref check error + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place}; + assign_value_to_value = input_value; + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign same expected as rvalue check payload + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = Expected{in_place, 42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place, 42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + + { // assign same expected as rvalue check error + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = Expected{in_place, 42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place, 42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + { // assign same expected as rvalue check error + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place}; + assign_value_to_value = Expected{in_place}; + static_assert(noexcept(assign_value_to_value = Expected{in_place}) == should_be_noexcept); + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place}; + static_assert(noexcept(assign_value_to_error = Expected{in_place}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + + { // assign base type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const payload_assign input_value{42}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + } + + { // assign base type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = payload_assign{42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = convertible{42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = payload_assign{42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = convertible{42}) == should_be_noexcept); + } + + { // assign convertible type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const convertible input_value{42}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + } + + { // assign convertible type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = convertible{42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = convertible{42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = convertible{42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = convertible{42}) == should_be_noexcept); + } + + { // assign error type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign expected error type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign error type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign expected error type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign convertible error const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign expected convertible error const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign convertible error rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign expected convertible error rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + } + + constexpr void test_assignment() noexcept { + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + } + + constexpr void test_emplace() noexcept { + struct payload_emplace { + constexpr payload_emplace(bool& destructor_called) noexcept : _destructor_called(destructor_called) {} + constexpr payload_emplace(bool& destructor_called, const convertible&) noexcept + : _destructor_called(destructor_called), _val(3) {} + constexpr payload_emplace(bool& destructor_called, convertible&&) noexcept + : _destructor_called(destructor_called), _val(42) {} + constexpr payload_emplace(initializer_list&, bool& destructor_called, convertible) noexcept + : _destructor_called(destructor_called), _val(1337) {} + constexpr ~payload_emplace() { + _destructor_called = true; + } + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + bool& _destructor_called; + int _val = 0; + }; + using Expected = expected; + + bool destructor_called = false; + { + const convertible input; + Expected emplaced_lvalue(destructor_called); + emplaced_lvalue.emplace(destructor_called, input); + assert(destructor_called); + assert(emplaced_lvalue.value() == 3); + } + + destructor_called = false; + { + const convertible input; + Expected emplaced_lvalue(unexpect); + emplaced_lvalue.emplace(destructor_called, input); + assert(!destructor_called); + assert(emplaced_lvalue.value() == 3); + } + + destructor_called = false; + { + Expected emplaced_rvalue(destructor_called); + emplaced_rvalue.emplace(destructor_called, convertible{}); + assert(destructor_called); + assert(emplaced_rvalue.value() == 42); + } + + destructor_called = false; + { + Expected emplaced_rvalue(unexpect); + emplaced_rvalue.emplace(destructor_called, convertible{}); + assert(!destructor_called); + assert(emplaced_rvalue.value() == 42); + } + + destructor_called = false; + { + Expected emplaced_ilist(destructor_called); + emplaced_ilist.emplace({1}, destructor_called, convertible{}); + assert(destructor_called); + assert(emplaced_ilist.value() == 1337); + } + + destructor_called = false; + { + Expected emplaced_ilist(unexpect); + emplaced_ilist.emplace({1}, destructor_called, convertible{}); + assert(!destructor_called); + assert(emplaced_ilist.value() == 1337); + } + + { + using ExpectedVoid = expected; + ExpectedVoid with_value{in_place}; + with_value.emplace(); + assert(with_value); + + ExpectedVoid with_error{unexpect, 42}; + with_error.emplace(); + assert(with_error); + } + } + + template + struct payload_swap { + constexpr payload_swap(const int val) noexcept : _val(val) {} + constexpr payload_swap(const payload_swap&) noexcept = default; + constexpr payload_swap(payload_swap&& other) noexcept( + nothrowMoveConstructible == IsNothrowMoveConstructible::Yes) + : _val(other._val + 42) {} + // Note: cannot declare friends of function local structs + constexpr friend void swap(payload_swap& left, payload_swap& right) noexcept( + nothrowSwappable == IsNothrowSwappable::Yes) { + left._val = exchange(right._val, left._val); + } + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + template + constexpr void test_swap() noexcept { + constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrowSwappable == IsNothrowSwappable::Yes; + + { // Check payload member + using Expected = expected, int>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5); + assert(first_error.value() == 1337 + 42); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3); + assert(second_error.value() == 1 + 42); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check error member + using Expected = expected>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + if constexpr (nothrow_move_constructible) { + assert(first_value.error() == 5 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(first_value.error() == 5 + 42); + } + assert(first_error.value() == 1337); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + if constexpr (nothrow_move_constructible) { + assert(second_value.error() == 3 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(second_value.error() == 3 + 42); + } + assert(second_error.value() == 1); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check expected error member + using Expected = expected>; + Expected first_value{in_place}; + Expected second_value{in_place}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5 + 42); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3 + 42); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check payload friend + using Expected = expected, int>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5); + assert(first_error.value() == 1337 + 42); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3); + assert(second_error.value() == 1 + 42); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + + { // Check error friend + using Expected = expected>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + if constexpr (nothrow_move_constructible) { + assert(first_value.error() == 5 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(first_value.error() == 5 + 42); + } + assert(first_error.value() == 1337); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + if constexpr (nothrow_move_constructible) { + assert(second_value.error() == 3 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(second_value.error() == 3 + 42); + } + assert(second_error.value() == 1); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + + { // Check expected error friend + using Expected = expected>; + Expected first_value{in_place}; + Expected second_value{in_place}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5 + 42); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3 + 42); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + } + + constexpr void test_swap() noexcept { + test_swap(); + test_swap(); + test_swap(); + test_swap(); + } + + constexpr void test_access() noexcept { + struct payload_access { + constexpr payload_access() noexcept : _val(this) {} + constexpr payload_access* operator&() noexcept { + return nullptr; + } + constexpr payload_access* operator&() const noexcept { + return nullptr; + } + + payload_access* _val; + }; + + { // operator->() + using Expected = expected; + Expected val; + assert(val.value()._val == val.operator->()); + assert(&val.value() == nullptr); + + const Expected const_val; + assert(&const_val.value() == nullptr); + } + + { // operator*() + using Expected = expected; + Expected lvalue{1}; + auto&& from_lvalue = *lvalue; + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{42}; + auto&& from_rvalue = *move(rvalue); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{1337}; + auto&& from_const_lvalue = *const_lvalue; + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{-42}; + auto&& from_const_rvalue = *move(const_rvalue); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected operator*() + using Expected = expected; + Expected lvalue{in_place}; + static_assert(is_same_v); + + Expected rvalue{in_place}; + static_assert(is_same_v); + + const Expected const_lvalue{in_place}; + static_assert(is_same_v); + + const Expected const_rvalue{in_place}; + static_assert(is_same_v); + } + + { // operator bool() + using Expected = expected; + const Expected defaulted; + assert(defaulted); + assert(defaulted.has_value()); + + const Expected with_value{in_place, 5}; + assert(with_value); + assert(with_value.has_value()); + + const Expected with_error{unexpect, 5}; + assert(!with_error); + assert(!with_error.has_value()); + } + + { // expected operator bool() + using Expected = expected; + const Expected defaulted; + assert(defaulted); + assert(defaulted.has_value()); + + const Expected with_value{in_place}; + assert(with_value); + assert(with_value.has_value()); + + const Expected with_error{unexpect}; + assert(!with_error); + assert(!with_error.has_value()); + } + + { // value() + using Expected = expected; + Expected lvalue{1}; + auto&& from_lvalue = lvalue.value(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{42}; + auto&& from_rvalue = move(rvalue).value(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{1337}; + auto&& from_const_lvalue = const_lvalue.value(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{-42}; + auto&& from_const_rvalue = move(const_rvalue).value(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected value() + using Expected = expected; + Expected lvalue{in_place}; + static_assert(is_same_v); + + Expected rvalue{in_place}; + static_assert(is_same_v); + + const Expected const_lvalue{in_place}; + static_assert(is_same_v); + + const Expected const_rvalue{in_place}; + static_assert(is_same_v); + } + + if (!is_constant_evaluated()) { // invalid value() + using Expected = expected; + try { + Expected lvalue{unexpect, 1}; + [[maybe_unused]] auto&& from_lvalue = lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1); + static_assert(is_same_v); + } + + try { + Expected rvalue{unexpect, 42}; + [[maybe_unused]] auto&& from_rvalue = move(rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 42); + static_assert(is_same_v); + } + + try { + const Expected const_lvalue{unexpect, 1337}; + [[maybe_unused]] auto&& from_rvalue = const_lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1337); + static_assert(is_same_v); + } + + try { + const Expected const_rvalue{unexpect, -42}; + [[maybe_unused]] auto&& from_const_rvalue = move(const_rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == -42); + static_assert(is_same_v); + } + } + + if (!is_constant_evaluated()) { // expected invalid value() + using Expected = expected; + try { + Expected lvalue{unexpect, 1}; + lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1); + static_assert(is_same_v); + } + + try { + Expected rvalue{unexpect, 42}; + move(rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 42); + static_assert(is_same_v); + } + + try { + const Expected const_lvalue{unexpect, 1337}; + const_lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1337); + static_assert(is_same_v); + } + + try { + const Expected const_rvalue{unexpect, -42}; + move(const_rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == -42); + static_assert(is_same_v); + } + } + + { // error() + using Expected = expected; + Expected lvalue{unexpect, 1}; + auto&& from_lvalue = lvalue.error(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{unexpect, 42}; + auto&& from_rvalue = move(rvalue).error(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{unexpect, 1337}; + auto&& from_const_lvalue = const_lvalue.error(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{unexpect, -42}; + auto&& from_const_rvalue = move(const_rvalue).error(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected error() + using Expected = expected; + Expected lvalue{unexpect, 1}; + auto&& from_lvalue = lvalue.error(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{unexpect, 42}; + auto&& from_rvalue = move(rvalue).error(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{unexpect, 1337}; + auto&& from_const_lvalue = const_lvalue.error(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{unexpect, -42}; + auto&& from_const_rvalue = move(const_rvalue).error(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + } + + template + constexpr void test_monadic() { + constexpr bool construction_is_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; + constexpr bool conversion_is_noexcept = nothrowConvertible == IsNothrowConvertible::Yes; + constexpr bool should_be_noexcept = construction_is_noexcept && conversion_is_noexcept; + + struct payload_monadic { + constexpr payload_monadic(const int val) noexcept : _val(val) {} + constexpr payload_monadic(const payload_monadic& other) noexcept(construction_is_noexcept) + : _val(other._val + 2) {} + constexpr payload_monadic(payload_monadic&& other) noexcept(construction_is_noexcept) + : _val(other._val + 3) {} + constexpr payload_monadic(const convertible& val) noexcept(conversion_is_noexcept) : _val(val._val + 4) {} + constexpr payload_monadic(convertible&& val) noexcept(conversion_is_noexcept) : _val(val._val + 5) {} + + [[nodiscard]] constexpr bool operator==(const payload_monadic& right) const noexcept { + return _val == right._val; + } + + int _val = 0; + }; + + { // with payload argument + using Expected = expected; + + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.value_or(payload_monadic{1}) == 42 + 2); + assert(const_with_value.value_or(payload_monadic{1}) == 1337 + 2); + static_assert(noexcept(with_value.value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_value.value_or(payload_monadic{1})) == construction_is_noexcept); + + assert(move(with_value).value_or(payload_monadic{1}) == 42 + 3); + assert(move(const_with_value).value_or(payload_monadic{1}) == 1337 + 2); + static_assert(noexcept(move(with_value).value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_value).value_or(payload_monadic{1})) == construction_is_noexcept); + + const payload_monadic input{2}; + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.value_or(payload_monadic{1}) == 1 + 3); + assert(const_with_error.value_or(input) == 2 + 2); + static_assert(noexcept(with_error.value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_error.value_or(input)) == construction_is_noexcept); + + assert(move(with_error).value_or(payload_monadic{1}) == 1 + 3); + assert(move(const_with_error).value_or(input) == 2 + 2); + static_assert(noexcept(move(with_error).value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_error).value_or(input)) == construction_is_noexcept); + } + + { // with convertible argument + using Expected = expected; + + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.value_or(convertible{1}) == 42 + 2); + assert(const_with_value.value_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(with_value.value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_value.value_or(convertible{1})) == should_be_noexcept); + + assert(move(with_value).value_or(convertible{1}) == 42 + 3); + assert(move(const_with_value).value_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(move(with_value).value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_value).value_or(convertible{1})) == should_be_noexcept); + + const convertible input{2}; + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.value_or(convertible{1}) == 1 + 5); + assert(const_with_error.value_or(input) == 2 + 4); + static_assert(noexcept(with_error.value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_error.value_or(input)) == should_be_noexcept); + + assert(move(with_error).value_or(convertible{1}) == 1 + 5); + assert(move(const_with_error).value_or(input) == 2 + 4); + static_assert(noexcept(move(with_error).value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_error).value_or(input)) == should_be_noexcept); + } + } + + constexpr void test_monadic() noexcept { + test_monadic(); + test_monadic(); + test_monadic(); + test_monadic(); + } + + template + constexpr void test_equality() noexcept { + constexpr bool should_be_noexcept = nothrowComparable == IsNothrowComparable::Yes; + + struct payload_equality { + constexpr payload_equality(const int val) noexcept : _val(val) {} + + [[nodiscard]] constexpr bool operator==(const payload_equality& right) const noexcept(should_be_noexcept) { + return _val == right._val; + } + + int _val = 0; + }; + + { // compare against same expected + using Expected = expected; + + const Expected with_value1{in_place, 42}; + const Expected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(with_value1 != with_value2); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const Expected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // expected compare against same expected + using Expected = expected; + + const Expected with_value{in_place}; + assert(with_value == with_value); + assert(!(with_value != with_value)); + static_assert(noexcept(with_value == with_value) == should_be_noexcept); + static_assert(noexcept(with_value != with_value) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const Expected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value != error1); + static_assert(noexcept(with_value != error1) == should_be_noexcept); + } + + { // compare against different expected + using Expected = expected; + using OtherExpected = expected; + + const Expected with_value1{in_place, 42}; + const OtherExpected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(with_value1 != with_value2); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const OtherExpected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // expected compare against different expected + using Expected = expected; + using OtherExpected = expected; + + const Expected with_value1{in_place}; + const OtherExpected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(!(with_value1 != with_value2)); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const OtherExpected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // compare against base type + using Base = payload_equality; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value == Base{42}); + assert(with_value != Base{1337}); + static_assert(noexcept(with_value == Base{42}) == should_be_noexcept); + static_assert(noexcept(with_value != Base{1337}) == should_be_noexcept); + + assert(with_error != 1337); + static_assert(noexcept(with_error != 1337) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // compare against unexpect with same base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // expected compare against unexpect with same base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + } + + { // compare against unexpect with different base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // expected compare against unexpect with different base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + } + } + + constexpr void test_equality() noexcept { + test_equality(); + test_equality(); + } + + constexpr bool test_all() noexcept { + test_aliases(); + test_special_members(); + test_constructors(); + test_assignment(); + test_emplace(); + test_swap(); + test_access(); + test_monadic(); + test_equality(); + + return true; + } +} // namespace test_expected + +int main() { + test_unexpected::test_all(); + static_assert(test_unexpected::test_all()); + test_expected::test_all(); + static_assert(test_expected::test_all()); +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index ea9dda81470..ffff358f324 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc +++ b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc @@ -21,6 +21,7 @@ "deque", "exception", "execution", + "expected", "filesystem", "format", "forward_list", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 80b90bf88ff..8b81c49c414 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -28,6 +28,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 96bf42755d9..46b6d3a694a 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -714,6 +714,20 @@ STATIC_ASSERT(__cpp_lib_execution == 201603L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_expected +#error __cpp_lib_expected is not defined +#elif __cpp_lib_expected != 202202L +#error __cpp_lib_expected is not 202202L +#else +STATIC_ASSERT(__cpp_lib_expected == 202202L); +#endif +#else +#ifdef __cpp_lib_expected +#error __cpp_lib_expected is defined +#endif +#endif + #ifndef __cpp_lib_experimental_erase_if #error __cpp_lib_experimental_erase_if is not defined #elif __cpp_lib_experimental_erase_if != 201411L