diff --git a/stl/inc/regex b/stl/inc/regex index 2cfb6d8de33..048e2e8b351 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -592,6 +592,18 @@ bool _Is_word(_Elem _Ch) { return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast(_UCh)); } +#if _HAS_CXX20 +template +struct _Get_member_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_member_comparison_category<_Ty, void_t> { + using type = typename _Ty::comparison_category; +}; +#endif // _HAS_CXX20 + // CLASS TEMPLATE sub_match template class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group @@ -606,6 +618,10 @@ public: // Note that _Size_type should always be std::size_t using _Size_type = typename string_type::size_type; +#if _HAS_CXX20 + using _Comparison_category = typename _Get_member_comparison_category<_Traits>::type; +#endif // _HAS_CXX20 + constexpr sub_match() : _Mybase(), matched(false) {} bool matched; @@ -708,6 +724,41 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI return _Left._Match_equal(_Right); } +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 +// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. +// In C++20 mode the only required comparison operators should be == and <=>. +template +_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) >= 0; +} +#endif // TRANSITION, VSO-900973 +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); @@ -732,9 +783,85 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND NTBS template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return _Left._Match_equal(_Right); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 +// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. +// In C++20 mode the only required comparison operators should be == and <=>. +template +_NODISCARD auto operator<=>(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); +} + +template +_NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_Left); +} + +template +_NODISCARD bool operator!=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) >= 0; +} + +template +_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return (_Left <=> _Right) >= 0; +} +#endif // TRANSTITION, VSO-900973 +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_Left); } @@ -764,11 +891,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return _Left._Match_equal(_Right); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left == _Right); @@ -793,9 +915,86 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND ELEMENT template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return _Left._Match_equal(_STD addressof(_Right), 1); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return static_cast::_Comparison_category>( + _Left._Compare(_STD addressof(_Right), 1) <=> 0); +} + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 +// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. +// In C++20 mode the only required comparison operators should be == and <=>. +template +_NODISCARD auto operator<=>(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); +} + +template +_NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_STD addressof(_Left), 1); +} + +template +_NODISCARD bool operator!=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) >= 0; +} + +template +_NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return (_Left <=> _Right) >= 0; +} +#endif // TRANSITION, VSO-900973 +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_STD addressof(_Left), 1); } @@ -825,11 +1024,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return _Left._Match_equal(_STD addressof(_Right), 1); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left == _Right); @@ -854,6 +1048,7 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND string template @@ -862,6 +1057,58 @@ _NODISCARD bool operator==( return _Left._Match_equal(_Right.data(), _Right.size()); } +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-900973 +// The compiler incorrectly performs overload resolution by preferring implicit conversions over rewrites. +// In C++20 mode the only required comparison operators should be == and <=>. +template +_NODISCARD auto operator<=>( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(0 <=> (_Right <=> _Left)); +} + +template +_NODISCARD bool operator==( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_Left.data(), _Left.size()); +} + +template +_NODISCARD bool operator!=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) < 0; +} + +template +_NODISCARD bool operator>( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) > 0; +} + +template +_NODISCARD bool operator<=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) <= 0; +} + +template +_NODISCARD bool operator>=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return (_Left <=> _Right) >= 0; +} + template _NODISCARD bool operator!=( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { @@ -871,27 +1118,28 @@ _NODISCARD bool operator!=( template _NODISCARD bool operator<( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return _Left._Less(_Right.data(), _Right.size()); + return (_Left <=> _Right) < 0; } template _NODISCARD bool operator>( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return _Right < _Left; + return (_Left <=> _Right) > 0; } template _NODISCARD bool operator<=( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return !(_Right < _Left); + return (_Left <=> _Right) <= 0; } template _NODISCARD bool operator>=( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return !(_Left < _Right); + return (_Left <=> _Right) >= 0; } - +#endif // TRANSITION, VSO-900973 +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==( const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { @@ -928,6 +1176,37 @@ _NODISCARD bool operator>=( return !(_Left < _Right); } +template +_NODISCARD bool operator!=( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return !(_Left == _Right); +} + +template +_NODISCARD bool operator<( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return _Left._Less(_Right.data(), _Right.size()); +} + +template +_NODISCARD bool operator>( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return _Right < _Left; +} + +template +_NODISCARD bool operator<=( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return !(_Right < _Left); +} + +template +_NODISCARD bool operator>=( + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return !(_Left < _Right); +} +#endif // !_HAS_CXX20 + // INSERT sub_match IN STREAM template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, const sub_match<_BidIt>& _Match) { @@ -1134,10 +1413,12 @@ _NODISCARD bool operator==(const match_results<_BidIt, _Alloc>& _Left, const mat } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const match_results<_BidIt, _Alloc>& _Left, const match_results<_BidIt, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // NFA PROPERTIES const unsigned int _BRE_MAX_GRP = 9U; @@ -2428,9 +2709,11 @@ public: && _MyVal._At(0) == _Right._MyVal._At(0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -2611,9 +2894,11 @@ public: return *_Res == *_Right._Res && _Pos == _Right._Pos && _Subs == _Right._Subs; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_token_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 5a215eac23b..7c76cee6b79 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,9 @@ #include #include +template +using SpaceshipType = decltype(std::declval() <=> std::declval()); + using PartiallyOrdered = double; struct WeaklyOrdered { @@ -54,29 +58,103 @@ struct SynthOrdered { } }; -template -inline constexpr bool is_pair = false; -template -inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented +struct OrderedChar { + OrderedChar() = default; + OrderedChar(const char other) : c(other) {} -template -void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { + OrderedChar& operator=(const char& other) { + c = other; + return *this; + } + + auto operator<=>(const OrderedChar&) const = default; + + operator char() const { + return c; + } + + char c; +}; + +struct WeaklyOrderedChar : OrderedChar {}; +struct WeaklyOrderedByOmissionChar : OrderedChar {}; +struct PartiallyOrderedChar : OrderedChar {}; + +namespace std { + template <> + struct char_traits : char_traits { + using char_type = OrderedChar; + + static int compare(const char_type* first1, const char_type* first2, size_t count) { + for (; 0 < count; --count, ++first1, ++first2) { + if (*first1 != *first2) { + return *first1 < *first2 ? -1 : +1; + } + } + + return 0; + } + + static bool eq(const char_type l, const char_type r) { + return l.c == r.c; + } + }; + + template <> + struct char_traits : char_traits { + using char_type = WeaklyOrderedChar; + using comparison_category = weak_ordering; + }; + + template <> + struct char_traits : char_traits { + using char_type = WeaklyOrderedByOmissionChar; + + private: + using comparison_category = strong_ordering; + }; + + template <> + struct char_traits : char_traits { + using char_type = PartiallyOrderedChar; + using comparison_category = partial_ordering; + }; +} // namespace std + +template +void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { + assert(smaller == smaller_equal); + assert(smaller_equal == smaller); + assert(smaller != larger); + assert(larger != smaller); assert(smaller < larger); - assert(smaller <= larger); + assert(!(larger < smaller)); assert(larger > smaller); + assert(!(smaller > larger)); + assert(smaller <= larger); + assert(!(larger <= smaller)); assert(larger >= smaller); - assert(smaller == smaller_equal); - assert(smaller != larger); + assert(!(smaller >= larger)); assert((smaller <=> larger) < 0); assert((larger <=> smaller) > 0); assert((smaller <=> smaller_equal) == 0); + static_assert(std::is_same_v larger), ReturnType>); +} + +template +inline constexpr bool is_pair = false; +template +inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented + +template +void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { using Elem = typename Container::value_type; if constexpr (is_pair // TRANSITION, std::pair spaceship not yet implemented || std::is_same_v) { - static_assert(std::is_same_v larger), std::weak_ordering>); + spaceship_test(smaller, smaller_equal, larger); } else { - static_assert(std::is_same_v larger), std::strong_ordering>); + spaceship_test(smaller, smaller_equal, larger); } } @@ -292,10 +370,49 @@ void ordering_test_cases() { std::stack b{std::deque{10, 20, 40}}; ordered_containers_test(a, a, b); } -} + { // sub_match + const std::string s1{"cats"}; + const std::string s2{"meow"}; + const std::regex all(".*"); + const std::regex each("."); + std::smatch m1; + std::smatch m2; + std::smatch m3; + std::smatch m4; -template -using SpaceshipType = decltype(std::declval() <=> std::declval()); + std::regex_match(s1, m1, all); + std::regex_match(s2, m2, all); + std::regex_search(s1, m3, each); + std::regex_search(s2, m4, each); + + std::ssub_match sm1 = m1[0]; + std::ssub_match sm1_equal = m1[0]; + std::ssub_match sm2 = m2[0]; + std::ssub_match sm3 = m3[0]; + std::ssub_match sm4 = m4[0]; + + // TRANSITION, std::char_traits doesn't define comparison_category + spaceship_test(sm1, sm1_equal, sm2); + spaceship_test(sm1, s1, s2); + spaceship_test(sm1, s1.c_str(), s2.c_str()); + spaceship_test(sm3, 'c', 'm'); + spaceship_test(s1, sm1, sm2); + spaceship_test(s1.c_str(), sm1, sm2); + spaceship_test('c', sm3, sm4); + + using StronglyOrderedMatch = std::ssub_match; + using WeaklyOrderedMatch = std::sub_match::const_iterator>; + using WeaklyOrderdByOmissionMatch = + std::sub_match::const_iterator>; + using PartiallyOrderedMatch = std::sub_match::const_iterator>; + + // TRANSITION, std::char_traits doesn't define comparison_category + static_assert(std::is_same_v, std::weak_ordering>); + static_assert(std::is_same_v, std::weak_ordering>); + static_assert(std::is_same_v, std::weak_ordering>); + static_assert(std::is_same_v, std::partial_ordering>); + } +} template void test_element_ordering() {