diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -47,6 +47,7 @@ - P2505R5 - Monadic operations for ``std::expected`` - P2711R1 - Making Multi-Param Constructors Of views explicit (``join_with_view`` is not done yet) - P2572R1 - ``std::format`` fill character allowances +- LWG3938 - Cannot use ``std::expected`` monadic ops with move-only error_type Improvements and New Features ----------------------------- 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|","Clang 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 @@ -137,6 +137,8 @@ struct NonCopyable { constexpr NonCopyable(int) {} NonCopyable(const NonCopyable&) = delete; + NonCopyable(NonCopyable&&) {} + NonCopyable(const NonCopyable&&) {} }; struct NonMovable { @@ -148,11 +150,17 @@ std::expected non_const() { return 1; } }; +struct MutableLValConstructible { + constexpr MutableLValConstructible(int) {} + MutableLValConstructible(MutableLValConstructible&) {} +}; + template concept has_and_then = requires(E&& e, F&& f) { {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 +174,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 {