diff --git a/stl/inc/compare b/stl/inc/compare index 389b5b58aa5..717e7b4b789 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -32,7 +32,7 @@ using _Compare_t = signed char; // These "pretty" enumerator names are safe since they reuse names of user-facing entities. enum class _Compare_eq : _Compare_t { equal = 0, equivalent = equal }; enum class _Compare_ord : _Compare_t { less = -1, greater = 1 }; -enum class _Compare_ncmp : _Compare_t { unordered = -127 }; +enum class _Compare_ncmp : _Compare_t { unordered = -128 }; // CLASS partial_ordering class partial_ordering { @@ -53,21 +53,7 @@ public: return _Val._Value == 0; } -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L - _NODISCARD friend constexpr bool operator==(const partial_ordering&, const partial_ordering&) noexcept = default; -#else // ^^^ supports <=> and P1185 / supports neither vvv - _NODISCARD friend constexpr bool operator!=(const partial_ordering _Val, _Literal_zero) noexcept { - return _Val._Value != 0; - } - - _NODISCARD friend constexpr bool operator==(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 == _Val._Value; - } - - _NODISCARD friend constexpr bool operator!=(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 != _Val._Value; - } -#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L + _NODISCARD friend constexpr bool operator==(partial_ordering, partial_ordering) noexcept = default; _NODISCARD friend constexpr bool operator<(const partial_ordering _Val, _Literal_zero) noexcept { return _Val._Value == static_cast<_Compare_t>(_Compare_ord::less); @@ -78,7 +64,10 @@ public: } _NODISCARD friend constexpr bool operator<=(const partial_ordering _Val, _Literal_zero) noexcept { - return _Val._Value <= 0 && _Val._Is_ordered(); + // The stored value is either less (0xff), equivalent (0x00), greater (0x01), or unordered (0x80). + // Subtracting from 0 produces either 0x01, 0x00, 0xff, or 0x80. The result is greater than or equal to 0 + // if and only if the initial value was less or equivalent, for which we want to return true. + return static_cast(0 - static_cast(_Val._Value)) >= 0; } _NODISCARD friend constexpr bool operator>=(const partial_ordering _Val, _Literal_zero) noexcept { @@ -86,36 +75,33 @@ public: } _NODISCARD friend constexpr bool operator<(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 < _Val._Value; + return _Val > 0; } _NODISCARD friend constexpr bool operator>(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 > _Val._Value && _Val._Is_ordered(); + return _Val < 0; } _NODISCARD friend constexpr bool operator<=(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 <= _Val._Value; + return _Val >= 0; } _NODISCARD friend constexpr bool operator>=(_Literal_zero, const partial_ordering _Val) noexcept { - return 0 >= _Val._Value && _Val._Is_ordered(); + return _Val <= 0; } -#ifdef __cpp_impl_three_way_comparison _NODISCARD friend constexpr partial_ordering operator<=>(const partial_ordering _Val, _Literal_zero) noexcept { return _Val; } _NODISCARD friend constexpr partial_ordering operator<=>(_Literal_zero, const partial_ordering _Val) noexcept { - return partial_ordering{static_cast<_Compare_ord>(-_Val._Value)}; + // The stored value is either less (0xff), equivalent (0x00), greater (0x01), or unordered (0x80). + // Subtracting from 0 produces either 0x01, 0x00, 0xff, or 0x80. Note that the effect is to + // exchange less for greater (and vice versa), while leaving equivalent and unordered unchanged. + return partial_ordering{static_cast<_Compare_ord>(0 - static_cast(_Val._Value))}; } -#endif // __cpp_impl_three_way_comparison private: - _NODISCARD constexpr bool _Is_ordered() const noexcept { - return _Value != static_cast<_Compare_t>(_Compare_ncmp::unordered); - } - _Compare_t _Value; }; @@ -144,21 +130,7 @@ public: return _Val._Value == 0; } -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L - _NODISCARD friend constexpr bool operator==(const weak_ordering&, const weak_ordering&) noexcept = default; -#else // ^^^ supports <=> and P1185 / supports neither vvv - _NODISCARD friend constexpr bool operator!=(const weak_ordering _Val, _Literal_zero) noexcept { - return _Val._Value != 0; - } - - _NODISCARD friend constexpr bool operator==(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 == _Val._Value; - } - - _NODISCARD friend constexpr bool operator!=(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 != _Val._Value; - } -#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L + _NODISCARD friend constexpr bool operator==(weak_ordering, weak_ordering) noexcept = default; _NODISCARD friend constexpr bool operator<(const weak_ordering _Val, _Literal_zero) noexcept { return _Val._Value < 0; @@ -177,22 +149,21 @@ public: } _NODISCARD friend constexpr bool operator<(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 < _Val._Value; + return _Val > 0; } _NODISCARD friend constexpr bool operator>(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 > _Val._Value; + return _Val < 0; } _NODISCARD friend constexpr bool operator<=(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 <= _Val._Value; + return _Val >= 0; } _NODISCARD friend constexpr bool operator>=(_Literal_zero, const weak_ordering _Val) noexcept { - return 0 >= _Val._Value; + return _Val <= 0; } -#ifdef __cpp_impl_three_way_comparison _NODISCARD friend constexpr weak_ordering operator<=>(const weak_ordering _Val, _Literal_zero) noexcept { return _Val; } @@ -200,7 +171,6 @@ public: _NODISCARD friend constexpr weak_ordering operator<=>(_Literal_zero, const weak_ordering _Val) noexcept { return weak_ordering{static_cast<_Compare_ord>(-_Val._Value)}; } -#endif // __cpp_impl_three_way_comparison private: _Compare_t _Value; @@ -235,21 +205,7 @@ public: return _Val._Value == 0; } -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L - _NODISCARD friend constexpr bool operator==(const strong_ordering&, const strong_ordering&) noexcept = default; -#else // ^^^ supports <=> and P1185 / supports neither vvv - _NODISCARD friend constexpr bool operator!=(const strong_ordering _Val, _Literal_zero) noexcept { - return _Val._Value != 0; - } - - _NODISCARD friend constexpr bool operator==(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 == _Val._Value; - } - - _NODISCARD friend constexpr bool operator!=(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 != _Val._Value; - } -#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L + _NODISCARD friend constexpr bool operator==(strong_ordering, strong_ordering) noexcept = default; _NODISCARD friend constexpr bool operator<(const strong_ordering _Val, _Literal_zero) noexcept { return _Val._Value < 0; @@ -268,22 +224,21 @@ public: } _NODISCARD friend constexpr bool operator<(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 < _Val._Value; + return _Val > 0; } _NODISCARD friend constexpr bool operator>(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 > _Val._Value; + return _Val < 0; } _NODISCARD friend constexpr bool operator<=(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 <= _Val._Value; + return _Val >= 0; } _NODISCARD friend constexpr bool operator>=(_Literal_zero, const strong_ordering _Val) noexcept { - return 0 >= _Val._Value; + return _Val <= 0; } -#ifdef __cpp_impl_three_way_comparison _NODISCARD friend constexpr strong_ordering operator<=>(const strong_ordering _Val, _Literal_zero) noexcept { return _Val; } @@ -291,7 +246,6 @@ public: _NODISCARD friend constexpr strong_ordering operator<=>(_Literal_zero, const strong_ordering _Val) noexcept { return strong_ordering{static_cast<_Compare_ord>(-_Val._Value)}; } -#endif // __cpp_impl_three_way_comparison private: _Compare_t _Value; @@ -364,7 +318,7 @@ struct common_comparison_category { using type = common_comparison_category_t<_Types...>; }; -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts // clang-format off template concept _Compares_as = same_as, _Cat>; @@ -409,7 +363,7 @@ struct compare_three_way { using is_transparent = int; }; // clang-format on -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts // Other components not yet implemented _STD_END diff --git a/stl/inc/span b/stl/inc/span index d61d8e375fc..8565955bfc5 100644 --- a/stl/inc/span +++ b/stl/inc/span @@ -12,13 +12,11 @@ #if !_HAS_CXX20 #pragma message("The contents of are available only with C++20 or later.") #else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv +#include #include #include #include -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L -#include -#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -153,7 +151,6 @@ struct _Span_iterator { return _Myptr == _Right._Myptr; } -#if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L _NODISCARD constexpr strong_ordering operator<=>(const _Span_iterator& _Right) const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY( @@ -161,31 +158,6 @@ struct _Span_iterator { #endif // _ITERATOR_DEBUG_LEVEL >= 1 return _Myptr <=> _Right._Myptr; } -#else // ^^^ use spaceship / no spaceship vvv - _NODISCARD constexpr bool operator!=(const _Span_iterator& _Right) const noexcept { - return !(*this == _Right); - } - - _NODISCARD constexpr bool operator<(const _Span_iterator& _Right) const noexcept { -#if _ITERATOR_DEBUG_LEVEL >= 1 - _STL_VERIFY( - _Mybegin == _Right._Mybegin && _Myend == _Right._Myend, "cannot compare incompatible span iterators"); -#endif // _ITERATOR_DEBUG_LEVEL >= 1 - return _Myptr < _Right._Myptr; - } - - _NODISCARD constexpr bool operator>(const _Span_iterator& _Right) const noexcept { - return _Right < *this; - } - - _NODISCARD constexpr bool operator<=(const _Span_iterator& _Right) const noexcept { - return !(_Right < *this); - } - - _NODISCARD constexpr bool operator>=(const _Span_iterator& _Right) const noexcept { - return !(*this < _Right); - } -#endif // defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201902L #if _ITERATOR_DEBUG_LEVEL >= 1 friend constexpr void _Verify_range(const _Span_iterator& _First, const _Span_iterator& _Last) noexcept { diff --git a/stl/inc/xutility b/stl/inc/xutility index 0728eaacc6c..f80ed07fa86 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -2105,13 +2105,13 @@ _NODISCARD _CONSTEXPR17 bool operator>=(const reverse_iterator<_BidIt1>& _Left, #endif // __cpp_lib_concepts { return _Left._Get_current() <= _Right._Get_current(); } -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts template _BidIt2> _NODISCARD constexpr compare_three_way_result_t<_BidIt1, _BidIt2> operator<=>( const reverse_iterator<_BidIt1>& _Left, const reverse_iterator<_BidIt2>& _Right) { return _Right._Get_current() <=> _Left._Get_current(); } -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts template _NODISCARD _CONSTEXPR17 auto operator-(const reverse_iterator<_BidIt1>& _Left, const reverse_iterator<_BidIt2>& _Right) @@ -4103,12 +4103,12 @@ _NODISCARD _CONSTEXPR17 bool operator==(const move_iterator<_Iter1>& _Left, cons #endif // __cpp_lib_concepts { return _Left.base() == _Right.base(); } -#if !_HAS_CXX20 || __cpp_impl_three_way_comparison < 201902L +#if !_HAS_CXX20 template _NODISCARD _CONSTEXPR17 bool operator!=(const move_iterator<_Iter1>& _Left, const move_iterator<_Iter2>& _Right) { return !(_Left == _Right); } -#endif // !_HAS_CXX20 || __cpp_impl_three_way_comparison < 201902L +#endif // !_HAS_CXX20 template _NODISCARD _CONSTEXPR17 bool operator<(const move_iterator<_Iter1>& _Left, const move_iterator<_Iter2>& _Right) @@ -4148,13 +4148,13 @@ _NODISCARD _CONSTEXPR17 bool operator>=(const move_iterator<_Iter1>& _Left, cons #endif // __cpp_lib_concepts { return !(_Left < _Right); } -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts template _Iter2> _NODISCARD constexpr compare_three_way_result_t<_Iter1, _Iter2> operator<=>( const move_iterator<_Iter1>& _Left, const move_iterator<_Iter2>& _Right) { return _Left.base() <=> _Right.base(); } -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts template _NODISCARD _CONSTEXPR17 auto operator-(const move_iterator<_Iter1>& _Left, const move_iterator<_Iter2>& _Right) @@ -4195,21 +4195,6 @@ namespace _Unreachable_sentinel_detail { _NODISCARD friend constexpr bool operator==(const unreachable_sentinel_t&, const _Winc&) noexcept { return false; } -#if !defined(__cpp_impl_three_way_comparison) || __cpp_impl_three_way_comparison < 201902L - template - _NODISCARD friend constexpr bool operator==(const _Winc&, const unreachable_sentinel_t&) noexcept { - return false; - } - - template - _NODISCARD friend constexpr bool operator!=(const unreachable_sentinel_t&, const _Winc&) noexcept { - return true; - } - template - _NODISCARD friend constexpr bool operator!=(const _Winc&, const unreachable_sentinel_t&) noexcept { - return true; - } -#endif // !defined(__cpp_impl_three_way_comparison) || __cpp_impl_three_way_comparison < 201902L }; } // namespace _Unreachable_sentinel_detail struct unreachable_sentinel_t : _Unreachable_sentinel_detail::_Base {}; // TRANSITION, /permissive- @@ -5236,7 +5221,7 @@ _NODISCARD bool lexicographical_compare( } #endif // _HAS_CXX17 -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts // FUNCTION TEMPLATE lexicographical_compare_three_way template _NODISCARD constexpr auto lexicographical_compare_three_way( @@ -5294,7 +5279,7 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2) { return _STD lexicographical_compare_three_way(_First1, _Last1, _First2, _Last2, compare_three_way{}); } -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts // FUNCTION TEMPLATE find template diff --git a/tests/std/tests/P0768R1_spaceship_operator/test.cpp b/tests/std/tests/P0768R1_spaceship_operator/test.cpp index 09a23f15d6b..33a1218f7d6 100644 --- a/tests/std/tests/P0768R1_spaceship_operator/test.cpp +++ b/tests/std/tests/P0768R1_spaceship_operator/test.cpp @@ -7,64 +7,89 @@ #include #include -enum class comp { equal, nonequal, less, greater, unordered }; +enum class comp { equal, less, greater, unordered }; template -constexpr bool test_ord(T val) { +constexpr bool test_order(const T val) { + // Validate that val is ordered relative to literal zero according to Z assert((val == 0) == (Z == comp::equal)); assert((0 == val) == (Z == comp::equal)); + assert((val != 0) == (Z != comp::equal)); assert((0 != val) == (Z != comp::equal)); -#ifdef __cpp_impl_three_way_comparison - assert(((val <=> 0) == 0) == (Z == comp::equal)); - assert(((0 <=> val) == 0) == (Z == comp::equal)); - -#if __cpp_impl_three_way_comparison >= 201907L - assert(val == val); - assert(!(val != val)); -#endif // __cpp_impl_three_way_comparison >= 201907L -#endif // __cpp_impl_three_way_comparison - assert(std::is_eq(val) == (Z == comp::equal)); - assert(std::is_neq(val) == (Z != comp::equal)); assert((val < 0) == (Z == comp::less)); assert((0 > val) == (Z == comp::less)); + assert((val > 0) == (Z == comp::greater)); assert((0 < val) == (Z == comp::greater)); + assert((val <= 0) == (Z != comp::greater && Z != comp::unordered)); assert((0 >= val) == (Z != comp::greater && Z != comp::unordered)); + assert((val >= 0) == (Z != comp::less && Z != comp::unordered)); assert((0 <= val) == (Z != comp::less && Z != comp::unordered)); -#ifdef __cpp_impl_three_way_comparison - assert(((val <=> 0) < 0) == (Z == comp::less)); - assert(((0 <=> val) < 0) == (Z == comp::greater)); -#if __cpp_impl_three_way_comparison >= 201907L - assert(val == val); - assert(!(val != val)); -#endif // __cpp_impl_three_way_comparison >= 201907L -#endif // __cpp_impl_three_way_comparison + assert(std::is_eq(val) == (Z == comp::equal)); + assert(std::is_neq(val) == (Z != comp::equal)); assert(std::is_lt(val) == (Z == comp::less)); assert(std::is_lteq(val) == (Z != comp::greater && Z != comp::unordered)); assert(std::is_gt(val) == (Z == comp::greater)); assert(std::is_gteq(val) == (Z != comp::less && Z != comp::unordered)); + // Validate that equality is reflexive for comparison category types + assert(val == val); + assert(!(val != val)); + return true; } -static_assert(test_ord(std::partial_ordering::equivalent)); -static_assert(test_ord(std::partial_ordering::less)); -static_assert(test_ord(std::partial_ordering::greater)); -static_assert(test_ord(std::partial_ordering::unordered)); +constexpr bool test_orderings() { + assert(test_order(std::partial_ordering::equivalent)); + assert(test_order(std::partial_ordering::less)); + assert(test_order(std::partial_ordering::greater)); + assert(test_order(std::partial_ordering::unordered)); + + assert(test_order(std::weak_ordering::equivalent)); + assert(test_order(std::weak_ordering::less)); + assert(test_order(std::weak_ordering::greater)); + + assert(test_order(std::strong_ordering::equal)); + assert(test_order(std::strong_ordering::equivalent)); + assert(test_order(std::strong_ordering::less)); + assert(test_order(std::strong_ordering::greater)); -static_assert(test_ord(std::weak_ordering::equivalent)); -static_assert(test_ord(std::weak_ordering::less)); -static_assert(test_ord(std::weak_ordering::greater)); + return true; +} + +constexpr bool test_spaceships() { + // Exhaustively validate x <=> 0 and 0 <=> x for all values of each comparison category type. + // Guards against regression of GH-1050: "0 <=> partial_ordering::unordered returns invalid value". + assert(std::partial_ordering::less <=> 0 == std::partial_ordering::less); + assert(0 <=> std::partial_ordering::less == std::partial_ordering::greater); + assert(std::partial_ordering::equivalent <=> 0 == std::partial_ordering::equivalent); + assert(0 <=> std::partial_ordering::equivalent == std::partial_ordering::equivalent); + assert(std::partial_ordering::greater <=> 0 == std::partial_ordering::greater); + assert(0 <=> std::partial_ordering::greater == std::partial_ordering::less); + assert(std::partial_ordering::unordered <=> 0 == std::partial_ordering::unordered); + assert(0 <=> std::partial_ordering::unordered == std::partial_ordering::unordered); + + assert(std::weak_ordering::less <=> 0 == std::weak_ordering::less); + assert(0 <=> std::weak_ordering::less == std::weak_ordering::greater); + assert(std::weak_ordering::equivalent <=> 0 == std::weak_ordering::equivalent); + assert(0 <=> std::weak_ordering::equivalent == std::weak_ordering::equivalent); + assert(std::weak_ordering::greater <=> 0 == std::weak_ordering::greater); + assert(0 <=> std::weak_ordering::greater == std::weak_ordering::less); + + assert(std::strong_ordering::less <=> 0 == std::strong_ordering::less); + assert(0 <=> std::strong_ordering::less == std::strong_ordering::greater); + assert(std::strong_ordering::equal <=> 0 == std::strong_ordering::equal); + assert(0 <=> std::strong_ordering::equal == std::strong_ordering::equal); + assert(std::strong_ordering::greater <=> 0 == std::strong_ordering::greater); + assert(0 <=> std::strong_ordering::greater == std::strong_ordering::less); -static_assert(test_ord(std::strong_ordering::equal)); -static_assert(test_ord(std::strong_ordering::equivalent)); -static_assert(test_ord(std::strong_ordering::less)); -static_assert(test_ord(std::strong_ordering::greater)); + return true; +} template constexpr bool test_common_cc = std::is_same_v, Expected>; @@ -121,7 +146,7 @@ static_assert(test_common_type()); static_assert(test_common_type()); static_assert(test_common_type()); -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts constexpr auto my_cmp_three_way = [](const auto& left, const auto& right) { return left <=> right; }; template @@ -187,29 +212,21 @@ void test_algorithm() { assert((test_algorithm2())); assert((test_algorithm2())); } -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts int main() { - test_ord(std::partial_ordering::equivalent); - test_ord(std::partial_ordering::less); - test_ord(std::partial_ordering::greater); - test_ord(std::partial_ordering::unordered); - - test_ord(std::weak_ordering::equivalent); - test_ord(std::weak_ordering::less); - test_ord(std::weak_ordering::greater); + static_assert(test_orderings()); + test_orderings(); - test_ord(std::strong_ordering::equal); - test_ord(std::strong_ordering::equivalent); - test_ord(std::strong_ordering::less); - test_ord(std::strong_ordering::greater); + static_assert(test_spaceships()); + test_spaceships(); -#if defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#ifdef __cpp_lib_concepts test_algorithm(); test_algorithm(); test_algorithm(); test_algorithm(); test_algorithm(); test_algorithm(); -#endif // defined(__cpp_impl_three_way_comparison) && defined(__cpp_lib_concepts) +#endif // __cpp_lib_concepts }