diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -290,7 +290,7 @@ "`3827 `__","Deprecate ```` and ```` macros","February 2023","","","" "`3828 `__","Sync ``intmax_t`` and ``uintmax_t`` with C2x","February 2023","","","" "`3833 `__","Remove specialization ``template struct formatter``","February 2023","|Complete|","17.0","|format|" -"`3836 `__","``std::expected`` conversion constructor ``expected(const expected&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","","","" +"`3836 `__","``std::expected`` conversion constructor ``expected(const expected&)`` should take precedence over ``expected(U&&)`` with operator ``bool``","February 2023","|Complete|","18.0","" "`3843 `__","``std::expected::value() &`` assumes ``E`` is copy constructible","February 2023","|Complete|","17.0","" "`3847 `__","``ranges::to`` can still return views","February 2023","|Complete|","17.0","|ranges|" "`3862 `__","``basic_const_iterator``'s ``common_type`` specialization is underconstrained","February 2023","","","" 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 @@ -167,14 +167,16 @@ using __can_convert = _And< is_constructible<_Tp, _UfQual>, is_constructible<_Err, _OtherErrQual>, - _Not&>>, - _Not>>, - _Not&>>, - _Not>>, - _Not&, _Tp>>, - _Not&&, _Tp>>, - _Not&, _Tp>>, - _Not&&, _Tp>>, + _If<_Not, bool>>::value, + _And< _Not&>>, + _Not>>, + _Not&>>, + _Not>>, + _Not&, _Tp>>, + _Not&&, _Tp>>, + _Not&, _Tp>>, + _Not&&, _Tp>>>, + true_type>, _Not, expected<_Up, _OtherErr>&>>, _Not, expected<_Up, _OtherErr>>>, _Not, const expected<_Up, _OtherErr>&>>, @@ -221,15 +223,14 @@ template requires(!is_same_v, in_place_t> && !is_same_v> && - !__is_std_unexpected>::value && is_constructible_v<_Tp, _Up>) + is_constructible_v<_Tp, _Up> && !__is_std_unexpected>::value && + (!is_same_v, bool> || !__is_std_expected>::value)) _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_Up, _Tp>) - expected(_Up&& __u) - noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened + expected(_Up&& __u) noexcept(is_nothrow_constructible_v<_Tp, _Up>) // strengthened : __has_val_(true) { std::construct_at(std::addressof(__union_.__val_), std::forward<_Up>(__u)); } - template requires is_constructible_v<_Err, const _OtherErr&> _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v) diff --git a/libcxx/include/optional b/libcxx/include/optional --- a/libcxx/include/optional +++ b/libcxx/include/optional @@ -731,7 +731,8 @@ template using _CheckOptionalArgsCtor = _If< _IsNotSame<__remove_cvref_t<_Up>, in_place_t>::value && - _IsNotSame<__remove_cvref_t<_Up>, optional>::value, + _IsNotSame<__remove_cvref_t<_Up>, optional>::value && + (!is_same_v, bool> || !__is_std_optional<__remove_cvref_t<_Up>>::value), _CheckOptionalArgsConstructor, __check_tuple_constructor_fail >; @@ -758,12 +759,12 @@ template _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_implicit() { return is_convertible<_QUp, _Tp>::value && - !__check_constructible_from_opt<_Up>::value; + (is_same_v, bool> || !__check_constructible_from_opt<_Up>::value); } template _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_explicit() { return !is_convertible<_QUp, _Tp>::value && - !__check_constructible_from_opt<_Up>::value; + (is_same_v, bool> || !__check_constructible_from_opt<_Up>::value); } template _LIBCPP_HIDE_FROM_ABI static constexpr bool __enable_assign() { 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 --- 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 @@ -67,6 +67,9 @@ friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; } }; +struct BaseError {}; +struct DerivedError : BaseError {}; + template constexpr void testInt() { std::expected e(5); @@ -112,17 +115,38 @@ assert(e.value().j == 6); } - // this is a confusing example, but the behaviour - // is exactly what is specified in the spec - // see https://cplusplus.github.io/LWG/issue3836 + // https://cplusplus.github.io/LWG/issue3836 + + // Test & { - struct BaseError {}; - struct DerivedError : BaseError {}; + std::expected e1(false); + std::expected e2(e1); + assert(e2.has_value()); + assert(!e2.value()); // yes, e2 holds "false" since LWG3836 + } + // Test && + { std::expected e1(false); + std::expected e2(std::move(e1)); + assert(e2.has_value()); + assert(!e2.value()); // yes, e2 holds "false" since LWG3836 + } + + // Test const& + { + const std::expected e1(false); std::expected e2(e1); assert(e2.has_value()); - assert(e2.value()); // yes, e2 holds "true" + assert(!e2.value()); // yes, e2 holds "false" since LWG3836 + } + + // Test const&& + { + const std::expected e1(false); + std::expected e2(std::move(e1)); + assert(e2.has_value()); + assert(!e2.value()); // yes, e2 holds "false" since LWG3836 } return true; } diff --git a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp --- a/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp +++ b/libcxx/test/std/utilities/optional/optional.object/optional.object.ctor/copy.pass.cpp @@ -170,5 +170,14 @@ static_assert( *o2 == 4, "" ); } + // LWG3836 https://wg21.link/LWG3836 + // std::optional conversion constructor optional(const optional&) + // should take precedence over optional(U&&) with operator bool + { + std::optional o1(false); + std::optional o2(o1); + assert(!o2.value()); + } + return 0; }