From 12fc4301a288677d4b134be4b0b851bcefba3cd6 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 8 Apr 2025 08:11:51 +0300 Subject: [PATCH 1/7] [libc++][pair] P2944R3: Constrain `pair` equality operator Implements https://wg21.link/P2944R3 (partially): - [pairs.spec](https://eel.is/c++draft/pairs.spec) Related issues: - Related to #105424 - Related to #118135 --- libcxx/include/__utility/pair.h | 10 ++- libcxx/include/tuple | 11 ++- .../size_incompatible_comparison.verify.cpp | 6 ++ .../pairs/pairs.spec/comparison.pass.cpp | 77 +++++++++++++++++++ 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h index 1f596a87f7cc7..72363cac9ebec 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -11,6 +11,7 @@ #include <__compare/common_comparison_category.h> #include <__compare/synth_three_way.h> +#include <__concepts/boolean_testable.h> #include <__concepts/different_from.h> #include <__config> #include <__cstddef/size_t.h> @@ -447,7 +448,14 @@ pair(_T1, _T2) -> pair<_T1, _T2>; template inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool -operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y) { +operator==(const pair<_T1, _T2>& __x, const pair<_U1, _U2>& __y) +#if _LIBCPP_STD_VER >= 26 + requires requires { + { __x.first == __y.first } -> __boolean_testable; + { __x.second == __y.second } -> __boolean_testable; + } +#endif +{ return __x.first == __y.first && __x.second == __y.second; } diff --git a/libcxx/include/tuple b/libcxx/include/tuple index e284f71200492..406feb90fcc51 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -216,6 +216,7 @@ template # include <__compare/common_comparison_category.h> # include <__compare/ordering.h> # include <__compare/synth_three_way.h> +# include <__concepts/boolean_testable.h> # include <__config> # include <__cstddef/size_t.h> # include <__fwd/array.h> @@ -1160,8 +1161,16 @@ struct __tuple_equal<0> { template inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool -operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) { +operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) +# if _LIBCPP_STD_VER >= 26 + requires requires { + { sizeof...(_Tp) == sizeof...(_Up) } -> __boolean_testable; + } +# endif +{ +# if _LIBCPP_STD_VER < 26 static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes"); +# endif return __tuple_equal()(__x, __y); } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp index 851f6fcd1fbac..54936ea535c2b 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp @@ -21,9 +21,15 @@ #include +#include "test_macros.h" + void f(std::tuple t1, std::tuple t2) { // We test only the core comparison operators and trust that the others // fall back on the same implementations prior to C++20. +#if TEST_STD_VER >= 26 + static_cast(t1 == t2); +#else static_cast(t1 == t2); // expected-error@*:* {{}} +#endif static_cast(t1 < t2); // expected-error@*:* {{}} } diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp index 76f9771f2b99b..da78503abee48 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp @@ -19,9 +19,86 @@ #include #include +#include #include "test_macros.h" +#if TEST_STD_VER >= 26 + +// template +// class EqualityComparable { +// public: +// constexpr EqualityComparable(T value) : value_{value} {}; + +// friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default; + +// private: +// T value_; +// }; + +// static_assert(std::equality_comparable>>); +// // static_assert(std::equality_comparable>, std::pair>); +// // static_assert(std::equality_comparable>, EqualityComparable>>); +// // static_assert(std::equality_comparable>, std::pair, std::pair>); +// // static_assert(std::equality_comparable>, EqualityComparable>, std::pair>); +// // static_assert(std::equality_comparable>, std::pair, EqualityComparable>>); +// // static_assert(std::equality_comparable>, EqualityComparable>, EqualityComparable>>); +// static_assert(std::equality_comparable>, std::pair, std::pair, EqualityComparable>>); +// static_assert(EqualityComparable>{std::pair{94, 82}} == EqualityComparable>{std::pair{94, 82}}); +// static_assert(EqualityComparable>{std::pair{94, 82}} != EqualityComparable>{std::pair{82, 82}}); + +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); +static_assert(EqualityComparable{94} == EqualityComparable{94}); +static_assert(EqualityComparable{94} != EqualityComparable{82}); + +static_assert(std::equality_comparable>); +static_assert(std::pair{EqualityComparable{94}, EqualityComparable{94}} == + std::pair{EqualityComparable{94}, EqualityComparable{94}}); +static_assert(std::pair{EqualityComparable{82}, EqualityComparable{94}} != + std::pair{EqualityComparable{94}, EqualityComparable{94}}); +static_assert(std::pair{EqualityComparable{94}, EqualityComparable{82}} != + std::pair{EqualityComparable{94}, EqualityComparable{94}}); + +struct NonComparable {}; + +static_assert(!std::three_way_comparable>); +static_assert(!std::three_way_comparable>); +static_assert(!std::three_way_comparable>); + +static_assert(!std::equality_comparable>); +static_assert(!std::equality_comparable>); + + +template +concept HasCompare = requires(T t, U u) { + { t == u } -> std::same_as; + { t != u } -> std::same_as; + { t < u } -> std::same_as; + { t > u } -> std::same_as; + { t <= u } -> std::same_as; + { t >= u } -> std::same_as; +}; + +template +concept HasCompare = requires(std::pair p1, std::pair p2) { + { t == u } -> std::same_as; + { t != u } -> std::same_as; + { t < u } -> std::same_as; + { t > u } -> std::same_as; + { t <= u } -> std::same_as; + { t >= u } -> std::same_as; +}; + +#endif // TEST_STD_VER >= 26 + int main(int, char**) { { From 6d482136c6a064b69f0704a29ed5838d6c6bccbe Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Fri, 18 Apr 2025 10:51:33 +0300 Subject: [PATCH 2/7] Updated tests --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- libcxx/include/tuple | 11 +--- .../size_incompatible_comparison.verify.cpp | 6 --- .../pairs/pairs.spec/comparison.pass.cpp | 52 +------------------ 4 files changed, 4 insertions(+), 67 deletions(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 0cc41d2058dd5..f29de08ba7b5f 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -59,7 +59,7 @@ "`P2248R8 `__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","" "`P2810R4 `__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","" "`P1068R11 `__","Vector API for random number generation","2024-03 (Tokyo)","","","" -"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","19","Implemented comparisons for ``reference_wrapper`` only" +"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper``, ``pair`" "`P2642R6 `__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","" "`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","" "","","","","","" diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 406feb90fcc51..e284f71200492 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -216,7 +216,6 @@ template # include <__compare/common_comparison_category.h> # include <__compare/ordering.h> # include <__compare/synth_three_way.h> -# include <__concepts/boolean_testable.h> # include <__config> # include <__cstddef/size_t.h> # include <__fwd/array.h> @@ -1161,16 +1160,8 @@ struct __tuple_equal<0> { template inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool -operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) -# if _LIBCPP_STD_VER >= 26 - requires requires { - { sizeof...(_Tp) == sizeof...(_Up) } -> __boolean_testable; - } -# endif -{ -# if _LIBCPP_STD_VER < 26 +operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) { static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes"); -# endif return __tuple_equal()(__x, __y); } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp index 54936ea535c2b..851f6fcd1fbac 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/size_incompatible_comparison.verify.cpp @@ -21,15 +21,9 @@ #include -#include "test_macros.h" - void f(std::tuple t1, std::tuple t2) { // We test only the core comparison operators and trust that the others // fall back on the same implementations prior to C++20. -#if TEST_STD_VER >= 26 - static_cast(t1 == t2); -#else static_cast(t1 == t2); // expected-error@*:* {{}} -#endif static_cast(t1 < t2); // expected-error@*:* {{}} } diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp index da78503abee48..e555978dacf23 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp @@ -25,27 +25,7 @@ #if TEST_STD_VER >= 26 -// template -// class EqualityComparable { -// public: -// constexpr EqualityComparable(T value) : value_{value} {}; - -// friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default; - -// private: -// T value_; -// }; - -// static_assert(std::equality_comparable>>); -// // static_assert(std::equality_comparable>, std::pair>); -// // static_assert(std::equality_comparable>, EqualityComparable>>); -// // static_assert(std::equality_comparable>, std::pair, std::pair>); -// // static_assert(std::equality_comparable>, EqualityComparable>, std::pair>); -// // static_assert(std::equality_comparable>, std::pair, EqualityComparable>>); -// // static_assert(std::equality_comparable>, EqualityComparable>, EqualityComparable>>); -// static_assert(std::equality_comparable>, std::pair, std::pair, EqualityComparable>>); -// static_assert(EqualityComparable>{std::pair{94, 82}} == EqualityComparable>{std::pair{94, 82}}); -// static_assert(EqualityComparable>{std::pair{94, 82}} != EqualityComparable>{std::pair{82, 82}}); +// Test SFINAE. struct EqualityComparable { constexpr EqualityComparable(int value) : value_{value} {}; @@ -60,43 +40,15 @@ static_assert(EqualityComparable{94} == EqualityComparable{94}); static_assert(EqualityComparable{94} != EqualityComparable{82}); static_assert(std::equality_comparable>); -static_assert(std::pair{EqualityComparable{94}, EqualityComparable{94}} == - std::pair{EqualityComparable{94}, EqualityComparable{94}}); -static_assert(std::pair{EqualityComparable{82}, EqualityComparable{94}} != - std::pair{EqualityComparable{94}, EqualityComparable{94}}); -static_assert(std::pair{EqualityComparable{94}, EqualityComparable{82}} != - std::pair{EqualityComparable{94}, EqualityComparable{94}}); struct NonComparable {}; -static_assert(!std::three_way_comparable>); -static_assert(!std::three_way_comparable>); -static_assert(!std::three_way_comparable>); +static_assert(!std::equality_comparable); static_assert(!std::equality_comparable>); static_assert(!std::equality_comparable>); -template -concept HasCompare = requires(T t, U u) { - { t == u } -> std::same_as; - { t != u } -> std::same_as; - { t < u } -> std::same_as; - { t > u } -> std::same_as; - { t <= u } -> std::same_as; - { t >= u } -> std::same_as; -}; - -template -concept HasCompare = requires(std::pair p1, std::pair p2) { - { t == u } -> std::same_as; - { t != u } -> std::same_as; - { t < u } -> std::same_as; - { t > u } -> std::same_as; - { t <= u } -> std::same_as; - { t >= u } -> std::same_as; -}; - #endif // TEST_STD_VER >= 26 int main(int, char**) From 92544cb796bda03f59eda61060a42baa006cb407 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 22 Apr 2025 12:24:07 +0300 Subject: [PATCH 3/7] Cleanup test --- .../std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp index e555978dacf23..597400b84fd9e 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp @@ -36,8 +36,6 @@ struct EqualityComparable { }; static_assert(std::equality_comparable); -static_assert(EqualityComparable{94} == EqualityComparable{94}); -static_assert(EqualityComparable{94} != EqualityComparable{82}); static_assert(std::equality_comparable>); From 672c7c31ea2a861f4f86165bc2f27b616c1f754f Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 22 Apr 2025 12:29:21 +0300 Subject: [PATCH 4/7] Fix CI --- .../std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp index 597400b84fd9e..c472906c5ed7f 100644 --- a/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp +++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/comparison.pass.cpp @@ -46,7 +46,6 @@ static_assert(!std::equality_comparable); static_assert(!std::equality_comparable>); static_assert(!std::equality_comparable>); - #endif // TEST_STD_VER >= 26 int main(int, char**) From 3079b7050c1cd1c7017fb02d62ad081007091fde Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 22 Apr 2025 12:33:57 +0300 Subject: [PATCH 5/7] Fix docs gen --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index f29de08ba7b5f..0ed0e9f3ccb7c 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -59,7 +59,7 @@ "`P2248R8 `__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","" "`P2810R4 `__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","" "`P1068R11 `__","Vector API for random number generation","2024-03 (Tokyo)","","","" -"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper``, ``pair`" +"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper``, ``pair``" "`P2642R6 `__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","" "`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","" "","","","","","" From 40b04ce69f15015bc0d60d284e772f7b1ebacac2 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Wed, 23 Apr 2025 13:24:23 +0300 Subject: [PATCH 6/7] Update libcxx/docs/Status/Cxx2cPapers.csv Co-authored-by: Nikolas Klauser --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 0ed0e9f3ccb7c..eed71d36c67af 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -59,7 +59,7 @@ "`P2248R8 `__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","" "`P2810R4 `__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","" "`P1068R11 `__","Vector API for random number generation","2024-03 (Tokyo)","","","" -"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper``, ``pair``" +"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper`` and ``pair``" "`P2642R6 `__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","" "`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","" "","","","","","" From 3ff0e71495b0c5c4aae2eb8f1075b938607e63bd Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Tue, 29 Apr 2025 11:04:59 +0300 Subject: [PATCH 7/7] Update libcxx/docs/Status/Cxx2cPapers.csv Co-authored-by: Nikolas Klauser --- libcxx/docs/Status/Cxx2cPapers.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index eed71d36c67af..fa6f9d065716a 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -59,7 +59,7 @@ "`P2248R8 `__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","","" "`P2810R4 `__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","","" "`P1068R11 `__","Vector API for random number generation","2024-03 (Tokyo)","","","" -"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","21","Implemented changes to ``reference_wrapper`` and ``pair``" +"`P2944R3 `__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","Implemented changes to ``reference_wrapper`` and ``pair``" "`P2642R6 `__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","","" "`P3029R1 `__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19","" "","","","","",""