Skip to content

Conversation

H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented May 10, 2025

Updates the implementation std::reference_wrapper - P2944R3 as discussed in
#117664 (comment) This PR also refactors the tests in preparation to implements the constrained comparisons for optional, variant etc.

Closes #138233

References:

Copy link

github-actions bot commented May 10, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2944R3-optional-constrained-equality branch from c1b6150 to bb90af9 Compare May 10, 2025 10:09
@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P2944R3-optional-constrained-equality branch from b31d91e to 4f009b5 Compare May 10, 2025 11:15
@Zingam Zingam marked this pull request as ready for review May 10, 2025 13:46
@Zingam Zingam requested a review from a team as a code owner May 10, 2025 13:46
@Zingam Zingam added libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. c++26 labels May 10, 2025
@llvmbot
Copy link
Member

llvmbot commented May 10, 2025

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

Implements P2944R3 partially, which adds constrained comparisons to std::optional and std::reference_wrapper.

  • Moves the test helpers (concepts and types) for testing constrained comparisons to test_comparisons.h.
  • NFC to std::expected tests to use the new names of the tests helpers from test_comparisions.h.
  • Updates the std::referencew_wrapper implementation to use the concept __core_convertible_to<bool> as per comments in #135759.
  • Implements constrained comparisons for std::optional.

Closes #136767
Closes #138233

Relates to: #135759

References:


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

41 Files Affected:

  • (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1)
  • (modified) libcxx/include/__functional/reference_wrapper.h (+4-4)
  • (modified) libcxx/include/optional (+205-21)
  • (modified) libcxx/include/version (+3-3)
  • (modified) libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp (-1)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp (+33)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/tuple.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/variant.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+2-2)
  • (modified) libcxx/test/std/utilities/expected/expected.expected/equality/equality.T2.pass.cpp (+4-4)
  • (modified) libcxx/test/std/utilities/expected/expected.expected/equality/equality.other_expected.pass.cpp (+11-11)
  • (modified) libcxx/test/std/utilities/expected/expected.expected/equality/equality.unexpected.pass.cpp (+4-4)
  • (modified) libcxx/test/std/utilities/expected/expected.void/equality/equality.other_expected.pass.cpp (+11-11)
  • (modified) libcxx/test/std/utilities/expected/expected.void/equality/equality.unexpected.pass.cpp (+4-4)
  • (modified) libcxx/test/std/utilities/expected/types.h (-13)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.const_ref.pass.cpp (+5-8)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.refwrap.pass.cpp (+5-9)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.refwrap_const.pass.cpp (+7-10)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.refwrap.const_ref.pass.cpp (+5-6)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.refwrap.refwrap.pass.cpp (+3-6)
  • (modified) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.refwrap.refwrap_const.pass.cpp (+5-8)
  • (removed) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/helper_concepts.h (-38)
  • (removed) libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/helper_types.h (-30)
  • (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/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp (+1-14)
  • (modified) libcxx/test/support/test_comparisons.h (+65-1)
  • (modified) libcxx/test/support/test_container_comparisons.h (-4)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (+5-2)
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 3809446a57896..99fc0b38077ab 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|","","Implemented changes to ``reference_wrapper`` and ``pair``"
+"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","Not implemented ``tuple`` and ``variant``"
 "`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/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index b409ad7511f6c..c46203a4ca9a4 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -11,7 +11,6 @@
 #define _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
 
 #include <__compare/synth_three_way.h>
-#include <__concepts/boolean_testable.h>
 #include <__config>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
@@ -19,6 +18,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/invoke.h>
 #include <__type_traits/is_const.h>
+#include <__type_traits/is_core_convertible.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>
@@ -75,7 +75,7 @@ class reference_wrapper : public __weak_result_type<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y)
     requires requires {
-      { __x.get() == __y.get() } -> __boolean_testable;
+      { __x.get() == __y.get() } -> __core_convertible_to<bool>;
     }
   {
     return __x.get() == __y.get();
@@ -83,7 +83,7 @@ class reference_wrapper : public __weak_result_type<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y)
     requires requires {
-      { __x.get() == __y } -> __boolean_testable;
+      { __x.get() == __y } -> __core_convertible_to<bool>;
     }
   {
     return __x.get() == __y;
@@ -91,7 +91,7 @@ class reference_wrapper : public __weak_result_type<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
     requires(!is_const_v<_Tp>) && requires {
-      { __x.get() == __y.get() } -> __boolean_testable;
+      { __x.get() == __y.get() } -> __core_convertible_to<bool>;
     }
   {
     return __x.get() == __y.get();
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 2153efb2ab899..059ca0156475b 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>
@@ -983,12 +984,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))
@@ -996,11 +1008,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))
@@ -1008,11 +1030,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))
@@ -1020,11 +1052,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))
@@ -1032,11 +1074,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))
@@ -1044,11 +1096,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))
@@ -1068,7 +1130,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);
@@ -1140,100 +1203,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/include/version b/libcxx/include/version
index 77d97b93adc6c..3a173e30c47ae 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -80,8 +80,8 @@ __cpp_lib_constexpr_tuple                               201811L <tuple>
 __cpp_lib_constexpr_typeinfo                            202106L <typeinfo>
 __cpp_lib_constexpr_utility                             201811L <utility>
 __cpp_lib_constexpr_vector                              201907L <vector>
-__cpp_lib_constrained_equality                          202403L <optional> <tuple> <utility>
-                                                                <variant>
+__cpp_lib_constrained_equality                          202411L <expected> <optional> <tuple>
+                                                                <utility> <variant>
 __cpp_lib_containers_ranges                             202202L <deque> <forward_list> <list>
                                                                 <map> <queue> <set>
                                                                 <stack> <string> <unordered_map>
@@ -545,7 +545,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # if !defined(_LIBCPP_ABI_VCRUNTIME)
 #   define __cpp_lib_constexpr_new                      202406L
 # endif
-// # define __cpp_lib_constrained_equality                 202403L
+// # define __cpp_lib_constrained_equality                 202411L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
 // # define __cpp_lib_default_template_type_for_algorithm_values 202403L
diff --git a/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp b/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp
index 01be1db73041b..671747f89a82e 100644
--- a/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp
+++ b/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp
@@ -26,7 +26,6 @@ constexpr std::size_t N{1};
 static_assert(std::three_way_comparable<std::array<int, N>>);
 
 // Thanks to SFINAE, the following is not a compiler error but returns `false`
-struct NonComparable {};
 static_assert(!std::three_way_comparable<std::array<NonComparable, N>>);
 
 // Implementation detail of `test_sequence_container_array_spaceship`
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp
index d58f726f66e2f..9c7a84f145dde 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp
@@ -20,6 +20,10 @@
 
 #if TEST_STD_VER < 14
 
+#  ifdef __cpp_lib_constrained_equality
+#    error "__cpp_lib_constrained_equality should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_expected
 #    error "__cpp_lib_expected should not be defined before c++23"
 #  endif
@@ -30,6 +34,10 @@
 
 #elif TEST_STD_VER == 14
 
+#  ifdef __cpp_lib_constrained_equality
+#    error "__cpp_lib_constrained_equality should not be defined before c++26"
+#  endif
+
 #  ifdef __cpp_lib_expected
 #    error "__cpp_lib_expected should not be defined before c++23"
 #  endif
@@ -40,6 +48,10 @@
 
 #elif TEST_STD_VER == 17
 
+#  ifdef __cpp_lib_constrained_equality
+#    error "__cpp_lib_cons...
[truncated]

@H-G-Hristov H-G-Hristov changed the title [libc++] P2944R3: Constrained comparisions - optional and reference_wrapper [libc++] P2944R3: Constrained comparisons - update reference_wrapper implementation Jun 4, 2025
@frederick-vs-ja
Copy link
Contributor

CI failures of ASan/MSan are unrleated.

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split this into multiple patches? There seems to be a lot of diff which isn't really related to the actual changes made. Especially the HasOperatorEqual and HasOperatorSpaceship canonicalizations change a lot of stuff but isn't really related to the actual changes. The changes to generate_feature_test_macros and the generated files of that as well.

@H-G-Hristov
Copy link
Contributor Author

Can we split this into multiple patches? There seems to be a lot of diff which isn't really related to the actual changes made. Especially the HasOperatorEqual and HasOperatorSpaceship canonicalizations change a lot of stuff but isn't really related to the actual changes. The changes to generate_feature_test_macros and the generated files of that as well.

I removed the changes related to std::expected. Should I resubmit them?

Copy link
Contributor

@philnik777 philnik777 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split this into multiple patches? There seems to be a lot of diff which isn't really related to the actual changes made. Especially the HasOperatorEqual and HasOperatorSpaceship canonicalizations change a lot of stuff but isn't really related to the actual changes. The changes to generate_feature_test_macros and the generated files of that as well.

I removed the changes related to std::expected. Should I resubmit them?

Feel free to. While I like simplifications in the headers more, I'm not against them in the tests. I just want to keep functional patches as small as possible to make it obvious where these functional changes are.

@Zingam
Copy link
Contributor

Zingam commented Jun 14, 2025

@philnik777 @frederick-vs-ja Thank you for the review.
What is the current state of the CI? It is either failed or not making progress for many ours. I also don't see the buildkite jobs.
I saw PRs merged with some jobs failed.

@Zingam Zingam merged commit a0c00cc into llvm:main Jun 15, 2025
155 of 166 checks passed
akuhlens pushed a commit to akuhlens/llvm-project that referenced this pull request Jun 24, 2025
…` implementation (llvm#139368)

Updates the implementation `std::reference_wrapper` -
[P2944R3](https://wg21.link/P2944R3) as discussed in
llvm#117664 (comment)
This PR also refactors the tests in preparation to implements the
constrained comparisons for `optional`, `variant` etc.

- Moves the test helpers (concepts and types) for testing constrained
comparisons to `test_comparisons.h`.
- Updates the `std::reference_wrapper` implementation to use the concept
`__core_convertible_to<bool>` as per comments in llvm#135759.

Closes llvm#138233

# References:
- [refwrap.comparisons](https://wg21.link/refwrap.comparisons)

---------

Co-authored-by: Hristo Hristov <zingam@outlook.com>
Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
Zingam pushed a commit that referenced this pull request Jun 25, 2025
This is a follow-up and depends on #139368 being merged first.

Implements [P2944R3](https://wg21.link/P2944R3) partially, which adds
constrained comparisons to `std::variant`

Closes #136769

Depends on #139368

# References

[variant.relops](https://wg21.link/variant.relops)
anthonyhatran pushed a commit to anthonyhatran/llvm-project that referenced this pull request Jun 26, 2025
This is a follow-up and depends on llvm#139368 being merged first.

Implements [P2944R3](https://wg21.link/P2944R3) partially, which adds
constrained comparisons to `std::variant`

Closes llvm#136769

Depends on llvm#139368

# References

[variant.relops](https://wg21.link/variant.relops)
Zingam added a commit that referenced this pull request Jun 29, 2025
…xpected` (#145668)

Refactored `std::expected` and `std::pair` tests to use the
canonicalized names from `test_comparisions.h`, which are shared between
tests.

This was split from #139368 as
per comment
#139368 (review)

Towards implementing [P2944R3: Comparisons for
reference_wrapper](#105424)

---------

Co-authored-by: Hristo Hristov <zingam@outlook.com>
Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
llvm-sync bot pushed a commit to arm/arm-toolchain that referenced this pull request Jun 29, 2025
…air` and `expected` (#145668)

Refactored `std::expected` and `std::pair` tests to use the
canonicalized names from `test_comparisions.h`, which are shared between
tests.

This was split from llvm/llvm-project#139368 as
per comment
llvm/llvm-project#139368 (review)

Towards implementing [P2944R3: Comparisons for
reference_wrapper](llvm/llvm-project#105424)

---------

Co-authored-by: Hristo Hristov <zingam@outlook.com>
Co-authored-by: Nikolas Klauser <nikolasklauser@berlin.de>
@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/P2944R3-optional-constrained-equality branch July 6, 2025 06:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++26 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: Update comparisons for reference_wrapper
5 participants