diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv index aaf561982e120..7bf7bc95c8281 100644 --- a/libcxx/docs/Status/Cxx2cIssues.csv +++ b/libcxx/docs/Status/Cxx2cIssues.csv @@ -78,7 +78,7 @@ "","","","","","" "`LWG3216 `__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 `__","" "`LWG3436 `__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 `__","" -"`LWG3886 `__","Monad mo' problems","2024-11 (Wrocław)","","","`#118336 `__","" +"`LWG3886 `__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 `__","" "`LWG3899 `__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 `__","" "`LWG3900 `__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 `__","" "`LWG3918 `__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 `__","" diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index 38a34121040f6..8b3eeebd38ae7 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -555,7 +555,7 @@ class expected : private __expected_base<_Tp, _Err> { is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened : __base(__other.__has_val(), std::move(__other.__union())) {} - template + template > requires(!is_same_v, in_place_t> && !is_same_v> && !is_same_v, unexpect_t> && is_constructible_v<_Tp, _Up> && !__is_std_unexpected>::value && @@ -669,7 +669,7 @@ class expected : private __expected_base<_Tp, _Err> { return *this; } - template + template > _LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(_Up&& __v) requires(!is_same_v> && !__is_std_unexpected>::value && is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> && @@ -887,14 +887,14 @@ class expected : private __expected_base<_Tp, _Err> { return std::move(this->__unex()); } - template + template > _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& { static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible"); static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type"); return this->__has_val() ? this->__val() : static_cast<_Tp>(std::forward<_Up>(__v)); } - template + template > _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && { static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible"); static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type"); diff --git a/libcxx/include/optional b/libcxx/include/optional index 39fcaa2c2ec18..ef1bfd3ec44c0 100644 --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -119,7 +119,7 @@ namespace std { constexpr explicit optional(in_place_t, Args &&...); template constexpr explicit optional(in_place_t, initializer_list, Args &&...); - template + template> constexpr explicit(see-below) optional(U &&); template explicit(see-below) optional(const optional &); // constexpr in C++20 @@ -133,7 +133,7 @@ namespace std { optional &operator=(nullopt_t) noexcept; // constexpr in C++20 constexpr optional &operator=(const optional &); constexpr optional &operator=(optional &&) noexcept(see below); - template optional &operator=(U &&); // constexpr in C++20 + template> optional &operator=(U &&); // constexpr in C++20 template optional &operator=(const optional &); // constexpr in C++20 template optional &operator=(optional &&); // constexpr in C++20 template T& emplace(Args &&...); // constexpr in C++20 @@ -161,8 +161,8 @@ namespace std { constexpr T &value() &; constexpr T &&value() &&; constexpr const T &&value() const &&; - template constexpr T value_or(U &&) const &; - template constexpr T value_or(U &&) &&; + template> constexpr T value_or(U &&) const &; + template> constexpr T value_or(U &&) &&; // [optional.monadic], monadic operations template constexpr auto and_then(F&& f) &; // since C++23 @@ -730,7 +730,8 @@ public: enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {} - template ::template __enable_explicit<_Up>(), int> = 0> + template , + enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {} // LWG2756: conditionally explicit conversion from const optional<_Up>& @@ -771,7 +772,7 @@ public: _LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&) = default; // LWG2756 - template , enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>, _Or<_IsNotSame<__remove_cvref_t<_Up>, value_type>, _Not>>, is_constructible, @@ -919,14 +920,14 @@ public: return std::move(this->__get()); } - template + template > _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& { static_assert(is_copy_constructible_v, "optional::value_or: T must be copy constructible"); static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); return this->has_value() ? this->__get() : static_cast(std::forward<_Up>(__v)); } - template + template > _LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && { static_assert(is_move_constructible_v, "optional::value_or: T must be move constructible"); static_assert(is_convertible_v<_Up, value_type>, "optional::value_or: U must be convertible to T"); diff --git a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp index 807a8af5bb5f6..795fa77e8338c 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp @@ -325,6 +325,44 @@ constexpr bool test() { } } + // Check move constructor selection + { + struct MoveOnlyMulti { + bool used_move1 = false; + bool used_move2 = false; + + constexpr MoveOnlyMulti() = default; + constexpr MoveOnlyMulti(const MoveOnlyMulti&) = delete; + constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&) = delete; + constexpr MoveOnlyMulti& operator=(MoveOnlyMulti&&) { + used_move1 = true; + return *this; + } + constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&&) { + used_move2 = true; + return *this; + }; + constexpr MoveOnlyMulti(MoveOnlyMulti&&) : used_move1(true) {} + constexpr MoveOnlyMulti(const MoveOnlyMulti&&) : used_move2(true) {} + }; + + { + MoveOnlyMulti t{}; + std::expected e1(std::unexpect); + static_assert(std::is_same_v); + e1 = {std::move(t)}; + assert(e1.value().used_move1); + } + { + const MoveOnlyMulti t{}; + std::expected e1(std::unexpect); + static_assert(std::is_same_v); + // _Up = remove_cv_t --> should use MoveOnlyMulti(MoveOnlyMulti&&) + e1 = {std::move(t)}; + assert(e1.value().used_move1); + } + } + return true; } diff --git a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp index 13c0da27bc533..fe664dfc97cfe 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp @@ -80,6 +80,17 @@ struct CopyOnly { friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; } }; +struct MoveOnly2 { + int j; + bool used_move1 = false; + bool used_move2 = false; + + constexpr explicit MoveOnly2(int jj) : j(jj) {} + constexpr MoveOnly2(const MoveOnly2&) = delete; + constexpr MoveOnly2(MoveOnly2&& m) : j(m.j), used_move1(true) {} + constexpr MoveOnly2(const MoveOnly2&& m) : j(m.j), used_move2(true) {} +}; + struct BaseError {}; struct DerivedError : BaseError {}; @@ -164,6 +175,22 @@ constexpr bool test() { assert(e2.has_value()); assert(!e2.value()); // yes, e2 holds "false" since LWG3836 } + + // Check move constructor selection + { + MoveOnly2 t{1}; + std::expected e1(std::move(t)); + assert(e1.has_value()); + assert(e1.value().used_move1 == true); + assert(e1.value().j == 1); + } + { + const MoveOnly2 t2{2}; + std::expected e1(std::move(t2)); + assert(e1.has_value()); + assert(e1.value().used_move2 == true); + assert(e1.value().j == 2); + } return true; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp index a5ee602ab7bce..a90fecfd075fe 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp @@ -59,7 +59,8 @@ constexpr bool explicit_conversion(Input&& in, const Expect& v) static_assert(!std::is_constructible::value, ""); static_assert(!std::is_constructible::value, ""); optional opt(std::forward(in)); - return opt && *opt == static_cast(v); + optional opt2{std::forward(in)}; + return opt && *opt == static_cast(v) && (opt2 && *opt2 == static_cast(v)); } void test_implicit() @@ -83,6 +84,11 @@ void test_implicit() using T = TestTypes::TestType; assert(implicit_conversion(3, T(3))); } + { + using T = TestTypes::TestType; + optional opt({3}); + assert(opt && *opt == static_cast(3)); + } { using O = optional; static_assert(!test_convertible(), ""); diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp index 4f9b6993c6f4f..8c063ae1a799c 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp @@ -40,6 +40,12 @@ struct X {return x.i_ == y.i_;} }; +struct Z { + int i_, j_; + constexpr Z(int i, int j) : i_(i), j_(j) {} + friend constexpr bool operator==(const Z& z1, const Z& z2) { return z1.i_ == z2.i_ && z1.j_ == z2.j_; } +}; + constexpr int test() { { @@ -64,6 +70,16 @@ constexpr int test() assert(std::move(opt).value_or(Y(3)) == 4); assert(!opt); } + { + optional opt; + assert(std::move(opt).value_or({Y(3)}) == 4); + assert(!opt); + } + { + optional opt; + assert((std::move(opt).value_or({2, 3}) == Z{2, 3})); + assert(!opt); + } return 0; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp index cf782f1137876..ec42890a3b995 100644 --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp @@ -75,6 +75,10 @@ int main(int, char**) const optional opt; assert(opt.value_or(Y(3)) == 4); } + { + const optional opt; + assert(opt.value_or({Y(3)}) == 4); + } return 0; }