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|","18.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 @@ -651,11 +651,11 @@ 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_std_expected<_Up>::value, "The result of f(**this) 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"); + "The result of f(**this) 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), __union_.__val_); } return _Up(unexpect, error()); } @@ -664,11 +664,11 @@ 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_std_expected<_Up>::value, "The result of f(**this) 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"); + "The result of f(**this) 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), __union_.__val_); } return _Up(unexpect, error()); } @@ -678,11 +678,11 @@ _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"); + __is_std_expected<_Up>::value, "The result of f(std::move(**this)) 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"); + "The result of f(std::move(**this)) 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(__union_.__val_)); } return _Up(unexpect, std::move(error())); } @@ -692,11 +692,11 @@ _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"); + __is_std_expected<_Up>::value, "The result of f(std::move(**this)) 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"); + "The result of f(std::move(**this)) 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(__union_.__val_)); } return _Up(unexpect, std::move(error())); } @@ -709,7 +709,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, __union_.__val_); } return std::invoke(std::forward<_Func>(__f), error()); } @@ -722,7 +722,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, __union_.__val_); } return std::invoke(std::forward<_Func>(__f), error()); } @@ -736,7 +736,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(__union_.__val_)); } return std::invoke(std::forward<_Func>(__f), std::move(error())); } @@ -750,7 +750,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(__union_.__val_)); } return std::invoke(std::forward<_Func>(__f), std::move(error())); } @@ -763,9 +763,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), __union_.__val_); } else { - std::invoke(std::forward<_Func>(__f), value()); + std::invoke(std::forward<_Func>(__f), __union_.__val_); return expected<_Up, _Err>(); } } @@ -778,9 +778,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), __union_.__val_); } else { - std::invoke(std::forward<_Func>(__f), value()); + std::invoke(std::forward<_Func>(__f), __union_.__val_); return expected<_Up, _Err>(); } } @@ -794,9 +794,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(__union_.__val_)); } else { - std::invoke(std::forward<_Func>(__f), std::move(value())); + std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_)); return expected<_Up, _Err>(); } } @@ -810,9 +810,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(__union_.__val_)); } else { - std::invoke(std::forward<_Func>(__f), std::move(value())); + std::invoke(std::forward<_Func>(__f), std::move(__union_.__val_)); return expected<_Up, _Err>(); } } @@ -824,7 +824,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, __union_.__val_); } return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); } @@ -836,7 +836,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, __union_.__val_); } return expected<_Tp, _Gp>(__expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), error()); } @@ -848,7 +848,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(__union_.__val_)); } return expected<_Tp, _Gp>( __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); @@ -861,7 +861,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(__union_.__val_)); } return expected<_Tp, _Gp>( __expected_construct_unexpected_from_invoke_tag{}, std::forward<_Func>(__f), std::move(error())); 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 --- 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 @@ -11,22 +11,22 @@ // Test the mandates // template constexpr auto and_then(F&& f) &; // Mandates: -// Let U be std::remove_cvref_t> +// 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> +// 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> +// 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> +// Let U be std::remove_cvref_t> // U is a specialization of std::expected and std::is_same_v is true #include @@ -52,7 +52,7 @@ { 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@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) 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{{.*}}}} } @@ -61,7 +61,7 @@ { 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}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must have the same error_type as this expected}} } } @@ -71,7 +71,7 @@ { 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@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) 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{{.*}}}} } @@ -80,7 +80,7 @@ { 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}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(**this) must have the same error_type as this expected}} } } @@ -91,7 +91,7 @@ { 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@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) 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{{.*}}}} } @@ -100,7 +100,7 @@ { 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}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}} } } @@ -110,7 +110,7 @@ { 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@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) 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{{.*}}}} } @@ -119,7 +119,7 @@ { 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}} + // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}The result of f(std::move(**this)) must have the same error_type as this expected}} } } } 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,11 @@ 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. +// There are no effects for `&` and `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 { @@ -260,9 +267,26 @@ std::move(e).and_then(l); } +constexpr void test_move_only_error_type() { + // Test && + { + std::expected e; + auto l = [](int) { return std::expected{}; }; + std::move(e).and_then(l); + } + + // Test const&& + { + const std::expected e; + auto l = [](const int) { return std::expected{}; }; + std::move(e).and_then(l); + } +} + constexpr bool test() { test_sfinae(); test_val_types(); + test_move_only_error_type(); std::expected e(std::unexpected(1)); const auto& ce = e; 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(MoveOnlyErrorType &)>); +static_assert(has_or_else&, std::expected(const MoveOnlyErrorType &)>); +static_assert(has_or_else&&, std::expected(MoveOnlyErrorType&&)>); +static_assert(has_or_else&&, std::expected(const MoveOnlyErrorType&&)>); + constexpr void test_val_types() { // Test & overload { @@ -175,9 +182,40 @@ std::move(e).or_else(l); } +constexpr void test_move_only_error_type() { + // Test & + { + std::expected e; + auto l = [](MoveOnlyErrorType&) { return std::expected{}; }; + e.or_else(l); + } + + // Test const& + { + const std::expected e; + auto l = [](const MoveOnlyErrorType&) { return std::expected{}; }; + e.or_else(l); + } + + // Test && + { + std::expected e; + auto l = [](MoveOnlyErrorType&&) { return std::expected{}; }; + std::move(e).or_else(l); + } + + // Test const&& + { + const std::expected e; + auto l = [](const MoveOnlyErrorType&&) { return std::expected{}; }; + std::move(e).or_else(l); + } +} + constexpr bool test() { test_sfinae(); test_val_types(); + test_move_only_error_type(); std::expected e(1); const auto& ce = e; 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,16 @@ { 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. +// There are no effects for `&` and `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 { @@ -236,10 +243,27 @@ std::move(ce1).transform(never_called); } +constexpr void test_move_only_error_type() { + // Test && + { + std::expected e; + auto l = [](int) { return 0; }; + std::move(e).transform(l); + } + + // Test const&& + { + const std::expected e; + auto l = [](const int) { return 0; }; + std::move(e).transform(l); + } +} + constexpr bool test() { test_sfinae(); test_val_types(); test_direct_non_list_init(); + test_move_only_error_type(); return true; } 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(MoveOnlyErrorType &)>); +static_assert(has_transform_error&, int(const MoveOnlyErrorType &)>); +static_assert(has_transform_error&&, int(MoveOnlyErrorType&&)>); +static_assert(has_transform_error&&, int(const MoveOnlyErrorType&&)>); + constexpr void test_val_types() { // Test & overload { @@ -206,10 +214,42 @@ std::move(ce1).transform_error(never_called); } +constexpr void test_move_only_error_type() { + // Test & + { + std::expected e; + auto l = [](MoveOnlyErrorType&) { return 0; }; + e.transform_error(l); + } + + // Test const& + { + const std::expected e; + auto l = [](const MoveOnlyErrorType&) { return 0; }; + e.transform_error(l); + } + + // Test && + { + std::expected e; + auto l = [](MoveOnlyErrorType&&) { return 0; }; + std::move(e).transform_error(l); + } + + // Test const&& + { + const std::expected e; + auto l = [](const MoveOnlyErrorType&&) { return 0; }; + std::move(e).transform_error(l); + } +} + constexpr bool test() { test_sfinae(); test_val_types(); test_direct_non_list_init(); + test_move_only_error_type(); + return true; } 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,12 @@ #endif // TEST_HAS_NO_EXCEPTIONS +struct MoveOnlyErrorType { + constexpr MoveOnlyErrorType(int) {} + MoveOnlyErrorType(MoveOnlyErrorType&&) {} + MoveOnlyErrorType(const MoveOnlyErrorType&&) {} + MoveOnlyErrorType(const MoveOnlyErrorType&) = delete; + MoveOnlyErrorType& operator=(const MoveOnlyErrorType&) = delete; +}; + #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H