Skip to content

Commit 4a509f8

Browse files
[libc++] Implement comparison operators for tuple added in C++23 (#148799)
And constrain the new `operator==` since C++26. This patch implements parts of P2165R4, P2944R3, and a possibly improved resolution of LWG3882. Currently, libstdc++ and MSVC STL constrain the new overloads in the same way. Also set feature-test macro `__cpp_lib_constrained_equality` and add related release note, as P2944R3 will completed with this patch. Fixes #136765 Fixes #136770 Fixes #105424
1 parent d7c7562 commit 4a509f8

18 files changed

+852
-425
lines changed

libcxx/docs/FeatureTestMacroTable.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ Status
432432
---------------------------------------------------------- -----------------
433433
``__cpp_lib_constexpr_queue`` ``202502L``
434434
---------------------------------------------------------- -----------------
435-
``__cpp_lib_constrained_equality`` *unimplemented*
435+
``__cpp_lib_constrained_equality`` ``202411L``
436436
---------------------------------------------------------- -----------------
437437
``__cpp_lib_copyable_function`` *unimplemented*
438438
---------------------------------------------------------- -----------------

libcxx/docs/ReleaseNotes/21.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ Implemented Papers
5353
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
5454
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
5555
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
56+
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://github.com/llvm/llvm-project/issues/105424>`__)
57+
- P3379R0: Constrain ``std::expected equality`` operators (`Github <https://github.com/llvm/llvm-project/issues/118135>`__)
5658

5759
Improvements and New Features
5860
-----------------------------

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","",""
6161
"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","",""
6262
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18",""
63-
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","Only the part for ``zip_view`` is implemented."
63+
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","Changes of ``tuple``, ``adjacent_view``, and ``cartesian_product_view`` are not yet implemented."
6464
"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","",""
6565
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16",""
6666
"`P2291R3 <https://wg21.link/P2291R3>`__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","2022-07 (Virtual)","|Complete|","16",""

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,5 @@
149149
"`LWG3343 <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 Adopted Yet","|Complete|","16",""
150150
"`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in ``<=>``","Not Adopted Yet","|Complete|","20",""
151151
"`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by ``std::from_chars`` is underspecified (option B)","Not Adopted Yet","|Complete|","20",""
152+
"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","21","The comparsion operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same."
152153
"","","","","",""

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","",""
6060
"`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","",""
6161
"`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","",""
62-
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
62+
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","21",""
6363
"`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","",""
6464
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19",""
6565
"","","","","",""

libcxx/include/tuple

Lines changed: 105 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ public:
106106
107107
void swap(tuple&) noexcept(AND(swap(declval<T&>(), declval<T&>())...)); // constexpr in C++20
108108
constexpr void swap(const tuple&) const noexcept(see-below); // C++23
109+
110+
template<tuple-like UTuple>
111+
friend constexpr bool operator==(const tuple& t, const UTuple& u); // C++23
112+
template<tuple-like UTuple>
113+
friend constexpr auto operator<=>(const tuple& t, const UTuple& u); // C++23
109114
};
110115
111116
@@ -220,6 +225,7 @@ template <class... Types>
220225
# include <__config>
221226
# include <__cstddef/size_t.h>
222227
# include <__fwd/array.h>
228+
# include <__fwd/get.h>
223229
# include <__fwd/pair.h>
224230
# include <__fwd/tuple.h>
225231
# include <__memory/allocator_arg_t.h>
@@ -229,6 +235,7 @@ template <class... Types>
229235
# include <__tuple/make_tuple_types.h>
230236
# include <__tuple/sfinae_helpers.h>
231237
# include <__tuple/tuple_element.h>
238+
# include <__tuple/tuple_like.h>
232239
# include <__tuple/tuple_like_ext.h>
233240
# include <__tuple/tuple_size.h>
234241
# include <__tuple/tuple_types.h>
@@ -287,6 +294,68 @@ _LIBCPP_BEGIN_NAMESPACE_STD
287294

288295
# ifndef _LIBCPP_CXX03_LANG
289296

297+
template <size_t _Ip, class _Tp, class _Up>
298+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __tuple_compare_equal(const _Tp& __x, const _Up& __y) {
299+
if constexpr (_Ip == 0)
300+
return true;
301+
else
302+
return std::__tuple_compare_equal<_Ip - 1>(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
303+
}
304+
305+
# if _LIBCPP_STD_VER >= 26
306+
template <class _Tp, class _Up, class _IndexSeq = make_index_sequence<tuple_size_v<_Tp>>>
307+
inline constexpr bool __can_tuple_compare_equal = false;
308+
309+
// TODO(LLVM 22): Remove `tuple_size_v<_Tp> == tuple_size_v<_Up>` here once once LLVM-20 support ends
310+
// because the resolution of CWG2369 landed in LLVM-21.
311+
template <class _Tp, class _Up, size_t... _Is>
312+
requires(tuple_size_v<_Tp> == tuple_size_v<_Up>)
313+
inline constexpr bool __can_tuple_compare_equal<_Tp, _Up, index_sequence<_Is...>> =
314+
__all<requires(const tuple_element_t<_Is, _Tp>& __t, const tuple_element_t<_Is, _Up>& __u) {
315+
{ __t == __u } -> __boolean_testable;
316+
}...>::value;
317+
# endif // _LIBCPP_STD_VER >= 26
318+
319+
# if _LIBCPP_STD_VER >= 20
320+
template <class _Ret, class _Tp, class _Up, size_t... _Is>
321+
_LIBCPP_HIDE_FROM_ABI constexpr _Ret __tuple_compare_three_way(const _Tp& __x, const _Up& __y, index_sequence<_Is...>) {
322+
_Ret __result = strong_ordering::equal;
323+
static_cast<void>(
324+
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
325+
return __result;
326+
}
327+
# endif // _LIBCPP_STD_VER >= 20
328+
329+
# if _LIBCPP_STD_VER >= 23
330+
template <class>
331+
inline constexpr bool __is_tuple_v = false;
332+
333+
template <class... _Tp>
334+
inline constexpr bool __is_tuple_v<tuple<_Tp...>> = true;
335+
336+
template <class _Tp>
337+
concept __tuple_like_no_tuple = __tuple_like<_Tp> && !__is_tuple_v<_Tp>;
338+
339+
template <class _Tp, class _Up, class _IndexSeq>
340+
struct __tuple_common_comparison_category_impl {};
341+
342+
// TODO(LLVM 22): Remove `tuple_size_v<_Tp> == tuple_size_v<_Up>` here once once LLVM-20 support ends
343+
// because the resolution of CWG2369 landed in LLVM-21.
344+
template <class _Tp, class _Up, size_t... _Is>
345+
requires(tuple_size_v<_Tp> == tuple_size_v<_Up>) && requires {
346+
typename common_comparison_category_t<
347+
__synth_three_way_result<tuple_element_t<_Is, _Tp>, tuple_element_t<_Is, _Up>>...>;
348+
}
349+
struct __tuple_common_comparison_category_impl<_Tp, _Up, index_sequence<_Is...>> {
350+
using type _LIBCPP_NODEBUG =
351+
common_comparison_category_t<__synth_three_way_result<tuple_element_t<_Is, _Tp>, tuple_element_t<_Is, _Up>>...>;
352+
};
353+
354+
template <__tuple_like _Tp, __tuple_like _Up>
355+
using __tuple_common_comparison_category _LIBCPP_NODEBUG =
356+
__tuple_common_comparison_category_impl<_Tp, _Up, make_index_sequence<tuple_size_v<_Tp>>>::type;
357+
# endif // _LIBCPP_STD_VER >= 23
358+
290359
// __tuple_leaf
291360

292361
template <size_t _Ip, class _Hp, bool = is_empty<_Hp>::value && !__libcpp_is_final<_Hp>::value >
@@ -956,7 +1025,24 @@ public:
9561025
noexcept(__all<is_nothrow_swappable_v<const _Tp&>...>::value) {
9571026
__base_.swap(__t.__base_);
9581027
}
959-
# endif // _LIBCPP_STD_VER >= 23
1028+
1029+
template <__tuple_like_no_tuple _UTuple>
1030+
# if _LIBCPP_STD_VER >= 26
1031+
requires __can_tuple_compare_equal<tuple, _UTuple> && (sizeof...(_Tp) == tuple_size_v<_UTuple>)
1032+
# endif // _LIBCPP_STD_VER >= 26
1033+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple& __x, const _UTuple& __y) {
1034+
static_assert(sizeof...(_Tp) == tuple_size_v<_UTuple>, "Can't compare tuple-like values of different sizes");
1035+
return std::__tuple_compare_equal<sizeof...(_Tp)>(__x, __y);
1036+
}
1037+
1038+
template <__tuple_like_no_tuple _UTuple>
1039+
requires(sizeof...(_Tp) == tuple_size_v<_UTuple>)
1040+
_LIBCPP_HIDE_FROM_ABI friend constexpr __tuple_common_comparison_category<tuple, _UTuple>
1041+
operator<=>(const tuple& __x, const _UTuple& __y) {
1042+
return std::__tuple_compare_three_way<__tuple_common_comparison_category<tuple, _UTuple>>(
1043+
__x, __y, index_sequence_for<_Tp...>{});
1044+
}
1045+
# endif // _LIBCPP_STD_VER >= 23
9601046
};
9611047

9621048
_LIBCPP_DIAGNOSTIC_PUSH
@@ -978,6 +1064,21 @@ public:
9781064
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(tuple&) _NOEXCEPT {}
9791065
# if _LIBCPP_STD_VER >= 23
9801066
_LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {}
1067+
1068+
template <__tuple_like_no_tuple _UTuple>
1069+
# if _LIBCPP_STD_VER >= 26
1070+
requires(tuple_size_v<_UTuple> == 0)
1071+
# endif // _LIBCPP_STD_VER >= 26
1072+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple&, const _UTuple&) {
1073+
static_assert(tuple_size_v<_UTuple> == 0, "Can't compare tuple-like values of different sizes");
1074+
return true;
1075+
}
1076+
1077+
template <__tuple_like_no_tuple _UTuple>
1078+
requires(tuple_size_v<_UTuple> == 0)
1079+
_LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering operator<=>(const tuple&, const _UTuple&) {
1080+
return strong_ordering::equal;
1081+
}
9811082
# endif
9821083
};
9831084
_LIBCPP_DIAGNOSTIC_POP
@@ -1096,22 +1197,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&&...> forwa
10961197
return tuple<_Tp&&...>(std::forward<_Tp>(__t)...);
10971198
}
10981199

1099-
template <size_t _Ip>
1100-
struct __tuple_equal {
1101-
template <class _Tp, class _Up>
1102-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp& __x, const _Up& __y) {
1103-
return __tuple_equal<_Ip - 1>()(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
1104-
}
1105-
};
1106-
1107-
template <>
1108-
struct __tuple_equal<0> {
1109-
template <class _Tp, class _Up>
1110-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp&, const _Up&) {
1111-
return true;
1112-
}
1113-
};
1114-
11151200
template <class... _Tp, class... _Up>
11161201
# if _LIBCPP_STD_VER >= 26
11171202
requires(__all<requires(const _Tp& __t, const _Up& __u) {
@@ -1121,27 +1206,19 @@ template <class... _Tp, class... _Up>
11211206
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
11221207
operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
11231208
static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes");
1124-
return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
1209+
return std::__tuple_compare_equal<sizeof...(_Tp)>(__x, __y);
11251210
}
11261211

11271212
# if _LIBCPP_STD_VER >= 20
11281213

11291214
// operator<=>
11301215

1131-
template <class... _Tp, class... _Up, size_t... _Is>
1132-
_LIBCPP_HIDE_FROM_ABI constexpr auto
1133-
__tuple_compare_three_way(const tuple<_Tp...>& __x, const tuple<_Up...>& __y, index_sequence<_Is...>) {
1134-
common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...> __result = strong_ordering::equal;
1135-
static_cast<void>(
1136-
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
1137-
return __result;
1138-
}
1139-
11401216
template <class... _Tp, class... _Up>
11411217
requires(sizeof...(_Tp) == sizeof...(_Up))
11421218
_LIBCPP_HIDE_FROM_ABI constexpr common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>
11431219
operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
1144-
return std::__tuple_compare_three_way(__x, __y, index_sequence_for<_Tp...>{});
1220+
return std::__tuple_compare_three_way<common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>>(
1221+
__x, __y, index_sequence_for<_Tp...>{});
11451222
}
11461223

11471224
# else // _LIBCPP_STD_VER >= 20

libcxx/include/version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ __cpp_lib_void_t 201411L <type_traits>
555555
# define __cpp_lib_constexpr_new 202406L
556556
# endif
557557
# define __cpp_lib_constexpr_queue 202502L
558-
// # define __cpp_lib_constrained_equality 202411L
558+
# define __cpp_lib_constrained_equality 202411L
559559
// # define __cpp_lib_copyable_function 202306L
560560
// # define __cpp_lib_debugging 202311L
561561
// # define __cpp_lib_default_template_type_for_algorithm_values 202403L

libcxx/test/std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,11 @@
9393

9494
#elif TEST_STD_VER > 23
9595

96-
# if !defined(_LIBCPP_VERSION)
97-
# ifndef __cpp_lib_constrained_equality
98-
# error "__cpp_lib_constrained_equality should be defined in c++26"
99-
# endif
100-
# if __cpp_lib_constrained_equality != 202411L
101-
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
102-
# endif
103-
# else
104-
# ifdef __cpp_lib_constrained_equality
105-
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
106-
# endif
96+
# ifndef __cpp_lib_constrained_equality
97+
# error "__cpp_lib_constrained_equality should be defined in c++26"
98+
# endif
99+
# if __cpp_lib_constrained_equality != 202411L
100+
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
107101
# endif
108102

109103
# ifndef __cpp_lib_expected

libcxx/test/std/language.support/support.limits/support.limits.general/optional.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,11 @@
119119

120120
#elif TEST_STD_VER > 23
121121

122-
# if !defined(_LIBCPP_VERSION)
123-
# ifndef __cpp_lib_constrained_equality
124-
# error "__cpp_lib_constrained_equality should be defined in c++26"
125-
# endif
126-
# if __cpp_lib_constrained_equality != 202411L
127-
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
128-
# endif
129-
# else
130-
# ifdef __cpp_lib_constrained_equality
131-
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
132-
# endif
122+
# ifndef __cpp_lib_constrained_equality
123+
# error "__cpp_lib_constrained_equality should be defined in c++26"
124+
# endif
125+
# if __cpp_lib_constrained_equality != 202411L
126+
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
133127
# endif
134128

135129
# if !defined(_LIBCPP_VERSION)

libcxx/test/std/language.support/support.limits/support.limits.general/tuple.version.compile.pass.cpp

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -270,17 +270,11 @@
270270
# error "__cpp_lib_constexpr_tuple should have the value 201811L in c++26"
271271
# endif
272272

273-
# if !defined(_LIBCPP_VERSION)
274-
# ifndef __cpp_lib_constrained_equality
275-
# error "__cpp_lib_constrained_equality should be defined in c++26"
276-
# endif
277-
# if __cpp_lib_constrained_equality != 202411L
278-
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
279-
# endif
280-
# else
281-
# ifdef __cpp_lib_constrained_equality
282-
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
283-
# endif
273+
# ifndef __cpp_lib_constrained_equality
274+
# error "__cpp_lib_constrained_equality should be defined in c++26"
275+
# endif
276+
# if __cpp_lib_constrained_equality != 202411L
277+
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
284278
# endif
285279

286280
# ifndef __cpp_lib_make_from_tuple

0 commit comments

Comments
 (0)