diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -103,6 +103,7 @@ __charconv/from_chars_result.h __charconv/to_chars_result.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 diff --git a/libcxx/include/__compare/comp_cat.h b/libcxx/include/__compare/comp_cat.h new file mode 100644 --- /dev/null +++ b/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 = _Dflt; +}; + +template +struct __comp_cat<_Traits, _Dflt, typename __void_t::type> { + using type = typename _Traits::comparison_category; +}; + +template +using __comp_cat_t = typename __comp_cat<_Traits, _Dflt>::type; + +#endif // _LIBCPP_STD_VER > 17 + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_COMP_CAT_H diff --git a/libcxx/include/__string b/libcxx/include/__string --- a/libcxx/include/__string +++ b/libcxx/include/__string @@ -18,6 +18,7 @@ #include <__algorithm/find_first_of.h> #include <__algorithm/find_end.h> #include <__algorithm/min.h> +#include <__compare/ordering.h> #include <__functional/hash.h> // for __murmur2_or_cityhash #include <__iterator/iterator_traits.h> #include // for EOF @@ -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;} @@ -436,6 +440,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;} @@ -567,6 +574,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;} @@ -678,6 +688,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;} @@ -800,6 +813,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;} diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -373,6 +373,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" } diff --git a/libcxx/include/string_view b/libcxx/include/string_view --- a/libcxx/include/string_view +++ b/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 @@ -191,6 +191,8 @@ */ +#include <__compare/comp_cat.h> +#include <__compare/ordering.h> #include <__config> #include <__debug> #include <__ranges/enable_borrowed_range.h> @@ -675,6 +677,43 @@ { 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 _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_HIDE_FROM_ABI __comp_cat_t<_Traits, weak_ordering> + operator<=>(basic_string_view __lhs, basic_string_view __rhs) _NOEXCEPT { + 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; @@ -695,168 +734,6 @@ basic_string_view(_It, _End) -> 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; -} - -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, diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/comp_cat.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/comp_cat.module.verify.cpp new file mode 100644 --- /dev/null +++ b/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> diff --git a/libcxx/test/std/strings/string.view/string.view.comparison/spaceship.pass.cpp b/libcxx/test/std/strings/string.view/string.view.comparison/spaceship.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/strings/string.view/string.view.comparison/spaceship.pass.cpp @@ -0,0 +1,130 @@ +//===----------------------------------------------------------------------===// +// +// 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 "constexpr_char_traits.h" +#include "make_string.h" + +template +struct ConvertibleTo { + T t_; + TEST_CONSTEXPR explicit ConvertibleTo(T t) : t_(t) {} + TEST_CONSTEXPR operator T() const { + return t_; + } +}; + +template +TEST_CONSTEXPR_CXX14 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 TraitsWithCompCat : std::char_traits { + using comparison_category = std::partial_ordering; +}; + +struct TraitsWithoutCompCat : std::char_traits { + 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>, std::weak_ordering>(); + static_assert(test>, std::weak_ordering>()); + + // Test that we forward the comparison category from the traits class. + test, std::partial_ordering>(); + test, std::weak_ordering>(); + static_assert(test, std::partial_ordering>()); + static_assert(test, std::weak_ordering>()); + + return 0; +}