From 8a7b0a1a7d21a2f15f26cad28dd9ff3860ac7b9f Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 29 Mar 2022 16:59:27 +0200 Subject: [PATCH 01/26] Implement P0323: expected Fixes #2529 --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/expected | 1020 +++++++++ stl/inc/header-units.json | 1 + stl/inc/yvals_core.h | 12 +- tests/std/test.lst | 1 + tests/std/tests/P0323R12_expected/env.lst | 4 + tests/std/tests/P0323R12_expected/test.cpp | 1865 +++++++++++++++++ .../importable_cxx_library_headers.jsonc | 1 + .../test.cpp | 1 + .../test.compile.pass.cpp | 14 + 11 files changed, 2918 insertions(+), 3 deletions(-) create mode 100644 stl/inc/expected create mode 100644 tests/std/tests/P0323R12_expected/env.lst create mode 100644 tests/std/tests/P0323R12_expected/test.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index fae0ffdb05..37644ec956 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -130,6 +130,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/deque ${CMAKE_CURRENT_LIST_DIR}/inc/exception ${CMAKE_CURRENT_LIST_DIR}/inc/execution + ${CMAKE_CURRENT_LIST_DIR}/inc/expected ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/coroutine ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/deque ${CMAKE_CURRENT_LIST_DIR}/inc/experimental/filesystem diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 8de1be161e..a2e1ce8fc7 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..afba47f1eb --- /dev/null +++ b/stl/inc/expected @@ -0,0 +1,1020 @@ +// expected standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _EXPECTED_ +#define _EXPECTED_ +#include +#if _STL_COMPILER_PREPROCESSOR +#if !_HAS_CXX23 && defined(__cpp_lib_concepts) +#pragma message("The contents of are available only with C++23 or later.") +#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv +#include +#include +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN + +// [expected.un.object] +// clang-format off +template + requires (is_object_v<_Err> && !is_array_v<_Err> && !is_volatile_v<_Err> && !is_const_v<_Err>) +class unexpected { + // clang-format on + static_assert(!_Is_specialization_v<_Err, unexpected>, "E must not be a specialization of unexpected"); + + template + friend class expected; + +public: + // [expected.un.ctor] + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit unexpected(in_place_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) // strengthened + : _Unexpected(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Err, initializer_list<_Uty>&, _Args...> + constexpr explicit unexpected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) // strengthened + : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + + template + requires (!is_same_v, unexpected> && !is_same_v, in_place_t> // + && is_constructible_v<_Err, _UError>) + constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened + : _Unexpected(_STD forward<_UError>(_Unex)) {} + // clang-format on + + // [expected.un.observe] + // TRANSITION, P2549 +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + // [expected.un.swap] + constexpr void swap(unexpected& _Other) noexcept(is_nothrow_swappable_v<_Err>) requires is_swappable_v<_Err> { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + + friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept( + is_nothrow_swappable_v<_Err>) requires is_swappable_v<_Err> { + _Left.swap(_Right); + } + + // [expected.un.eq] + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const unexpected& _Left, const unexpected<_UErr>& _Right) noexcept( // + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + return _Left._Unexpected == _Right.error(); + } + +private: + _Err _Unexpected; +}; + +template +class bad_expected_access; + +template <> +class bad_expected_access : public exception { +public: + _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { + return "Bad expected access"; + } + +#if !_HAS_EXCEPTIONS +protected: + void _Doraise() const override { // perform class-specific exception handling + _RAISE(*this); + } +#endif // !_HAS_EXCEPTIONS +}; + +template +class bad_expected_access : public bad_expected_access { +public: + __CLR_OR_THIS_CALL explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) + : _Unexpected(_STD move(_Unex)) {} + +// TRANSITION, P2549 +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + +private: + _Err _Unexpected; +}; + +struct unexpect_t { + explicit unexpect_t() = default; +}; + +inline constexpr unexpect_t unexpect{}; + +template +class expected { + template + friend class expected; + +public: + using value_type = _Ty; + using error_type = _Err; + using unexpected_type = unexpected<_Err>; + + template + using rebind = expected<_Uty, error_type>; + + // [expected.object.ctor] + // clang-format off + constexpr expected() requires is_default_constructible_v<_Ty> : _Has_value(true), _Value() {}; + + constexpr expected(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err>) // + requires (!is_trivially_copy_constructible_v<_Ty> && !is_trivially_copy_constructible_v<_Err> // + && is_copy_constructible_v<_Ty> && is_copy_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + // clang-format on + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _Other._Value); + } else { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + // clang-format off + expected(const expected& _Other) requires + is_trivially_copy_constructible_v<_Ty> && is_trivially_copy_constructible_v<_Err> = default; + + constexpr expected(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err>) // + requires (!is_trivially_move_constructible_v<_Ty> && !is_trivially_move_constructible_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + // clang-format on + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _STD move(_Other._Value)); + } else { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + } + }; + + // clang-format off + expected(expected&&) requires + is_trivially_move_constructible_v<_Ty> && is_trivially_move_constructible_v<_Err> = default; + // clang-format on + + template + static constexpr bool _Is_not_convertible = !is_constructible_v<_Ty, expected<_Uty, _UErr>&> // + && !is_constructible_v<_Ty, expected<_Uty, _UErr>> // + && !is_constructible_v<_Ty, const expected<_Uty, _UErr>&> // + && !is_constructible_v<_Ty, const expected<_Uty, _UErr>> // + && !is_convertible_v&, _Ty> // + && !is_convertible_v&&, _Ty> // + && !is_convertible_v&, _Ty> // + && !is_convertible_v&&, _Ty> // + && !is_constructible_v, expected<_Uty, _UErr>&> // + && !is_constructible_v, expected<_Uty, _UErr>> // + && !is_constructible_v, const expected<_Uty, _UErr>&> // + && !is_constructible_v, const expected<_Uty, _UErr>>; + + template + requires is_constructible_v<_Ty, const _Uty&> && is_constructible_v<_Err, const _UErr&> // + && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v || !is_convertible_v) + expected(const expected<_Uty, _UErr>& _Other) noexcept(is_nothrow_constructible_v<_Ty, const _Uty&>&& + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _Other._Value); + } else { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + template + requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v<_Uty, _Ty> || !is_convertible_v<_UErr, _Err>) + expected(expected<_Uty, _UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (_Has_value) { + _STD construct_at(_STD addressof(_Value), _STD forward<_Uty>(_Other._Value)); + } else { + _STD construct_at(_STD addressof(_Unexpected), _STD forward<_UErr>(_Other._Unexpected)); + } + } + + // clang-format off + template + requires (!is_same_v, in_place_t> && !is_same_v, expected> // + && !_Is_specialization_v, unexpected> && is_constructible_v<_Ty, _Uty>) + constexpr explicit(!is_convertible_v<_Uty, _Ty>) + expected(_Uty&& _Other) noexcept(is_nothrow_constructible_v<_Ty, _Uty>) /* strengthened */ + : _Has_value(true), _Value(_STD forward<_Uty>(_Other)) {} + // clang-format on + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr expected(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(false), _Unexpected(_Other._Unexpected) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + + template + requires is_constructible_v<_Ty, _Args...> + constexpr explicit expected(in_place_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Ty, _Args...>) /* strengthened */ + : _Has_value(true), _Value(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Ty, initializer_list<_Uty>&,_Args... > + constexpr explicit expected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Ty, initializer_list<_Uty>&, _Args...>) /* strengthened */ + : _Has_value(true), _Value(_Ilist, _STD forward<_Args>(_Vals)...) {} + // clang-format on + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + + // clang-format off + template + requires is_constructible_v<_Err, initializer_list<_Uty>&, _Args... > + constexpr explicit expected(unexpect_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + // clang-format on + + // [expected.object.dtor] + constexpr ~expected() noexcept { + if (_Has_value) { + _Value.~_Ty(); + } else { + _Unexpected.~_Err(); + } + } + + // clang-format off + ~expected() requires is_trivially_destructible_v<_Ty> && is_trivially_destructible_v<_Err> = default; + // clang-format on + + + // [expected.object.assign] + template + static constexpr void _Reinit_expected(_First& _New_val, _Second& _Old_val, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_First, _Args...> || is_nothrow_move_constructible_v<_First>) // strengthened + { + if constexpr (is_nothrow_constructible_v<_First, _Args...>) { + _STD destroy_at(_STD addressof(_Old_val)); + _STD construct_at(_STD addressof(_New_val), _STD forward<_Args>(_Vals)...); + } else if constexpr (is_nothrow_move_constructible_v<_First>) { + _First _Tmp(_STD forward<_Args>(_Vals)...); + _STD destroy_at(_STD addressof(_Old_val)); + _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); + } else { + _Second _Tmp(_STD move(_Old_val)); + _STD destroy_at(addressof(_Old_val)); + try { + _STD construct_at(addressof(_New_val), _STD forward<_Args>(_Vals)...); + } catch (...) { + _STD construct_at(addressof(_Old_val), _STD move(_Tmp)); + throw; + } + } + } + + constexpr expected& operator=(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err> // + && is_nothrow_copy_assignable_v<_Ty>&& is_nothrow_copy_assignable_v<_Err>) // strengthened + requires is_copy_assignable_v<_Ty> && is_copy_constructible_v<_Ty> && is_copy_assignable_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Value = _Other._Value; + } else if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _Other._Unexpected); + } else if (_Other._Has_value) { + _Reinit_expected(_Value, _Unexpected, _Other._Value); + } else { + _Unexpected = _Other._Unexpected; + } + + _Has_value = _Other._Has_value; + return *this; + } + + constexpr expected& operator=(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err> // + && is_nothrow_move_assignable_v<_Ty>&& is_nothrow_move_assignable_v<_Err>) // strengthened + requires is_move_assignable_v<_Ty> && is_move_constructible_v<_Ty> // + && is_move_assignable_v<_Err> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Value = _STD move(_Other._Value); + } else if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); + } else if (_Other._Has_value) { + _Reinit_expected(_Value, _Unexpected, _STD move(_Other._Value)); + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + _Has_value = _Other._Has_value; + return *this; + } + + // clang-format off + template + requires (!is_same_v, expected> && !_Is_specialization_v, unexpected> + && is_constructible_v<_Ty, _Uty> && is_assignable_v<_Ty&, _Uty> && (is_nothrow_constructible_v<_Ty, _Uty> + || is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>)) + constexpr expected& operator=(_Uty&& _Other) noexcept( + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_assignable_v<_Ty&, _Uty>) { // strengthened + // clang-format on + if (_Has_value) { + _Value = _STD forward<_Uty>(_Other); + } else { + _Reinit_expected(_Value, _Unexpected, _STD forward<_Uty>(_Other)); + } + + _Has_value = true; + return *this; + } + + // clang-format off + template + requires (is_constructible_v<_Err, const _UErr&>&& is_assignable_v<_Err&, const _UErr&> // + && (is_nothrow_constructible_v<_Err, _UErr> || is_nothrow_move_constructible_v<_Ty> // + || is_nothrow_move_constructible_v<_Err>) ) + constexpr expected& operator=(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>&& is_nothrow_assignable_v<_Err&, const _UErr&>) { // strengthened + // clang-format on + if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _Other._Unexpected); + } else { + _Unexpected = _Other._Unexpected; + } + + _Has_value = false; + return *this; + } + + // clang-format off + template + requires (is_constructible_v<_Err, _UErr>&& is_assignable_v<_Err&, _UErr> + && (is_nothrow_constructible_v<_Err, _UErr> || is_nothrow_move_constructible_v<_Ty> // + || is_nothrow_move_constructible_v<_Err>) ) + constexpr expected& operator=(unexpected<_UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Err, _UErr>&& is_nothrow_assignable_v<_Err&, _UErr>) { // strengthened + // clang-format on + if (_Has_value) { + _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + _Has_value = false; + return *this; + } + + template + requires is_nothrow_constructible_v<_Ty, _Args...> + constexpr _Ty& emplace(_Args&&... _Vals) noexcept { + if (_Has_value) { + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } + } else { + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } + _Has_value = true; + } + + return *_STD construct_at(_STD addressof(_Value), _STD forward<_Args>(_Vals)...); + } + + // clang-format off + template + requires is_nothrow_constructible_v<_Ty, initializer_list<_Uty>&, _Args...> + constexpr _Ty& emplace(initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept { + // clang-format on + if (_Has_value) { + _Value.~_Ty(); + } else { + _Unexpected.~_Err(); + _Has_value = true; + } + + return *_STD construct_at(_STD addressof(_Value), _Ilist, _STD forward<_Args>(_Vals)...); + } + + // [expected.object.swap] + constexpr void swap(expected& _Other) noexcept(is_nothrow_move_constructible_v<_Ty>&& is_nothrow_swappable_v<_Ty>&& + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Ty> && is_swappable_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + if (_Has_value && _Other._Has_value) { + _Swap_adl(_Value, _Other._Value); + } else if (_Has_value) { + if constexpr (is_nothrow_move_constructible_v<_Ty> && is_nothrow_move_constructible_v<_Err>) { + _Err _Tmp(_STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Value.~_Ty(); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); + } else if constexpr (is_nothrow_move_constructible_v<_Err>) { + _Err _Tmp(_STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + try { + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Value.~_Ty(); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); + } catch (...) { + _STD construct_at(_STD addressof(_Other._Unexpected), _STD move(_Tmp)); + throw; + } + } else { + _Ty _Tmp(_STD move(_Value)); + _Value.~_Ty(); + try { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); + } catch (...) { + _STD construct_at(_STD addressof(_Value), _STD move(_Tmp)); + throw; + } + } + + _Has_value = false; + _Other._Has_value = true; + } else if (_Other._Has_value) { + _Other.swap(*this); + } else { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + } + + friend constexpr void swap(expected& _Lhs, expected& _Rhs) noexcept(is_nothrow_move_constructible_v<_Ty>&& + is_nothrow_swappable_v<_Ty>&& is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Ty> && is_swappable_v<_Err> // + && is_move_constructible_v<_Ty> && is_move_constructible_v<_Err> // + &&(is_nothrow_move_constructible_v<_Ty> || is_nothrow_move_constructible_v<_Err>) { + _Lhs.swap(_Rhs); + } + + // [expected.object.observe] + _NODISCARD constexpr const _Ty* operator->() const noexcept { + return _STD addressof(_Value); + } + _NODISCARD constexpr _Ty* operator->() noexcept { + return _STD addressof(_Value); + } + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) operator*(this expected&& _Self) { + return _STD forward(_Self)._Value; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Ty& operator*() const& noexcept { + return _Value; + } + _NODISCARD constexpr _Ty& operator*() & noexcept { + return _Value; + } + _NODISCARD constexpr const _Ty&& operator*() const&& noexcept { + return _STD move(_Value); + } + _NODISCARD constexpr _Ty&& operator*() && noexcept { + return _STD move(_Value); + } +#endif // !__cpp_explicit_this_parameter + + _NODISCARD constexpr explicit operator bool() const noexcept { + return _Has_value; + } + _NODISCARD constexpr bool has_value() const noexcept { + return _Has_value; + } + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) value(this expected&& _Self) { + if (_Has_value) { + return _STD forward(_Self)._Value; + } + _Throw_bad_expected_access(); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Ty& value() const& { + if (_Has_value) { + return _Value; + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr _Ty& value() & { + if (_Has_value) { + return _Value; + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr const _Ty&& value() const&& { + if (_Has_value) { + return _STD move(_Value); + } + _Throw_bad_expected_access(); + } + _NODISCARD constexpr _Ty&& value() && { + if (_Has_value) { + return _STD move(_Value); + } + _Throw_bad_expected_access(); + } +#endif // !__cpp_explicit_this_parameter + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + template _Uty> + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened + requires is_copy_constructible_v<_Ty> { + if (_Has_value) { + return _Value; + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + template _Uty> + _NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept( + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened + requires is_move_constructible_v<_Ty> { + if (_Has_value) { + return _STD move(_Value); + } else { + return static_cast<_Ty>(_STD forward<_Uty>(_Other)); + } + } + + // [expected.object.eq] +#ifdef __clang__ // TRANSITION, LLVM-XXXXX + template _UErr> +#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv + template <_Weakly_equality_comparable_with<_Ty> _Uty, _Weakly_equality_comparable_with<_Err> _UErr> +#endif // !__clang__ + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Value == *_Right)) && noexcept( + _Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value != _Right.has_value()) { + return false; + } else if (_Left._Has_value) { + return _Left._Value == *_Right; + } else { + return _Left._Unexpected == _Right.error(); + } + } + +#ifdef __clang__ // TRANSITION, LLVM-XXXXX + template +#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv + template <_Weakly_equality_comparable_with<_Ty> _Uty> +#endif // !__clang__ + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened + if (!_Left._Has_value) { + return false; + } else { + return _Left._Value == _Right; + } + } + + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return _Left._Unexpected == _Right.error(); + } + } + +private: +#ifdef __cpp_explicit_this_parameter + [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { + _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + [[noreturn]] inline void _Throw_bad_expected_access() const& { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] inline void _Throw_bad_expected_access() & { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() const&& { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() && { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } +#endif // !__cpp_explicit_this_parameter + + bool _Has_value; + union { + _Ty _Value; + _Err _Unexpected; + }; +}; + +template + requires is_void_v<_Ty> +class expected<_Ty, _Err> { +public: + using value_type = _Ty; + using error_type = _Err; + using unexpected_type = unexpected<_Err>; + + template + using rebind = expected<_Uty, error_type>; + + // [expected.void.ctor] + constexpr expected() noexcept : _Has_value(true) {} + + // clang-format off + constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) /* strengthened */ + requires (!is_trivially_copy_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + } + } + + expected(const expected& ) requires is_trivially_copy_constructible_v<_Err> = default; + + constexpr expected(expected&& _Other) noexcept(is_nothrow_move_constructible_v<_Err>) /* strengthened */ + requires (!is_trivially_move_constructible_v<_Err> && is_move_constructible_v<_Err>) + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + } + } + + expected(expected&&) requires is_trivially_move_constructible_v<_Err> = default; + // clang-format on + + template + static constexpr bool _Is_not_convertible = !is_constructible_v, expected<_Uty, _UErr>&> // + && !is_constructible_v, expected<_Uty, _UErr>> // + && !is_constructible_v, const expected<_Uty, _UErr>&> // + && !is_constructible_v, const expected<_Uty, _UErr>>; + + template + requires is_void_v<_Uty> && is_constructible_v<_Err, const _UErr&> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v) expected(const expected<_Uty, _UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _Unexpected = _Other._Unexpected; + } + } + + template + requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + constexpr explicit(!is_convertible_v<_UErr, _Err>) + expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(_Other._Has_value) { + if (!_Has_value) { + _Unexpected = _STD forward<_UErr>(_Other._Unexpected); + } + } + + template + requires is_constructible_v<_Err, const _UErr&> + constexpr expected(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + : _Has_value(false), _Unexpected(_Other._Unexpected) {} + + template + requires is_constructible_v<_Err, _UErr> + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + + constexpr explicit expected(in_place_t) noexcept : _Has_value(true) {} + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + + template + requires is_constructible_v<_Err, _Args...> + constexpr explicit expected(unexpect_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( + is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ + : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + + + // [expected.void.dtor] + constexpr ~expected() noexcept { + if (!_Has_value) { + _Unexpected.~_Err(); + } + } + + // clang-format off + ~expected() requires is_trivially_destructible_v<_Err> = default; + // clang-format on + + // [expected.void.assign] + constexpr expected& operator=(const expected& _Other) noexcept( + is_nothrow_copy_constructible_v<_Err>&& is_nothrow_copy_assignable_v<_Err>) // strengthened + requires is_copy_assignable_v<_Err> && is_copy_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing to do + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + _Has_value = false; + } else if (_Other._Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } else { + _Unexpected = _Other._Unexpected; + } + + return *this; + } + + constexpr expected& operator=(expected&& _Other) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_move_assignable_v<_Err>) // strengthened + requires is_move_assignable_v<_Err> && is_move_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing to do + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Has_value = false; + } else if (_Other._Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + return *this; + } + + template + requires is_constructible_v<_Err, const _UErr&> && is_assignable_v<_Err&, const _UErr&> + constexpr expected& operator=(const unexpected<_UErr>& _Other) noexcept( + is_nothrow_constructible_v<_Err, const _UErr&>&& is_nothrow_assignable_v<_Err&, const _UErr&>) { // strengthened + if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); + _Has_value = false; + } else { + _Unexpected = _Other._Unexpected; + } + + return *this; + } + + template + requires is_constructible_v<_Err, _UErr> && is_assignable_v<_Err&, _UErr> + constexpr expected& operator=(unexpected<_UErr>&& _Other) noexcept( + is_nothrow_constructible_v<_Err, _UErr>&& is_nothrow_assignable_v<_Err&, _UErr>) { // strengthened + if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Has_value = false; + } else { + _Unexpected = _STD move(_Other._Unexpected); + } + + return *this; + } + + constexpr void emplace() noexcept { + if (!_Has_value) { + _Unexpected.~_Err(); + _Has_value = true; + } + } + + // [expected.void.swap] + constexpr void swap(expected& _Other) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Err> && is_move_constructible_v<_Err> { + if (_Has_value && _Other._Has_value) { + // nothing + } else if (_Has_value) { + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Other._Unexpected.~_Err(); + _Has_value = false; + _Other._Has_value = true; + } else if (_Other._Has_value) { + _STD construct_at(_STD addressof(_Other._Unexpected), _STD move(_Unexpected)); + _Unexpected.~_Err(); + _Has_value = true; + _Other._Has_value = false; + } else { + _Swap_adl(_Unexpected, _Other._Unexpected); + } + } + + friend constexpr void swap(expected& _Left, expected& _Right) noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Err> && is_move_constructible_v<_Err> { + if (_Left._Has_value && _Right._Has_value) { + // nothing + } else if (_Left._Has_value) { + _STD construct_at(_STD addressof(_Left._Unexpected), _STD move(_Right._Unexpected)); + _Right._Unexpected.~_Err(); + _Left._Has_value = false; + _Right._Has_value = true; + } else if (_Right._Has_value) { + _STD construct_at(_STD addressof(_Right._Unexpected), _STD move(_Left._Unexpected)); + _Left._Unexpected.~_Err(); + _Left._Has_value = true; + _Right._Has_value = false; + } else { + _Swap_adl(_Left._Unexpected, _Right._Unexpected); + } + } + + // [expected.void.observe] + constexpr explicit operator bool() const noexcept { + return _Has_value; + } + constexpr bool has_value() const noexcept { + return _Has_value; + } + + constexpr void operator*() const noexcept {} +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr void value(this expected&& _Self) { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr void value() const& { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() & { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() const&& { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } + _NODISCARD constexpr void value() && { + if (!_Has_value) { + _Throw_bad_expected_access(); + } + } +#endif // !__cpp_explicit_this_parameter + +#ifdef __cpp_explicit_this_parameter + _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened + return _STD forward(_Self)._Unexpected; + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr _Err& error() & noexcept { // strengthened + return _Unexpected; + } + _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + return _STD move(_Unexpected); + } + _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + return _STD move(_Unexpected); + } +#endif // !__cpp_explicit_this_parameter + + // [expected.void.eq] + template _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value != _Right.has_value()) { + return false; + } else { + return _Left._Has_value || static_cast(_Left._Unexpected == _Right.error()); + } + } + + template <_Weakly_equality_comparable_with<_Err> _UErr> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + if (_Left._Has_value) { + return false; + } else { + return static_cast(_Left._Unexpected == _Right.error()); + } + } + +private: +#ifdef __cpp_explicit_this_parameter + [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { + _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); + } +#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + [[noreturn]] inline void _Throw_bad_expected_access() const& { + _THROW(bad_expected_access{_Unexpected}); + } + [[noreturn]] inline void _Throw_bad_expected_access() & { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() const&& { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } + [[noreturn]] inline void _Throw_bad_expected_access() && { + _THROW(bad_expected_access{_STD move(_Unexpected)}); + } +#endif // !__cpp_explicit_this_parameter + + bool _Has_value; + union { + _Err _Unexpected; + }; +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _HAS_CXX23 +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _EXPECTED_ diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index c323fb152d..8d40a1b4eb 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -54,6 +54,7 @@ "deque", "exception", "execution", + "expected", "filesystem", "format", "forward_list", diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 08fd100799..a12ee701de 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -281,6 +281,7 @@ // _HAS_CXX23 directly controls: // P0288R9 move_only_function +// P0323R12 // P0401R6 Providing Size Feedback In The Allocator Interface // P0448R4 // P0627R6 unreachable() @@ -1372,9 +1373,14 @@ #define __cpp_lib_associative_heterogeneous_erasure 202110L #define __cpp_lib_byteswap 202110L -#define __cpp_lib_invoke_r 202106L -#define __cpp_lib_is_scoped_enum 202011L -#define __cpp_lib_move_only_function 202110L + +#ifdef __cpp_lib_concepts +#define __cpp_lib_expected 202202L +#endif // __cpp_lib_concepts + +#define __cpp_lib_invoke_r 202106L +#define __cpp_lib_is_scoped_enum 202011L +#define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts #define __cpp_lib_out_ptr 202106L diff --git a/tests/std/test.lst b/tests/std/test.lst index 2a8cf5e9ac..5c8106e83c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -254,6 +254,7 @@ tests\P0220R1_searchers tests\P0220R1_string_view tests\P0288R9_move_only_function tests\P0295R0_gcd_lcm +tests\P0323R12_expected tests\P0325R4_to_array tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_clocks diff --git a/tests/std/tests/P0323R12_expected/env.lst b/tests/std/tests/P0323R12_expected/env.lst new file mode 100644 index 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..9d109fb789 --- /dev/null +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -0,0 +1,1865 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +enum class IsDefaultConstructible : bool { No, Yes }; +enum class IsTriviallyCopyConstructible : bool { No, Yes }; +enum class IsTriviallyMoveConstructible : bool { No, Yes }; +enum class IsTriviallyDestructible : bool { No, Yes }; + +enum class IsNothrowConstructible : bool { No, Yes }; +enum class IsNothrowCopyConstructible : bool { No, Yes }; +enum class IsNothrowMoveConstructible : bool { No, Yes }; +enum class IsNothrowCopyAssignable : bool { No, Yes }; +enum class IsNothrowMoveAssignable : bool { No, Yes }; +enum class IsNothrowConvertible : bool { No, Yes }; +enum class IsNothrowComparable : bool { No, Yes }; +enum class IsNothrowSwappable : bool { No, Yes }; + +enum class IsExplicitConstructible : bool { No, Yes }; + +struct convertible { + constexpr convertible() = default; + constexpr convertible(const int val) noexcept : _val(val) {} + + [[nodiscard]] constexpr bool operator==(const int other) const noexcept { + return other == _val; + } + + int _val = 0; +}; + +namespace test_unexpected { + template + constexpr void test() { + constexpr bool copy_construction_is_noexcept = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + constexpr bool move_construction_is_noexcept = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool compare_is_noexcept = nothrowComparable == IsNothrowComparable::Yes; + + struct test_error { + constexpr test_error(const int& val) noexcept(copy_construction_is_noexcept) : _val(val) {} + constexpr test_error(int&& val) noexcept(move_construction_is_noexcept) : _val(val) {} + + constexpr test_error(initializer_list, const int& val) noexcept(copy_construction_is_noexcept) + : _val(val) {} + constexpr test_error(initializer_list, int&& val) noexcept(move_construction_is_noexcept) + : _val(val) {} + + constexpr test_error(const convertible& other) noexcept(copy_construction_is_noexcept) : _val(other._val) {} + constexpr test_error(convertible&& other) noexcept(move_construction_is_noexcept) : _val(other._val) {} + + [[nodiscard]] constexpr bool operator==(const test_error& right) const noexcept(compare_is_noexcept) { + return _val == right._val; + } + [[nodiscard]] constexpr bool operator==(const convertible& right) const noexcept(compare_is_noexcept) { + return _val == right._val; + } + + int _val = 0; + }; + using Unexpect = std::unexpected; + + // [expected.un.ctor] + const int& input = 1; + Unexpect in_place_lvalue_constructed{in_place, input}; + static_assert(noexcept(Unexpect{in_place, input}) == copy_construction_is_noexcept); + assert(in_place_lvalue_constructed == Unexpect{test_error{1}}); + + Unexpect in_place_rvalue_constructed{in_place, 42}; + static_assert(noexcept(Unexpect{in_place, 42}) == move_construction_is_noexcept); + assert(in_place_rvalue_constructed == Unexpect{test_error{42}}); + + Unexpect in_place_ilist_lvalue_constructed{in_place, {2}, input}; + static_assert(noexcept(Unexpect{in_place, {2}, input}) == copy_construction_is_noexcept); + assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}}); + + Unexpect in_place_ilist_rvalue_constructed{in_place, {2}, 1337}; + static_assert(noexcept(Unexpect{in_place, {2}, 1337}) == move_construction_is_noexcept); + assert(in_place_ilist_rvalue_constructed == Unexpect{test_error{1337}}); + + Unexpect base_error_constructed{test_error{3}}; + static_assert(noexcept(Unexpect{test_error{3}}) == move_construction_is_noexcept); + + Unexpect conversion_error_constructed{convertible{4}}; + static_assert(noexcept(Unexpect{convertible{4}}) == move_construction_is_noexcept); + + // [expected.un.eq] + assert(in_place_lvalue_constructed == in_place_lvalue_constructed); + assert(in_place_lvalue_constructed != in_place_rvalue_constructed); + static_assert(noexcept(in_place_lvalue_constructed == in_place_lvalue_constructed) == compare_is_noexcept); + static_assert(noexcept(in_place_lvalue_constructed != in_place_lvalue_constructed) == compare_is_noexcept); + + const auto converted = std::unexpected{convertible{3}}; + assert(base_error_constructed == converted); + assert(conversion_error_constructed != converted); + static_assert(noexcept(base_error_constructed == converted) == compare_is_noexcept); + static_assert(noexcept(conversion_error_constructed != converted) == compare_is_noexcept); + + // [expected.un.swap] + in_place_lvalue_constructed.swap(in_place_rvalue_constructed); + assert(in_place_lvalue_constructed == Unexpect{test_error{42}}); + assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}}); + + swap(base_error_constructed, conversion_error_constructed); + assert(base_error_constructed == Unexpect{test_error{4}}); + assert(conversion_error_constructed == Unexpect{test_error{3}}); + + // [expected.un.observe] + auto&& lvalue_error = base_error_constructed.error(); + assert(lvalue_error == test_error{4}); + static_assert(is_same_v); + + auto&& rvalue_error = move(conversion_error_constructed).error(); + assert(rvalue_error == test_error{3}); + static_assert(is_same_v); + + auto&& const_lvalue_error = as_const(in_place_lvalue_constructed).error(); + assert(const_lvalue_error == test_error{42}); + static_assert(is_same_v); + + auto&& const_rvalue_error = move(as_const(in_place_ilist_lvalue_constructed)).error(); + assert(const_rvalue_error == test_error{1}); + static_assert(is_same_v); + } + + constexpr bool test_all() { + test(); + test(); + test(); + test(); + test(); + test(); + test(); + test(); + + return true; + } +} // namespace test_unexpected + +namespace test_unexpect { + auto copy = unexpect; + static_assert(is_same_v); + static_assert(is_trivial_v); + static_assert(is_empty_v); +} // namespace test_unexpect + +namespace test_expected { + constexpr void test_aliases() { + struct value_tag {}; + struct error_tag {}; + + { + using Expected = expected; + static_assert(same_as); + static_assert(same_as); + static_assert(same_as>); + + static_assert(same_as, expected>); + } + + { + using Expected = expected; + static_assert(same_as); + static_assert(same_as); + static_assert(same_as>); + + static_assert(same_as, expected>); + } + } + + template + constexpr void test_default_constructors() { + constexpr bool should_be_defaultable = defaultConstructible == IsDefaultConstructible::Yes; + + struct payload_default_constructor { + constexpr payload_default_constructor() requires(should_be_defaultable) : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + static_assert(is_default_constructible_v> == should_be_defaultable); + // we only care about payload type + static_assert(is_default_constructible_v>); + static_assert(is_default_constructible_v>); + + if constexpr (should_be_defaultable) { + const expected defaulted; + assert(defaulted.value() == 42); + } + } + + template + constexpr void test_copy_constructors() { + constexpr bool should_be_trivial = triviallyCopyConstructible == IsTriviallyCopyConstructible::Yes; + constexpr bool should_be_noexcept = + should_be_trivial || nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + + struct payload_copy_constructor { + payload_copy_constructor() = default; + payload_copy_constructor& operator=(const payload_copy_constructor&) = delete; + constexpr payload_copy_constructor(const payload_copy_constructor&) requires should_be_trivial = default; + constexpr payload_copy_constructor(const payload_copy_constructor&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // Check payload type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + assert(from_value.value() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == 0); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + + { // Check error type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + assert(from_value.value() == 0); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + + { // Check void payload + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_copy_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_copy_constructible_v); + + const Expected with_value{in_place}; + const Expected from_value{with_value}; + assert(from_value); + static_assert(noexcept(Expected{with_value}) == should_be_noexcept); + + const Expected with_error{unexpect}; + const Expected from_error{with_error}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{with_error}) == should_be_noexcept); + } + } + + template + constexpr void test_move_constructors() { + constexpr bool should_be_trivial = triviallyMoveConstructible == IsTriviallyMoveConstructible::Yes; + constexpr bool should_be_noexcept = + should_be_trivial || nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + + struct payload_move_constructor { + payload_move_constructor() = default; + payload_move_constructor(const payload_move_constructor&) = default; + payload_move_constructor& operator=(payload_move_constructor&&) = delete; + constexpr payload_move_constructor(payload_move_constructor&&) requires should_be_trivial = default; + constexpr payload_move_constructor(payload_move_constructor&&) noexcept(should_be_noexcept) // + requires(!should_be_trivial) + : _val(42) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // Check payload type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + assert(from_value.value() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == 0); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + + { // Check error type + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + assert(from_value.value() == 0); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + + { // Check void payload + using Expected = expected; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_move_constructible_v == should_be_trivial); +#endif // !__clang__ + static_assert(is_move_constructible_v); + + Expected value_input{in_place}; + const Expected from_value{move(value_input)}; + assert(from_value); + static_assert(noexcept(Expected{move(value_input)}) == should_be_noexcept); + + Expected error_input{unexpect}; + const Expected from_error{move(error_input)}; + assert(!from_error); + assert(from_error.error() == (should_be_trivial ? 0 : 42)); + static_assert(noexcept(Expected{move(error_input)}) == should_be_noexcept); + } + } + + template + struct payload_destructor { + constexpr payload_destructor(bool& destructor_called) : _destructor_called(destructor_called) {} + bool& _destructor_called; + }; + template <> // TRANSITION, LLVM-46269 + struct payload_destructor { + constexpr payload_destructor(bool& destructor_called) : _destructor_called(destructor_called) {} + payload_destructor(const payload_destructor&) = default; + constexpr ~payload_destructor() { + _destructor_called = true; + }; + + bool& _destructor_called; + }; + template + constexpr void test_destructors() { + constexpr bool is_trivial = triviallyDestructible == IsTriviallyDestructible::Yes; + bool destructor_called = false; + { // Check payload + using Expected = expected, int>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected val{in_place, destructor_called}; + } + assert(destructor_called == !is_trivial); + destructor_called = false; + + { // Check error + using Expected = expected>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected err{unexpect, destructor_called}; + } + assert(destructor_called == !is_trivial); + destructor_called = false; + + { // Check void error + using Expected = expected>; +#ifndef __clang__ // TRANSITION, LLVM-46269 + static_assert(is_trivially_destructible_v == is_trivial); +#endif // !__clang__ + + Expected err{unexpect, destructor_called}; + } + assert(destructor_called == !is_trivial); + } + + constexpr void test_special_members() { + test_default_constructors(); + test_default_constructors(); + + test_copy_constructors(); + test_copy_constructors(); + test_copy_constructors(); + test_copy_constructors(); + + test_move_constructors(); + test_move_constructors(); + test_move_constructors(); + test_move_constructors(); + + test_destructors(); + test_destructors(); + } + + template + constexpr void test_constructors() noexcept { + constexpr bool should_be_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; + constexpr bool should_be_explicit = explicitConstructible == IsExplicitConstructible::Yes; + + struct payload_constructors { + payload_constructors() = default; + // Note clang does not accept local variables in explicit + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(const convertible&) noexcept(should_be_noexcept) + : _val(3) {} + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(convertible&&) noexcept(should_be_noexcept) + : _val(42) {} + constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + payload_constructors(initializer_list&, convertible) noexcept(should_be_noexcept) + : _val(1337) {} + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + { // constructing from convertible payload + using Input = convertible; + using Expected = expected; + static_assert(is_convertible_v != should_be_explicit); + static_assert(is_convertible_v != should_be_explicit); + + const Input const_input_value{}; + const Expected copy_constructed_value{const_input_value}; + assert(copy_constructed_value); + assert(copy_constructed_value.value() == 3); + static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept); + + const Expected move_constructed_value{Input{}}; + assert(move_constructed_value); + assert(move_constructed_value.value() == 42); + static_assert(noexcept(Expected{Input{}}) == should_be_noexcept); + } + + { // converting from different expected + using Input = expected; + using Expected = expected; + static_assert(is_convertible_v != should_be_explicit); + static_assert(is_convertible_v != should_be_explicit); + + const Input const_input_value{}; + const Expected copy_constructed_value{const_input_value}; + assert(copy_constructed_value); + assert(copy_constructed_value.value() == 3); + static_assert(noexcept(Expected{const_input_value}) == should_be_noexcept); + + const Expected move_constructed_value{Input{in_place}}; + assert(move_constructed_value); + assert(move_constructed_value.value() == 42); + static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept); + + const Input const_input_error{unexpect}; + const Expected copy_constructed_error{const_input_error}; + assert(!copy_constructed_error); + assert(copy_constructed_error.error() == 3); + static_assert(noexcept(Expected{const_input_error}) == should_be_noexcept); + + const Expected move_constructed_error{Input{unexpect}}; + assert(!move_constructed_error); + assert(move_constructed_error.error() == 42); + static_assert(noexcept(Expected{Input{unexpect}}) == should_be_noexcept); + } + + { // converting from unexpected + using Input = std::unexpected; + using Expected = expected; + + const Input const_input{in_place}; + const Expected copy_constructed{const_input}; + assert(!copy_constructed); + assert(copy_constructed.error() == 3); + static_assert(noexcept(Expected{const_input}) == should_be_noexcept); + + const Expected move_constructed{Input{in_place}}; + assert(!move_constructed); + assert(move_constructed.error() == 42); + static_assert(noexcept(Expected{Input{in_place}}) == should_be_noexcept); + } + + { // in place payload + using Expected = expected; + const Expected default_constructed{in_place}; + assert(default_constructed); + assert(default_constructed.value() == 0); + static_assert(noexcept(Expected{in_place})); + + const Expected value_constructed{in_place, convertible{}}; + assert(value_constructed); + assert(value_constructed.value() == 42); + static_assert(noexcept(Expected{in_place, convertible{}}) == should_be_noexcept); + + const Expected ilist_value_constructed{in_place, {1}, convertible{}}; + assert(ilist_value_constructed); + assert(ilist_value_constructed.value() == 1337); + static_assert(noexcept(Expected{in_place, {1}, convertible{}}) == should_be_noexcept); + } + + { // in place error + using Expected = expected; + const Expected default_constructed{unexpect}; + assert(!default_constructed); + assert(default_constructed.error() == 0); + static_assert(noexcept(Expected{unexpect})); + + const Expected value_constructed{unexpect, convertible{}}; + assert(!value_constructed); + assert(value_constructed.error() == 42); + static_assert(noexcept(Expected{unexpect, convertible{}}) == should_be_noexcept); + + const Expected ilist_value_constructed{unexpect, {1}, convertible{}}; + assert(!ilist_value_constructed); + assert(ilist_value_constructed.error() == 1337); + static_assert(noexcept(Expected{unexpect, {1}, convertible{}}) == should_be_noexcept); + } + } + + constexpr void test_constructors() noexcept { + test_constructors(); + test_constructors(); + test_constructors(); + test_constructors(); + } + + template + constexpr void test_assignment() noexcept { + constexpr bool nothrow_copy_constructible = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool nothrow_copy_assignable = nothrowCopyAssignable == IsNothrowCopyAssignable::Yes; + constexpr bool nothrow_move_assignable = nothrowMoveAssignable == IsNothrowMoveAssignable::Yes; + + struct payload_assign { + payload_assign() = default; + constexpr payload_assign(const int val) noexcept : _val(val) {} + constexpr payload_assign(const payload_assign& other) noexcept(nothrow_copy_constructible) + : _val(other._val) {} + constexpr payload_assign(payload_assign&& other) noexcept(nothrow_move_constructible) : _val(other._val) {} + constexpr payload_assign& operator=(const payload_assign& other) noexcept(nothrow_copy_assignable) { + _val = other._val; + return *this; + } + constexpr payload_assign& operator=(payload_assign&& other) noexcept(nothrow_move_assignable) { + _val = other._val; + return *this; + } + + constexpr payload_assign(const convertible& other) noexcept(nothrow_copy_constructible) + : _val(other._val) {} + constexpr payload_assign(convertible&& other) noexcept(nothrow_move_constructible) : _val(other._val) {} + constexpr payload_assign& operator=(const convertible& other) noexcept(nothrow_copy_assignable) { + _val = other._val; + return *this; + } + constexpr payload_assign& operator=(convertible&& other) noexcept(nothrow_move_assignable) { + _val = other._val; + return *this; + } + + [[nodiscard]] constexpr bool operator==(const int other) const noexcept { + return other == _val; + } + int _val = 0; + }; + + { // assign same expected as const ref check payload + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place, 42}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign same expected as const ref check error + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place, 42}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + { // assign same expected as const ref check error + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const Expected input_value{in_place}; + const Expected input_error{unexpect, 1337}; + + Expected assign_value_to_value{in_place}; + assign_value_to_value = input_value; + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign same expected as rvalue check payload + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = Expected{in_place, 42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place, 42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + + { // assign same expected as rvalue check error + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = Expected{in_place, 42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place, 42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = Expected{in_place, 42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + { // assign same expected as rvalue check error + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place}; + assign_value_to_value = Expected{in_place}; + static_assert(noexcept(assign_value_to_value = Expected{in_place}) == should_be_noexcept); + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Expected{unexpect, 1337}; + assert(assign_error_to_value.error() == 1337); + static_assert(noexcept(assign_error_to_value = Expected{unexpect, 1337}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = Expected{in_place}; + static_assert(noexcept(assign_value_to_error = Expected{in_place}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Expected{unexpect, 1337}; + assert(assign_error_to_error.error() == 1337); + static_assert(noexcept(assign_error_to_error = Expected{unexpect, 1337}) == should_be_noexcept); + } + + { // assign base type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const payload_assign input_value{42}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + } + + { // assign base type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = payload_assign{42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = convertible{42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = payload_assign{42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = convertible{42}) == should_be_noexcept); + } + + { // assign convertible type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + const convertible input_value{42}; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = input_value; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = input_value) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = input_value; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = input_value) == should_be_noexcept); + } + + { // assign convertible type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + + Expected assign_value_to_value{in_place, 1}; + assign_value_to_value = convertible{42}; + assert(assign_value_to_value.value() == 42); + static_assert(noexcept(assign_value_to_value = convertible{42}) == should_be_noexcept); + + Expected assign_value_to_error{unexpect, 1}; + assign_value_to_error = convertible{42}; + assert(assign_value_to_error.value() == 42); + static_assert(noexcept(assign_value_to_error = convertible{42}) == should_be_noexcept); + } + + { // assign error type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign expected error type const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign error type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign expected error type rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign convertible error const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign expected convertible error const ref + constexpr bool should_be_noexcept = nothrow_copy_constructible && nothrow_copy_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + const Unexpected input_error{42}; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = input_error; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = input_error) == should_be_noexcept); + + Expected assign_error_to_error{unexpect}; + assign_error_to_error = input_error; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = input_error) == should_be_noexcept); + } + + { // assign convertible error rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place, 1}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + + { // assign expected convertible error rvalue + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrow_move_assignable; + using Expected = expected; + using Unexpected = std::unexpected; + + Expected assign_error_to_value{in_place}; + assign_error_to_value = Unexpected{42}; + assert(assign_error_to_value.error() == 42); + static_assert(noexcept(assign_error_to_value = Unexpected{42}) == should_be_noexcept); + + Expected assign_error_to_error{unexpect, 1}; + assign_error_to_error = Unexpected{42}; + assert(assign_error_to_error.error() == 42); + static_assert(noexcept(assign_error_to_error = Unexpected{42}) == should_be_noexcept); + } + } + + constexpr void test_assignment() noexcept { + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + test_assignment(); + } + + constexpr void test_emplace() noexcept { + struct payload_emplace { + constexpr payload_emplace(bool& destructor_called) noexcept : _destructor_called(destructor_called) {} + constexpr payload_emplace(bool& destructor_called, const convertible&) noexcept + : _destructor_called(destructor_called), _val(3) {} + constexpr payload_emplace(bool& destructor_called, convertible&&) noexcept + : _destructor_called(destructor_called), _val(42) {} + constexpr payload_emplace(initializer_list&, bool& destructor_called, convertible) noexcept + : _destructor_called(destructor_called), _val(1337) {} + constexpr ~payload_emplace() { + _destructor_called = true; + } + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + bool& _destructor_called; + int _val = 0; + }; + using Expected = expected; + + bool destructor_called = false; + { + const convertible input; + Expected emplaced_lvalue(destructor_called); + emplaced_lvalue.emplace(destructor_called, input); + assert(destructor_called); + assert(emplaced_lvalue.value() == 3); + } + + destructor_called = false; + { + const convertible input; + Expected emplaced_lvalue(unexpect); + emplaced_lvalue.emplace(destructor_called, input); + assert(!destructor_called); + assert(emplaced_lvalue.value() == 3); + } + + destructor_called = false; + { + Expected emplaced_rvalue(destructor_called); + emplaced_rvalue.emplace(destructor_called, convertible{}); + assert(destructor_called); + assert(emplaced_rvalue.value() == 42); + } + + destructor_called = false; + { + Expected emplaced_rvalue(unexpect); + emplaced_rvalue.emplace(destructor_called, convertible{}); + assert(!destructor_called); + assert(emplaced_rvalue.value() == 42); + } + + destructor_called = false; + { + Expected emplaced_ilist(destructor_called); + emplaced_ilist.emplace({1}, destructor_called, convertible{}); + assert(destructor_called); + assert(emplaced_ilist.value() == 1337); + } + + destructor_called = false; + { + Expected emplaced_ilist(unexpect); + emplaced_ilist.emplace({1}, destructor_called, convertible{}); + assert(!destructor_called); + assert(emplaced_ilist.value() == 1337); + } + + { + using ExpectedVoid = expected; + ExpectedVoid with_value{in_place}; + with_value.emplace(); + assert(with_value); + + ExpectedVoid with_error{unexpect, 42}; + with_error.emplace(); + assert(with_error); + } + } + + template + struct payload_swap { + constexpr payload_swap(const int val) noexcept : _val(val) {} + constexpr payload_swap(const payload_swap&) noexcept = default; + constexpr payload_swap(payload_swap&& other) noexcept( + nothrowMoveConstructible == IsNothrowMoveConstructible::Yes) + : _val(other._val + 42) {} + // Note: cannot declare friends of function local structs + constexpr friend void swap(payload_swap& left, payload_swap& right) noexcept( + nothrowSwappable == IsNothrowSwappable::Yes) { + left._val = exchange(right._val, left._val); + } + + [[nodiscard]] constexpr bool operator==(const int val) const noexcept { + return _val == val; + } + + int _val = 0; + }; + + template + constexpr void test_swap() noexcept { + constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + constexpr bool should_be_noexcept = nothrow_move_constructible && nothrowSwappable == IsNothrowSwappable::Yes; + + { // Check payload member + using Expected = expected, int>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5); + assert(first_error.value() == 1337 + 42); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3); + assert(second_error.value() == 1 + 42); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check error member + using Expected = expected>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + if constexpr (nothrow_move_constructible) { + assert(first_value.error() == 5 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(first_value.error() == 5 + 42); + } + assert(first_error.value() == 1337); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + if constexpr (nothrow_move_constructible) { + assert(second_value.error() == 3 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(second_value.error() == 3 + 42); + } + assert(second_error.value() == 1); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check expected error member + using Expected = expected>; + Expected first_value{in_place}; + Expected second_value{in_place}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + first_value.swap(second_value); + assert(first_value && second_value); + static_assert(noexcept(first_value.swap(second_value)) == should_be_noexcept); + + first_error.swap(second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(first_error.swap(second_error)) == should_be_noexcept); + + first_value.swap(first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5 + 42); + static_assert(noexcept(first_value.swap(first_error)) == should_be_noexcept); + + second_error.swap(second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3 + 42); + static_assert(noexcept(second_error.swap(second_value)) == should_be_noexcept); + } + + { // Check payload friend + using Expected = expected, int>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5); + assert(first_error.value() == 1337 + 42); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3); + assert(second_error.value() == 1 + 42); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + + { // Check error friend + using Expected = expected>; + Expected first_value{1}; + Expected second_value{1337}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + assert(first_value.value() == 1337); + assert(second_value.value() == 1); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + if constexpr (nothrow_move_constructible) { + assert(first_value.error() == 5 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(first_value.error() == 5 + 42); + } + assert(first_error.value() == 1337); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + if constexpr (nothrow_move_constructible) { + assert(second_value.error() == 3 + 42 + 42); + } else { + // Here we are storing _Ty as a temporary so we only move once + assert(second_value.error() == 3 + 42); + } + assert(second_error.value() == 1); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + + { // Check expected error friend + using Expected = expected>; + Expected first_value{in_place}; + Expected second_value{in_place}; + Expected first_error{unexpect, 3}; + Expected second_error{unexpect, 5}; + + swap(first_value, second_value); + assert(first_value && second_value); + static_assert(noexcept(swap(first_value, second_value)) == should_be_noexcept); + + swap(first_error, second_error); + assert(!first_error && !second_error); + assert(first_error.error() == 5); + assert(second_error.error() == 3); + static_assert(noexcept(swap(first_error, second_error)) == should_be_noexcept); + + swap(first_value, first_error); + assert(first_error && !first_value); + assert(first_value.error() == 5 + 42); + static_assert(noexcept(swap(first_value, first_error)) == should_be_noexcept); + + swap(second_error, second_value); + assert(second_error && !second_value); + assert(second_value.error() == 3 + 42); + static_assert(noexcept(swap(second_error, second_value)) == should_be_noexcept); + } + } + + constexpr void test_swap() noexcept { + test_swap(); + test_swap(); + test_swap(); + test_swap(); + } + + constexpr void test_access() noexcept { + struct payload_access { + constexpr payload_access() noexcept : _val(this) {} + constexpr payload_access* operator&() noexcept { + return nullptr; + } + constexpr payload_access* operator&() const noexcept { + return nullptr; + } + + payload_access* _val; + }; + + { // operator->() + using Expected = expected; + Expected val; + assert(val.value()._val == val.operator->()); + assert(&val.value() == nullptr); + + const Expected const_val; + assert(&const_val.value() == nullptr); + } + + { // operator*() + using Expected = expected; + Expected lvalue{1}; + auto&& from_lvalue = *lvalue; + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{42}; + auto&& from_rvalue = *move(rvalue); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{1337}; + auto&& from_const_lvalue = *const_lvalue; + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{-42}; + auto&& from_const_rvalue = *move(const_rvalue); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected operator*() + using Expected = expected; + Expected lvalue{in_place}; + static_assert(is_same_v); + + Expected rvalue{in_place}; + static_assert(is_same_v); + + const Expected const_lvalue{in_place}; + static_assert(is_same_v); + + const Expected const_rvalue{in_place}; + static_assert(is_same_v); + } + + { // operator bool() + using Expected = expected; + const Expected defaulted; + assert(defaulted); + assert(defaulted.has_value()); + + const Expected with_value{in_place, 5}; + assert(with_value); + assert(with_value.has_value()); + + const Expected with_error{unexpect, 5}; + assert(!with_error); + assert(!with_error.has_value()); + } + + { // expected operator bool() + using Expected = expected; + const Expected defaulted; + assert(defaulted); + assert(defaulted.has_value()); + + const Expected with_value{in_place}; + assert(with_value); + assert(with_value.has_value()); + + const Expected with_error{unexpect}; + assert(!with_error); + assert(!with_error.has_value()); + } + + { // value() + using Expected = expected; + Expected lvalue{1}; + auto&& from_lvalue = lvalue.value(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{42}; + auto&& from_rvalue = move(rvalue).value(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{1337}; + auto&& from_const_lvalue = const_lvalue.value(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{-42}; + auto&& from_const_rvalue = move(const_rvalue).value(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected value() + using Expected = expected; + Expected lvalue{in_place}; + static_assert(is_same_v); + + Expected rvalue{in_place}; + static_assert(is_same_v); + + const Expected const_lvalue{in_place}; + static_assert(is_same_v); + + const Expected const_rvalue{in_place}; + static_assert(is_same_v); + } + + if (!is_constant_evaluated()) { // invalid value() + using Expected = expected; + try { + Expected lvalue{unexpect, 1}; + [[maybe_unused]] auto&& from_lvalue = lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1); + static_assert(is_same_v); + } + + try { + Expected rvalue{unexpect, 42}; + [[maybe_unused]] auto&& from_rvalue = move(rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 42); + static_assert(is_same_v); + } + + try { + const Expected const_lvalue{unexpect, 1337}; + [[maybe_unused]] auto&& from_rvalue = const_lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1337); + static_assert(is_same_v); + } + + try { + const Expected const_rvalue{unexpect, -42}; + [[maybe_unused]] auto&& from_const_rvalue = move(const_rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == -42); + static_assert(is_same_v); + } + } + + if (!is_constant_evaluated()) { // expected invalid value() + using Expected = expected; + try { + Expected lvalue{unexpect, 1}; + lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1); + static_assert(is_same_v); + } + + try { + Expected rvalue{unexpect, 42}; + move(rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 42); + static_assert(is_same_v); + } + + try { + const Expected const_lvalue{unexpect, 1337}; + const_lvalue.value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == 1337); + static_assert(is_same_v); + } + + try { + const Expected const_rvalue{unexpect, -42}; + move(const_rvalue).value(); + assert(false); + } catch (bad_expected_access& with_error) { + assert(with_error.error() == -42); + static_assert(is_same_v); + } + } + + { // error() + using Expected = expected; + Expected lvalue{unexpect, 1}; + auto&& from_lvalue = lvalue.error(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{unexpect, 42}; + auto&& from_rvalue = move(rvalue).error(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{unexpect, 1337}; + auto&& from_const_lvalue = const_lvalue.error(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{unexpect, -42}; + auto&& from_const_rvalue = move(const_rvalue).error(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + + { // expected error() + using Expected = expected; + Expected lvalue{unexpect, 1}; + auto&& from_lvalue = lvalue.error(); + assert(from_lvalue == 1); + static_assert(is_same_v); + + Expected rvalue{unexpect, 42}; + auto&& from_rvalue = move(rvalue).error(); + assert(from_rvalue == 42); + static_assert(is_same_v); + + const Expected const_lvalue{unexpect, 1337}; + auto&& from_const_lvalue = const_lvalue.error(); + assert(from_const_lvalue == 1337); + static_assert(is_same_v); + + const Expected const_rvalue{unexpect, -42}; + auto&& from_const_rvalue = move(const_rvalue).error(); + assert(from_const_rvalue == -42); + static_assert(is_same_v); + } + } + + template + constexpr void test_monadic() { + constexpr bool construction_is_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; + constexpr bool conversion_is_noexcept = nothrowConvertible == IsNothrowConvertible::Yes; + constexpr bool should_be_noexcept = construction_is_noexcept && conversion_is_noexcept; + + struct payload_monadic { + constexpr payload_monadic(const int val) noexcept : _val(val) {} + constexpr payload_monadic(const payload_monadic& other) noexcept(construction_is_noexcept) + : _val(other._val + 2) {} + constexpr payload_monadic(payload_monadic&& other) noexcept(construction_is_noexcept) + : _val(other._val + 3) {} + constexpr payload_monadic(const convertible& val) noexcept(conversion_is_noexcept) : _val(val._val + 4) {} + constexpr payload_monadic(convertible&& val) noexcept(conversion_is_noexcept) : _val(val._val + 5) {} + + [[nodiscard]] constexpr bool operator==(const payload_monadic& right) const noexcept { + return _val == right._val; + } + + int _val = 0; + }; + + { // with payload argument + using Expected = expected; + + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.value_or(payload_monadic{1}) == 42 + 2); + assert(const_with_value.value_or(payload_monadic{1}) == 1337 + 2); + static_assert(noexcept(with_value.value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_value.value_or(payload_monadic{1})) == construction_is_noexcept); + + assert(move(with_value).value_or(payload_monadic{1}) == 42 + 3); + assert(move(const_with_value).value_or(payload_monadic{1}) == 1337 + 2); + static_assert(noexcept(move(with_value).value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_value).value_or(payload_monadic{1})) == construction_is_noexcept); + + const payload_monadic input{2}; + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.value_or(payload_monadic{1}) == 1 + 3); + assert(const_with_error.value_or(input) == 2 + 2); + static_assert(noexcept(with_error.value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_error.value_or(input)) == construction_is_noexcept); + + assert(move(with_error).value_or(payload_monadic{1}) == 1 + 3); + assert(move(const_with_error).value_or(input) == 2 + 2); + static_assert(noexcept(move(with_error).value_or(payload_monadic{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_error).value_or(input)) == construction_is_noexcept); + } + + { // with convertible argument + using Expected = expected; + + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.value_or(convertible{1}) == 42 + 2); + assert(const_with_value.value_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(with_value.value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_value.value_or(convertible{1})) == should_be_noexcept); + + assert(move(with_value).value_or(convertible{1}) == 42 + 3); + assert(move(const_with_value).value_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(move(with_value).value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_value).value_or(convertible{1})) == should_be_noexcept); + + const convertible input{2}; + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.value_or(convertible{1}) == 1 + 5); + assert(const_with_error.value_or(input) == 2 + 4); + static_assert(noexcept(with_error.value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_error.value_or(input)) == should_be_noexcept); + + assert(move(with_error).value_or(convertible{1}) == 1 + 5); + assert(move(const_with_error).value_or(input) == 2 + 4); + static_assert(noexcept(move(with_error).value_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_error).value_or(input)) == should_be_noexcept); + } + } + + constexpr void test_monadic() noexcept { + test_monadic(); + test_monadic(); + test_monadic(); + test_monadic(); + } + + template + constexpr void test_equality() noexcept { + constexpr bool should_be_noexcept = nothrowComparable == IsNothrowComparable::Yes; + + struct payload_equality { + constexpr payload_equality(const int val) noexcept : _val(val) {} + + [[nodiscard]] constexpr bool operator==(const payload_equality& right) const noexcept(should_be_noexcept) { + return _val == right._val; + } + + int _val = 0; + }; + + { // compare against same expected + using Expected = expected; + + const Expected with_value1{in_place, 42}; + const Expected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(with_value1 != with_value2); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const Expected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // expected compare against same expected + using Expected = expected; + + const Expected with_value{in_place}; + assert(with_value == with_value); + assert(!(with_value != with_value)); + static_assert(noexcept(with_value == with_value) == should_be_noexcept); + static_assert(noexcept(with_value != with_value) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const Expected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value != error1); + static_assert(noexcept(with_value != error1) == should_be_noexcept); + } + + { // compare against different expected + using Expected = expected; + using OtherExpected = expected; + + const Expected with_value1{in_place, 42}; + const OtherExpected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(with_value1 != with_value2); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const OtherExpected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // expected compare against different expected + using Expected = expected; + using OtherExpected = expected; + + const Expected with_value1{in_place}; + const OtherExpected with_value2{in_place, 1337}; + assert(with_value1 == with_value1); + assert(!(with_value1 != with_value2)); + static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); + static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); + + const Expected error1{unexpect, 42}; + const OtherExpected error2{unexpect, 1337}; + assert(error1 == error1); + assert(error1 != error2); + static_assert(noexcept(error1 == error1) == should_be_noexcept); + static_assert(noexcept(error1 != error2) == should_be_noexcept); + + assert(with_value1 != error1); + static_assert(noexcept(with_value1 != error1) == should_be_noexcept); + } + + { // compare against base type + using Base = payload_equality; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value == Base{42}); + assert(with_value != Base{1337}); + static_assert(noexcept(with_value == Base{42}) == should_be_noexcept); + static_assert(noexcept(with_value != Base{1337}) == should_be_noexcept); + + assert(with_error != 1337); + static_assert(noexcept(with_error != 1337) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // compare against unexpect with same base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // expected compare against unexpect with same base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + } + + { // compare against unexpect with different base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place, 42}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + + assert(with_error != Base{1337}); + static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); + } + + { // expected compare against unexpect with different base + using Base = payload_equality; + using Unexpected = std::unexpected; + using Expected = expected; + + const Expected with_value{in_place}; + const Expected with_error{unexpect, 1337}; + assert(with_value != Unexpected{1337}); + static_assert(noexcept(with_value != Unexpected{1337}) == should_be_noexcept); + + assert(with_error == Unexpected{1337}); + assert(with_error != Unexpected{42}); + static_assert(noexcept(with_error == Unexpected{1337}) == should_be_noexcept); + static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); + } + } + + constexpr void test_equality() noexcept { + test_equality(); + test_equality(); + } + + constexpr bool test_all() noexcept { + test_aliases(); + test_special_members(); + test_constructors(); + test_assignment(); + test_emplace(); + test_swap(); + test_access(); + test_monadic(); + test_equality(); + + return true; + } +} // namespace test_expected + +int main() { + test_unexpected::test_all(); + static_assert(test_unexpected::test_all()); + test_expected::test_all(); + static_assert(test_expected::test_all()); +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc b/tests/std/tests/P1502R1_standard_library_header_units/importable_cxx_library_headers.jsonc index 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 80b90bf88f..8b81c49c41 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -28,6 +28,7 @@ import ; import ; import ; import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 96bf42755d..46b6d3a694 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -714,6 +714,20 @@ STATIC_ASSERT(__cpp_lib_execution == 201603L); #endif #endif +#if _HAS_CXX23 +#ifndef __cpp_lib_expected +#error __cpp_lib_expected is not defined +#elif __cpp_lib_expected != 202202L +#error __cpp_lib_expected is not 202202L +#else +STATIC_ASSERT(__cpp_lib_expected == 202202L); +#endif +#else +#ifdef __cpp_lib_expected +#error __cpp_lib_expected is defined +#endif +#endif + #ifndef __cpp_lib_experimental_erase_if #error __cpp_lib_experimental_erase_if is not defined #elif __cpp_lib_experimental_erase_if != 201411L From f1f5ff118a3720b83f0fdd5e672d2c1b8b4777cb Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 9 Apr 2022 08:56:10 +0200 Subject: [PATCH 02/26] Address review comments --- stl/inc/expected | 9 +++++---- .../tests/P1502R1_standard_library_header_units/test.cpp | 6 ++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index afba47f1eb..a4faadf49d 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -8,9 +8,9 @@ #define _EXPECTED_ #include #if _STL_COMPILER_PREPROCESSOR -#if !_HAS_CXX23 && defined(__cpp_lib_concepts) +#ifndef __cpp_lib_expected #pragma message("The contents of are available only with C++23 or later.") -#else // ^^^ !_HAS_CXX23 / _HAS_CXX23 vvv +#else // ^^^ !__cpp_lib_expected / __cpp_lib_expected vvv #include #include #include @@ -171,7 +171,7 @@ public: // [expected.object.ctor] // clang-format off - constexpr expected() requires is_default_constructible_v<_Ty> : _Has_value(true), _Value() {}; + constexpr expected() requires is_default_constructible_v<_Ty> : _Has_value(true), _Value() {} constexpr expected(const expected& _Other) noexcept( is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_copy_constructible_v<_Err>) // @@ -324,6 +324,7 @@ public: _STD destroy_at(_STD addressof(_Old_val)); _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); } else { + _STL_INTERNAL_STATIC_ASSERT(is_nothrow_move_constructible_v<_Second>); _Second _Tmp(_STD move(_Old_val)); _STD destroy_at(addressof(_Old_val)); try { @@ -1015,6 +1016,6 @@ _STD_END _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // _HAS_CXX23 +#endif // __cpp_lib_expected #endif // _STL_COMPILER_PREPROCESSOR #endif // _EXPECTED_ 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 8b81c49c41..9fd4d6ae11 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -292,6 +292,12 @@ int main() { assert(count(execution::par, begin(arr), end(arr), 0) == 4); } + { + puts("Testing ."); + constexpr expected test{unexpect, 42}; + assert(test.error() == 42); + } + { puts("Testing ."); constexpr wstring_view dot{L"."}; From af61334f2a5f9c0486210420e84110df0ed4faa7 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sat, 9 Apr 2022 13:26:42 +0200 Subject: [PATCH 03/26] Add some transition comments and paper number --- stl/inc/expected | 41 ++++++++----------- stl/inc/yvals_core.h | 1 + .../test.compile.pass.cpp | 2 +- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index a4faadf49d..37f462a18e 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -29,13 +29,13 @@ _STD_BEGIN // [expected.un.object] // clang-format off -template +template // TRANSITION, LWG-3688 requires (is_object_v<_Err> && !is_array_v<_Err> && !is_volatile_v<_Err> && !is_const_v<_Err>) class unexpected { // clang-format on static_assert(!_Is_specialization_v<_Err, unexpected>, "E must not be a specialization of unexpected"); - template + template // TRANSITION, LWG-3688 friend class expected; public: @@ -61,8 +61,7 @@ public: // clang-format on // [expected.un.observe] - // TRANSITION, P2549 -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { return _STD forward(_Self)._Unexpected; } @@ -126,8 +125,7 @@ public: __CLR_OR_THIS_CALL explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) : _Unexpected(_STD move(_Unex)) {} -// TRANSITION, P2549 -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { return _STD forward(_Self)._Unexpected; } @@ -156,9 +154,9 @@ struct unexpect_t { inline constexpr unexpect_t unexpect{}; -template +template // TRANSITION, LWG-3688 class expected { - template + template // TRANSITION, LWG-3688 friend class expected; public: @@ -525,7 +523,7 @@ public: return _STD addressof(_Value); } -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) operator*(this expected&& _Self) { return _STD forward(_Self)._Value; } @@ -551,7 +549,7 @@ public: return _Has_value; } -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) value(this expected&& _Self) { if (_Has_value) { return _STD forward(_Self)._Value; @@ -585,7 +583,7 @@ public: } #endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened return _STD forward(_Self)._Unexpected; } @@ -626,11 +624,7 @@ public: } // [expected.object.eq] -#ifdef __clang__ // TRANSITION, LLVM-XXXXX - template _UErr> -#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv template <_Weakly_equality_comparable_with<_Ty> _Uty, _Weakly_equality_comparable_with<_Err> _UErr> -#endif // !__clang__ _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Value == *_Right)) && noexcept( _Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened @@ -643,13 +637,12 @@ public: } } -#ifdef __clang__ // TRANSITION, LLVM-XXXXX + // clang-format off template -#else // ^^^ __clang__ ^^^ / vvv !__clang__ vvv - template <_Weakly_equality_comparable_with<_Ty> _Uty> -#endif // !__clang__ + requires (!same_as<_Uty, expected> && _Weakly_equality_comparable_with<_Ty, _Uty>) _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened + // clang-format on if (!_Left._Has_value) { return false; } else { @@ -668,7 +661,7 @@ public: } private: -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); } @@ -694,7 +687,7 @@ private: }; }; -template +template // TRANSITION, LWG-3688 requires is_void_v<_Ty> class expected<_Ty, _Err> { public: @@ -915,7 +908,7 @@ public: } constexpr void operator*() const noexcept {} -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr void value(this expected&& _Self) { if (!_Has_value) { _Throw_bad_expected_access(); @@ -944,7 +937,7 @@ public: } #endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened return _STD forward(_Self)._Unexpected; } @@ -985,7 +978,7 @@ public: } private: -#ifdef __cpp_explicit_this_parameter +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a12ee701de..fd9e5c23cf 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -304,6 +304,7 @@ // P2186R2 Removing Garbage Collection Support // P2273R3 constexpr unique_ptr // P2443R1 views::chunk_by +// P2549R0 unexpected::error() // Parallel Algorithms Notes // C++ allows an implementation to implement parallel algorithms as calls to the serial algorithms. 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 46b6d3a694..f0e4922c68 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,7 +714,7 @@ STATIC_ASSERT(__cpp_lib_execution == 201603L); #endif #endif -#if _HAS_CXX23 +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, EDG concepts support #ifndef __cpp_lib_expected #error __cpp_lib_expected is not defined #elif __cpp_lib_expected != 202202L From b6300ad721fc65575fdbf18c57acfd4129258423 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Sun, 10 Apr 2022 13:14:43 +0200 Subject: [PATCH 04/26] Use guard type rather than try catch blocks --- stl/inc/expected | 56 +++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 37f462a18e..e3c278cf17 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -310,6 +310,19 @@ public: // [expected.object.assign] + template + requires is_nothrow_move_constructible_v<_Uty> + struct _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...> || is_nothrow_move_constructible_v<_First>) // strengthened @@ -322,15 +335,12 @@ public: _STD destroy_at(_STD addressof(_Old_val)); _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); } else { - _STL_INTERNAL_STATIC_ASSERT(is_nothrow_move_constructible_v<_Second>); _Second _Tmp(_STD move(_Old_val)); - _STD destroy_at(addressof(_Old_val)); - try { - _STD construct_at(addressof(_New_val), _STD forward<_Args>(_Vals)...); - } catch (...) { - _STD construct_at(addressof(_Old_val), _STD move(_Tmp)); - throw; - } + _STD destroy_at(_STD addressof(_Old_val)); + + _GuardTy<_Second> _Guard{_STD addressof(_Old_val), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_New_val), _STD forward<_Args>(_Vals)...); + _Guard._Target = nullptr; } } @@ -477,25 +487,23 @@ public: } else if constexpr (is_nothrow_move_constructible_v<_Err>) { _Err _Tmp(_STD move(_Other._Unexpected)); _Other._Unexpected.~_Err(); - try { - _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); - _Value.~_Ty(); - _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); - } catch (...) { - _STD construct_at(_STD addressof(_Other._Unexpected), _STD move(_Tmp)); - throw; - } + + _GuardTy<_Err> _Guard{_STD addressof(_Other._Unexpected), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); + _Guard._Target = nullptr; + + _Value.~_Ty(); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); } else { _Ty _Tmp(_STD move(_Value)); _Value.~_Ty(); - try { - _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); - _Other._Unexpected.~_Err(); - _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); - } catch (...) { - _STD construct_at(_STD addressof(_Value), _STD move(_Tmp)); - throw; - } + + _GuardTy<_Ty> _Guard{_STD addressof(_Value), _STD addressof(_Tmp)}; + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); + _Guard._Target = nullptr; + + _Other._Unexpected.~_Err(); + _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); } _Has_value = false; From a6a7c24cf51ff06a2c79b9b7315b59cd4b98a87a Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 11 Apr 2022 10:28:58 +0200 Subject: [PATCH 05/26] Use consistent condition for feature test checks --- .../tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f0e4922c68..1fa6317ffa 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,7 +714,7 @@ STATIC_ASSERT(__cpp_lib_execution == 201603L); #endif #endif -#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, EDG concepts support +#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 From c4e5abd6090d5b4f2e8a7182e57f29731372f502 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Mon, 18 Apr 2022 21:44:02 +0200 Subject: [PATCH 06/26] Address review comments --- stl/inc/expected | 202 ++++++++++-------- tests/std/tests/P0323R12_expected/test.cpp | 4 + .../include_each_header_alone_matrix.lst | 1 + 3 files changed, 113 insertions(+), 94 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index e3c278cf17..d7ea29fe85 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -28,14 +28,14 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN // [expected.un.object] -// clang-format off -template // TRANSITION, LWG-3688 - requires (is_object_v<_Err> && !is_array_v<_Err> && !is_volatile_v<_Err> && !is_const_v<_Err>) +template class unexpected { - // clang-format on + static_assert(is_object_v<_Err>, "E must be an object type"); + static_assert(!is_array_v<_Err>, "E must not be an array type"); + static_assert(!is_volatile_v<_Err> && !is_const_v<_Err>, "E must not be cv-qualified"); static_assert(!_Is_specialization_v<_Err, unexpected>, "E must not be a specialization of unexpected"); - template // TRANSITION, LWG-3688 + template friend class expected; public: @@ -54,16 +54,16 @@ public: : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} template - requires (!is_same_v, unexpected> && !is_same_v, in_place_t> // + requires (!is_same_v, unexpected> && !is_same_v, in_place_t> && is_constructible_v<_Err, _UError>) constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened : _Unexpected(_STD forward<_UError>(_Unex)) {} // clang-format on - // [expected.un.observe] -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { - return _STD forward(_Self)._Unexpected; + // [expected.un.obs] +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 + _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { + return (_STD forward(_Self)._Unexpected); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { @@ -81,17 +81,18 @@ public: #endif // !__cpp_explicit_this_parameter // [expected.un.swap] - constexpr void swap(unexpected& _Other) noexcept(is_nothrow_swappable_v<_Err>) requires is_swappable_v<_Err> { + 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> { + friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept(is_nothrow_swappable_v<_Err>) { + static_assert(is_swappable_v<_Err>, "E must be swappable"); _Left.swap(_Right); } // [expected.un.eq] - template <_Weakly_equality_comparable_with<_Err> _UErr> + 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(); @@ -101,6 +102,9 @@ private: _Err _Unexpected; }; +template +unexpected(_Err) -> unexpected<_Err>; + template class bad_expected_access; @@ -111,8 +115,14 @@ public: return "Bad expected access"; } -#if !_HAS_EXCEPTIONS 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); } @@ -122,24 +132,24 @@ protected: template class bad_expected_access : public bad_expected_access { public: - __CLR_OR_THIS_CALL explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) + explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened : _Unexpected(_STD move(_Unex)) {} -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) error(this unexpected&& _Self) noexcept { - return _STD forward(_Self)._Unexpected; +#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 + _NODISCARD decltype(auto) error(this auto&& _Self) noexcept { + return (_STD forward(_Self)._Unexpected); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv - _NODISCARD constexpr const _Err& error() const& noexcept { + _NODISCARD const _Err& error() const& noexcept { return _Unexpected; } - _NODISCARD constexpr _Err& error() & noexcept { + _NODISCARD _Err& error() & noexcept { return _Unexpected; } - _NODISCARD constexpr const _Err&& error() const&& noexcept { + _NODISCARD const _Err&& error() const&& noexcept { return _STD move(_Unexpected); } - _NODISCARD constexpr _Err&& error() && noexcept { + _NODISCARD _Err&& error() && noexcept { return _STD move(_Unexpected); } #endif // !__cpp_explicit_this_parameter @@ -154,9 +164,9 @@ struct unexpect_t { inline constexpr unexpect_t unexpect{}; -template // TRANSITION, LWG-3688 +template class expected { - template // TRANSITION, LWG-3688 + template friend class expected; public: @@ -168,12 +178,14 @@ public: using rebind = expected<_Uty, error_type>; // [expected.object.ctor] - // clang-format off - constexpr expected() requires is_default_constructible_v<_Ty> : _Has_value(true), _Value() {} + constexpr expected() 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>) // - requires (!is_trivially_copy_constructible_v<_Ty> && !is_trivially_copy_constructible_v<_Err> // + 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 @@ -189,8 +201,8 @@ public: 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_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err>) // strengthened + 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 @@ -225,7 +237,7 @@ public: && _Is_not_convertible<_Uty, _UErr> constexpr explicit(!is_convertible_v || !is_convertible_v) expected(const expected<_Uty, _UErr>& _Other) noexcept(is_nothrow_constructible_v<_Ty, const _Uty&>&& - is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened : _Has_value(_Other._Has_value) { if (_Has_value) { _STD construct_at(_STD addressof(_Value), _Other._Value); @@ -238,7 +250,7 @@ public: requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> constexpr explicit(!is_convertible_v<_Uty, _Ty> || !is_convertible_v<_UErr, _Err>) expected(expected<_Uty, _UErr>&& _Other) noexcept( - is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) // strengthened : _Has_value(_Other._Has_value) { if (_Has_value) { _STD construct_at(_STD addressof(_Value), _STD forward<_Uty>(_Other._Value)); @@ -252,47 +264,47 @@ public: requires (!is_same_v, in_place_t> && !is_same_v, expected> // && !_Is_specialization_v, unexpected> && is_constructible_v<_Ty, _Uty>) constexpr explicit(!is_convertible_v<_Uty, _Ty>) - expected(_Uty&& _Other) noexcept(is_nothrow_constructible_v<_Ty, _Uty>) /* strengthened */ - : _Has_value(true), _Value(_STD forward<_Uty>(_Other)) {} + 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 expected(const unexpected<_UErr>& _Other) noexcept( - is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ - : _Has_value(false), _Unexpected(_Other._Unexpected) {} + is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + : _Unexpected(_Other._Unexpected), _Has_value(false) {} template requires is_constructible_v<_Err, _UErr> - constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ - : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened + : _Unexpected(_STD forward<_UErr>(_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 */ - : _Has_value(true), _Value(_STD forward<_Args>(_Vals)...) {} + 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 */ - : _Has_value(true), _Value(_Ilist, _STD forward<_Args>(_Vals)...) {} + 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 */ - : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + 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 */ - : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + 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] @@ -523,7 +535,7 @@ public: _Lhs.swap(_Rhs); } - // [expected.object.observe] + // [expected.object.obs] _NODISCARD constexpr const _Ty* operator->() const noexcept { return _STD addressof(_Value); } @@ -531,9 +543,9 @@ public: return _STD addressof(_Value); } -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) operator*(this expected&& _Self) { - return _STD forward(_Self)._Value; +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + _NODISCARD constexpr decltype(auto) operator*(this auto&& _Self) noexcept { + return (_STD forward(_Self)._Value); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& operator*() const& noexcept { @@ -557,43 +569,48 @@ public: return _Has_value; } -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) value(this expected&& _Self) { - if (_Has_value) { - return _STD forward(_Self)._Value; +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + _NODISCARD constexpr decltype(auto) value(this auto&& _Self) { + if (_Self._Has_value) { + return (_STD forward(_Self)._Value); } - _Throw_bad_expected_access(); + + _STD forward(_Self)._Throw_bad_expected_access(); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& value() const& { if (_Has_value) { return _Value; } + _Throw_bad_expected_access(); } _NODISCARD constexpr _Ty& value() & { if (_Has_value) { return _Value; } + _Throw_bad_expected_access(); } _NODISCARD constexpr const _Ty&& value() const&& { if (_Has_value) { return _STD move(_Value); } + _Throw_bad_expected_access(); } _NODISCARD constexpr _Ty&& value() && { if (_Has_value) { return _STD move(_Value); } + _Throw_bad_expected_access(); } #endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened - return _STD forward(_Self)._Unexpected; +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { // strengthened + return (_STD forward(_Self)._Unexpected); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened @@ -632,7 +649,7 @@ public: } // [expected.object.eq] - template <_Weakly_equality_comparable_with<_Ty> _Uty, _Weakly_equality_comparable_with<_Err> _UErr> + template _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 @@ -645,12 +662,9 @@ public: } } - // clang-format off template - requires (!same_as<_Uty, expected> && _Weakly_equality_comparable_with<_Ty, _Uty>) _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened - // clang-format on if (!_Left._Has_value) { return false; } else { @@ -658,7 +672,7 @@ public: } } - template <_Weakly_equality_comparable_with<_Err> _UErr> + template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value) { @@ -669,9 +683,9 @@ public: } private: -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { - _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { + _THROW(bad_expected_access{(_STD forward(_Self)._Unexpected)}); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { @@ -688,14 +702,14 @@ private: } #endif // !__cpp_explicit_this_parameter - bool _Has_value; union { _Ty _Value; _Err _Unexpected; }; + bool _Has_value; }; -template // TRANSITION, LWG-3688 +template requires is_void_v<_Ty> class expected<_Ty, _Err> { public: @@ -710,7 +724,7 @@ public: constexpr expected() noexcept : _Has_value(true) {} // clang-format off - constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) /* strengthened */ + constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened requires (!is_trivially_copy_constructible_v<_Err>) : _Has_value(_Other._Has_value) { if (!_Has_value) { @@ -720,7 +734,7 @@ public: expected(const expected& ) requires is_trivially_copy_constructible_v<_Err> = default; - constexpr expected(expected&& _Other) noexcept(is_nothrow_move_constructible_v<_Err>) /* strengthened */ + constexpr expected(expected&& _Other) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened requires (!is_trivially_move_constructible_v<_Err> && is_move_constructible_v<_Err>) : _Has_value(_Other._Has_value) { if (!_Has_value) { @@ -740,7 +754,7 @@ public: template requires is_void_v<_Uty> && is_constructible_v<_Err, const _UErr&> && _Is_not_convertible<_Uty, _UErr> constexpr explicit(!is_convertible_v) expected(const expected<_Uty, _UErr>& _Other) noexcept( - is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ + is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened : _Has_value(_Other._Has_value) { if (!_Has_value) { _Unexpected = _Other._Unexpected; @@ -750,7 +764,7 @@ public: template requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> constexpr explicit(!is_convertible_v<_UErr, _Err>) - expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ + expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened : _Has_value(_Other._Has_value) { if (!_Has_value) { _Unexpected = _STD forward<_UErr>(_Other._Unexpected); @@ -760,27 +774,27 @@ public: template requires is_constructible_v<_Err, const _UErr&> constexpr expected(const unexpected<_UErr>& _Other) noexcept( - is_nothrow_constructible_v<_Err, const _UErr&>) /* strengthened */ - : _Has_value(false), _Unexpected(_Other._Unexpected) {} + is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + : _Unexpected(_Other._Unexpected), _Has_value(false) {} template requires is_constructible_v<_Err, _UErr> - constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) /* strengthened */ - : _Has_value(false), _Unexpected(_STD forward<_UErr>(_Other._Unexpected)) {} + constexpr expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened + : _Unexpected(_STD forward<_UErr>(_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 */ - : _Has_value(false), _Unexpected(_STD forward<_Args>(_Vals)...) {} + is_nothrow_constructible_v<_Err, _Args...>) // strengthened + : _Unexpected(_STD forward<_Args>(_Vals)...), _Has_value(false) {} template requires is_constructible_v<_Err, _Args...> constexpr explicit expected(unexpect_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( - is_nothrow_constructible_v<_Err, _Args...>) /* strengthened */ - : _Has_value(false), _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} + is_nothrow_constructible_v<_Err, _Args...>) // strengthened + : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...), _Has_value(false) {} // [expected.void.dtor] @@ -907,7 +921,7 @@ public: } } - // [expected.void.observe] + // [expected.void.obs] constexpr explicit operator bool() const noexcept { return _Has_value; } @@ -916,10 +930,10 @@ public: } constexpr void operator*() const noexcept {} -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr void value(this expected&& _Self) { - if (!_Has_value) { - _Throw_bad_expected_access(); +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + _NODISCARD constexpr void value(this auto&& _Self) { + if (!_Self._Has_value) { + _STD forward(_Self)._Throw_bad_expected_access(); } } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv @@ -945,9 +959,9 @@ public: } #endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - _NODISCARD constexpr decltype(auto) error(this expected&& _Self) noexcept { // strengthened - return _STD forward(_Self)._Unexpected; +#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 + _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { // strengthened + return (_STD forward(_Self)._Unexpected); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened @@ -965,7 +979,7 @@ public: #endif // !__cpp_explicit_this_parameter // [expected.void.eq] - template _UErr> + template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value != _Right.has_value()) { @@ -975,7 +989,7 @@ public: } } - template <_Weakly_equality_comparable_with<_Err> _UErr> + template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value) { @@ -987,8 +1001,8 @@ public: private: #ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - [[noreturn]] inline void _Throw_bad_expected_access(this expected&& _Self) { - _THROW(bad_expected_access{_STD forward(_Self)._Unexpected}); + [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { + _THROW(bad_expected_access{(_STD forward(_Self)._Unexpected)}); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { @@ -1005,10 +1019,10 @@ private: } #endif // !__cpp_explicit_this_parameter - bool _Has_value; union { _Err _Unexpected; }; + bool _Has_value; }; _STD_END diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index 9d109fb789..573f270f81 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -126,6 +126,10 @@ namespace test_unexpected { 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 + auto deduced = std::unexpected(test_error{42}); + static_assert(same_as); } constexpr bool test_all() { diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 34d1708711..41d82c601d 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" From e305bfa86ad41b434e51a6d928405aa4f26dcabb Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 19 Apr 2022 13:49:14 +0200 Subject: [PATCH 07/26] More mandates instead of constraints --- stl/inc/expected | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index d7ea29fe85..5d19e4aa77 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -629,8 +629,7 @@ public: template _Uty> _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( - is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened - requires is_copy_constructible_v<_Ty> { + is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened if (_Has_value) { return _Value; } else { @@ -639,8 +638,7 @@ public: } template _Uty> _NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept( - is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) // strengthened - requires is_move_constructible_v<_Ty> { + is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened if (_Has_value) { return _STD move(_Value); } else { From 64d19d49a069f19c4dab2e045d16f6120b5c57b8 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Tue, 19 Apr 2022 15:21:43 +0200 Subject: [PATCH 08/26] Add debugger visualisation as suggested by @SuperWig --- stl/debugger/STL.natvis | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) 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} }} From 6e0094aa07dba8e487d6780903faa0f895def3cc Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 29 Apr 2022 14:23:59 +0200 Subject: [PATCH 09/26] Address review comments --- stl/inc/expected | 49 ++++++++--------- stl/inc/xutility | 9 ++++ tests/std/tests/P0323R12_expected/test.cpp | 61 ++++++++++++++++++++-- 3 files changed, 90 insertions(+), 29 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 5d19e4aa77..f2753c5151 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -62,8 +62,8 @@ public: // [expected.un.obs] #ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 - _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { - return (_STD forward(_Self)._Unexpected); + _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { + return _STD _Forward_as(_Self)._Unexpected; } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { @@ -136,8 +136,8 @@ public: : _Unexpected(_STD move(_Unex)) {} #ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 - _NODISCARD decltype(auto) error(this auto&& _Self) noexcept { - return (_STD forward(_Self)._Unexpected); + _NODISCARD auto&& error(this auto&& _Self) noexcept { + return _STD _Forward_as(_Self)._Unexpected; } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD const _Err& error() const& noexcept { @@ -185,7 +185,7 @@ public: // 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> + 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 @@ -197,12 +197,12 @@ public: } // clang-format off - expected(const expected& _Other) requires + 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>) // strengthened - requires (!is_trivially_move_constructible_v<_Ty> && !is_trivially_move_constructible_v<_Err> + 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 @@ -211,7 +211,7 @@ public: } else { _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); } - }; + } // clang-format off expected(expected&&) requires @@ -253,15 +253,15 @@ public: is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_constructible_v<_Err, _UErr>) // strengthened : _Has_value(_Other._Has_value) { if (_Has_value) { - _STD construct_at(_STD addressof(_Value), _STD forward<_Uty>(_Other._Value)); + _STD construct_at(_STD addressof(_Value), _STD move(_Other._Value)); } else { - _STD construct_at(_STD addressof(_Unexpected), _STD forward<_UErr>(_Other._Unexpected)); + _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> // + 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 @@ -317,6 +317,7 @@ public: } // 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 @@ -544,8 +545,8 @@ public: } #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr decltype(auto) operator*(this auto&& _Self) noexcept { - return (_STD forward(_Self)._Value); + _NODISCARD constexpr auto&& operator*(this auto&& _Self) noexcept { + return _STD _Forward_as(_Self)._Value; } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& operator*() const& noexcept { @@ -570,12 +571,12 @@ public: } #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr decltype(auto) value(this auto&& _Self) { + _NODISCARD constexpr auto&& value(this auto&& _Self) { if (_Self._Has_value) { - return (_STD forward(_Self)._Value); + return _STD _Forward_as(_Self)._Value; } - _STD forward(_Self)._Throw_bad_expected_access(); + _Forward_as(_Self)._Throw_bad_expected_access(); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& value() const& { @@ -609,8 +610,8 @@ public: #endif // !__cpp_explicit_this_parameter #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { // strengthened - return (_STD forward(_Self)._Unexpected); + _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { // strengthened + return _STD _Forward_as(_Self)._Unexpected; } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened @@ -683,7 +684,7 @@ public: private: #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { - _THROW(bad_expected_access{(_STD forward(_Self)._Unexpected)}); + _THROW(bad_expected_access{_Forward_as(_Self)._Unexpected}); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { @@ -931,7 +932,7 @@ public: #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 _NODISCARD constexpr void value(this auto&& _Self) { if (!_Self._Has_value) { - _STD forward(_Self)._Throw_bad_expected_access(); + _Forward_as(_Self)._Throw_bad_expected_access(); } } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv @@ -958,8 +959,8 @@ public: #endif // !__cpp_explicit_this_parameter #ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr decltype(auto) error(this auto&& _Self) noexcept { // strengthened - return (_STD forward(_Self)._Unexpected); + _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { // strengthened + return _STD _Forward_as(_Self)._Unexpected; } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened @@ -1000,7 +1001,7 @@ public: private: #ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { - _THROW(bad_expected_access{(_STD forward(_Self)._Unexpected)}); + _THROW(bad_expected_access{_Forward_as(_Self)._Unexpected}); } #else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { diff --git a/stl/inc/xutility b/stl/inc/xutility index 6f8fc7c442..dc08ee37fe 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -208,6 +208,15 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; +#if _HAS_CXX23 +template +_NODISCARD constexpr auto&& _Forward_as(auto&& _Val) noexcept { + _STL_INTERNAL_STATIC_ASSERT(same_as<_Source&, decltype(_Val)&>); + using _Cooked = typename _Add_qualifiers<_Source&&>::template _Apply<_Raw>; + return (_Cooked) _Val; // C-style cast used to bypass access control +} +#endif // _HAS_CXX23 + template _NODISCARD constexpr void* _Voidify_iter(_Iter _It) noexcept { if constexpr (is_pointer_v<_Iter>) { diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index 573f270f81..1b3573d6d8 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -128,7 +128,7 @@ namespace test_unexpected { static_assert(is_same_v); // deduction guide - auto deduced = std::unexpected(test_error{42}); + std::unexpected deduced(test_error{42}); static_assert(same_as); } @@ -211,11 +211,11 @@ namespace test_expected { struct payload_copy_constructor { payload_copy_constructor() = default; - payload_copy_constructor& operator=(const payload_copy_constructor&) = delete; - constexpr payload_copy_constructor(const payload_copy_constructor&) requires should_be_trivial = default; + 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; @@ -282,6 +282,15 @@ namespace test_expected { 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>); + } } template >); + static_assert(!is_move_constructible_v>); + } } template @@ -953,6 +971,39 @@ namespace test_expected { 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>); + + 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>); + + struct not_move_constructible { + not_move_constructible(not_move_constructible&&) = delete; + }; + + static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); + } } constexpr void test_assignment() noexcept { From dcb6544c56e5dde97e14bf2e33a978fb0134b18d Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sat, 30 Apr 2022 21:48:42 -0700 Subject: [PATCH 10/26] use is_same_v instead of same_as in code guarded by _HAS_CXX23 but not __cpp_lib_concepts. --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index dc08ee37fe..1710ecc59f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -211,7 +211,7 @@ struct _Get_rebind_alias<_Ty, _Other, void_t _NODISCARD constexpr auto&& _Forward_as(auto&& _Val) noexcept { - _STL_INTERNAL_STATIC_ASSERT(same_as<_Source&, decltype(_Val)&>); + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Source&, decltype(_Val)&>); using _Cooked = typename _Add_qualifiers<_Source&&>::template _Apply<_Raw>; return (_Cooked) _Val; // C-style cast used to bypass access control } From 854159d219dc686d7b96e65f2d69296a1ffa2468 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 19 May 2022 15:44:53 -0700 Subject: [PATCH 11/26] Drop __cpp_explicit_this_parameter. --- stl/inc/expected | 61 +----------------------------------------------- stl/inc/xutility | 9 ------- 2 files changed, 1 insertion(+), 69 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index f2753c5151..3f5ab22354 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -61,11 +61,6 @@ public: // clang-format on // [expected.un.obs] -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 - _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { - return _STD _Forward_as(_Self)._Unexpected; - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { return _Unexpected; } @@ -78,7 +73,6 @@ public: _NODISCARD constexpr _Err&& error() && noexcept { return _STD move(_Unexpected); } -#endif // !__cpp_explicit_this_parameter // [expected.un.swap] constexpr void swap(unexpected& _Other) noexcept(is_nothrow_swappable_v<_Err>) { @@ -135,11 +129,6 @@ public: explicit bad_expected_access(_Err _Unex) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened : _Unexpected(_STD move(_Unex)) {} -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549, P0847R7 - _NODISCARD auto&& error(this auto&& _Self) noexcept { - return _STD _Forward_as(_Self)._Unexpected; - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD const _Err& error() const& noexcept { return _Unexpected; } @@ -152,7 +141,6 @@ public: _NODISCARD _Err&& error() && noexcept { return _STD move(_Unexpected); } -#endif // !__cpp_explicit_this_parameter private: _Err _Unexpected; @@ -544,11 +532,6 @@ public: return _STD addressof(_Value); } -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr auto&& operator*(this auto&& _Self) noexcept { - return _STD _Forward_as(_Self)._Value; - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& operator*() const& noexcept { return _Value; } @@ -561,7 +544,6 @@ public: _NODISCARD constexpr _Ty&& operator*() && noexcept { return _STD move(_Value); } -#endif // !__cpp_explicit_this_parameter _NODISCARD constexpr explicit operator bool() const noexcept { return _Has_value; @@ -570,15 +552,6 @@ public: return _Has_value; } -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr auto&& value(this auto&& _Self) { - if (_Self._Has_value) { - return _STD _Forward_as(_Self)._Value; - } - - _Forward_as(_Self)._Throw_bad_expected_access(); - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Ty& value() const& { if (_Has_value) { return _Value; @@ -607,13 +580,7 @@ public: _Throw_bad_expected_access(); } -#endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { // strengthened - return _STD _Forward_as(_Self)._Unexpected; - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened return _Unexpected; } @@ -626,7 +593,6 @@ public: _NODISCARD constexpr _Err&& error() && noexcept { // strengthened return _STD move(_Unexpected); } -#endif // !__cpp_explicit_this_parameter template _Uty> _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( @@ -682,11 +648,6 @@ public: } private: -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { - _THROW(bad_expected_access{_Forward_as(_Self)._Unexpected}); - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { _THROW(bad_expected_access{_Unexpected}); } @@ -699,7 +660,6 @@ private: [[noreturn]] inline void _Throw_bad_expected_access() && { _THROW(bad_expected_access{_STD move(_Unexpected)}); } -#endif // !__cpp_explicit_this_parameter union { _Ty _Value; @@ -929,13 +889,7 @@ public: } constexpr void operator*() const noexcept {} -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr void value(this auto&& _Self) { - if (!_Self._Has_value) { - _Forward_as(_Self)._Throw_bad_expected_access(); - } - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv + _NODISCARD constexpr void value() const& { if (!_Has_value) { _Throw_bad_expected_access(); @@ -956,13 +910,7 @@ public: _Throw_bad_expected_access(); } } -#endif // !__cpp_explicit_this_parameter -#ifdef __cpp_explicit_this_parameter // TRANSITION, P0847R7 - _NODISCARD constexpr auto&& error(this auto&& _Self) noexcept { // strengthened - return _STD _Forward_as(_Self)._Unexpected; - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened return _Unexpected; } @@ -975,7 +923,6 @@ public: _NODISCARD constexpr _Err&& error() && noexcept { // strengthened return _STD move(_Unexpected); } -#endif // !__cpp_explicit_this_parameter // [expected.void.eq] template @@ -999,11 +946,6 @@ public: } private: -#ifdef __cpp_explicit_this_parameter // TRANSITION, P2549 - [[noreturn]] inline void _Throw_bad_expected_access(this auto&& _Self) { - _THROW(bad_expected_access{_Forward_as(_Self)._Unexpected}); - } -#else // ^^^ __cpp_explicit_this_parameter ^^^ / vvv !__cpp_explicit_this_parameter vvv [[noreturn]] inline void _Throw_bad_expected_access() const& { _THROW(bad_expected_access{_Unexpected}); } @@ -1016,7 +958,6 @@ private: [[noreturn]] inline void _Throw_bad_expected_access() && { _THROW(bad_expected_access{_STD move(_Unexpected)}); } -#endif // !__cpp_explicit_this_parameter union { _Err _Unexpected; diff --git a/stl/inc/xutility b/stl/inc/xutility index 39b38cbc8b..dc5f37a0c8 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -208,15 +208,6 @@ struct _Get_rebind_alias<_Ty, _Other, void_t; }; -#if _HAS_CXX23 -template -_NODISCARD constexpr auto&& _Forward_as(auto&& _Val) noexcept { - _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Source&, decltype(_Val)&>); - using _Cooked = typename _Add_qualifiers<_Source&&>::template _Apply<_Raw>; - return (_Cooked) _Val; // C-style cast used to bypass access control -} -#endif // _HAS_CXX23 - template _NODISCARD constexpr void* _Voidify_iter(_Iter _It) noexcept { if constexpr (is_pointer_v<_Iter>) { From e339374fe71370eca4fd1cf73e87cb40a9edccad Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 19 May 2022 18:14:39 -0700 Subject: [PATCH 12/26] Code review feedback, fixing several bugs. --- stl/inc/expected | 223 +++++++++++++++++++++++++++-------------------- 1 file changed, 127 insertions(+), 96 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 3f5ab22354..c5f35cf486 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -11,10 +11,8 @@ #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 #include #include @@ -27,13 +25,23 @@ _STL_DISABLE_CLANG_WARNINGS _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.object] template class unexpected { - static_assert(is_object_v<_Err>, "E must be an object type"); - static_assert(!is_array_v<_Err>, "E must not be an array type"); - static_assert(!is_volatile_v<_Err> && !is_const_v<_Err>, "E must not be cv-qualified"); - static_assert(!_Is_specialization_v<_Err, unexpected>, "E must not be a specialization of unexpected"); + static_assert(_Check_unexpected_argument<_Err>::value); template friend class expected; @@ -80,8 +88,8 @@ public: _Swap_adl(_Unexpected, _Other._Unexpected); } - friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept(is_nothrow_swappable_v<_Err>) { - static_assert(is_swappable_v<_Err>, "E must be swappable"); + friend constexpr void swap(unexpected& _Left, unexpected& _Right) noexcept(is_nothrow_swappable_v<_Err>) // + requires is_swappable_v<_Err> { _Left.swap(_Right); } @@ -154,6 +162,17 @@ 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; @@ -185,7 +204,7 @@ public: } // clang-format off - expected(const expected&) requires + expected(const expected&) requires is_trivially_copy_constructible_v<_Ty> && is_trivially_copy_constructible_v<_Err> = default; constexpr expected(expected&& _Other) noexcept( @@ -224,8 +243,8 @@ public: requires is_constructible_v<_Ty, const _Uty&> && is_constructible_v<_Err, const _UErr&> // && _Is_not_convertible<_Uty, _UErr> constexpr explicit(!is_convertible_v || !is_convertible_v) - expected(const expected<_Uty, _UErr>& _Other) noexcept(is_nothrow_constructible_v<_Ty, const _Uty&>&& - is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + 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); @@ -258,14 +277,15 @@ public: template requires is_constructible_v<_Err, const _UErr&> - constexpr expected(const unexpected<_UErr>& _Other) noexcept( - is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + 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 expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened - : _Unexpected(_STD forward<_UErr>(_Other._Unexpected)), _Has_value(false) {} + 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...> @@ -275,7 +295,7 @@ public: // clang-format off template - requires is_constructible_v<_Ty, initializer_list<_Uty>&,_Args... > + 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) {} @@ -289,7 +309,7 @@ public: // clang-format off template - requires is_constructible_v<_Err, initializer_list<_Uty>&, _Args... > + 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) {} @@ -313,7 +333,7 @@ public: // [expected.object.assign] template requires is_nothrow_move_constructible_v<_Uty> - struct _GuardTy { + struct _NODISCARD _GuardTy { constexpr _GuardTy(_Uty* _Target_, _Uty* _Tmp_) noexcept : _Target(_Target_), _Tmp(_Tmp_) {} constexpr ~_GuardTy() noexcept { if (_Target) { @@ -329,15 +349,15 @@ public: is_nothrow_constructible_v<_First, _Args...> || is_nothrow_move_constructible_v<_First>) // strengthened { if constexpr (is_nothrow_constructible_v<_First, _Args...>) { - _STD destroy_at(_STD addressof(_Old_val)); + _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)...); - _STD destroy_at(_STD addressof(_Old_val)); + _Old_val.~_Second(); _STD construct_at(_STD addressof(_New_val), _STD move(_Tmp)); } else { _Second _Tmp(_STD move(_Old_val)); - _STD destroy_at(_STD addressof(_Old_val)); + _Old_val.~_Second(); _GuardTy<_Second> _Guard{_STD addressof(_Old_val), _STD addressof(_Tmp)}; _STD construct_at(_STD addressof(_New_val), _STD forward<_Args>(_Vals)...); @@ -348,7 +368,8 @@ public: 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> // + 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; @@ -366,7 +387,7 @@ public: constexpr expected& operator=(expected&& _Other) noexcept( is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_constructible_v<_Err> // - && is_nothrow_move_assignable_v<_Ty>&& is_nothrow_move_assignable_v<_Err>) // strengthened + && 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>) { @@ -396,27 +417,27 @@ public: _Value = _STD forward<_Uty>(_Other); } else { _Reinit_expected(_Value, _Unexpected, _STD forward<_Uty>(_Other)); + _Has_value = true; } - _Has_value = true; return *this; } // clang-format off template requires (is_constructible_v<_Err, const _UErr&>&& is_assignable_v<_Err&, const _UErr&> // - && (is_nothrow_constructible_v<_Err, _UErr> || is_nothrow_move_constructible_v<_Ty> // + && (is_nothrow_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; } - _Has_value = false; return *this; } @@ -430,11 +451,11 @@ public: // clang-format on if (_Has_value) { _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); + _Has_value = false; } else { _Unexpected = _STD move(_Other._Unexpected); } - _Has_value = false; return *this; } @@ -461,9 +482,13 @@ public: constexpr _Ty& emplace(initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept { // clang-format on if (_Has_value) { - _Value.~_Ty(); + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } } else { - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } _Has_value = true; } @@ -479,19 +504,17 @@ public: if (_Has_value && _Other._Has_value) { _Swap_adl(_Value, _Other._Value); } else if (_Has_value) { - if constexpr (is_nothrow_move_constructible_v<_Ty> && is_nothrow_move_constructible_v<_Err>) { - _Err _Tmp(_STD move(_Other._Unexpected)); - _Other._Unexpected.~_Err(); - _STD construct_at(_STD addressof(_Other._Value), _STD move(_Value)); - _Value.~_Ty(); - _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); - } else if constexpr (is_nothrow_move_constructible_v<_Err>) { + if constexpr (is_nothrow_move_constructible_v<_Err>) { _Err _Tmp(_STD move(_Other._Unexpected)); _Other._Unexpected.~_Err(); - _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_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; + } _Value.~_Ty(); _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); @@ -557,55 +580,65 @@ public: return _Value; } - _Throw_bad_expected_access(); + _Throw_bad_expected_access_lv(); } _NODISCARD constexpr _Ty& value() & { if (_Has_value) { return _Value; } - _Throw_bad_expected_access(); + _Throw_bad_expected_access_lv(); } _NODISCARD constexpr const _Ty&& value() const&& { if (_Has_value) { return _STD move(_Value); } - _Throw_bad_expected_access(); + _Throw_bad_expected_access_rv(); } _NODISCARD constexpr _Ty&& value() && { if (_Has_value) { return _STD move(_Value); } - _Throw_bad_expected_access(); + _Throw_bad_expected_access_rv(); } - _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + _NODISCARD constexpr const _Err& error() const& noexcept { return _Unexpected; } - _NODISCARD constexpr _Err& error() & noexcept { // strengthened + _NODISCARD constexpr _Err& error() & noexcept { return _Unexpected; } - _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + _NODISCARD constexpr const _Err&& error() const&& noexcept { return _STD move(_Unexpected); } - _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + _NODISCARD constexpr _Err&& error() && noexcept { return _STD move(_Unexpected); } - template _Uty> + 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 _Uty> + 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 { @@ -613,11 +646,16 @@ public: } } + // [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) { @@ -630,10 +668,10 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened - if (!_Left._Has_value) { - return false; - } else { + if (_Left._Has_value) { return _Left._Value == _Right; + } else { + return false; } } @@ -648,16 +686,16 @@ public: } private: - [[noreturn]] inline void _Throw_bad_expected_access() const& { + [[noreturn]] void _Throw_bad_expected_access_lv() const { _THROW(bad_expected_access{_Unexpected}); } - [[noreturn]] inline void _Throw_bad_expected_access() & { - _THROW(bad_expected_access{_STD move(_Unexpected)}); + [[noreturn]] void _Throw_bad_expected_access_lv() { + _THROW(bad_expected_access{_Unexpected}); } - [[noreturn]] inline void _Throw_bad_expected_access() const&& { + [[noreturn]] void _Throw_bad_expected_access_rv() const { _THROW(bad_expected_access{_STD move(_Unexpected)}); } - [[noreturn]] inline void _Throw_bad_expected_access() && { + [[noreturn]] void _Throw_bad_expected_access_rv() { _THROW(bad_expected_access{_STD move(_Unexpected)}); } @@ -671,6 +709,11 @@ private: 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; @@ -684,16 +727,16 @@ public: // clang-format off constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened - requires (!is_trivially_copy_constructible_v<_Err>) + 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; + expected(const expected&) requires is_trivially_copy_constructible_v<_Err> = default; - constexpr expected(expected&& _Other) noexcept(is_nothrow_move_constructible_v<_Err>) // strengthened + 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) { @@ -716,7 +759,7 @@ public: is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened : _Has_value(_Other._Has_value) { if (!_Has_value) { - _Unexpected = _Other._Unexpected; + _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); } } @@ -726,20 +769,21 @@ public: expected(expected<_Uty, _UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened : _Has_value(_Other._Has_value) { if (!_Has_value) { - _Unexpected = _STD forward<_UErr>(_Other._Unexpected); + _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); } } template requires is_constructible_v<_Err, const _UErr&> - constexpr expected(const unexpected<_UErr>& _Other) noexcept( - is_nothrow_constructible_v<_Err, const _UErr&>) // strengthened + 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 expected(unexpected<_UErr>&& _Other) noexcept(is_nothrow_constructible_v<_Err, _UErr>) // strengthened - : _Unexpected(_STD forward<_UErr>(_Other._Unexpected)), _Has_value(false) {} + 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) {} @@ -749,12 +793,13 @@ public: is_nothrow_constructible_v<_Err, _Args...>) // strengthened : _Unexpected(_STD forward<_Args>(_Vals)...), _Has_value(false) {} + // clang-format off template - requires is_constructible_v<_Err, _Args...> + 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, _Args...>) // strengthened + 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 { @@ -764,6 +809,7 @@ public: } // clang-format off + // TRANSITION, LLVM-46269, destructor order is significant ~expected() requires is_trivially_destructible_v<_Err> = default; // clang-format on @@ -787,7 +833,7 @@ public: } constexpr expected& operator=(expected&& _Other) noexcept( - is_nothrow_move_constructible_v<_Err>&& is_nothrow_move_assignable_v<_Err>) // strengthened + 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 @@ -881,33 +927,23 @@ public: } // [expected.void.obs] - constexpr explicit operator bool() const noexcept { + _NODISCARD constexpr explicit operator bool() const noexcept { return _Has_value; } - constexpr bool has_value() const noexcept { + _NODISCARD constexpr bool has_value() const noexcept { return _Has_value; } constexpr void operator*() const noexcept {} - _NODISCARD constexpr void value() const& { - if (!_Has_value) { - _Throw_bad_expected_access(); - } - } - _NODISCARD constexpr void value() & { + constexpr void value() const& { if (!_Has_value) { - _Throw_bad_expected_access(); + _Throw_bad_expected_access_lv(); } } - _NODISCARD constexpr void value() const&& { + constexpr void value() && { if (!_Has_value) { - _Throw_bad_expected_access(); - } - } - _NODISCARD constexpr void value() && { - if (!_Has_value) { - _Throw_bad_expected_access(); + _Throw_bad_expected_access_rv(); } } @@ -926,8 +962,9 @@ public: // [expected.void.eq] template - _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + requires is_void_v<_Uty> // + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) // + noexcept(noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value != _Right.has_value()) { return false; } else { @@ -946,16 +983,10 @@ public: } private: - [[noreturn]] inline void _Throw_bad_expected_access() const& { + [[noreturn]] void _Throw_bad_expected_access_lv() const { _THROW(bad_expected_access{_Unexpected}); } - [[noreturn]] inline void _Throw_bad_expected_access() & { - _THROW(bad_expected_access{_STD move(_Unexpected)}); - } - [[noreturn]] inline void _Throw_bad_expected_access() const&& { - _THROW(bad_expected_access{_STD move(_Unexpected)}); - } - [[noreturn]] inline void _Throw_bad_expected_access() && { + [[noreturn]] void _Throw_bad_expected_access_rv() { _THROW(bad_expected_access{_STD move(_Unexpected)}); } From 1d8c6e5f015fd6e34594a2d870d1c8c70032389b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 19 May 2022 19:04:17 -0700 Subject: [PATCH 13/26] Drop tests comparing `expected` to `expected`. These can't compile because the equality operators are constrained to prevent it. --- tests/std/tests/P0323R12_expected/test.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index 1b3573d6d8..ec40262493 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -1781,28 +1781,6 @@ namespace test_expected { static_assert(noexcept(with_value1 != error1) == should_be_noexcept); } - { // expected compare against different expected - using Expected = expected; - using OtherExpected = expected; - - const Expected with_value1{in_place}; - const OtherExpected with_value2{in_place, 1337}; - assert(with_value1 == with_value1); - assert(!(with_value1 != with_value2)); - static_assert(noexcept(with_value1 == with_value1) == should_be_noexcept); - static_assert(noexcept(with_value1 != with_value2) == should_be_noexcept); - - const Expected error1{unexpect, 42}; - const OtherExpected error2{unexpect, 1337}; - assert(error1 == error1); - assert(error1 != error2); - static_assert(noexcept(error1 == error1) == should_be_noexcept); - static_assert(noexcept(error1 != error2) == should_be_noexcept); - - assert(with_value1 != error1); - static_assert(noexcept(with_value1 != error1) == should_be_noexcept); - } - { // compare against base type using Base = payload_equality; using Expected = expected; From 4a00d41c090eb8225d33aa18d9a1d47392c501c3 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 19 May 2022 19:06:20 -0700 Subject: [PATCH 14/26] Add tests for `expected` constructors. This verifies that friendship has been granted. --- tests/std/tests/P0323R12_expected/test.cpp | 71 ++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index ec40262493..6e81f44365 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -578,6 +578,77 @@ namespace test_expected { 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 { From 3198d49f7e4b02eabb541557d16543a670bcca9c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 19 May 2022 19:09:52 -0700 Subject: [PATCH 15/26] Formatting: Drop `//` within `clang-format off`. --- stl/inc/expected | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index c5f35cf486..d60b071d4f 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -425,8 +425,8 @@ public: // 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> // + 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 @@ -444,7 +444,7 @@ public: // 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_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 From 2b2eff4c574ea6d64aed06d10ee697db71a21e2b Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 20 May 2022 09:04:16 +0200 Subject: [PATCH 16/26] Fix formatting damage --- stl/inc/expected | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index d60b071d4f..18f37ab765 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -961,10 +961,12 @@ public: } // [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(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + requires is_void_v<_Uty> + _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + // clang-format on if (_Left._Has_value != _Right.has_value()) { return false; } else { From b5ac7469a5db6232bea54509a31aeba7692d5567 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 20 May 2022 09:04:37 +0200 Subject: [PATCH 17/26] Do not call trivial destructors --- stl/inc/expected | 52 ++++++++++++++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 18f37ab765..9ad540dbf2 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -318,9 +318,13 @@ public: // [expected.object.dtor] constexpr ~expected() noexcept { if (_Has_value) { - _Value.~_Ty(); + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } } else { - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } } } @@ -506,7 +510,9 @@ public: } else if (_Has_value) { if constexpr (is_nothrow_move_constructible_v<_Err>) { _Err _Tmp(_STD move(_Other._Unexpected)); - _Other._Unexpected.~_Err(); + 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)); @@ -516,17 +522,23 @@ public: _Guard._Target = nullptr; } - _Value.~_Ty(); + if constexpr (!is_trivially_destructible_v<_Ty>) { + _Value.~_Ty(); + } _STD construct_at(_STD addressof(_Unexpected), _STD move(_Tmp)); } else { _Ty _Tmp(_STD move(_Value)); - _Value.~_Ty(); + 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; - _Other._Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Other._Unexpected.~_Err(); + } _STD construct_at(_STD addressof(_Other._Value), _STD move(_Tmp)); } @@ -823,7 +835,9 @@ public: _STD construct_at(_STD addressof(_Unexpected), _Other._Unexpected); _Has_value = false; } else if (_Other._Has_value) { - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } _Has_value = true; } else { _Unexpected = _Other._Unexpected; @@ -841,7 +855,9 @@ public: _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); _Has_value = false; } else if (_Other._Has_value) { - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } _Has_value = true; } else { _Unexpected = _STD move(_Other._Unexpected); @@ -880,7 +896,9 @@ public: constexpr void emplace() noexcept { if (!_Has_value) { - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } _Has_value = true; } } @@ -893,12 +911,16 @@ public: // nothing } else if (_Has_value) { _STD construct_at(_STD addressof(_Unexpected), _STD move(_Other._Unexpected)); - _Other._Unexpected.~_Err(); + 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)); - _Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Unexpected.~_Err(); + } _Has_value = true; _Other._Has_value = false; } else { @@ -913,12 +935,16 @@ public: // nothing } else if (_Left._Has_value) { _STD construct_at(_STD addressof(_Left._Unexpected), _STD move(_Right._Unexpected)); - _Right._Unexpected.~_Err(); + 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)); - _Left._Unexpected.~_Err(); + if constexpr (!is_trivially_destructible_v<_Err>) { + _Left._Unexpected.~_Err(); + } _Left._Has_value = true; _Right._Has_value = false; } else { From 3dd480be662e599e0845d873166701ad648ae95c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 00:04:58 -0700 Subject: [PATCH 18/26] Test review feedback. --- tests/std/tests/P0323R12_expected/test.cpp | 167 ++++++++++++++++----- 1 file changed, 131 insertions(+), 36 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index 6e81f44365..abebc24017 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -2,8 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include +#include +#include #include #include +#include +#include using namespace std; @@ -85,9 +89,15 @@ namespace test_unexpected { 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); @@ -104,13 +114,15 @@ namespace test_unexpected { // [expected.un.swap] in_place_lvalue_constructed.swap(in_place_rvalue_constructed); assert(in_place_lvalue_constructed == Unexpect{test_error{42}}); - assert(in_place_ilist_lvalue_constructed == Unexpect{test_error{1}}); + 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.observe] + // [expected.un.obs] auto&& lvalue_error = base_error_constructed.error(); assert(lvalue_error == test_error{4}); static_assert(is_same_v); @@ -198,6 +210,7 @@ namespace test_expected { if constexpr (should_be_defaultable) { const expected defaulted; + assert(defaulted); assert(defaulted.value() == 42); } } @@ -290,6 +303,7 @@ namespace test_expected { static_assert(!is_copy_constructible_v>); static_assert(!is_copy_constructible_v>); + static_assert(!is_copy_constructible_v>); } } @@ -382,6 +396,7 @@ namespace test_expected { static_assert(!is_move_constructible_v>); static_assert(!is_move_constructible_v>); + static_assert(!is_move_constructible_v>); } } @@ -496,6 +511,11 @@ namespace test_expected { 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 @@ -707,21 +727,25 @@ namespace test_expected { 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); } @@ -734,24 +758,29 @@ namespace test_expected { 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; @@ -760,19 +789,23 @@ namespace test_expected { 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); } @@ -783,21 +816,25 @@ namespace test_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); } @@ -808,43 +845,52 @@ namespace test_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); } @@ -856,11 +902,13 @@ namespace test_expected { 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); } @@ -871,13 +919,32 @@ namespace test_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 = convertible{42}) == should_be_noexcept); + 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 = convertible{42}) == should_be_noexcept); + 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 @@ -887,11 +954,13 @@ namespace test_expected { 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); } @@ -902,11 +971,13 @@ namespace test_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); } @@ -919,11 +990,13 @@ namespace test_expected { 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); } @@ -936,11 +1009,13 @@ namespace test_expected { 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); } @@ -952,11 +1027,13 @@ namespace test_expected { 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); } @@ -968,11 +1045,13 @@ namespace test_expected { 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); } @@ -985,11 +1064,13 @@ namespace test_expected { 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); } @@ -1002,11 +1083,13 @@ namespace test_expected { 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}; + 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); } @@ -1018,11 +1101,13 @@ namespace test_expected { 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); } @@ -1034,11 +1119,13 @@ namespace test_expected { 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); } @@ -1051,6 +1138,7 @@ namespace test_expected { 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; @@ -1067,6 +1155,7 @@ namespace test_expected { 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; @@ -1074,6 +1163,7 @@ namespace test_expected { static_assert(!is_move_assignable_v>); static_assert(!is_move_assignable_v>); + static_assert(!is_move_assignable_v>); } } @@ -1140,6 +1230,7 @@ namespace test_expected { Expected emplaced_lvalue(destructor_called); emplaced_lvalue.emplace(destructor_called, input); assert(destructor_called); + assert(emplaced_lvalue); assert(emplaced_lvalue.value() == 3); } @@ -1149,6 +1240,7 @@ namespace test_expected { Expected emplaced_lvalue(unexpect); emplaced_lvalue.emplace(destructor_called, input); assert(!destructor_called); + assert(emplaced_lvalue); assert(emplaced_lvalue.value() == 3); } @@ -1157,6 +1249,7 @@ namespace test_expected { Expected emplaced_rvalue(destructor_called); emplaced_rvalue.emplace(destructor_called, convertible{}); assert(destructor_called); + assert(emplaced_rvalue); assert(emplaced_rvalue.value() == 42); } @@ -1165,6 +1258,7 @@ namespace test_expected { Expected emplaced_rvalue(unexpect); emplaced_rvalue.emplace(destructor_called, convertible{}); assert(!destructor_called); + assert(emplaced_rvalue); assert(emplaced_rvalue.value() == 42); } @@ -1173,6 +1267,7 @@ namespace test_expected { Expected emplaced_ilist(destructor_called); emplaced_ilist.emplace({1}, destructor_called, convertible{}); assert(destructor_called); + assert(emplaced_ilist); assert(emplaced_ilist.value() == 1337); } @@ -1181,6 +1276,7 @@ namespace test_expected { Expected emplaced_ilist(unexpect); emplaced_ilist.emplace({1}, destructor_called, convertible{}); assert(!destructor_called); + assert(emplaced_ilist); assert(emplaced_ilist.value() == 1337); } @@ -1435,25 +1531,20 @@ namespace test_expected { constexpr void test_access() noexcept { struct payload_access { - constexpr payload_access() noexcept : _val(this) {} - constexpr payload_access* operator&() noexcept { - return nullptr; - } - constexpr payload_access* operator&() const noexcept { - return nullptr; - } + payload_access* operator&() = delete; + const payload_access* operator&() const = delete; - payload_access* _val; + int x = 17; + int y = 29; }; { // operator->() using Expected = expected; Expected val; - assert(val.value()._val == val.operator->()); - assert(&val.value() == nullptr); + assert(val->x == 17); const Expected const_val; - assert(&const_val.value() == nullptr); + assert(const_val->y == 29); } { // operator*() @@ -1577,27 +1668,27 @@ namespace test_expected { Expected rvalue{unexpect, 42}; [[maybe_unused]] auto&& from_rvalue = move(rvalue).value(); assert(false); - } catch (bad_expected_access& with_error) { + } catch (const bad_expected_access& with_error) { assert(with_error.error() == 42); - static_assert(is_same_v); + static_assert(is_same_v); } try { const Expected const_lvalue{unexpect, 1337}; - [[maybe_unused]] auto&& from_rvalue = const_lvalue.value(); + [[maybe_unused]] auto&& from_const_lvalue = const_lvalue.value(); assert(false); } catch (bad_expected_access& with_error) { - assert(with_error.error() == 1337); - static_assert(is_same_v); + 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 (bad_expected_access& with_error) { - assert(with_error.error() == -42); - static_assert(is_same_v); + } catch (const bad_expected_access& with_error) { + assert(move(with_error).error() == -42); + static_assert(is_same_v); } } @@ -1607,36 +1698,32 @@ namespace test_expected { Expected lvalue{unexpect, 1}; lvalue.value(); assert(false); - } catch (bad_expected_access& with_error) { + } catch (const bad_expected_access& with_error) { assert(with_error.error() == 1); - static_assert(is_same_v); } try { Expected rvalue{unexpect, 42}; move(rvalue).value(); assert(false); - } catch (bad_expected_access& with_error) { + } catch (const bad_expected_access& with_error) { assert(with_error.error() == 42); - static_assert(is_same_v); } try { const Expected const_lvalue{unexpect, 1337}; const_lvalue.value(); assert(false); - } catch (bad_expected_access& with_error) { + } catch (const bad_expected_access& with_error) { assert(with_error.error() == 1337); - static_assert(is_same_v); } try { const Expected const_rvalue{unexpect, -42}; move(const_rvalue).value(); assert(false); - } catch (bad_expected_access& with_error) { + } catch (const bad_expected_access& with_error) { assert(with_error.error() == -42); - static_assert(is_same_v); } } @@ -1870,7 +1957,7 @@ namespace test_expected { static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); } - { // compare against unexpect with same base + { // compare against unexpected with same base using Base = payload_equality; using Unexpected = std::unexpected; using Expected = expected; @@ -1889,7 +1976,7 @@ namespace test_expected { static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); } - { // expected compare against unexpect with same base + { // expected compare against unexpected with same base using Base = payload_equality; using Unexpected = std::unexpected; using Expected = expected; @@ -1905,7 +1992,7 @@ namespace test_expected { static_assert(noexcept(with_error != Unexpected{42}) == should_be_noexcept); } - { // compare against unexpect with different base + { // compare against unexpected with different base using Base = payload_equality; using Unexpected = std::unexpected; using Expected = expected; @@ -1924,7 +2011,7 @@ namespace test_expected { static_assert(noexcept(with_error != Base{1337}) == should_be_noexcept); } - { // expected compare against unexpect with different base + { // expected compare against unexpected with different base using Base = payload_equality; using Unexpected = std::unexpected; using Expected = expected; @@ -1966,4 +2053,12 @@ int main() { 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*>); } From 86e4f11e765858e3dbd6659b53615d79525f89c9 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 00:39:46 -0700 Subject: [PATCH 19/26] Test: Use typedefs to shorten long lines. --- tests/std/tests/P0323R12_expected/test.cpp | 53 +++++++++------------- 1 file changed, 21 insertions(+), 32 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index abebc24017..7e97769660 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -1168,38 +1168,27 @@ namespace test_expected { } constexpr void test_assignment() noexcept { - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); - test_assignment(); + 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 { From 691621a171b2f2af31674335770eb0a11bcea0f2 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 00:42:40 -0700 Subject: [PATCH 20/26] Rename No to Not. --- tests/std/tests/P0323R12_expected/test.cpp | 112 ++++++++++----------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index 7e97769660..e55989381f 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -11,21 +11,21 @@ using namespace std; -enum class IsDefaultConstructible : bool { No, Yes }; -enum class IsTriviallyCopyConstructible : bool { No, Yes }; -enum class IsTriviallyMoveConstructible : bool { No, Yes }; -enum class IsTriviallyDestructible : bool { No, Yes }; - -enum class IsNothrowConstructible : bool { No, Yes }; -enum class IsNothrowCopyConstructible : bool { No, Yes }; -enum class IsNothrowMoveConstructible : bool { No, Yes }; -enum class IsNothrowCopyAssignable : bool { No, Yes }; -enum class IsNothrowMoveAssignable : bool { No, Yes }; -enum class IsNothrowConvertible : bool { No, Yes }; -enum class IsNothrowComparable : bool { No, Yes }; -enum class IsNothrowSwappable : bool { No, Yes }; - -enum class IsExplicitConstructible : bool { No, Yes }; +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 }; struct convertible { constexpr convertible() = default; @@ -145,14 +145,14 @@ namespace test_unexpected { } constexpr bool test_all() { - test(); - test(); - test(); + test(); + test(); + test(); test(); - test(); - test(); - test(); - test(); + test(); + test(); + test(); + test(); return true; } @@ -406,7 +406,7 @@ namespace test_expected { bool& _destructor_called; }; template <> // TRANSITION, LLVM-46269 - struct payload_destructor { + struct payload_destructor { constexpr payload_destructor(bool& destructor_called) : _destructor_called(destructor_called) {} payload_destructor(const payload_destructor&) = default; constexpr ~payload_destructor() { @@ -454,20 +454,20 @@ namespace test_expected { constexpr void test_special_members() { test_default_constructors(); - test_default_constructors(); + test_default_constructors(); test_copy_constructors(); - test_copy_constructors(); - test_copy_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_move_constructors(); + test_move_constructors(); + test_move_constructors(); test_destructors(); - test_destructors(); + test_destructors(); } template @@ -673,9 +673,9 @@ namespace test_expected { constexpr void test_constructors() noexcept { test_constructors(); - test_constructors(); - test_constructors(); - test_constructors(); + test_constructors(); + test_constructors(); + test_constructors(); } template (); - 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(); + 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 { @@ -1513,9 +1513,9 @@ namespace test_expected { constexpr void test_swap() noexcept { test_swap(); - test_swap(); - test_swap(); - test_swap(); + test_swap(); + test_swap(); + test_swap(); } constexpr void test_access() noexcept { @@ -1846,9 +1846,9 @@ namespace test_expected { constexpr void test_monadic() noexcept { test_monadic(); - test_monadic(); - test_monadic(); - test_monadic(); + test_monadic(); + test_monadic(); + test_monadic(); } template @@ -2019,7 +2019,7 @@ namespace test_expected { constexpr void test_equality() noexcept { test_equality(); - test_equality(); + test_equality(); } constexpr bool test_all() noexcept { From 4f5cc256cf5ffe06f9b2ef2d4d343e5771fefdd6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 00:50:35 -0700 Subject: [PATCH 21/26] Sort Not/Yes patterns, so they're tested in "binary order" consistently. --- tests/std/tests/P0323R12_expected/test.cpp | 68 +++++++++++----------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index e55989381f..c07e3fb0b3 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -145,14 +145,14 @@ namespace test_unexpected { } constexpr bool test_all() { + test(); test(); - test(); + test(); test(); - test(); - test(); test(); - test(); + test(); test(); + test(); return true; } @@ -453,21 +453,21 @@ namespace test_expected { } constexpr void test_special_members() { - test_default_constructors(); test_default_constructors(); + test_default_constructors(); - test_copy_constructors(); - test_copy_constructors(); - test_copy_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_move_constructors(); + test_move_constructors(); + test_move_constructors(); - test_destructors(); test_destructors(); + test_destructors(); } template @@ -672,10 +672,10 @@ namespace test_expected { } constexpr void test_constructors() noexcept { - test_constructors(); + test_constructors(); test_constructors(); test_constructors(); - test_constructors(); + test_constructors(); } template (); - 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(); + 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 { @@ -1512,10 +1512,10 @@ namespace test_expected { } constexpr void test_swap() noexcept { - test_swap(); + test_swap(); test_swap(); test_swap(); - test_swap(); + test_swap(); } constexpr void test_access() noexcept { @@ -1845,10 +1845,10 @@ namespace test_expected { } constexpr void test_monadic() noexcept { - test_monadic(); + test_monadic(); test_monadic(); test_monadic(); - test_monadic(); + test_monadic(); } template @@ -2018,8 +2018,8 @@ namespace test_expected { } constexpr void test_equality() noexcept { - test_equality(); test_equality(); + test_equality(); } constexpr bool test_all() noexcept { From bb06fa1e3c5c136240f99f3847925066c3096b1c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 00:55:25 -0700 Subject: [PATCH 22/26] Add IsYes() to reduce verbosity. --- tests/std/tests/P0323R12_expected/test.cpp | 59 +++++++++++----------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index c07e3fb0b3..c11c7114d0 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -27,6 +27,11 @@ 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) {} @@ -42,9 +47,9 @@ namespace test_unexpected { template constexpr void test() { - constexpr bool copy_construction_is_noexcept = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; - constexpr bool move_construction_is_noexcept = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; - constexpr bool compare_is_noexcept = nothrowComparable == IsNothrowComparable::Yes; + 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) {} @@ -191,7 +196,7 @@ namespace test_expected { template constexpr void test_default_constructors() { - constexpr bool should_be_defaultable = defaultConstructible == IsDefaultConstructible::Yes; + constexpr bool should_be_defaultable = IsYes(defaultConstructible); struct payload_default_constructor { constexpr payload_default_constructor() requires(should_be_defaultable) : _val(42) {} @@ -218,9 +223,8 @@ namespace test_expected { template constexpr void test_copy_constructors() { - constexpr bool should_be_trivial = triviallyCopyConstructible == IsTriviallyCopyConstructible::Yes; - constexpr bool should_be_noexcept = - should_be_trivial || nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; + 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; @@ -310,9 +314,8 @@ namespace test_expected { template constexpr void test_move_constructors() { - constexpr bool should_be_trivial = triviallyMoveConstructible == IsTriviallyMoveConstructible::Yes; - constexpr bool should_be_noexcept = - should_be_trivial || nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; + 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; @@ -417,7 +420,7 @@ namespace test_expected { }; template constexpr void test_destructors() { - constexpr bool is_trivial = triviallyDestructible == IsTriviallyDestructible::Yes; + constexpr bool is_trivial = IsYes(triviallyDestructible); bool destructor_called = false; { // Check payload using Expected = expected, int>; @@ -472,19 +475,19 @@ namespace test_expected { template constexpr void test_constructors() noexcept { - constexpr bool should_be_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; - constexpr bool should_be_explicit = explicitConstructible == IsExplicitConstructible::Yes; + 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(explicitConstructible == IsExplicitConstructible::Yes) + constexpr explicit(IsYes(explicitConstructible)) payload_constructors(const convertible&) noexcept(should_be_noexcept) : _val(3) {} - constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + constexpr explicit(IsYes(explicitConstructible)) payload_constructors(convertible&&) noexcept(should_be_noexcept) : _val(42) {} - constexpr explicit(explicitConstructible == IsExplicitConstructible::Yes) + constexpr explicit(IsYes(explicitConstructible)) payload_constructors(initializer_list&, convertible) noexcept(should_be_noexcept) : _val(1337) {} @@ -681,10 +684,10 @@ namespace test_expected { template constexpr void test_assignment() noexcept { - constexpr bool nothrow_copy_constructible = nothrowCopyConstructible == IsNothrowCopyConstructible::Yes; - constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; - constexpr bool nothrow_copy_assignable = nothrowCopyAssignable == IsNothrowCopyAssignable::Yes; - constexpr bool nothrow_move_assignable = nothrowMoveAssignable == IsNothrowMoveAssignable::Yes; + 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; @@ -1285,12 +1288,10 @@ namespace test_expected { struct payload_swap { constexpr payload_swap(const int val) noexcept : _val(val) {} constexpr payload_swap(const payload_swap&) noexcept = default; - constexpr payload_swap(payload_swap&& other) noexcept( - nothrowMoveConstructible == IsNothrowMoveConstructible::Yes) + 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( - nothrowSwappable == IsNothrowSwappable::Yes) { + constexpr friend void swap(payload_swap& left, payload_swap& right) noexcept(IsYes(nothrowSwappable)) { left._val = exchange(right._val, left._val); } @@ -1303,8 +1304,8 @@ namespace test_expected { template constexpr void test_swap() noexcept { - constexpr bool nothrow_move_constructible = nothrowMoveConstructible == IsNothrowMoveConstructible::Yes; - constexpr bool should_be_noexcept = nothrow_move_constructible && nothrowSwappable == IsNothrowSwappable::Yes; + constexpr bool nothrow_move_constructible = IsYes(nothrowMoveConstructible); + constexpr bool should_be_noexcept = nothrow_move_constructible && IsYes(nothrowSwappable); { // Check payload member using Expected = expected, int>; @@ -1765,8 +1766,8 @@ namespace test_expected { template constexpr void test_monadic() { - constexpr bool construction_is_noexcept = nothrowConstructible == IsNothrowConstructible::Yes; - constexpr bool conversion_is_noexcept = nothrowConvertible == IsNothrowConvertible::Yes; + 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 { @@ -1853,7 +1854,7 @@ namespace test_expected { template constexpr void test_equality() noexcept { - constexpr bool should_be_noexcept = nothrowComparable == IsNothrowComparable::Yes; + constexpr bool should_be_noexcept = IsYes(nothrowComparable); struct payload_equality { constexpr payload_equality(const int val) noexcept : _val(val) {} From 45a39ec2f6a50b3b791dbfeecf99b9ec58afbad1 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 20 May 2022 14:49:24 -0700 Subject: [PATCH 23/26] `expected::error()` is now mandated to be noexcept. Thanks to https://github.com/cplusplus/draft/pull/5381 , this shouldn't be marked as strengthened. --- stl/inc/expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 9ad540dbf2..565001342e 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -973,16 +973,16 @@ public: } } - _NODISCARD constexpr const _Err& error() const& noexcept { // strengthened + _NODISCARD constexpr const _Err& error() const& noexcept { return _Unexpected; } - _NODISCARD constexpr _Err& error() & noexcept { // strengthened + _NODISCARD constexpr _Err& error() & noexcept { return _Unexpected; } - _NODISCARD constexpr const _Err&& error() const&& noexcept { // strengthened + _NODISCARD constexpr const _Err&& error() const&& noexcept { return _STD move(_Unexpected); } - _NODISCARD constexpr _Err&& error() && noexcept { // strengthened + _NODISCARD constexpr _Err&& error() && noexcept { return _STD move(_Unexpected); } From 0796610bf19f3ee78193135841ba7d307bce8237 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 21 May 2022 15:33:55 -0700 Subject: [PATCH 24/26] Work around VSO-1543660. "Standard Library Header Units: ICEs with Assertion failed: IsInClassDefn()" --- .../test.cpp | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) 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 910fb88ec5..017357e983 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -28,7 +28,9 @@ import ; import ; import ; import ; +#if 0 // TRANSITION, VSO-1543660 import ; +#endif // ^^^ no workaround ^^^ import ; import ; import ; @@ -259,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); @@ -292,11 +294,13 @@ int main() { assert(count(execution::par, begin(arr), end(arr), 0) == 4); } +#if 0 // TRANSITION, VSO-1543660 { puts("Testing ."); constexpr expected test{unexpect, 42}; assert(test.error() == 42); } +#endif // ^^^ no workaround ^^^ { puts("Testing ."); @@ -608,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); } { @@ -618,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))); } { @@ -689,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); } { @@ -713,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))); } { @@ -806,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 ^^^ From 2fec1f21db06cd532f933fccc777ccc9580fcbcc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sun, 22 May 2022 21:18:22 -0700 Subject: [PATCH 25/26] Casey's review comments --- stl/inc/expected | 121 ++++++++++++++------- tests/std/tests/P0323R12_expected/test.cpp | 27 +++++ 2 files changed, 111 insertions(+), 37 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index 565001342e..d4774ffe2f 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -38,7 +38,7 @@ struct _Check_unexpected_argument : true_type { "E must not be a specialization of unexpected. (N4910 [expected.un.object.general]/1)"); }; -// [expected.un.object] +// [expected.un.general] template class unexpected { static_assert(_Check_unexpected_argument<_Err>::value); @@ -48,6 +48,15 @@ class unexpected { 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( @@ -60,12 +69,6 @@ public: constexpr explicit unexpected(in_place_t, initializer_list<_Uty> _Ilist, _Args&&... _Vals) noexcept( is_nothrow_constructible_v<_Err, initializer_list<_Uty>&, _Args...>) // strengthened : _Unexpected(_Ilist, _STD forward<_Args>(_Vals)...) {} - - template - requires (!is_same_v, unexpected> && !is_same_v, in_place_t> - && is_constructible_v<_Err, _UError>) - constexpr explicit unexpected(_UError&& _Unex) noexcept(is_nothrow_constructible_v<_Err, _UError>) // strengthened - : _Unexpected(_STD forward<_UError>(_Unex)) {} // clang-format on // [expected.un.obs] @@ -226,22 +229,22 @@ public: // clang-format on template - static constexpr bool _Is_not_convertible = !is_constructible_v<_Ty, expected<_Uty, _UErr>&> // - && !is_constructible_v<_Ty, expected<_Uty, _UErr>> // - && !is_constructible_v<_Ty, const expected<_Uty, _UErr>&> // - && !is_constructible_v<_Ty, const expected<_Uty, _UErr>> // - && !is_convertible_v&, _Ty> // - && !is_convertible_v&&, _Ty> // - && !is_convertible_v&, _Ty> // - && !is_convertible_v&&, _Ty> // - && !is_constructible_v, expected<_Uty, _UErr>&> // - && !is_constructible_v, expected<_Uty, _UErr>> // - && !is_constructible_v, const expected<_Uty, _UErr>&> // - && !is_constructible_v, const expected<_Uty, _UErr>>; + 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&> // - && _Is_not_convertible<_Uty, _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 @@ -254,7 +257,7 @@ public: } template - requires is_constructible_v<_Ty, _Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + 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 @@ -333,7 +336,6 @@ public: ~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> @@ -350,7 +352,7 @@ public: template static constexpr void _Reinit_expected(_First& _New_val, _Second& _Old_val, _Args&&... _Vals) noexcept( - is_nothrow_constructible_v<_First, _Args...> || is_nothrow_move_constructible_v<_First>) // strengthened + is_nothrow_constructible_v<_First, _Args...>) // strengthened { if constexpr (is_nothrow_constructible_v<_First, _Args...>) { _Old_val.~_Second(); @@ -561,22 +563,40 @@ public: // [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); } @@ -617,15 +637,27 @@ public: } _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); } @@ -658,7 +690,6 @@ public: } } - // [expected.object.eq] // clang-format off @@ -679,9 +710,9 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Value == _Right))) { // strengthened + noexcept(static_cast(_Left._Value == _Right))) { // strengthened if (_Left._Has_value) { - return _Left._Value == _Right; + return static_cast(_Left._Value == _Right); } else { return false; } @@ -689,11 +720,11 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value) { return false; } else { - return _Left._Unexpected == _Right.error(); + return static_cast(_Left._Unexpected == _Right.error()); } } @@ -760,13 +791,13 @@ public: // clang-format on template - static constexpr bool _Is_not_convertible = !is_constructible_v, expected<_Uty, _UErr>&> // - && !is_constructible_v, expected<_Uty, _UErr>> // - && !is_constructible_v, const expected<_Uty, _UErr>&> // - && !is_constructible_v, const expected<_Uty, _UErr>>; + 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&> && _Is_not_convertible<_Uty, _UErr> + 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) { @@ -776,7 +807,7 @@ public: } template - requires is_void_v<_Uty> && is_constructible_v<_Err, _UErr> && _Is_not_convertible<_Uty, _UErr> + 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) { @@ -960,7 +991,11 @@ public: return _Has_value; } - constexpr void operator*() const noexcept {} + 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) { @@ -974,15 +1009,27 @@ public: } _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); } @@ -991,7 +1038,7 @@ public: 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._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened // clang-format on if (_Left._Has_value != _Right.has_value()) { return false; @@ -1002,7 +1049,7 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( - noexcept(_Implicitly_convert_to(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened if (_Left._Has_value) { return false; } else { diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index c11c7114d0..9ccf3753a5 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#define _CONTAINER_DEBUG_LEVEL 1 + #include #include #include @@ -2038,6 +2040,29 @@ namespace test_expected { } } // 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()); @@ -2051,4 +2076,6 @@ int main() { static_assert(is_convertible_v*, bad_expected_access*>); static_assert(is_convertible_v*, exception*>); static_assert(is_convertible_v*, exception*>); + + test_reinit_regression(); } From bb261ad619258ac1887c8041b309a32ddd8c964f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 23 May 2022 12:44:52 -0700 Subject: [PATCH 26/26] Guard `_Old_val.~_Second()`, enable header unit test internally. --- stl/inc/expected | 12 +++++++++--- .../P1502R1_standard_library_header_units/test.cpp | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/stl/inc/expected b/stl/inc/expected index d4774ffe2f..00164414f4 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -355,15 +355,21 @@ public: is_nothrow_constructible_v<_First, _Args...>) // strengthened { if constexpr (is_nothrow_constructible_v<_First, _Args...>) { - _Old_val.~_Second(); + 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)...); - _Old_val.~_Second(); + 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)); - _Old_val.~_Second(); + 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)...); 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 017357e983..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,7 +28,7 @@ import ; import ; import ; import ; -#if 0 // TRANSITION, VSO-1543660 +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VSO-1543660 fixed in 17.3 Preview 3 import ; #endif // ^^^ no workaround ^^^ import ; @@ -294,7 +294,7 @@ int main() { assert(count(execution::par, begin(arr), end(arr), 0) == 4); } -#if 0 // TRANSITION, VSO-1543660 +#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VSO-1543660 fixed in 17.3 Preview 3 { puts("Testing ."); constexpr expected test{unexpect, 42};