diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -100,7 +100,7 @@ "`P1478R8 `__","LWG", "``Byte-wise`` ``atomic`` ``memcpy``", "November 2022","","","|concurrency TS|" "`P2167R3 `__","LWG", "Improved Proposed Wording for LWG 2114", "November 2022","","","" "`P2396R1 `__","LWG", "Concurrency TS 2 fixes ", "November 2022","","","|concurrency TS|" -"`P2505R5 `__","LWG", "Monadic Functions for ``std::expected``", "November 2022","","","" +"`P2505R5 `__","LWG", "Monadic Functions for ``std::expected``", "November 2022","July 2022","|Complete|","16.0" "`P2539R4 `__","LWG", "Should the output of ``std::print`` to a terminal be synchronized with the underlying stream?", "November 2022","","","|format|" "`P2602R2 `__","LWG", "Poison Pills are Too Toxic", "November 2022","","","|ranges|" "`P2708R1 `__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","","" diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -18,6 +18,7 @@ #include <__memory/construct_at.h> #include <__type_traits/conjunction.h> #include <__type_traits/disjunction.h> +#include <__type_traits/integral_constant.h> #include <__type_traits/is_assignable.h> #include <__type_traits/is_constructible.h> #include <__type_traits/is_convertible.h> @@ -49,6 +50,7 @@ #include <__utility/move.h> #include <__utility/swap.h> #include <__utility/transaction.h> +#include <__functional/invoke.h> #include // for std::abort #include @@ -60,18 +62,27 @@ _LIBCPP_BEGIN_NAMESPACE_STD +template class expected; + namespace __expected { template _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) { # ifndef _LIBCPP_NO_EXCEPTIONS - throw bad_expected_access<_Err>(std::forward<_Arg>(__arg)); + throw bad_expected_access<_Err>(_VSTD::forward<_Arg>(__arg)); # else (void)__arg; - std::abort(); + _VSTD::abort(); # endif } +template +struct __is_expected : false_type {}; +template +struct __is_expected> : true_type {}; + +struct __expected_construct_in_place_from_invoke_tag {}; +struct __expected_construct_unexpected_from_invoke_tag {}; } // namespace __expected template @@ -102,12 +113,9 @@ using rebind = expected<_Up, error_type>; // [expected.object.ctor], constructors - _LIBCPP_HIDE_FROM_ABI constexpr expected() - noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened + _LIBCPP_HIDE_FROM_ABI constexpr expected() noexcept(is_nothrow_default_constructible_v<_Tp>) // strengthened requires is_default_constructible_v<_Tp> - : __has_val_(true) { - std::construct_at(std::addressof(__union_.__val_)); - } + : __val_(), __has_val_(true) {} _LIBCPP_HIDE_FROM_ABI constexpr expected(const expected&) = delete; @@ -124,9 +132,9 @@ !(is_trivially_copy_constructible_v<_Tp> && is_trivially_copy_constructible_v<_Err>)) : __has_val_(__other.__has_val_) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_); + _VSTD::construct_at(_VSTD::addressof(__val_), __other.__val_); } else { - std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_); + _VSTD::construct_at(_VSTD::addressof(__unex_), __other.__unex_); } } @@ -142,9 +150,9 @@ !(is_trivially_move_constructible_v<_Tp> && is_trivially_move_constructible_v<_Err>)) : __has_val_(__other.__has_val_) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_)); + _VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::move(__other.__val_)); } else { - std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__other.__unex_)); } } @@ -177,9 +185,9 @@ is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened : __has_val_(__other.__has_val_) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__val_), __other.__union_.__val_); + _VSTD::construct_at(_VSTD::addressof(__val_), __other.__val_); } else { - std::construct_at(std::addressof(__union_.__unex_), __other.__union_.__unex_); + _VSTD::construct_at(_VSTD::addressof(__unex_), __other.__unex_); } } @@ -190,9 +198,9 @@ noexcept(is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened : __has_val_(__other.__has_val_) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__val_), std::move(__other.__union_.__val_)); + _VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::move(__other.__val_)); } else { - std::construct_at(std::addressof(__union_.__unex_), std::move(__other.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__other.__unex_)); } } @@ -203,7 +211,7 @@ expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened : __has_val_(true) { - std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u)); + _VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::forward<_Up>(__u)); } @@ -213,7 +221,7 @@ expected(const unexpected<_OtherErr>& __unex) noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), __unex.error()); + _VSTD::construct_at(_VSTD::addressof(__unex_), __unex.error()); } template @@ -222,7 +230,7 @@ expected(unexpected<_OtherErr>&& __unex) noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error())); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__unex.error())); } template @@ -230,7 +238,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, _Args...>) // strengthened : __has_val_(true) { - std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::forward<_Args>(__args)...); } template @@ -239,7 +247,7 @@ expected(in_place_t, initializer_list<_Up> __il, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, initializer_list<_Up>&, _Args...>) // strengthened : __has_val_(true) { - std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__val_), __il, _VSTD::forward<_Args>(__args)...); } template @@ -247,7 +255,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::forward<_Args>(__args)...); } template @@ -256,7 +264,7 @@ expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__unex_), __il, _VSTD::forward<_Args>(__args)...); } // [expected.object.dtor], destructor @@ -269,9 +277,9 @@ requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>) { if (__has_val_) { - std::destroy_at(std::addressof(__union_.__val_)); + _VSTD::destroy_at(_VSTD::addressof(__val_)); } else { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); } } @@ -279,25 +287,34 @@ template _LIBCPP_HIDE_FROM_ABI static constexpr void __reinit_expected(_T1& __newval, _T2& __oldval, _Args&&... __args) { if constexpr (is_nothrow_constructible_v<_T1, _Args...>) { - std::destroy_at(std::addressof(__oldval)); - std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...); + _VSTD::destroy_at(_VSTD::addressof(__oldval)); + _VSTD::construct_at(_VSTD::addressof(__newval), _VSTD::forward<_Args>(__args)...); } else if constexpr (is_nothrow_move_constructible_v<_T1>) { - _T1 __tmp(std::forward<_Args>(__args)...); - std::destroy_at(std::addressof(__oldval)); - std::construct_at(std::addressof(__newval), std::move(__tmp)); + _T1 __tmp(_VSTD::forward<_Args>(__args)...); + _VSTD::destroy_at(_VSTD::addressof(__oldval)); + _VSTD::construct_at(_VSTD::addressof(__newval), _VSTD::move(__tmp)); } else { static_assert( is_nothrow_move_constructible_v<_T2>, "To provide strong exception guarantee, T2 has to satisfy `is_nothrow_move_constructible_v` so that it can " "be reverted to the previous state in case an exception is thrown during the assignment."); - _T2 __tmp(std::move(__oldval)); - std::destroy_at(std::addressof(__oldval)); - __transaction __trans([&] { std::construct_at(std::addressof(__oldval), std::move(__tmp)); }); - std::construct_at(std::addressof(__newval), std::forward<_Args>(__args)...); + _T2 __tmp(_VSTD::move(__oldval)); + _VSTD::destroy_at(_VSTD::addressof(__oldval)); + __transaction __trans([&] { _VSTD::construct_at(_VSTD::addressof(__oldval), _VSTD::move(__tmp)); }); + _VSTD::construct_at(_VSTD::addressof(__newval), _VSTD::forward<_Args>(__args)...); __trans.__complete(); } } + template + constexpr explicit expected(__expected::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __val_(_VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::forward<_Args>(__args)...)), __has_val_(true) {} + + template + constexpr explicit expected( + __expected::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(_VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::forward<_Args>(__args)...)), __has_val_(false) {} + public: // [expected.object.assign], assignment _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected&) = delete; @@ -315,13 +332,13 @@ is_nothrow_move_constructible_v<_Err>)) { if (__has_val_ && __rhs.__has_val_) { - __union_.__val_ = __rhs.__union_.__val_; + __val_ = __rhs.__val_; } else if (__has_val_) { - __reinit_expected(__union_.__unex_, __union_.__val_, __rhs.__union_.__unex_); + __reinit_expected(__unex_, __val_, __rhs.__unex_); } else if (__rhs.__has_val_) { - __reinit_expected(__union_.__val_, __union_.__unex_, __rhs.__union_.__val_); + __reinit_expected(__val_, __unex_, __rhs.__val_); } else { - __union_.__unex_ = __rhs.__union_.__unex_; + __unex_ = __rhs.__unex_; } // note: only reached if no exception+rollback was done inside __reinit_expected __has_val_ = __rhs.__has_val_; @@ -341,13 +358,13 @@ is_nothrow_move_constructible_v<_Err>)) { if (__has_val_ && __rhs.__has_val_) { - __union_.__val_ = std::move(__rhs.__union_.__val_); + __val_ = _VSTD::move(__rhs.__val_); } else if (__has_val_) { - __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__rhs.__union_.__unex_)); + __reinit_expected(__unex_, __val_, _VSTD::move(__rhs.__unex_)); } else if (__rhs.__has_val_) { - __reinit_expected(__union_.__val_, __union_.__unex_, std::move(__rhs.__union_.__val_)); + __reinit_expected(__val_, __unex_, _VSTD::move(__rhs.__val_)); } else { - __union_.__unex_ = std::move(__rhs.__union_.__unex_); + __unex_ = _VSTD::move(__rhs.__unex_); } // note: only reached if no exception+rollback was done inside __reinit_expected __has_val_ = __rhs.__has_val_; @@ -365,9 +382,9 @@ is_nothrow_move_constructible_v<_Err>)) { if (__has_val_) { - __union_.__val_ = std::forward<_Up>(__v); + __val_ = _VSTD::forward<_Up>(__v); } else { - __reinit_expected(__union_.__val_, __union_.__unex_, std::forward<_Up>(__v)); + __reinit_expected(__val_, __unex_, _VSTD::forward<_Up>(__v)); __has_val_ = true; } return *this; @@ -388,10 +405,10 @@ requires(__can_assign_from_unexpected) _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) { if (__has_val_) { - __reinit_expected(__union_.__unex_, __union_.__val_, __un.error()); + __reinit_expected(__unex_, __val_, __un.error()); __has_val_ = false; } else { - __union_.__unex_ = __un.error(); + __unex_ = __un.error(); } return *this; } @@ -400,10 +417,10 @@ requires(__can_assign_from_unexpected<_OtherErr>) _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) { if (__has_val_) { - __reinit_expected(__union_.__unex_, __union_.__val_, std::move(__un.error())); + __reinit_expected(__unex_, __val_, _VSTD::move(__un.error())); __has_val_ = false; } else { - __union_.__unex_ = std::move(__un.error()); + __unex_ = _VSTD::move(__un.error()); } return *this; } @@ -412,24 +429,24 @@ requires is_nothrow_constructible_v<_Tp, _Args...> _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(_Args&&... __args) noexcept { if (__has_val_) { - std::destroy_at(std::addressof(__union_.__val_)); + _VSTD::destroy_at(_VSTD::addressof(__val_)); } else { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); __has_val_ = true; } - return *std::construct_at(std::addressof(__union_.__val_), std::forward<_Args>(__args)...); + return *_VSTD::construct_at(_VSTD::addressof(__val_), _VSTD::forward<_Args>(__args)...); } template requires is_nothrow_constructible_v< _Tp, initializer_list<_Up>&, _Args... > _LIBCPP_HIDE_FROM_ABI constexpr _Tp& emplace(initializer_list<_Up> __il, _Args&&... __args) noexcept { if (__has_val_) { - std::destroy_at(std::addressof(__union_.__val_)); + _VSTD::destroy_at(_VSTD::addressof(__val_)); } else { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); __has_val_ = true; } - return *std::construct_at(std::addressof(__union_.__val_), __il, std::forward<_Args>(__args)...); + return *_VSTD::construct_at(_VSTD::addressof(__val_), __il, _VSTD::forward<_Args>(__args)...); } @@ -449,26 +466,26 @@ { auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) { if constexpr (is_nothrow_move_constructible_v<_Err>) { - _Err __tmp(std::move(__with_err.__union_.__unex_)); - std::destroy_at(std::addressof(__with_err.__union_.__unex_)); + _Err __tmp(_VSTD::move(__with_err.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__with_err.__unex_)); __transaction __trans([&] { - std::construct_at(std::addressof(__with_err.__union_.__unex_), std::move(__tmp)); + _VSTD::construct_at(_VSTD::addressof(__with_err.__unex_), _VSTD::move(__tmp)); }); - std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__with_val.__union_.__val_)); + _VSTD::construct_at(_VSTD::addressof(__with_err.__val_), _VSTD::move(__with_val.__val_)); __trans.__complete(); - std::destroy_at(std::addressof(__with_val.__union_.__val_)); - std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__tmp)); + _VSTD::destroy_at(_VSTD::addressof(__with_val.__val_)); + _VSTD::construct_at(_VSTD::addressof(__with_val.__unex_), _VSTD::move(__tmp)); } else { static_assert(is_nothrow_move_constructible_v<_Tp>, "To provide strong exception guarantee, Tp has to satisfy `is_nothrow_move_constructible_v` so " "that it can be reverted to the previous state in case an exception is thrown during swap."); - _Tp __tmp(std::move(__with_val.__union_.__val_)); - std::destroy_at(std::addressof(__with_val.__union_.__val_)); - __transaction __trans([&] { std::construct_at(std::addressof(__with_val.__union_.__val_), std::move(__tmp)); }); - std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_)); + _Tp __tmp(_VSTD::move(__with_val.__val_)); + _VSTD::destroy_at(_VSTD::addressof(__with_val.__val_)); + __transaction __trans([&] { _VSTD::construct_at(_VSTD::addressof(__with_val.__val_), _VSTD::move(__tmp)); }); + _VSTD::construct_at(_VSTD::addressof(__with_val.__unex_), _VSTD::move(__with_err.__unex_)); __trans.__complete(); - std::destroy_at(std::addressof(__with_err.__union_.__unex_)); - std::construct_at(std::addressof(__with_err.__union_.__val_), std::move(__tmp)); + _VSTD::destroy_at(_VSTD::addressof(__with_err.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__with_err.__val_), _VSTD::move(__tmp)); } __with_val.__has_val_ = false; __with_err.__has_val_ = true; @@ -476,8 +493,8 @@ if (__has_val_) { if (__rhs.__has_val_) { - using std::swap; - swap(__union_.__val_, __rhs.__union_.__val_); + using _VSTD::swap; + swap(__val_, __rhs.__val_); } else { __swap_val_unex_impl(*this, __rhs); } @@ -485,8 +502,8 @@ if (__rhs.__has_val_) { __swap_val_unex_impl(__rhs, *this); } else { - using std::swap; - swap(__union_.__unex_, __rhs.__union_.__unex_); + using _VSTD::swap; + swap(__unex_, __rhs.__unex_); } } } @@ -501,32 +518,32 @@ // [expected.object.obs], observers _LIBCPP_HIDE_FROM_ABI constexpr const _Tp* operator->() const noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator-> requires the expected to contain a value"); - return std::addressof(__union_.__val_); + return _VSTD::addressof(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr _Tp* operator->() noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator-> requires the expected to contain a value"); - return std::addressof(__union_.__val_); + return _VSTD::addressof(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator* requires the expected to contain a value"); - return __union_.__val_; + return __val_; } _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator* requires the expected to contain a value"); - return __union_.__val_; + return __val_; } _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator* requires the expected to contain a value"); - return std::move(__union_.__val_); + return _VSTD::move(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept { _LIBCPP_ASSERT(__has_val_, "expected::operator* requires the expected to contain a value"); - return std::move(__union_.__val_); + return _VSTD::move(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr explicit operator bool() const noexcept { return __has_val_; } @@ -535,64 +552,289 @@ _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& value() const& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + __expected::__throw_bad_expected_access<_Err>(__unex_); } - return __union_.__val_; + return __val_; } _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + __expected::__throw_bad_expected_access<_Err>(__unex_); } - return __union_.__val_; + return __val_; } _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + __expected::__throw_bad_expected_access<_Err>(_VSTD::move(__unex_)); } - return std::move(__union_.__val_); + return _VSTD::move(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + __expected::__throw_bad_expected_access<_Err>(_VSTD::move(__unex_)); } - return std::move(__union_.__val_); + return _VSTD::move(__val_); } _LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return __union_.__unex_; + return __unex_; } _LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return __union_.__unex_; + return __unex_; } _LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return std::move(__union_.__unex_); + return _VSTD::move(__unex_); } _LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return std::move(__union_.__unex_); + return _VSTD::move(__unex_); } template _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& { static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible"); static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type"); - return __has_val_ ? __union_.__val_ : static_cast<_Tp>(std::forward<_Up>(__v)); + return __has_val_ ? __val_ : static_cast<_Tp>(_VSTD::forward<_Up>(__v)); } template _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible"); static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type"); - return __has_val_ ? std::move(__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v)); + return __has_val_ ? _VSTD::move(__val_) : static_cast<_Tp>(_VSTD::forward<_Up>(__v)); + } + + template + requires(is_copy_constructible_v<_Err> && is_convertible_v<_Up, _Err>) + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __v) const& { + return has_value() ? _VSTD::forward<_Up>(__v) : error(); + } + + template + requires(is_move_constructible_v<_Err> && is_convertible_v<_Up, _Err>) + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __v) && { + return has_value() ? _VSTD::forward<_Up>(__v) : _VSTD::move(error()); + } + + // [expected.void.monadic], monadic + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(value()) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + } + return _Up(unexpect, error()); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(value()) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + } + return _Up(unexpect, error()); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(value())) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + } + return _Up(unexpect, _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(value())) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + } + return _Up(unexpect, _VSTD::move(error())); + } + + template + requires is_copy_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(error()) should be the same type as this expected"); + if (has_value()) { + return _Up(in_place, value()); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), error()); + } + + template + requires is_copy_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(error()) should be the same type as this expected"); + if (has_value()) { + return _Up(in_place, value()); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), error()); + } + + template + requires is_move_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(error())) should be the same type as this expected"); + if (has_value()) { + return _Up(in_place, _VSTD::move(value())); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(error())) should be the same type as this expected"); + if (has_value()) { + return _Up(in_place, _VSTD::move(value())); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(error())); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, error()); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f), value()); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f), value()); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, error()); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(value())); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(value())); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(value())); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, _VSTD::move(error())); + } + + template + requires is_copy_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) & { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(in_place, value()); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, _VSTD::forward<_Func>(__f), error()); + } + + template + requires is_copy_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(in_place, value()); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, _VSTD::forward<_Func>(__f), error()); + } + + template + requires is_move_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) && { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(in_place, _VSTD::move(value())); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Tp> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(in_place, _VSTD::move(value())); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(error())); } // [expected.object.eq], equality operators @@ -603,42 +845,30 @@ return false; } else { if (__x.__has_val_) { - return __x.__union_.__val_ == __y.__union_.__val_; + return __x.__val_ == __y.__val_; } else { - return __x.__union_.__unex_ == __y.__union_.__unex_; + return __x.__unex_ == __y.__unex_; } } } template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) { - return __x.__has_val_ && static_cast(__x.__union_.__val_ == __v); + return __x.__has_val_ && static_cast(__x.__val_ == __v); } template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) { - return !__x.__has_val_ && static_cast(__x.__union_.__unex_ == __e.error()); + return !__x.__has_val_ && static_cast(__x.__unex_ == __e.error()); } private: struct __empty_t {}; - // use named union because [[no_unique_address]] cannot be applied to an unnamed union - _LIBCPP_NO_UNIQUE_ADDRESS union __union_t { - _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {} - - _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>) - = default; - - // the expected's destructor handles this - _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(!is_trivially_destructible_v<_Tp> || !is_trivially_destructible_v<_Err>) - {} - - _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_; - _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_; - _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_; - } __union_; + union { + __empty_t __empty_; + _Tp __val_; + _Err __unex_; + }; bool __has_val_; }; @@ -684,7 +914,7 @@ requires(is_copy_constructible_v<_Err> && !is_trivially_copy_constructible_v<_Err>) : __has_val_(__rhs.__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_); + _VSTD::construct_at(_VSTD::addressof(__unex_), __rhs.__unex_); } } @@ -697,7 +927,7 @@ requires(is_move_constructible_v<_Err> && !is_trivially_move_constructible_v<_Err>) : __has_val_(__rhs.__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__rhs.__unex_)); } } @@ -708,7 +938,7 @@ noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened : __has_val_(__rhs.__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_); + _VSTD::construct_at(_VSTD::addressof(__unex_), __rhs.__unex_); } } @@ -719,7 +949,7 @@ noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened : __has_val_(__rhs.__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__rhs.__unex_)); } } @@ -729,7 +959,7 @@ expected(const unexpected<_OtherErr>& __unex) noexcept(is_nothrow_constructible_v<_Err, const _OtherErr&>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), __unex.error()); + _VSTD::construct_at(_VSTD::addressof(__unex_), __unex.error()); } template @@ -738,7 +968,7 @@ expected(unexpected<_OtherErr>&& __unex) noexcept(is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__unex.error())); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__unex.error())); } _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(in_place_t) noexcept : __has_val_(true) {} @@ -748,7 +978,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Err, _Args...>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::forward<_Args>(__args)...); } template @@ -756,7 +986,7 @@ _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(unexpect_t, initializer_list<_Up> __il, _Args&&... __args) noexcept(is_nothrow_constructible_v<_Err, initializer_list<_Up>&, _Args...>) // strengthened : __has_val_(false) { - std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...); + _VSTD::construct_at(_VSTD::addressof(__unex_), __il, _VSTD::forward<_Args>(__args)...); } // [expected.void.dtor], destructor @@ -769,10 +999,23 @@ requires(!is_trivially_destructible_v<_Err>) { if (!__has_val_) { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); } } +private: + template + constexpr explicit expected(__expected::__expected_construct_in_place_from_invoke_tag, _Func&& __f) + : __has_val_(true) { + _VSTD::forward<_Func>(__f)(); + } + + template + constexpr explicit expected( + __expected::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(_VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::forward<_Args>(__args)...)), __has_val_(false) {} + +public: // [expected.void.assign], assignment _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const expected&) = delete; @@ -783,15 +1026,15 @@ { if (__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), __rhs.__union_.__unex_); + _VSTD::construct_at(_VSTD::addressof(__unex_), __rhs.__unex_); __has_val_ = false; } } else { if (__rhs.__has_val_) { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); __has_val_ = true; } else { - __union_.__unex_ = __rhs.__union_.__unex_; + __unex_ = __rhs.__unex_; } } return *this; @@ -807,15 +1050,15 @@ { if (__has_val_) { if (!__rhs.__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__rhs.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__rhs.__unex_)); __has_val_ = false; } } else { if (__rhs.__has_val_) { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); __has_val_ = true; } else { - __union_.__unex_ = std::move(__rhs.__union_.__unex_); + __unex_ = _VSTD::move(__rhs.__unex_); } } return *this; @@ -825,10 +1068,10 @@ requires(is_constructible_v<_Err, const _OtherErr&> && is_assignable_v<_Err&, const _OtherErr&>) _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(const unexpected<_OtherErr>& __un) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), __un.error()); + _VSTD::construct_at(_VSTD::addressof(__unex_), __un.error()); __has_val_ = false; } else { - __union_.__unex_ = __un.error(); + __unex_ = __un.error(); } return *this; } @@ -837,17 +1080,17 @@ requires(is_constructible_v<_Err, _OtherErr> && is_assignable_v<_Err&, _OtherErr>) _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(unexpected<_OtherErr>&& __un) { if (__has_val_) { - std::construct_at(std::addressof(__union_.__unex_), std::move(__un.error())); + _VSTD::construct_at(_VSTD::addressof(__unex_), _VSTD::move(__un.error())); __has_val_ = false; } else { - __union_.__unex_ = std::move(__un.error()); + __unex_ = _VSTD::move(__un.error()); } return *this; } _LIBCPP_HIDE_FROM_ABI constexpr void emplace() noexcept { if (!__has_val_) { - std::destroy_at(std::addressof(__union_.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__unex_)); __has_val_ = true; } } @@ -858,8 +1101,8 @@ requires(is_swappable_v<_Err> && is_move_constructible_v<_Err>) { auto __swap_val_unex_impl = [&](expected& __with_val, expected& __with_err) { - std::construct_at(std::addressof(__with_val.__union_.__unex_), std::move(__with_err.__union_.__unex_)); - std::destroy_at(std::addressof(__with_err.__union_.__unex_)); + _VSTD::construct_at(_VSTD::addressof(__with_val.__unex_), _VSTD::move(__with_err.__unex_)); + _VSTD::destroy_at(_VSTD::addressof(__with_err.__unex_)); __with_val.__has_val_ = false; __with_err.__has_val_ = true; }; @@ -872,8 +1115,8 @@ if (__rhs.__has_val_) { __swap_val_unex_impl(__rhs, *this); } else { - using std::swap; - swap(__union_.__unex_, __rhs.__union_.__unex_); + using _VSTD::swap; + swap(__unex_, __rhs.__unex_); } } } @@ -896,34 +1139,247 @@ _LIBCPP_HIDE_FROM_ABI constexpr void value() const& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + __expected::__throw_bad_expected_access<_Err>(__unex_); } } _LIBCPP_HIDE_FROM_ABI constexpr void value() && { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + __expected::__throw_bad_expected_access<_Err>(_VSTD::move(__unex_)); } } _LIBCPP_HIDE_FROM_ABI constexpr const _Err& error() const& noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return __union_.__unex_; + return __unex_; } _LIBCPP_HIDE_FROM_ABI constexpr _Err& error() & noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return __union_.__unex_; + return __unex_; } _LIBCPP_HIDE_FROM_ABI constexpr const _Err&& error() const&& noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return std::move(__union_.__unex_); + return _VSTD::move(__unex_); } _LIBCPP_HIDE_FROM_ABI constexpr _Err&& error() && noexcept { _LIBCPP_ASSERT(!__has_val_, "expected::error requires the expected to contain an error"); - return std::move(__union_.__unex_); + return _VSTD::move(__unex_); + } + + template + requires(is_copy_constructible_v<_Err> && is_convertible_v<_Up, _Err>) + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __v) const& { + return has_value() ? _VSTD::forward<_Up>(__v) : error(); + } + + template + requires(is_move_constructible_v<_Err> && is_convertible_v<_Up, _Err>) + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __v) && { + return has_value() ? _VSTD::forward<_Up>(__v) : _VSTD::move(error()); + } + + // [expected.void.monadic], monadic + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(value()) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f)); + } + return _Up(unexpect, error()); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(value()) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f)); + } + return _Up(unexpect, error()); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(value())) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f)); + } + return _Up(unexpect, _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(value())) must be a specialization of std::expected"); + if (has_value()) { + return _VSTD::invoke(_VSTD::forward<_Func>(__f)); + } + return _Up(unexpect, _VSTD::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(error()) should be the same type as this expected"); + if (has_value()) { + return _Up(); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(error()) should be the same type as this expected"); + if (has_value()) { + return _Up(); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(error())) should be the same type as this expected"); + if (has_value()) { + return _Up(); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(__expected::__is_expected<_Up>::value && is_same_v, + "Result of f(std::move(error())) should be the same type as this expected"); + if (has_value()) { + return _Up(); + } + return _VSTD::invoke(_VSTD::forward<_Func>(__f), _VSTD::move(error())); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f)); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, error()); + } + + template + requires is_copy_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f)); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, error()); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f)); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, _VSTD::move(error())); + } + + template + requires is_move_constructible_v<_Err> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (has_value()) { + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected::__expected_construct_in_place_from_invoke_tag{}, _VSTD::forward<_Func>(__f)); + } else { + _VSTD::invoke(_VSTD::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + return expected<_Up, _Err>(unexpect, _VSTD::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) & { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, _VSTD::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, _VSTD::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) && { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (has_value()) { + return expected<_Tp, _Up>(in_place); + } + return expected<_Tp, _Up>( + __expected::__expected_construct_unexpected_from_invoke_tag{}, + _VSTD::forward<_Func>(__f), + _VSTD::move(error())); } // [expected.void.eq], equality operators @@ -933,33 +1389,21 @@ if (__x.__has_val_ != __y.__has_val_) { return false; } else { - return __x.__has_val_ || static_cast(__x.__union_.__unex_ == __y.__union_.__unex_); + return __x.__has_val_ || static_cast(__x.__unex_ == __y.__unex_); } } template _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) { - return !__x.__has_val_ && static_cast(__x.__union_.__unex_ == __y.error()); + return !__x.__has_val_ && static_cast(__x.__unex_ == __y.error()); } private: struct __empty_t {}; - // use named union because [[no_unique_address]] cannot be applied to an unnamed union - _LIBCPP_NO_UNIQUE_ADDRESS union __union_t { - _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {} - - _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(is_trivially_destructible_v<_Err>) - = default; - - // the expected's destructor handles this - _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(!is_trivially_destructible_v<_Err>) - {} - - _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_; - _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_; - } __union_; + union { + __empty_t __empty_; + _Err __unex_; + }; bool __has_val_; }; diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/expected/expected.expected/no_unique_address.compile.pass.cpp +++ /dev/null @@ -1,19 +0,0 @@ -//===----------------------------------------------------------------------===// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 - -// clang-cl and cl currently don't support [[no_unique_address]] -// XFAIL: msvc - -// test [[no_unique_address]] is applied to the union - -#include - -struct Empty {}; - -static_assert(sizeof(std::expected) == sizeof(bool)); diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp deleted file mode 100644 --- a/libcxx/test/libcxx/utilities/expected/expected.void/no_unique_address.compile.pass.cpp +++ /dev/null @@ -1,19 +0,0 @@ -//===----------------------------------------------------------------------===// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 - -// clang-cl and cl currently don't support [[no_unique_address]] -// XFAIL: msvc - -// test [[no_unique_address]] is applied to the union - -#include - -struct Empty {}; - -static_assert(sizeof(std::expected) == sizeof(bool)); diff --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/and_then.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/and_then.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/and_then.pass.cpp @@ -0,0 +1,266 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto and_then(F&& f) &; +// template constexpr auto and_then(F&& f) const &; +// template constexpr auto and_then(F&& f) &&; +// template constexpr auto and_then(F&& f) const &&; + +#include +#include + + +struct LVal { + constexpr std::expected operator()(int&) { return 1; } + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct CLVal { + std::expected operator()(int&) = delete; + constexpr std::expected operator()(const int&) { return 1; } + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct RVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + constexpr std::expected operator()(int&&) { return 1; } + std::expected operator()(const int&&) = delete; +}; + +struct CRVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + constexpr std::expected operator()(const int&&) { return 1; } +}; + +struct RefQual { + constexpr std::expected operator()(int) & { return 1; } + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct CRefQual { + std::expected operator()(int) & = delete; + constexpr std::expected operator()(int) const& { return 1; } + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct RVRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + constexpr std::expected operator()(int) && { return 1; } + std::expected operator()(int) const&& = delete; +}; + +struct RVCRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + constexpr std::expected operator()(int) const&& { return 1; } +}; + +struct NOLVal { + constexpr std::expected operator()(int&) { return std::expected(std::unexpected(5)); } + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct NOCLVal { + std::expected operator()(int&) = delete; + constexpr std::expected operator()(const int&) { return std::expected(std::unexpected(5)); } + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct NORVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + constexpr std::expected operator()(int&&) { return std::expected(std::unexpected(5)); } + std::expected operator()(const int&&) = delete; +}; + +struct NOCRVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + constexpr std::expected operator()(const int&&) { return std::expected(std::unexpected(5)); } +}; + +struct NORefQual { + constexpr std::expected operator()(int) & { return std::expected(std::unexpected(5)); } + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct NOCRefQual { + std::expected operator()(int) & = delete; + constexpr std::expected operator()(int) const& { return std::expected(std::unexpected(5)); } + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct NORVRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + constexpr std::expected operator()(int) && { return std::expected(std::unexpected(5)); } + std::expected operator()(int) const&& = delete; +}; + +struct NORVCRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + constexpr std::expected operator()(int) const&& { return std::expected(std::unexpected(5)); } +}; + +struct NoCopy { + NoCopy() = default; + NoCopy(const NoCopy&) { assert(false); } + std::expected operator()(const NoCopy&&) { return 1; } +}; + +struct NonConst { + std::expected non_const() { return 1; } +}; + +constexpr void test_val_types() { + // Test & overload + { + // Without &qualifier on F'soperator() + { + std::expected e{0}; + assert(e.and_then(LVal{}).value() == 1); + assert(e.and_then(NOLVal{}).error() == 5); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + std::expected e{0}; + RefQual l{}; + assert(e.and_then(l) == 1); + NORefQual nl{}; + assert(e.and_then(nl).error() == 5); + static_assert(std::is_same_v>); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::expected e{0}; + assert(e.and_then(CLVal{}).value() == 1); + assert(e.and_then(NOCLVal{}).error() == 5); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + const std::expected e{0}; + const CRefQual l{}; + assert(e.and_then(l) == 1); + const NOCRefQual nl{}; + assert(e.and_then(nl).error() == 5); + static_assert(std::is_same_v>); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::expected e{0}; + assert(std::move(e).and_then(RVal{}) == 1); + assert(std::move(e).and_then(NORVal{}).error() == 5); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + std::expected e{0}; + assert(e.and_then(RVRefQual{}) == 1); + assert(e.and_then(NORVRefQual{}).error() == 5); + static_assert(std::is_same_v>); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::expected e{0}; + assert(std::move(e).and_then(CRVal{}) == 1); + assert(std::move(e).and_then(NOCRVal{}).error() == 5); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + const std::expected e{0}; + const RVCRefQual l{}; + assert(std::move(e).and_then(std::move(l)) == 1); + const NORVCRefQual nl{}; + assert(std::move(e).and_then(std::move(nl)).error() == 5); + static_assert(std::is_same_v>); + } + } +} + +// check that the lambda body is not instantiated during overload resolution +constexpr void test_sfinae() { + std::expected e(std::unexpected(2)); + auto l = [](auto&& x) { return x.non_const(); }; + e.and_then(l); + std::move(e).and_then(l); +} + +constexpr bool test() { + test_sfinae(); + test_val_types(); + + std::expected e(std::unexpected(1)); + const auto &ce = e; + + const auto never_called = [](int) { + assert(false); + return std::expected(); + }; + + e.and_then(never_called); + std::move(e).and_then(never_called); + ce.and_then(never_called); + std::move(ce).and_then(never_called); + + std::expected nc(std::unexpect, 0); + const auto &cnc = nc; + std::move(nc).and_then(NoCopy{}); + std::move(cnc).and_then(NoCopy{}); + + return true; +} + +int main(int, char **) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/or_else.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/or_else.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/or_else.pass.cpp @@ -0,0 +1,197 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// + +// template constexpr auto or_else(F&& f) &; +// template constexpr auto or_else(F&& f) const &; +// template constexpr auto or_else(F&& f) &&; +// template constexpr auto or_else(F&& f) const &&; + +#include +#include + +struct LVal { + constexpr std::expected operator()(int&) { return 1; } + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct CLVal { + std::expected operator()(int&) = delete; + constexpr std::expected operator()(const int&) { return 1; } + std::expected operator()(int&&) = delete; + std::expected operator()(const int&&) = delete; +}; + +struct RVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + constexpr std::expected operator()(int&&) { return 1; } + std::expected operator()(const int&&) = delete; +}; + +struct CRVal { + std::expected operator()(int&) = delete; + std::expected operator()(const int&) = delete; + std::expected operator()(int&&) = delete; + constexpr std::expected operator()(const int&&) { return 1; } +}; + +struct RefQual { + constexpr std::expected operator()(int) & { return 1; } + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct CRefQual { + std::expected operator()(int) & = delete; + constexpr std::expected operator()(int) const& { return 1; } + std::expected operator()(int) && = delete; + std::expected operator()(int) const&& = delete; +}; + +struct RVRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + constexpr std::expected operator()(int) && { return 1; } + std::expected operator()(int) const&& = delete; +}; + +struct RVCRefQual { + std::expected operator()(int) & = delete; + std::expected operator()(int) const& = delete; + std::expected operator()(int) && = delete; + constexpr std::expected operator()(int) const&& { return 1; } +}; + +struct NoCopy { + NoCopy() = default; + NoCopy(const NoCopy&) { assert(false); } + std::expected operator()(const NoCopy&&) { return 1; } +}; + +constexpr void test_val_types() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(e.or_else(LVal{}) == 1); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator + { + std::expected e(std::unexpected(0)); + RefQual l{}; + assert(e.or_else(l) == 1); + static_assert(std::is_same_v>); + } + } + + // Test const& overload + { + // Without const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + assert(e.or_else(CLVal{}) == 1); + static_assert(std::is_same_v>); + } + + // With const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const CRefQual l{}; + assert(e.or_else(l) == 1); + static_assert(std::is_same_v>); + } + } + + // Test && overload + { + // Without && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(std::move(e).or_else(RVal{}) == 1); + static_assert(std::is_same_v>); + } + + // With && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(std::move(e).or_else(RVRefQual{}) == 1); + static_assert(std::is_same_v>); + } + } + + // Test const&& overload + { + // Without const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + assert(std::move(e).or_else(CRVal{}) == 1); + static_assert(std::is_same_v>); + } + + // With const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const RVCRefQual l{}; + assert(std::move(e).or_else(std::move(l)) == 1); + static_assert(std::is_same_v>); + } + } +} + +struct NonConst { + std::expected non_const() { + return std::expected(std::unexpect, 1); + } +}; + +// check that the lambda body is not instantiated during overload resolution +constexpr void test_sfinae() { + std::expected e{1}; + auto l = [](auto &&x) { return x.non_const(); }; + e.or_else(l); + std::move(e).or_else(l); +} + +constexpr bool test() { + test_sfinae(); + test_val_types(); + + std::expected e(1); + const auto &ce = e; + + const auto never_called = [](int) { + assert(false); + return std::expected(); + }; + + e.or_else(never_called); + std::move(e).or_else(never_called); + ce.or_else(never_called); + std::move(ce).or_else(never_called); + + std::expected nc(0); + const auto &cnc = nc; + std::move(nc).or_else(NoCopy{}); + std::move(cnc).or_else(NoCopy{}); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp @@ -0,0 +1,240 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto transform(F&& f) &; +// template constexpr auto transform(F&& f) const &; +// template constexpr auto transform(F&& f) &&; +// template constexpr auto transform(F&& f) const &&; + +#include +#include +#include + +struct LVal { + constexpr int operator()(int&) { return 1; } + int operator()(const int&) = delete; + int operator()(int&&) = delete; + int operator()(const int&&) = delete; +}; + +struct CLVal { + int operator()(int&) = delete; + constexpr int operator()(const int&) { return 1; } + int operator()(int&&) = delete; + int operator()(const int&&) = delete; +}; + +struct RVal { + int operator()(int&) = delete; + int operator()(const int&) = delete; + constexpr int operator()(int&&) { return 1; } + int operator()(const int&&) = delete; +}; + +struct CRVal { + int operator()(int&) = delete; + int operator()(const int&) = delete; + int operator()(int&&) = delete; + constexpr int operator()(const int&&) { return 1; } +}; + +struct RefQual { + constexpr int operator()(int) & { return 1; } + int operator()(int) const& = delete; + int operator()(int) && = delete; + int operator()(int) const&& = delete; +}; + +struct CRefQual { + int operator()(int) & = delete; + constexpr int operator()(int) const& { return 1; } + int operator()(int) && = delete; + int operator()(int) const&& = delete; +}; + +struct RVRefQual { + int operator()(int) & = delete; + int operator()(int) const& = delete; + constexpr int operator()(int) && { return 1; } + int operator()(int) const&& = delete; +}; + +struct RVCRefQual { + int operator()(int) & = delete; + int operator()(int) const& = delete; + int operator()(int) && = delete; + constexpr int operator()(int) const&& { return 1; } +}; + +struct NoCopy { + int value; + explicit NoCopy(int value) : value(value) {} + NoCopy(const NoCopy&) = delete; +}; + +struct NonConst { + int non_const() { return 1; } +}; + +constexpr void test_val_types() { + // Test & overload + { + // Without &qualifier on F'soperator() + { + std::expected e(0); + assert(e.transform(LVal{}).value() == 1); + using A = decltype(e.transform(LVal{})); + using B = std::expected; + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + std::expected e(0); + RefQual l{}; + assert(e.transform(l) == 1); + static_assert(std::is_same_v>); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::expected e(0); + assert(e.transform(CLVal{}).value() == 1); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + const std::expected e(0); + const CRefQual l{}; + assert(e.transform(l).value() == 1); + static_assert(std::is_same_v>); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::expected e(0); + assert(std::move(e).transform(RVal{}).value() == 1); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + std::expected e(0); + assert(e.transform(RVRefQual{}).value() == 1); + static_assert(std::is_same_v>); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::expected e(0); + assert(std::move(e).transform(CRVal{}) == 1); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + const std::expected e(0); + const RVCRefQual l{}; + assert(e.transform(std::move(l)) == 1); + static_assert(std::is_same_v>); + } + } +} + +constexpr void test_take_val_return_void() { + std::expected e(1); + int val = 0; + e.transform([&val](T&&) -> void { + static_assert(std::is_same_v); + assert(val == 0); + val = 1; + }); + assert(val == 1); + std::move(e).transform([&val](T&&) -> void { + static_assert(std::is_same_v); + assert(val == 1); + val = 2; + }); + + const auto &ce = e; + assert(val == 2); + ce.transform([&val](T&&) -> void { + static_assert(std::is_same_v); + assert(val == 2); + val = 3; + }); + assert(val == 3); + std::move(ce).transform([&val](T&&) -> void { + static_assert(std::is_same_v); + assert(val == 3); + val = 4; + }); + assert(val == 4); +} + +// check val member is direct-non-list-initialized with invoke(std​::​forward(f), value()) +constexpr void test_xval() { + struct NonCopyable { + constexpr NonCopyable(int i) : i(i) {} + NonCopyable(const NonCopyable&) = delete; + int i; + }; + + auto xform = [](int i) { return NonCopyable(i); }; + std::expected e2(2); + std::expected n2 = e2.transform(xform); + assert(n2.value().i == 2); +} + +// check that the lambda body is not instantiated during overload resolution +constexpr void test_sfinae() { + std::expected e(std::unexpected(2)); + auto l = [](auto&& x) { return x.non_const(); }; + e.transform(l); + std::move(e).transform(l); + + std::expected e1(std::unexpected(1)); + const auto& ce1 = e1; + const auto never_called = [](int) { + assert(false); + return std::expected(); + }; + + e1.transform(never_called); + std::move(e1).transform(never_called); + ce1.and_then(never_called); + std::move(ce1).transform(never_called); +} + +constexpr bool test() { + test_xval(); + test_sfinae(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform_error.pass.cpp @@ -0,0 +1,211 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto transform_error(F&& f) &; +// template constexpr auto transform_error(F&& f) const &; +// template constexpr auto transform_error(F&& f) &&; +// template constexpr auto transform_error(F&& f) const &&; + +#include +#include +#include + +struct LVal { + constexpr int operator()(int&) { return 1; } + int operator()(const int&) = delete; + int operator()(int&&) = delete; + int operator()(const int&&) = delete; +}; + +struct CLVal { + int operator()(int&) = delete; + constexpr int operator()(const int&) { return 1; } + int operator()(int&&) = delete; + int operator()(const int&&) = delete; +}; + +struct RVal { + int operator()(int&) = delete; + int operator()(const int&) = delete; + constexpr int operator()(int&&) { return 1; } + int operator()(const int&&) = delete; +}; + +struct CRVal { + int operator()(int&) = delete; + int operator()(const int&) = delete; + int operator()(int&&) = delete; + constexpr int operator()(const int&&) { return 1; } +}; + +struct RefQual { + constexpr int operator()(int) & { return 1; } + int operator()(int) const& = delete; + int operator()(int) && = delete; + int operator()(int) const&& = delete; +}; + +struct CRefQual { + int operator()(int) & = delete; + constexpr int operator()(int) const& { return 1; } + int operator()(int) && = delete; + int operator()(int) const&& = delete; +}; + +struct RVRefQual { + int operator()(int) & = delete; + int operator()(int) const& = delete; + constexpr int operator()(int) && { return 1; } + int operator()(int) const&& = delete; +}; + +struct RVCRefQual { + int operator()(int) & = delete; + int operator()(int) const& = delete; + int operator()(int) && = delete; + constexpr int operator()(int) const&& { return 1; } +}; + +struct NoCopy { + int value; + explicit NoCopy(int value) : value(value) {} + NoCopy(const NoCopy&) = delete; +}; + +struct NonConst { + int non_const() { return 1; } +}; + +constexpr void test_val_types() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(e.transform_error(LVal{}).error() == 1); + static_assert(std::is_same_v>); + } + + // With & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + RefQual l{}; + assert(e.transform_error(l).error() == 1); + static_assert(std::is_same_v>); + } + } + + // Test const& overload + { + // Without const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + assert(e.transform_error(CLVal{}).error() == 1); + static_assert(std::is_same_v>); + } + + // With const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const CRefQual l{}; + assert(e.transform_error(l).error() == 1); + static_assert(std::is_same_v>); + } + } + + // Test && overload + { + // Without && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(std::move(e).transform_error(RVal{}).error() == 1); + static_assert(std::is_same_v>); + } + + // With && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + assert(std::move(e).transform_error(RVRefQual{}).error() == 1); + static_assert(std::is_same_v>); + } + } + + // Test const&& overload + { + // Without const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + assert(std::move(e).transform_error(CRVal{}).error() == 1); + static_assert(std::is_same_v>); + } + + // With const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const RVCRefQual l{}; + assert(std::move(e).transform_error(std::move(l)).error() == 1); + static_assert(std::is_same_v>); + } + } +} + +// check val member is direct-non-list-initialized with invoke(std​::​forward(f), value()) +constexpr void test_xval() { + struct NonCopyable { + constexpr NonCopyable(int i) : i(i) {} + NonCopyable(const NonCopyable&) = delete; + int i; + }; + + auto xform = [](int i) { return NonCopyable(i); }; + std::expected e2(std::unexpected(2)); + std::expected n2 = e2.transform_error(xform); + assert(n2.error().i == 2); +} + +// check that the lambda body is not instantiated during overload resolution +constexpr void test_sfinae() { + std::expected e(2); + auto l = [](auto&& x) { return x.non_const(); }; + e.transform_error(l); + std::move(e).transform_error(l); +} + +constexpr void test_fail() { + std::expected e1; + const auto& ce1 = e1; + + const auto never_called = [](int) { + assert(false); + return 0; + }; + + e1.transform_error(never_called); + std::move(e1).transform_error(never_called); + ce1.transform_error(never_called); + std::move(ce1).transform_error(never_called); +} + +constexpr bool test() { + test_xval(); + test_fail(); + test_sfinae(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.expected/observers/error_or.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/observers/error_or.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.expected/observers/error_or.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template constexpr E error_or(G&& e) const &; +// template constexpr E error_or(G&& e) &&; + +#include +#include +#include +#include +#include + +#include "MoveOnly.h" +#include "test_macros.h" + +constexpr bool test() { + // const &, has_value() + { + const std::expected e(5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x == 10); + } + + // const &, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x == 5); + } + + // &&, has_value() + { + std::expected e(5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x == 10); + } + + // &&, !has_value() + { + std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x == 5); + } + + return true; +} + +int main(int, char **) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/utilities/expected/expected.void/monadic/and_then.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/monadic/and_then.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.void/monadic/and_then.pass.cpp @@ -0,0 +1,93 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto and_then(F&& f) &; +// template constexpr auto and_then(F&& f) const &; +// template constexpr auto and_then(F&& f) &&; +// template constexpr auto and_then(F&& f) const &&; + +#include +#include + +constexpr void test_val_types() { + + // Test & overload + { + auto l = [] -> std::expected { return 2; }; + std::expected v; + assert(v.and_then(l).value() == 2); + } + + // Test const& overload + { + auto l = [] -> std::expected { return 2; }; + const std::expected v; + assert(v.and_then(l).value() == 2); + } + + // Test && overload + { + auto l = [] -> std::expected { return 2; }; + std::expected v; + assert(std::move(v).and_then(l).value() == 2); + } + + // Test const&& overload + { + auto l = [] -> std::expected { return 2; }; + const std::expected v; + assert(std::move(v).and_then(l).value() == 2); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto f = [] -> std::expected { throw 1; }; + std::expected v(std::unexpected(2)); + assert(v.and_then(f).error() == 2); + } + + // Test const& overload + { + auto f = [] -> std::expected { throw 1; }; + const std::expected v(std::unexpected(2)); + assert(v.and_then(f).error() == 2); + } + + // Test && overload + { + auto f = [] -> std::expected { throw 1; }; + std::expected v(std::unexpected(2)); + assert(std::move(v).and_then(f).error() == 2); + } + + // Test const&& overload + { + auto f = [] -> std::expected { throw 1; }; + const std::expected v(std::unexpected(2)); + assert(std::move(v).and_then(f).error() == 2); + } +} + +constexpr bool test() { + test_fail(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.void/monadic/or_else.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/monadic/or_else.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.void/monadic/or_else.pass.cpp @@ -0,0 +1,92 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto or_else(F&& f) &; +// template constexpr auto or_else(F&& f) const &; +// template constexpr auto or_else(F&& f) &&; +// template constexpr auto or_else(F&& f) const &&; + +#include +#include + +constexpr void test_val_types() { + // Test & overload + { + auto l = [](auto) -> std::expected { return {}; }; + std::expected v(std::unexpected(1)); + assert(v.or_else(l).has_value()); + } + + // Test const& overload + { + auto l = [](auto) -> std::expected { return {}; }; + const std::expected v(std::unexpected(1)); + assert(v.or_else(l).has_value()); + } + + // Test && overload + { + auto l = [](auto) -> std::expected { return {}; }; + std::expected v(std::unexpected(1)); + assert(std::move(v).or_else(l).has_value()); + } + + // Test const&& overload + { + auto l = [](auto) -> std::expected { return {}; }; + const std::expected v(std::unexpected(1)); + assert(std::move(v).or_else(l).has_value()); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto l = [](auto) -> std::expected { throw 1; }; + std::expected v; + assert(v.or_else(l).has_value()); + } + + // Test const& overload + { + auto l = [](auto) -> std::expected { throw 1; }; + const std::expected v; + assert(v.or_else(l).has_value()); + } + + // Test && overload + { + auto l = [](auto) -> std::expected { throw 1; }; + std::expected v; + assert(std::move(v).or_else(l).has_value()); + } + + // Test const&& overload + { + auto l = [](auto) -> std::expected { throw 1; }; + const std::expected v; + assert(std::move(v).or_else(l).has_value()); + } +} + +constexpr bool test() { + test_fail(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.void/monadic/transform.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/monadic/transform.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.void/monadic/transform.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto transform_error(F&& f) &; +// template constexpr auto transform_error(F&& f) const &; +// template constexpr auto transform_error(F&& f) &&; +// template constexpr auto transform_error(F&& f) const &&; + +#include +#include + +constexpr void test_val_types() { + // Test & overload + { + auto l = [](auto) -> int { return 1; }; + std::expected v(std::unexpected(1)); + assert(v.transform_error(l).error() == 1); + } + + // Test const& overload + { + auto l = [](auto) -> int { return 1; }; + const std::expected v(std::unexpected(1)); + assert(v.transform_error(l).error() == 1); + } + + // Test && overload + { + auto l = [](auto) -> int { return 1; }; + std::expected v(std::unexpected(1)); + assert(std::move(v).transform_error(l).error() == 1); + } + + // Test const&& overload + { + auto l = [](auto) -> int { return 1; }; + const std::expected v(std::unexpected(1)); + assert(std::move(v).transform_error(l).error() == 1); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto l = [](auto) -> int { throw 1; }; + std::expected v; + assert(v.transform_error(l).has_value()); + } + + // Test const& overload + { + auto l = [](auto) -> int { throw 1; }; + const std::expected v; + assert(v.transform_error(l).has_value()); + } + + // Test && overload + { + auto l = [](auto) -> int { throw 1; }; + std::expected v; + assert(std::move(v).transform_error(l).has_value()); + } + + // Test const&& overload + { + auto l = [](auto) -> int { throw 1; }; + const std::expected v; + assert(std::move(v).transform_error(l).has_value()); + } +} + +constexpr void test_xval() { + struct NonCopyable { + constexpr NonCopyable(int i) : i(i) { } + NonCopyable(const NonCopyable&) = delete; + int i; + }; + + auto x = [](int i) { return NonCopyable(i); }; + std::expected v(std::unexpected(2)); + std::expected nv = v.transform_error(x); + assert(nv.error().i == 2); +} + +constexpr bool test() { + test_fail(); + test_xval(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.void/monadic/transform_error.pass.cpp @@ -0,0 +1,106 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + +// + +// template constexpr auto transform_error(F&& f) &; +// template constexpr auto transform_error(F&& f) const &; +// template constexpr auto transform_error(F&& f) &&; +// template constexpr auto transform_error(F&& f) const &&; + +#include +#include + +constexpr void test_val_types() { + // Test & overload + { + auto l = [](int) -> int { return 1; }; + std::expected v; + assert(v.transform_error(l).error() == 1); + } + + // Test const& overload + { + auto l = [] -> int { return 1; }; + const std::expected v; + assert(v.transform_error(l).error() == 1); + } + + // Test && overload + { + auto l = [] -> int { return 1; }; + std::expected v; + assert(std::move(v).transform_error(l).error() == 1); + } + + // Test const&& overload + { + auto l = [] -> int { return 1; }; + const std::expected v; + assert(std::move(v).transform_error(l).error() == 1); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto l = [](int) -> int { throw 1; }; + std::expected v; + assert(v.transform_error(l).has_value()); + } + + // Test const& overload + { + auto l = [] -> int { throw 1; }; + const std::expected v; + assert(v.transform_error(l).has_value()); + } + + // Test && overload + { + auto l = [] -> int { throw 1; }; + std::expected v; + assert(std::move(v).transform_error(l).has_value()); + } + + // Test const&& overload + { + auto l = [] -> int { throw 1; }; + const std::expected v; + assert(std::move(v).transform_error(l).has_value()); + } +} + +constexpr void test_xval() { + struct NonCopyable { + constexpr NonCopyable(int i) : i(i) { } + NonCopyable(const NonCopyable&) = delete; + int i; + }; + + auto x = [](int i) { return NonCopyable(1); }; + std::expected v(std::unexpected(2)); + std::expected nv = v.transform_error(x); + assert(nv.error().i == 2); +} + +constexpr bool test() { + test_fail(); + test_xval(); + test_val_types(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + return 0; +} diff --git a/libcxx/test/std/utilities/expected/expected.void/observers/error_or.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/observers/error_or.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/expected/expected.void/observers/error_or.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// template constexpr E error_or(G&& e) const &; +// template constexpr E error_or(G&& e) &&; + +#include +#include +#include +#include +#include + +#include "test_macros.h" + +constexpr bool test() { + // const &, has_value() + { + const std::expected e; + std::same_as decltype(auto) x = e.error_or(10); + assert(x == 10); + } + + // const &, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x == 5); + } + + // &&, has_value() + { + std::expected e; + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x == 10); + } + + // &&, !has_value() + { + std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x == 5); + } + + return true; +} + +int main(int, char **) { + test(); + static_assert(test()); +} +