[libcxx-commits] [libcxx] [libc++] P3379R1: Constrain `std::expected` equality operators (PR #117664)

Xiaoyang Liu via libcxx-commits libcxx-commits at lists.llvm.org
Sat Nov 30 18:58:21 PST 2024


https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/117664

>From 002fcac983085257ad28dfa9b45d8a7493e7dccc Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Mon, 25 Nov 2024 23:26:19 -0500
Subject: [PATCH 1/5] [libc++] P3379R1: Constrain 'std::expected' equality
 operators

---
 libcxx/include/__expected/expected.h | 22 ++++++++++++++++++++--
 1 file changed, 20 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 3d3f11967ee746..7dc0248fcd72f9 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___EXPECTED_EXPECTED_H
 
 #include <__assert>
+#include <__concepts/convertible_to.h>
 #include <__config>
 #include <__expected/bad_expected_access.h>
 #include <__expected/unexpect.h>
@@ -1139,7 +1140,11 @@ class expected : private __expected_base<_Tp, _Err> {
 
   // [expected.object.eq], equality operators
   template <class _T2, class _E2>
-    requires(!is_void_v<_T2>)
+    requires(!is_void_v<_T2> &&
+             requires(const _Tp& __tp, const _T2& __t2, const _Err& __err, const _E2& __e2) {
+               { __tp == __t2 } -> convertible_to<bool>;
+               { __err == __e2 } -> convertible_to<bool>;
+             })
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
     if (__x.__has_val() != __y.__has_val()) {
       return false;
@@ -1153,11 +1158,18 @@ class expected : private __expected_base<_Tp, _Err> {
   }
 
   template <class _T2>
+    requires(!__is_std_expected<_T2>::value &&
+             requires(const _Tp& __tp, const _T2& __t2) {
+               { __tp == __t2 } -> convertible_to<bool>;
+             })
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) {
     return __x.__has_val() && static_cast<bool>(__x.__val() == __v);
   }
 
   template <class _E2>
+    requires requires(const _Err& __err, const _E2& __e2) {
+      { __err == __e2 } -> convertible_to<bool>;
+    }
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
     return !__x.__has_val() && static_cast<bool>(__x.__unex() == __e.error());
   }
@@ -1850,7 +1862,10 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
 
   // [expected.void.eq], equality operators
   template <class _T2, class _E2>
-    requires is_void_v<_T2>
+    requires(is_void_v<_T2> &&
+             requires(const _Err& __err, const _E2& __e2) {
+               { __err == __e2 } -> convertible_to<bool>;
+             })
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
     if (__x.__has_val() != __y.__has_val()) {
       return false;
@@ -1860,6 +1875,9 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   }
 
   template <class _E2>
+    requires requires(const _Err& __err, const _E2& __e2) {
+      { __err == __e2 } -> convertible_to<bool>;
+    }
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
     return !__x.__has_val() && static_cast<bool>(__x.__unex() == __y.error());
   }

>From 408b3315450bf97e4b1c0e23f37dc9462c71578b Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Thu, 28 Nov 2024 01:16:30 -0500
Subject: [PATCH 2/5] [libc++] P3379R1: Constrain 'std::expected' equality
 operators

---
 libcxx/include/__expected/expected.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index 7dc0248fcd72f9..ca9dcfb1569e8b 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -1140,11 +1140,15 @@ class expected : private __expected_base<_Tp, _Err> {
 
   // [expected.object.eq], equality operators
   template <class _T2, class _E2>
+#  if _LIBCPP_STD_VER >= 26
     requires(!is_void_v<_T2> &&
              requires(const _Tp& __tp, const _T2& __t2, const _Err& __err, const _E2& __e2) {
                { __tp == __t2 } -> convertible_to<bool>;
                { __err == __e2 } -> convertible_to<bool>;
              })
+#  else
+    requires(!is_void_v<_T2>)
+#  endif
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
     if (__x.__has_val() != __y.__has_val()) {
       return false;
@@ -1158,18 +1162,22 @@ class expected : private __expected_base<_Tp, _Err> {
   }
 
   template <class _T2>
+#  if _LIBCPP_STD_VER >= 26
     requires(!__is_std_expected<_T2>::value &&
              requires(const _Tp& __tp, const _T2& __t2) {
                { __tp == __t2 } -> convertible_to<bool>;
              })
+#  endif
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const _T2& __v) {
     return __x.__has_val() && static_cast<bool>(__x.__val() == __v);
   }
 
   template <class _E2>
+#  if _LIBCPP_STD_VER >= 26
     requires requires(const _Err& __err, const _E2& __e2) {
       { __err == __e2 } -> convertible_to<bool>;
     }
+#  endif
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __e) {
     return !__x.__has_val() && static_cast<bool>(__x.__unex() == __e.error());
   }
@@ -1862,10 +1870,14 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
 
   // [expected.void.eq], equality operators
   template <class _T2, class _E2>
+#  if _LIBCPP_STD_VER >= 26
     requires(is_void_v<_T2> &&
              requires(const _Err& __err, const _E2& __e2) {
                { __err == __e2 } -> convertible_to<bool>;
              })
+#  else
+    requires is_void_v<_T2>
+#  endif
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const expected<_T2, _E2>& __y) {
     if (__x.__has_val() != __y.__has_val()) {
       return false;
@@ -1875,9 +1887,11 @@ class expected<_Tp, _Err> : private __expected_void_base<_Err> {
   }
 
   template <class _E2>
+#  if _LIBCPP_STD_VER >= 26
     requires requires(const _Err& __err, const _E2& __e2) {
       { __err == __e2 } -> convertible_to<bool>;
     }
+#  endif
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const expected& __x, const unexpected<_E2>& __y) {
     return !__x.__has_val() && static_cast<bool>(__x.__unex() == __y.error());
   }

>From 6d540b6305757133c777a1a768d06e6b4abcc9a8 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 30 Nov 2024 20:51:37 -0500
Subject: [PATCH 3/5] [libc++] P3379R1: Constrain 'std::expected' equality
 operators

---
 .../expected.void/equality/equality.other_expected.pass.cpp  | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
index 8b24875586852f..5de9d9b34f0aa0 100644
--- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
@@ -23,15 +23,12 @@
 template <class T1, class T2>
 concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
 
-struct Foo{};
+struct Foo {};
 static_assert(!CanCompare<Foo, Foo>);
 
 static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
 static_assert(CanCompare<std::expected<void, int>, std::expected<void, short>>);
 
-// Note this is true because other overloads in expected<non-void> are unconstrained
-static_assert(CanCompare<std::expected<void, int>, std::expected<int, int>>);
-
 constexpr bool test() {
   // x.has_value() && y.has_value()
   {

>From 02d1d91bbc52c1059305f7c42a93641b012ed318 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 30 Nov 2024 21:11:37 -0500
Subject: [PATCH 4/5] [libc++] P3379R1: Constrain 'std::expected' equality
 operators

---
 libcxx/docs/ReleaseNotes/20.rst                              | 1 +
 .../equality/equality.other_expected.pass.cpp                | 5 +----
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index d520c46bae1ef1..0c69e76560755d 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -45,6 +45,7 @@ Implemented Papers
 - ``std::jthread`` and ``<stop_token>`` are not guarded behind ``-fexperimental-library`` anymore
 - P2674R1: A trait for implicit lifetime types (`Github <https://github.com/llvm/llvm-project/issues/105259>`__)
 - P0429R9: A Standard ``flat_map`` is partially implemented and ``flat_map`` is provided (`Github <https://github.com/llvm/llvm-project/issues/105190>`__)
+- P3379R1: Constrain ``std::expected`` equality operators
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
index 9325c6c61ad2db..58789f6c421d10 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
@@ -23,15 +23,12 @@
 template <class T1, class T2>
 concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
 
-struct Foo{};
+struct Foo {};
 static_assert(!CanCompare<Foo, Foo>);
 
 static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
 static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);
 
-// Note this is true because other overloads are unconstrained
-static_assert(CanCompare<std::expected<int, int>, std::expected<void, int>>);
-
 constexpr bool test() {
   // x.has_value() && y.has_value()
   {

>From 805ac6a8b2ceac7541e991070963963f75f35e4d Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 30 Nov 2024 21:58:04 -0500
Subject: [PATCH 5/5] [libc++] P3379R1: Constrain 'std::expected' equality
 operators

---
 .../equality/equality.other_expected.pass.cpp         | 11 +++++++++--
 .../equality/equality.other_expected.pass.cpp         |  9 +++++++--
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
index 58789f6c421d10..3a044afe937284 100644
--- a/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp
@@ -23,12 +23,19 @@
 template <class T1, class T2>
 concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
 
-struct Foo {};
-static_assert(!CanCompare<Foo, Foo>);
+struct NonComparable {};
+static_assert(!CanCompare<NonComparable, NonComparable>);
 
 static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
 static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);
 
+#if _LIBCPP_STD_VER >= 26
+static_assert(!CanCompare<std::expected<int, int>, std::expected<void, int>>);
+static_assert(!CanCompare<std::expected<NonComparable, int>, std::expected<NonComparable, int>>);
+static_assert(!CanCompare<std::expected<int, NonComparable>, std::expected<int, NonComparable>>);
+static_assert(!CanCompare<std::expected<NonComparable, NonComparable>, std::expected<NonComparable, NonComparable>>);
+#endif
+
 constexpr bool test() {
   // x.has_value() && y.has_value()
   {
diff --git a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
index 5de9d9b34f0aa0..731368c4d23964 100644
--- a/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
+++ b/libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp
@@ -23,12 +23,17 @@
 template <class T1, class T2>
 concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };
 
-struct Foo {};
-static_assert(!CanCompare<Foo, Foo>);
+struct NonComparable {};
+static_assert(!CanCompare<NonComparable, NonComparable>);
 
 static_assert(CanCompare<std::expected<void, int>, std::expected<void, int>>);
 static_assert(CanCompare<std::expected<void, int>, std::expected<void, short>>);
 
+#if _LIBCPP_STD_VER >= 26
+static_assert(!CanCompare<std::expected<void, int>, std::expected<int, int>>);
+static_assert(!CanCompare<std::expected<void, NonComparable>, std::expected<void, NonComparable>>);
+#endif
+
 constexpr bool test() {
   // x.has_value() && y.has_value()
   {



More information about the libcxx-commits mailing list