From 254986d2df8d8407b46329e452c16748d29ed4cd Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Sat, 6 May 2023 15:41:41 +0300 Subject: [libc++][spaceship] Implement `operator<=>` for `array` Implements part of P1614R2 "The Mothership has Landed" Reviewed By: Mordante, #libc, philnik Differential Revision: https://reviews.llvm.org/D132265 --- libcxx/docs/Status/Cxx20Issues.csv | 2 +- libcxx/docs/Status/SpaceshipProjects.csv | 3 +- libcxx/include/array | 55 +++++------ .../containers/sequences/array/compare.fail.cpp | 68 -------------- .../containers/sequences/array/compare.pass.cpp | 103 +++++++++++---------- .../sequences/array/compare.three_way.pass.cpp | 87 +++++++++++++++++ .../sequences/array/compare.three_way.verify.cpp | 37 ++++++++ .../containers/sequences/array/compare.verify.cpp | 58 ++++++++++++ libcxx/test/support/test_container_comparisons.h | 8 +- 9 files changed, 273 insertions(+), 148 deletions(-) delete mode 100644 libcxx/test/std/containers/sequences/array/compare.fail.cpp create mode 100644 libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp create mode 100644 libcxx/test/std/containers/sequences/array/compare.three_way.verify.cpp create mode 100644 libcxx/test/std/containers/sequences/array/compare.verify.cpp (limited to 'libcxx') diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv index c0143931be01..d9f87112a5e6 100644 --- a/libcxx/docs/Status/Cxx20Issues.csv +++ b/libcxx/docs/Status/Cxx20Issues.csv @@ -259,7 +259,7 @@ "`3338 `__","Rename ``default_constructible``\ to ``default_initializable``\ ","Prague","|Complete|","13.0" "`3340 `__","Formatting functions should throw on argument/format string mismatch in |sect|\ [format.functions]","Prague","|Complete|","14.0","|format|" "`3346 `__","``pair``\ and ``tuple``\ copy and move constructor have backwards specification","Prague","","" -"`3347 `__","``std::pair``\ now requires ``T``\ and ``U``\ to be less-than-comparable","Prague","","" +"`3347 `__","``std::pair``\ now requires ``T``\ and ``U``\ to be less-than-comparable","Prague","|Complete|","17.0" "`3348 `__","``__cpp_lib_unwrap_ref``\ in wrong header","Prague","|Complete|","12.0" "`3349 `__","Missing ``__cpp_lib_constexpr_complex``\ for P0415R1","Prague","|Complete|","16.0" "`3350 `__","Simplify return type of ``lexicographical_compare_three_way``\ ","Prague","|Complete|","17.0","|spaceship|" diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv index 3eecc354b811..6dd14bdd09db 100644 --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -35,7 +35,8 @@ Section,Description,Dependencies,Assignee,Complete | `[stacktrace.basic.cmp] `_,| `basic_stacktrace `_,[alg.three.way],Nikolas Klauser,|In Progress| | `[string.cmp] `_,| `basic_string `_,None,Mark de Wever,|Complete| | `[string.view.comparison] `_,| `basic_string_view `_,None,Mark de Wever,|Complete| -| `[array.syn] `_ (`general `_),| `array `_,[expos.only.func],Adrian Vogelsgesang,|In Progress| +| `[array.syn] `_ (`general `_),| `array `_,[expos.only.func],"| Adrian Vogelsgesang +| Hristo Hristov",|Complete| | `[deque.syn] `_ (`general `_),| `deque `_,[expos.only.func],Hristo Hristov,|Complete| | `[forward.list.syn] `_ (`general `_),| `forward_list `_,[expos.only.func],Hristo Hristov,|Complete| | `[list.syn] `_ (`general `_),| `list `_,[expos.only.func],Adrian Vogelsgesang,|Complete| diff --git a/libcxx/include/array b/libcxx/include/array index 58e0fb273f01..5a8a1acca475 100644 --- a/libcxx/include/array +++ b/libcxx/include/array @@ -77,15 +77,18 @@ template template bool operator==(const array& x, const array& y); // constexpr in C++20 template - bool operator!=(const array& x, const array& y); // constexpr in C++20 + bool operator!=(const array& x, const array& y); // removed in C++20 template - bool operator<(const array& x, const array& y); // constexpr in C++20 + bool operator<(const array& x, const array& y); // removed in C++20 template - bool operator>(const array& x, const array& y); // constexpr in C++20 + bool operator>(const array& x, const array& y); // removed in C++20 template - bool operator<=(const array& x, const array& y); // constexpr in C++20 + bool operator<=(const array& x, const array& y); // removed in C++20 template - bool operator>=(const array& x, const array& y); // constexpr in C++20 + bool operator>=(const array& x, const array& y); // removed in C++20 +template + constexpr synth-three-way-result + operator<=>(const array& x, const array& y); // since C++20 template void swap(array& x, array& y) noexcept(noexcept(x.swap(y))); // constexpr in C++20 @@ -111,6 +114,7 @@ template const T&& get(const array&&) noexce #include <__algorithm/equal.h> #include <__algorithm/fill_n.h> #include <__algorithm/lexicographical_compare.h> +#include <__algorithm/lexicographical_compare_three_way.h> #include <__algorithm/swap_ranges.h> #include <__assert> // all public C++ headers provide the assertion handler #include <__config> @@ -401,47 +405,44 @@ operator==(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) return _VSTD::equal(__x.begin(), __x.end(), __y.begin()); } +#if _LIBCPP_STD_VER <= 17 + template -inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_CONSTEXPR_SINCE_CXX20 bool -operator!=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) -{ +inline _LIBCPP_HIDE_FROM_ABI bool operator!=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { return !(__x == __y); } template -inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_CONSTEXPR_SINCE_CXX20 bool -operator<(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) -{ - return _VSTD::lexicographical_compare(__x.begin(), __x.end(), - __y.begin(), __y.end()); +inline _LIBCPP_HIDE_FROM_ABI bool operator<(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { + return _VSTD::lexicographical_compare(__x.begin(), __x.end(), __y.begin(), __y.end()); } template -inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_CONSTEXPR_SINCE_CXX20 bool -operator>(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) -{ +inline _LIBCPP_HIDE_FROM_ABI bool operator>(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { return __y < __x; } template -inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_CONSTEXPR_SINCE_CXX20 bool -operator<=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) -{ +inline _LIBCPP_HIDE_FROM_ABI bool operator<=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { return !(__y < __x); } template -inline _LIBCPP_INLINE_VISIBILITY -_LIBCPP_CONSTEXPR_SINCE_CXX20 bool -operator>=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) -{ +inline _LIBCPP_HIDE_FROM_ABI bool operator>=(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { return !(__x < __y); } +#else // _LIBCPP_STD_VER <= 17 + +template +_LIBCPP_HIDE_FROM_ABI constexpr __synth_three_way_result<_Tp> +operator<=>(const array<_Tp, _Size>& __x, const array<_Tp, _Size>& __y) { + return std::lexicographical_compare_three_way( + __x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way<_Tp, _Tp>); +} + +#endif // _LIBCPP_STD_VER <= 17 + template inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX20 __enable_if_t<_Size == 0 || __is_swappable<_Tp>::value, void> diff --git a/libcxx/test/std/containers/sequences/array/compare.fail.cpp b/libcxx/test/std/containers/sequences/array/compare.fail.cpp deleted file mode 100644 index 1fe4c706b62c..000000000000 --- a/libcxx/test/std/containers/sequences/array/compare.fail.cpp +++ /dev/null @@ -1,68 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -// - -// bool operator==(array const&, array const&); -// bool operator!=(array const&, array const&); -// bool operator<(array const&, array const&); -// bool operator<=(array const&, array const&); -// bool operator>(array const&, array const&); -// bool operator>=(array const&, array const&); - - -#include -#include -#include - -#include "test_macros.h" - -template -void test_compare(const Array& LHS, const Array& RHS) { - typedef std::vector Vector; - const Vector LHSV(LHS.begin(), LHS.end()); - const Vector RHSV(RHS.begin(), RHS.end()); - assert((LHS == RHS) == (LHSV == RHSV)); - assert((LHS != RHS) == (LHSV != RHSV)); - assert((LHS < RHS) == (LHSV < RHSV)); - assert((LHS <= RHS) == (LHSV <= RHSV)); - assert((LHS > RHS) == (LHSV > RHSV)); - assert((LHS >= RHS) == (LHSV >= RHSV)); -} - -template struct NoCompare {}; - -int main(int, char**) -{ - { - typedef NoCompare<0> T; - typedef std::array C; - C c1 = {{}}; - // expected-error@*:* 2 {{invalid operands to binary expression}} - TEST_IGNORE_NODISCARD (c1 == c1); - TEST_IGNORE_NODISCARD (c1 < c1); - } - { - typedef NoCompare<1> T; - typedef std::array C; - C c1 = {{}}; - // expected-error@*:* 2 {{invalid operands to binary expression}} - TEST_IGNORE_NODISCARD (c1 != c1); - TEST_IGNORE_NODISCARD (c1 > c1); - } - { - typedef NoCompare<2> T; - typedef std::array C; - C c1 = {{}}; - // expected-error@*:* 2 {{invalid operands to binary expression}} - TEST_IGNORE_NODISCARD (c1 == c1); - TEST_IGNORE_NODISCARD (c1 < c1); - } - - return 0; -} diff --git a/libcxx/test/std/containers/sequences/array/compare.pass.cpp b/libcxx/test/std/containers/sequences/array/compare.pass.cpp index 972600992980..f79a638c5688 100644 --- a/libcxx/test/std/containers/sequences/array/compare.pass.cpp +++ b/libcxx/test/std/containers/sequences/array/compare.pass.cpp @@ -8,13 +8,18 @@ // -// bool operator==(array const&, array const&); // constexpr in C++20 -// bool operator!=(array const&, array const&); // constexpr in C++20 -// bool operator<(array const&, array const&); // constexpr in C++20 -// bool operator<=(array const&, array const&); // constexpr in C++20 -// bool operator>(array const&, array const&); // constexpr in C++20 -// bool operator>=(array const&, array const&); // constexpr in C++20 - +// template +// bool operator==(const array& x, const array& y); // constexpr in C++20 +// template +// bool operator!=(const array& x, const array& y); // removed in C++20 +// template +// bool operator<(const array& x, const array& y); // removed in C++20 +// template +// bool operator>(const array& x, const array& y); // removed in C++20 +// template +// bool operator<=(const array& x, const array& y); // removed in C++20 +// template +// bool operator>=(const array& x, const array& y); // removed in C++20 #include #include @@ -22,49 +27,53 @@ #include "test_macros.h" #include "test_comparisons.h" -TEST_CONSTEXPR_CXX20 bool tests() -{ - { - typedef std::array C; - const C c1 = {1, 2, 3}; - const C c2 = {1, 2, 3}; - const C c3 = {3, 2, 1}; - const C c4 = {1, 2, 1}; - assert(testComparisons(c1, c2, true, false)); - assert(testComparisons(c1, c3, false, true)); - assert(testComparisons(c1, c4, false, false)); - } - { - typedef std::array C; - const C c1 = {}; - const C c2 = {}; - assert(testComparisons(c1, c2, true, false)); - } - { - typedef std::array C; - const C c1 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(3)}; - const C c2 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(3)}; - const C c3 = {LessAndEqComp(3), LessAndEqComp(2), LessAndEqComp(1)}; - const C c4 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(1)}; - assert(testComparisons(c1, c2, true, false)); - assert(testComparisons(c1, c3, false, true)); - assert(testComparisons(c1, c4, false, false)); - } - { - typedef std::array C; - const C c1 = {}; - const C c2 = {}; - assert(testComparisons(c1, c2, true, false)); - } +TEST_CONSTEXPR_CXX20 bool tests() { + // Arrays where the elements support all comparison operators + AssertComparisonsReturnBool >(); + { + typedef std::array C; + const C c1 = {1, 2, 3}; + const C c2 = {1, 2, 3}; + const C c3 = {3, 2, 1}; + const C c4 = {1, 2, 1}; + assert(testComparisons(c1, c2, true, false)); + assert(testComparisons(c1, c3, false, true)); + assert(testComparisons(c1, c4, false, false)); + } + // Empty array + { + typedef std::array C; + const C c1 = {}; + const C c2 = {}; + assert(testComparisons(c1, c2, true, false)); + } + // Arrays where the elements support only less and equality comparisons + AssertComparisonsReturnBool >(); + { + typedef std::array C; + const C c1 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(3)}; + const C c2 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(3)}; + const C c3 = {LessAndEqComp(3), LessAndEqComp(2), LessAndEqComp(1)}; + const C c4 = {LessAndEqComp(1), LessAndEqComp(2), LessAndEqComp(1)}; + assert(testComparisons(c1, c2, true, false)); + assert(testComparisons(c1, c3, false, true)); + assert(testComparisons(c1, c4, false, false)); + } + // Empty array where the elements support only less and equality comparisons + { + typedef std::array C; + const C c1 = {}; + const C c2 = {}; + assert(testComparisons(c1, c2, true, false)); + } - return true; + return true; } -int main(int, char**) -{ - tests(); +int main(int, char**) { + tests(); #if TEST_STD_VER >= 20 - static_assert(tests(), ""); + static_assert(tests()); #endif - return 0; + return 0; } diff --git a/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp b/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp new file mode 100644 index 000000000000..01be1db73041 --- /dev/null +++ b/libcxx/test/std/containers/sequences/array/compare.three_way.pass.cpp @@ -0,0 +1,87 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// constexpr synth-three-way-result +// operator<=>(const array& x, const array& y); + +#include +#include + +#include "test_comparisons.h" + +// SFINAE + +constexpr std::size_t N{1}; + +// The container should fulfill `std::three_way_comparable` +static_assert(std::three_way_comparable>); + +// Thanks to SFINAE, the following is not a compiler error but returns `false` +struct NonComparable {}; +static_assert(!std::three_way_comparable>); + +// Implementation detail of `test_sequence_container_array_spaceship` +template +constexpr void test_sequence_container_array_spaceship_with_type() { + // Empty containers + { + std::array l1 = {}; + std::array l2 = {}; + assert(testOrder(l1, l2, Order::equivalent)); + } + // Identical contents + { + std::array l1{Elem{1}, Elem{1}}; + std::array l2{Elem{1}, Elem{1}}; + assert(testOrder(l1, l2, Order::equivalent)); + } + // Less, due to contained values + { + std::array l1{Elem{1}, Elem{1}}; + std::array l2{Elem{1}, Elem{2}}; + assert(testOrder(l1, l2, Order::less)); + } + // Greater, due to contained values + { + std::array l1{Elem{1}, Elem{3}}; + std::array l2{Elem{1}, Elem{2}}; + assert(testOrder(l1, l2, Order::greater)); + } + // Shorter list - unsupported - containers must be of equal lengths + // Longer list - unsupported - containers must be of equal lengths + // Unordered + if constexpr (std::is_same_v) { + std::array l1{Elem{1}, Elem{std::numeric_limits::min()}}; + std::array l2{Elem{1}, Elem{2}}; + assert(testOrder(l1, l2, Order::unordered)); + } +} + +// Tests the `operator<=>` on sequence containers `array` +constexpr bool test_sequence_container_array_spaceship() { + // Test different comparison categories + test_sequence_container_array_spaceship_with_type(); + test_sequence_container_array_spaceship_with_type(); + test_sequence_container_array_spaceship_with_type(); + test_sequence_container_array_spaceship_with_type(); + + // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<` + test_sequence_container_array_spaceship_with_type(); + + return true; +} + +int main(int, char**) { + assert(test_sequence_container_array_spaceship()); + static_assert(test_sequence_container_array_spaceship()); + return 0; +} diff --git a/libcxx/test/std/containers/sequences/array/compare.three_way.verify.cpp b/libcxx/test/std/containers/sequences/array/compare.three_way.verify.cpp new file mode 100644 index 000000000000..b7267ec9b932 --- /dev/null +++ b/libcxx/test/std/containers/sequences/array/compare.three_way.verify.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// + +// template +// constexpr synth-three-way-result +// operator<=>(const array& x, const array& y); + +// arrays with different sizes should not compare + +#include + +int main(int, char**) { + { + std::array a1{1}; + std::array a2{1, 2}; + + // expected-error@*:* {{invalid operands to binary expression}} + a1 <=> a2; + } + { + std::array a1{1, 2}; + std::array a2{1}; + + // expected-error@*:* {{invalid operands to binary expression}} + a1 <=> a2; + } + + return 0; +} diff --git a/libcxx/test/std/containers/sequences/array/compare.verify.cpp b/libcxx/test/std/containers/sequences/array/compare.verify.cpp new file mode 100644 index 000000000000..4b001601a4fe --- /dev/null +++ b/libcxx/test/std/containers/sequences/array/compare.verify.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// bool operator==(const array& x, const array& y); // constexpr in C++20 +// template +// bool operator!=(const array& x, const array& y); // removed in C++20 +// template +// bool operator<(const array& x, const array& y); // removed in C++20 +// template +// bool operator>(const array& x, const array& y); // removed in C++20 +// template +// bool operator<=(const array& x, const array& y); // removed in C++20 +// template +// bool operator>=(const array& x, const array& y); // removed in C++20 + +#include + +#include "test_macros.h" + +template +struct NoCompare {}; + +int main(int, char**) { + { + typedef NoCompare<0> T; + typedef std::array C; + C c1 = {{}}; + // expected-error@*:* 2 {{invalid operands to binary expression}} + TEST_IGNORE_NODISCARD(c1 == c1); + TEST_IGNORE_NODISCARD(c1 < c1); + } + { + typedef NoCompare<1> T; + typedef std::array C; + C c1 = {{}}; + // expected-error@*:* 2 {{invalid operands to binary expression}} + TEST_IGNORE_NODISCARD(c1 != c1); + TEST_IGNORE_NODISCARD(c1 > c1); + } + { + typedef NoCompare<2> T; + typedef std::array C; + C c1 = {{}}; + // expected-error@*:* 2 {{invalid operands to binary expression}} + TEST_IGNORE_NODISCARD(c1 == c1); + TEST_IGNORE_NODISCARD(c1 < c1); + } + + return 0; +} diff --git a/libcxx/test/support/test_container_comparisons.h b/libcxx/test/support/test_container_comparisons.h index 561275c8bb79..f9dae9d6455a 100644 --- a/libcxx/test/support/test_container_comparisons.h +++ b/libcxx/test/support/test_container_comparisons.h @@ -62,7 +62,7 @@ constexpr void test_sequence_container_spaceship_with_type() { // Tests the `operator<=>` on sequence containers template