Skip to content

[libc++] Implement comparison operators for tuple added in C++23 #148799

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_constexpr_queue`` ``202502L``
---------------------------------------------------------- -----------------
``__cpp_lib_constrained_equality`` *unimplemented*
``__cpp_lib_constrained_equality`` ``202411L``
---------------------------------------------------------- -----------------
``__cpp_lib_copyable_function`` *unimplemented*
---------------------------------------------------------- -----------------
Expand Down
2 changes: 2 additions & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ Implemented Papers
- P2711R1: Making multi-param constructors of ``views`` ``explicit`` (`Github <https://github.com/llvm/llvm-project/issues/105252>`__)
- P2770R0: Stashing stashing ``iterators`` for proper flattening (`Github <https://github.com/llvm/llvm-project/issues/105250>`__)
- P2655R3: ``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type (`Github <https://github.com/llvm/llvm-project/issues/105260>`__)
- P2944R3: Comparisons for ``reference_wrapper`` (`Github <https://github.com/llvm/llvm-project/issues/105424>`__)
- P3379R0: Constrain ``std::expected equality`` operators (`Github <https://github.com/llvm/llvm-project/issues/118135>`__)

Improvements and New Features
-----------------------------
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","",""
"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","",""
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18",""
"`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."
"`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."
Copy link
Member

Choose a reason for hiding this comment

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

In principle, this would need to be an update to the BEGIN-RST-NOTES tag on #105200. But I agree this is not ergonomic since you really want the change to be associated to this PR and blameable somehow. Perhaps this means that we should make the status pages be the canonical representation of our conformance state, or perhaps it means that the version of the notes in the status pages should be the canonical ones? As I said in #148874, I'd love to find a workflow that works and is relatively simple.

"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","",""
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16",""
"`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",""
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/Status/Cxx2cIssues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -149,4 +149,5 @@
"`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",""
"`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in ``<=>``","Not Adopted Yet","|Complete|","20",""
"`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by ``std::from_chars`` is underspecified (option B)","Not Adopted Yet","|Complete|","20",""
"`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."
"","","","","",""
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx2cPapers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","",""
"`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","",""
"`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","",""
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Complete|","21",""
"`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",""
"","","","","",""
Expand Down
132 changes: 104 additions & 28 deletions libcxx/include/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@ public:

void swap(tuple&) noexcept(AND(swap(declval<T&>(), declval<T&>())...)); // constexpr in C++20
constexpr void swap(const tuple&) const noexcept(see-below); // C++23

template<tuple-like UTuple>
friend constexpr bool operator==(const tuple& t, const UTuple& u); // C++23
template<tuple-like UTuple>
friend constexpr auto operator<=>(const tuple& t, const UTuple& u); // C++23
};


Expand Down Expand Up @@ -220,7 +225,9 @@ template <class... Types>
# include <__config>
# include <__cstddef/size_t.h>
# include <__fwd/array.h>
# include <__fwd/complex.h>
# include <__fwd/pair.h>
# include <__fwd/subrange.h>
# include <__fwd/tuple.h>
# include <__memory/allocator_arg_t.h>
# include <__memory/uses_allocator.h>
Expand All @@ -230,6 +237,7 @@ template <class... Types>
# include <__tuple/sfinae_helpers.h>
# include <__tuple/tuple_element.h>
# include <__tuple/tuple_indices.h>
# include <__tuple/tuple_like.h>
# include <__tuple/tuple_like_ext.h>
# include <__tuple/tuple_size.h>
# include <__tuple/tuple_types.h>
Expand Down Expand Up @@ -288,6 +296,66 @@ _LIBCPP_BEGIN_NAMESPACE_STD

# ifndef _LIBCPP_CXX03_LANG

template <size_t _Ip, class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool __tuple_compare_equal(const _Tp& __x, const _Up& __y) {
if constexpr (_Ip == 0)
return true;
else
return std::__tuple_compare_equal<_Ip - 1>(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
}

# if _LIBCPP_STD_VER >= 26
template <class _Tp, class _Up, class _IndexSeq>
inline constexpr bool __can_tuple_compare_equal_impl = false;
template <class _Tp, class _Up, size_t... _Is>
requires(tuple_size_v<_Tp> == tuple_size_v<_Up>)
inline constexpr bool __can_tuple_compare_equal_impl<_Tp, _Up, index_sequence<_Is...>> =
__all<requires(const tuple_element_t<_Is, _Tp>& __t, const tuple_element_t<_Is, _Up>& __u) {
{ __t == __u } -> __boolean_testable;
}...>::value;

template <class _Tp, class _Up>
inline constexpr bool __can_tuple_compare_equal =
__can_tuple_compare_equal_impl<_Tp, _Up, make_index_sequence<tuple_size_v<_Tp>>>;
# endif // _LIBCPP_STD_VER >= 26

# if _LIBCPP_STD_VER >= 20
template <class _Ret, class _Tp, class _Up, size_t... _Is>
_LIBCPP_HIDE_FROM_ABI constexpr _Ret __tuple_compare_three_way(const _Tp& __x, const _Up& __y, index_sequence<_Is...>) {
_Ret __result = strong_ordering::equal;
static_cast<void>(
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
return __result;
}
# endif // _LIBCPP_STD_VER >= 20

# if _LIBCPP_STD_VER >= 23
template <class>
inline constexpr bool __is_tuple_v = false;

template <class... _Tp>
inline constexpr bool __is_tuple_v<tuple<_Tp...>> = true;

template <class _Tp>
concept __tuple_like_no_tuple = __tuple_like<_Tp> && !__is_tuple_v<_Tp>;

template <class _Tp, class _Up, class _IndexSeq>
struct __tuple_common_comparison_category_impl {};
template <class _Tp, class _Up, size_t... _Is>
requires(tuple_size_v<_Tp> == tuple_size_v<_Up>) && requires {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

(tuple_size_v<_Tp> == tuple_size_v<_Up>) && is added for CWG2369. The version of Clang used for CI hasn't got #122423 landed.

typename common_comparison_category_t<
__synth_three_way_result<tuple_element_t<_Is, _Tp>, tuple_element_t<_Is, _Up>>...>;
}
struct __tuple_common_comparison_category_impl<_Tp, _Up, index_sequence<_Is...>> {
using type _LIBCPP_NODEBUG =
common_comparison_category_t<__synth_three_way_result<tuple_element_t<_Is, _Tp>, tuple_element_t<_Is, _Up>>...>;
};

template <__tuple_like _Tp, __tuple_like _Up>
using __tuple_common_comparison_category _LIBCPP_NODEBUG =
__tuple_common_comparison_category_impl<_Tp, _Up, make_index_sequence<tuple_size_v<_Tp>>>::type;
# endif // _LIBCPP_STD_VER >= 23

// __tuple_leaf

template <size_t _Ip, class _Hp, bool = is_empty<_Hp>::value && !__libcpp_is_final<_Hp>::value >
Expand Down Expand Up @@ -997,7 +1065,24 @@ public:
noexcept(__all<is_nothrow_swappable_v<const _Tp&>...>::value) {
__base_.swap(__t.__base_);
}
# endif // _LIBCPP_STD_VER >= 23

template <__tuple_like_no_tuple _UTuple>
# if _LIBCPP_STD_VER >= 26
requires __can_tuple_compare_equal<tuple, _UTuple> && (sizeof...(_Tp) == tuple_size_v<_UTuple>)
# endif // _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple& __x, const _UTuple& __y) {
static_assert(sizeof...(_Tp) == tuple_size_v<_UTuple>, "Can't compare tuple-like values of different sizes");
return std::__tuple_compare_equal<sizeof...(_Tp)>(__x, __y);
}

template <__tuple_like_no_tuple _UTuple>
requires(sizeof...(_Tp) == tuple_size_v<_UTuple>)
_LIBCPP_HIDE_FROM_ABI friend constexpr __tuple_common_comparison_category<tuple, _UTuple>
operator<=>(const tuple& __x, const _UTuple& __y) {
return std::__tuple_compare_three_way<__tuple_common_comparison_category<tuple, _UTuple>>(
__x, __y, index_sequence_for<_Tp...>{});
}
# endif // _LIBCPP_STD_VER >= 23
};

_LIBCPP_DIAGNOSTIC_PUSH
Expand All @@ -1019,6 +1104,21 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(tuple&) _NOEXCEPT {}
# if _LIBCPP_STD_VER >= 23
_LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {}

template <__tuple_like_no_tuple _UTuple>
# if _LIBCPP_STD_VER >= 26
requires(tuple_size_v<_UTuple> == 0)
# endif // _LIBCPP_STD_VER >= 26
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple&, const _UTuple&) {
static_assert(tuple_size_v<_UTuple> == 0, "Can't compare tuple-like values of different sizes");
return true;
}

template <__tuple_like_no_tuple _UTuple>
requires(tuple_size_v<_UTuple> == 0)
_LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering operator<=>(const tuple&, const _UTuple&) {
return strong_ordering::equal;
}
# endif
};
_LIBCPP_DIAGNOSTIC_POP
Expand Down Expand Up @@ -1137,22 +1237,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&&...> forwa
return tuple<_Tp&&...>(std::forward<_Tp>(__t)...);
}

template <size_t _Ip>
struct __tuple_equal {
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp& __x, const _Up& __y) {
return __tuple_equal<_Ip - 1>()(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
}
};

template <>
struct __tuple_equal<0> {
template <class _Tp, class _Up>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp&, const _Up&) {
return true;
}
};

template <class... _Tp, class... _Up>
# if _LIBCPP_STD_VER >= 26
requires(__all<requires(const _Tp& __t, const _Up& __u) {
Expand All @@ -1162,27 +1246,19 @@ template <class... _Tp, class... _Up>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes");
return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
return std::__tuple_compare_equal<sizeof...(_Tp)>(__x, __y);
}

# if _LIBCPP_STD_VER >= 20

// operator<=>

template <class... _Tp, class... _Up, size_t... _Is>
_LIBCPP_HIDE_FROM_ABI constexpr auto
__tuple_compare_three_way(const tuple<_Tp...>& __x, const tuple<_Up...>& __y, index_sequence<_Is...>) {
common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...> __result = strong_ordering::equal;
static_cast<void>(
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
return __result;
}

template <class... _Tp, class... _Up>
requires(sizeof...(_Tp) == sizeof...(_Up))
_LIBCPP_HIDE_FROM_ABI constexpr common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>
operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
return std::__tuple_compare_three_way(__x, __y, index_sequence_for<_Tp...>{});
return std::__tuple_compare_three_way<common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>>(
__x, __y, index_sequence_for<_Tp...>{});
}

# else // _LIBCPP_STD_VER >= 20
Expand Down
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_constexpr_new 202406L
# endif
# define __cpp_lib_constexpr_queue 202502L
// # define __cpp_lib_constrained_equality 202411L
# 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,11 @@

#elif TEST_STD_VER > 23

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# ifndef __cpp_lib_expected
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,17 +119,11 @@

#elif TEST_STD_VER > 23

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,17 +270,11 @@
# error "__cpp_lib_constexpr_tuple should have the value 201811L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# ifndef __cpp_lib_make_from_tuple
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,17 +401,11 @@
# error "__cpp_lib_constexpr_utility should have the value 201811L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# ifndef __cpp_lib_exchange_function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,17 +99,11 @@

#elif TEST_STD_VER > 23

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6631,17 +6631,11 @@
# error "__cpp_lib_constexpr_vector should have the value 201907L in c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif
# else
# ifdef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_constrained_equality
# error "__cpp_lib_constrained_equality should be defined in c++26"
# endif
# if __cpp_lib_constrained_equality != 202411L
# error "__cpp_lib_constrained_equality should have the value 202411L in c++26"
# endif

# ifndef __cpp_lib_containers_ranges
Expand Down
Loading
Loading