public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386]
@ 2024-04-10  8:45 Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10  8:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux.

Since this only affects C++20 and later it seems OK for trunk now.

-- >8 --

I'm only treating this as a DR for C++20 for now, because it's less work
and only requires changes to operator== and operator<=>. To do this for
older standards would require changes to the six relational operators
used pre-C++20.

libstdc++-v3/ChangeLog:

	PR libstdc++/113386
	* include/bits/stl_pair.h (operator==, operator<=>): Support
	heterogeneous comparisons, as per LWG 3865.
	* testsuite/20_util/pair/comparison_operators/lwg3865.cc: New
	test.
---
 libstdc++-v3/include/bits/stl_pair.h          | 32 ++++++++++++++-----
 .../pair/comparison_operators/lwg3865.cc      | 15 +++++++++
 2 files changed, 39 insertions(+), 8 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 4f5c8389fa6..45317417c9c 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -1000,23 +1000,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
 #endif
 
-  /// Two pairs of the same type are equal iff their members are equal.
-  template<typename _T1, typename _T2>
+#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
+  // _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
-    operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+    operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
     { return __x.first == __y.first && __x.second == __y.second; }
 
-#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
-  template<typename _T1, typename _T2>
-    constexpr common_comparison_category_t<__detail::__synth3way_t<_T1>,
-					   __detail::__synth3way_t<_T2>>
-    operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+  /** Defines a lexicographical order for pairs.
+   *
+   * For two pairs of comparable types, `P` is ordered before `Q` if
+   * `P.first` is less than `Q.first`, or if `P.first` and `Q.first`
+   * are equivalent (neither is less than the other) and `P.second` is
+   * less than `Q.second`.
+  */
+  template<typename _T1, typename _T2, typename _U1, typename _U2>
+    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)
     {
       if (auto __c = __detail::__synth3way(__x.first, __y.first); __c != 0)
 	return __c;
       return __detail::__synth3way(__x.second, __y.second);
     }
 #else
+  /// Two pairs of the same type are equal iff their members are equal.
+  template<typename _T1, typename _T2>
+    inline _GLIBCXX_CONSTEXPR bool
+    operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+    { return __x.first == __y.first && __x.second == __y.second; }
+
   /** Defines a lexicographical order for pairs.
    *
    * For two pairs of the same type, `P` is ordered before `Q` if
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
new file mode 100644
index 00000000000..2bbd54af192
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
@@ -0,0 +1,15 @@
+// { dg-do run { target c++20 } }
+
+// LWG 3865. Sorting a range of pairs
+
+#include <utility>
+#include <testsuite_hooks.h>
+
+int main()
+{
+  std::pair<int, int> p(1, 2);
+  std::pair<int&, int&> p2(p.first, p.second);
+  VERIFY( p == p2 );
+  VERIFY( p <= p2 );
+  VERIFY( p >= p2 );
+}
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26
  2024-04-10  8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
@ 2024-04-10  8:45 ` Jonathan Wakely
  2024-04-15 18:30   ` Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10  8:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux.

Since this only affects C++26 it seems OK for trunk now.

-- >8 --

This C++26 change was just approved in Tokyo, in P2944R3. It adds
operator== and operator<=> overloads to std::reference_wrapper.

The operator<=> overloads in the paper cause compilation errors for any
type without <=> so they're implemented here with deduced return types
and constrained by a requires clause.

libstdc++-v3/ChangeLog:

	* include/bits/refwrap.h (reference_wrapper): Add comparison
	operators as proposed by P2944R3.
	* include/bits/version.def (reference_wrapper): Define.
	* include/bits/version.h: Regenerate.
	* include/std/functional: Enable feature test macro.
	* testsuite/20_util/reference_wrapper/compare.cc: New test.
---
 libstdc++-v3/include/bits/refwrap.h           | 45 +++++++++
 libstdc++-v3/include/bits/version.def         |  8 ++
 libstdc++-v3/include/bits/version.h           | 10 ++
 libstdc++-v3/include/std/functional           |  1 +
 .../20_util/reference_wrapper/compare.cc      | 95 +++++++++++++++++++
 5 files changed, 159 insertions(+)
 create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc

diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
index 2d4338b718f..fd1cc2b63e6 100644
--- a/libstdc++-v3/include/bits/refwrap.h
+++ b/libstdc++-v3/include/bits/refwrap.h
@@ -38,6 +38,10 @@
 #include <bits/invoke.h>
 #include <bits/stl_function.h> // for unary_function and binary_function
 
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+# include <compare>
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -358,6 +362,47 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
 #endif
 	  return std::__invoke(get(), std::forward<_Args>(__args)...);
 	}
+
+#if __glibcxx_reference_wrapper >= 202403L // >= C++26
+      // [refwrap.comparisons], comparisons
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, reference_wrapper __y)
+      requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+      { return __x.get() == __y.get(); }
+
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, const _Tp& __y)
+      requires requires { { __x.get() == __y } -> convertible_to<bool>; }
+      { return __x.get() == __y; }
+
+      [[nodiscard]]
+      friend constexpr bool
+      operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+      requires (!is_const_v<_Tp>)
+	&& requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
+      { return __x.get() == __y.get(); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
+      requires requires { __detail::__synth3way(__x.get(), __y.get()); }
+      { return __detail::__synth3way(__x.get(), __y.get()); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, const _Tp& __y)
+      requires requires { __detail::__synth3way(__x.get(), __y); }
+      { return __detail::__synth3way(__x.get(), __y); }
+
+      [[nodiscard]]
+      friend constexpr auto
+      operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+      requires (!is_const_v<_Tp>)
+	&& requires { __detail::__synth3way(__x.get(), __y.get()); }
+      { return __detail::__synth3way(__x.get(), __y.get()); }
+#endif
     };
 
 #if __cpp_deduction_guides
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5ad44941bff..5c0477fb61e 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1760,6 +1760,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = reference_wrapper;
+  values = {
+    v = 202403;
+    cxxmin = 26;
+  };
+};
+
 ftms = {
   name = saturation_arithmetic;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 460a3e0116a..65e708c73fb 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1963,6 +1963,16 @@
 #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
 #undef __glibcxx_want_ratio
 
+#if !defined(__cpp_lib_reference_wrapper)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_reference_wrapper 202403L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
+#   define __cpp_lib_reference_wrapper 202403L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
+#undef __glibcxx_want_reference_wrapper
+
 #if !defined(__cpp_lib_saturation_arithmetic)
 # if (__cplusplus >  202302L)
 #  define __glibcxx_saturation_arithmetic 202311L
diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
index 766558b3ce0..99364286a72 100644
--- a/libstdc++-v3/include/std/functional
+++ b/libstdc++-v3/include/std/functional
@@ -83,6 +83,7 @@
 #define __glibcxx_want_move_only_function
 #define __glibcxx_want_not_fn
 #define __glibcxx_want_ranges
+#define __glibcxx_want_reference_wrapper
 #define __glibcxx_want_transparent_operators
 #include <bits/version.h>
 
diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
new file mode 100644
index 00000000000..039c9d26496
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
@@ -0,0 +1,95 @@
+// { dg-do compile { target c++26 } }
+
+
+#include <functional>
+
+#ifndef __cpp_lib_reference_wrapper
+# error "Feature-test macro for reference_wrapper missing"
+#elif __cpp_lib_reference_wrapper != 202403
+# error "Feature-test macro for reference_wrapper has wrong value"
+#endif
+
+// P2944R3 Comparisons for reference_wrapper
+
+auto check(int i, std::reference_wrapper<int> r) -> bool {
+  return i == r;
+}
+
+template <class T> using Ref = std::reference_wrapper<T>;
+
+template <class T>
+concept ref_equality_comparable
+= requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
+    // the usual T is equality-comparable with itself
+    a == a;
+    a == ca;
+    ca == ca;
+
+    // Ref<T> is equality-comparable with itself
+    r == r;
+    r == cr;
+    cr == cr;
+
+    // T and Ref<T> are equality-comparable
+    a == r;
+    a == cr;
+    ca == r;
+    ca == cr;
+};
+
+static_assert( ref_equality_comparable<int> );
+
+struct A {
+    auto operator==(A const&) const -> bool { return true; }
+};
+
+struct B {
+    friend auto operator==(B const&, B const&) -> bool { return true; }
+};
+
+template <class T>
+struct C {
+    friend auto operator==(C const&, C const&) -> bool { return true; }
+};
+
+template <class T>
+struct D { };
+template <class T>
+auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
+
+static_assert(ref_equality_comparable<int>);
+static_assert(ref_equality_comparable<A>);
+static_assert(ref_equality_comparable<B>);
+static_assert(ref_equality_comparable<C<int>>);
+static_assert(ref_equality_comparable<D<int>>);
+#include <string_view>
+static_assert(ref_equality_comparable<std::string_view>);
+
+template <typename T>
+struct ValArray {
+  friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
+    return {};
+  }
+};
+
+void f(ValArray<int> v) {
+  // this is valid and has type ValArray<bool>
+  v == v;
+
+  // this is also valid today and has the same type
+  std::ref(v) == std::ref(v);
+}
+
+struct ComparesAsInt {
+  friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
+};
+
+auto f(std::reference_wrapper<ComparesAsInt> a,
+    std::reference_wrapper<ComparesAsInt> b) {
+  // today: compiles and returns int
+  // proposed: compiles and returns bool
+  return a == b;
+}
+
+ComparesAsInt& c();
+static_assert( std::is_same_v<decltype(f(c(), c())), bool> );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant
  2024-04-10  8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
@ 2024-04-10  8:45 ` Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
  2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
  3 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10  8:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux.

Since this only affects C++20 and later (except for adding [[nodiscard]]
to relational ops) it seems OK for trunk now.

-- >8 --

Implement the changes from P2944R3 which add constraints to the
comparison operators of std::pair, std::tuple, and std::variant.

The paper also changes std::optional, but we already constrain its
comparisons using SFINAE on the return type. However, we need some
additional constraints on the [optional.comp.with.t] operators that
compare an optional with a value. The paper doesn't say to do that, but
I think it's needed because otherwise when the comparison for two
optional objects fails its constraints, the two overloads that are
supposed to be for comparing to a non-optional become the best overload
candidates, but are ambiguous (and we don't even get as far as checking
the constraints for satisfaction).

The paper does not change std::expected, but probably should have done.
I'll submit an LWG issue about that and implement it separately.

Also add [[nodiscard]] to all these comparison operators.

libstdc++-v3/ChangeLog:

	* include/bits/stl_pair.h (operator==): Add constraint.
	* include/bits/version.def (constrained_equality): Define.
	* include/bits/version.h: Regenerate.
	* include/std/optional: Define feature test macro.
	(__optional_rep_op_t): Use is_convertible_v instead of
	is_convertible.
	* include/std/tuple: Define feature test macro.
	(operator==, __tuple_cmp, operator<=>): Reimplement C++20
	comparisons using lambdas. Add constraints.
	* include/std/utility: Define feature test macro.
	* include/std/variant: Define feature test macro.
	(_VARIANT_RELATION_FUNCTION_TEMPLATE): Add constraints.
	(variant): Remove unnecessary friend declarations for comparison
	operators.
	* testsuite/20_util/optional/relops/constrained.cc: New test.
	* testsuite/20_util/pair/comparison_operators/constrained.cc:
	New test.
	* testsuite/20_util/tuple/comparison_operators/constrained.cc:
	New test.
	* testsuite/20_util/variant/relops/constrained.cc: New test.
	* testsuite/20_util/tuple/comparison_operators/overloaded.cc:
	Disable for C++20 and later.
	* testsuite/20_util/tuple/comparison_operators/overloaded2.cc:
	Remove dg-error line for target c++20.
---
 libstdc++-v3/include/bits/stl_pair.h          |  16 +-
 libstdc++-v3/include/bits/version.def         |   9 +
 libstdc++-v3/include/bits/version.h           |  10 +
 libstdc++-v3/include/std/optional             |  48 +++-
 libstdc++-v3/include/std/tuple                | 102 ++++---
 libstdc++-v3/include/std/utility              |   1 +
 libstdc++-v3/include/std/variant              |  28 +-
 .../20_util/optional/relops/constrained.cc    | 258 ++++++++++++++++++
 .../pair/comparison_operators/constrained.cc  |  48 ++++
 .../tuple/comparison_operators/constrained.cc |  50 ++++
 .../tuple/comparison_operators/overloaded.cc  |   6 +-
 .../tuple/comparison_operators/overloaded2.cc |   1 -
 .../20_util/variant/relops/constrained.cc     | 175 ++++++++++++
 13 files changed, 677 insertions(+), 75 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc

diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 45317417c9c..0c1e5719a1a 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -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); }
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 5c0477fb61e..f0ba4f2bb3d 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -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 = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 65e708c73fb..f30f51dcedc 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -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
diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional
index 3507c36a4d8..e245e76e7a0 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -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,94 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     { return !__rhs; }
 #endif // three-way-comparison
 
+#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 +1454,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
 
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index 3065058e184..df3f6e38eeb 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -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)
diff --git a/libstdc++-v3/include/std/utility b/libstdc++-v3/include/std/utility
index 212513f6f48..56467160a2f 100644
--- a/libstdc++-v3/include/std/utility
+++ b/libstdc++-v3/include/std/utility
@@ -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)
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index f79d95db7a8..5ba6d9d42e3 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -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>
diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
new file mode 100644
index 00000000000..0e325618008
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
@@ -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> );
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
new file mode 100644
index 00000000000..a35dbd265a7
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
@@ -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>> );
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
new file mode 100644
index 00000000000..47035ab18ba
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
@@ -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>> );
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
index 9f8a0f91785..7ae7f42d1a1 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded.cc
@@ -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
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
index 19617a5676e..8ea7ed0b797 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/overloaded2.cc
@@ -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 }
diff --git a/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
new file mode 100644
index 00000000000..95e8f754d1e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
@@ -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> );
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 4/4] libstdc++: Simplify std::variant comparison operators
  2024-04-10  8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
  2024-04-10  8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
@ 2024-04-10  8:45 ` Jonathan Wakely
  2024-04-10 10:45   ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
  2024-05-07 13:45   ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
  2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
  3 siblings, 2 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10  8:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Tested x86_64-linux.

This is just a minor clean-up and could wait for stage 1.

-- >8 --

libstdc++-v3/ChangeLog:

	* include/std/variant (_VARIANT_RELATION_FUNCTION_TEMPLATE):
	Simplify.
---
 libstdc++-v3/include/std/variant | 20 +++++++++-----------
 1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 5ba6d9d42e3..2be0f0c1db7 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -1245,7 +1245,7 @@ namespace __variant
 # define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
 #endif
 
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
+#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
   template<typename... _Types> \
     _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
     constexpr bool \
@@ -1262,22 +1262,20 @@ namespace __variant
 	        { \
 		  auto& __this_mem = std::get<__rhs_index>(__lhs);	\
                   __ret = __this_mem __OP __rhs_mem; \
+		  return; \
                 } \
-	      else \
-		__ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
             } \
-          else \
-            __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
+	  __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
 	}, __rhs); \
       return __ret; \
     }
 
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(<, less)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(<=, less_equal)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(==, equal)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(!=, not_equal)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(>=, greater_equal)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(>, greater)
+  _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
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros
  2024-04-10  8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
@ 2024-04-10 10:45   ` Jonathan Wakely
  2024-05-07 13:45   ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
  1 sibling, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-10 10:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

I think this is considerably nicer than the macro version, but it can
definitely wait for stage 1.

-- >8 --

libstdc++-v3/ChangeLog:

	* include/std/variant (__detail::__variant::__compare): New
	function template.
	(operator==, operator!=, operator<, operator>, operator<=)
	(operator>=): Replace macro definition with handwritten function
	calling __detail::__variant::__compare.
	(operator<=>): Call __detail::__variant::__compare.
---
 libstdc++-v3/include/std/variant | 167 +++++++++++++++++++++----------
 1 file changed, 114 insertions(+), 53 deletions(-)

diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index 87a119df8b5..c2277e8831a 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -48,6 +48,7 @@
 #include <bits/stl_construct.h>
 #include <bits/utility.h> // in_place_index_t
 #if __cplusplus >= 202002L
+# include <concepts>
 # include <compare>
 #endif
 
@@ -1237,47 +1238,119 @@ 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
+namespace __detail::__variant
+{
+  template<typename _Ret, typename _Vp, typename _Op>
+    constexpr _Ret
+    __compare(_Ret __ret, const _Vp& __lhs, const _Vp& __rhs, _Op __op)
+    {
+      __variant::__raw_idx_visit(
+	[&__ret, &__lhs, __op] (auto&& __rhs_mem, auto __rhs_index) mutable
+	{
+	  if constexpr (__rhs_index != variant_npos)
+	    {
+	      if (__lhs.index() == __rhs_index.value)
+		{
+		  auto& __this_mem = std::get<__rhs_index>(__lhs);
+		  __ret = __op(__this_mem, __rhs_mem);
+		  return;
+		}
+	    }
+	  __ret = __op(__lhs.index() + 1, __rhs_index + 1);
+	}, __rhs);
+      return __ret;
+    }
+} // namespace __detail::__variant
 
-#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
-  template<typename... _Types> \
-    _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
-    constexpr bool \
-    operator __OP [[nodiscard]] (const variant<_Types...>& __lhs, \
-				 const variant<_Types...>& __rhs) \
-    { \
-      bool __ret = true; \
-      __detail::__variant::__raw_idx_visit( \
-        [&__ret, &__lhs] (auto&& __rhs_mem, auto __rhs_index) mutable \
-        { \
-	  if constexpr (__rhs_index != variant_npos) \
-	    { \
-	      if (__lhs.index() == __rhs_index) \
-	        { \
-		  auto& __this_mem = std::get<__rhs_index>(__lhs);	\
-                  __ret = __this_mem __OP __rhs_mem; \
-		  return; \
-                } \
-            } \
-	  __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
-	}, __rhs); \
-      return __ret; \
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t == __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator== [[nodiscard]] (const variant<_Types...>& __lhs,
+			      const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l == __r;
+					    });
     }
 
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(<)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(<=)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(==)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(!=)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(>=)
-  _VARIANT_RELATION_FUNCTION_TEMPLATE(>)
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t != __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator!= [[nodiscard]] (const variant<_Types...>& __lhs,
+			      const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l != __r;
+					    });
+    }
 
-#undef _VARIANT_RELATION_FUNCTION_TEMPLATE
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t < __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator< [[nodiscard]] (const variant<_Types...>& __lhs,
+			     const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l < __r;
+					    });
+    }
+
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t <= __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator<= [[nodiscard]] (const variant<_Types...>& __lhs,
+			      const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l <= __r;
+					    });
+    }
+
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t > __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator> [[nodiscard]] (const variant<_Types...>& __lhs,
+			     const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l > __r;
+					    });
+    }
+
+  template<typename... _Types>
+#if __cpp_lib_concepts
+    requires ((requires (const _Types& __t) {
+      { __t >= __t } -> convertible_to<bool>; }) && ...)
+#endif
+    constexpr bool
+    operator>= [[nodiscard]] (const variant<_Types...>& __lhs,
+			      const variant<_Types...>& __rhs)
+    {
+      return __detail::__variant::__compare(true, __lhs, __rhs,
+					    [](auto&& __l, auto&& __r) {
+					      return __l >= __r;
+					    });
+    }
 
   constexpr bool operator==(monostate, monostate) noexcept { return true; }
 
@@ -1290,22 +1363,10 @@ namespace __variant
     {
       common_comparison_category_t<compare_three_way_result_t<_Types>...> __ret
 	= strong_ordering::equal;
-
-      __detail::__variant::__raw_idx_visit(
-	[&__ret, &__v] (auto&& __w_mem, auto __w_index) mutable
-	{
-	  if constexpr (__w_index != variant_npos)
-	    {
-	      if (__v.index() == __w_index)
-		{
-		  auto& __this_mem = std::get<__w_index>(__v);
-		  __ret = __this_mem <=> __w_mem;
-		  return;
-		}
-	    }
-	  __ret = (__v.index() + 1) <=> (__w_index + 1);
-	}, __w);
-      return __ret;
+      return __detail::__variant::__compare(__ret, __v, __w,
+					    [](auto&& __l, auto&& __r) {
+					      return __l <=> __r;
+					    });
     }
 
   constexpr strong_ordering
-- 
2.44.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386]
  2024-04-10  8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
                   ` (2 preceding siblings ...)
  2024-04-10  8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
@ 2024-04-15 18:29 ` Jonathan Wakely
  3 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-15 18:29 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Pushed to trunk now.

On Wed, 10 Apr 2024 at 09:51, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Tested x86_64-linux.
>
> Since this only affects C++20 and later it seems OK for trunk now.
>
> -- >8 --
>
> I'm only treating this as a DR for C++20 for now, because it's less work
> and only requires changes to operator== and operator<=>. To do this for
> older standards would require changes to the six relational operators
> used pre-C++20.
>
> libstdc++-v3/ChangeLog:
>
>         PR libstdc++/113386
>         * include/bits/stl_pair.h (operator==, operator<=>): Support
>         heterogeneous comparisons, as per LWG 3865.
>         * testsuite/20_util/pair/comparison_operators/lwg3865.cc: New
>         test.
> ---
>  libstdc++-v3/include/bits/stl_pair.h          | 32 ++++++++++++++-----
>  .../pair/comparison_operators/lwg3865.cc      | 15 +++++++++
>  2 files changed, 39 insertions(+), 8 deletions(-)
>  create mode 100644 libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
>
> diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
> index 4f5c8389fa6..45317417c9c 100644
> --- a/libstdc++-v3/include/bits/stl_pair.h
> +++ b/libstdc++-v3/include/bits/stl_pair.h
> @@ -1000,23 +1000,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    template<typename _T1, typename _T2> pair(_T1, _T2) -> pair<_T1, _T2>;
>  #endif
>
> -  /// Two pairs of the same type are equal iff their members are equal.
> -  template<typename _T1, typename _T2>
> +#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
> +  // _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
> -    operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> +    operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y)
>      { return __x.first == __y.first && __x.second == __y.second; }
>
> -#if __cpp_lib_three_way_comparison && __cpp_lib_concepts
> -  template<typename _T1, typename _T2>
> -    constexpr common_comparison_category_t<__detail::__synth3way_t<_T1>,
> -                                          __detail::__synth3way_t<_T2>>
> -    operator<=>(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> +  /** Defines a lexicographical order for pairs.
> +   *
> +   * For two pairs of comparable types, `P` is ordered before `Q` if
> +   * `P.first` is less than `Q.first`, or if `P.first` and `Q.first`
> +   * are equivalent (neither is less than the other) and `P.second` is
> +   * less than `Q.second`.
> +  */
> +  template<typename _T1, typename _T2, typename _U1, typename _U2>
> +    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)
>      {
>        if (auto __c = __detail::__synth3way(__x.first, __y.first); __c != 0)
>         return __c;
>        return __detail::__synth3way(__x.second, __y.second);
>      }
>  #else
> +  /// Two pairs of the same type are equal iff their members are equal.
> +  template<typename _T1, typename _T2>
> +    inline _GLIBCXX_CONSTEXPR bool
> +    operator==(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
> +    { return __x.first == __y.first && __x.second == __y.second; }
> +
>    /** Defines a lexicographical order for pairs.
>     *
>     * For two pairs of the same type, `P` is ordered before `Q` if
> diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
> new file mode 100644
> index 00000000000..2bbd54af192
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/lwg3865.cc
> @@ -0,0 +1,15 @@
> +// { dg-do run { target c++20 } }
> +
> +// LWG 3865. Sorting a range of pairs
> +
> +#include <utility>
> +#include <testsuite_hooks.h>
> +
> +int main()
> +{
> +  std::pair<int, int> p(1, 2);
> +  std::pair<int&, int&> p2(p.first, p.second);
> +  VERIFY( p == p2 );
> +  VERIFY( p <= p2 );
> +  VERIFY( p >= p2 );
> +}
> --
> 2.44.0
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26
  2024-04-10  8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
@ 2024-04-15 18:30   ` Jonathan Wakely
  0 siblings, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-04-15 18:30 UTC (permalink / raw)
  To: libstdc++, gcc-patches

Pushed to trunk now.



On Wed, 10 Apr 2024 at 09:53, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> Tested x86_64-linux.
>
> Since this only affects C++26 it seems OK for trunk now.
>
> -- >8 --
>
> This C++26 change was just approved in Tokyo, in P2944R3. It adds
> operator== and operator<=> overloads to std::reference_wrapper.
>
> The operator<=> overloads in the paper cause compilation errors for any
> type without <=> so they're implemented here with deduced return types
> and constrained by a requires clause.
>
> libstdc++-v3/ChangeLog:
>
>         * include/bits/refwrap.h (reference_wrapper): Add comparison
>         operators as proposed by P2944R3.
>         * include/bits/version.def (reference_wrapper): Define.
>         * include/bits/version.h: Regenerate.
>         * include/std/functional: Enable feature test macro.
>         * testsuite/20_util/reference_wrapper/compare.cc: New test.
> ---
>  libstdc++-v3/include/bits/refwrap.h           | 45 +++++++++
>  libstdc++-v3/include/bits/version.def         |  8 ++
>  libstdc++-v3/include/bits/version.h           | 10 ++
>  libstdc++-v3/include/std/functional           |  1 +
>  .../20_util/reference_wrapper/compare.cc      | 95 +++++++++++++++++++
>  5 files changed, 159 insertions(+)
>  create mode 100644 libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
>
> diff --git a/libstdc++-v3/include/bits/refwrap.h b/libstdc++-v3/include/bits/refwrap.h
> index 2d4338b718f..fd1cc2b63e6 100644
> --- a/libstdc++-v3/include/bits/refwrap.h
> +++ b/libstdc++-v3/include/bits/refwrap.h
> @@ -38,6 +38,10 @@
>  #include <bits/invoke.h>
>  #include <bits/stl_function.h> // for unary_function and binary_function
>
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> +# include <compare>
> +#endif
> +
>  namespace std _GLIBCXX_VISIBILITY(default)
>  {
>  _GLIBCXX_BEGIN_NAMESPACE_VERSION
> @@ -358,6 +362,47 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, false_type, true_type)
>  #endif
>           return std::__invoke(get(), std::forward<_Args>(__args)...);
>         }
> +
> +#if __glibcxx_reference_wrapper >= 202403L // >= C++26
> +      // [refwrap.comparisons], comparisons
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, reference_wrapper __y)
> +      requires requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> +      { return __x.get() == __y.get(); }
> +
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, const _Tp& __y)
> +      requires requires { { __x.get() == __y } -> convertible_to<bool>; }
> +      { return __x.get() == __y; }
> +
> +      [[nodiscard]]
> +      friend constexpr bool
> +      operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> +      requires (!is_const_v<_Tp>)
> +       && requires { { __x.get() == __y.get() } -> convertible_to<bool>; }
> +      { return __x.get() == __y.get(); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, reference_wrapper<_Tp> __y)
> +      requires requires { __detail::__synth3way(__x.get(), __y.get()); }
> +      { return __detail::__synth3way(__x.get(), __y.get()); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, const _Tp& __y)
> +      requires requires { __detail::__synth3way(__x.get(), __y); }
> +      { return __detail::__synth3way(__x.get(), __y); }
> +
> +      [[nodiscard]]
> +      friend constexpr auto
> +      operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
> +      requires (!is_const_v<_Tp>)
> +       && requires { __detail::__synth3way(__x.get(), __y.get()); }
> +      { return __detail::__synth3way(__x.get(), __y.get()); }
> +#endif
>      };
>
>  #if __cpp_deduction_guides
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 5ad44941bff..5c0477fb61e 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1760,6 +1760,14 @@ ftms = {
>    };
>  };
>
> +ftms = {
> +  name = reference_wrapper;
> +  values = {
> +    v = 202403;
> +    cxxmin = 26;
> +  };
> +};
> +
>  ftms = {
>    name = saturation_arithmetic;
>    values = {
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 460a3e0116a..65e708c73fb 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -1963,6 +1963,16 @@
>  #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
>  #undef __glibcxx_want_ratio
>
> +#if !defined(__cpp_lib_reference_wrapper)
> +# if (__cplusplus >  202302L)
> +#  define __glibcxx_reference_wrapper 202403L
> +#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_reference_wrapper)
> +#   define __cpp_lib_reference_wrapper 202403L
> +#  endif
> +# endif
> +#endif /* !defined(__cpp_lib_reference_wrapper) && defined(__glibcxx_want_reference_wrapper) */
> +#undef __glibcxx_want_reference_wrapper
> +
>  #if !defined(__cpp_lib_saturation_arithmetic)
>  # if (__cplusplus >  202302L)
>  #  define __glibcxx_saturation_arithmetic 202311L
> diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional
> index 766558b3ce0..99364286a72 100644
> --- a/libstdc++-v3/include/std/functional
> +++ b/libstdc++-v3/include/std/functional
> @@ -83,6 +83,7 @@
>  #define __glibcxx_want_move_only_function
>  #define __glibcxx_want_not_fn
>  #define __glibcxx_want_ranges
> +#define __glibcxx_want_reference_wrapper
>  #define __glibcxx_want_transparent_operators
>  #include <bits/version.h>
>
> diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> new file mode 100644
> index 00000000000..039c9d26496
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/compare.cc
> @@ -0,0 +1,95 @@
> +// { dg-do compile { target c++26 } }
> +
> +
> +#include <functional>
> +
> +#ifndef __cpp_lib_reference_wrapper
> +# error "Feature-test macro for reference_wrapper missing"
> +#elif __cpp_lib_reference_wrapper != 202403
> +# error "Feature-test macro for reference_wrapper has wrong value"
> +#endif
> +
> +// P2944R3 Comparisons for reference_wrapper
> +
> +auto check(int i, std::reference_wrapper<int> r) -> bool {
> +  return i == r;
> +}
> +
> +template <class T> using Ref = std::reference_wrapper<T>;
> +
> +template <class T>
> +concept ref_equality_comparable
> += requires (T a, T const ca, Ref<T> r, Ref<T const> cr) {
> +    // the usual T is equality-comparable with itself
> +    a == a;
> +    a == ca;
> +    ca == ca;
> +
> +    // Ref<T> is equality-comparable with itself
> +    r == r;
> +    r == cr;
> +    cr == cr;
> +
> +    // T and Ref<T> are equality-comparable
> +    a == r;
> +    a == cr;
> +    ca == r;
> +    ca == cr;
> +};
> +
> +static_assert( ref_equality_comparable<int> );
> +
> +struct A {
> +    auto operator==(A const&) const -> bool { return true; }
> +};
> +
> +struct B {
> +    friend auto operator==(B const&, B const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct C {
> +    friend auto operator==(C const&, C const&) -> bool { return true; }
> +};
> +
> +template <class T>
> +struct D { };
> +template <class T>
> +auto operator==(D<T> const&, D<T> const&) -> bool { return true; }
> +
> +static_assert(ref_equality_comparable<int>);
> +static_assert(ref_equality_comparable<A>);
> +static_assert(ref_equality_comparable<B>);
> +static_assert(ref_equality_comparable<C<int>>);
> +static_assert(ref_equality_comparable<D<int>>);
> +#include <string_view>
> +static_assert(ref_equality_comparable<std::string_view>);
> +
> +template <typename T>
> +struct ValArray {
> +  friend auto operator==(ValArray const&, ValArray const&) -> ValArray<bool> {
> +    return {};
> +  }
> +};
> +
> +void f(ValArray<int> v) {
> +  // this is valid and has type ValArray<bool>
> +  v == v;
> +
> +  // this is also valid today and has the same type
> +  std::ref(v) == std::ref(v);
> +}
> +
> +struct ComparesAsInt {
> +  friend auto operator==(ComparesAsInt, ComparesAsInt) -> int;
> +};
> +
> +auto f(std::reference_wrapper<ComparesAsInt> a,
> +    std::reference_wrapper<ComparesAsInt> b) {
> +  // today: compiles and returns int
> +  // proposed: compiles and returns bool
> +  return a == b;
> +}
> +
> +ComparesAsInt& c();
> +static_assert( std::is_same_v<decltype(f(c(), c())), bool> );
> --
> 2.44.0
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 4/4] libstdc++: Simplify std::variant comparison operators
  2024-04-10  8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
  2024-04-10 10:45   ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
@ 2024-05-07 13:45   ` Jonathan Wakely
  1 sibling, 0 replies; 8+ messages in thread
From: Jonathan Wakely @ 2024-05-07 13:45 UTC (permalink / raw)
  To: libstdc++, gcc-patches

On Wed, 10 Apr 2024 at 09:51, Jonathan Wakely wrote:
>
> Tested x86_64-linux.
>
> This is just a minor clean-up and could wait for stage 1.

Pushed now.

>
> -- >8 --
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/variant (_VARIANT_RELATION_FUNCTION_TEMPLATE):
>         Simplify.
> ---
>  libstdc++-v3/include/std/variant | 20 +++++++++-----------
>  1 file changed, 9 insertions(+), 11 deletions(-)
>
> diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
> index 5ba6d9d42e3..2be0f0c1db7 100644
> --- a/libstdc++-v3/include/std/variant
> +++ b/libstdc++-v3/include/std/variant
> @@ -1245,7 +1245,7 @@ namespace __variant
>  # define _VARIANT_RELATION_FUNCTION_CONSTRAINTS(TYPES, OP)
>  #endif
>
> -#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP, __NAME) \
> +#define _VARIANT_RELATION_FUNCTION_TEMPLATE(__OP) \
>    template<typename... _Types> \
>      _VARIANT_RELATION_FUNCTION_CONSTRAINTS(_Types, __OP) \
>      constexpr bool \
> @@ -1262,22 +1262,20 @@ namespace __variant
>                 { \
>                   auto& __this_mem = std::get<__rhs_index>(__lhs);      \
>                    __ret = __this_mem __OP __rhs_mem; \
> +                 return; \
>                  } \
> -             else \
> -               __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
>              } \
> -          else \
> -            __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
> +         __ret = (__lhs.index() + 1) __OP (__rhs_index + 1); \
>         }, __rhs); \
>        return __ret; \
>      }
>
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(<, less)
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(<=, less_equal)
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(==, equal)
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(!=, not_equal)
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(>=, greater_equal)
> -  _VARIANT_RELATION_FUNCTION_TEMPLATE(>, greater)
> +  _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
>
> --
> 2.44.0
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2024-05-07 13:45 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-10  8:45 [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely
2024-04-10  8:45 ` [PATCH 2/4] libstdc++: Add std::reference_wrapper comparison operators for C++26 Jonathan Wakely
2024-04-15 18:30   ` Jonathan Wakely
2024-04-10  8:45 ` [PATCH 3/4] libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant Jonathan Wakely
2024-04-10  8:45 ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-10 10:45   ` [PATCH 5/4] libstdc++: Rewrite std::variant comparisons without macros Jonathan Wakely
2024-05-07 13:45   ` [PATCH 4/4] libstdc++: Simplify std::variant comparison operators Jonathan Wakely
2024-04-15 18:29 ` [PATCH 1/4] libstdc++: Heterogeneous std::pair comparisons [PR113386] Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).