-
Notifications
You must be signed in to change notification settings - Fork 13.6k
[libc++][functional] P2944R3 (partial): Comparisons for reference_wrapper
(reference_wrapper
operators only)
#88384
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
[libc++][functional] P2944R3 (partial): Comparisons for reference_wrapper
(reference_wrapper
operators only)
#88384
Conversation
@llvm/pr-subscribers-libcxx Author: Hristo Hristov (H-G-Hristov) ChangesImplements https://wg21.link/P2944R3 (partially) Full diff: https://github.com/llvm/llvm-project/pull/88384.diff 12 Files Affected:
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 81c05b9112bd26..a4c6975764cd73 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -45,6 +45,7 @@ Implemented Papers
- P2867R2 - Remove Deprecated ``strstream``\s 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``
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 fa11da62bc080e..333d8fe824039f 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","","",""
"","","","","","",""
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 94b39e3bc78616..6c6643882886d7 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,46 @@ 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) {
+ 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) {
+ 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>)
+ {
+ 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 std::__synth_three_way(__x.get(), __y.get());
+ }
+
+ friend constexpr __synth_three_way_result<_Tp> operator<=>(reference_wrapper __x, const _Tp& __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>)
+ {
+ 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..8012c0c40d5b8e
--- /dev/null
+++ b/libcxx/test/std/utilities/function.objects/refwrap/refwrap.comparissons/equal.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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;
+ int j = 84;
+
+ // ==
+ {
+ std::reference_wrapper<int> lhs{i};
+ std::reference_wrapper<int> rhs = lhs;
+ assert(lhs == rhs);
+ }
+ {
+ std::reference_wrapper<int> lhs{i};
+ assert(lhs == i);
+ }
+ {
+ std::reference_wrapper<int> lhs{i};
+ std::reference_wrapper<const int> rhs = lhs;
+ assert(lhs == rhs);
+ }
+ // !=
+ {
+ std::reference_wrapper<int> lhs{i};
+ std::reference_wrapper<int> rhs{j};
+ assert(lhs != rhs);
+ }
+ {
+ std::reference_wrapper<int> lhs{i};
+ assert(lhs != j);
+ }
+ {
+ std::reference_wrapper<int> lhs{i};
+ std::reference_wrapper<const 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",
|
65c8d4c
to
7616aee
Compare
Oops, perhaps we need to implement LWG4071 together. |
Thanks! This makes sense. I am not familiar with the LWG issues process? When do they get "voted in" or whatever the action is called? |
✅ With the latest revision this PR passed the C/C++ code formatter. |
a565160
to
96c33c6
Compare
…rapper`` (``reference_wrapper`` operators only) Implements parially https://wg21.link/P2944R3
96c33c6
to
24dc20b
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this!
.../test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
Outdated
Show resolved
Hide resolved
requires requires(const _Tp t) { | ||
{ t < t } -> __boolean_testable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requires requires(const _Tp t) { | |
{ t < t } -> __boolean_testable; | |
requires requires(const _Tp __t) { | |
{ __t < __t } -> __boolean_testable; |
However I wonder why this constraint is used. It does not match LWG 4071.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See gcc-mirror/gcc@d86472a. This approach is also suggested in LWG4071 as an equivalent but simpler implementation stategy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
requires requires(const _Tp t) { | |
{ t < t } -> __boolean_testable; | |
// `operator<=>`: Checks the constraints of `synth-three-way` as per https://wg21.link/LWG4071 directly |
The comment on line 99 is supposed to explain just that the contraint is implemented according to the recommendation in LWG4071, obviously I failed to do that.
In practice the requires-clause can be implemented more simply (and efficiently) by checking the constraints of synth-three-way directly:
requires (const T t) { { t < t } -> boolean-testable; }
but when specified in prose in a Constraints: element it seems clearer to just use synth-three-way(x.get(), y.get()).
.../test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
Outdated
Show resolved
Hide resolved
.../test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
Outdated
Show resolved
Hide resolved
.../test/std/utilities/function.objects/refwrap/refwrap.comparissons/compare.three_way.pass.cpp
Outdated
Show resolved
Hide resolved
...s/function.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.const_ref.pass.cpp
Outdated
Show resolved
Hide resolved
...ies/function.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.refwrap.pass.cpp
Outdated
Show resolved
Hide resolved
...nction.objects/refwrap/refwrap.comparissons/compare.three_way.refwrap.refwrap_const.pass.cpp
Outdated
Show resolved
Hide resolved
|
||
_LIBCPP_HIDE_FROM_ABI friend constexpr auto operator<=>(reference_wrapper __x, reference_wrapper __y) | ||
requires requires(const _Tp __t) { | ||
{ __t < __t } -> __boolean_testable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not implement the proposed resolution of LWG4071. Can you update it to the current proposed resolution? We should also have additonal tests that detect objects that pass this constraint but do not satisfy the __synth_three_way
concept.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your last change misses one change.
You were right and __cpp_lib_reference_wrapper
should indeed be marked as implemented in this patch.
With that fixed it LGTM.
I didn't bother to set it because I wanted to ask you about your comment:
I don't quite understand it: Could you please confirm again that your are OK for me to merge the PR now? |
reference_wrapper
(reference_wrapper
operators only)reference_wrapper
(reference_wrapper
operators only)
reference_wrapper
(reference_wrapper
operators only)reference_wrapper
(reference_wrapper
operators only)
@mordante I would also like to start adding |
Normally I wouldn't have minded to do it in this patch. However since this patch is as good as finished I rather have it in a separate patch. Just to make reviewing easier. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Thank you! |
Implements https://wg21.link/P2944R3 (partially)
Implements https://wg21.link/LWG4071 / https://cplusplus.github.io/LWG/issue4071 (fixes build failures in the test suite)