diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -320,7 +320,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_constexpr_typeinfo`` ``202106L`` ------------------------------------------------- ----------------- - ``__cpp_lib_expected`` ``202202L`` + ``__cpp_lib_expected`` ``202211L`` ------------------------------------------------- ----------------- ``__cpp_lib_format_ranges`` ``202207L`` ------------------------------------------------- ----------------- diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -41,6 +41,7 @@ - P1328R1 - ``constexpr type_info::operator==()`` - P1413R3 - Formatting ``thread::id`` (the ``stacktrace`` is not done yet) - P2675R1 - ``format``'s width estimation is too approximate and not forward compatible +- P2505R5 - Monadic operations for ``std::expected`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv --- a/libcxx/docs/Status/Cxx2bIssues.csv +++ b/libcxx/docs/Status/Cxx2bIssues.csv @@ -274,7 +274,7 @@ "`3853 `__","``basic_const_iterator::operator->`` is ill-formed","February 2023","","","" "`3857 `__","``basic_string_view`` should allow explicit conversion when only traits vary","February 2023","|Complete|","17.0","" "`3860 `__","``range_common_reference_t`` is missing","February 2023","|Complete|","17.0","|ranges|" -"`3866 `__","Bad Mandates for ``expected::transform_error`` overloads","February 2023","","","" +"`3866 `__","Bad Mandates for ``expected::transform_error`` overloads","February 2023","|Complete|","17.0","" "`3867 `__","Should ``std::basic_osyncstream``'s move assignment operator be ``noexcept``?","February 2023","","","" "`3441 `__","Misleading note about calls to customization points","February 2023","","","" "`3622 `__","Misspecified transitivity of equivalence in ยง[unord.req.general]","February 2023","","","" @@ -301,7 +301,7 @@ "`3872 `__","``basic_const_iterator`` should have custom ``iter_move``","February 2023","","","" "`3875 `__","``std::ranges::repeat_view::iterator`` may be ill-formed","February 2023","","","|ranges|" "`3876 `__","Default constructor of ``std::layout_XX::mapping`` misses precondition","February 2023","","","" -"`3877 `__","Incorrect constraints on ``const``-qualified monadic overloads for ``std::expected``","February 2023","","","" +"`3877 `__","Incorrect constraints on ``const``-qualified monadic overloads for ``std::expected``","February 2023","|Complete|","17.0","" "`3878 `__","import ``std;`` should guarantee initialization of standard iostreams objects","February 2023","","","" "`3879 `__","``erase_if`` for ``flat_{,multi}set`` is incorrectly specified","February 2023","","","" "`3880 `__","Clarify ``operator+=`` complexity for ``{chunk,stride}_view::iterator``","February 2023","","","|ranges|" 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","|Complete|","17.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 @@ -14,10 +14,12 @@ #include <__expected/bad_expected_access.h> #include <__expected/unexpect.h> #include <__expected/unexpected.h> +#include <__functional/invoke.h> #include <__memory/addressof.h> #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> @@ -60,7 +62,17 @@ _LIBCPP_BEGIN_NAMESPACE_STD -namespace __expected { +template +class expected; + +template +struct __is_std_expected : false_type {}; + +template +struct __is_std_expected> : true_type {}; + +struct __expected_construct_in_place_from_invoke_tag {}; +struct __expected_construct_unexpected_from_invoke_tag {}; template _LIBCPP_HIDE_FROM_ABI void __throw_bad_expected_access(_Arg&& __arg) { @@ -72,8 +84,6 @@ # endif } -} // namespace __expected - template class expected { static_assert( @@ -166,6 +176,15 @@ _Not, const expected<_Up, _OtherErr>&>>, _Not, const expected<_Up, _OtherErr>>> >; + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit expected( + std::__expected_construct_in_place_from_invoke_tag __tag, _Func&& __f, _Args&&... __args) + : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(true) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit expected( + std::__expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args) + : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {} public: template @@ -538,28 +557,28 @@ _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& value() const& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + std::__throw_bad_expected_access<_Err>(__union_.__unex_); } return __union_.__val_; } _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + std::__throw_bad_expected_access<_Err>(__union_.__unex_); } return __union_.__val_; } _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& value() const&& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); } return std::move(__union_.__val_); } _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); } return std::move(__union_.__val_); } @@ -598,6 +617,245 @@ return __has_val_ ? std::move(__union_.__val_) : static_cast<_Tp>(std::forward<_Up>(__v)); } + template + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __error) const& { + static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible"); + static_assert(is_convertible_v<_Up, _Err>, "argument has to be convertible to error_type"); + if (has_value()) + return std::forward<_Up>(__error); + return error(); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __error) && { + static_assert(is_move_constructible_v<_Err>, "error_type has to be move constructible"); + static_assert(is_convertible_v<_Up, _Err>, "argument has to be convertible to error_type"); + if (has_value()) + return std::forward<_Up>(__error); + return std::move(error()); + } + + // [expected.void.monadic], monadic + template + requires is_constructible_v<_Err, _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(value()) must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f), value()); + } + return _Up(unexpect, error()); + } + + template + requires is_constructible_v<_Err, const _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f(value()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(value()) must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f), value()); + } + return _Up(unexpect, error()); + } + + template + requires is_constructible_v<_Err, _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert( + __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(value())) must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f), std::move(value())); + } + return _Up(unexpect, std::move(error())); + } + + template + requires is_constructible_v<_Err, const _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert( + __is_std_expected<_Up>::value, "The result of f(std::move(value())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(value())) must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f), std::move(value())); + } + return _Up(unexpect, std::move(error())); + } + + template + requires is_constructible_v<_Tp, _Tp&> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(error()) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(in_place, value()); + } + return std::invoke(std::forward<_Func>(__f), error()); + } + + template + requires is_constructible_v<_Tp, const _Tp&> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(error()) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(in_place, value()); + } + return std::invoke(std::forward<_Func>(__f), error()); + } + + template + requires is_constructible_v<_Tp, _Tp&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && { + using _Gp = remove_cvref_t>; + static_assert( + __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(error())) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(in_place, std::move(value())); + } + return std::invoke(std::forward<_Func>(__f), std::move(error())); + } + + template + requires is_constructible_v<_Tp, const _Tp&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& { + using _Gp = remove_cvref_t>; + static_assert( + __is_std_expected<_Gp>::value, "The result of f(std::move(error())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(error())) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(in_place, std::move(value())); + } + return std::invoke(std::forward<_Func>(__f), std::move(error())); + } + + template + requires is_constructible_v<_Err, _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, error()); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), value()); + } else { + std::invoke(std::forward<_Func>(__f), value()); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, const _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, error()); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), value()); + } else { + std::invoke(std::forward<_Func>(__f), value()); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, std::move(error())); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value())); + } else { + std::invoke(std::forward<_Func>(__f), std::move(value())); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, const _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, std::move(error())); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>( + __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value())); + } else { + std::invoke(std::forward<_Func>(__f), std::move(value())); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Tp, _Tp&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) & { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(error()) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(in_place, value()); + } + return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); + } + + template + requires is_constructible_v<_Tp, const _Tp&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const& { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(error()) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(in_place, value()); + } + return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); + } + + template + requires is_constructible_v<_Tp, _Tp&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) && { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(std::move(error())) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(in_place, std::move(value())); + } + return expected<_Tp, _Gp>( + __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); + } + + template + requires is_constructible_v<_Tp, const _Tp&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const&& { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(std::move(error())) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(in_place, std::move(value())); + } + return expected<_Tp, _Gp>( + __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); + } + // [expected.object.eq], equality operators template requires(!is_void_v<_T2>) @@ -625,24 +883,66 @@ 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 { + + template + union __union_t { + _LIBCPP_HIDE_FROM_ABI constexpr __union_t() {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __val_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() + requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>) + = default; + + // the expected's destructor handles this + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {} + + _ValueType __val_; + _ErrorType __unex_; + }; + + // use named union because [[no_unique_address]] cannot be applied to an unnamed union, + // also guaranteed elision into a potentially-overlapping subobject is unsettled (and + // it's not clear that it's implementable, given that the function is allowed to clobber + // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107. + template + requires(is_trivially_move_constructible_v<_ValueType> && is_trivially_move_constructible_v<_ErrorType>) + union __union_t<_ValueType, _ErrorType> { _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {} + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + std::__expected_construct_in_place_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __val_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + std::__expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Err>) + requires(is_trivially_destructible_v<_ValueType> && is_trivially_destructible_v<_ErrorType>) = 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>) + requires(!is_trivially_destructible_v<_ValueType> || !is_trivially_destructible_v<_ErrorType>) {} _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_; - _LIBCPP_NO_UNIQUE_ADDRESS _Tp __val_; - _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_; - } __union_; + _LIBCPP_NO_UNIQUE_ADDRESS _ValueType __val_; + _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_; + }; + _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Tp, _Err> __union_; bool __has_val_; }; @@ -762,6 +1062,19 @@ std::construct_at(std::addressof(__union_.__unex_), __il, std::forward<_Args>(__args)...); } +private: + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit expected(__expected_construct_in_place_from_invoke_tag, _Func&& __f) + : __has_val_(true) { + std::invoke(std::forward<_Func>(__f)); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit expected( + __expected_construct_unexpected_from_invoke_tag __tag, _Func&& __f, _Args&&... __args) + : __union_(__tag, std::forward<_Func>(__f), std::forward<_Args>(__args)...), __has_val_(false) {} + +public: // [expected.void.dtor], destructor _LIBCPP_HIDE_FROM_ABI constexpr ~expected() @@ -899,13 +1212,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr void value() const& { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(__union_.__unex_); + std::__throw_bad_expected_access<_Err>(__union_.__unex_); } } _LIBCPP_HIDE_FROM_ABI constexpr void value() && { if (!__has_val_) { - __expected::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); + std::__throw_bad_expected_access<_Err>(std::move(__union_.__unex_)); } } @@ -929,6 +1242,235 @@ return std::move(__union_.__unex_); } + template + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __error) const& { + static_assert(is_copy_constructible_v<_Err>, "error_type has to be copy constructible"); + static_assert(is_convertible_v<_Up, _Err>, "argument has to be convertible to error_type"); + if (has_value()) { + return std::forward<_Up>(__error); + } + return error(); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr _Err error_or(_Up&& __error) && { + static_assert(is_move_constructible_v<_Err>, "error_type has to be move constructible"); + static_assert(is_convertible_v<_Up, _Err>, "argument has to be convertible to error_type"); + if (has_value()) { + return std::forward<_Up>(__error); + } + return std::move(error()); + } + + // [expected.void.monadic], monadic + template + requires is_constructible_v<_Err, _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected"); + static_assert( + is_same_v, "The result of f() must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f)); + } + return _Up(unexpect, error()); + } + + template + requires is_constructible_v<_Err, const _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected"); + static_assert( + is_same_v, "The result of f() must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f)); + } + return _Up(unexpect, error()); + } + + template + requires is_constructible_v<_Err, _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected"); + static_assert( + is_same_v, "The result of f() must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f)); + } + return _Up(unexpect, std::move(error())); + } + + template + requires is_constructible_v<_Err, const _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& { + using _Up = remove_cvref_t>; + static_assert(__is_std_expected<_Up>::value, "The result of f() must be a specialization of std::expected"); + static_assert( + is_same_v, "The result of f() must have the same error_type as this expected"); + if (has_value()) { + return std::invoke(std::forward<_Func>(__f)); + } + return _Up(unexpect, std::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) & { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(error()) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(); + } + return std::invoke(std::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const& { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, "The result of f(error()) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(error()) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(); + } + return std::invoke(std::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) && { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, + "The result of f(std::move(error())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(error())) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(); + } + return std::invoke(std::forward<_Func>(__f), std::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto or_else(_Func&& __f) const&& { + using _Gp = remove_cvref_t>; + static_assert(__is_std_expected<_Gp>::value, + "The result of f(std::move(error())) must be a specialization of std::expected"); + static_assert(is_same_v, + "The result of f(std::move(error())) must have the same value_type as this expected"); + if (has_value()) { + return _Gp(); + } + return std::invoke(std::forward<_Func>(__f), std::move(error())); + } + + template + requires is_constructible_v<_Err, _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, error()); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f)); + } else { + std::invoke(std::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, const _Err&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, error()); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f)); + } else { + std::invoke(std::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, std::move(error())); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f)); + } else { + std::invoke(std::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + + template + requires is_constructible_v<_Err, const _Err&&> + _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& { + using _Up = remove_cv_t>; + if (!has_value()) { + return expected<_Up, _Err>(unexpect, std::move(error())); + } + if constexpr (!is_void_v<_Up>) { + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f)); + } else { + std::invoke(std::forward<_Func>(__f)); + return expected<_Up, _Err>(); + } + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) & { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(error()) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(); + } + return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const& { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(error()) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(); + } + return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) && { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(std::move(error())) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(); + } + return expected<_Tp, _Gp>( + __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); + } + + template + _LIBCPP_HIDE_FROM_ABI constexpr auto transform_error(_Func&& __f) const&& { + using _Gp = remove_cv_t>; + static_assert(__valid_std_unexpected<_Gp>::value, + "The result of f(std::move(error())) must be a valid template argument for unexpected"); + if (has_value()) { + return expected<_Tp, _Gp>(); + } + return expected<_Tp, _Gp>( + __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); + } + // [expected.void.eq], equality operators template requires is_void_v<_T2> @@ -947,23 +1489,55 @@ 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 { + + template + union __union_t { _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {} + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() + requires(is_trivially_destructible_v<_ErrorType>) + = default; + + // the expected's destructor handles this + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() {} + + __empty_t __empty_; + _ErrorType __unex_; + }; + + // use named union because [[no_unique_address]] cannot be applied to an unnamed union, + // also guaranteed elision into a potentially-overlapping subobject is unsettled (and + // it's not clear that it's implementable, given that the function is allowed to clobber + // the tail padding) - see https://github.com/itanium-cxx-abi/cxx-abi/issues/107. + template + requires is_trivially_move_constructible_v<_ErrorType> + union __union_t<_ErrorType> { + _LIBCPP_HIDE_FROM_ABI constexpr __union_t() : __empty_() {} + + template + _LIBCPP_HIDE_FROM_ABI constexpr explicit __union_t( + __expected_construct_unexpected_from_invoke_tag, _Func&& __f, _Args&&... __args) + : __unex_(std::invoke(std::forward<_Func>(__f), std::forward<_Args>(__args)...)) {} + _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(is_trivially_destructible_v<_Err>) + requires(is_trivially_destructible_v<_ErrorType>) = default; // the expected's destructor handles this _LIBCPP_HIDE_FROM_ABI constexpr ~__union_t() - requires(!is_trivially_destructible_v<_Err>) + requires(!is_trivially_destructible_v<_ErrorType>) {} _LIBCPP_NO_UNIQUE_ADDRESS __empty_t __empty_; - _LIBCPP_NO_UNIQUE_ADDRESS _Err __unex_; - } __union_; + _LIBCPP_NO_UNIQUE_ADDRESS _ErrorType __unex_; + }; + _LIBCPP_NO_UNIQUE_ADDRESS __union_t<_Err> __union_; bool __has_val_; }; diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -82,7 +82,7 @@ __cpp_lib_exchange_function 201304L __cpp_lib_execution 201902L 201603L // C++17 -__cpp_lib_expected 202202L +__cpp_lib_expected 202211L __cpp_lib_filesystem 201703L __cpp_lib_format 202106L __cpp_lib_format_ranges 202207L @@ -399,7 +399,7 @@ # undef __cpp_lib_constexpr_memory # define __cpp_lib_constexpr_memory 202202L # define __cpp_lib_constexpr_typeinfo 202106L -# define __cpp_lib_expected 202202L +# define __cpp_lib_expected 202211L # if !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT) # define __cpp_lib_format_ranges 202207L # endif diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.expected/and_then.mandates.verify.cpp @@ -0,0 +1,126 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr auto and_then(F&& f) &; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) const &; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) &&; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) const &&; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +#include +#include + +struct NotSameAsInt {}; + +int lval_return_not_std_expected(int&) { return 0; } +int clval_return_not_std_expected(const int&) { return 0; } +int rval_return_not_std_expected(int&&) { return 0; } +int crval_return_not_std_expected(const int&&) { return 0; } + +std::expected lval_error_type_not_same_as_int(int&) { return {}; } +std::expected clval_error_type_not_same_as_int(const int&) { return {}; } +std::expected rval_error_type_not_same_as_int(int&&) { return {}; } +std::expected crval_error_type_not_same_as_int(const int&&) { return {}; } + +// clang-format off +void test() { + // Test & overload + { + // U is not a specialization of std::expected + { + std::expected f1(1); + f1.and_then(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1(1); + f1.and_then(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)(int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must have the same error_type as this expected}} + } + } + + // Test const& overload + { + // U is not a specialization of std::expected + { + const std::expected f1(1); + f1.and_then(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1(1); + f1.and_then(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)(const int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(value()) must have the same error_type as this expected}} + + } + } + + // Test && overload + { + // U is not a specialization of std::expected + { + std::expected f1(1); + std::move(f1).and_then(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1(1); + std::move(f1).and_then(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)(int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}} + } + } + + // Test const&& overload + { + // U is not a specialization of std::expected + { + const std::expected f1(1); + std::move(f1).and_then(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1(1); + std::move(f1).and_then(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)(const int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(value())) must have the same error_type as this expected}} + } + } +} +// clang-format on diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/error_or.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/error_or.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.expected/error_or.mandates.verify.cpp @@ -0,0 +1,72 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr E error_or(G&&) const &; +// Mandates: is_copy_constructible_v is true and is_convertible_v is true. + +// template constexpr E error_or(G&&) &&; +// Mandates: is_move_constructible_v is true and is_convertible_v is true. + +#include +#include + +struct NonCopyable { + NonCopyable(int) {} + NonCopyable(const NonCopyable&) = delete; +}; + +struct NonMovable { + NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; + +struct NotConvertibleFromInt {}; + +// clang-format off +void test() { + // const & overload + // !is_copy_constructible_v, + { + const std::expected f1(std::unexpect, 0); + f1.error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}error_type has to be copy constructible}} + // expected-error-re@*:* {{call to deleted constructor of{{.*}}}} + } + + // const & overload + // !is_convertible_v + { + const std::expected f1(std::unexpect, NotConvertibleFromInt{}); + f1.error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to error_type}} + // expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}} + + } + + // && overload + // !is_move_constructible_v, + { + std::expected f1(std::unexpect, 0); + std::move(f1).error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}error_type has to be move constructible}} + // expected-error-re@*:* {{call to deleted constructor of{{.*}}}} + } + + // && overload + // !is_convertible_v + { + std::expected f1(std::unexpect, NotConvertibleFromInt{}); + std::move(f1).error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + //expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to error_type}} + // expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}} + } +} +// clang-format on 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 --- 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 @@ -16,4 +16,20 @@ struct Empty {}; +struct A { + int x_; + int y_; +}; + +struct B : public A { + int z_; + virtual ~B() = default; +}; + static_assert(sizeof(std::expected) == sizeof(bool)); +static_assert(sizeof(std::expected) == 2 * sizeof(int) + alignof(std::expected)); +static_assert(sizeof(std::expected) == sizeof(B) + alignof(std::expected)); +static_assert(sizeof(std::expected) == 2 * sizeof(int) + alignof(std::expected)); +static_assert(sizeof(std::expected) == 2 * sizeof(int) + alignof(std::expected)); +static_assert(sizeof(std::expected) == sizeof(B) + alignof(std::expected)); +static_assert(sizeof(std::expected) == sizeof(B) + alignof(std::expected)); diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/or_else.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/or_else.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.expected/or_else.mandates.verify.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr auto or_else(F&& f) &; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) const &; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) &&; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) const &&; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +#include +#include + +struct NotSameAsInt {}; + +int lval_return_not_std_expected(int&) { return 0; } +int clval_return_not_std_expected(const int&) { return 0; } +int rval_return_not_std_expected(int&&) { return 0; } +int crval_return_not_std_expected(const int&&) { return 0; } + +std::expected lval_error_type_not_same_as_int(int&) { return {}; } +std::expected clval_error_type_not_same_as_int(const int&) { return {}; } +std::expected rval_error_type_not_same_as_int(int&&) { return {}; } +std::expected crval_error_type_not_same_as_int(const int&&) { return {}; } + +// clang-format off +void test() { + // Test & overload + { + // G is not a specialization of std::expected + { + std::expected f1(std::unexpected(1)); + f1.or_else(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1(std::unexpected(1)); + f1.or_else(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must have the same value_type as this expected}} + } + } + + // Test const& overload + { + // G is not a specialization of std::expected + { + const std::expected f1(std::unexpected(1)); + f1.or_else(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1(std::unexpected(1)); + f1.or_else(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(const int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must have the same value_type as this expected}} + } + } + + // Test && overload + { + // G is not a specialization of std::expected + { + std::expected f1(std::unexpected(1)); + std::move(f1).or_else(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1(std::unexpected(1)); + std::move(f1).or_else(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must have the same value_type as this expected}} + } + } + + // Test const&& overload + { + // G is not a specialization of std::expected + { + const std::expected f1(std::unexpected(1)); + std::move(f1).or_else(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1(std::unexpected(1)); + std::move(f1).or_else(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(const int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must have the same value_type as this expected}} + } + } +} +// clang-format on diff --git a/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.expected/transform_error.mandates.verify.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates + +// template constexpr auto transform_error(F&& f) &; +// template constexpr auto transform_error(F&& f) const &; +// +// Let G be remove_cv_t> +// G is a valid template argument for unexpected ([expected.un.general]) and the declaration +// G g(invoke(std::forward(f), error())); is well-formed. + +// template constexpr auto transform_error(F&& f) &&; +// template constexpr auto transform_error(F&& f) const &&; +// +// Let G be remove_cv_t>. +// G is a valid template argument for unexpected ([expected.un.general]) and the declaration +// G g(invoke(std::forward(f), std::move(error()))); is well-formed. + +#include +#include + +static int val; + +template +std::unexpected return_unexpected(T) { + return std::unexpected(1); +} + +template +int& return_no_object(T) { + return val; +} + +// clang-format off +void test() { + + // Test & overload + { + std::expected e; + e.transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}[expected.object.general] A program that instantiates the definition of template expected for {{.*}} is ill-formed.}} + + e.transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}[expected.object.general] A program that instantiates the definition of template expected for {{.*}} is ill-formed.}} + } + + // Test const& overload + { + const std::expected e; + e.transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + e.transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } + + // Test && overload + { + std::expected e; + std::move(e).transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + std::move(e).transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } + + // Test const&& overload + { + const std::expected e; + std::move(e).transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + std::move(e).transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* 2 {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } +} +// clang-format on diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/and_then.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/and_then.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.void/and_then.mandates.verify.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr auto and_then(F&& f) &; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) const &; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) &&; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto and_then(F&& f) const &&; +// Mandates: +// Let U be std::remove_cvref_t> +// U is a specialization of std::expected and std::is_same_v is true + +#include +#include + +struct NotSameAsInt {}; + +int lval_return_not_std_expected(void) { return 0; } +int clval_return_not_std_expected(void) { return 0; } +int rval_return_not_std_expected(void) { return 0; } +int crval_return_not_std_expected(void) { return 0; } + +std::expected lval_error_type_not_same_as_int(void) { return {}; } +std::expected clval_error_type_not_same_as_int(void) { return {}; } +std::expected rval_error_type_not_same_as_int(void) { return {}; } +std::expected crval_error_type_not_same_as_int(void) { return {}; } + +// clang-format off +void test() { + // Test & overload + { + // U is not a specialization of std::expected + { + std::expected f1; + f1.and_then(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1; + f1.and_then(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)()>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must have the same error_type as this expected}} + } + } + + // Test const& overload + { + // U is not a specialization of std::expected + { + const std::expected f1; + f1.and_then(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1; + f1.and_then(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)()>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must have the same error_type as this expected}} + } + } + + // Test && overload + { + // U is not a specialization of std::expected + { + std::expected f1; + std::move(f1).and_then(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + std::expected f1; + std::move(f1).and_then(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)()>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must have the same error_type as this expected}} + } + } + + // Test const&& overload + { + // U is not a specialization of std::expected + { + const std::expected f1; + std::move(f1).and_then(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::and_then' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + // expected-error-re@*:* {{no matching constructor for initialization of{{.*}}}} + } + + // !std::is_same_v + { + const std::expected f1; + std::move(f1).and_then(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::and_then (&)()>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f() must have the same error_type as this expected}} + } + } +} +// clang-format on diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/error_or.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/error_or.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.void/error_or.mandates.verify.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr E error_or(G&&) const &; +// Mandates: is_copy_constructible_v is true and is_convertible_v is true. + +// template constexpr E error_or(G&&) &&; +// Mandates: is_move_constructible_v is true and is_convertible_v is true. + +#include +#include + +struct NonCopyable { + NonCopyable(int) {} + NonCopyable(const NonCopyable&) = delete; +}; + +struct NonMovable { + NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; + +struct NotConvertibleFromInt {}; + +// clang-format off +void test() { + // const & overload + // !is_copy_constructible_v, + { + const std::expected f1(std::unexpect, 0); + f1.error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}error_type has to be copy constructible}} + // expected-error-re@*:* {{call to deleted constructor of{{.*}}}} + } + + // const & overload + // !is_convertible_v + { + const std::expected f1(std::unexpect, NotConvertibleFromInt{}); + f1.error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to error_type}} + // expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}} + } + + // && overload + // !is_move_constructible_v, + { + std::expected f1(std::unexpect, 0); + std::move(f1).error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}error_type has to be move constructible}} + // expected-error-re@*:* {{call to deleted constructor of{{.*}}}} + } + + // && overload + // !is_convertible_v + { + std::expected f1(std::unexpect, NotConvertibleFromInt{}); + std::move(f1).error_or(5); // expected-note{{in instantiation of function template specialization 'std::expected::error_or' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}argument has to be convertible to error_type}} + // expected-error-re@*:* {{no viable conversion from returned value of type{{.*}}}} + } +} +// clang-format on 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 --- 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 @@ -16,4 +16,16 @@ struct Empty {}; +struct A { + int x_; + int y_; +}; + +struct B : public A { + int z_; + virtual ~B() = default; +}; + static_assert(sizeof(std::expected) == sizeof(bool)); +static_assert(sizeof(std::expected) == 2 * sizeof(int) + alignof(std::expected)); +static_assert(sizeof(std::expected) == sizeof(B) + alignof(std::expected)); diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/or_else.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/or_else.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.void/or_else.mandates.verify.cpp @@ -0,0 +1,121 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates +// template constexpr auto or_else(F&& f) &; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) const &; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) &&; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +// template constexpr auto or_else(F&& f) const &&; +// Mandates: +// Let G be std::remove_cvref_t> +// G is a specialization of std::expected and std::is_same_v is true + +#include +#include + +struct NotSameAsInt {}; + +int lval_return_not_std_expected(int&) { return 0; } +int clval_return_not_std_expected(const int&) { return 0; } +int rval_return_not_std_expected(int&&) { return 0; } +int crval_return_not_std_expected(const int&&) { return 0; } + +std::expected lval_error_type_not_same_as_int(int&) { return {}; } +std::expected clval_error_type_not_same_as_int(const int&) { return {}; } +std::expected rval_error_type_not_same_as_int(int&&) { return {}; } +std::expected crval_error_type_not_same_as_int(const int&&) { return {}; } + +// clang-format off +void test() { + // Test & overload + { + // G is not a specialization of std::expected + { + std::expected f1(std::unexpected(1)); + f1.or_else(lval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + } + + // !std::is_same_v + { + std::expected f1(std::unexpected(1)); + f1.or_else(lval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must have the same value_type as this expected}} + } + } + + // Test const& overload + { + // G is not a specialization of std::expected + { + const std::expected f1(std::unexpected(1)); + f1.or_else(clval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + } + + // !std::is_same_v + { + const std::expected f1(std::unexpected(1)); + f1.or_else(clval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(const int &)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(error()) must have the same value_type as this expected}} + } + } + + // Test && overload + { + // G is not a specialization of std::expected + { + std::expected f1(std::unexpected(1)); + std::move(f1).or_else(rval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + } + + // !std::is_same_v + { + std::expected f1(std::unexpected(1)); + std::move(f1).or_else(rval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must have the same value_type as this expected}} + } + } + + // Test const&& overload + { + // G is not a specialization of std::expected + { + const std::expected f1(std::unexpected(1)); + std::move(f1).or_else(crval_return_not_std_expected); // expected-note{{in instantiation of function template specialization 'std::expected::or_else' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must be a specialization of std::expected}} + // expected-error-re@*:* {{{{.*}}cannot be used prior to '::' because it has no members}} + } + + // !std::is_same_v + { + const std::expected f1(std::unexpected(1)); + std::move(f1).or_else(crval_error_type_not_same_as_int); // expected-note{{in instantiation of function template specialization 'std::expected::or_else (&)(const int &&)>' requested here}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(error())) must have the same value_type as this expected}} + } + } +} +// clang-format on diff --git a/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp b/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/expected/expected.void/transform_error.mandates.verify.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// Test the mandates + +// template constexpr auto transform_error(F&& f) &; +// template constexpr auto transform_error(F&& f) const &; +// +// Let G be remove_cv_t> +// G is a valid template argument for unexpected ([expected.un.general]) and the declaration +// G g(invoke(std::forward(f), error())); is well-formed. + +// template constexpr auto transform_error(F&& f) &&; +// template constexpr auto transform_error(F&& f) const &&; +// +// Let G be remove_cv_t>. +// G is a valid template argument for unexpected ([expected.un.general]) and the declaration +// G g(invoke(std::forward(f), std::move(error()))); is well-formed. + +#include +#include + +static int val; + +template +std::unexpected return_unexpected(T) { + return std::unexpected(1); +} + +template +int& return_no_object(T) { + return val; +} + +// clang-format off +void test() { + + // Test & overload + { + std::expected e; + e.transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}A program that instantiates expected with a E that is not a valid argument for unexpected is ill-formed}} + + e.transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}A program that instantiates expected with a E that is not a valid argument for unexpected is ill-formed}} + } + + // Test const& overload + { + const std::expected e; + e.transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + e.transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } + + // Test && overload + { + std::expected e; + std::move(e).transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + std::move(e).transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } + + // Test const&& overload + { + const std::expected e; + std::move(e).transform_error(return_unexpected); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + std::move(e).transform_error(return_no_object); // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of {{.*}} must be a valid template argument for unexpected}} + // expected-error-re@*:* {{{{(excess elements in struct initializer|no matching constructor for initialization of)}}{{.*}}}} + } +} +// clang-format on diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp @@ -16,7 +16,7 @@ // Test the feature test macros defined by /* Constant Value - __cpp_lib_expected 202202L [C++2b] + __cpp_lib_expected 202211L [C++2b] */ #include @@ -51,8 +51,8 @@ # ifndef __cpp_lib_expected # error "__cpp_lib_expected should be defined in c++2b" # endif -# if __cpp_lib_expected != 202202L -# error "__cpp_lib_expected should have the value 202202L in c++2b" +# if __cpp_lib_expected != 202211L +# error "__cpp_lib_expected should have the value 202211L in c++2b" # endif #endif // TEST_STD_VER > 20 diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp @@ -76,7 +76,7 @@ __cpp_lib_exchange_function 201304L [C++14] __cpp_lib_execution 201603L [C++17] 201902L [C++20] - __cpp_lib_expected 202202L [C++2b] + __cpp_lib_expected 202211L [C++2b] __cpp_lib_filesystem 201703L [C++17] __cpp_lib_format 202106L [C++20] __cpp_lib_format_ranges 202207L [C++2b] @@ -4161,8 +4161,8 @@ # ifndef __cpp_lib_expected # error "__cpp_lib_expected should be defined in c++2b" # endif -# if __cpp_lib_expected != 202202L -# error "__cpp_lib_expected should have the value 202202L in c++2b" +# if __cpp_lib_expected != 202211L +# error "__cpp_lib_expected should have the value 202211L in c++2b" # endif # if !defined(_LIBCPP_AVAILABILITY_HAS_NO_FILESYSTEM) 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,288 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 +#include +#include +#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 UnexpectedLVal { + 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 UnexpectedCLVal { + 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 UnexpectedRVal { + 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 UnexpectedCRVal { + 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 UnexpectedRefQual { + 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 UnexpectedCRefQual { + 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 UnexpectedRVRefQual { + 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 UnexpectedRVCRefQual { + 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 NonCopyable { + constexpr NonCopyable(int) {} + NonCopyable(const NonCopyable&) = delete; +}; + +struct NonMovable { + constexpr NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; + +struct NonConst { + std::expected non_const() { return 1; } +}; + +template +concept has_and_then = requires(E&& e, F&& f) { + {std::forward(e).and_then(std::forward(f))}; +}; + +static_assert( has_and_then&, std::expected(int&)>); +static_assert(!has_and_then&, std::expected(int&)>); +static_assert( has_and_then&, std::expected(const int&)>); +static_assert(!has_and_then&, std::expected(const int&)>); +static_assert( has_and_then&&, std::expected(int)>); +static_assert(!has_and_then&&, std::expected(int)>); +static_assert( has_and_then&&, std::expected(const int)>); +static_assert(!has_and_then&&, std::expected(const int)>); + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_and_then>&, int()>); +static_assert(!has_and_then>&&, int()>); + +// clang-format off +constexpr void test_val_types() { + // Test & overload + { + // Without &qualifier on F'soperator() + { + std::expected e{0}; + std::same_as> decltype(auto) val = e.and_then(LVal{}); + assert(val == 1); + assert(e.and_then(UnexpectedLVal{}).error() == 5); + } + + // With & qualifier on F's operator() + { + std::expected e{0}; + RefQual l{}; + std::same_as> decltype(auto) val = e.and_then(l); + assert(val == 1); + UnexpectedRefQual nl{}; + assert(e.and_then(nl).error() == 5); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::expected e{0}; + std::same_as> decltype(auto) val = e.and_then(CLVal{}); + assert(val == 1); + assert(e.and_then(UnexpectedCLVal{}).error() == 5); + } + + // With & qualifier on F's operator() + { + const std::expected e{0}; + const CRefQual l{}; + std::same_as> decltype(auto) val = e.and_then(l); + assert(val == 1); + const UnexpectedCRefQual nl{}; + assert(e.and_then(nl).error() == 5); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::expected e{0}; + std::same_as> decltype(auto) val = std::move(e).and_then(RVal{}); + assert(val == 1); + assert(std::move(e).and_then(UnexpectedRVal{}).error() == 5); + } + + // With & qualifier on F's operator() + { + std::expected e{0}; + std::same_as> decltype(auto) val = std::move(e).and_then(RVRefQual{}); + assert(val == 1); + assert(e.and_then(UnexpectedRVRefQual{}).error() == 5); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::expected e{0}; + std::same_as> decltype(auto) val = std::move(e).and_then(CRVal{}); + assert(val == 1); + assert(std::move(e).and_then(UnexpectedCRVal{}).error() == 5); + } + + // With & qualifier on F's operator() + { + const std::expected e{0}; + const RVCRefQual l{}; + std::same_as> decltype(auto) val = std::move(e).and_then(std::move(l)); + assert(val == 1); + const UnexpectedRVCRefQual nl{}; + assert(std::move(e).and_then(std::move(nl)).error() == 5); + } + } +} +// clang-format on + +// 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); + + 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,202 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#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; } +}; + +template +concept has_or_else = + requires(E&& e, F&& f) { + { std::forward(e).or_else(std::forward(f)) }; + }; + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_or_else, int>&, int()>); +static_assert(!has_or_else, int>&&, int()>); + +// clang-format off +constexpr void test_val_types() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = e.or_else(LVal{}); + assert(val == 1); + } + + // With & qualifier on F's operator + { + std::expected e(std::unexpected(0)); + RefQual l{}; + std::same_as> decltype(auto) val = e.or_else(l); + assert(val == 1); + } + } + + // Test const& overload + { + // Without const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = e.or_else(CLVal{}); + assert(val == 1); + } + + // With const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const CRefQual l{}; + std::same_as> decltype(auto) val = e.or_else(l); + assert(val == 1); + } + } + + // Test && overload + { + // Without && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).or_else(RVal{}); + assert(val == 1); + } + + // With && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).or_else(RVRefQual{}); + assert(val == 1); + } + } + + // Test const&& overload + { + // Without const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).or_else(CRVal{}); + assert(val == 1); + } + + // With const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const RVCRefQual l{}; + std::same_as> decltype(auto) val = std::move(e).or_else(std::move(l)); + assert(val == 1); + } + } +} +// clang-format on + +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); + 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,251 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`, +// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to +// avoid this issue. + +// + +// 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 +#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 NonCopy { + int value; + constexpr explicit NonCopy(int val) : value(val) {} + NonCopy(const NonCopy&) = delete; +}; + +struct NonConst { + int non_const() { return 1; } +}; + +template +concept has_transform = + requires(E&& e, F&& f) { + { std::forward(e).transform(std::forward(f)) }; + }; + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_transform>&, int()>); +static_assert(!has_transform>&&, int()>); + +// clang-format off +constexpr void test_val_types() { + // Test & overload + { + // Without &qualifier on F'soperator() + { + std::expected e(0); + std::same_as> decltype(auto) val = e.transform(LVal{}); + assert(val == 1); + } + + // With & qualifier on F's operator() + { + std::expected e(0); + RefQual l{}; + std::same_as> decltype(auto) val = e.transform(l); + assert(val == 1); + } + } + + // Test const& overload + { + // Without & qualifier on F's operator() + { + const std::expected e(0); + std::same_as> decltype(auto) val = e.transform(CLVal{}); + assert(val == 1); + } + + // With & qualifier on F's operator() + { + const std::expected e(0); + const CRefQual l{}; + std::same_as> decltype(auto) val = e.transform(l); + assert(val == 1); + } + } + + // Test && overload + { + // Without & qualifier on F's operator() + { + std::expected e(0); + std::same_as> decltype(auto) val = std::move(e).transform(RVal{}); + assert(val == 1); + } + + // With & qualifier on F's operator() + { + std::expected e(0); + std::same_as> decltype(auto) val = std::move(e).transform(RVRefQual{}); + assert(val == 1); + } + } + + // Test const&& overload + { + // Without & qualifier on F's operator() + { + const std::expected e(0); + std::same_as> decltype(auto) val = std::move(e).transform(CRVal{}); + assert(val == 1); + } + + // With & qualifier on F's operator() + { + const std::expected e(0); + const RVCRefQual l{}; + std::same_as> decltype(auto) val = e.transform(std::move(l)); + assert(val == 1); + } + } +} +// clang-format on + +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_direct_non_list_init() { + auto xform = [](int i) { return NonCopy(i); }; + std::expected e(2); + std::expected n = e.transform(xform); + assert(n.value().value == 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_sfinae(); + test_val_types(); + test_direct_non_list_init(); + 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,221 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`, +// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to +// avoid this issue. + +// + +// 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 +#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 NonCopy { + int value; + constexpr explicit NonCopy(int val) : value(val) {} + NonCopy(const NonCopy&) = delete; +}; + +struct NonConst { + int non_const() { return 1; } +}; + +template +concept has_transform_error = + requires(E&& e, F&& f) { + { std::forward(e).transform_error(std::forward(f)) }; + }; + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_transform_error, int>&, int()>); +static_assert(!has_transform_error, int>&&, int()>); + +// clang-format off +constexpr void test_val_types() { + // Test & overload + { + // Without & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = e.transform_error(LVal{}); + assert(val.error() == 1); + } + + // With & qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + RefQual l{}; + std::same_as> decltype(auto) val = e.transform_error(l); + assert(val.error() == 1); + } + } + + // Test const& overload + { + // Without const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = e.transform_error(CLVal{}); + assert(val.error() == 1); + } + + // With const& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const CRefQual l{}; + std::same_as> decltype(auto) val = e.transform_error(l); + assert(val.error() == 1); + } + } + + // Test && overload + { + // Without && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).transform_error(RVal{}); + assert(val.error() == 1); + } + + // With && qualifier on F's operator() + { + std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).transform_error(RVRefQual{}); + assert(val.error() == 1); + } + } + + // Test const&& overload + { + // Without const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + std::same_as> decltype(auto) val = std::move(e).transform_error(CRVal{}); + assert(val.error() == 1); + } + + // With const&& qualifier on F's operator() + { + const std::expected e(std::unexpected(0)); + const RVCRefQual l{}; + std::same_as> decltype(auto) val = std::move(e).transform_error(std::move(l)); + assert(val.error() == 1); + } + } +} +// clang-format on + +// check unex member is direct-non-list-initialized with invoke(std::forward(f), error()) +constexpr void test_direct_non_list_init() { + auto xform = [](int i) { return NonCopy(i); }; + std::expected e(std::unexpected(2)); + std::expected n = e.transform_error(xform); + assert(n.error().value == 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); + + 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_sfinae(); + test_val_types(); + test_direct_non_list_init(); + 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,96 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +struct ConstructFromInt { + int value; + constexpr ConstructFromInt(int v) : value(v) {} +}; + +constexpr bool test_default_template_arg() { + // const &, has_value() + { + const std::expected e(5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x.value == 10); + } + + // const &, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x.value == 5); + } + + // &&, has_value() + { + const std::expected e(5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x.value == 10); + } + + // &&, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x.value == 5); + } + + return true; +} + +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()); + test_default_template_arg(); + static_assert(test_default_template_arg()); + + return 0; +} 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,149 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 +#include +#include +#include +#include + +struct NonCopyable { + constexpr NonCopyable(int) {} + NonCopyable(const NonCopyable&) = delete; +}; + +struct NonMovable { + constexpr NonMovable(int) {} + NonMovable(NonMovable&&) = delete; +}; + +template +concept has_and_then = + requires(E&& e, F&& f) { + { std::forward(e).and_then(std::forward(f)) }; + }; + +std::expected return_int() { return {}; } +std::expected return_noncopyable() { return {}; } +std::expected return_nonmovable() { return {}; } + +static_assert(has_and_then&, decltype(return_int)>); +static_assert(!has_and_then&, decltype(return_noncopyable)>); +static_assert(has_and_then&, decltype(return_int)>); +static_assert(!has_and_then&, decltype(return_noncopyable)>); +static_assert(has_and_then&&, decltype(return_int)>); +static_assert(!has_and_then&&, decltype(return_nonmovable)>); +static_assert(has_and_then&&, decltype(return_int)>); +static_assert(!has_and_then&&, decltype(return_nonmovable)>); + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_and_then>&, int()>); +static_assert(!has_and_then>&&, int()>); + +constexpr void test_val_types() { + // Test & overload + { + auto l = [] -> std::expected { return 2; }; + std::expected v; + std::same_as> decltype(auto) val = v.and_then(l); + assert(val == 2); + } + + // Test const& overload + { + auto l = [] -> std::expected { return 2; }; + const std::expected v; + assert(v.and_then(l).value() == 2); + static_assert(std::is_same_v< decltype(v.and_then(l)), std::expected>); + } + + // Test && overload + { + auto l = [] -> std::expected { return 2; }; + std::expected v; + std::same_as> decltype(auto) val = std::move(v).and_then(l); + assert(val == 2); + } + + // Test const&& overload + { + auto l = [] -> std::expected { return 2; }; + const std::expected v; + std::same_as> decltype(auto) val = std::move(v).and_then(l); + assert(val == 2); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto f = [] -> std::expected { + assert(false); + return 0; + }; + std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = v.and_then(f); + assert(val.error() == 2); + } + + // Test const& overload + { + auto f = [] -> std::expected { + assert(false); + return 0; + }; + const std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = v.and_then(f); + assert(val.error() == 2); + } + + // Test && overload + { + auto f = [] -> std::expected { + assert(false); + return 0; + }; + std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = std::move(v).and_then(f); + assert(val.error() == 2); + } + + // Test const&& overload + { + auto f = [] -> std::expected { + assert(false); + return 0; + }; + const std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = std::move(v).and_then(f); + assert(val.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,104 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#include + +constexpr void test_val_types() { + // Test & overload + { + auto l = [](auto) -> std::expected { return {}; }; + std::expected v(std::unexpected(1)); + std::same_as> decltype(auto) val = v.or_else(l); + assert(val.has_value()); + } + + // Test const& overload + { + auto l = [](auto) -> std::expected { return {}; }; + const std::expected v(std::unexpected(1)); + std::same_as> decltype(auto) val = v.or_else(l); + assert(val.has_value()); + } + + // Test && overload + { + auto l = [](auto) -> std::expected { return {}; }; + std::expected v(std::unexpected(1)); + std::same_as> decltype(auto) val = std::move(v).or_else(l); + assert(val.has_value()); + } + + // Test const&& overload + { + auto l = [](auto) -> std::expected { return {}; }; + const std::expected v(std::unexpected(1)); + std::same_as> decltype(auto) val = std::move(v).or_else(l); + assert(val.has_value()); + } +} + +constexpr void test_fail() { + auto never_called = [](auto) -> std::expected { + assert(false); + return std::expected(std::unexpected(5)); + }; + + // Test & overload + { + std::expected v; + std::same_as> decltype(auto) val = v.or_else(never_called); + assert(val.has_value()); + } + + // Test const& overload + { + const std::expected v; + std::same_as> decltype(auto) val = v.or_else(never_called); + assert(val.has_value()); + } + + // Test && overload + { + std::expected v; + std::same_as> decltype(auto) val = std::move(v).or_else(never_called); + assert(val.has_value()); + } + + // Test const&& overload + { + const std::expected v; + std::same_as> decltype(auto) val = std::move(v).or_else(never_called); + assert(val.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,126 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 +#include +#include +#include + +template +concept has_transform = + requires(E&& e, F&& f) { + { std::forward(e).transform(std::forward(f)) }; + }; + +// [LWG 3877] https://cplusplus.github.io/LWG/issue3877, check constraint failing but not compile error inside the function body. +static_assert(!has_transform>&, int()>); +static_assert(!has_transform>&&, int()>); + +constexpr void test_val_types() { + // Test & overload + { + auto l = [] -> int { return 1; }; + std::expected v; + std::same_as> decltype(auto) val = v.transform(l); + assert(val == 1); + } + + // Test const& overload + { + auto l = [] -> int { return 1; }; + const std::expected v; + std::same_as> decltype(auto) val = v.transform(l); + assert(val == 1); + } + + // Test && overload + { + auto l = [] -> int { return 1; }; + std::expected v; + std::same_as> decltype(auto) val = std::move(v).transform(l); + assert(val == 1); + } + + // Test const&& overload + { + auto l = [] -> int { return 1; }; + const std::expected v; + std::same_as> decltype(auto) val = std::move(v).transform(l); + assert(val == 1); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto l = [] -> int { + assert(false); + return 0; + }; + std::expected v(std::unexpected(5)); + std::same_as> decltype(auto) val = v.transform(l); + assert(val.error() == 5); + } + + // Test const& overload + { + auto l = [] -> int { + assert(false); + return 0; + }; + const std::expected v(std::unexpected(5)); + std::same_as> decltype(auto) val = v.transform(l); + assert(val.error() == 5); + } + + // Test && overload + { + auto l = [] -> int { + assert(false); + return 0; + }; + std::expected v(std::unexpected(5)); + std::same_as> decltype(auto) val = std::move(v).transform(l); + assert(val.error() == 5); + } + + // Test const&& overload + { + auto l = [] -> int { + assert(false); + return 0; + }; + const std::expected v(std::unexpected(5)); + std::same_as> decltype(auto) val = std::move(v).transform(l); + assert(val.error() == 5); + } +} + +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_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,134 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// GCC has a issue for `Guaranteed copy elision for potentially-overlapping non-static data members`, +// please refer to: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108333, but we have a workaround to +// avoid this issue. + +// + +// 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 +#include +#include + +struct NonCopy { + int value; + constexpr explicit NonCopy(int val) : value(val) {} + NonCopy(const NonCopy&) = delete; +}; + +constexpr void test_val_types() { + // Test & overload + { + auto l = [](auto) -> int { return 1; }; + std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = v.transform_error(l); + assert(val.error() == 1); + } + + // Test const& overload + { + auto l = [](auto) -> int { return 1; }; + const std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = v.transform_error(l); + assert(val.error() == 1); + } + + // Test && overload + { + auto l = [](auto) -> int { return 1; }; + std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = std::move(v).transform_error(l); + assert(val.error() == 1); + } + + // Test const&& overload + { + auto l = [](auto) -> int { return 1; }; + const std::expected v(std::unexpected(2)); + std::same_as> decltype(auto) val = std::move(v).transform_error(l); + assert(val.error() == 1); + } +} + +constexpr void test_fail() { + // Test & overload + { + auto l = [](auto) -> int { + assert(false); + return 0; + }; + std::expected v; + std::same_as> decltype(auto) val = v.transform_error(l); + assert(val.has_value()); + } + + // Test const& overload + { + auto l = [](auto) -> int { + assert(false); + return 0; + }; + const std::expected v; + std::same_as> decltype(auto) val = v.transform_error(l); + assert(val.has_value()); + } + + // Test && overload + { + auto l = [](auto) -> int { + assert(false); + return 0; + }; + std::expected v; + std::same_as> decltype(auto) val = std::move(v).transform_error(l); + assert(val.has_value()); + } + + // Test const&& overload + { + auto l = [](auto) -> int { + assert(false); + return 0; + }; + const std::expected v; + std::same_as> decltype(auto) val = std::move(v).transform_error(l); + assert(val.has_value()); + } +} + +// check unex member is direct-non-list-initialized with invoke(std::forward(f)) +constexpr void test_direct_non_list_init() { + auto x = [](int i) { return NonCopy(i); }; + std::expected v(std::unexpected(2)); + std::expected nv = v.transform_error(x); + assert(nv.error().value == 2); +} + +constexpr bool test() { + test_fail(); + test_val_types(); + test_direct_non_list_init(); + 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,96 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +struct ConstructFromInt { + int value; + constexpr ConstructFromInt(int v) : value(v) {} +}; + +constexpr bool test_default_template_arg() { + // const &, has_value() + { + const std::expected e; + std::same_as decltype(auto) x = e.error_or(10); + assert(x.value == 10); + } + + // const &, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = e.error_or(10); + assert(x.value == 5); + } + + // &&, has_value() + { + const std::expected e; + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x.value == 10); + } + + // &&, !has_value() + { + const std::expected e(std::unexpect, 5); + std::same_as decltype(auto) x = std::move(e).error_or(10); + assert(x.value == 5); + } + + return true; +} + +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()); + test_default_template_arg(); + static_assert(test_default_template_arg()); + + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -306,7 +306,7 @@ "unimplemented": True, }, { "name": "__cpp_lib_expected", - "values": { "c++2b": 202202 }, + "values": { "c++2b": 202211 }, "headers": ["expected"], }, { "name": "__cpp_lib_filesystem",