[libcxx-commits] [libcxx] WIP [libc++]P2944R3: Constrained comparisions - `variant` and `tuple` (PR #141396)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun May 25 00:28:44 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Hristo Hristov (H-G-Hristov)
<details>
<summary>Changes</summary>
This is a follow-up and depends on #<!-- -->139368 being merged first.
Implements [P2944R3](https://wg21.link/P2944R3) partially, which adds constrained comparisons to `std::variant` and `std::tuple`.
The missing overloads introduced in [P2165R4](https://wg21.link/P2165R4) are not implemented.
Relates to #<!-- -->136765
Closes #<!-- -->136769
Relates to: #<!-- -->139368
# References
[tuple.rel](https://wg21.link/tuple.rel)
[variant.relops](https://wg21.link/variant.relops)
---
Full diff: https://github.com/llvm/llvm-project/pull/141396.diff
6 Files Affected:
- (modified) libcxx/include/tuple (+7)
- (modified) libcxx/include/variant (+31)
- (modified) libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp (+21-2)
- (modified) libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp (+6)
- (modified) libcxx/test/std/utilities/variant/variant.relops/relops.pass.cpp (+49)
- (modified) libcxx/test/support/test_comparisons.h (+65-1)
``````````diff
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 8dd62ae624f5e..35ea37a5ea5ed 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -1161,9 +1161,16 @@ struct __tuple_equal<0> {
};
template <class... _Tp, class... _Up>
+# 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<sizeof...(_Tp)>()(__x, __y);
}
diff --git a/libcxx/include/variant b/libcxx/include/variant
index bf611aec704ca..f33058b708528 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>
@@ -1453,6 +1454,11 @@ struct __convert_to_bool {
};
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t == __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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())
@@ -1485,6 +1491,11 @@ operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
# endif // _LIBCPP_STD_VER >= 20
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t != __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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())
@@ -1495,6 +1506,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const variant<_Types...>& __lhs,
}
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t < __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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())
@@ -1509,6 +1525,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const variant<_Types...>& __lhs,
}
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t > __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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())
@@ -1523,6 +1544,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const variant<_Types...>& __lhs,
}
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t <= __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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())
@@ -1537,6 +1563,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const variant<_Types...>& __lhs,
}
template <class... _Types>
+# if _LIBCPP_STD_VER >= 26
+ requires(requires(const _Types& __t) {
+ { __t >= __t } -> __core_convertible_to<bool>;
+ } && ...)
+# 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..baca362f7783b 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,31 @@
// UNSUPPORTED: c++03
-#include <tuple>
-#include <string>
+#include <array>
#include <cassert>
+#include <tuple>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+
+static_assert(std::equality_comparable<std::tuple<EqualityComparable>>);
+static_assert(std::equality_comparable<std::tuple<EqualityComparable, EqualityComparable>>);
+
+static_assert(!std::equality_comparable<std::tuple<NonComparable>>);
+static_assert(!std::equality_comparable<std::tuple<EqualityComparable, NonComparable>>);
+static_assert(!std::equality_comparable<std::tuple<NonComparable, EqualityComparable>>);
+static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable>, std::tuple<NonComparable>>);
+static_assert(!std::equality_comparable_with<std::tuple<NonComparable>, std::tuple<EqualityComparable>>);
+// Size mismatch.
+static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable>, std::tuple<EqualityComparable, EqualityComparable>>);
+static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable, EqualityComparable>, std::tuple<EqualityComparable>>);
+
+#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 <tuple>
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 26
+// expected-no-diagnostics
+#else
void f(std::tuple<int> t1, std::tuple<int, long> 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<void>(t1 == t2); // expected-error@*:* {{}}
static_cast<void>(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 <utility>
#include <variant>
+#include "test_comparisons.h"
#include "test_macros.h"
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+
+// ==
+static_assert(HasOperatorEqual<std::variant<EqualityComparable>>);
+static_assert(HasOperatorEqual<std::variant<EqualityComparable, int, long>>);
+
+static_assert(!HasOperatorEqual<std::variant<NonComparable>>);
+static_assert(!HasOperatorEqual<std::variant<NonComparable, EqualityComparable>>);
+
+// >
+static_assert(HasOperatorGreaterThan<std::variant<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThan<std::variant<ThreeWayComparable, int, long>>);
+
+static_assert(!HasOperatorGreaterThan<std::variant<NonComparable>>);
+static_assert(!HasOperatorGreaterThan<std::variant<NonComparable, ThreeWayComparable>>);
+
+// >=
+static_assert(HasOperatorGreaterThanEqual<std::variant<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThanEqual<std::variant<ThreeWayComparable, int, long>>);
+
+static_assert(!HasOperatorGreaterThanEqual<std::variant<NonComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<std::variant<NonComparable, ThreeWayComparable>>);
+
+// <
+static_assert(HasOperatorLessThan<std::variant<ThreeWayComparable>>);
+static_assert(HasOperatorLessThan<std::variant<ThreeWayComparable, int, long>>);
+
+static_assert(!HasOperatorLessThan<std::variant<NonComparable>>);
+static_assert(!HasOperatorLessThan<std::variant<NonComparable, ThreeWayComparable>>);
+
+// <=
+static_assert(HasOperatorLessThanEqual<std::variant<ThreeWayComparable>>);
+static_assert(HasOperatorLessThanEqual<std::variant<ThreeWayComparable, int, long>>);
+
+static_assert(!HasOperatorLessThanEqual<std::variant<NonComparable>>);
+static_assert(!HasOperatorLessThanEqual<std::variant<NonComparable, ThreeWayComparable>>);
+
+// !=
+static_assert(HasOperatorNotEqual<std::variant<EqualityComparable>>);
+static_assert(HasOperatorNotEqual<std::variant<EqualityComparable, int, long>>);
+
+static_assert(!HasOperatorNotEqual<std::variant<NonComparable>>);
+static_assert(!HasOperatorNotEqual<std::variant<NonComparable, EqualityComparable>>);
+
+#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 <typename T1, typename T2 = T1>
+concept HasOperatorEqual = requires(T1 t1, T2 t2) { t1 == t2; };
+
+template <typename T1, typename T2 = T1>
+concept HasOperatorGreaterThan = requires(T1 t1, T2 t2) { t1 > t2; };
+
+template <typename T1, typename T2 = T1>
+concept HasOperatorGreaterThanEqual = requires(T1 t1, T2 t2) { t1 >= t2; };
+template <typename T1, typename T2 = T1>
+concept HasOperatorLessThan = requires(T1 t1, T2 t2) { t1 < t2; };
+
+template <typename T1, typename T2 = T1>
+concept HasOperatorLessThanEqual = requires(T1 t1, T2 t2) { t1 <= t2; };
+
+template <typename T1, typename T2 = T1>
+concept HasOperatorNotEqual = requires(T1 t1, T2 t2) { t1 != t2; };
+
+template <typename T1, typename T2 = T1>
+concept HasOperatorSpaceship = requires(T1 t1, T2 t2) { t1 <=> t2; };
+
+struct NonComparable {};
+static_assert(!std::equality_comparable<NonComparable>);
+static_assert(!HasOperatorEqual<NonComparable>);
+static_assert(!HasOperatorGreaterThan<NonComparable>);
+static_assert(!HasOperatorGreaterThanEqual<NonComparable>);
+static_assert(!HasOperatorLessThan<NonComparable>);
+static_assert(!HasOperatorLessThanEqual<NonComparable>);
+static_assert(!HasOperatorNotEqual<NonComparable>);
+static_assert(!HasOperatorSpaceship<NonComparable>);
+
+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<EqualityComparable>);
+static_assert(HasOperatorEqual<EqualityComparable>);
+static_assert(HasOperatorNotEqual<EqualityComparable>);
+
+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<ThreeWayComparable>);
+static_assert(std::three_way_comparable<ThreeWayComparable>);
+static_assert(HasOperatorEqual<ThreeWayComparable>);
+static_assert(HasOperatorGreaterThan<ThreeWayComparable>);
+static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable>);
+static_assert(HasOperatorLessThan<ThreeWayComparable>);
+static_assert(HasOperatorLessThanEqual<ThreeWayComparable>);
+static_assert(HasOperatorNotEqual<ThreeWayComparable>);
+static_assert(HasOperatorSpaceship<ThreeWayComparable>);
+
+#endif // TEST_STD_VER >= 20
#endif // TEST_COMPARISONS_H
``````````
</details>
https://github.com/llvm/llvm-project/pull/141396
More information about the libcxx-commits
mailing list