diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 318279f3fb..5bcd5e643d 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -131,6 +131,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/debugger/STL.natvis b/stl/debugger/STL.natvis index 5885050d8d..8216bd9f12 100644 --- a/stl/debugger/STL.natvis +++ b/stl/debugger/STL.natvis @@ -247,6 +247,45 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + + + {unex()} + + unex() + + + + + + {unex()} + + unex() + + + + + + + + {value()} + {unex()} + + value() + unex() + + + + + + + void + {unex()} + + unex() + + + + {{ size={$T1} }} diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 3d7a53d472..71c8782195 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 0000000000..00164414f4 --- /dev/null +++ b/stl/inc/expected @@ -0,0 +1,1088 @@ +// 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 +#ifndef __cpp_lib_expected +#pragma message("The contents of are available only with C++23 or later.") +#else // ^^^ !__cpp_lib_expected / __cpp_lib_expected vvv +#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 + +template +class unexpected; + +template +struct _Check_unexpected_argument : true_type { + static_assert(is_object_v<_Err>, "E must be an object type. (N4910 [expected.un.object.general]/1)"); + static_assert(!is_array_v<_Err>, "E must not be an array type. (N4910 [expected.un.object.general]/1)"); + static_assert(!is_const_v<_Err>, "E must not be const. (N4910 [expected.un.object.general]/1)"); + static_assert(!is_volatile_v<_Err>, "E must not be volatile. (N4910 [expected.un.object.general]/1)"); + static_assert(!_Is_specialization_v<_Err, unexpected>, + "E must not be a specialization of unexpected. (N4910 [expected.un.object.general]/1)"); +}; + +// [expected.un.general] +template +class unexpected { + static_assert(_Check_unexpected_argument<_Err>::value); + + template + friend class expected; + +public: + // [expected.un.ctor] + + // clang-format off + 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 + + 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)...) {} + // clang-format on + + // [expected.un.obs] + _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); + } + + // [expected.un.swap] + constexpr void swap(unexpected& _Other) noexcept(is_nothrow_swappable_v<_Err>) { + static_assert(is_swappable_v<_Err>, "E must be swappable"); + _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 + _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 +unexpected(_Err) -> unexpected<_Err>; + +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"; + } + +protected: + bad_expected_access() = default; + bad_expected_access(const bad_expected_access&) = default; + bad_expected_access(bad_expected_access&&) = default; + bad_expected_access& operator=(const bad_expected_access&) = default; + bad_expected_access& operator=(bad_expected_access&&) = default; + +#if !_HAS_EXCEPTIONS + void _Doraise() const override { // perform class-specific exception handling + _RAISE(*this); + } +#endif // !_HAS_EXCEPTIONS +}; + +template +class bad_expected_access : public bad_expected_access { +public: + explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened + : _Unexpected(_STD move(_Unex)) {} + + _NODISCARD const _Err& error() const& noexcept { + return _Unexpected; + } + _NODISCARD _Err& error() & noexcept { + return _Unexpected; + } + _NODISCARD const _Err&& error() const&& noexcept { + return _STD move(_Unexpected); + } + _NODISCARD _Err&& error() && noexcept { + return _STD move(_Unexpected); + } + +private: + _Err _Unexpected; +}; + +struct unexpect_t { + explicit unexpect_t() = default; +}; + +inline constexpr unexpect_t unexpect{}; + +template +class expected { + static_assert(!is_reference_v<_Ty>, "T must not be a reference type. (N4910 [expected.object.general]/2)"); + static_assert(!is_function_v<_Ty>, "T must not be a function type. (N4910 [expected.object.general]/2)"); + static_assert(!is_same_v, in_place_t>, + "T must not be (possibly cv-qualified) in_place_t. (N4910 [expected.object.general]/2)"); + static_assert(!is_same_v, unexpect_t>, + "T must not be (possibly cv-qualified) unexpect_t. (N4910 [expected.object.general]/2)"); + static_assert(!_Is_specialization_v, unexpected>, + "T must not be a (possibly cv-qualified) specialization of unexpected. (N4910 [expected.object.general]/2)"); + + static_assert(_Check_unexpected_argument<_Err>::value); + + 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] + constexpr expected() noexcept(is_nothrow_default_constructible_v<_Ty>) // strengthened + requires is_default_constructible_v<_Ty> // + : _Value(), _Has_value(true) {} + + // clang-format off + constexpr expected(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err>) // strengthened + 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&) 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 _Allow_unwrapping = !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&> // + && _Allow_unwrapping<_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> && _Allow_unwrapping<_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 move(_Other._Value)); + } else { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_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 + : _Value(_STD forward<_Uty>(_Other)), _Has_value(true) {} + // clang-format on + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr explicit(!is_convertible_v) expected(const unexpected<_UErr>& _Other) // + noexcept(is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + : _Unexpected(_Other._Unexpected), _Has_value(false) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr explicit(!is_convertible_v<_UErr, _Err>) expected(unexpected<_UErr>&& _Other) // + noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened + : _Unexpected(_STD move(_Other._Unexpected)), _Has_value(false) {} + + template + requires is_constructible_v<_Ty, _Args...> + constexpr explicit expected(in_place_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Ty, _Args...>) // strengthened + : _Value(_STD forward<_Args>(_Vals)...), _Has_value(true) {} + + // 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 + : _Value(_Ilist, _STD forward<_Args>(_Vals)...), _Has_value(true) {} + // 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 + : _Unexpected(_STD forward<_Args>(_Vals)...), _Has_value(false) {} + + // 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 + : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...), _Has_value(false) {} + // clang-format on + + // [expected.object.dtor] + constexpr ~expected() noexcept { + if (_Has_value) { + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } + } else { + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } + } + } + + // clang-format off + // TRANSITION, LLVM-46269, destructor order is significant + ~expected() requires is_trivially_destructible_v<_Ty> && is_trivially_destructible_v<_Err> = default; + // clang-format on + + // [expected.object.assign] + template + requires is_nothrow_move_constructible_v<_Uty> + struct _NODISCARD _GuardTy { + constexpr _GuardTy(_Uty* _Target_, _Uty* _Tmp_) noexcept : _Target(_Target_), _Tmp(_Tmp_) {} + constexpr ~_GuardTy() noexcept { + if (_Target) { + _STD construct_at(_Target, _STD move(*_Tmp)); + } + } + _Uty* _Target; + _Uty* _Tmp; + }; + + template + static constexpr void _Reinit_expected(_First& _New_val, _Second& _Old_val, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_First, _Args...>) // strengthened + { + if constexpr (is_nothrow_constructible_v<_First, _Args...>) { + if constexpr (!is_trivially_destructible_v<_Second>) { + _Old_val.~_Second(); + } + _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)...); + if constexpr (!is_trivially_destructible_v<_Second>) { + _Old_val.~_Second(); + } + _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); + } else { + _Second _Tmp(_STD move(_Old_val)); + if constexpr (!is_trivially_destructible_v<_Second>) { + _Old_val.~_Second(); + } + + _GuardTy<_Second> _Guard{_STD addressof(_Old_val), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_New_val), _STD forward<_Args>(_Vals)...); + _Guard._Target = nullptr; + } + } + + 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_copy_constructible_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>) // + 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, const _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); + _Has_value = false; + } else { + _Unexpected = _Other._Unexpected; + } + + 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)); + _Has_value = false; + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + 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) { + 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), _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<_Err>) { + _Err _Tmp(_STD move(_Other._Unexpected)); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Other._Unexpected.~_Err(); + } + + if constexpr (is_nothrow_move_constructible_v<_Ty>) { + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + } else { + _GuardTy<_Err> _Guard{_STD addressof(_Other._Unexpected), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Guard._Target = nullptr; + } + + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); + } else { + _Ty _Tmp(_STD move(_Value)); + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } + + _GuardTy<_Ty> _Guard{_STD addressof(_Value), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Guard._Target = nullptr; + + if constexpr (!is_trivially_destructible_v<_Err>) { + _Other._Unexpected.~_Err(); + } + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); + } + + _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.obs] + _NODISCARD constexpr const _Ty* operator->() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD addressof(_Value); + } + _NODISCARD constexpr _Ty* operator->() noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD addressof(_Value); + } + + _NODISCARD constexpr const _Ty& operator*() const& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Value; + } + _NODISCARD constexpr _Ty& operator*() & noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Value; + } + _NODISCARD constexpr const _Ty&& operator*() const&& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Value); + } + _NODISCARD constexpr _Ty&& operator*() && noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Value); + } + + _NODISCARD constexpr explicit operator bool() const noexcept { + return _Has_value; + } + _NODISCARD constexpr bool has_value() const noexcept { + return _Has_value; + } + + _NODISCARD constexpr const _Ty& value() const& { + if (_Has_value) { + return _Value; + } + + _Throw_bad_expected_access_lv(); + } + _NODISCARD constexpr _Ty& value() & { + if (_Has_value) { + return _Value; + } + + _Throw_bad_expected_access_lv(); + } + _NODISCARD constexpr const _Ty&& value() const&& { + if (_Has_value) { + return _STD move(_Value); + } + + _Throw_bad_expected_access_rv(); + } + _NODISCARD constexpr _Ty&& value() && { + if (_Has_value) { + return _STD move(_Value); + } + + _Throw_bad_expected_access_rv(); + } + + _NODISCARD constexpr const _Err& error() const& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Unexpected); + } + + template + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened + static_assert( + is_copy_constructible_v<_Ty>, "is_copy_constructible_v must be true. (N4910 [expected.object.obs]/16)"); + static_assert( + is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4910 [expected.object.obs]/16)"); + + if (_Has_value) { + return _Value; + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + template + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened + static_assert( + is_move_constructible_v<_Ty>, "is_move_constructible_v must be true. (N4910 [expected.object.obs]/18)"); + static_assert( + is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4910 [expected.object.obs]/18)"); + + if (_Has_value) { + return _STD move(_Value); + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + + // [expected.object.eq] + + // clang-format off + template + requires (!is_void_v<_Uty>) + _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 + // clang-format on + if (_Left._Has_value != _Right.has_value()) { + return false; + } else if (_Left._Has_value) { + return _Left._Value == *_Right; + } else { + return _Left._Unexpected == _Right.error(); + } + } + + template + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( + noexcept(static_cast(_Left._Value == _Right))) { // strengthened + if (_Left._Has_value) { + return static_cast(_Left._Value == _Right); + } else { + return false; + } + } + + template + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return static_cast(_Left._Unexpected == _Right.error()); + } + } + +private: + [[noreturn]] void _Throw_bad_expected_access_lv() const { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] void _Throw_bad_expected_access_lv() { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] void _Throw_bad_expected_access_rv() const { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] void _Throw_bad_expected_access_rv() { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + + union { + _Ty _Value; + _Err _Unexpected; + }; + bool _Has_value; +}; + +template + requires is_void_v<_Ty> +class expected<_Ty, _Err> { + static_assert(_Check_unexpected_argument<_Err>::value); + + 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.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> && is_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>) + 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 _Allow_unwrapping = !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&> && _Allow_unwrapping<_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) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + template + requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Allow_unwrapping<_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) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + } + } + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr explicit(!is_convertible_v) expected(const unexpected<_UErr>& _Other) // + noexcept(is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + : _Unexpected(_Other._Unexpected), _Has_value(false) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr explicit(!is_convertible_v<_UErr, _Err>) expected(unexpected<_UErr>&& _Other) // + noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened + : _Unexpected(_STD move(_Other._Unexpected)), _Has_value(false) {} + + 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 + : _Unexpected(_STD forward<_Args>(_Vals)...), _Has_value(false) {} + + // 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 + : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...), _Has_value(false) {} + // clang-format on + + // [expected.void.dtor] + constexpr ~expected() noexcept { + if (!_Has_value) { + _Unexpected.~_Err(); + } + } + + // clang-format off + // TRANSITION, LLVM-46269, destructor order is significant + ~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) { + if constexpr (!is_trivially_destructible_v<_Err>) { + _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>) // + 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) { + if constexpr (!is_trivially_destructible_v<_Err>) { + _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) { + if constexpr (!is_trivially_destructible_v<_Err>) { + _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)); + if constexpr (!is_trivially_destructible_v<_Err>) { + _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)); + if constexpr (!is_trivially_destructible_v<_Err>) { + _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)); + if constexpr (!is_trivially_destructible_v<_Err>) { + _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)); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Left._Unexpected.~_Err(); + } + _Left._Has_value = true; + _Right._Has_value = false; + } else { + _Swap_adl(_Left._Unexpected, _Right._Unexpected); + } + } + + // [expected.void.obs] + _NODISCARD constexpr explicit operator bool() const noexcept { + return _Has_value; + } + _NODISCARD constexpr bool has_value() const noexcept { + return _Has_value; + } + + constexpr void operator*() const noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Has_value, "expected stores an error, not a value"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + constexpr void value() const& { + if (!_Has_value) { + _Throw_bad_expected_access_lv(); + } + } + constexpr void value() && { + if (!_Has_value) { + _Throw_bad_expected_access_rv(); + } + } + + _NODISCARD constexpr const _Err& error() const& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(!_Has_value, "expected stores a value, not an error"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + return _STD move(_Unexpected); + } + + // [expected.void.eq] + // clang-format off + template + requires is_void_v<_Uty> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + // clang-format on + if (_Left._Has_value != _Right.has_value()) { + return false; + } else { + return _Left._Has_value || static_cast(_Left._Unexpected == _Right.error()); + } + } + + template + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return static_cast(_Left._Unexpected == _Right.error()); + } + } + +private: + [[noreturn]] void _Throw_bad_expected_access_lv() const { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] void _Throw_bad_expected_access_rv() { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + + union { + _Err _Unexpected; + }; + bool _Has_value; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // __cpp_lib_expected +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _EXPECTED_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 476bff26a5..f63097a68f 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -55,6 +55,7 @@ "deque", "exception", "execution", + "expected", "filesystem", "format", "forward_list", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 01c3f352d2..088b22c121 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() @@ -307,6 +308,7 @@ // (changes to pair, tuple, and vector::reference only) // P2442R1 Windowing Range Adaptors: views::chunk, views::slide // P2443R1 views::chunk_by +// P2549R0 unexpected::error() // Parallel Algorithms Notes // C++ allows an implementation to implement parallel algorithms as calls to the serial algorithms. @@ -1438,9 +1440,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 11293348bf..1240901c1b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -257,6 +257,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 0000000000..8ac7033b20 --- /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 0000000000..9ccf3753a5 --- /dev/null +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -0,0 +1,2081 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +enum class IsDefaultConstructible : bool { Not, Yes }; +enum class IsTriviallyCopyConstructible : bool { Not, Yes }; +enum class IsTriviallyMoveConstructible : bool { Not, Yes }; +enum class IsTriviallyDestructible : bool { Not, Yes }; + +enum class IsNothrowConstructible : bool { Not, Yes }; +enum class IsNothrowCopyConstructible : bool { Not, Yes }; +enum class IsNothrowMoveConstructible : bool { Not, Yes }; +enum class IsNothrowCopyAssignable : bool { Not, Yes }; +enum class IsNothrowMoveAssignable : bool { Not, Yes }; +enum class IsNothrowConvertible : bool { Not, Yes }; +enum class IsNothrowComparable : bool { Not, Yes }; +enum class IsNothrowSwappable : bool { Not, Yes }; + +enum class IsExplicitConstructible : bool { Not, Yes }; + +template +[[nodiscard]] constexpr bool IsYes(const E e) noexcept { + return e == E::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 = IsYes(nothrowCopyConstructible); + constexpr bool move_construction_is_noexcept = IsYes(nothrowMoveConstructible); + constexpr bool compare_is_noexcept = IsYes(nothrowComparable); + + 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); + assert(base_error_constructed.error()._val == 3); + + Unexpect conversion_error_constructed{convertible{4}}; + static_assert(noexcept(Unexpect{convertible{4}}) == move_construction_is_noexcept); + assert(conversion_error_constructed.error()._val == 4); + + Unexpect brace_error_constructed{{5}}; + static_assert(noexcept(Unexpect{{5}}) == move_construction_is_noexcept); + assert(brace_error_constructed.error()._val == 5); + + // [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_rvalue_constructed == Unexpect{test_error{1}}); + static_assert(noexcept(in_place_lvalue_constructed.swap(in_place_rvalue_constructed))); + + swap(base_error_constructed, conversion_error_constructed); + assert(base_error_constructed == Unexpect{test_error{4}}); + assert(conversion_error_constructed == Unexpect{test_error{3}}); + static_assert(noexcept(swap(base_error_constructed, conversion_error_constructed))); + + // [expected.un.obs] + 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); + + // deduction guide + std::unexpected deduced(test_error{42}); + static_assert(same_as); + } + + 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 = IsYes(defaultConstructible); + + 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); + assert(defaulted.value() == 42); + } + } + + template + constexpr void test_copy_constructors() { + constexpr bool should_be_trivial = IsYes(triviallyCopyConstructible); + constexpr bool should_be_noexcept = should_be_trivial || IsYes(nothrowCopyConstructible); + + 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&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + constexpr payload_copy_constructor(const payload_copy_constructor&) = default; + + [[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); + } + + { // ensure we are not copy constructible if either the payload or the error are not + struct not_copy_constructible { + not_copy_constructible(const not_copy_constructible&) = delete; + }; + + static_assert(!is_copy_constructible_v>); + static_assert(!is_copy_constructible_v>); + static_assert(!is_copy_constructible_v>); + } + } + + template + constexpr void test_move_constructors() { + constexpr bool should_be_trivial = IsYes(triviallyMoveConstructible); + constexpr bool should_be_noexcept = should_be_trivial || IsYes(nothrowMoveConstructible); + + 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&&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + constexpr payload_move_constructor(payload_move_constructor&&) = default; + + [[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); + } + + { // ensure we are not move constructible if either the payload or the error are not + struct not_move_constructible { + not_move_constructible(not_move_constructible&&) = delete; + }; + + static_assert(!is_move_constructible_v>); + static_assert(!is_move_constructible_v>); + static_assert(!is_move_constructible_v>); + } + } + + 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 = IsYes(triviallyDestructible); + 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 = IsYes(nothrowConstructible); + constexpr bool should_be_explicit = IsYes(explicitConstructible); + + struct payload_constructors { + payload_constructors() = default; + // Note clang does not accept local variables in explicit + constexpr explicit(IsYes(explicitConstructible)) + payload_constructors(const convertible&) noexcept(should_be_noexcept) + : _val(3) {} + constexpr explicit(IsYes(explicitConstructible)) + payload_constructors(convertible&&) noexcept(should_be_noexcept) + : _val(42) {} + constexpr explicit(IsYes(explicitConstructible)) + 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{}; + 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); + + const Expected brace_constructed_value{{}}; + assert(brace_constructed_value); + assert(brace_constructed_value.value() == 0); + static_assert(noexcept(Expected{{}})); + } + + { // 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{}; + 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); + } + + { // expected: 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{}; + const Expected copy_constructed_value{const_input_value}; + assert(copy_constructed_value); + copy_constructed_value.value(); + static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept); + + const Expected move_constructed_value{Input{in_place}}; + assert(move_constructed_value); + move_constructed_value.value(); + 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); + } + + { // expected: 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); + } + + { // expected: in place payload + using Expected = expected; + const Expected default_constructed{in_place}; + assert(default_constructed); + default_constructed.value(); + static_assert(noexcept(Expected{in_place})); + } + + { // expected: 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 = IsYes(nothrowCopyConstructible); + constexpr bool nothrow_move_constructible = IsYes(nothrowMoveConstructible); + constexpr bool nothrow_copy_assignable = IsYes(nothrowCopyAssignable); + constexpr bool nothrow_move_assignable = IsYes(nothrowMoveAssignable); + + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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; + assert(assign_value_to_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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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}; + assert(assign_value_to_value); + 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); + 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}; + assert(assign_value_to_error); + 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); + 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); + 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); + 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); + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = payload_assign{42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = payload_assign{42}; + assert(assign_value_to_error); + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = payload_assign{42}) == should_be_noexcept); + } + + { // assign base type braces + 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 = {42}; + assert(assign_value_to_value); + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = {42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = {42}; + assert(assign_value_to_error); + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = {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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // ensure we are not copy_assignable if either the payload or the error are not copy_assignable or the payload + // is not copy_constructible + struct not_copy_assignable { + not_copy_assignable& operator=(not_copy_assignable&&) = delete; + }; + + static_assert(!is_copy_assignable_v>); + static_assert(!is_copy_assignable_v>); + static_assert(!is_copy_assignable_v>); + + struct not_copy_constructible { + not_copy_constructible(const not_copy_constructible&) = delete; + }; + + static_assert(!is_copy_assignable_v>); + } + + { // ensure we are not move_assignable if either the payload or the error are not move_assignable or + // move_constructible + struct not_move_assignable { + not_move_assignable& operator=(not_move_assignable&&) = delete; + }; + + static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); + + struct not_move_constructible { + not_move_constructible(not_move_constructible&&) = delete; + }; + + static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); + } + } + + constexpr void test_assignment() noexcept { + using NCC = IsNothrowCopyConstructible; + using NMC = IsNothrowMoveConstructible; + using NCA = IsNothrowCopyAssignable; + using NMA = IsNothrowMoveAssignable; + + 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); + 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); + 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); + assert(emplaced_rvalue.value() == 42); + } + + destructor_called = false; + { + Expected emplaced_rvalue(unexpect); + emplaced_rvalue.emplace(destructor_called, convertible{}); + assert(!destructor_called); + assert(emplaced_rvalue); + 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); + 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); + 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(IsYes(nothrowMoveConstructible)) + : _val(other._val + 42) {} + // Note: cannot declare friends of function local structs + constexpr friend void swap(payload_swap& left, payload_swap& right) noexcept(IsYes(nothrowSwappable)) { + 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 = IsYes(nothrowMoveConstructible); + constexpr bool should_be_noexcept = nothrow_move_constructible && IsYes(nothrowSwappable); + + { // 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 { + payload_access* operator&() = delete; + const payload_access* operator&() const = delete; + + int x = 17; + int y = 29; + }; + + { // operator->() + using Expected = expected; + Expected val; + assert(val->x == 17); + + const Expected const_val; + assert(const_val->y == 29); + } + + { // 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 (const 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_const_lvalue = const_lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(move(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 (const bad_expected_access& with_error) { + assert(move(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 (const bad_expected_access& with_error) { + assert(with_error.error() == 1); + } + + try { + Expected rvalue{unexpect, 42}; + move(rvalue).value(); + assert(false); + } catch (const bad_expected_access& with_error) { + assert(with_error.error() == 42); + } + + try { + const Expected const_lvalue{unexpect, 1337}; + const_lvalue.value(); + assert(false); + } catch (const bad_expected_access& with_error) { + assert(with_error.error() == 1337); + } + + try { + const Expected const_rvalue{unexpect, -42}; + move(const_rvalue).value(); + assert(false); + } catch (const bad_expected_access& with_error) { + assert(with_error.error() == -42); + } + } + + { // 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 = IsYes(nothrowConstructible); + constexpr bool conversion_is_noexcept = IsYes(nothrowConvertible); + 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 = IsYes(nothrowComparable); + + 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); + } + + { // 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 unexpected 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 unexpected 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 unexpected 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 unexpected 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 + +void test_reinit_regression() { + // _Reinit_expected had a bug in its conditional noexcept that would terminate the program + // when switching from error state to value state when the value type is nothrow-movable + // but the conversion throws. + + constexpr int magic = 1729; + + struct throwing_int_conversion { + [[noreturn]] operator int() const { + throw magic; + } + }; + + expected e{unexpect, false}; + + try { + e = throwing_int_conversion{}; + assert(false); + } catch (const int& i) { + assert(i == magic); + } +} + +int main() { + test_unexpected::test_all(); + static_assert(test_unexpected::test_all()); + test_expected::test_all(); + static_assert(test_expected::test_all()); + + static_assert(is_base_of_v, bad_expected_access>); + static_assert(is_base_of_v>); + static_assert(is_base_of_v>); + + static_assert(is_convertible_v*, bad_expected_access*>); + static_assert(is_convertible_v*, exception*>); + static_assert(is_convertible_v*, exception*>); + + test_reinit_regression(); +} 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 ea9dda8147..ffff358f32 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 d455290997..db34dc79c7 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,9 @@ import ; import ; import ; import ; +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VSO-1543660 fixed in 17.3 Preview 3 +import ; +#endif // ^^^ no workaround ^^^ import ; import ; import ; @@ -258,8 +261,8 @@ int main() { odd.join(); even.join(); - const vector expected = {5, 51, 512, 5121, 51212, 512121, 5121212}; - assert(vec == expected); + const vector expected_val = {5, 51, 512, 5121, 51212, 512121, 5121212}; + assert(vec == expected_val); static_assert(static_cast(cv_status::no_timeout) == 0); static_assert(static_cast(cv_status::timeout) == 1); @@ -291,6 +294,14 @@ int main() { assert(count(execution::par, begin(arr), end(arr), 0) == 4); } +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VSO-1543660 fixed in 17.3 Preview 3 + { + puts("Testing ."); + constexpr expected test{unexpect, 42}; + assert(test.error() == 42); + } +#endif // ^^^ no workaround ^^^ + { puts("Testing ."); constexpr wstring_view dot{L"."}; @@ -601,8 +612,8 @@ int main() { v.push_back(it->str()); } - const vector expected{"cute", "fluffy", "kittens"}; - assert(v == expected); + const vector expected_val{"cute", "fluffy", "kittens"}; + assert(v == expected_val); } { @@ -611,8 +622,8 @@ int main() { v.push_back(11); v.push_back(22); v.push_back(33); - constexpr int expected[]{11, 22, 33}; - assert(equal(v.begin(), v.end(), begin(expected), end(expected))); + constexpr int expected_val[]{11, 22, 33}; + assert(equal(v.begin(), v.end(), begin(expected_val), end(expected_val))); } { @@ -682,8 +693,8 @@ int main() { odd.join(); even.join(); - const vector expected = {5, 51, 512, 5121, 51212, 512121, 5121212}; - assert(vec == expected); + const vector expected_val = {5, 51, 512, 5121, 51212, 512121, 5121212}; + assert(vec == expected_val); } { @@ -706,31 +717,31 @@ int main() { char ibuffer[] = "1 2 3 4 5"; ispanstream is{span{ibuffer}}; int read = 0; - for (int expected = 1; expected <= 5; ++expected) { + for (int expected_val = 1; expected_val <= 5; ++expected_val) { assert(is.good()); is >> read; - assert(read == expected); + assert(read == expected_val); } const char const_buffer[] = "1 2 3 4 5"; basic_ispanstream is_const_buffer{span{const_buffer}}; read = 0; - for (int expected = 1; expected <= 5; ++expected) { + for (int expected_val = 1; expected_val <= 5; ++expected_val) { assert(is_const_buffer.good()); is_const_buffer >> read; - assert(read == expected); + assert(read == expected_val); } - const auto expected = "102030"sv; + const auto expected_val = "102030"sv; char obuffer[10]; ospanstream os{span{obuffer}}; os << 10 << 20 << 30; - assert(equal(begin(os.span()), end(os.span()), begin(expected), end(expected))); + assert(equal(begin(os.span()), end(os.span()), begin(expected_val), end(expected_val))); char buffer[10]; spanstream s{span{buffer}}; s << 10 << 20 << 30; - assert(equal(begin(s.span()), end(s.span()), begin(expected), end(expected))); + assert(equal(begin(s.span()), end(s.span()), begin(expected_val), end(expected_val))); } { @@ -799,13 +810,13 @@ int main() { }}; l.wait(); // wait for jt to generate the sequence } // destroying jt will ask it to stop - static constexpr int expected[]{1729, 5188, 2594, 1297, 3892, 1946, 973, 2920, 1460, 730, 365, 1096, 548, 274, - 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, - 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, - 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, - 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, - 80, 40, 20, 10, 5, 16, 8, 4, 2, 1, -1000}; - assert(equal(vec.begin(), vec.end(), begin(expected), end(expected))); + static constexpr int expected_val[]{1729, 5188, 2594, 1297, 3892, 1946, 973, 2920, 1460, 730, 365, 1096, 548, + 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, + 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, + 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, + 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, + 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1, -1000}; + assert(equal(vec.begin(), vec.end(), begin(expected_val), end(expected_val))); } #endif // ^^^ no workaround ^^^ 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 67f24666dd..83f3c86bf2 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 && !defined(__EDG__) // TRANSITION, EDG concepts support +#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 diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index b6a08effe8..9aa79d26b0 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -24,6 +24,7 @@ PM_CL="/DMEOW_HEADER=coroutine" PM_CL="/DMEOW_HEADER=deque" PM_CL="/DMEOW_HEADER=exception" PM_CL="/DMEOW_HEADER=execution" +PM_CL="/DMEOW_HEADER=expected" PM_CL="/DMEOW_HEADER=filesystem" PM_CL="/DMEOW_HEADER=format" PM_CL="/DMEOW_HEADER=forward_list"