[libcxx-commits] [libcxx] WIP - [libc++][functional] P2944R3 (partial): Comparisons for ``reference_wrapper`` (``reference_wrapper`` operators only) (PR #88384)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sun Apr 21 04:35:43 PDT 2024


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/88384

>From d4023142fb3ee65b8e3325a965ddd3270c35eb51 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 10:20:05 +0300
Subject: [PATCH 1/8] [libc++][functional] P2944R3 (partial): Comparisons for
 ``reference_wrapper`` (``reference_wrapper`` operators only)

Implements parially https://wg21.link/P2944R3
---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +-
 libcxx/docs/ReleaseNotes/19.rst               |  1 +
 libcxx/docs/Status/Cxx2c.rst                  |  1 +
 libcxx/docs/Status/Cxx2cPapers.csv            |  2 +-
 .../include/__functional/reference_wrapper.h  | 52 +++++++++++++++++++
 libcxx/include/functional                     |  9 ++++
 libcxx/include/version                        |  2 +-
 .../functional.version.compile.pass.cpp       | 16 ++----
 .../version.version.compile.pass.cpp          | 16 ++----
 .../compare.three_way.pass.cpp                | 33 ++++++++++++
 .../refwrap.comparissons/equal.pass.cpp       | 48 +++++++++++++++++
 .../generate_feature_test_macro_components.py |  1 -
 12 files changed, 157 insertions(+), 26 deletions(-)
 create mode 100644 libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
 create mode 100644 libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 3197d2cd1b271c..83d8213ec29f9e 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -444,7 +444,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_rcu``                                          *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_reference_wrapper``                            *unimplemented*
+    ``__cpp_lib_reference_wrapper``                            ``202403L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_saturation_arithmetic``                        ``202311L``
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index 53cc7a77d1af48..513852d8c71531 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -46,6 +46,7 @@ Implemented Papers
 - P2869R4 - Remove Deprecated ``shared_ptr`` Atomic Access APIs from C++26
 - P2872R3 - Remove ``wstring_convert`` From C++26
 - P3142R0 - Printing Blank Lines with ``println`` (as DR against C++23)
+- P2944R3 - Comparisons for ``reference_wrapper`` (comparison operators for ``reference_wrapper`` only)
 - P2302R4 - ``std::ranges::contains``
 - P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
 - P3029R1 - Better ``mdspan``'s CTAD
diff --git a/libcxx/docs/Status/Cxx2c.rst b/libcxx/docs/Status/Cxx2c.rst
index a7ebc4662f517c..b6f1714a182a6c 100644
--- a/libcxx/docs/Status/Cxx2c.rst
+++ b/libcxx/docs/Status/Cxx2c.rst
@@ -40,6 +40,7 @@ Paper Status
 .. note::
 
    .. [#note-P2510R3] This paper is applied as DR against C++20. (MSVC STL and libstdc++ will do the same.)
+   .. [#note-P2944R3] Implemented comparisons for ``reference_wrapper`` only.
 
 .. _issues-status-cxx2c:
 
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 1b00a05ad3d6fd..31314a209dfe8d 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -59,7 +59,7 @@
 "`P2248R8 <https://wg21.link/P2248R8>`__","LWG","Enabling list-initialization for algorithms","Tokyo March 2024","","",""
 "`P2810R4 <https://wg21.link/P2810R4>`__","LWG","``is_debugger_present`` ``is_replaceable``","Tokyo March 2024","","",""
 "`P1068R11 <https://wg21.link/P1068R11>`__","LWG","Vector API for random number generation","Tokyo March 2024","","",""
-"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","","",""
+"`P2944R3 <https://wg21.link/P2944R3>`__","LWG","Comparisons for ``reference_wrapper``","Tokyo March 2024","|Partial|","19.0",""
 "`P2642R6 <https://wg21.link/P2642R6>`__","LWG","Padded ``mdspan`` layouts","Tokyo March 2024","","",""
 "`P3029R1 <https://wg21.link/P3029R1>`__","LWG","Better ``mdspan``'s CTAD","Tokyo March 2024","|Complete|","19.0",""
 "","","","","","",""
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 94b39e3bc78616..8c7ba9e36da094 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -10,11 +10,14 @@
 #ifndef _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
 #define _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
 
+#include <__compare/synth_three_way.h>
 #include <__config>
 #include <__functional/invoke.h>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
 #include <__type_traits/enable_if.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_convertible.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>
@@ -64,6 +67,55 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
   {
     return std::__invoke(get(), std::forward<_ArgTypes>(__args)...);
   }
+
+#if _LIBCPP_STD_VER >= 26
+
+  // [refwrap.comparisons], comparisons
+
+  friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y) {
+    // Mandates: The expression x.get() == y.get() is well-formed and its result is convertible to bool.
+    static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
+
+    return __x.get() == __y.get();
+  }
+
+  friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y) {
+    // Mandates: The expression x.get() == y is well-formed and its result is convertible to bool.
+    static_assert(is_convertible_v<decltype(__x.get() == __y), bool>);
+
+    return __x.get() == __y;
+  }
+
+  friend constexpr bool operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+    requires(!is_const_v<_Tp>)
+  {
+    // Constraints: is_const_v<T> is false.
+    // Mandates: The expression x.get() == y.get() is well-formed and its result is convertible to bool.
+    static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
+
+    return __x.get() == __y.get();
+  }
+
+  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper __y) {
+    // return __synth_three_way_result(x.get(), y.get());
+    return std::__synth_three_way(__x.get(), __y.get());
+  }
+
+  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, const _Tp& __y) {
+    // return __synth_three_way_result(__x.get(), __y);
+    return std::__synth_three_way(__x.get(), __y.get());
+  }
+
+  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+    requires(!is_const_v<_Tp>)
+  {
+    // Constraints: is_const_v<T> is false.
+
+    // return __synth_three_way_result(__x.get(), __y.get());
+    return std::__synth_three_way(__x.get(), __y.get());
+  }
+
+#endif // _LIBCPP_STD_VER >= 26
 };
 
 #if _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/functional b/libcxx/include/functional
index a2476c93ad1b43..1da953f82241b5 100644
--- a/libcxx/include/functional
+++ b/libcxx/include/functional
@@ -77,6 +77,15 @@ template <class T> struct unwrap_ref_decay : unwrap_reference<decay_t<T>> { };
 template <class T> using unwrap_reference_t = typename unwrap_reference<T>::type; // since C++20
 template <class T> using unwrap_ref_decay_t = typename unwrap_ref_decay<T>::type; // since C++20
 
+// [refwrap.comparisons], comparisons
+friend constexpr bool operator==(reference_wrapper, reference_wrapper);                                // Since C++26
+friend constexpr bool operator==(reference_wrapper, const T&);                                         // Since C++26
+friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>);                       // Since C++26
+
+friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper);          // Since C++26
+friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, const T&);                   // Since C++26
+friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>); // Since C++26
+
 template <class T> // <class T=void> in C++14
 struct plus {
     T operator()(const T& x, const T& y) const;
diff --git a/libcxx/include/version b/libcxx/include/version
index 0ed77345baa71d..0ce262462eb3ee 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -524,7 +524,7 @@ __cpp_lib_within_lifetime                               202306L <type_traits>
 // # define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ratio                                202306L
 // # define __cpp_lib_rcu                                  202306L
-// # define __cpp_lib_reference_wrapper                    202403L
+# define __cpp_lib_reference_wrapper                    202403L
 # define __cpp_lib_saturation_arithmetic                202311L
 // # define __cpp_lib_smart_ptr_owner_equality             202306L
 # define __cpp_lib_span_at                              202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
index aeb09a30b42591..27e76e5b2b05a3 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/functional.version.compile.pass.cpp
@@ -535,17 +535,11 @@
 #   error "__cpp_lib_ranges should have the value 202207L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_reference_wrapper
-#     error "__cpp_lib_reference_wrapper should be defined in c++26"
-#   endif
-#   if __cpp_lib_reference_wrapper != 202403L
-#     error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_reference_wrapper
-#     error "__cpp_lib_reference_wrapper should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_reference_wrapper
+#   error "__cpp_lib_reference_wrapper should be defined in c++26"
+# endif
+# if __cpp_lib_reference_wrapper != 202403L
+#   error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
 # endif
 
 # ifndef __cpp_lib_result_of_sfinae
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 3ec548f56cea1d..54fb538680ebad 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -7433,17 +7433,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_reference_wrapper
-#     error "__cpp_lib_reference_wrapper should be defined in c++26"
-#   endif
-#   if __cpp_lib_reference_wrapper != 202403L
-#     error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_reference_wrapper
-#     error "__cpp_lib_reference_wrapper should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_reference_wrapper
+#   error "__cpp_lib_reference_wrapper should be defined in c++26"
+# endif
+# if __cpp_lib_reference_wrapper != 202403L
+#   error "__cpp_lib_reference_wrapper should have the value 202403L in c++26"
 # endif
 
 # ifndef __cpp_lib_remove_cvref
diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
new file mode 100644
index 00000000000000..cbf5d8830200b5
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// class reference_wrapper
+
+// // [refwrap.comparisons], comparisons
+
+// friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper);          // Since C++26
+// friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, const T&);                   // Since C++26
+// friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>); // Since C++26
+
+#include <cassert>
+#include <functional>
+
+#include "test_macros.h"
+
+constexpr bool test() { return true; }
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
new file mode 100644
index 00000000000000..fb5c122b41d79c
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
+
+// <functional>
+
+// class reference_wrapper
+
+// // [refwrap.comparisons], comparisons
+// friend constexpr bool operator==(reference_wrapper, reference_wrapper);                                // Since C++26
+// friend constexpr bool operator==(reference_wrapper, const T&);                                         // Since C++26
+// friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>);                       // Since C++26
+
+#include <cassert>
+#include <functional>
+
+#include "test_macros.h"
+
+constexpr bool test() {
+  {
+    int i = 92;
+    std::reference_wrapper<int> lhs{i};
+    std::reference_wrapper<int> rhs = lhs;
+    assert(lhs == rhs);
+  }
+  {
+    int i = 92;
+    std::reference_wrapper<int> lhs{i};
+    int j = 84;
+    std::reference_wrapper<int> rhs{j};
+    assert(lhs != rhs);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index f2b8d55c0e11b0..5f127bd1213c14 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1040,7 +1040,6 @@ def add_version_header(tc):
             "name": "__cpp_lib_reference_wrapper",
             "values": {"c++26": 202403}, # P2944R3: Comparisons for reference_wrapper
             "headers": ["functional"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_remove_cvref",

>From 1f22a3347694d06163b4d7ec3bbad1496b5e2bb4 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 11 Apr 2024 15:31:18 +0300
Subject: [PATCH 2/8] Tests: operator== WIP

---
 .../include/__functional/reference_wrapper.h  | 11 +---
 .../refwrap.comparissons/equal.pass.cpp       | 51 +++++++++++++++----
 2 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 8c7ba9e36da094..738d57e32efe38 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -73,14 +73,12 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
   // [refwrap.comparisons], comparisons
 
   friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y) {
-    // Mandates: The expression x.get() == y.get() is well-formed and its result is convertible to bool.
     static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
 
     return __x.get() == __y.get();
   }
 
   friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y) {
-    // Mandates: The expression x.get() == y is well-formed and its result is convertible to bool.
     static_assert(is_convertible_v<decltype(__x.get() == __y), bool>);
 
     return __x.get() == __y;
@@ -89,29 +87,22 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
   friend constexpr bool operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
     requires(!is_const_v<_Tp>)
   {
-    // Constraints: is_const_v<T> is false.
-    // Mandates: The expression x.get() == y.get() is well-formed and its result is convertible to bool.
     static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
 
     return __x.get() == __y.get();
   }
 
   friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper __y) {
-    // return __synth_three_way_result(x.get(), y.get());
     return std::__synth_three_way(__x.get(), __y.get());
   }
 
   friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, const _Tp& __y) {
-    // return __synth_three_way_result(__x.get(), __y);
-    return std::__synth_three_way(__x.get(), __y.get());
+    return std::__synth_three_way(__x.get(), __y);
   }
 
   friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
     requires(!is_const_v<_Tp>)
   {
-    // Constraints: is_const_v<T> is false.
-
-    // return __synth_three_way_result(__x.get(), __y.get());
     return std::__synth_three_way(__x.get(), __y.get());
   }
 
diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
index fb5c122b41d79c..9f0d4b7adb5a1a 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -23,18 +23,51 @@
 #include "test_macros.h"
 
 constexpr bool test() {
+  int i = 92;
+  int j = 84;
+
+  // ==
+  {
+    // refwrap, refwrap
+    std::reference_wrapper<int> rw1{i};
+    std::reference_wrapper<int> rw2 = rw1;
+    assert(rw1 == rw2);
+    assert(rw2 == rw1);
+  }
+  {
+    // refwrap, const&
+    std::reference_wrapper<int> rw{i};
+    assert(rw == i);
+    assert(i == rw);
+  }
+  {
+    // refwrap, refwrap<const>
+    std::reference_wrapper<int> rw1{i};
+    std::reference_wrapper<const int> rw2 = rw1;
+    assert(rw1 == rw2);
+    assert(rw2 == rw1);
+  }
+
+  // !=
+  {
+    // refwrap, refwrap
+    std::reference_wrapper<int> rw1{i};
+    std::reference_wrapper<int> rw2{j};
+    assert(rw1 != rw2);
+    assert(rw2 != rw1);
+  }
   {
-    int i = 92;
-    std::reference_wrapper<int> lhs{i};
-    std::reference_wrapper<int> rhs = lhs;
-    assert(lhs == rhs);
+    // refwrap, const&
+    std::reference_wrapper<int> rw{i};
+    assert(rw != j);
+    assert(j != rw);
   }
   {
-    int i = 92;
-    std::reference_wrapper<int> lhs{i};
-    int j = 84;
-    std::reference_wrapper<int> rhs{j};
-    assert(lhs != rhs);
+    // refwrap, refwrap<const>
+    std::reference_wrapper<int> rw1{i};
+    std::reference_wrapper<const int> rw2{j};
+    assert(rw1 != rw2);
+    assert(rw2 != rw1);
   }
 
   return true;

>From ca199a8cbaa2aa7df76f87120143258bb4a56c2e Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Wed, 17 Apr 2024 21:47:54 +0300
Subject: [PATCH 3/8] =?UTF-8?q?Test:=20operator<=3D=3F>=20WIP?=

---
 .../compare.three_way.pass.cpp                | 36 ++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
index cbf5d8830200b5..bc158c74f3738e 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
@@ -21,9 +21,43 @@
 #include <cassert>
 #include <functional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
-constexpr bool test() { return true; }
+template <typename Order>
+constexpr void test() {
+  int integer = 47;
+
+  int bigger  = 94;
+  int smaller = 82;
+
+  // Identical contents
+  {
+    std::reference_wrapper<int> rw1{integer};
+    std::reference_wrapper<int> rw2{integer};
+    assert(testOrder(rw1, rw2, Order::equivalent));
+  }
+  // Less
+  {
+    std::reference_wrapper<int> rw1{smaller};
+    std::reference_wrapper<int> rw2{bigger};
+    assert(testOrder(rw1, rw2, Order::less));
+  }
+  // Greater
+  {
+    std::reference_wrapper<int> rw1{bigger};
+    std::reference_wrapper<int> rw2{smaller};
+    assert(testOrder(rw1, rw2, Order::greater));
+  }
+}
+
+constexpr bool test() {
+  test<std::strong_ordering>();
+  test<std::weak_ordering>();
+  test<std::partial_ordering>();
+
+  return true;
+}
 
 int main(int, char**) {
   test();

>From 24dc20b487405f0ee5e1d5fa616e4be80e232518 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Apr 2024 09:56:14 +0300
Subject: [PATCH 4/8] Implemented https://wg21.link/LWG4071

---
 .../include/__functional/reference_wrapper.h  | 42 +++++++++++++------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 738d57e32efe38..04e6fe0c90d968 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -72,36 +72,52 @@ class _LIBCPP_TEMPLATE_VIS reference_wrapper : public __weak_result_type<_Tp> {
 
   // [refwrap.comparisons], comparisons
 
-  friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y) {
-    static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
-
+  friend constexpr bool operator==(reference_wrapper __x, reference_wrapper __y)
+    requires requires {
+      { __x.get() == __y.get() } -> __boolean_testable;
+    }
+  {
     return __x.get() == __y.get();
   }
 
-  friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y) {
-    static_assert(is_convertible_v<decltype(__x.get() == __y), bool>);
-
+  friend constexpr bool operator==(reference_wrapper __x, const _Tp& __y)
+    requires requires {
+      { __x.get() == __y } -> __boolean_testable;
+    }
+  {
     return __x.get() == __y;
   }
 
   friend constexpr bool operator==(reference_wrapper __x, reference_wrapper<const _Tp> __y)
-    requires(!is_const_v<_Tp>)
+    requires(!is_const_v<_Tp>) && requires {
+      { __x.get() == __y.get() } -> __boolean_testable;
+    }
   {
-    static_assert(is_convertible_v<decltype(__x.get() == __y.get()), bool>);
-
     return __x.get() == __y.get();
   }
 
-  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper __y) {
+  // `operator<=>`: Checks the constraints of `synth-three-way` as per https://wg21.link/LWG4071 directly
+
+  friend constexpr auto operator<=>(reference_wrapper __x, reference_wrapper __y)
+    requires requires(const _Tp t) {
+      { t < t } -> __boolean_testable;
+    }
+  {
     return std::__synth_three_way(__x.get(), __y.get());
   }
 
-  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, const _Tp& __y) {
+  friend constexpr auto operator<=>(reference_wrapper __x, const _Tp& __y)
+    requires requires(const _Tp t) {
+      { t < t } -> __boolean_testable;
+    }
+  {
     return std::__synth_three_way(__x.get(), __y);
   }
 
-  friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
-    requires(!is_const_v<_Tp>)
+  friend constexpr auto operator<=>(reference_wrapper __x, reference_wrapper<const _Tp> __y)
+    requires(!is_const_v<_Tp>) && requires(const _Tp t) {
+      { t < t } -> __boolean_testable;
+    }
   {
     return std::__synth_three_way(__x.get(), __y.get());
   }

>From f100fa25f95f7f56c9c3fd6c0ff55b6cd0876d47 Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Apr 2024 10:34:16 +0300
Subject: [PATCH 5/8] Fixed includes

---
 libcxx/include/__functional/reference_wrapper.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 04e6fe0c90d968..c5e8b88f3fcd0a 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -11,13 +11,13 @@
 #define _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H
 
 #include <__compare/synth_three_way.h>
+#include <__concepts/boolean_testable.h>
 #include <__config>
 #include <__functional/invoke.h>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_const.h>
-#include <__type_traits/is_convertible.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>

>From 62541657e553efb46cd5863236e2045c08c8698c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Apr 2024 14:09:43 +0300
Subject: [PATCH 6/8] Tests: WIP SFINAE `operator==`

---
 libcxx/docs/Status/Cxx2cIssues.csv            |  1 +
 .../refwrap.comparissons/equal.pass.cpp       | 70 +++++++++++++++----
 2 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 008f7418ab9c05..97257ca48c0b33 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -64,4 +64,5 @@
 "","","","","",""
 "`3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Yet Adopted","|Complete|","16.0",""
 "XXXX","","The sys_info range should be affected by save","Not Yet Adopted","|Complete|","19.0"
+"`4071 <https://wg21.link/LWG4071>`__","","``reference_wrapper`` comparisons are not SFINAE-friendly","Not Yet Adopted","|Complete|","19.0"
 "","","","","",""
diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
index 9f0d4b7adb5a1a..1a4516c52f731e 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -18,32 +18,71 @@
 // friend constexpr bool operator==(reference_wrapper, reference_wrapper<const T>);                       // Since C++26
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 
+#include "test_comparisons.h"
 #include "test_macros.h"
 
-constexpr bool test() {
-  int i = 92;
-  int j = 84;
+// Test SFINAE
+
+struct EqualityComparable {
+  constexpr EqualityComparable(int value) : value_{value} {};
+
+  friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default;
+
+  int value_;
+};
+
+static_assert(std::equality_comparable<EqualityComparable>);
+static_assert(EqualityComparable{94} == EqualityComparable{94});
+static_assert(EqualityComparable{94} != EqualityComparable{82});
+
+// struct EqualityComparableWith {
+//   constexpr EqualityComparableWith(int value) : value_{value} {};
+
+//   friend constexpr bool operator==(const EqualityComparableWith&, const EqualityComparableWith&) noexcept = default;
+//   friend constexpr bool operator==(const EqualityComparableWith& x, const int& y) noexcept { return x.value_ == y; };
+
+//   int value_;
+// };
+
+struct NonComparable {};
+
+static_assert(!std::equality_comparable<NonComparable>);
+
+// struct NonComparableWith {
+//   constexpr NonComparableWith(int value);
+// };
+
+static_assert(std::equality_comparable<std::reference_wrapper<EqualityComparable>>);
+// static_assert(std::equality_comparable_with<std::reference_wrapper<EqualityComparableWith>, int>);
+static_assert(!std::equality_comparable<std::reference_wrapper<NonComparable>>);
+// static_assert(!std::equality_comparable_with<std::reference_wrapper<NonComparableWith>, int>);
+
+template <typename T>
+constexpr void test() {
+  T i{92};
+  T j{84};
 
   // ==
   {
     // refwrap, refwrap
-    std::reference_wrapper<int> rw1{i};
-    std::reference_wrapper<int> rw2 = rw1;
+    std::reference_wrapper<T> rw1{i};
+    std::reference_wrapper<T> rw2 = rw1;
     assert(rw1 == rw2);
     assert(rw2 == rw1);
   }
   {
     // refwrap, const&
-    std::reference_wrapper<int> rw{i};
+    std::reference_wrapper<T> rw{i};
     assert(rw == i);
     assert(i == rw);
   }
   {
     // refwrap, refwrap<const>
-    std::reference_wrapper<int> rw1{i};
-    std::reference_wrapper<const int> rw2 = rw1;
+    std::reference_wrapper<T> rw1{i};
+    std::reference_wrapper<const T> rw2 = rw1;
     assert(rw1 == rw2);
     assert(rw2 == rw1);
   }
@@ -51,24 +90,29 @@ constexpr bool test() {
   // !=
   {
     // refwrap, refwrap
-    std::reference_wrapper<int> rw1{i};
-    std::reference_wrapper<int> rw2{j};
+    std::reference_wrapper<T> rw1{i};
+    std::reference_wrapper<T> rw2{j};
     assert(rw1 != rw2);
     assert(rw2 != rw1);
   }
   {
     // refwrap, const&
-    std::reference_wrapper<int> rw{i};
+    std::reference_wrapper<T> rw{i};
     assert(rw != j);
     assert(j != rw);
   }
   {
     // refwrap, refwrap<const>
-    std::reference_wrapper<int> rw1{i};
-    std::reference_wrapper<const int> rw2{j};
+    std::reference_wrapper<T> rw1{i};
+    std::reference_wrapper<const T> rw2{j};
     assert(rw1 != rw2);
     assert(rw2 != rw1);
   }
+}
+
+constexpr bool test() {
+  test<int>();
+  test<EqualityComparable>();
 
   return true;
 }

>From fc106043db78b710314d6dc1fdc53c630bb7812c Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Apr 2024 14:26:02 +0300
Subject: [PATCH 7/8] Tests: WIP SFINAE `operator<=>`

---
 .../compare.three_way.pass.cpp                | 44 +++++++++++++------
 .../refwrap.comparissons/equal.pass.cpp       | 19 ++------
 2 files changed, 35 insertions(+), 28 deletions(-)

diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
index bc158c74f3738e..3b14ab8944899c 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
@@ -19,42 +19,60 @@
 // friend constexpr synth-three-way-result<T> operator<=>(reference_wrapper, reference_wrapper<const T>); // Since C++26
 
 #include <cassert>
+#include <concepts>
 #include <functional>
 
 #include "test_comparisons.h"
 #include "test_macros.h"
 
-template <typename Order>
+struct NonComparable {};
+
+static_assert(!std::three_way_comparable<NonComparable>);
+
+// Test SFINAE.
+
+static_assert(std::three_way_comparable<std::reference_wrapper<StrongOrder>>);
+static_assert(std::three_way_comparable<std::reference_wrapper<WeakOrder>>);
+static_assert(std::three_way_comparable<std::reference_wrapper<PartialOrder>>);
+
+static_assert(!std::three_way_comparable<std::reference_wrapper<NonComparable>>);
+
+// Test comparisons.
+
+template <typename T, typename Order>
 constexpr void test() {
-  int integer = 47;
+  T t{47};
 
-  int bigger  = 94;
-  int smaller = 82;
+  T bigger{94};
+  T smaller{82};
 
   // Identical contents
   {
-    std::reference_wrapper<int> rw1{integer};
-    std::reference_wrapper<int> rw2{integer};
+    std::reference_wrapper<T> rw1{t};
+    std::reference_wrapper<T> rw2{t};
     assert(testOrder(rw1, rw2, Order::equivalent));
   }
   // Less
   {
-    std::reference_wrapper<int> rw1{smaller};
-    std::reference_wrapper<int> rw2{bigger};
+    std::reference_wrapper<T> rw1{smaller};
+    std::reference_wrapper<T> rw2{bigger};
     assert(testOrder(rw1, rw2, Order::less));
   }
   // Greater
   {
-    std::reference_wrapper<int> rw1{bigger};
-    std::reference_wrapper<int> rw2{smaller};
+    std::reference_wrapper<T> rw1{bigger};
+    std::reference_wrapper<T> rw2{smaller};
     assert(testOrder(rw1, rw2, Order::greater));
   }
 }
 
 constexpr bool test() {
-  test<std::strong_ordering>();
-  test<std::weak_ordering>();
-  test<std::partial_ordering>();
+  test<int, std::strong_ordering>();
+  test<StrongOrder, std::strong_ordering>();
+  test<int, std::weak_ordering>();
+  test<WeakOrder, std::weak_ordering>();
+  test<int, std::partial_ordering>();
+  test<PartialOrder, std::partial_ordering>();
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
index 1a4516c52f731e..2cd96c7cc9aef2 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -24,7 +24,6 @@
 #include "test_comparisons.h"
 #include "test_macros.h"
 
-// Test SFINAE
 
 struct EqualityComparable {
   constexpr EqualityComparable(int value) : value_{value} {};
@@ -38,27 +37,17 @@ static_assert(std::equality_comparable<EqualityComparable>);
 static_assert(EqualityComparable{94} == EqualityComparable{94});
 static_assert(EqualityComparable{94} != EqualityComparable{82});
 
-// struct EqualityComparableWith {
-//   constexpr EqualityComparableWith(int value) : value_{value} {};
-
-//   friend constexpr bool operator==(const EqualityComparableWith&, const EqualityComparableWith&) noexcept = default;
-//   friend constexpr bool operator==(const EqualityComparableWith& x, const int& y) noexcept { return x.value_ == y; };
-
-//   int value_;
-// };
-
 struct NonComparable {};
 
 static_assert(!std::equality_comparable<NonComparable>);
 
-// struct NonComparableWith {
-//   constexpr NonComparableWith(int value);
-// };
+// Test SFINAE.
 
 static_assert(std::equality_comparable<std::reference_wrapper<EqualityComparable>>);
-// static_assert(std::equality_comparable_with<std::reference_wrapper<EqualityComparableWith>, int>);
+
 static_assert(!std::equality_comparable<std::reference_wrapper<NonComparable>>);
-// static_assert(!std::equality_comparable_with<std::reference_wrapper<NonComparableWith>, int>);
+
+// Test equality.
 
 template <typename T>
 constexpr void test() {

>From 288bbc0d74f00cbf896004b38abb1cdfb3c2f39f Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Sun, 21 Apr 2024 14:35:22 +0300
Subject: [PATCH 8/8] Fixed formatting

---
 .../function.objects/refwrap/refwrap.comparissons/equal.pass.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
index 2cd96c7cc9aef2..3681a767e76997 100644
--- a/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -24,7 +24,6 @@
 #include "test_comparisons.h"
 #include "test_macros.h"
 
-
 struct EqualityComparable {
   constexpr EqualityComparable(int value) : value_{value} {};
 



More information about the libcxx-commits mailing list