Index: libcxx/include/CMakeLists.txt =================================================================== --- libcxx/include/CMakeLists.txt +++ libcxx/include/CMakeLists.txt @@ -112,6 +112,7 @@ __chrono/system_clock.h __chrono/time_point.h __compare/common_comparison_category.h + __compare/comp_cat.h __compare/compare_three_way.h __compare/compare_three_way_result.h __compare/is_eq.h Index: libcxx/include/__compare/comp_cat.h =================================================================== --- /dev/null +++ libcxx/include/__compare/comp_cat.h @@ -0,0 +1,44 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___COMPARE_COMP_CAT_H +#define _LIBCPP___COMPARE_COMP_CAT_H + +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 + +// [string.view.comparison] +// Let R denote the type Traits::comparison_category if that qualified-id is +// valid and denotes a type; otherwise R is weak_ordering. + +template +struct __comp_cat { + using type = _Default; +}; + +template +struct __comp_cat<_Traits, _Default, typename __void_t::type> { + using type = typename _Traits::comparison_category; +}; + +template +using __comp_cat_t = typename __comp_cat<_Traits, _Default>::type; + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_COMP_CAT_H Index: libcxx/include/__compare/ordering.h =================================================================== --- libcxx/include/__compare/ordering.h +++ libcxx/include/__compare/ordering.h @@ -312,6 +312,13 @@ inline constexpr strong_ordering strong_ordering::equivalent(_OrdResult::__equiv); inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater); +// [cmp.categories.pre] +// The types partial_ordering, weak_ordering, and strong_ordering +// are collectively termed the comparison category types. + +template +inline constexpr bool __is_comparison_category_v = __one_of_v<_Tp, partial_ordering, weak_ordering, strong_ordering>; + #endif // _LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD Index: libcxx/include/__string =================================================================== --- libcxx/include/__string +++ libcxx/include/__string @@ -17,6 +17,7 @@ #include <__algorithm/find_end.h> #include <__algorithm/find_first_of.h> #include <__algorithm/min.h> +#include <__compare/ordering.h> #include <__config> #include <__functional/hash.h> // for __murmur2_or_cityhash #include <__iterator/iterator_traits.h> @@ -330,6 +331,9 @@ typedef streamoff off_type; typedef streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + typedef strong_ordering comparison_category; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -449,6 +453,9 @@ typedef streamoff off_type; typedef streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + typedef strong_ordering comparison_category; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -580,6 +587,9 @@ typedef streamoff off_type; typedef u8streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + typedef strong_ordering comparison_category; +#endif static inline constexpr void assign(char_type& __c1, const char_type& __c2) noexcept {__c1 = __c2;} @@ -691,6 +701,9 @@ typedef streamoff off_type; typedef u16streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + typedef strong_ordering comparison_category; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} @@ -813,6 +826,9 @@ typedef streamoff off_type; typedef u32streampos pos_type; typedef mbstate_t state_type; +#if _LIBCPP_STD_VER > 17 + typedef strong_ordering comparison_category; +#endif static inline _LIBCPP_CONSTEXPR_AFTER_CXX14 void assign(char_type& __c1, const char_type& __c2) _NOEXCEPT {__c1 = __c2;} Index: libcxx/include/module.modulemap =================================================================== --- libcxx/include/module.modulemap +++ libcxx/include/module.modulemap @@ -385,6 +385,7 @@ module __compare { module common_comparison_category { private header "__compare/common_comparison_category.h" } + module comp_cat { private header "__compare/comp_cat.h" } module compare_three_way { private header "__compare/compare_three_way.h" } module compare_three_way_result { private header "__compare/compare_three_way_result.h" } module is_eq { private header "__compare/is_eq.h" } Index: libcxx/include/string_view =================================================================== --- libcxx/include/string_view +++ libcxx/include/string_view @@ -26,6 +26,7 @@ inline constexpr bool ranges::enable_borrowed_range> = true; // C++20 // 7.9, basic_string_view non-member comparison functions + // We make these hidden friends, in lieu of "sufficient additional overloads" template constexpr bool operator==(basic_string_view x, basic_string_view y) noexcept; @@ -44,7 +45,6 @@ template constexpr bool operator>=(basic_string_view x, basic_string_view y) noexcept; - // see below, sufficient additional overloads of comparison functions // 7.10, Inserters and extractors template @@ -195,6 +195,8 @@ */ +#include <__compare/comp_cat.h> +#include <__compare/ordering.h> #include <__config> #include <__debug> #include <__ranges/concepts.h> @@ -702,6 +704,45 @@ { return find(__s) != npos; } #endif + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator==(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return (__lhs.size() == __rhs.size()) && (__lhs.compare(__rhs) == 0); + } + +#if _LIBCPP_STD_VER > 17 + friend constexpr _LIBCPP_HIDE_FROM_ABI __comp_cat_t<_Traits, weak_ordering> + operator<=>(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + using _Rp = __comp_cat_t<_Traits, weak_ordering>; + static_assert(__is_comparison_category_v<_Rp>, "Mandates: R denotes a comparison category type"); + return __lhs.compare(__rhs) <=> 0; + } +#else + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator!=(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return (__lhs.size() != __rhs.size()) || (__lhs.compare(__rhs) != 0); + } + + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator<(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return __lhs.compare(__rhs) < 0; + } + + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator<=(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return __lhs.compare(__rhs) <= 0; + } + + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator>(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return __lhs.compare(__rhs) > 0; + } + + friend _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI bool + operator>=(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + return __lhs.compare(__rhs) >= 0; + } +#endif // _LIBCPP_STD_VER > 17 + private: const value_type* __data; size_type __size; @@ -722,176 +763,11 @@ basic_string_view(_It, _End) -> basic_string_view>; #endif - #if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_RANGES) template basic_string_view(_Range) -> basic_string_view>; #endif -// [string.view.comparison] -// operator == -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator==(basic_string_view<_CharT, _Traits> __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) return false; - return __lhs.compare(__rhs) == 0; -} - -// The dummy default template parameters are used to work around a MSVC issue with mangling, see VSO-409326 for details. -// This applies to the other sufficient overloads below for the other comparison operators. -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator==(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) return false; - return __lhs.compare(__rhs) == 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator==(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) return false; - return __lhs.compare(__rhs) == 0; -} - - -// operator != -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator!=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) - return true; - return __lhs.compare(__rhs) != 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator!=(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) - return true; - return __lhs.compare(__rhs) != 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator!=(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - if ( __lhs.size() != __rhs.size()) - return true; - return __lhs.compare(__rhs) != 0; -} - - -// operator < -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) < 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) < 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) < 0; -} - - -// operator > -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator> (basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) > 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator>(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) > 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator>(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) > 0; -} - - -// operator <= -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) <= 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<=(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) <= 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator<=(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) <= 0; -} - - -// operator >= -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator>=(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) >= 0; -} - - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator>=(basic_string_view<_CharT, _Traits> __lhs, - typename common_type >::type __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) >= 0; -} - -template -_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY -bool operator>=(typename common_type >::type __lhs, - basic_string_view<_CharT, _Traits> __rhs) _NOEXCEPT -{ - return __lhs.compare(__rhs) >= 0; -} - - template basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, Index: libcxx/test/libcxx/diagnostics/detail.headers/compare/comp_cat.module.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/diagnostics/detail.headers/compare/comp_cat.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__compare/comp_cat.h'}} +#include <__compare/comp_cat.h> Index: libcxx/test/std/strings/string.view/string.view.comparison/spaceship.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/strings/string.view/string.view.comparison/spaceship.pass.cpp @@ -0,0 +1,146 @@ +//===----------------------------------------------------------------------===// +// +// 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 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 "test_macros.h" +#include "make_string.h" + +template +struct ConvertibleTo { + T t_; + constexpr explicit ConvertibleTo(T t) : t_(t) {} + constexpr operator T() const { + return t_; + } +}; + +template +constexpr bool test() +{ + typedef typename SV::value_type CharT; + typedef typename SV::traits_type Traits; + + // Test the behavior of the operator, both with and without implicit conversions. + SV v[] = { + SV(MAKE_CSTRING(CharT, "")), + SV(MAKE_CSTRING(CharT, "abc")), + SV(MAKE_CSTRING(CharT, "abcdef")), + SV(MAKE_CSTRING(CharT, "acb")), + }; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + // See http://eel.is/c++draft/string.view#tab:string.view.comparison.overloads + std::strong_ordering expected = (i <=> j); + assert((v[i] <=> v[j]) == expected); + assert((v[i].data() <=> v[j]) == expected); + assert((v[i] <=> v[j].data()) == expected); + assert((ConvertibleTo(v[i]) <=> v[j]) == expected); + assert((v[i] <=> ConvertibleTo(v[j])) == expected); + + ASSERT_SAME_TYPE(decltype(v[i] <=> v[j]), OrderingT); + ASSERT_SAME_TYPE(decltype(v[i].data() <=> v[j]), OrderingT); + ASSERT_SAME_TYPE(decltype(v[i] <=> v[j].data()), OrderingT); + ASSERT_SAME_TYPE(decltype(ConvertibleTo(v[i]) <=> v[j]), OrderingT); + ASSERT_SAME_TYPE(decltype(v[i] <=> ConvertibleTo(v[j])), OrderingT); + + if (!TEST_IS_CONSTANT_EVALUATED) { + // TODO FIXME: once P0980 "Making std::string constexpr" is implemented + assert((std::basic_string(v[i]) <=> v[j]) == expected); + assert((v[i] <=> std::basic_string(v[j])) == expected); + } + } + } + + // Test its behavior with embedded null bytes. + SV abc = SV(MAKE_CSTRING(CharT, "abc")); + SV abc0def = SV(MAKE_CSTRING(CharT, "abc\0def"), 7); + SV abcdef = SV(MAKE_CSTRING(CharT, "abcdef")); + assert((abc <=> abc0def) == std::strong_ordering::less); + assert((abc <=> abcdef) == std::strong_ordering::less); + assert((abc0def <=> abc) == std::strong_ordering::greater); + assert((abc0def <=> abcdef) == std::strong_ordering::less); + assert((abcdef <=> abc) == std::strong_ordering::greater); + assert((abcdef <=> abc0def) == std::strong_ordering::greater); + + assert((abc.data() <=> abc0def) == std::strong_ordering::less); + assert((abc0def <=> abc.data()) == std::strong_ordering::greater); + + if (!TEST_IS_CONSTANT_EVALUATED) { + // TODO FIXME: once P0980 "Making std::string constexpr" is implemented + assert((std::basic_string(abc) <=> abc0def) == std::strong_ordering::less); + assert((abc0def <=> std::basic_string(abc)) == std::strong_ordering::greater); + } + + return true; +} + +struct TraitsBase { + using char_type = char; + using int_type = int; + struct off_type {}; + struct pos_type {}; + struct state_type {}; + static bool eq(char c, char d) { return c == d; } + static bool lt(char c, char d) { return c < d; } + static int compare(const char *p, const char *q, size_t n) { return memcmp(p, q, n); } + static size_t length(const char *p) { return strlen(p); } + static const char *find(const char *p, size_t n, char c) { return (const char*)memchr(p, c, n); } + static char *move(char *s, const char *p, size_t n) { return (char*)memmove(s, p, n); } + static char *copy(char *s, const char *p, size_t n) { return (char*)memcpy(s, p, n); } + static void assign(char& r, char d) { r = d; } + static char *assign(char *s, size_t n, char c) { return (char*)memset(s, c, n); } + static int not_eof(int e) { return e == -1 ? 0 : e; } + static char to_char_type(int e) { return e; } + static int to_int_type(char c) { return c; } + static bool eq_int_type(int e, int f) { return e == f; } + static int eof() { return -1; } +}; + +struct TraitsWithCompCat : TraitsBase { + using comparison_category = std::partial_ordering; +}; + +struct TraitsWithoutCompCat : TraitsBase { + void comparison_category(); // does not denote a type +}; + +int main(int, char**) +{ + test(); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + test(); +#endif + test(); + test(); + test(); + static_assert(test()); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + static_assert(test()); +#endif + static_assert(test()); + static_assert(test()); + static_assert(test()); + + // Test that we forward the comparison category from the traits class. + test, std::partial_ordering>(); + test, std::weak_ordering>(); + + return 0; +} Index: libcxx/test/std/strings/string.view/string.view.comparison/spaceship_mandates.verify.cpp =================================================================== --- /dev/null +++ libcxx/test/std/strings/string.view/string.view.comparison/spaceship_mandates.verify.cpp @@ -0,0 +1,59 @@ +//===----------------------------------------------------------------------===// +// +// 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 R operator<=>(basic_string_view lhs, basic_string_view rhs); +// Mandates: R denotes a comparison category type. + +#include +#include +#include + +struct TraitsWithUserDefinedCompCat { + using char_type = char; + using int_type = int; + struct off_type {}; + struct pos_type {}; + struct state_type {}; + static bool eq(char c, char d) { return c == d; } + static bool lt(char c, char d) { return c < d; } + static int compare(const char *p, const char *q, size_t n) { return memcmp(p, q, n); } + static size_t length(const char *p) { return strlen(p); } + static const char *find(const char *p, size_t n, char c) { return (const char*)memchr(p, c, n); } + static char *move(char *s, const char *p, size_t n) { return (char*)memmove(s, p, n); } + static char *copy(char *s, const char *p, size_t n) { return (char*)memcpy(s, p, n); } + static void assign(char& r, char d) { r = d; } + static char *assign(char *s, size_t n, char c) { return (char*)memset(s, c, n); } + static int not_eof(int e) { return e == -1 ? 0 : e; } + static char to_char_type(int e) { return e; } + static int to_int_type(char c) { return c; } + static bool eq_int_type(int e, int f) { return e == f; } + static int eof() { return -1; } + + struct comparison_category { + comparison_category(std::strong_ordering o); + bool operator<(int) const; + }; +}; + +void test1() +{ + std::basic_string_view sv = "hello"; + (void)(sv == sv); // OK + (void)(sv != sv); // OK +} + +void test2() +{ + std::basic_string_view sv = "hello"; + (void)(sv < sv); // expected-error@string_view:* {{Mandates: R denotes a comparison category type}} +}