diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 6e7a430d219ea..c7580f1000df2 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -216,6 +216,7 @@ template # include <__compare/common_comparison_category.h> # include <__compare/ordering.h> # include <__compare/synth_three_way.h> +# include <__concepts/boolean_testable.h> # include <__config> # include <__cstddef/size_t.h> # include <__fwd/array.h> @@ -1153,9 +1154,16 @@ struct __tuple_equal<0> { }; template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Tp& __t, const _Up& __u) { + { __t == __u } -> __boolean_testable; + } && ...) && (sizeof...(_Tp) == sizeof...(_Up)) +# endif inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) { +# if _LIBCPP_STD_VER < 26 static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes"); +# endif return __tuple_equal()(__x, __y); } diff --git a/libcxx/include/variant b/libcxx/include/variant index dac6f786cc198..ede9f486ecc2e 100644 --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -242,6 +242,7 @@ namespace std { # 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_destructible.h> # include <__type_traits/is_nothrow_assignable.h> # include <__type_traits/is_nothrow_constructible.h> @@ -1442,6 +1443,11 @@ struct __convert_to_bool { }; template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t == __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__lhs.index() != __rhs.index()) @@ -1474,6 +1480,11 @@ operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { # endif // _LIBCPP_STD_VER >= 20 template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t != __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__lhs.index() != __rhs.index()) @@ -1484,6 +1495,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const variant<_Types...>& __lhs, } template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t < __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__rhs.valueless_by_exception()) @@ -1498,6 +1514,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const variant<_Types...>& __lhs, } template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t > __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__lhs.valueless_by_exception()) @@ -1512,6 +1533,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const variant<_Types...>& __lhs, } template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t <= __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__lhs.valueless_by_exception()) @@ -1526,6 +1552,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const variant<_Types...>& __lhs, } template +# if _LIBCPP_STD_VER >= 26 + requires(requires(const _Types& __t) { + { __t >= __t } -> __core_convertible_to; + } && ...) +# endif _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) { using __variant_detail::__visitation::__variant; if (__rhs.valueless_by_exception()) diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp index a8de656313d45..779a89b163f04 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp @@ -16,12 +16,33 @@ // UNSUPPORTED: c++03 -#include -#include +#include #include +#include +#include "test_comparisons.h" #include "test_macros.h" +#if TEST_STD_VER >= 26 + +// Test SFINAE. + +static_assert(std::equality_comparable>); +static_assert(std::equality_comparable>); + +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable_with, std::tuple>); +static_assert(!std::equality_comparable_with, std::tuple>); +// Size mismatch. +static_assert( + !std::equality_comparable_with, std::tuple>); +static_assert( + !std::equality_comparable_with, std::tuple>); + +#endif + int main(int, char**) { { diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp index 851f6fcd1fbac..a5dbb6b313e6a 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp @@ -21,9 +21,15 @@ #include +#include "test_macros.h" + +#if TEST_STD_VER >= 26 +// expected-no-diagnostics +#else void f(std::tuple t1, std::tuple t2) { // We test only the core comparison operators and trust that the others // fall back on the same implementations prior to C++20. static_cast(t1 == t2); // expected-error@*:* {{}} static_cast(t1 < t2); // expected-error@*:* {{}} } +#endif diff --git a/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp b/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp index c1a5b8e474a74..2c00703662687 100644 --- a/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp +++ b/libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp @@ -39,8 +39,57 @@ #include #include +#include "test_comparisons.h" #include "test_macros.h" +#if TEST_STD_VER >= 26 + +// Test SFINAE. + +// == +static_assert(HasOperatorEqual>); +static_assert(HasOperatorEqual>); + +static_assert(!HasOperatorEqual>); +static_assert(!HasOperatorEqual>); + +// > +static_assert(HasOperatorGreaterThan>); +static_assert(HasOperatorGreaterThan>); + +static_assert(!HasOperatorGreaterThan>); +static_assert(!HasOperatorGreaterThan>); + +// >= +static_assert(HasOperatorGreaterThanEqual>); +static_assert(HasOperatorGreaterThanEqual>); + +static_assert(!HasOperatorGreaterThanEqual>); +static_assert(!HasOperatorGreaterThanEqual>); + +// < +static_assert(HasOperatorLessThan>); +static_assert(HasOperatorLessThan>); + +static_assert(!HasOperatorLessThan>); +static_assert(!HasOperatorLessThan>); + +// <= +static_assert(HasOperatorLessThanEqual>); +static_assert(HasOperatorLessThanEqual>); + +static_assert(!HasOperatorLessThanEqual>); +static_assert(!HasOperatorLessThanEqual>); + +// != +static_assert(HasOperatorNotEqual>); +static_assert(HasOperatorNotEqual>); + +static_assert(!HasOperatorNotEqual>); +static_assert(!HasOperatorNotEqual>); + +#endif + #ifndef TEST_HAS_NO_EXCEPTIONS struct MakeEmptyT { MakeEmptyT() = default; diff --git a/libcxx/test/support/test_comparisons.h b/libcxx/test/support/test_comparisons.h index db6977a96a2fe..e37ab44828c70 100644 --- a/libcxx/test/support/test_comparisons.h +++ b/libcxx/test/support/test_comparisons.h @@ -268,6 +268,70 @@ struct PartialOrder { } }; -#endif +template +concept HasOperatorEqual = requires(T1 t1, T2 t2) { t1 == t2; }; + +template +concept HasOperatorGreaterThan = requires(T1 t1, T2 t2) { t1 > t2; }; + +template +concept HasOperatorGreaterThanEqual = requires(T1 t1, T2 t2) { t1 >= t2; }; +template +concept HasOperatorLessThan = requires(T1 t1, T2 t2) { t1 < t2; }; + +template +concept HasOperatorLessThanEqual = requires(T1 t1, T2 t2) { t1 <= t2; }; + +template +concept HasOperatorNotEqual = requires(T1 t1, T2 t2) { t1 != t2; }; + +template +concept HasOperatorSpaceship = requires(T1 t1, T2 t2) { t1 <=> t2; }; + +struct NonComparable {}; +static_assert(!std::equality_comparable); +static_assert(!HasOperatorEqual); +static_assert(!HasOperatorGreaterThan); +static_assert(!HasOperatorGreaterThanEqual); +static_assert(!HasOperatorLessThan); +static_assert(!HasOperatorLessThanEqual); +static_assert(!HasOperatorNotEqual); +static_assert(!HasOperatorSpaceship); + +class EqualityComparable { +public: + constexpr EqualityComparable(int value) : value_{value} {}; + + friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default; + +private: + int value_; +}; +static_assert(std::equality_comparable); +static_assert(HasOperatorEqual); +static_assert(HasOperatorNotEqual); + +class ThreeWayComparable { +public: + constexpr ThreeWayComparable(int value) : value_{value} {}; + + friend constexpr bool operator==(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default; + friend constexpr std::strong_ordering + operator<=>(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default; + +private: + int value_; +}; +static_assert(std::equality_comparable); +static_assert(std::three_way_comparable); +static_assert(HasOperatorEqual); +static_assert(HasOperatorGreaterThan); +static_assert(HasOperatorGreaterThanEqual); +static_assert(HasOperatorLessThan); +static_assert(HasOperatorLessThanEqual); +static_assert(HasOperatorNotEqual); +static_assert(HasOperatorSpaceship); + +#endif // TEST_STD_VER >= 20 #endif // TEST_COMPARISONS_H