diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 63e19f097e301..7b13b32b3cce5 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -78,7 +78,7 @@ "","","","","","" "`P3136R1 `__","Retiring niebloids","2024-11 (Wrocław)","","","" "`P3138R5 `__","``views::cache_latest``","2024-11 (Wrocław)","","","" -"`P3379R0 `__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","","","" +"`P3379R0 `__","Constrain ``std::expected`` equality operators","2024-11 (Wrocław)","|Complete|","21","" "`P2862R1 `__","``text_encoding::name()`` should never return null values","2024-11 (Wrocław)","","","" "`P2897R7 `__","``aligned_accessor``: An ``mdspan`` accessor expressing pointer over-alignment","2024-11 (Wrocław)","|Complete|","21","" "`P3355R1 `__","Fix ``submdspan`` for C++26","2024-11 (Wrocław)","","","" diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h index 03bbd1623ed5c..6b3d335f2151c 100644 --- a/libcxx/include/__expected/expected.h +++ b/libcxx/include/__expected/expected.h @@ -25,6 +25,7 @@ #include <__type_traits/is_assignable.h> #include <__type_traits/is_constructible.h> #include <__type_traits/is_convertible.h> +#include <__type_traits/is_core_convertible.h> #include <__type_traits/is_function.h> #include <__type_traits/is_nothrow_assignable.h> #include <__type_traits/is_nothrow_constructible.h> @@ -1139,8 +1140,15 @@ class expected : private __expected_base<_Tp, _Err> { // [expected.object.eq], equality operators template + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) requires(!is_void_v<_T2>) - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { +# if _LIBCPP_STD_VER >= 26 + && requires { + { *__x == *__y } -> __core_convertible_to; + { __x.error() == __y.error() } -> __core_convertible_to; + } +# endif + { if (__x.__has_val() != __y.__has_val()) { return false; } else { @@ -1153,12 +1161,24 @@ class expected : private __expected_base<_Tp, _Err> { } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) +# if _LIBCPP_STD_VER >= 26 + requires(!__is_std_expected<_T2>::value) && requires { + { *__x == __v } -> __core_convertible_to; + } +# endif + { return __x.__has_val() && static_cast(__x.__val() == __v); } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) +# if _LIBCPP_STD_VER >= 26 + requires requires { + { __x.error() == __e.error() } -> __core_convertible_to; + } +# endif + { return !__x.__has_val() && static_cast(__x.__unex() == __e.error()); } }; @@ -1851,7 +1871,13 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { // [expected.void.eq], equality operators template requires is_void_v<_T2> - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) +# if _LIBCPP_STD_VER >= 26 + requires requires { + { __x.error() == __y.error() } -> __core_convertible_to; + } +# endif + { if (__x.__has_val() != __y.__has_val()) { return false; } else { @@ -1860,7 +1886,13 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> { } template - _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) { + _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) +# if _LIBCPP_STD_VER >= 26 + requires requires { + { __x.error() == __y.error() } -> __core_convertible_to; + } +# endif + { return !__x.__has_val() && static_cast(__x.__unex() == __y.error()); } }; diff --git a/libcxx/include/__type_traits/is_core_convertible.h b/libcxx/include/__type_traits/is_core_convertible.h index 93e23d24d6624..ca3a346c17cd7 100644 --- a/libcxx/include/__type_traits/is_core_convertible.h +++ b/libcxx/include/__type_traits/is_core_convertible.h @@ -30,6 +30,13 @@ template struct __is_core_convertible<_Tp, _Up, decltype(static_cast(0)(static_cast<_Tp (*)()>(0)()))> : true_type {}; +#if _LIBCPP_STD_VER >= 20 + +template +concept __core_convertible_to = __is_core_convertible<_Tp, _Up>::value; + +#endif // _LIBCPP_STD_VER >= 20 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___TYPE_TRAITS_IS_CORE_CONVERTIBLE_H diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp index bc8b9de97e4d2..25eb97a2df4d3 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp @@ -17,18 +17,19 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +#if TEST_STD_VER >= 26 +// https://wg21.link/P3379R0 +static_assert(CanCompare, int>); +static_assert(CanCompare, EqualityComparable>); +static_assert(!CanCompare, NonComparable>); +#endif constexpr bool test() { // x.has_value() { - const std::expected e1(std::in_place, 5); + const std::expected e1(std::in_place, 5); int i2 = 10; int i3 = 5; assert(e1 != i2); @@ -37,7 +38,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); int i2 = 10; int i3 = 5; assert(e1 != i2); diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp index 9325c6c61ad2d..f0f549b6b7772 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp @@ -18,20 +18,26 @@ #include #include "test_macros.h" +#include "../../types.h" // Test constraint -template -concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; - -struct Foo{}; -static_assert(!CanCompare); +static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); -// Note this is true because other overloads are unconstrained -static_assert(CanCompare, std::expected>); - +#if TEST_STD_VER >= 26 +// https://wg21.link/P3379R0 +static_assert(!CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +#else +// Note this is true because other overloads in expected are unconstrained +static_assert(CanCompare, std::expected>); +#endif constexpr bool test() { // x.has_value() && y.has_value() { diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp index a8c469d01be28..6c7d2f39514e7 100644 --- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp @@ -17,18 +17,19 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +#if TEST_STD_VER >= 26 +// https://wg21.link/P3379R0 +static_assert(CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(!CanCompare, std::unexpected>); +#endif constexpr bool test() { // x.has_value() { - const std::expected e1(std::in_place, 5); + const std::expected e1(std::in_place, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); @@ -37,7 +38,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp index 8b24875586852..b6c3d8deee644 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp @@ -18,10 +18,7 @@ #include #include "test_macros.h" - -// Test constraint -template -concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; +#include "../../types.h" struct Foo{}; static_assert(!CanCompare); @@ -29,8 +26,18 @@ static_assert(!CanCompare); static_assert(CanCompare, std::expected>); static_assert(CanCompare, std::expected>); +#if TEST_STD_VER >= 26 +// https://wg21.link/P3379R0 +static_assert(!CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +static_assert(!CanCompare, std::expected>); +#else // Note this is true because other overloads in expected are unconstrained static_assert(CanCompare, std::expected>); +#endif constexpr bool test() { // x.has_value() && y.has_value() diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp index 4500971131b65..f37f38bb71512 100644 --- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp +++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp @@ -17,18 +17,19 @@ #include #include "test_macros.h" +#include "../../types.h" -struct Data { - int i; - constexpr Data(int ii) : i(ii) {} - - friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; } -}; +#if TEST_STD_VER >= 26 +// https://wg21.link/P3379R0 +static_assert(CanCompare, std::unexpected>); +static_assert(CanCompare, std::unexpected>); +static_assert(!CanCompare, std::unexpected>); +#endif constexpr bool test() { // x.has_value() { - const std::expected e1; + const std::expected e1; std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); @@ -37,7 +38,7 @@ constexpr bool test() { // !x.has_value() { - const std::expected e1(std::unexpect, 5); + const std::expected e1(std::unexpect, 5); std::unexpected un2(10); std::unexpected un3(5); assert(e1 != un2); diff --git a/libcxx/test/std/utilities/expected/types.h b/libcxx/test/std/utilities/expected/types.h index df73ebdfe495e..11473ca3d97de 100644 --- a/libcxx/test/std/utilities/expected/types.h +++ b/libcxx/test/std/utilities/expected/types.h @@ -336,4 +336,17 @@ struct CheckForInvalidWrites : public CheckForInvalidWritesBase +concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; }; + #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H