diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -33,7 +33,7 @@ | `[stacktrace.entry.cmp] `_,| stacktrace_entry,None,Unassigned,|Not Started| | `[stacktrace.basic.cmp] `_,| basic_stacktrace,[alg.three.way],Unassigned,|Not Started| | `[string.cmp] `_,| `basic_string `_,None,Christopher Di Bella,|In Progress| -| `[string.view.comparison] `_,| `basic_string_view `_,None,Christopher Di Bella,|In Progress| +| `[string.view.comparison] `_,|,None,Mark de Wever,|Complete| | `[array.syn] `_ (`general `_),| array,[expos.only.func],Unassigned,|Not Started| | `[deque.syn] `_ (`general `_),| deque,[expos.only.func],Unassigned,|Not Started| | `[forward.list.syn] `_ (`general `_),| forward_list,[expos.only.func],Unassigned,|Not Started| diff --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h --- a/libcxx/include/__string/char_traits.h +++ b/libcxx/include/__string/char_traits.h @@ -17,6 +17,7 @@ #include <__config> #include <__functional/hash.h> #include <__iterator/iterator_traits.h> +#include #include #include #include @@ -193,6 +194,9 @@ typedef streamoff off_type; typedef streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + using comparison_category = strong_ordering; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -307,6 +311,9 @@ typedef streamoff off_type; typedef streampos pos_type; typedef mbstate_t state_type; +# if _LIBCPP_STD_VER > 17 + using comparison_category = strong_ordering; +# endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -423,6 +430,9 @@ typedef streamoff off_type; typedef u8streampos pos_type; typedef mbstate_t state_type; +# if _LIBCPP_STD_VER > 17 + using comparison_category = strong_ordering; +# endif static inline constexpr void assign(char_type& __c1, const char_type& __c2) noexcept {__c1 = __c2;} @@ -524,6 +534,9 @@ typedef streamoff off_type; typedef u16streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + using comparison_category = strong_ordering; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -615,6 +628,9 @@ typedef streamoff off_type; typedef u32streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + using comparison_category = strong_ordering; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} diff --git a/libcxx/include/string_view b/libcxx/include/string_view --- a/libcxx/include/string_view +++ b/libcxx/include/string_view @@ -14,6 +14,8 @@ string_view synopsis +#include + namespace std { // 7.2, Class template basic_string_view @@ -30,21 +32,25 @@ template constexpr bool operator==(basic_string_view x, basic_string_view y) noexcept; - template + template // Removed in C++20 constexpr bool operator!=(basic_string_view x, basic_string_view y) noexcept; template constexpr bool operator< (basic_string_view x, basic_string_view y) noexcept; - template + template // Removed in C++20 constexpr bool operator> (basic_string_view x, basic_string_view y) noexcept; - template + template // Removed in C++20 constexpr bool operator<=(basic_string_view x, basic_string_view y) noexcept; - template + template // Removed in C++20 constexpr bool operator>=(basic_string_view x, basic_string_view y) noexcept; + template // Since C++20 + constexpr see below operator<=>(basic_string_view x, + basic_string_view y) noexcept; + // see below, sufficient additional overloads of comparison functions // 7.10, Inserters and extractors @@ -780,6 +786,41 @@ } +#if _LIBCPP_STD_VER > 17 + +// operator <=> + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto +operator<=>(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) noexcept { + if constexpr (requires { typename _Traits::comparison_category; }) + return static_cast(__lhs.compare(__rhs) <=> 0); + else + return static_cast(__lhs.compare(__rhs) <=> 0); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>( + basic_string_view<_CharT, _Traits> __lhs, + typename common_type >::type __rhs) noexcept { + if constexpr (requires { typename _Traits::comparison_category; }) + return static_cast(__lhs.compare(__rhs) <=> 0); + else + return static_cast(__lhs.compare(__rhs) <=> 0); +} + +template +_LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>( + typename common_type >::type __lhs, + basic_string_view<_CharT, _Traits> __rhs) noexcept { + if constexpr (requires { typename _Traits::comparison_category; }) + return static_cast(__lhs.compare(__rhs) <=> 0); + else + return static_cast(__lhs.compare(__rhs) <=> 0); +} + +#else // _LIBCPP_STD_VER > 17 + // operator != template _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY @@ -911,6 +952,7 @@ return __lhs.compare(__rhs) >= 0; } +#endif // _LIBCPP_STD_VER > 17 template basic_ostream<_CharT, _Traits>& diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/types.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/types.pass.cpp --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/types.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/types.pass.cpp @@ -15,6 +15,7 @@ // typedef streamoff off_type; // typedef streampos pos_type; // typedef mbstate_t state_type; +// using comparison_category = strong_ordering; #include #include @@ -28,6 +29,9 @@ static_assert((std::is_same::off_type, std::streamoff>::value), ""); static_assert((std::is_same::pos_type, std::streampos>::value), ""); static_assert((std::is_same::state_type, std::mbstate_t>::value), ""); +#if TEST_STD_VER > 17 + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); +#endif return 0; } diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char16_t/types.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char16_t/types.pass.cpp --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char16_t/types.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char16_t/types.pass.cpp @@ -15,6 +15,7 @@ // typedef streamoff off_type; // typedef u16streampos pos_type; // typedef mbstate_t state_type; +// using comparison_category = strong_ordering; #include #include @@ -29,6 +30,9 @@ static_assert((std::is_same::off_type, std::streamoff>::value), ""); static_assert((std::is_same::pos_type, std::u16streampos>::value), ""); static_assert((std::is_same::state_type, std::mbstate_t>::value), ""); +#if TEST_STD_VER > 17 + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); +#endif return 0; } diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char32_t/types.compile.pass.cpp @@ -15,6 +15,7 @@ // typedef streamoff off_type; // typedef u32streampos pos_type; // typedef mbstate_t state_type; +// using comparison_category = strong_ordering; #include #include @@ -25,3 +26,4 @@ static_assert((std::is_same::off_type, std::streamoff>::value), ""); static_assert((std::is_same::pos_type, std::u32streampos>::value), ""); static_assert((std::is_same::state_type, std::mbstate_t>::value), ""); +static_assert(std::is_same_v::comparison_category, std::strong_ordering>); diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char8_t/types.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char8_t/types.pass.cpp --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char8_t/types.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.char8_t/types.pass.cpp @@ -16,6 +16,7 @@ // typedef streamoff off_type; // typedef u8streampos pos_type; // typedef mbstate_t state_type; +// using comparison_category = strong_ordering; #include #include @@ -31,6 +32,7 @@ static_assert((std::is_same::off_type, std::streamoff>::value), ""); static_assert((std::is_same::pos_type, std::u8streampos>::value), ""); static_assert((std::is_same::state_type, std::mbstate_t>::value), ""); + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); #endif return 0; diff --git a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/types.pass.cpp b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/types.pass.cpp --- a/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/types.pass.cpp +++ b/libcxx/test/std/strings/char.traits/char.traits.specializations/char.traits.specializations.wchar.t/types.pass.cpp @@ -15,6 +15,7 @@ // typedef streamoff off_type; // typedef streampos pos_type; // typedef mbstate_t state_type; +// using comparison_category = strong_ordering; // UNSUPPORTED: no-wide-characters @@ -30,6 +31,9 @@ static_assert((std::is_same::off_type, std::streamoff>::value), ""); static_assert((std::is_same::pos_type, std::wstreampos>::value), ""); static_assert((std::is_same::state_type, std::mbstate_t>::value), ""); +#if TEST_STD_VER > 17 + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); +#endif return 0; } diff --git a/libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp b/libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.comparison/comparison.pass.cpp @@ -0,0 +1,113 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// Starting with C++20 the spaceship operator was included. This tests +// comparison in that context, thus doesn't support older language versions. +// These are tested per operator. + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// + +// template +// constexpr bool operator==(basic_string_view lhs, basic_string_view rhs); +// template +// constexpr auto operator<=>(basic_string_view lhs, basic_string_view rhs); +// (plus "sufficient additional overloads" to make implicit conversions work as intended) + +#include + +#include +#include +#include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "test_comparisons.h" +#include "test_macros.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + +template +constexpr bool test() { + AssertOrderAreNoexcept(); + AssertOrderReturn(); + + using CharT = typename T::value_type; + + // sorted values + std::array v{ + SV(""), + SV("abc"), + SV("abcdef"), + SV("acb"), + }; + + // sorted values with embedded NUL character + std::array vn{ + SV("abc"), + SV("abc\0"), + SV("abc\0def"), + SV("acb\0"), + }; + static_assert(v.size() == vn.size()); + + for (size_t i = 0; i < v.size(); ++i) { + for (size_t j = 0; j < v.size(); ++j) { + assert(testOrder(v[i], v[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater)); + assert(testOrder( + v[i], + std::basic_string{v[j]}, + i == j ? Ordering::equivalent + : i < j ? Ordering::less + : Ordering::greater)); + + assert(testOrder( + v[i], + std::basic_string{v[j]}.c_str(), + i == j ? Ordering::equivalent + : i < j ? Ordering::less + : Ordering::greater)); + + // NUL test omitted for c-strings since it will fail. + assert(testOrder(vn[i], vn[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater)); + assert(testOrder( + vn[i], + std::basic_string{vn[j]}, + i == j ? Ordering::equivalent + : i < j ? Ordering::less + : Ordering::greater)); + } + } + + return true; +} + +constexpr bool test_all_types() { + test(); + test>, std::weak_ordering>(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); + test>, std::weak_ordering>(); +#endif + test(); + test>, std::weak_ordering>(); + test(); + test>, std::weak_ordering>(); + test(); + test>, std::weak_ordering>(); + + return true; +} + +int main(int, char**) { + test_all_types(); + static_assert(test_all_types()); + + return 0; +} diff --git a/libcxx/test/support/constexpr_char_traits.h b/libcxx/test/support/constexpr_char_traits.h --- a/libcxx/test/support/constexpr_char_traits.h +++ b/libcxx/test/support/constexpr_char_traits.h @@ -23,6 +23,8 @@ typedef std::streamoff off_type; typedef std::streampos pos_type; typedef std::mbstate_t state_type; + // The comparison_category is omitted so the class will have weak_ordering + // in C++20. This is intentionally. static TEST_CONSTEXPR_CXX14 void assign(char_type& __c1, const char_type& __c2) TEST_NOEXCEPT {__c1 = __c2;} diff --git a/libcxx/test/support/test_comparisons.h b/libcxx/test/support/test_comparisons.h --- a/libcxx/test/support/test_comparisons.h +++ b/libcxx/test/support/test_comparisons.h @@ -92,8 +92,7 @@ } template -void AssertComparisonsAreNoexcept() -{ +TEST_CONSTEXPR_CXX14 void AssertComparisonsAreNoexcept() { ASSERT_NOEXCEPT(std::declval() == std::declval()); ASSERT_NOEXCEPT(std::declval() != std::declval()); ASSERT_NOEXCEPT(std::declval() < std::declval()); @@ -103,8 +102,7 @@ } template -void AssertComparisonsReturnBool() -{ +TEST_CONSTEXPR_CXX14 void AssertComparisonsReturnBool() { ASSERT_SAME_TYPE(decltype(std::declval() == std::declval()), bool); ASSERT_SAME_TYPE(decltype(std::declval() != std::declval()), bool); ASSERT_SAME_TYPE(decltype(std::declval() < std::declval()), bool); @@ -113,7 +111,6 @@ ASSERT_SAME_TYPE(decltype(std::declval() >= std::declval()), bool); } - template void AssertComparisonsConvertibleToBool() { @@ -127,21 +124,26 @@ #if TEST_STD_VER > 17 template -void AssertOrderAreNoexcept() { - AssertComparisonsAreNoexcept(); - ASSERT_NOEXCEPT(std::declval() <=> std::declval()); +constexpr void AssertOrderAreNoexcept() { + AssertComparisonsAreNoexcept(); + ASSERT_NOEXCEPT(std::declval() <=> std::declval()); } template -void AssertOrderReturn() { - AssertComparisonsReturnBool(); - ASSERT_SAME_TYPE(decltype(std::declval() <=> std::declval()), Order); +constexpr void AssertOrderReturn() { + AssertComparisonsReturnBool(); + ASSERT_SAME_TYPE(decltype(std::declval() <=> std::declval()), Order); } template constexpr bool testOrder(const T& t1, const U& t2, Order order) { - return (t1 <=> t2 == order) && - testComparisons(t1, t2, order == Order::equal || order == Order::equivalent, order == Order::less); + bool equal = order == Order::equivalent; + if constexpr (std::same_as) + equal |= order == Order::equal; + + bool less = order == Order::less; + + return (t1 <=> t2 == order) && testComparisons(t1, t2, equal, less); } template