Skip to content

[libc++] P2944R3: Constrained comparisions - optional #144249

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Jun 15, 2025

Partially implements P2944R3 which adds constrained comparisons to std::optional.

Closes #136767

References

optional.relops
optional.comp.with.t

@H-G-Hristov H-G-Hristov changed the title WIP [libc+] Constrained eqaulity opational [libc++] P2944R3: Constrained comparisions - optional Jun 15, 2025
@H-G-Hristov H-G-Hristov marked this pull request as ready for review June 15, 2025 19:57
@H-G-Hristov H-G-Hristov requested a review from a team as a code owner June 15, 2025 19:57
@Zingam Zingam added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Jun 15, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 15, 2025

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Partially implements P2944R3 which adds constrained comparisons to std::optional.

Closes #136767

References

optional.relops
optional.comp.with.t


Patch is 31.41 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144249.diff

15 Files Affected:

  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/optional (+205-21)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp (+21)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp (+20)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp (+20)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp (+20)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/less_than.pass.cpp (+20)
  • (modified) libcxx/test/std/utilities/optional/optional.comp_with_t/not_equal.pass.cpp (+21)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/equal.pass.cpp (+14)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/greater_equal.pass.cpp (+13)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/greater_than.pass.cpp (+13)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/less_equal.pass.cpp (+13)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/less_than.pass.cpp (+13)
  • (modified) libcxx/test/std/utilities/optional/optional.relops/not_equal.pass.cpp (+14)
  • (modified) libcxx/test/support/test_comparisons.h (+41)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 8a0417e120d75..77d09bebe3462 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -59,7 +59,7 @@
 "`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","",""
 "`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","",""
 "`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","",""
-"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``optional``, ``tuple`` and ``variant`` are not yet implemented"
+"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``tuple`` and ``variant`` are not yet implemented"
 "`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","",""
 "`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19",""
 "","","","","",""
diff --git a/libcxx/include/optional b/libcxx/include/optional
index fa32d75ef86dd..68053bdfb5df3 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -205,6 +205,7 @@ namespace std {
 #  include <__type_traits/is_assignable.h>
 #  include <__type_traits/is_constructible.h>
 #  include <__type_traits/is_convertible.h>
+#  include <__type_traits/is_core_convertible.h>
 #  include <__type_traits/is_destructible.h>
 #  include <__type_traits/is_nothrow_assignable.h>
 #  include <__type_traits/is_nothrow_constructible.h>
@@ -982,12 +983,23 @@ public:
 template <class _Tp>
 optional(_Tp) -> optional<_Tp>;
 
-// Comparisons between optionals
+// [optional.relops] Relational operators
+
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x == *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (static_cast<bool>(__x) != static_cast<bool>(__y))
     return false;
   if (!static_cast<bool>(__x))
@@ -995,11 +1007,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
   return *__x == *__y;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x != *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (static_cast<bool>(__x) != static_cast<bool>(__y))
     return true;
   if (!static_cast<bool>(__x))
@@ -1007,11 +1029,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
   return *__x != *__y;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x < *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (!static_cast<bool>(__y))
     return false;
   if (!static_cast<bool>(__x))
@@ -1019,11 +1051,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const o
   return *__x < *__y;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x > *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (!static_cast<bool>(__x))
     return false;
   if (!static_cast<bool>(__y))
@@ -1031,11 +1073,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const o
   return *__x > *__y;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x <= *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (!static_cast<bool>(__x))
     return true;
   if (!static_cast<bool>(__y))
@@ -1043,11 +1095,21 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
   return *__x <= *__y;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y)
+#    if _LIBCPP_STD_VER >= 26
+  requires requires {
+    { *__x >= *__y } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   if (!static_cast<bool>(__y))
     return true;
   if (!static_cast<bool>(__x))
@@ -1067,7 +1129,8 @@ operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
 
 #    endif // _LIBCPP_STD_VER >= 20
 
-// Comparisons with nullopt
+// [optional.nullops] Comparison with nullopt
+
 template <class _Tp>
 _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
   return !static_cast<bool>(__x);
@@ -1139,100 +1202,221 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
 
 #    endif // _LIBCPP_STD_VER <= 17
 
-// Comparisons with T
+// [optional.comp.with.t] Comparison with T
+
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x == __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x == __v : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v == *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v == *__x : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x != __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x != __v : true;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v != *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v != *__x : true;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x < __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x < __v : true;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v < *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v < *__x : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x <= __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x <= __v : true;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v <= *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v <= *__x : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x > __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x > __v : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v > *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v > *__x : true;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Up>::value) && requires {
+    { *__x >= __v } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? *__x >= __v : false;
 }
 
+#    if _LIBCPP_STD_VER >= 26
+template < class _Tp, class _Up>
+#    else
 template <
     class _Tp,
     class _Up,
     enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
-_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
+#    endif
+_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x)
+#    if _LIBCPP_STD_VER >= 26
+  requires(!__is_std_optional<_Tp>::value) && requires {
+    { __v >= *__x } -> __core_convertible_to<bool>;
+  }
+#    endif
+{
   return static_cast<bool>(__x) ? __v >= *__x : true;
 }
 
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
index 0636da7bcdf23..54965b270dcce 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp
@@ -14,8 +14,29 @@
 
 #include <optional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+
+static_assert(HasOperatorEqual<int, std::optional<int>>);
+static_assert(HasOperatorEqual<int, std::optional<EqualityComparable>>);
+static_assert(HasOperatorEqual<EqualityComparable, std::optional<EqualityComparable>>);
+
+static_assert(!HasOperatorEqual<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorEqual<NonComparable, std::optional<EqualityComparable>>);
+
+static_assert(HasOperatorEqual<std::optional<int>, int>);
+static_assert(HasOperatorEqual<std::optional<EqualityComparable>, int>);
+static_assert(HasOperatorEqual<std::optional<EqualityComparable>, EqualityComparable>);
+
+static_assert(!HasOperatorEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, NonComparable>);
+
+#endif
+
 using std::optional;
 
 struct X {
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
index 2010157a8d011..b63b85b000930 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp
@@ -14,8 +14,28 @@
 
 #include <optional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThan<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorGreaterThan<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThan<ThreeWayComparable, std::optional<ThreeWayComparable>>);
+
+static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<ThreeWayComparable>>);
+static_assert(!HasOperatorGreaterThan<ThreeWayComparable, std::optional<NonComparable>>);
+
+#endif
+
 using std::optional;
 
 struct X {
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
index 2e1e9b9316111..bb1a8c551d3d5 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp
@@ -14,8 +14,28 @@
 
 #include <optional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorGreaterThanEqual<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>>);
+
+static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<NonComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
+static_assert(!HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
+
+#endif
+
 using std::optional;
 
 struct X {
diff --git a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
index 7026c24de5dbf..8b08ec3deea5a 100644
--- a/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp
@@ -14,8 +14,28 @@
 
 #include <optional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
+#if TEST_STD_VER >= 26
+
+// Test SFINAE.
+static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, int>);
+static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
+
+static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, NonComparable>);
+static_assert(!HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
+static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
+
+static_assert(HasOperatorLessThanEqual<int, std::optional<ThreeWayComparable>>);
+static_assert(HasOperatorLessThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

P2944R3: Constrained equality - std::optional
3 participants