Skip to content

Commit 96377e5

Browse files
committed
[libc++][expected] Implement LWG3836
Implement LWG3836 (https://wg21.link/LWG3836) `std::expected<bool, E1>` conversion constructor `expected(const expected<U, G>&)` should take precedence over `expected(U&&)` with operator `bool` Reviewed By: #libc, Mordante Differential Revision: https://reviews.llvm.org/D155701
1 parent b1aa7cd commit 96377e5

File tree

5 files changed

+57
-22
lines changed

5 files changed

+57
-22
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@
290290
"`3827 <https://wg21.link/LWG3827>`__","Deprecate ``<stdalign.h>`` and ``<stdbool.h>`` macros","February 2023","","",""
291291
"`3828 <https://wg21.link/LWG3828>`__","Sync ``intmax_t`` and ``uintmax_t`` with C2x","February 2023","","",""
292292
"`3833 <https://wg21.link/LWG3833>`__","Remove specialization ``template<size_t N> struct formatter<const charT[N], charT>``","February 2023","|Complete|","17.0","|format|"
293-
"`3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","","",""
293+
"`3836 <https://wg21.link/LWG3836>`__","``std::expected<bool, E1>`` conversion constructor ``expected(const expected<U, G>&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","|Complete|","18.0",""
294294
"`3843 <https://wg21.link/LWG3843>`__","``std::expected<T,E>::value() &`` assumes ``E`` is copy constructible","February 2023","|Complete|","17.0",""
295295
"`3847 <https://wg21.link/LWG3847>`__","``ranges::to`` can still return views","February 2023","|Complete|","17.0","|ranges|"
296296
"`3862 <https://wg21.link/LWG3862>`__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","February 2023","","",""

libcxx/include/__expected/expected.h

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -167,14 +167,16 @@ class expected {
167167
using __can_convert =
168168
_And< is_constructible<_Tp, _UfQual>,
169169
is_constructible<_Err, _OtherErrQual>,
170-
_Not<is_constructible<_Tp, expected<_Up, _OtherErr>&>>,
171-
_Not<is_constructible<_Tp, expected<_Up, _OtherErr>>>,
172-
_Not<is_constructible<_Tp, const expected<_Up, _OtherErr>&>>,
173-
_Not<is_constructible<_Tp, const expected<_Up, _OtherErr>>>,
174-
_Not<is_convertible<expected<_Up, _OtherErr>&, _Tp>>,
175-
_Not<is_convertible<expected<_Up, _OtherErr>&&, _Tp>>,
176-
_Not<is_convertible<const expected<_Up, _OtherErr>&, _Tp>>,
177-
_Not<is_convertible<const expected<_Up, _OtherErr>&&, _Tp>>,
170+
_If<_Not<is_same<remove_cv_t<_Tp>, bool>>::value,
171+
_And< _Not<is_constructible<_Tp, expected<_Up, _OtherErr>&>>,
172+
_Not<is_constructible<_Tp, expected<_Up, _OtherErr>>>,
173+
_Not<is_constructible<_Tp, const expected<_Up, _OtherErr>&>>,
174+
_Not<is_constructible<_Tp, const expected<_Up, _OtherErr>>>,
175+
_Not<is_convertible<expected<_Up, _OtherErr>&, _Tp>>,
176+
_Not<is_convertible<expected<_Up, _OtherErr>&&, _Tp>>,
177+
_Not<is_convertible<const expected<_Up, _OtherErr>&, _Tp>>,
178+
_Not<is_convertible<const expected<_Up, _OtherErr>&&, _Tp>>>,
179+
true_type>,
178180
_Not<is_constructible<unexpected<_Err>, expected<_Up, _OtherErr>&>>,
179181
_Not<is_constructible<unexpected<_Err>, expected<_Up, _OtherErr>>>,
180182
_Not<is_constructible<unexpected<_Err>, const expected<_Up, _OtherErr>&>>,
@@ -221,15 +223,14 @@ class expected {
221223

222224
template <class _Up = _Tp>
223225
requires(!is_same_v<remove_cvref_t<_Up>, in_place_t> && !is_same_v<expected, remove_cvref_t<_Up>> &&
224-
!__is_std_unexpected<remove_cvref_t<_Up>>::value && is_constructible_v<_Tp, _Up>)
226+
is_constructible_v<_Tp, _Up> && !__is_std_unexpected<remove_cvref_t<_Up>>::value &&
227+
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_expected<remove_cvref_t<_Up>>::value))
225228
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>)
226-
expected(_Up&& __u)
227-
noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
229+
expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened
228230
: __has_val_(true) {
229231
std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u));
230232
}
231233

232-
233234
template <class _OtherErr>
234235
requires is_constructible_v<_Err, const _OtherErr&>
235236
_LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<const _OtherErr&, _Err>)

libcxx/include/optional

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -731,7 +731,8 @@ private:
731731
template <class _Up>
732732
using _CheckOptionalArgsCtor = _If<
733733
_IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value &&
734-
_IsNotSame<__remove_cvref_t<_Up>, optional>::value,
734+
_IsNotSame<__remove_cvref_t<_Up>, optional>::value &&
735+
(!is_same_v<remove_cv_t<_Tp>, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value),
735736
_CheckOptionalArgsConstructor,
736737
__check_tuple_constructor_fail
737738
>;
@@ -758,12 +759,12 @@ private:
758759
template <class _Up, class _QUp = _QualUp>
759760
_LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() {
760761
return is_convertible<_QUp, _Tp>::value &&
761-
!__check_constructible_from_opt<_Up>::value;
762+
(is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
762763
}
763764
template <class _Up, class _QUp = _QualUp>
764765
_LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() {
765766
return !is_convertible<_QUp, _Tp>::value &&
766-
!__check_constructible_from_opt<_Up>::value;
767+
(is_same_v<remove_cv_t<_Tp>, bool> || !__check_constructible_from_opt<_Up>::value);
767768
}
768769
template <class _Up, class _QUp = _QualUp>
769770
_LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() {

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ struct CopyOnly {
6767
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
6868
};
6969

70+
struct BaseError {};
71+
struct DerivedError : BaseError {};
72+
7073
template <class T>
7174
constexpr void testInt() {
7275
std::expected<T, int> e(5);
@@ -112,17 +115,38 @@ constexpr bool test() {
112115
assert(e.value().j == 6);
113116
}
114117

115-
// this is a confusing example, but the behaviour
116-
// is exactly what is specified in the spec
117-
// see https://cplusplus.github.io/LWG/issue3836
118+
// https://cplusplus.github.io/LWG/issue3836
119+
120+
// Test &
118121
{
119-
struct BaseError {};
120-
struct DerivedError : BaseError {};
122+
std::expected<bool, DerivedError> e1(false);
123+
std::expected<bool, BaseError> e2(e1);
124+
assert(e2.has_value());
125+
assert(!e2.value()); // yes, e2 holds "false" since LWG3836
126+
}
121127

128+
// Test &&
129+
{
122130
std::expected<bool, DerivedError> e1(false);
131+
std::expected<bool, BaseError> e2(std::move(e1));
132+
assert(e2.has_value());
133+
assert(!e2.value()); // yes, e2 holds "false" since LWG3836
134+
}
135+
136+
// Test const&
137+
{
138+
const std::expected<bool, DerivedError> e1(false);
123139
std::expected<bool, BaseError> e2(e1);
124140
assert(e2.has_value());
125-
assert(e2.value()); // yes, e2 holds "true"
141+
assert(!e2.value()); // yes, e2 holds "false" since LWG3836
142+
}
143+
144+
// Test const&&
145+
{
146+
const std::expected<bool, DerivedError> e1(false);
147+
std::expected<bool, BaseError> e2(std::move(e1));
148+
assert(e2.has_value());
149+
assert(!e2.value()); // yes, e2 holds "false" since LWG3836
126150
}
127151
return true;
128152
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,14 @@ int main(int, char**)
170170
static_assert( *o2 == 4, "" );
171171
}
172172

173+
// LWG3836 https://wg21.link/LWG3836
174+
// std::optional<bool> conversion constructor optional(const optional<U>&)
175+
// should take precedence over optional(U&&) with operator bool
176+
{
177+
std::optional<bool> o1(false);
178+
std::optional<bool> o2(o1);
179+
assert(!o2.value());
180+
}
181+
173182
return 0;
174183
}

0 commit comments

Comments
 (0)