diff mbox series

[committed] libstdc++: Constrain std::expected comparisons (P3379R0)

Message ID 20241014155031.1901606-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Constrain std::expected comparisons (P3379R0) | expand

Commit Message

Jonathan Wakely Oct. 14, 2024, 3:50 p.m. UTC
Tested x86_64-linux. Pushed to trunk.

-- >8 --

This proposal of mine has been approved by LEWG and forwarded to LWG. I
expect it to be voted into the draft without significant changes.

libstdc++-v3/ChangeLog:

	* include/bits/version.def (constrained_equality): Bump value.
	* include/bits/version.h: Regenerate.
	* include/std/expected (operator==): Add constraints and
	noexcept specifiers.
	* testsuite/20_util/optional/relops/constrained.cc: Adjust
	check for feature test macro.
	* testsuite/20_util/pair/comparison_operators/constrained.cc:
	Likewise.
	* testsuite/20_util/tuple/comparison_operators/constrained.cc:
	Likewise.
	* testsuite/20_util/variant/relops/constrained.cc: Likewise.
	* testsuite/20_util/expected/equality_constrained.cc: New test.
---
 libstdc++-v3/include/bits/version.def         |   2 +-
 libstdc++-v3/include/bits/version.h           |   4 +-
 libstdc++-v3/include/std/expected             |  31 ++-
 .../20_util/expected/equality_constrained.cc  | 181 ++++++++++++++++++
 .../20_util/optional/relops/constrained.cc    |   2 +-
 .../pair/comparison_operators/constrained.cc  |   2 +-
 .../tuple/comparison_operators/constrained.cc |   2 +-
 .../20_util/variant/relops/constrained.cc     |   2 +-
 8 files changed, 213 insertions(+), 13 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/expected/equality_constrained.cc
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 477651f358a..4fa820a5a4a 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1256,7 +1256,7 @@  ftms = {
 ftms = {
   name = constrained_equality;
   values = {
-    v = 202403;
+    v = 202411; // FIXME: 202403 for P2944R3, ??? for P3379R0
     cxxmin = 20;
     extra_cond = "__glibcxx_three_way_comparison";
   };
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 342ee6b4c7a..fdbee8161d9 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1392,9 +1392,9 @@ 
 
 #if !defined(__cpp_lib_constrained_equality)
 # if (__cplusplus >= 202002L) && (__glibcxx_three_way_comparison)
-#  define __glibcxx_constrained_equality 202403L
+#  define __glibcxx_constrained_equality 202411L
 #  if defined(__glibcxx_want_all) || defined(__glibcxx_want_constrained_equality)
-#   define __cpp_lib_constrained_equality 202403L
+#   define __cpp_lib_constrained_equality 202411L
 #  endif
 # endif
 #endif /* !defined(__cpp_lib_constrained_equality) && defined(__glibcxx_want_constrained_equality) */
diff --git a/libstdc++-v3/include/std/expected b/libstdc++-v3/include/std/expected
index d4a4bc17541..7de2aeffc70 100644
--- a/libstdc++-v3/include/std/expected
+++ b/libstdc++-v3/include/std/expected
@@ -35,6 +35,7 @@ 
 
 #define __glibcxx_want_expected
 #define __glibcxx_want_freestanding_expected
+#define __glibcxx_want_constrained_equality
 #include <bits/version.h>
 
 #ifdef __cpp_lib_expected // C++ >= 23 && __cpp_concepts >= 202002L
@@ -1152,10 +1153,15 @@  namespace __expected
 
       template<typename _Up, typename _Er2>
 	requires (!is_void_v<_Up>)
+	  && requires (const _Tp& __t, const _Up& __u,
+		       const _Er& __e, const _Er2& __e2) {
+	    { __t == __u } -> convertible_to<bool>;
+	    { __e == __e2 } -> convertible_to<bool>;
+	  }
 	friend constexpr bool
 	operator==(const expected& __x, const expected<_Up, _Er2>& __y)
-	// FIXME: noexcept(noexcept(bool(*__x == *__y))
-		  // && noexcept(bool(__x.error() == __y.error())))
+	noexcept(noexcept(bool(*__x == *__y))
+		  && noexcept(bool(__x.error() == __y.error())))
 	{
 	  if (__x.has_value())
 	    return __y.has_value() && bool(*__x == *__y);
@@ -1164,15 +1170,22 @@  namespace __expected
 	}
 
       template<typename _Up>
+	requires (!__expected::__is_expected<_Up>)
+	  && requires (const _Tp& __t, const _Up& __u) {
+	    { __t == __u } -> convertible_to<bool>;
+	  }
 	friend constexpr bool
 	operator==(const expected& __x, const _Up& __v)
-	// FIXME: noexcept(noexcept(bool(*__x == __v)))
+	noexcept(noexcept(bool(*__x == __v)))
 	{ return __x.has_value() && bool(*__x == __v); }
 
       template<typename _Er2>
+	requires requires (const _Er& __e, const _Er2& __e2) {
+	  { __e == __e2 } -> convertible_to<bool>;
+	}
 	friend constexpr bool
 	operator==(const expected& __x, const unexpected<_Er2>& __e)
-	// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
+	noexcept(noexcept(bool(__x.error() == __e.error())))
 	{ return !__x.has_value() && bool(__x.error() == __e.error()); }
 
       friend constexpr void
@@ -1819,9 +1832,12 @@  namespace __expected
 
       template<typename _Up, typename _Er2>
 	requires is_void_v<_Up>
+	  && requires (const _Er& __e, const _Er2& __e2) {
+	    { __e == __e2 } -> convertible_to<bool>;
+	  }
 	friend constexpr bool
 	operator==(const expected& __x, const expected<_Up, _Er2>& __y)
-	// FIXME: noexcept(noexcept(bool(__x.error() == __y.error())))
+	noexcept(noexcept(bool(__x.error() == __y.error())))
 	{
 	  if (__x.has_value())
 	    return __y.has_value();
@@ -1830,9 +1846,12 @@  namespace __expected
 	}
 
       template<typename _Er2>
+	requires requires (const _Er& __e, const _Er2& __e2) {
+	  { __e == __e2 } -> convertible_to<bool>;
+	}
 	friend constexpr bool
 	operator==(const expected& __x, const unexpected<_Er2>& __e)
-	// FIXME: noexcept(noexcept(bool(__x.error() == __e.error())))
+	noexcept(noexcept(bool(__x.error() == __e.error())))
 	{ return !__x.has_value() && bool(__x.error() == __e.error()); }
 
       friend constexpr void
diff --git a/libstdc++-v3/testsuite/20_util/expected/equality_constrained.cc b/libstdc++-v3/testsuite/20_util/expected/equality_constrained.cc
new file mode 100644
index 00000000000..7f6cefae748
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/expected/equality_constrained.cc
@@ -0,0 +1,181 @@ 
+// { dg-do compile { target c++23 } }
+
+#include <expected>
+
+#ifndef __cpp_lib_constrained_equality
+# error "Feature-test macro for constrained_equality missing in <expected>"
+#elif __cpp_lib_constrained_equality < 202411L // TODO: use final value
+# error "Feature-test macro for constrained_equality has wrong value"
+#endif
+
+template<typename T, typename U = T, typename E = int, typename E2 = E>
+concept eq_comparable
+= requires (const std::expected<T, E>& t, const std::expected<U, E2>& u) {
+  t == u;
+};
+
+static_assert( eq_comparable<int> );
+static_assert( eq_comparable<int, long> );
+static_assert( eq_comparable<short, int> );
+static_assert( eq_comparable<int, long, unsigned int, unsigned long> );
+static_assert( eq_comparable<void, const void> );
+
+struct A { };
+static_assert( ! eq_comparable<A> );
+static_assert( ! eq_comparable<A, A> );
+static_assert( ! eq_comparable<A, int> );
+static_assert( ! eq_comparable<int, int, A> );
+static_assert( ! eq_comparable<int, int, int, A> );
+static_assert( ! eq_comparable<void, const void, A> );
+
+struct B { };
+void operator==(B, B);
+static_assert( ! eq_comparable<B> );
+static_assert( ! eq_comparable<B, B> );
+static_assert( ! eq_comparable<B, int> );
+static_assert( ! eq_comparable<int, int, B> );
+static_assert( ! eq_comparable<int, int, int, B> );
+static_assert( ! eq_comparable<void, void, B> );
+
+struct C { };
+bool operator==(C, C);
+static_assert( eq_comparable<C> );
+static_assert( eq_comparable<C, C> );
+static_assert( eq_comparable<C, C> );
+static_assert( eq_comparable<C, C, C> );
+static_assert( eq_comparable<int, int, C> );
+static_assert( ! eq_comparable<C, C, A, A> );
+static_assert( ! eq_comparable<C, C, A, int> );
+static_assert( eq_comparable<void, void, C> );
+
+struct D { };
+int operator==(D, D);
+bool operator!=(D, D) = delete;
+static_assert( eq_comparable<D> );
+static_assert( eq_comparable<D, D> );
+static_assert( eq_comparable<int, int, D> );
+static_assert( eq_comparable<D, D, D> );
+static_assert( ! eq_comparable<D, D, D, int> );
+
+struct E { };
+bool operator==(/* not-const */ E&, const E&);
+static_assert( ! eq_comparable<E> );
+static_assert( ! eq_comparable<E, E> );
+static_assert( ! eq_comparable<E, E> );
+static_assert( ! eq_comparable<int, int, E> );
+
+bool operator==(A, B);
+static_assert( eq_comparable<A, B> );
+static_assert( eq_comparable<B, A> );
+static_assert( eq_comparable<int, long, B, A> );
+
+int operator==(C, D);
+static_assert( eq_comparable<C, D> );
+static_assert( eq_comparable<D, C> );
+static_assert( eq_comparable<int, int, C, D> );
+static_assert( eq_comparable<int, int, D, C> );
+static_assert( eq_comparable<const void, void, C, D> );
+static_assert( eq_comparable<const void, void, D, C> );
+
+template<typename T, typename U = T, typename E = int>
+concept eq_comparable_val
+= requires (const std::expected<T, E>& t, const U& u) {
+  t == u;
+};
+
+static_assert( eq_comparable_val<int> );
+static_assert( eq_comparable_val<int, long> );
+static_assert( eq_comparable_val<short, int> );
+static_assert( eq_comparable_val<int, long, unsigned> );
+static_assert( ! eq_comparable_val<void, const void> );
+
+static_assert( ! eq_comparable_val<A> );
+static_assert( ! eq_comparable_val<A, A> );
+static_assert( ! eq_comparable_val<A, int> );
+static_assert( eq_comparable_val<int, int, A> );
+static_assert( ! eq_comparable_val<void, A> );
+
+static_assert( ! eq_comparable_val<B> );
+static_assert( ! eq_comparable_val<B, B> );
+static_assert( ! eq_comparable_val<B, int> );
+static_assert( eq_comparable_val<int, int, B> );
+static_assert( ! eq_comparable_val<void, B> );
+
+static_assert( eq_comparable_val<C> );
+static_assert( eq_comparable_val<C, C> );
+static_assert( eq_comparable_val<C, C> );
+static_assert( eq_comparable_val<C, C, C> );
+static_assert( eq_comparable_val<int, int, C> );
+static_assert( eq_comparable_val<C, C, A> );
+static_assert( ! eq_comparable_val<void, C> );
+
+static_assert( eq_comparable_val<D> );
+static_assert( eq_comparable_val<D, D> );
+static_assert( ! eq_comparable_val<D, int> );
+static_assert( eq_comparable_val<int, int, D> );
+static_assert( eq_comparable_val<D, D, D> );
+
+static_assert( ! eq_comparable_val<E> );
+static_assert( ! eq_comparable_val<E, E> );
+static_assert( ! eq_comparable_val<E, int> );
+static_assert( eq_comparable_val<int, int, E> );
+
+static_assert( eq_comparable_val<A, B> );
+static_assert( eq_comparable_val<B, A> );
+
+static_assert( eq_comparable_val<C, D> );
+static_assert( ! eq_comparable_val<D, C> );
+
+template<typename E, typename U, typename T = int>
+concept eq_comparable_unex
+= requires (const std::expected<T, E>& t, const std::unexpected<U>& u) {
+  t == u;
+};
+
+static_assert( eq_comparable_unex<int, int> );
+static_assert( eq_comparable_unex<int, long> );
+static_assert( eq_comparable_unex<short, int> );
+static_assert( eq_comparable_unex<int, int, void> );
+static_assert( eq_comparable_unex<int, long, void> );
+static_assert( eq_comparable_unex<short, int, void> );
+
+static_assert( ! eq_comparable_unex<A, A> );
+static_assert( ! eq_comparable_unex<A, int> );
+static_assert( ! eq_comparable_unex<int, A> );
+static_assert( ! eq_comparable_unex<A, A, void> );
+static_assert( ! eq_comparable_unex<A, int, void> );
+static_assert( ! eq_comparable_unex<int, A, void> );
+
+static_assert( ! eq_comparable_unex<B, B> );
+static_assert( ! eq_comparable_unex<B, int> );
+static_assert( ! eq_comparable_unex<int, B> );
+static_assert( ! eq_comparable_unex<B, B, void> );
+static_assert( ! eq_comparable_unex<B, int, void> );
+static_assert( ! eq_comparable_unex<int, B, void> );
+
+static_assert( eq_comparable_unex<C, C> );
+static_assert( eq_comparable_unex<C, C, void> );
+
+static_assert( eq_comparable_unex<D, D> );
+static_assert( ! eq_comparable_unex<D, int> );
+static_assert( ! eq_comparable_unex<int, D> );
+static_assert( eq_comparable_unex<D, D, void> );
+static_assert( ! eq_comparable_unex<D, int, void> );
+static_assert( ! eq_comparable_unex<int, D, void> );
+
+static_assert( ! eq_comparable_unex<E, E> );
+static_assert( ! eq_comparable_unex<E, int> );
+static_assert( ! eq_comparable_unex<int, E> );
+static_assert( ! eq_comparable_unex<E, E, void> );
+static_assert( ! eq_comparable_unex<E, int, void> );
+static_assert( ! eq_comparable_unex<int, E, void> );
+
+static_assert( eq_comparable_unex<A, B> );
+static_assert( eq_comparable_unex<B, A> );
+static_assert( eq_comparable_unex<A, B, void> );
+static_assert( eq_comparable_unex<B, A, void> );
+
+static_assert( eq_comparable_unex<C, D> );
+static_assert( ! eq_comparable_unex<D, C> );
+static_assert( eq_comparable_unex<C, D, void> );
+static_assert( ! eq_comparable_unex<D, C, void> );
diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
index 0e325618008..3e393257928 100644
--- a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
+++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc
@@ -4,7 +4,7 @@ 
 
 #ifndef __cpp_lib_constrained_equality
 # error "Feature-test macro for constrained_equality missing"
-#elif __cpp_lib_constrained_equality != 202403
+#elif __cpp_lib_constrained_equality < 202403L
 # error "Feature-test macro for constrained_equality has wrong value"
 #endif
 
diff --git a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
index a35dbd265a7..dabe35ff746 100644
--- a/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
+++ b/libstdc++-v3/testsuite/20_util/pair/comparison_operators/constrained.cc
@@ -4,7 +4,7 @@ 
 
 #ifndef __cpp_lib_constrained_equality
 # error "Feature-test macro for constrained_equality missing"
-#elif __cpp_lib_constrained_equality != 202403
+#elif __cpp_lib_constrained_equality < 202403L
 # error "Feature-test macro for constrained_equality has wrong value"
 #endif
 
diff --git a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
index 47035ab18ba..994d4a491a1 100644
--- a/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
+++ b/libstdc++-v3/testsuite/20_util/tuple/comparison_operators/constrained.cc
@@ -4,7 +4,7 @@ 
 
 #ifndef __cpp_lib_constrained_equality
 # error "Feature-test macro for constrained_equality missing"
-#elif __cpp_lib_constrained_equality != 202403
+#elif __cpp_lib_constrained_equality < 202403L
 # error "Feature-test macro for constrained_equality has wrong value"
 #endif
 
diff --git a/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
index 95e8f754d1e..fbe10e0ba03 100644
--- a/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
+++ b/libstdc++-v3/testsuite/20_util/variant/relops/constrained.cc
@@ -4,7 +4,7 @@ 
 
 #ifndef __cpp_lib_constrained_equality
 # error "Feature-test macro for constrained_equality missing"
-#elif __cpp_lib_constrained_equality != 202403
+#elif __cpp_lib_constrained_equality < 202403L
 # error "Feature-test macro for constrained_equality has wrong value"
 #endif