From 8f912858dba1972f5501d9130f7307a646a019f5 Mon Sep 17 00:00:00 2001 From: Sy Brand Date: Fri, 10 Feb 2023 23:10:35 +0000 Subject: [PATCH] Implement P2505R5 Monadic Functions For std::expected (#3361) Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/expected | 847 +++++++++++++++++- stl/inc/yvals_core.h | 3 +- tests/libcxx/expected_results.txt | 3 + tests/libcxx/skipped_tests.txt | 3 + tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 381 ++++++++ .../test.compile.pass.cpp | 6 +- 8 files changed, 1208 insertions(+), 40 deletions(-) create mode 100644 tests/std/tests/P2505R5_monadic_functions_for_std_expected/env.lst create mode 100644 tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp diff --git a/stl/inc/expected b/stl/inc/expected index 8b2399f78..9d4a583cf 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -30,12 +30,12 @@ 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_object_v<_Err>, "E must be an object type. (N4928 [expected.un.general]/2)"); + static_assert(!is_array_v<_Err>, "E must not be an array type. (N4928 [expected.un.general]/2)"); + static_assert(!is_const_v<_Err>, "E must not be const. (N4928 [expected.un.general]/2)"); + static_assert(!is_volatile_v<_Err>, "E must not be volatile. (N4928 [expected.un.general]/2)"); static_assert(!_Is_specialization_v<_Err, unexpected>, - "E must not be a specialization of unexpected. (N4910 [expected.un.object.general]/1)"); + "E must not be a specialization of unexpected. (N4928 [expected.un.general]/2)"); }; // [expected.un.general] @@ -47,8 +47,7 @@ class unexpected { friend class expected; public: - // [expected.un.ctor] - + // [expected.un.cons] template requires (!is_same_v, unexpected> && !is_same_v, in_place_t> && is_constructible_v<_Err, _UError>) @@ -96,7 +95,7 @@ public: // [expected.un.eq] template _NODISCARD_FRIEND constexpr bool operator==(const unexpected& _Left, const unexpected<_UErr>& _Right) noexcept( - noexcept(_Fake_copy_init(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(_Fake_copy_init(_Left._Unexpected == _Right.error()))) /* strengthened */ { return _Left._Unexpected == _Right.error(); } @@ -160,17 +159,32 @@ _EXPORT_STD struct unexpect_t { _EXPORT_STD inline constexpr unexpect_t unexpect{}; +struct _Construct_expected_from_invoke_result_tag { + explicit _Construct_expected_from_invoke_result_tag() = default; +}; + +template +concept _Is_invoke_constructible = requires(_Fn&& _Func, _Tys&&... _Vals) { + static_cast>>( + _STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Tys>(_Vals)...)); + }; + +template +struct _Check_expected_argument : true_type { + static_assert(!is_reference_v<_Ty>, "T must not be a reference type. (N4928 [expected.object.general]/2)"); + static_assert(!is_function_v<_Ty>, "T must not be a function type. (N4928 [expected.object.general]/2)"); + static_assert(!is_array_v<_Ty>, "T must not be an array type. (N4928 [expected.object.general]/2)"); + static_assert(!is_same_v, in_place_t>, + "T must not be (possibly cv-qualified) in_place_t. (N4928 [expected.object.general]/2)"); + static_assert(!is_same_v, unexpect_t>, + "T must not be (possibly cv-qualified) unexpect_t. (N4928 [expected.object.general]/2)"); + static_assert(!_Is_specialization_v, unexpected>, + "T must not be a (possibly cv-qualified) specialization of unexpected. (N4928 [expected.object.general]/2)"); +}; + _EXPORT_STD 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_expected_argument<_Ty>::value); static_assert(_Check_unexpected_argument<_Err>::value); template @@ -184,7 +198,7 @@ public: template using rebind = expected<_Uty, error_type>; - // [expected.object.ctor] + // [expected.object.cons] constexpr expected() noexcept(is_nothrow_default_constructible_v<_Ty>) // strengthened requires is_default_constructible_v<_Ty> : _Value(), _Has_value(true) {} @@ -417,7 +431,7 @@ public: && (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 + is_nothrow_constructible_v<_Ty, _Uty>&& is_nothrow_assignable_v<_Ty&, _Uty>) /* strengthened */ { if (_Has_value) { _Value = _STD forward<_Uty>(_Other); } else { @@ -433,7 +447,8 @@ public: && (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 + is_nothrow_constructible_v<_Err, const _UErr&>&& + is_nothrow_assignable_v<_Err&, const _UErr&>) /* strengthened */ { if (_Has_value) { _Reinit_expected(_Unexpected, _Value, _Other._Unexpected); _Has_value = false; @@ -449,7 +464,7 @@ public: && (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 + is_nothrow_constructible_v<_Err, _UErr>&& is_nothrow_assignable_v<_Err&, _UErr>) /* strengthened */ { if (_Has_value) { _Reinit_expected(_Unexpected, _Value, _STD move(_Other._Unexpected)); _Has_value = false; @@ -658,11 +673,11 @@ public: template _NODISCARD constexpr _Ty value_or(_Uty&& _Other) const& noexcept( - is_nothrow_copy_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened + 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)"); + is_copy_constructible_v<_Ty>, "is_copy_constructible_v must be true. (N4928 [expected.object.obs]/16)"); static_assert( - is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4910 [expected.object.obs]/16)"); + is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4928 [expected.object.obs]/16)"); if (_Has_value) { return _Value; @@ -672,11 +687,11 @@ public: } template _NODISCARD constexpr _Ty value_or(_Uty&& _Other) && noexcept( - is_nothrow_move_constructible_v<_Ty>&& is_nothrow_convertible_v<_Uty, _Ty>) { // strengthened + 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)"); + is_move_constructible_v<_Ty>, "is_move_constructible_v must be true. (N4928 [expected.object.obs]/18)"); static_assert( - is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4910 [expected.object.obs]/18)"); + is_convertible_v<_Uty, _Ty>, "is_convertible_v must be true. (N4928 [expected.object.obs]/18)"); if (_Has_value) { return _STD move(_Value); @@ -685,13 +700,385 @@ public: } } - // [expected.object.eq] + template + _NODISCARD constexpr _Err error_or(_Uty&& _Other) const& noexcept( + is_nothrow_copy_constructible_v<_Err>&& is_nothrow_convertible_v<_Uty, _Err>) /* strengthened */ { + static_assert( + is_copy_constructible_v<_Err>, "is_copy_constructible_v must be true. (N4928 [expected.object.obs]/20)"); + static_assert( + is_convertible_v<_Uty, _Err>, "is_convertible_v must be true. (N4928 [expected.object.obs]/20)"); + if (_Has_value) { + return _STD forward<_Uty>(_Other); + } else { + return _Unexpected; + } + } + + template + _NODISCARD constexpr _Err error_or(_Uty&& _Other) && noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_convertible_v<_Uty, _Err>) /* strengthened */ { + static_assert( + is_move_constructible_v<_Err>, "is_move_constructible_v must be true. (N4928 [expected.object.obs]/22)"); + static_assert( + is_convertible_v<_Uty, _Err>, "is_convertible_v must be true. (N4928 [expected.object.obs]/22)"); + + if (_Has_value) { + return _STD forward<_Uty>(_Other); + } else { + return _STD move(_Unexpected); + } + } + + // [expected.object.monadic] + template + requires is_copy_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) & { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/3)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.object.monadic]/3)"); + + if (_Has_value) { + return _STD invoke(_STD forward<_Fn>(_Func), _Value); + } else { + return _Uty{unexpect, _Unexpected}; + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) const& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/3)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.object.monadic]/3)"); + + if (_Has_value) { + return _STD invoke(_STD forward<_Fn>(_Func), _Value); + } else { + return _Uty{unexpect, _Unexpected}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) && { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/7)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.object.monadic]/7)"); + + if (_Has_value) { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Value)); + } else { + return _Uty{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) const&& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/7)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.object.monadic]/7)"); + + if (_Has_value) { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Value)); + } else { + return _Uty{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_copy_constructible_v<_Ty> + constexpr auto or_else(_Fn&& _Func) & { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/11)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.object.monadic]/11)"); + + if (_Has_value) { + return _Uty{in_place, _Value}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _Unexpected); + } + } + + template + requires is_copy_constructible_v<_Ty> + constexpr auto or_else(_Fn&& _Func) const& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/11)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.object.monadic]/11)"); + + if (_Has_value) { + return _Uty{in_place, _Value}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _Unexpected); + } + } + + template + requires is_move_constructible_v<_Ty> + constexpr auto or_else(_Fn&& _Func) && { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/15)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.object.monadic]/15)"); + + if (_Has_value) { + return _Uty{in_place, _STD move(_Value)}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Unexpected)); + } + } + + template + requires is_move_constructible_v<_Ty> + constexpr auto or_else(_Fn&& _Func) const&& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.object.monadic]/15)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.object.monadic]/15)"); + + if (_Has_value) { + return _Uty{in_place, _STD move(_Value)}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Unexpected)); + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) & { + static_assert(invocable<_Fn, _Ty&>, "expected::transform(F) requires that F is invocable with T. " + "(N4928 [expected.object.monadic]/19)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn, _Ty&>, + "expected::transform(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/19)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD invoke(_STD forward<_Fn>(_Func), _Value); + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{ + _Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func), _Value}; + } + } else { + return expected<_Uty, _Err>{unexpect, _Unexpected}; + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) const& { + static_assert(invocable<_Fn, const _Ty&>, "expected::transform(F) requires that F is invocable with T. " + "(N4928 [expected.object.monadic]/19)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn, const _Ty&>, + "expected::transform(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/19)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD invoke(_STD forward<_Fn>(_Func), _Value); + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{ + _Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func), _Value}; + } + } else { + return expected<_Uty, _Err>{unexpect, _Unexpected}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) && { + static_assert(invocable<_Fn, _Ty>, "expected::transform(F) requires that F is invocable with T. " + "(N4928 [expected.object.monadic]/23)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn, _Ty>, + "expected::transform(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/23)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Value)); + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{ + _Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func), _STD move(_Value)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) const&& { + static_assert(invocable<_Fn, const _Ty>, "expected::transform(F) requires that F is invocable with T. " + "(N4928 [expected.object.monadic]/23)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn, const _Ty>, + "expected::transform(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/23)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Value)); + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{ + _Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func), _STD move(_Value)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_copy_constructible_v<_Ty> + constexpr auto transform_error(_Fn&& _Func) & { + static_assert(invocable<_Fn, _Err&>, "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.object.monadic]/27)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, _Err&>, + "expected::transform_error(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/27)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{in_place, _Value}; + } else { + return expected<_Ty, _Uty>{ + _Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), _Unexpected}; + } + } + + template + requires is_copy_constructible_v<_Ty> + constexpr auto transform_error(_Fn&& _Func) const& { + static_assert(invocable<_Fn, const _Err&>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.object.monadic]/27)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, const _Err&>, + "expected::transform_error(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/27)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{in_place, _Value}; + } else { + return expected<_Ty, _Uty>{ + _Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), _Unexpected}; + } + } + + template + requires is_move_constructible_v<_Ty> + constexpr auto transform_error(_Fn&& _Func) && { + static_assert(invocable<_Fn, _Err>, "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.object.monadic]/31)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, _Err>, + "expected::transform_error(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/31)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{in_place, _STD move(_Value)}; + } else { + return expected<_Ty, _Uty>{_Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), + _STD move(_Unexpected)}; + } + } + + template + requires is_move_constructible_v<_Ty> + constexpr auto transform_error(_Fn&& _Func) const&& { + static_assert(invocable<_Fn, const _Err>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.object.monadic]/31)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, const _Err>, + "expected::transform_error(F) requires that the return type of F is constructible with the result of " + "invoking f. (N4928 [expected.object.monadic]/31)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{in_place, _STD move(_Value)}; + } else { + return expected<_Ty, _Uty>{_Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), + _STD move(_Unexpected)}; + } + } + + // [expected.object.eq] template requires (!is_void_v<_Uty>) _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( noexcept(_Fake_copy_init(_Left._Value == *_Right)) && noexcept( - _Fake_copy_init(_Left._Unexpected == _Right.error()))) { // strengthened + _Fake_copy_init(_Left._Unexpected == _Right.error()))) /* strengthened */ { if (_Left._Has_value != _Right.has_value()) { return false; } else if (_Left._Has_value) { @@ -703,7 +1090,7 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const _Uty& _Right) noexcept( - noexcept(static_cast(_Left._Value == _Right))) { // strengthened + noexcept(static_cast(_Left._Value == _Right))) /* strengthened */ { if (_Left._Has_value) { return static_cast(_Left._Value == _Right); } else { @@ -713,7 +1100,7 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( - noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) /* strengthened */ { if (_Left._Has_value) { return false; } else { @@ -722,6 +1109,23 @@ public: } private: + // These overloads force copy elision from the invoke call into _Value + template + constexpr expected(_Construct_expected_from_invoke_result_tag, _Fn&& _Func, _Ux&& _Arg) noexcept( + noexcept(static_cast<_Ty>(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))))) + : _Value(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))), _Has_value{true} {} + + // For when transform is called on an expected and requires calling _Func with no arg + template + constexpr expected(_Construct_expected_from_invoke_result_tag, _Fn&& _Func) noexcept( + noexcept(static_cast<_Ty>(_STD forward<_Fn>(_Func)()))) // f() is equivalent to invoke(f) + : _Value(_STD forward<_Fn>(_Func)()), _Has_value{true} {} // f() is equivalent to invoke(f) + + template + constexpr expected(_Construct_expected_from_invoke_result_tag, unexpect_t, _Fn&& _Func, _Ux&& _Arg) noexcept( + noexcept(static_cast<_Err>(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))))) + : _Unexpected(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))), _Has_value{false} {} + [[noreturn]] void _Throw_bad_expected_access_lv() const { _THROW(bad_expected_access{_Unexpected}); } @@ -758,7 +1162,7 @@ public: template using rebind = expected<_Uty, error_type>; - // [expected.void.ctor] + // [expected.void.cons] constexpr expected() noexcept : _Has_value(true) {} constexpr expected(const expected& _Other) noexcept(is_nothrow_copy_constructible_v<_Err>) // strengthened @@ -898,7 +1302,8 @@ public: 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 + 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; @@ -912,7 +1317,7 @@ public: 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 + 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; @@ -1033,11 +1438,376 @@ public: return _STD move(_Unexpected); } + template + _NODISCARD constexpr _Err error_or(_Uty&& _Other) const& noexcept( + is_nothrow_copy_constructible_v<_Err>&& is_nothrow_convertible_v<_Uty, _Err>) /* strengthened */ { + static_assert( + is_copy_constructible_v<_Err>, "is_copy_constructible_v must be true. (N4928 [expected.void.obs]/9)"); + static_assert( + is_convertible_v<_Uty, _Err>, "is_convertible_v must be true. (N4928 [expected.void.obs]/9)"); + + if (_Has_value) { + return _STD forward<_Uty>(_Other); + } else { + return _Unexpected; + } + } + + template + _NODISCARD constexpr _Err error_or(_Uty&& _Other) && noexcept( + is_nothrow_move_constructible_v<_Err>&& is_nothrow_convertible_v<_Uty, _Err>) /* strengthened */ { + static_assert( + is_move_constructible_v<_Err>, "is_move_constructible_v must be true. (N4928 [expected.void.obs]/11)"); + static_assert( + is_convertible_v<_Uty, _Err>, "is_convertible_v must be true. (N4928 [expected.void.obs]/11)"); + + if (_Has_value) { + return _STD forward<_Uty>(_Other); + } else { + return _STD move(_Unexpected); + } + } + + // [expected.void.monadic] + template + requires is_copy_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) & { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/3)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.void.monadic]/3)"); + + if (_Has_value) { + return _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + } else { + return _Uty{unexpect, _Unexpected}; + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) const& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/3)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.void.monadic]/3)"); + + if (_Has_value) { + return _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + } else { + return _Uty{unexpect, _Unexpected}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) && { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/7)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.void.monadic]/7)"); + + if (_Has_value) { + return _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + } else { + return _Uty{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto and_then(_Fn&& _Func) const&& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::and_then(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/7)"); + static_assert(is_same_v, + "expected::and_then(F) requires the error type of the return type of F to be E. " + "(N4928 [expected.void.monadic]/7)"); + + if (_Has_value) { + return _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + } else { + return _Uty{unexpect, _STD move(_Unexpected)}; + } + } + + template + constexpr auto or_else(_Fn&& _Func) & { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/10)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.void.monadic]/10)"); + + if (_Has_value) { + return _Uty{}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _Unexpected); + } + } + + template + constexpr auto or_else(_Fn&& _Func) const& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/10)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.void.monadic]/10)"); + + if (_Has_value) { + return _Uty{}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _Unexpected); + } + } + + template + constexpr auto or_else(_Fn&& _Func) && { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/13)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.void.monadic]/13)"); + + if (_Has_value) { + return _Uty{}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Unexpected)); + } + } + + template + constexpr auto or_else(_Fn&& _Func) const&& { + using _Uty = remove_cvref_t>; + + static_assert(_Is_specialization_v<_Uty, expected>, + "expected::or_else(F) requires the return type of F to be a specialization of expected. " + "(N4928 [expected.void.monadic]/13)"); + static_assert(is_same_v, + "expected::or_else(F) requires the value type of the return type of F to be T. " + "(N4928 [expected.void.monadic]/13)"); + + if (_Has_value) { + return _Uty{}; + } else { + return _STD invoke(_STD forward<_Fn>(_Func), _STD move(_Unexpected)); + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) & { + static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " + "(N4928 [expected.void.monadic]/17)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn>, "expected::transform(F) requires that the return " + "type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/17)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{_Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _Unexpected}; + } + } + + template + requires is_copy_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) const& { + static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " + "(N4928 [expected.void.monadic]/17)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn>, "expected::transform(F) requires that the return " + "type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/17)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{_Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _Unexpected}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) && { + static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " + "(N4928 [expected.void.monadic]/21)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn>, "expected::transform(F) requires that the return " + "type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/21)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{_Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _STD move(_Unexpected)}; + } + } + + template + requires is_move_constructible_v<_Err> + constexpr auto transform(_Fn&& _Func) const&& { + static_assert(invocable<_Fn>, "expected::transform(F) requires that F is invocable with no arguments. " + "(N4928 [expected.void.monadic]/21)"); + using _Uty = remove_cv_t>; + + if constexpr (!is_void_v<_Uty>) { + static_assert(_Is_invoke_constructible<_Fn>, "expected::transform(F) requires that the return " + "type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/21)"); + } + static_assert(_Check_expected_argument<_Uty>::value); + + if (_Has_value) { + if constexpr (is_void_v<_Uty>) { + _STD forward<_Fn>(_Func)(); // f() is equivalent to invoke(f) + return expected<_Uty, _Err>{}; + } else { + return expected<_Uty, _Err>{_Construct_expected_from_invoke_result_tag{}, _STD forward<_Fn>(_Func)}; + } + } else { + return expected<_Uty, _Err>{unexpect, _STD move(_Unexpected)}; + } + } + + template + constexpr auto transform_error(_Fn&& _Func) & { + static_assert(invocable<_Fn, _Err&>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.void.monadic]/24)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, _Err&>, "expected::transform_error(F) requires that the " + "return type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/24)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{}; + } else { + return expected<_Ty, _Uty>{ + _Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), _Unexpected}; + } + } + + template + constexpr auto transform_error(_Fn&& _Func) const& { + static_assert(invocable<_Fn, const _Err&>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.void.monadic]/24)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, const _Err&>, + "expected::transform_error(F) requires that the " + "return type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/24)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{}; + } else { + return expected<_Ty, _Uty>{ + _Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), _Unexpected}; + } + } + + template + constexpr auto transform_error(_Fn&& _Func) && { + static_assert(invocable<_Fn, _Err>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.void.monadic]/27)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, _Err>, "expected::transform_error(F) requires that the " + "return type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/27)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{}; + } else { + return expected<_Ty, _Uty>{_Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), + _STD move(_Unexpected)}; + } + } + + template + constexpr auto transform_error(_Fn&& _Func) const&& { + static_assert(invocable<_Fn, const _Err>, + "expected::transform_error(F) requires that F is invocable with E. " + "(N4928 [expected.void.monadic]/27)"); + using _Uty = remove_cv_t>; + static_assert(_Is_invoke_constructible<_Fn, const _Err>, + "expected::transform_error(F) requires that the " + "return type of F is constructible with the result of " + "invoking f. (N4928 [expected.void.monadic]/27)"); + + static_assert(_Check_unexpected_argument<_Uty>::value); + + if (_Has_value) { + return expected<_Ty, _Uty>{}; + } else { + return expected<_Ty, _Uty>{_Construct_expected_from_invoke_result_tag{}, unexpect, _STD forward<_Fn>(_Func), + _STD move(_Unexpected)}; + } + } + // [expected.void.eq] template requires is_void_v<_Uty> _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const expected<_Uty, _UErr>& _Right) noexcept( - noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) /* strengthened */ { if (_Left._Has_value != _Right.has_value()) { return false; } else { @@ -1047,7 +1817,7 @@ public: template _NODISCARD_FRIEND constexpr bool operator==(const expected& _Left, const unexpected<_UErr>& _Right) noexcept( - noexcept(static_cast(_Left._Unexpected == _Right.error()))) { // strengthened + noexcept(static_cast(_Left._Unexpected == _Right.error()))) /* strengthened */ { if (_Left._Has_value) { return false; } else { @@ -1056,6 +1826,11 @@ public: } private: + template + constexpr expected(_Construct_expected_from_invoke_result_tag, unexpect_t, _Fn&& _Func, _Ux&& _Arg) noexcept( + noexcept(_Err(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))))) + : _Unexpected(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))), _Has_value{false} {} + [[noreturn]] void _Throw_bad_expected_access_lv() const { _THROW(bad_expected_access{_Unexpected}); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index cb76b2081..dc1305bb8 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -347,6 +347,7 @@ // P2474R2 views::repeat // P2494R2 Relaxing Range Adaptors To Allow Move-Only Types // P2499R0 string_view Range Constructor Should Be explicit +// P2505R5 Monadic Functions For expected // P2549R1 unexpected::error() // P2602R2 Poison Pills Are Too Toxic @@ -1679,7 +1680,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #ifdef __cpp_lib_concepts #define __cpp_lib_containers_ranges 202202L -#define __cpp_lib_expected 202202L +#define __cpp_lib_expected 202211L #endif // __cpp_lib_concepts #define __cpp_lib_forward_like 202207L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a6f952056..dcb235dad 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -82,6 +82,9 @@ std/utilities/utility/mem.res/mem.res.pool/mem.res.pool.mem/unsync_deallocate_ma # Too many constexpr operations std/utilities/charconv/charconv.to.chars/integral.pass.cpp FAIL +# libc++ has not implemented P2505R5: "Monadic Functions for std::expected" +std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp FAIL + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 7f4cea3d9..6f494826f 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -82,6 +82,9 @@ utilities\utility\mem.res\mem.res.pool\mem.res.pool.mem\unsync_deallocate_matche # Too many constexpr operations utilities\charconv\charconv.to.chars\integral.pass.cpp +# libc++ has not implemented P2505R5: "Monadic Functions for std::expected" +language.support\support.limits\support.limits.general\expected.version.compile.pass.cpp + # *** INTERACTIONS WITH CONTEST / C1XX THAT UPSTREAM LIKELY WON'T FIX *** # Tracked by VSO-593630 " Enable libcxx filesystem tests" diff --git a/tests/std/test.lst b/tests/std/test.lst index 56a939374..dc8519a3c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -590,6 +590,7 @@ tests\P2467R1_exclusive_mode_fstreams tests\P2474R2_views_repeat tests\P2474R2_views_repeat_death tests\P2494R2_move_only_range_adaptors +tests\P2505R5_monadic_functions_for_std_expected tests\P2517R1_apply_conditional_noexcept tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/env.lst b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/env.lst new file mode 100644 index 000000000..18e2d7c71 --- /dev/null +++ b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp new file mode 100644 index 000000000..9ce4e74d8 --- /dev/null +++ b/tests/std/tests/P2505R5_monadic_functions_for_std_expected/test.cpp @@ -0,0 +1,381 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +namespace detail { + static constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} // namespace detail + +constexpr bool is_permissive = detail::Derived::test(); + +enum class IsNothrowConstructible : bool { Not, Yes }; +enum class IsNothrowConvertible : bool { Not, Yes }; + +template +[[nodiscard]] constexpr bool IsYes(const E e) noexcept { + return e == E::Yes; +} + +struct convertible { + constexpr convertible(const int val) noexcept : _val(val) {} + + [[nodiscard]] constexpr bool operator==(const int other) const noexcept { + return other == _val; + } + + int _val = 0; +}; + +struct Immovable { + constexpr Immovable(const int x) : v(x) {} + Immovable(const Immovable&) = delete; + Immovable(Immovable&&) = delete; + Immovable& operator=(const Immovable&) = delete; + Immovable& operator=(Immovable&&) = delete; + constexpr ~Immovable() = default; + + int v; +}; + +struct Thingy { + expected x; + constexpr int member_func() const { + return 66; + } +}; + +template +constexpr void test_impl(Expected&& engaged, Expected&& unengaged) { + assert(engaged.has_value()); + assert(!unengaged.has_value()); + static_assert(is_same_v::error_type, int>); + using Val = typename remove_cvref_t::value_type; + + const auto succeed = [](auto...) { return expected{33}; }; + const auto fail = [](auto...) { return expected{unexpect, 44}; }; + + { + decltype(auto) result = forward(engaged).and_then(succeed); + static_assert(is_same_v>); + assert(result == 33); + } + { + decltype(auto) result = forward(unengaged).and_then(succeed); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + { + decltype(auto) result = forward(engaged).and_then(fail); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 44); + } + { + decltype(auto) result = forward(unengaged).and_then(fail); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + + if constexpr (!is_void_v) { + { + decltype(auto) result = forward(engaged).and_then(&Thingy::x); + static_assert(is_same_v>); + assert(result == 11); + } + { + decltype(auto) result = forward(unengaged).and_then(&Thingy::x); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + } + + const auto f = [](auto...) { return 55; }; + const auto immov = [](auto...) { return Immovable{88}; }; + const auto to_void = [](auto...) { return; }; + + { + decltype(auto) result = forward(engaged).transform(f); + static_assert(is_same_v>); + assert(result == 55); + } + { + decltype(auto) result = forward(unengaged).transform(f); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + + if constexpr (!is_void_v) { + { + decltype(auto) result = forward(engaged).transform(&Thingy::member_func); + static_assert(is_same_v>); + assert(result == 66); + } + { + decltype(auto) result = forward(unengaged).transform(&Thingy::member_func); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + } + + if constexpr (!is_permissive) { // TRANSITION, VSO-1734935 + { + decltype(auto) result = forward(engaged).transform(immov); + static_assert(is_same_v>); + assert(result->v == 88); + } + { + decltype(auto) result = forward(unengaged).transform(immov); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + } + + { + decltype(auto) result = forward(engaged).transform(to_void); + static_assert(is_same_v>); + assert(result); + } + { + decltype(auto) result = forward(unengaged).transform(to_void); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 22); + } + + const auto to_thingy = [](int i) { return Thingy{i}; }; + + { + decltype(auto) result = forward(engaged).transform_error(to_thingy); + static_assert(is_same_v>); + assert(result); + if constexpr (!is_void_v) { + assert(result->x == 11); + } + } + { + decltype(auto) result = forward(unengaged).transform_error(to_thingy); + static_assert(is_same_v>); + assert(!result); + assert(result.error().x == 22); + } + { + decltype(auto) result = + forward(engaged).transform_error(to_thingy).transform_error(&Thingy::member_func); + static_assert(is_same_v>); + assert(result); + if constexpr (!is_void_v) { + assert(result->x == 11); + } + } + { + decltype(auto) result = + forward(unengaged).transform_error(to_thingy).transform_error(&Thingy::member_func); + static_assert(is_same_v>); + assert(!result); + assert(result.error() == 66); + } + + if constexpr (!is_permissive) { // TRANSITION, VSO-1734935 + { + decltype(auto) result = forward(engaged).transform_error(immov); + static_assert(is_same_v>); + assert(result); + if constexpr (!is_void_v) { + assert(result->x == 11); + } + } + { + decltype(auto) result = forward(unengaged).transform_error(immov); + static_assert(is_same_v>); + assert(!result); + assert(result.error().v == 88); + } + } + + const auto to_expected_thingy = [](auto...) { + if constexpr (is_void_v) { + return expected{}; + } else { + return expected{Thingy{77}}; + } + }; + { + decltype(auto) result = forward(engaged).or_else(to_expected_thingy); + static_assert(is_same_v>); + assert(result); + if constexpr (!is_void_v) { + assert(result.value().x == 11); + } + } + { + decltype(auto) result = forward(unengaged).or_else(to_expected_thingy); + static_assert(is_same_v>); + assert(result); + if constexpr (!is_void_v) { + assert(result.value().x == 77); + } + } + + engaged.transform([](auto...) { return ""; }); +} + +template +constexpr void test_error_or() { + 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_error_or { + constexpr payload_error_or() noexcept : _val(55) {} + constexpr payload_error_or(const int val) noexcept : _val(val) {} + constexpr payload_error_or(const payload_error_or& other) noexcept(construction_is_noexcept) + : _val(other._val + 2) {} + constexpr payload_error_or(payload_error_or&& other) noexcept(construction_is_noexcept) + : _val(other._val + 3) {} + constexpr payload_error_or(const convertible& val) noexcept(conversion_is_noexcept) : _val(val._val + 4) {} + constexpr payload_error_or(convertible&& val) noexcept(conversion_is_noexcept) : _val(val._val + 5) {} + + [[nodiscard]] constexpr bool operator==(const payload_error_or&) const noexcept = default; + + int _val; + }; + + { // with payload argument + using Expected = expected; + + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.error_or(payload_error_or{1}) == 42 + 2); + assert(const_with_error.error_or(payload_error_or{1}) == 1337 + 2); + static_assert(noexcept(with_error.error_or(payload_error_or{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_error.error_or(payload_error_or{1})) == construction_is_noexcept); + + assert(move(with_error).error_or(payload_error_or{1}) == 42 + 3); + assert(move(const_with_error).error_or(payload_error_or{1}) == 1337 + 2); + static_assert(noexcept(move(with_error).error_or(payload_error_or{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_error).error_or(payload_error_or{1})) == construction_is_noexcept); + + const payload_error_or input{2000}; + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.error_or(payload_error_or{1}) == 1 + 3); + assert(const_with_value.error_or(input) == 2000 + 2); + static_assert(noexcept(with_value.error_or(payload_error_or{1})) == construction_is_noexcept); + static_assert(noexcept(const_with_value.error_or(input)) == construction_is_noexcept); + + assert(move(with_value).error_or(payload_error_or{1}) == 1 + 3); + assert(move(const_with_value).error_or(input) == 2000 + 2); + static_assert(noexcept(move(with_value).error_or(payload_error_or{1})) == construction_is_noexcept); + static_assert(noexcept(move(const_with_value).error_or(input)) == construction_is_noexcept); + } + + { // with convertible argument + using Expected = expected; + + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + assert(with_error.error_or(convertible{1}) == 42 + 2); + assert(const_with_error.error_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(with_error.error_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_error.error_or(convertible{1})) == should_be_noexcept); + + assert(move(with_error).error_or(convertible{1}) == 42 + 3); + assert(move(const_with_error).error_or(convertible{1}) == 1337 + 2); + static_assert(noexcept(move(with_error).error_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_error).error_or(convertible{1})) == should_be_noexcept); + + const convertible input{2000}; + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + assert(with_value.error_or(convertible{1}) == 1 + 5); + assert(const_with_value.error_or(input) == 2000 + 4); + static_assert(noexcept(with_value.error_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(const_with_value.error_or(input)) == should_be_noexcept); + + assert(move(with_value).error_or(convertible{1}) == 1 + 5); + assert(move(const_with_value).error_or(input) == 2000 + 4); + static_assert(noexcept(move(with_value).error_or(convertible{1})) == should_be_noexcept); + static_assert(noexcept(move(const_with_value).error_or(input)) == should_be_noexcept); + } + + { // test error_or({}) + using Expected = expected; + + Expected with_error{unexpect, 42}; + const Expected const_with_error{unexpect, 1337}; + Expected with_value{in_place, 42}; + const Expected const_with_value{in_place, 1337}; + + assert(with_error.error_or({}) == 42 + 2); + assert(const_with_error.error_or({}) == 1337 + 2); + assert(with_value.error_or({}) == 55 + 3); + assert(const_with_value.error_or({}) == 55 + 3); + } +} + +constexpr void test_error_or() noexcept { + test_error_or(); + test_error_or(); + test_error_or(); + test_error_or(); +} + +constexpr void test_monadic() { + { + expected engaged{Thingy{11}}; + expected unengaged{unexpect, 22}; + test_impl(engaged, unengaged); + test_impl(as_const(engaged), as_const(unengaged)); + test_impl(move(engaged), move(unengaged)); + test_impl(move(as_const(engaged)), move(as_const(unengaged))); + } + + { + expected engaged{}; + expected unengaged{unexpect, 22}; + test_impl(engaged, unengaged); + test_impl(as_const(engaged), as_const(unengaged)); + test_impl(move(engaged), move(unengaged)); + test_impl(move(as_const(engaged)), move(as_const(unengaged))); + } +} + +constexpr bool test() { + test_error_or(); + test_monadic(); + + return true; +} + +int main() { + test(); + static_assert(test()); +} 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 dbb1d9fdf..e266b30ed 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 @@ -809,10 +809,10 @@ STATIC_ASSERT(__cpp_lib_execution == 201603L); #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_expected #error __cpp_lib_expected is not defined -#elif __cpp_lib_expected != 202202L -#error __cpp_lib_expected is not 202202L +#elif __cpp_lib_expected != 202211L +#error __cpp_lib_expected is not 202211L #else -STATIC_ASSERT(__cpp_lib_expected == 202202L); +STATIC_ASSERT(__cpp_lib_expected == 202211L); #endif #else #ifdef __cpp_lib_expected