Skip to content

Commit fb72f0c

Browse files
authored
[libc++] Implement Resolution of LWG 3886 (#155356)
Resolves #118336 - Implement the resolution of [LWG3886](https://cplusplus.github.io/LWG/issue3886) for `optional` and `expected`
1 parent 59b4621 commit fb72f0c

File tree

8 files changed

+106
-14
lines changed

8 files changed

+106
-14
lines changed

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
"","","","","",""
7979
"`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
8080
"`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
81-
"`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","","","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
81+
"`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
8282
"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
8383
"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
8484
"`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""

libcxx/include/__expected/expected.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ class expected : private __expected_base<_Tp, _Err> {
555555
is_nothrow_constructible_v<_Tp, _Up> && is_nothrow_constructible_v<_Err, _OtherErr>) // strengthened
556556
: __base(__other.__has_val(), std::move(__other.__union())) {}
557557

558-
template <class _Up = _Tp>
558+
template <class _Up = remove_cv_t<_Tp>>
559559
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
560560
!is_same_v<remove_cvref_t<_Up>, unexpect_t> && is_constructible_v<_Tp, _Up> &&
561561
!__is_std_unexpected<remove_cvref_t<_Up>>::value &&
@@ -669,7 +669,7 @@ class expected : private __expected_base<_Tp, _Err> {
669669
return *this;
670670
}
671671

672-
template <class _Up = _Tp>
672+
template <class _Up = remove_cv_t<_Tp>>
673673
_LIBCPP_HIDE_FROM_ABI constexpr expected& operator=(_Up&& __v)
674674
requires(!is_same_v<expected, remove_cvref_t<_Up>> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
675675
is_constructible_v<_Tp, _Up> && is_assignable_v<_Tp&, _Up> &&
@@ -887,14 +887,14 @@ class expected : private __expected_base<_Tp, _Err> {
887887
return std::move(this->__unex());
888888
}
889889

890-
template <class _Up>
890+
template <class _Up = remove_cv_t<_Tp>>
891891
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
892892
static_assert(is_copy_constructible_v<_Tp>, "value_type has to be copy constructible");
893893
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");
894894
return this->__has_val() ? this->__val() : static_cast<_Tp>(std::forward<_Up>(__v));
895895
}
896896

897-
template <class _Up>
897+
template <class _Up = remove_cv_t<_Tp>>
898898
_LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
899899
static_assert(is_move_constructible_v<_Tp>, "value_type has to be move constructible");
900900
static_assert(is_convertible_v<_Up, _Tp>, "argument has to be convertible to value_type");

libcxx/include/optional

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ namespace std {
119119
constexpr explicit optional(in_place_t, Args &&...);
120120
template<class U, class... Args>
121121
constexpr explicit optional(in_place_t, initializer_list<U>, Args &&...);
122-
template<class U = T>
122+
template<class U = remove_cv_t<T>>
123123
constexpr explicit(see-below) optional(U &&);
124124
template<class U>
125125
explicit(see-below) optional(const optional<U> &); // constexpr in C++20
@@ -133,7 +133,7 @@ namespace std {
133133
optional &operator=(nullopt_t) noexcept; // constexpr in C++20
134134
constexpr optional &operator=(const optional &);
135135
constexpr optional &operator=(optional &&) noexcept(see below);
136-
template<class U = T> optional &operator=(U &&); // constexpr in C++20
136+
template<class U = remove_cv_t<T>> optional &operator=(U &&); // constexpr in C++20
137137
template<class U> optional &operator=(const optional<U> &); // constexpr in C++20
138138
template<class U> optional &operator=(optional<U> &&); // constexpr in C++20
139139
template<class... Args> T& emplace(Args &&...); // constexpr in C++20
@@ -161,8 +161,8 @@ namespace std {
161161
constexpr T &value() &;
162162
constexpr T &&value() &&;
163163
constexpr const T &&value() const &&;
164-
template<class U> constexpr T value_or(U &&) const &;
165-
template<class U> constexpr T value_or(U &&) &&;
164+
template<class U = remove_cv_t<T>> constexpr T value_or(U &&) const &;
165+
template<class U = remove_cv_t<T>> constexpr T value_or(U &&) &&;
166166
167167
// [optional.monadic], monadic operations
168168
template<class F> constexpr auto and_then(F&& f) &; // since C++23
@@ -730,7 +730,8 @@ public:
730730
enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), int> = 0>
731731
_LIBCPP_HIDE_FROM_ABI constexpr optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
732732

733-
template <class _Up, enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
733+
template <class _Up = remove_cv_t<_Tp>,
734+
enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), int> = 0>
734735
_LIBCPP_HIDE_FROM_ABI constexpr explicit optional(_Up&& __v) : __base(in_place, std::forward<_Up>(__v)) {}
735736

736737
// LWG2756: conditionally explicit conversion from const optional<_Up>&
@@ -771,7 +772,7 @@ public:
771772
_LIBCPP_HIDE_FROM_ABI constexpr optional& operator=(optional&&) = default;
772773

773774
// LWG2756
774-
template <class _Up = value_type,
775+
template <class _Up = remove_cv_t<value_type>,
775776
enable_if_t<_And<_IsNotSame<__remove_cvref_t<_Up>, optional>,
776777
_Or<_IsNotSame<__remove_cvref_t<_Up>, value_type>, _Not<is_scalar<value_type>>>,
777778
is_constructible<value_type, _Up>,
@@ -919,14 +920,14 @@ public:
919920
return std::move(this->__get());
920921
}
921922

922-
template <class _Up>
923+
template <class _Up = remove_cv_t<_Tp>>
923924
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) const& {
924925
static_assert(is_copy_constructible_v<value_type>, "optional<T>::value_or: T must be copy constructible");
925926
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");
926927
return this->has_value() ? this->__get() : static_cast<value_type>(std::forward<_Up>(__v));
927928
}
928929

929-
template <class _Up>
930+
template <class _Up = remove_cv_t<_Tp>>
930931
_LIBCPP_HIDE_FROM_ABI constexpr value_type value_or(_Up&& __v) && {
931932
static_assert(is_move_constructible_v<value_type>, "optional<T>::value_or: T must be move constructible");
932933
static_assert(is_convertible_v<_Up, value_type>, "optional<T>::value_or: U must be convertible to T");

libcxx/test/std/utilities/expected/expected.expected/assign/assign.U.pass.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,44 @@ constexpr bool test() {
325325
}
326326
}
327327

328+
// Check move constructor selection
329+
{
330+
struct MoveOnlyMulti {
331+
bool used_move1 = false;
332+
bool used_move2 = false;
333+
334+
constexpr MoveOnlyMulti() = default;
335+
constexpr MoveOnlyMulti(const MoveOnlyMulti&) = delete;
336+
constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&) = delete;
337+
constexpr MoveOnlyMulti& operator=(MoveOnlyMulti&&) {
338+
used_move1 = true;
339+
return *this;
340+
}
341+
constexpr MoveOnlyMulti& operator=(const MoveOnlyMulti&&) {
342+
used_move2 = true;
343+
return *this;
344+
};
345+
constexpr MoveOnlyMulti(MoveOnlyMulti&&) : used_move1(true) {}
346+
constexpr MoveOnlyMulti(const MoveOnlyMulti&&) : used_move2(true) {}
347+
};
348+
349+
{
350+
MoveOnlyMulti t{};
351+
std::expected<MoveOnlyMulti, int> e1(std::unexpect);
352+
static_assert(std::is_same_v<decltype(std::move(t)), MoveOnlyMulti&&>);
353+
e1 = {std::move(t)};
354+
assert(e1.value().used_move1);
355+
}
356+
{
357+
const MoveOnlyMulti t{};
358+
std::expected<MoveOnlyMulti, int> e1(std::unexpect);
359+
static_assert(std::is_same_v<decltype(std::move(t)), const MoveOnlyMulti&&>);
360+
// _Up = remove_cv_t<const MoveOnlyMulti&&> --> should use MoveOnlyMulti(MoveOnlyMulti&&)
361+
e1 = {std::move(t)};
362+
assert(e1.value().used_move1);
363+
}
364+
}
365+
328366
return true;
329367
}
330368

libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.u.pass.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ struct CopyOnly {
8080
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
8181
};
8282

83+
struct MoveOnly2 {
84+
int j;
85+
bool used_move1 = false;
86+
bool used_move2 = false;
87+
88+
constexpr explicit MoveOnly2(int jj) : j(jj) {}
89+
constexpr MoveOnly2(const MoveOnly2&) = delete;
90+
constexpr MoveOnly2(MoveOnly2&& m) : j(m.j), used_move1(true) {}
91+
constexpr MoveOnly2(const MoveOnly2&& m) : j(m.j), used_move2(true) {}
92+
};
93+
8394
struct BaseError {};
8495
struct DerivedError : BaseError {};
8596

@@ -164,6 +175,22 @@ constexpr bool test() {
164175
assert(e2.has_value());
165176
assert(!e2.value()); // yes, e2 holds "false" since LWG3836
166177
}
178+
179+
// Check move constructor selection
180+
{
181+
MoveOnly2 t{1};
182+
std::expected<MoveOnly2, BaseError> e1(std::move(t));
183+
assert(e1.has_value());
184+
assert(e1.value().used_move1 == true);
185+
assert(e1.value().j == 1);
186+
}
187+
{
188+
const MoveOnly2 t2{2};
189+
std::expected<MoveOnly2, BaseError> e1(std::move(t2));
190+
assert(e1.has_value());
191+
assert(e1.value().used_move2 == true);
192+
assert(e1.value().j == 2);
193+
}
167194
return true;
168195
}
169196

libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/U.pass.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@ constexpr bool explicit_conversion(Input&& in, const Expect& v)
5959
static_assert(!std::is_constructible<O, void*>::value, "");
6060
static_assert(!std::is_constructible<O, Input, int>::value, "");
6161
optional<To> opt(std::forward<Input>(in));
62-
return opt && *opt == static_cast<To>(v);
62+
optional<To> opt2{std::forward<Input>(in)};
63+
return opt && *opt == static_cast<To>(v) && (opt2 && *opt2 == static_cast<To>(v));
6364
}
6465

6566
void test_implicit()
@@ -83,6 +84,11 @@ void test_implicit()
8384
using T = TestTypes::TestType;
8485
assert(implicit_conversion<T>(3, T(3)));
8586
}
87+
{
88+
using T = TestTypes::TestType;
89+
optional<T> opt({3});
90+
assert(opt && *opt == static_cast<T>(3));
91+
}
8692
{
8793
using O = optional<ImplicitAny>;
8894
static_assert(!test_convertible<O, std::in_place_t>(), "");

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or.pass.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ struct X
4040
{return x.i_ == y.i_;}
4141
};
4242

43+
struct Z {
44+
int i_, j_;
45+
constexpr Z(int i, int j) : i_(i), j_(j) {}
46+
friend constexpr bool operator==(const Z& z1, const Z& z2) { return z1.i_ == z2.i_ && z1.j_ == z2.j_; }
47+
};
48+
4349
constexpr int test()
4450
{
4551
{
@@ -64,6 +70,16 @@ constexpr int test()
6470
assert(std::move(opt).value_or(Y(3)) == 4);
6571
assert(!opt);
6672
}
73+
{
74+
optional<X> opt;
75+
assert(std::move(opt).value_or({Y(3)}) == 4);
76+
assert(!opt);
77+
}
78+
{
79+
optional<Z> opt;
80+
assert((std::move(opt).value_or({2, 3}) == Z{2, 3}));
81+
assert(!opt);
82+
}
6783
return 0;
6884
}
6985

libcxx/test/std/utilities/optional/optional.object/optional.object.observe/value_or_const.pass.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ int main(int, char**)
7575
const optional<X> opt;
7676
assert(opt.value_or(Y(3)) == 4);
7777
}
78+
{
79+
const optional<X> opt;
80+
assert(opt.value_or({Y(3)}) == 4);
81+
}
7882

7983
return 0;
8084
}

0 commit comments

Comments
 (0)