@@ -1000,14 +1000,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
#endif
-#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+#if __cpp_lib_three_way_comparison
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3865. Sorting a range of pairs
/// Two pairs are equal iff their members are equal.
template<typename _T1, typename _T2, typename _U1, typename _U2>
- inline _GLIBCXX_CONSTEXPR bool
+ [[nodiscard]]
+ constexpr bool
operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
+ requires requires {
+ { __x.first == __y.first } -> __detail::__boolean_testable;
+ { __x.second == __y.second } -> __detail::__boolean_testable;
+ }
{ return __x.first == __y.first && __x.second == __y.second; }
/** Defines a lexicographical order for pairs.
@@ -1018,6 +1023,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* less than `Q.second`.
*/
template<typename _T1, typename _T2, typename _U1, typename _U2>
+ [[nodiscard]]
constexpr common_comparison_category_t<__detail::__synth3way_t<_T1, _U1>,
__detail::__synth3way_t<_T2, _U2>>
operator<=>(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
@@ -1029,6 +1035,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#else
/// Two pairs of the same type are equal iff their members are equal.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __x.first == __y.first && __x.second == __y.second; }
@@ -1041,6 +1048,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* than `Q.second`.
*/
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __x.first < __y.first
@@ -1048,24 +1056,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/// Uses @c operator== to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator!=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__x == __y); }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return __y < __x; }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator<=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__y < __x); }
/// Uses @c operator< to find the result.
template<typename _T1, typename _T2>
+ _GLIBCXX_NODISCARD
inline _GLIBCXX_CONSTEXPR bool
operator>=(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
{ return !(__x < __y); }
@@ -1237,6 +1237,15 @@ ftms = {
};
};
+ftms = {
+ name = constrained_equality;
+ values = {
+ v = 202403;
+ cxxmin = 20;
+ extra_cond = "__glibcxx_three_way_comparison";
+ };
+};
+
ftms = {
name = erase_if;
values = {
@@ -1373,6 +1373,16 @@
#endif /* !defined(__cpp_lib_constexpr_vector) && defined(__glibcxx_want_constexpr_vector) */
#undef __glibcxx_want_constexpr_vector
+#if !defined(__cpp_lib_constrained_equality)
+# if (__cplusplus >= 202002L) && (__glibcxx_three_way_comparison)
+# define __glibcxx_constrained_equality 202403L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_constrained_equality)
+# define __cpp_lib_constrained_equality 202403L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_constrained_equality) && defined(__glibcxx_want_constrained_equality) */
+#undef __glibcxx_want_constrained_equality
+
#if !defined(__cpp_lib_erase_if)
# if (__cplusplus >= 202002L) && _GLIBCXX_HOSTED
# define __glibcxx_erase_if 202002L
@@ -34,6 +34,7 @@
#define __glibcxx_want_freestanding_optional
#define __glibcxx_want_optional
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
#ifdef __cpp_lib_optional // C++ >= 17
@@ -1194,7 +1195,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Tp>
using __optional_relop_t =
- enable_if_t<is_convertible<_Tp, bool>::value, bool>;
+ enable_if_t<is_convertible_v<_Tp, bool>, bool>;
template<typename _Tp, typename _Up>
using __optional_eq_t = __optional_relop_t<
@@ -1279,6 +1280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp, three_way_comparable_with<_Tp> _Up>
+ [[nodiscard]]
constexpr compare_three_way_result_t<_Tp, _Up>
operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y)
{
@@ -1288,12 +1290,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Comparisons with nullopt.
template<typename _Tp>
+ [[nodiscard]]
constexpr bool
operator==(const optional<_Tp>& __lhs, nullopt_t) noexcept
{ return !__lhs; }
#ifdef __cpp_lib_three_way_comparison
template<typename _Tp>
+ [[nodiscard]]
constexpr strong_ordering
operator<=>(const optional<_Tp>& __x, nullopt_t) noexcept
{ return bool(__x) <=> false; }
@@ -1354,76 +1358,96 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{ return !__rhs; }
#endif // three-way-comparison
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 4072. std::optional comparisons: constrain harder
+#if __cpp_lib_concepts
+# define _REQUIRES_NOT_OPTIONAL(T) requires (!__is_optional_v<T>)
+#else
+# define _REQUIRES_NOT_OPTIONAL(T)
+#endif
+
// Comparisons with value type.
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator==(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator== [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_eq_t<_Tp, _Up>
{ return __lhs && *__lhs == __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
operator==(const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_eq_t<_Up, _Tp>
{ return __rhs && __lhs == *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator!=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator!= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_ne_t<_Tp, _Up>
{ return !__lhs || *__lhs != __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator!=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator!= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_ne_t<_Up, _Tp>
{ return !__rhs || __lhs != *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator< [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_lt_t<_Tp, _Up>
{ return !__lhs || *__lhs < __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator< [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_lt_t<_Up, _Tp>
{ return __rhs && __lhs < *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator> [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_gt_t<_Tp, _Up>
{ return __lhs && *__lhs > __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator> [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_gt_t<_Up, _Tp>
{ return !__rhs || __lhs > *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator<= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_le_t<_Tp, _Up>
{ return !__lhs || *__lhs <= __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator<=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator<= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_le_t<_Up, _Tp>
{ return __rhs && __lhs <= *__rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>=(const optional<_Tp>& __lhs, const _Up& __rhs)
+ operator>= [[nodiscard]] (const optional<_Tp>& __lhs, const _Up& __rhs)
-> __optional_ge_t<_Tp, _Up>
{ return __lhs && *__lhs >= __rhs; }
template<typename _Tp, typename _Up>
+ _REQUIRES_NOT_OPTIONAL(_Up)
constexpr auto
- operator>=(const _Up& __lhs, const optional<_Tp>& __rhs)
+ operator>= [[nodiscard]] (const _Up& __lhs, const optional<_Tp>& __rhs)
-> __optional_ge_t<_Up, _Tp>
{ return !__rhs || __lhs >= *__rhs; }
@@ -1432,7 +1456,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
requires (!__is_optional_v<_Up>)
&& three_way_comparable_with<_Up, _Tp>
constexpr compare_three_way_result_t<_Tp, _Up>
- operator<=>(const optional<_Tp>& __x, const _Up& __v)
+ operator<=> [[nodiscard]] (const optional<_Tp>& __x, const _Up& __v)
{ return bool(__x) ? *__x <=> __v : strong_ordering::less; }
#endif
@@ -51,6 +51,7 @@
#define __glibcxx_want_make_from_tuple
#define __glibcxx_want_ranges_zip
#define __glibcxx_want_tuple_like
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
namespace std _GLIBCXX_VISIBILITY(default)
@@ -250,17 +251,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cpp_lib_tuple_like // >= C++23
struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; };
- // These forward declarations are used by the operator<=> overload for
+ // This forward declaration is used by the operator<=> overload for
// tuple-like types.
- template<typename _Cat, typename _Tp, typename _Up>
+ template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq>
constexpr _Cat
- __tuple_cmp(const _Tp&, const _Up&, index_sequence<>);
-
- template<typename _Cat, typename _Tp, typename _Up,
- size_t _Idx0, size_t... _Idxs>
- constexpr _Cat
- __tuple_cmp(const _Tp& __t, const _Up& __u,
- index_sequence<_Idx0, _Idxs...>);
+ __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq);
#endif // C++23
/**
@@ -1848,7 +1843,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<__tuple_like _UTuple>
requires (!__is_tuple_v<_UTuple>)
friend constexpr bool
- operator==(const tuple& __t, const _UTuple& __u)
+ operator== [[nodiscard]] (const tuple& __t, const _UTuple& __u)
{
static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>,
"tuple objects can only be compared if they have equal sizes.");
@@ -2521,6 +2516,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#endif
+#if __cpp_lib_three_way_comparison
+ template<typename... _Tps, typename... _Ups>
+ requires (sizeof...(_Tps) == sizeof...(_Ups))
+ && (requires (const _Tps& __t, const _Ups& __u) {
+ { __t == __u } -> __detail::__boolean_testable;
+ } && ...)
+ constexpr bool
+ operator== [[nodiscard]] (const tuple<_Tps...>& __t,
+ const tuple<_Ups...>& __u)
+ {
+ return [&]<size_t... _Inds>(index_sequence<_Inds...>) {
+ // Fold == over the tuples until non-equal elements are found.
+ return ((std::get<_Inds>(__t) == std::get<_Inds>(__u)) && ...);
+ }(index_sequence_for<_Tps...>{});
+ }
+
+ template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq>
+ [[nodiscard]]
+ constexpr _Cat
+ __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq __indices)
+ {
+ _Cat __c = _Cat::equivalent;
+
+ // Set __c to the comparison result of two corresponding elements.
+ // Return true they are equivalent.
+ auto __cmp = [&]<size_t _Ind>(integral_constant<size_t, _Ind>) {
+ __c = __detail::__synth3way(std::get<_Ind>(__t), std::get<_Ind>(__u));
+ return __c == 0;
+ };
+
+ [&]<size_t... _Inds>(index_sequence<_Inds...>) {
+ // Fold __cmp over the tuples until non-equivalent elements are found.
+ (void)(__cmp(integral_constant<size_t, _Inds>{}) && ...);
+ }(__indices);
+
+ return __c;
+ }
+
+ template<typename... _Tps, typename... _Ups>
+ requires (sizeof...(_Tps) == sizeof...(_Ups))
+ && (requires { typename __detail::__synth3way_t<_Tps, _Ups>; } && ...)
+ constexpr
+ common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>
+ operator<=> [[nodiscard]] (const tuple<_Tps...>& __t,
+ const tuple<_Ups...>& __u)
+ {
+ using _Cat
+ = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>;
+ return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>());
+ }
+#else
+
// This class performs the comparison operations on tuples
template<typename _Tp, typename _Up, size_t __i, size_t __size>
struct __tuple_compare
@@ -2552,6 +2599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator==(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
@@ -2564,36 +2612,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __compare::__eq(__t, __u);
}
-#if __cpp_lib_three_way_comparison
- template<typename _Cat, typename _Tp, typename _Up>
- constexpr _Cat
- __tuple_cmp(const _Tp&, const _Up&, index_sequence<>)
- { return _Cat::equivalent; }
-
- template<typename _Cat, typename _Tp, typename _Up,
- size_t _Idx0, size_t... _Idxs>
- constexpr _Cat
- __tuple_cmp(const _Tp& __t, const _Up& __u,
- index_sequence<_Idx0, _Idxs...>)
- {
- auto __c
- = __detail::__synth3way(std::get<_Idx0>(__t), std::get<_Idx0>(__u));
- if (__c != 0)
- return __c;
- return std::__tuple_cmp<_Cat>(__t, __u, index_sequence<_Idxs...>());
- }
-
- template<typename... _Tps, typename... _Ups>
- constexpr
- common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>
- operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u)
- {
- using _Cat
- = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>;
- return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>());
- }
-#else
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator<(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
@@ -2607,24 +2627,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator!=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__t == __u); }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator>(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return __u < __t; }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator<=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
{ return !(__u < __t); }
template<typename... _TElements, typename... _UElements>
+ _GLIBCXX_NODISCARD
constexpr bool
operator>=(const tuple<_TElements...>& __t,
const tuple<_UElements...>& __u)
@@ -93,6 +93,7 @@
#define __glibcxx_want_tuples_by_type
#define __glibcxx_want_unreachable
#define __glibcxx_want_tuple_like
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
namespace std _GLIBCXX_VISIBILITY(default)
@@ -33,6 +33,7 @@
#define __glibcxx_want_freestanding_variant
#define __glibcxx_want_variant
+#define __glibcxx_want_constrained_equality
#include <bits/version.h>
#ifdef __cpp_lib_variant // C++ >= 17
@@ -1236,9 +1237,19 @@ namespace __variant
struct monostate { };
+#if __cpp_lib_concepts
+# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP) \
+ requires ((requires (const TYPES& __t) { \
+ { __t OP __t } -> __detail::__boolean_testable; }) && ...)
+#else
+# define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
+#endif
+
#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
template<typename... _Types> \
- constexpr bool operator __OP(const variant<_Types...>& __lhs, \
+ _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
+ constexpr bool \
+ operator __OP [[nodiscard]] (const variant<_Types...>& __lhs, \
const variant<_Types...>& __rhs) \
{ \
bool __ret = true; \
@@ -1690,21 +1701,6 @@ namespace __variant
template<size_t _Np, typename _Vp>
friend constexpr decltype(auto)
__detail::__variant::__get(_Vp&& __v) noexcept;
-
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
- template<typename... _Tp> \
- friend constexpr bool \
- operator __OP(const variant<_Tp...>& __lhs, \
- const variant<_Tp...>& __rhs);
-
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
- _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
-
-#undef _VARIANT_RELATION_FUNCTION_TEMPLATE
};
template<size_t _Np, typename... _Types>
new file mode 100644
@@ -0,0 +1,258 @@
+// { dg-do compile { target c++20 } }
+
+#include <optional>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T, typename U = T>
+concept eq_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t == u;
+ *t == u;
+ t == *u;
+};
+
+template<typename T, typename U = T>
+concept ne_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t != u;
+ *t != u;
+ t != *u;
+};
+
+template<typename T, typename U = T>
+concept lt_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t < u;
+ *t < u;
+ t < *u;
+};
+
+template<typename T, typename U = T>
+concept le_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t <= u;
+ *t <= u;
+ t <= *u;
+};
+
+template<typename T, typename U = T>
+concept gt_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t > u;
+ *t > u;
+ t > *u;
+};
+
+template<typename T, typename U = T>
+concept ge_comparable
+= requires (const std::optional<T>& t, const std::optional<U>& u) {
+ t >= u;
+ *t >= u;
+ t >= *u;
+};
+
+static_assert( eq_comparable<int> );
+static_assert( ne_comparable<int> );
+static_assert( lt_comparable<int> );
+static_assert( le_comparable<int> );
+static_assert( gt_comparable<int> );
+static_assert( ge_comparable<int> );
+static_assert( eq_comparable<int, long> );
+static_assert( ne_comparable<int, long> );
+static_assert( lt_comparable<int, long> );
+static_assert( le_comparable<int, long> );
+static_assert( gt_comparable<int, long> );
+static_assert( ge_comparable<int, long> );
+static_assert( eq_comparable<short, int> );
+static_assert( ne_comparable<short, int> );
+static_assert( lt_comparable<short, int> );
+static_assert( le_comparable<short, int> );
+static_assert( gt_comparable<short, int> );
+static_assert( ge_comparable<short, int> );
+
+struct A { };
+static_assert( ! eq_comparable<A> );
+static_assert( ! ne_comparable<A> );
+static_assert( ! lt_comparable<A> );
+static_assert( ! le_comparable<A> );
+static_assert( ! gt_comparable<A> );
+static_assert( ! ge_comparable<A> );
+static_assert( ! eq_comparable<A, A> );
+static_assert( ! ne_comparable<A, A> );
+static_assert( ! lt_comparable<A, A> );
+static_assert( ! le_comparable<A, A> );
+static_assert( ! gt_comparable<A, A> );
+static_assert( ! ge_comparable<A, A> );
+static_assert( ! eq_comparable<A, int> );
+static_assert( ! ne_comparable<A, int> );
+static_assert( ! lt_comparable<A, int> );
+static_assert( ! le_comparable<A, int> );
+static_assert( ! gt_comparable<A, int> );
+static_assert( ! ge_comparable<A, int> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! eq_comparable<B> );
+static_assert( ! ne_comparable<B> );
+static_assert( ! lt_comparable<B> );
+static_assert( ! le_comparable<B> );
+static_assert( ! gt_comparable<B> );
+static_assert( ! ge_comparable<B> );
+static_assert( ! eq_comparable<B, B> );
+static_assert( ! ne_comparable<B, B> );
+static_assert( ! lt_comparable<B, B> );
+static_assert( ! le_comparable<B, B> );
+static_assert( ! gt_comparable<B, B> );
+static_assert( ! ge_comparable<B, B> );
+static_assert( ! eq_comparable<B, int> );
+static_assert( ! ne_comparable<B, int> );
+static_assert( ! lt_comparable<B, int> );
+static_assert( ! le_comparable<B, int> );
+static_assert( ! gt_comparable<B, int> );
+static_assert( ! ge_comparable<B, int> );
+
+struct C { };
+bool operator==(C, C);
+static_assert( eq_comparable<C> );
+static_assert( ne_comparable<C> );
+static_assert( ! lt_comparable<C> );
+static_assert( ! le_comparable<C> );
+static_assert( ! gt_comparable<C> );
+static_assert( ! ge_comparable<C> );
+static_assert( eq_comparable<C, C> );
+static_assert( ne_comparable<C, C> );
+static_assert( eq_comparable<C, C> );
+static_assert( ne_comparable<C, C> );
+
+struct D { };
+int operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( eq_comparable<D> );
+static_assert( ! ne_comparable<D> );
+static_assert( ! lt_comparable<D> );
+static_assert( ! le_comparable<D> );
+static_assert( ! gt_comparable<D> );
+static_assert( ! ge_comparable<D> );
+static_assert( eq_comparable<D, D> );
+static_assert( ! ne_comparable<D, D> );
+static_assert( eq_comparable<D, D> );
+static_assert( ! ne_comparable<D, D> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! eq_comparable<E> );
+static_assert( ! ne_comparable<E> );
+static_assert( ! lt_comparable<E> );
+static_assert( ! le_comparable<E> );
+static_assert( ! gt_comparable<E> );
+static_assert( ! ge_comparable<E> );
+static_assert( ! eq_comparable<E, E> );
+static_assert( ! eq_comparable<E, E> );
+
+struct F { };
+bool operator<(F, F);
+void operator>(F, F);
+static_assert( ! eq_comparable<F> );
+static_assert( ! ne_comparable<F> );
+static_assert( lt_comparable<F> );
+static_assert( ! le_comparable<F> );
+static_assert( ! gt_comparable<F> );
+static_assert( ! ge_comparable<F> );
+static_assert( lt_comparable<F, F> );
+static_assert( lt_comparable<F, F> );
+
+struct G { };
+bool operator<=(G, G);
+void operator<(G, G);
+static_assert( ! eq_comparable<G> );
+static_assert( ! ne_comparable<G> );
+static_assert( ! lt_comparable<G> );
+static_assert( le_comparable<G> );
+static_assert( ! gt_comparable<G> );
+static_assert( ! ge_comparable<G> );
+static_assert( le_comparable<G, G> );
+static_assert( le_comparable<G, G> );
+
+struct H { };
+bool operator>(H, H);
+void operator>=(H, H);
+static_assert( ! eq_comparable<H> );
+static_assert( ! ne_comparable<H> );
+static_assert( ! lt_comparable<H> );
+static_assert( ! le_comparable<H> );
+static_assert( gt_comparable<H> );
+static_assert( ! ge_comparable<H> );
+static_assert( gt_comparable<H, H> );
+static_assert( gt_comparable<H, H> );
+
+struct I { };
+bool operator>=(I, I);
+void operator<=(I, I);
+static_assert( ! eq_comparable<I> );
+static_assert( ! ne_comparable<I> );
+static_assert( ! lt_comparable<I> );
+static_assert( ! le_comparable<I> );
+static_assert( ! gt_comparable<I> );
+static_assert( ge_comparable<I> );
+static_assert( ge_comparable<I, I> );
+static_assert( ge_comparable<I, I> );
+
+struct J { };
+bool operator==(J, J);
+std::weak_ordering operator<=>(J, J);
+static_assert( eq_comparable<J> );
+static_assert( ne_comparable<J> );
+static_assert( lt_comparable<J> );
+static_assert( le_comparable<J> );
+static_assert( gt_comparable<J> );
+static_assert( ge_comparable<J> );
+
+struct K { };
+int operator==(K, K); // non-bool prevents synthesis of !=
+void operator<=(K, K);
+std::weak_ordering operator<=>(K, K);
+static_assert( eq_comparable<K> );
+static_assert( ! ne_comparable<K> );
+static_assert( lt_comparable<K> );
+static_assert( ! le_comparable<K> );
+static_assert( gt_comparable<K> );
+static_assert( ge_comparable<K> );
+
+bool operator==(A, B);
+static_assert( eq_comparable<A, B> );
+static_assert( eq_comparable<B, A> );
+static_assert( ne_comparable<A, B> );
+static_assert( ne_comparable<B, A> );
+static_assert( ! lt_comparable<A, B> );
+static_assert( ! le_comparable<A, B> );
+static_assert( ! gt_comparable<A, B> );
+static_assert( ! ge_comparable<A, B> );
+
+int operator==(C, D); // non-bool prevents synthesis of != and reversed args
+static_assert( eq_comparable<C, D> );
+static_assert( ! eq_comparable<D, C> );
+static_assert( ! ne_comparable<C, D> );
+static_assert( ! ne_comparable<D, C> );
+static_assert( ! lt_comparable<C, D> );
+static_assert( ! le_comparable<C, D> );
+static_assert( ! gt_comparable<C, D> );
+static_assert( ! ge_comparable<C, D> );
+
+std::weak_ordering operator<=>(E, F);
+static_assert( ! eq_comparable<E, F> );
+static_assert( ! eq_comparable<F, E> );
+static_assert( ! ne_comparable<E, F> );
+static_assert( ! ne_comparable<F, E> );
+static_assert( lt_comparable<E, F> );
+static_assert( le_comparable<E, F> );
+static_assert( gt_comparable<E, F> );
+static_assert( ge_comparable<E, F> );
+static_assert( lt_comparable<F, E> );
+static_assert( le_comparable<F, E> );
+static_assert( gt_comparable<F, E> );
+static_assert( ge_comparable<F, E> );
new file mode 100644
@@ -0,0 +1,48 @@
+// { dg-do compile { target c++20 } }
+
+#include <utility>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T>
+concept equality_comparable = requires (const T& t) { t == t; t != t; };
+
+static_assert( equality_comparable<std::pair<int, long>> );
+
+struct A { };
+static_assert( ! equality_comparable<std::pair<A, long>> );
+static_assert( ! equality_comparable<std::pair<int, A>> );
+static_assert( ! equality_comparable<std::pair<A, A>> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! equality_comparable<std::pair<B, long>> );
+static_assert( ! equality_comparable<std::pair<int, B>> );
+static_assert( ! equality_comparable<std::pair<B, B>> );
+static_assert( ! equality_comparable<std::pair<A, B>> );
+
+struct C { };
+int operator==(C, C);
+static_assert( equality_comparable<std::pair<C, long>> );
+static_assert( equality_comparable<std::pair<int, C>> );
+static_assert( equality_comparable<std::pair<C, C>> );
+static_assert( ! equality_comparable<std::pair<A, C>> );
+
+struct D { };
+bool operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( equality_comparable<std::pair<D, long>> );
+static_assert( equality_comparable<std::pair<int, D>> );
+static_assert( equality_comparable<std::pair<D, D>> );
+static_assert( equality_comparable<std::pair<C, D>> );
+static_assert( ! equality_comparable<std::pair<A, C>> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! equality_comparable<std::pair<E, long>> );
+static_assert( ! equality_comparable<std::pair<int, E>> );
+static_assert( ! equality_comparable<std::pair<E, E>> );
new file mode 100644
@@ -0,0 +1,50 @@
+// { dg-do compile { target c++20 } }
+
+#include <tuple>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T>
+concept equality_comparable = requires (const T& t) { t == t; t != t; };
+
+static_assert( equality_comparable<std::tuple<>> );
+static_assert( equality_comparable<std::tuple<int>> );
+static_assert( equality_comparable<std::tuple<int, long>> );
+static_assert( equality_comparable<std::tuple<int, long, short>> );
+
+struct A { };
+static_assert( ! equality_comparable<std::tuple<A>> );
+static_assert( ! equality_comparable<std::tuple<int, A>> );
+static_assert( ! equality_comparable<std::tuple<A, A>> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! equality_comparable<std::tuple<B>> );
+static_assert( ! equality_comparable<std::tuple<int, B>> );
+static_assert( ! equality_comparable<std::tuple<B, B>> );
+
+struct C { };
+int operator==(C, C);
+static_assert( equality_comparable<std::tuple<C>> );
+static_assert( equality_comparable<std::tuple<int, C>> );
+static_assert( equality_comparable<std::tuple<C, C>> );
+static_assert( ! equality_comparable<std::tuple<A, C>> );
+
+struct D { };
+bool operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( equality_comparable<std::tuple<D>> );
+static_assert( equality_comparable<std::tuple<int, D>> );
+static_assert( equality_comparable<std::tuple<D, D>> );
+static_assert( equality_comparable<std::tuple<C, D>> );
+static_assert( ! equality_comparable<std::tuple<A, C>> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! equality_comparable<std::tuple<E>> );
+static_assert( ! equality_comparable<std::tuple<int, E>> );
+static_assert( ! equality_comparable<std::tuple<E, E>> );
@@ -1,4 +1,5 @@
-// { dg-do compile { target c++11 } }
+// { dg-do compile { target { c++11 && c++17_down } } }
+// Not valid in C++20, because TwistedLogic doesn't model boolean-testable.
// Copyright (C) 2014-2024 Free Software Foundation, Inc.
//
@@ -49,8 +50,5 @@ TwistedLogic operator<(const Compares&, const Compares&) { return {false}; }
auto a = std::make_tuple(nullptr, Compares{}, 2, 'U');
auto b = a == a;
-#if ! __cpp_lib_three_way_comparison
-// Not valid in C++20, because TwistedLogic doesn't model boolean-testable.
auto c = std::make_tuple("", Compares{}, 2, 'U');
auto d = c < c;
-#endif
@@ -50,5 +50,4 @@ auto a = std::make_tuple(nullptr, Compares{}, 2, 'U');
auto b = a < a;
// { dg-error "no match for 'operator<'" "" { target c++20 } 0 }
-// { dg-error "no match for .*_Synth3way|in requirements" "" { target c++20 } 0 }
// { dg-error "ordered comparison" "" { target c++17_down } 0 }
new file mode 100644
@@ -0,0 +1,175 @@
+// { dg-do compile { target c++20 } }
+
+#include <variant>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing"
+#elif __cpp_lib_constrained_equality != 202403
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T, typename... U>
+concept eq_comparable
+= requires (const std::variant<int, T, U...>& t) { t == t; };
+
+template<typename T, typename... U>
+concept ne_comparable
+= requires (const std::variant<int, T, U...>& t) { t != t; };
+
+template<typename T, typename... U>
+concept lt_comparable
+= requires (const std::variant<int, T, U...>& t) { t < t; };
+
+template<typename T, typename... U>
+concept le_comparable
+= requires (const std::variant<int, T, U...>& t) { t <= t; };
+
+template<typename T, typename... U>
+concept gt_comparable
+= requires (const std::variant<int, T, U...>& t) { t > t; };
+
+template<typename T, typename... U>
+concept ge_comparable
+= requires (const std::variant<int, T, U...>& t) { t >= t; };
+
+static_assert( eq_comparable<int> );
+static_assert( ne_comparable<int> );
+static_assert( lt_comparable<int> );
+static_assert( le_comparable<int> );
+static_assert( gt_comparable<int> );
+static_assert( ge_comparable<int> );
+static_assert( eq_comparable<int, long> );
+static_assert( ne_comparable<int, long> );
+static_assert( lt_comparable<int, long> );
+static_assert( le_comparable<int, long> );
+static_assert( gt_comparable<int, long> );
+static_assert( ge_comparable<int, long> );
+static_assert( eq_comparable<short, int> );
+static_assert( ne_comparable<short, int> );
+static_assert( lt_comparable<short, int> );
+static_assert( le_comparable<short, int> );
+static_assert( gt_comparable<short, int> );
+static_assert( ge_comparable<short, int> );
+static_assert( eq_comparable<std::monostate, char, int> );
+static_assert( ne_comparable<std::monostate, char, int> );
+static_assert( lt_comparable<std::monostate, char, int> );
+static_assert( le_comparable<std::monostate, char, int> );
+static_assert( gt_comparable<std::monostate, char, int> );
+static_assert( ge_comparable<std::monostate, char, int> );
+
+struct A { };
+static_assert( ! eq_comparable<A> );
+static_assert( ! ne_comparable<A> );
+static_assert( ! lt_comparable<A> );
+static_assert( ! le_comparable<A> );
+static_assert( ! gt_comparable<A> );
+static_assert( ! ge_comparable<A> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! eq_comparable<B> );
+static_assert( ! ne_comparable<B> );
+static_assert( ! lt_comparable<B> );
+static_assert( ! le_comparable<B> );
+static_assert( ! gt_comparable<B> );
+static_assert( ! ge_comparable<B> );
+
+struct C { };
+bool operator==(C, C);
+static_assert( eq_comparable<C> );
+static_assert( ne_comparable<C> );
+static_assert( ! lt_comparable<C> );
+static_assert( ! le_comparable<C> );
+static_assert( ! gt_comparable<C> );
+static_assert( ! ge_comparable<C> );
+static_assert( ne_comparable<C, C> );
+
+struct D { };
+int operator==(D, D); // variant's operator== returns bool despite int here
+bool operator!=(D, D) = delete;
+static_assert( eq_comparable<D> );
+static_assert( ne_comparable<D> ); // variant's operator== can be used
+static_assert( ! lt_comparable<D> );
+static_assert( ! le_comparable<D> );
+static_assert( ! gt_comparable<D> );
+static_assert( ! ge_comparable<D> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! eq_comparable<E> );
+static_assert( ! ne_comparable<E> );
+static_assert( ! lt_comparable<E> );
+static_assert( ! le_comparable<E> );
+static_assert( ! gt_comparable<E> );
+static_assert( ! ge_comparable<E> );
+
+struct F { };
+bool operator<(F, F);
+void operator>(F, F);
+static_assert( ! eq_comparable<F> );
+static_assert( ! ne_comparable<F> );
+static_assert( lt_comparable<F> );
+static_assert( ! le_comparable<F> );
+static_assert( ! gt_comparable<F> );
+static_assert( ! ge_comparable<F> );
+
+struct G { };
+bool operator<=(G, G);
+void operator<(G, G);
+static_assert( ! eq_comparable<G> );
+static_assert( ! ne_comparable<G> );
+static_assert( ! lt_comparable<G> );
+static_assert( le_comparable<G> );
+static_assert( ! gt_comparable<G> );
+static_assert( ! ge_comparable<G> );
+
+struct H { };
+bool operator>(H, H);
+void operator>=(H, H);
+static_assert( ! eq_comparable<H> );
+static_assert( ! ne_comparable<H> );
+static_assert( ! lt_comparable<H> );
+static_assert( ! le_comparable<H> );
+static_assert( gt_comparable<H> );
+static_assert( ! ge_comparable<H> );
+static_assert( gt_comparable<H, H> );
+static_assert( gt_comparable<H, H> );
+
+struct I { };
+bool operator>=(I, I);
+void operator<=(I, I);
+static_assert( ! eq_comparable<I> );
+static_assert( ! ne_comparable<I> );
+static_assert( ! lt_comparable<I> );
+static_assert( ! le_comparable<I> );
+static_assert( ! gt_comparable<I> );
+static_assert( ge_comparable<I> );
+static_assert( ge_comparable<I, I> );
+static_assert( ge_comparable<I, I> );
+
+struct J { };
+bool operator==(J, J);
+std::weak_ordering operator<=>(J, J);
+static_assert( eq_comparable<J> );
+static_assert( ne_comparable<J> );
+static_assert( lt_comparable<J> );
+static_assert( le_comparable<J> );
+static_assert( gt_comparable<J> );
+static_assert( ge_comparable<J> );
+
+struct K { };
+int operator==(K, K); // variant's operator== returns bool despite int here
+void operator<=(K, K);
+std::weak_ordering operator<=>(K, K);
+static_assert( eq_comparable<K> );
+static_assert( ne_comparable<K> ); // variant's operator== can be used
+static_assert( lt_comparable<K> );
+static_assert( ! le_comparable<K> );
+static_assert( gt_comparable<K> );
+static_assert( ge_comparable<K> );
+static_assert( eq_comparable<K, J> );
+static_assert( ne_comparable<K, J> );
+static_assert( lt_comparable<K, J> );
+static_assert( ! le_comparable<K, J> );
+static_assert( gt_comparable<K, J> );
+static_assert( ge_comparable<K, J> );