diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -14,7 +14,7 @@ "`3925 `__","Concept ``formattable``'s definition is incorrect","Varna June 2023","|Complete|","17.0","|format|" "`3927 `__","Unclear preconditions for ``operator[]`` for sequence containers","Varna June 2023","|Nothing To Do|","","" "`3935 `__","``template constexpr complex& operator=(const complex&)`` has no specification","Varna June 2023","|Complete|","3.4","" -"`3938 `__","Cannot use ``std::expected`` monadic ops with move-only ``error_type``","Varna June 2023","","","" +"`3938 `__","Cannot use ``std::expected`` monadic ops with move-only ``error_type``","Varna June 2023","|Complete|","17.0","" "`3940 `__","``std::expected::value()`` also needs ``E`` to be copy constructible","Varna June 2023","","","" "","","","","","" "`3343 `__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0","" 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 @@ -644,7 +644,7 @@ 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 std::invoke(std::forward<_Func>(__f), **this); } return _Up(unexpect, error()); } @@ -657,7 +657,7 @@ 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 std::invoke(std::forward<_Func>(__f), **this); } return _Up(unexpect, error()); } @@ -671,7 +671,7 @@ 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 std::invoke(std::forward<_Func>(__f), std::move(**this)); } return _Up(unexpect, std::move(error())); } @@ -685,7 +685,7 @@ 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 std::invoke(std::forward<_Func>(__f), std::move(**this)); } return _Up(unexpect, std::move(error())); } @@ -698,7 +698,7 @@ 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 _Gp(in_place, **this); } return std::invoke(std::forward<_Func>(__f), error()); } @@ -711,7 +711,7 @@ 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 _Gp(in_place, **this); } return std::invoke(std::forward<_Func>(__f), error()); } @@ -725,7 +725,7 @@ 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 _Gp(in_place, std::move(**this)); } return std::invoke(std::forward<_Func>(__f), std::move(error())); } @@ -739,7 +739,7 @@ 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 _Gp(in_place, std::move(**this)); } return std::invoke(std::forward<_Func>(__f), std::move(error())); } @@ -752,9 +752,9 @@ 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()); + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), **this); } else { - std::invoke(std::forward<_Func>(__f), value()); + std::invoke(std::forward<_Func>(__f), **this); return expected<_Up, _Err>(); } } @@ -767,9 +767,9 @@ 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()); + return expected<_Up, _Err>(__expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), **this); } else { - std::invoke(std::forward<_Func>(__f), value()); + std::invoke(std::forward<_Func>(__f), **this); return expected<_Up, _Err>(); } } @@ -783,9 +783,9 @@ } if constexpr (!is_void_v<_Up>) { return expected<_Up, _Err>( - __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value())); + __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(**this)); } else { - std::invoke(std::forward<_Func>(__f), std::move(value())); + std::invoke(std::forward<_Func>(__f), std::move(**this)); return expected<_Up, _Err>(); } } @@ -799,9 +799,9 @@ } if constexpr (!is_void_v<_Up>) { return expected<_Up, _Err>( - __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(value())); + __expected_construct_in_place_from_invoke_tag{}, std::forward<_Func>(__f), std::move(**this)); } else { - std::invoke(std::forward<_Func>(__f), std::move(value())); + std::invoke(std::forward<_Func>(__f), std::move(**this)); return expected<_Up, _Err>(); } } @@ -813,7 +813,7 @@ 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>(in_place, **this); } return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); } @@ -825,7 +825,7 @@ 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>(in_place, **this); } return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); } @@ -837,7 +837,7 @@ 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>(in_place, std::move(**this)); } return expected<_Tp, _Gp>( __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); @@ -850,7 +850,7 @@ 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>(in_place, std::move(**this)); } return expected<_Tp, _Gp>( __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); 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 --- 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 @@ -22,6 +22,8 @@ #include #include +#include "../../types.h" + struct LVal { constexpr std::expected operator()(int&) { return 1; } std::expected operator()(const int&) = delete; @@ -153,6 +155,7 @@ {std::forward(e).and_then(std::forward(f))}; }; +// clang-format off 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&)>); @@ -166,7 +169,12 @@ static_assert(!has_and_then>&, int()>); static_assert(!has_and_then>&&, int()>); -// clang-format off +// [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. +static_assert(has_and_then&, std::expected(int&)>); +// There are no effects for `const &` overload, because the constraints requires is_constructible_v is true. +static_assert(has_and_then&&, std::expected(int)>); +static_assert(has_and_then&&, std::expected(const int)>); + constexpr void test_val_types() { // Test & overload { 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 --- 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 @@ -22,6 +22,8 @@ #include #include +#include "../../types.h" + struct LVal { constexpr std::expected operator()(int&) { return 1; } std::expected operator()(const int&) = delete; @@ -83,12 +85,17 @@ requires(E&& e, F&& f) { { std::forward(e).or_else(std::forward(f)) }; }; - +// clang-format off // [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 +// [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. +static_assert(has_or_else&, std::expected(CopyConstructibleErrorType &)>); +static_assert(has_or_else&, std::expected(const CopyConstructibleErrorType &)>); +static_assert(has_or_else&&, std::expected(MoveOnlyErrorType&&)>); +static_assert(has_or_else&&, std::expected(const MoveOnlyErrorType&&)>); + constexpr void test_val_types() { // Test & overload { 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 --- a/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/monadic/transform.pass.cpp @@ -26,6 +26,8 @@ #include #include +#include "../../types.h" + struct LVal { constexpr int operator()(int&) { return 1; } int operator()(const int&) = delete; @@ -98,11 +100,17 @@ { std::forward(e).transform(std::forward(f)) }; }; +// clang-format off // [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 +// [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. +static_assert(has_transform&, int(int&)>); +// There are no effects for `const &` overload, because the constraints requires is_constructible_v is true. +static_assert(has_transform&&, int(int)>); +static_assert(has_transform&&, int(const int)>); + constexpr void test_val_types() { // Test & overload { 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 --- 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 @@ -26,6 +26,8 @@ #include #include +#include "../../types.h" + struct LVal { constexpr int operator()(int&) { return 1; } int operator()(const int&) = delete; @@ -98,11 +100,17 @@ { std::forward(e).transform_error(std::forward(f)) }; }; +// clang-format off // [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 +// [LWG 3983] https://cplusplus.github.io/LWG/issue3938, check std::expected monadic ops well-formed with move-only error_type. +static_assert(has_transform_error&, int(CopyConstructibleErrorType &)>); +static_assert(has_transform_error&, int(const CopyConstructibleErrorType &)>); +static_assert(has_transform_error&&, int(MoveOnlyErrorType&&)>); +static_assert(has_transform_error&&, int(const MoveOnlyErrorType&&)>); + constexpr void test_val_types() { // Test & overload { diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h --- a/libcxx/test/std/utilities/expected/types.h +++ b/libcxx/test/std/utilities/expected/types.h @@ -142,4 +142,18 @@ #endif // TEST_HAS_NO_EXCEPTIONS +struct MoveOnlyErrorType { + constexpr MoveOnlyErrorType(int) {} + MoveOnlyErrorType(MoveOnlyErrorType&&) {} + MoveOnlyErrorType(const MoveOnlyErrorType&&) {} + MoveOnlyErrorType(const MoveOnlyErrorType&) = delete; + MoveOnlyErrorType& operator=(const MoveOnlyErrorType&) = delete; +}; + +struct CopyConstructibleErrorType { + constexpr CopyConstructibleErrorType(int) {} + CopyConstructibleErrorType(CopyConstructibleErrorType&) {} + CopyConstructibleErrorType(const CopyConstructibleErrorType&) {} +}; + #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H