From 889b8c8dba89a2585b4b95338daacc96e19d176d Mon Sep 17 00:00:00 2001 From: ahanamuk Date: Tue, 28 Jul 2020 05:16:47 -0400 Subject: [PATCH 01/18] feature/spaceship: Clause 22: Containers (#1046) Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/array | 25 ++ stl/inc/deque | 10 + stl/inc/forward_list | 21 +- stl/inc/list | 16 +- stl/inc/map | 22 ++ stl/inc/queue | 13 + stl/inc/set | 22 ++ stl/inc/stack | 13 + stl/inc/unordered_map | 4 + stl/inc/unordered_set | 4 + stl/inc/utility | 31 ++ stl/inc/vector | 167 +++++++-- tests/std/test.lst | 3 +- .../env.lst | 0 .../test.cpp | 162 +++++++++ .../test.cpp | 82 ----- tests/std/tests/P1614R2_spaceship/env.lst | 4 + tests/std/tests/P1614R2_spaceship/test.cpp | 330 ++++++++++++++++++ 18 files changed, 809 insertions(+), 120 deletions(-) rename tests/std/tests/{Dev11_0135139_vector_bool_equality_perf => Dev11_0135139_vector_bool_comparisons}/env.lst (100%) create mode 100644 tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp delete mode 100644 tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp create mode 100644 tests/std/tests/P1614R2_spaceship/env.lst create mode 100644 tests/std/tests/P1614R2_spaceship/test.cpp diff --git a/stl/inc/array b/stl/inc/array index d51bc8f54b9..23c892f3a15 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -775,17 +775,41 @@ _CONSTEXPR20 void swap(array<_Ty, _Size>& _Left, array<_Ty, _Size>& _Right) noex template _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD equal(_Left.begin(), _Left.end(), _Right.begin()); +#else // ^^^ workaround / no workaround vvv + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); +#endif // ^^^ no workaround ^^^ } +#if !_HAS_CXX20 template _NODISCARD _CONSTEXPR20 bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr _Synth_three_way_result<_Ty> operator<=>( + const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 + return _STD lexicographical_compare_three_way( + _Left.begin(), _Left.end(), _Right.begin(), _Right.end(), _Synth_three_way{}); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +#endif // ^^^ no workaround ^^^ +} +#else // __cpp_lib_concepts template _NODISCARD _CONSTEXPR20 bool operator<(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#endif // ^^^ no workaround ^^^ } template @@ -802,6 +826,7 @@ template _NODISCARD _CONSTEXPR20 bool operator>=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 // FUNCTION TEMPLATE to_array diff --git a/stl/inc/deque b/stl/inc/deque index 73a74b7a717..08f1f6fb47d 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -1581,11 +1581,20 @@ _NODISCARD bool operator==(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Al && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -1606,6 +1615,7 @@ template _NODISCARD bool operator>=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 24227c66c43..c55199de311 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -814,6 +814,10 @@ public: return {}; } + _Unchecked_const_iterator _Unchecked_end_iter() const noexcept { + return _Unchecked_const_iterator(nullptr, nullptr); + } + iterator _Make_iter(_Nodeptr _Where) const noexcept { return iterator(_Where, _STD addressof(_Mypair._Myval2)); } @@ -1518,17 +1522,29 @@ void swap(forward_list<_Ty, _Alloc>& _Left, forward_list<_Ty, _Alloc>& _Right) n template _NODISCARD bool operator==(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD equal(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } template @@ -1545,6 +1561,7 @@ template _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/list b/stl/inc/list index a223036db12..d6877e32ebd 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -1807,17 +1807,28 @@ void swap(list<_Ty, _Alloc>& _Left, list<_Ty, _Alloc>& _Right) noexcept /* stren template _NODISCARD bool operator==(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() && _STD equal(_Left.begin(), _Left.end(), _Right.begin()); + return _Left.size() == _Right.size() + && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); } template @@ -1834,6 +1845,7 @@ template _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts #if _HAS_CXX20 template diff --git a/stl/inc/map b/stl/inc/map index f33f3df84f3..5deb485deeb 100644 --- a/stl/inc/map +++ b/stl/inc/map @@ -369,11 +369,21 @@ _NODISCARD bool operator==(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_ && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -394,6 +404,7 @@ template _NODISCARD bool operator>=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(map<_Kty, _Ty, _Pr, _Alloc>& _Left, map<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( @@ -557,12 +568,22 @@ _NODISCARD bool operator==( && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { @@ -587,6 +608,7 @@ _NODISCARD bool operator>=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/queue b/stl/inc/queue index 8954046397d..8f7ca511ec5 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -54,6 +54,14 @@ _NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class queue { public: @@ -148,6 +156,11 @@ public: friend bool operator> <>(const queue&, const queue&); friend bool operator<= <>(const queue&, const queue&); friend bool operator>= <>(const queue&, const queue&); +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const queue<_Ty2, _Container2>&, const queue<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts // clang-format on protected: diff --git a/stl/inc/set b/stl/inc/set index a049d279f22..e80f3838e48 100644 --- a/stl/inc/set +++ b/stl/inc/set @@ -180,11 +180,21 @@ _NODISCARD bool operator==(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -205,6 +215,7 @@ template _NODISCARD bool operator>=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(set<_Kty, _Pr, _Alloc>& _Left, set<_Kty, _Pr, _Alloc>& _Right) noexcept(noexcept(_Left.swap(_Right))) { @@ -352,11 +363,21 @@ _NODISCARD bool operator==(const multiset<_Kty, _Pr, _Alloc>& _Left, const multi && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -377,6 +398,7 @@ template _NODISCARD bool operator>=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts template void swap(multiset<_Kty, _Pr, _Alloc>& _Left, multiset<_Kty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/stack b/stl/inc/stack index 55889064e87..f347ec0b57b 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -52,6 +52,14 @@ _NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class stack { public: @@ -138,6 +146,11 @@ public: friend bool operator> <>(const stack&, const stack&); friend bool operator<= <>(const stack&, const stack&); friend bool operator>= <>(const stack&, const stack&); +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const stack<_Ty2, _Container2>&, const stack<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts // clang-format on protected: diff --git a/stl/inc/unordered_map b/stl/inc/unordered_map index 0a564aaba2e..c5633ff84a4 100644 --- a/stl/inc/unordered_map +++ b/stl/inc/unordered_map @@ -468,11 +468,13 @@ _NODISCARD bool operator==(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multimap template , class _Keyeq = equal_to<_Kty>, @@ -758,11 +760,13 @@ _NODISCARD bool operator==(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/unordered_set b/stl/inc/unordered_set index 1f32080e230..2a7125567b6 100644 --- a/stl/inc/unordered_set +++ b/stl/inc/unordered_set @@ -322,11 +322,13 @@ _NODISCARD bool operator==(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _ return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multiset template , class _Keyeq = equal_to<_Kty>, class _Alloc = allocator<_Kty>> @@ -584,11 +586,13 @@ _NODISCARD bool operator==(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/utility b/stl/inc/utility index f40b42623e6..73d0c260f5d 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -695,6 +695,37 @@ _NODISCARD constexpr bool cmp_greater_equal(const _Ty1 _Left, const _Ty2 _Right) return !_STD cmp_less(_Left, _Right); } +#ifdef __cpp_lib_concepts +// STRUCT _Synth_three_way +struct _Synth_three_way { + // clang-format off + template + _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const + requires requires { + { _Left < _Right } -> _Boolean_testable; + { _Right < _Left } -> _Boolean_testable; + } + // clang-format on + { + if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { + return _Left <=> _Right; + } else { + if (_Left < _Right) { + return weak_ordering::less; + } else if (_Right < _Left) { + return weak_ordering::greater; + } else { + return weak_ordering::equivalent; + } + } + } +}; + +// ALIAS TEMPLATE _Synth_three_way_result +template +using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE in_range template _NODISCARD constexpr _Ty _Min_limit() noexcept { // same as (numeric_limits<_Ty>::min)(), less throughput cost diff --git a/stl/inc/vector b/stl/inc/vector index 17902275c58..4f784ca7eb2 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1755,26 +1755,134 @@ template >, vector(_Iter, _Iter, _Alloc = _Alloc()) -> vector<_Iter_value_t<_Iter>, _Alloc>; #endif // _HAS_CXX17 -template -void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { - _Left.swap(_Right); -} +template +class vector; + +using _Vbase = unsigned int; // word type for vector representation +constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word template _NODISCARD bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() - && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + if (_Left.size() != _Right.size()) { + return false; + } + +#if _HAS_IF_CONSTEXPR + if constexpr (is_same_v<_Ty, bool>) { + return _STD equal( + _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); + } else +#endif // _HAS_IF_CONSTEXPR + { + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 + +#if _HAS_IF_CONSTEXPR +// Optimize vector lexicographical comparisons. + +// There are several endianness/ordering issues to consider here. +// * Machine endianness is irrelevant. (That affects how an unsigned int is stored +// as a sequence of bytes. While all of our supported architectures are little-endian, +// that's irrelevant as long as we avoid reinterpreting unsigned int as a sequence of bytes.) +// * Appending bits to vector eventually appends words to its underlying storage. +// For example, vb[10] is stored within vb._Myvec[0], while vb[100] is stored within vb._Myvec[3]. +// This allows us to translate lexicographical comparisons from theoretical bits to physical words. +// * Unsigned integers are written and compared as big-endian (most significant bit first). +// For example, 0x10u > 0x07u. +// * However, vector packs bits into words as little-endian (least significant bit first). +// For example, vector{false, true, true, true} stores 0b0000'0000'0000'0000'0000'0000'0000'1110u. +// We could bit-reverse words before comparing, but we just need to find the least significant bit that differs. + +template +struct _Vbase_compare_three_way { + _NODISCARD constexpr _Ret operator()(const _Vbase _Left, const _Vbase _Right) const noexcept { + const _Vbase _Differing_bits = _Left ^ _Right; + + if (_Differing_bits == 0) { // improves _Countr_zero codegen below +#ifdef __cpp_lib_concepts + return strong_ordering::equal; +#else // __cpp_lib_concepts + return 0; +#endif // __cpp_lib_concepts + } + + const int _Bit_index = _Countr_zero(_Differing_bits); // number of least significant bits that match + _STL_INTERNAL_CHECK(_Bit_index < _VBITS); // because we return early for equality + + const _Vbase _Mask = _Vbase{1} << _Bit_index; // selects the least significant bit that differs + + // Instead of comparing (_Left & _Mask) to (_Right & _Mask), we know that exactly one side will be zero. +#ifdef __cpp_lib_concepts + return (_Left & _Mask) == 0 ? strong_ordering::less : strong_ordering::greater; +#else // __cpp_lib_concepts + return (_Left & _Mask) == 0 ? -1 : 1; +#endif // __cpp_lib_concepts + } +}; +#endif // _HAS_IF_CONSTEXPR +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + const auto _Min_word_size = (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Left_words = _Left._Myvec._Unchecked_begin(); + const auto _Right_words = _Right._Myvec._Unchecked_begin(); + + using _Comp = _Vbase_compare_three_way; + + const strong_ordering _Word_comparison = _STD lexicographical_compare_three_way( + _Left_words, _Left_words + _Min_word_size, _Right_words, _Right_words + _Min_word_size, _Comp{}); + + if (_Word_comparison != 0) { + return _Word_comparison; + } + + return _Left.size() <=> _Right.size(); + } else { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); + } +} +#else // __cpp_lib_concepts template _NODISCARD bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare( - _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#if _HAS_IF_CONSTEXPR + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + auto _First = _Left._Myvec._Unchecked_begin(); + auto _Other = _Right._Myvec._Unchecked_begin(); + + const auto _Last = _First + (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + + for (; _First != _Last; ++_First, (void) ++_Other) { + using _Comp = _Vbase_compare_three_way; + const auto _Result = _Comp{}(*_First, *_Other); + + if (_Result < 0) { + return true; + } else if (_Result > 0) { + return false; + } + } + + return _Left.size() < _Right.size(); + } else +#endif // _HAS_IF_CONSTEXPR + { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } } template @@ -1791,11 +1899,26 @@ template _NODISCARD bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // __cpp_lib_concepts -// CLASS TEMPLATE vector AND FRIENDS -using _Vbase = unsigned int; // word type for vector representation -constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word +template +void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +#if _HAS_CXX20 +template +typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { + return _Erase_remove(_Cont, _Val); +} + +template +typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { + return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); +} +#endif // _HAS_CXX20 +// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; @@ -2843,16 +2966,6 @@ public: } }; -template -_NODISCARD bool operator==(const vector& _Left, const vector& _Right) { - return _Left.size() == _Right.size() && _Left._Myvec == _Right._Myvec; -} - -template -_NODISCARD bool operator!=(const vector& _Left, const vector& _Right) { - return !(_Left == _Right); -} - // STRUCT TEMPLATE SPECIALIZATION hash template struct hash> { @@ -2864,18 +2977,6 @@ struct hash> { } }; -#if _HAS_CXX20 -template -typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Erase_remove(_Cont, _Val); -} - -template -typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { - return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); -} -#endif // _HAS_CXX20 - #if _HAS_CXX17 namespace pmr { template diff --git a/tests/std/test.lst b/tests/std/test.lst index 8b46ac33bfa..504942ff6d8 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -99,7 +99,7 @@ tests\Dev11_0000000_user_defined_literals tests\Dev11_0019127_singular_iterators tests\Dev11_0091392_string_erase_resize_perf tests\Dev11_0133625_locale0_implib_cpp -tests\Dev11_0135139_vector_bool_equality_perf +tests\Dev11_0135139_vector_bool_comparisons tests\Dev11_0235721_async_and_packaged_task tests\Dev11_0253803_debug_pointer tests\Dev11_0272959_make_signed @@ -296,6 +296,7 @@ tests\P1032R1_miscellaneous_constexpr tests\P1135R6_atomic_flag_test tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation +tests\P1614R2_spaceship tests\P1645R1_constexpr_numeric tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst similarity index 100% rename from tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst rename to tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst diff --git a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp new file mode 100644 index 00000000000..9a17e51d2b1 --- /dev/null +++ b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +vector vb_from_str(const char* str) { + vector vb; + + for (; *str != '\0'; ++str) { + assert(*str == '0' || *str == '1'); + + vb.push_back(*str == '1'); + } + + return vb; +} + +enum class Ordering : int { Less = -1, Equal = 0, Greater = 1 }; + +constexpr Ordering Lt = Ordering::Less; +constexpr Ordering Eq = Ordering::Equal; +constexpr Ordering Gt = Ordering::Greater; + +void test_comparison(const char* const left_str, const char* const right_str, const Ordering order) { + const auto left = vb_from_str(left_str); + const auto right = vb_from_str(right_str); + +#ifdef __cpp_lib_concepts + assert((left <=> right) == (static_cast(order) <=> 0)); + assert((right <=> left) == (0 <=> static_cast(order))); +#endif // __cpp_lib_concepts + + switch (order) { + case Lt: + assert(!(left == right)); + assert(!(right == left)); + assert(left < right); + assert(!(right < left)); + break; + case Eq: + assert(left == right); + assert(right == left); + assert(!(left < right)); + assert(!(right < left)); + break; + case Gt: + assert(!(left == right)); + assert(!(right == left)); + assert(!(left < right)); + assert(right < left); + break; + default: + assert(false); + break; + } +} + +int main() { + + { + mt19937 eng(1729); + + uniform_int_distribution dist(0, 1); + + const size_t N = 137; + + vector x(N); + vector y(N); + + for (size_t i = 0; i < N; ++i) { + const bool b = dist(eng) != 0; + + x[i] = b; + y[i] = b; + } + + assert(x == y); + + y.push_back(0); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + y.push_back(1); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + x.back().flip(); + + assert(x != y); + + y.back().flip(); + + assert(x == y); + } + + { + // Also test DevDiv#850453 ": Missing emplace methods in std::vector container". + + vector v(47, allocator()); + + v.emplace_back(make_shared(123)); + v.emplace_back(shared_ptr()); + + v.emplace(v.cbegin(), make_shared(3.14)); + v.emplace(v.cbegin(), make_unique(456)); + v.emplace(v.cbegin(), shared_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + + + vector correct; + + correct.insert(correct.cend(), 3, false); + correct.insert(correct.cend(), 2, true); + correct.insert(correct.cend(), 47, false); + correct.insert(correct.cend(), 1, true); + correct.insert(correct.cend(), 1, false); + + assert(v == correct); + } + + // Also test GH-1046 optimizing vector spaceship and less-than comparisons. + test_comparison("", "", Eq); // both empty + test_comparison("", "00", Lt); // empty vs. partial word + test_comparison("", "01", Lt); + test_comparison("", "00000000000000000000000000000000", Lt); // empty vs. full word + test_comparison("", "01000000000000000000000000000000", Lt); + test_comparison("", "0000000000000000000000000000000000", Lt); // empty vs. full and partial words + test_comparison("", "0100000000000000000000000000000001", Lt); + + test_comparison("010111010", "010111010", Eq); + test_comparison("010111010", "011110010", Lt); // test that bits are compared in the correct direction + + // same test, after an initial matching word + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111010111010", Eq); + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111011110010", Lt); + + test_comparison("00001111", "00001111", Eq); + test_comparison("00001111", "000011110", Lt); // matching prefixes, test size comparison + test_comparison("00001111", "000011111", Lt); + test_comparison("00001111", "00001111000000000000000000000000", Lt); // full word + test_comparison("00001111", "00001111000000000000000000000001", Lt); + test_comparison("00001111", "0000111100000000000000000000000000", Lt); // full and partial words + test_comparison("00001111", "0000111100000000000000000000000001", Lt); + + test_comparison("10", "01111", Gt); // shorter but greater + test_comparison("10", "01111111111111111111111111111111", Gt); // full word + test_comparison("10", "0111111111111111111111111111111111", Gt); // full and partial words +} diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp deleted file mode 100644 index 4036fe360c6..00000000000 --- a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include - -using namespace std; - -int main() { - - { - mt19937 eng(1729); - - uniform_int_distribution dist(0, 1); - - const size_t N = 137; - - vector x(N); - vector y(N); - - for (size_t i = 0; i < N; ++i) { - const bool b = dist(eng) != 0; - - x[i] = b; - y[i] = b; - } - - assert(x == y); - - y.push_back(0); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - y.push_back(1); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - x.back().flip(); - - assert(x != y); - - y.back().flip(); - - assert(x == y); - } - - { - // Also test DevDiv#850453 ": Missing emplace methods in std::vector container". - - vector v(47, allocator()); - - v.emplace_back(make_shared(123)); - v.emplace_back(shared_ptr()); - - v.emplace(v.cbegin(), make_shared(3.14)); - v.emplace(v.cbegin(), make_unique(456)); - v.emplace(v.cbegin(), shared_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - - - vector correct; - - correct.insert(correct.cend(), 3, false); - correct.insert(correct.cend(), 2, true); - correct.insert(correct.cend(), 47, false); - correct.insert(correct.cend(), 1, true); - correct.insert(correct.cend(), 1, false); - - assert(v == correct); - } -} diff --git a/tests/std/tests/P1614R2_spaceship/env.lst b/tests/std/tests/P1614R2_spaceship/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp new file mode 100644 index 00000000000..5a215eac23b --- /dev/null +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -0,0 +1,330 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Covers: +// * spaceship for containers + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using PartiallyOrdered = double; + +struct WeaklyOrdered { + [[nodiscard]] constexpr bool operator==(const WeaklyOrdered&) const { + return true; + } + + [[nodiscard]] constexpr std::weak_ordering operator<=>(const WeaklyOrdered&) const { + return std::weak_ordering::equivalent; + } +}; + +using StronglyOrdered = int; + +// Activates synth-three-way in N4861 16.4.2.1 [expos.only.func]/2. +struct SynthOrdered { + int val; + + constexpr SynthOrdered(const int x) : val{x} {} + + [[nodiscard]] constexpr bool operator==(const SynthOrdered& other) const { + return val == other.val; + } + + [[nodiscard]] constexpr bool operator<(const SynthOrdered& other) const { + return val < other.val; + } +}; + +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) { + assert(smaller < larger); + assert(smaller <= larger); + assert(larger > smaller); + assert(larger >= smaller); + assert(smaller == smaller_equal); + assert(smaller != larger); + assert((smaller <=> larger) < 0); + assert((larger <=> smaller) > 0); + assert((smaller <=> smaller_equal) == 0); + + 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>); + } else { + static_assert(std::is_same_v larger), std::strong_ordering>); + } +} + +template +void unordered_containers_test( + const Container& something, const Container& something_equal, const Container& different) { + assert(something == something_equal); + assert(something != different); +} + +void ordering_test_cases() { + { // constexpr array + constexpr std::array a0{{2, 8, 9, 1, 9}}; + constexpr std::array a1{{2, 8, 9}}; + constexpr std::array a2{{2, 8, 9, 1, 8}}; + + static_assert((a0 <=> a0) == 0); + static_assert((a1 <=> a1) == 0); + static_assert((a2 <=> a0) < 0); + static_assert((a0 <=> a2) > 0); + } + { // constexpr array SynthOrdered + constexpr std::array a = {10, 20, 30}; + constexpr std::array b = {10, 20, 40}; + + static_assert((a <=> a) == 0); + static_assert((a <=> b) < 0); + static_assert((b <=> a) > 0); + } + { // array + std::array a1 = {100, 100, 100}; + std::array a2 = {100, 100, 100}; + std::array b1 = {200, 200}; + ordered_containers_test(a1, a2, b1); + } + { // array SynthOrdered + std::array a = {10, 20, 30}; + std::array b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // deque + std::deque a1(3, 100); + std::deque a2(3, 100); + std::deque b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // deque SynthOrdered + std::deque a = {10, 20, 30}; + std::deque b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // list + std::list a1(3, 100); + std::list a2(3, 100); + std::list b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // list SynthOrdered + std::list a = {10, 20, 30}; + std::list b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // vector + std::vector a1(3, 100); + std::vector a2(3, 100); + std::vector b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // vector SynthOrdered + std::vector a = {10, 20, 30}; + std::vector b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // vector + std::vector c1 = {false, true, false}; + std::vector c2 = {false, true, false}; + std::vector d1 = {true, false}; + ordered_containers_test(c1, c2, d1); + } + { // forward_list + std::forward_list a1(3, 100); + std::forward_list a2(3, 100); + std::forward_list b1(2, 200); + ordered_containers_test(a1, a2, b1); + } + { // forward_list SynthOrdered + std::forward_list a = {10, 20, 30}; + std::forward_list b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // map + std::map a1; + a1["hi"] = 1; + a1["hola"] = 2; + std::map a2; + a2["hi"] = 1; + a2["hola"] = 2; + std::map b1; + b1["zoe"] = 3; + b1["koala"] = 4; + ordered_containers_test(a1, a2, b1); + } + { // map SynthOrdered + std::map a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; + std::map b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; + ordered_containers_test(a, a, b); + } + { // multimap + std::multimap a1 = {{'a', 1}, {'b', 2}, {'a', 3}}; + std::multimap a2 = {{'a', 1}, {'a', 3}, {'b', 2}}; + std::multimap b1 = {{'z', 4}, {'y', 90}, {'z', 12}}; + ordered_containers_test(a1, a2, b1); + } + { // multimap SynthOrdered + std::multimap a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; + std::multimap b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; + ordered_containers_test(a, a, b); + } + { // set + std::set a1; + a1.insert(10); + a1.insert(20); + + std::set a2; + a2.insert(10); + a2.insert(20); + + std::set b1; + b1.insert(30); + b1.insert(40); + ordered_containers_test(a1, a2, b1); + } + { // set SynthOrdered + std::set a = {10, 20, 30}; + std::set b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // multiset + std::multiset a1; + a1.insert(10); + a1.insert(10); + a1.insert(20); + + std::multiset a2; + a2.insert(10); + a2.insert(20); + a2.insert(10); + + std::multiset b1; + b1.insert(30); + b1.insert(40); + b1.insert(40); + ordered_containers_test(a1, a2, b1); + } + { // multiset SynthOrdered + std::multiset a = {10, 20, 30}; + std::multiset b = {10, 20, 40}; + ordered_containers_test(a, a, b); + } + { // unordered_map + using stringmap = std::unordered_map; + stringmap a = {{"cat", "tabby"}, {"dog", "poodle"}, {"bear", "grizzly"}}; + stringmap b = {{"dog", "poodle"}, {"bear", "grizzly"}, {"cat", "tabby"}}; + stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; + unordered_containers_test(a, b, c); + } + { // unordered_multimap + using stringmap = std::unordered_multimap; + stringmap a = {{"cat", "tabby"}, {"dog", "poodle"}, {"cat", "siamese"}, {"dog", "poodle"}}; + stringmap b = {{"dog", "poodle"}, {"cat", "siamese"}, {"cat", "tabby"}, {"dog", "poodle"}}; + stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; + unordered_containers_test(a, b, c); + } + { // unordered_set + std::unordered_set a = {"cat", "dog", "bear"}; + std::unordered_set b = {"bear", "cat", "dog"}; + std::unordered_set c = {"mouse", "cat", "bear", "dog"}; + unordered_containers_test(a, b, c); + } + { // unordered_multiset + std::unordered_multiset a = {"cat", "dog", "cat"}; + std::unordered_multiset b = {"cat", "cat", "dog"}; + std::unordered_multiset c = {"mouse", "cat", "bear", "dog"}; + unordered_containers_test(a, b, c); + } + { // queue + std::deque deq1(3, 100); + std::deque deq2(2, 200); + std::queue a(deq1); + std::queue b(deq1); + std::queue c(deq2); + ordered_containers_test(a, b, c); + } + { // queue SynthOrdered + std::queue a{std::deque{10, 20, 30}}; + std::queue b{std::deque{10, 20, 40}}; + ordered_containers_test(a, a, b); + } + { // stack + std::stack a; + a.push(2); + a.push(2); + std::stack b; + b.push(2); + b.push(2); + std::stack c; + c.push(3); + c.push(3); + ordered_containers_test(a, b, c); + } + { // stack SynthOrdered + std::stack a{std::deque{10, 20, 30}}; + std::stack b{std::deque{10, 20, 40}}; + ordered_containers_test(a, a, b); + } +} + +template +using SpaceshipType = decltype(std::declval() <=> std::declval()); + +template +void test_element_ordering() { + if constexpr (!std::is_same_v) { // SynthOrdered inherently doesn't support <=> directly + static_assert(std::is_same_v, Ordering>); + } + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + + // TRANSITION, std::pair spaceship not yet implemented + static_assert(std::is_same_v>, std::weak_ordering>); + static_assert(std::is_same_v>, std::weak_ordering>); + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); + + static_assert(std::is_same_v>, Ordering>); + static_assert(std::is_same_v>, Ordering>); +} + +int main() { + ordering_test_cases(); + + test_element_ordering(); + test_element_ordering(); + test_element_ordering(); + test_element_ordering(); +} From fa344bfd736ba71b6e83c13666e1908d7a2d52e9 Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Wed, 19 Aug 2020 17:40:18 -0700 Subject: [PATCH 02/18] feature/spaceship: Clause 30: Regex (#1145) Co-authored-by: Casey Carter Co-authored-by: Curtis Bezault --- stl/inc/regex | 315 ++++++++++++++++++++- tests/std/tests/P1614R2_spaceship/test.cpp | 145 +++++++++- 2 files changed, 431 insertions(+), 29 deletions(-) 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() { From f0eb0107f168655cc696fcaa33686b02097b916e Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Wed, 2 Sep 2020 19:36:53 -0700 Subject: [PATCH 03/18] feature/spaceship: Clause 19: Diagnostics (#1180) Co-authored-by: Ahana Mukhopadhyay Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/system_error | 103 ++++++++++++++++----- tests/std/tests/P1614R2_spaceship/env.lst | 3 + tests/std/tests/P1614R2_spaceship/test.cpp | 69 ++++++++++++++ 3 files changed, 152 insertions(+), 23 deletions(-) diff --git a/stl/inc/system_error b/stl/inc/system_error index c500401a377..6329e6eda66 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -16,7 +16,11 @@ #include #ifndef _M_CEE_PURE #include -#endif +#endif // _M_CEE_PURE + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -90,13 +94,22 @@ public: return _Addr == _Right._Addr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const error_category& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD strong_ordering operator<=>(const error_category& _Right) const noexcept { + return compare_three_way{}(_Addr, _Right._Addr); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD bool operator<(const error_category& _Right) const noexcept { return _Addr < _Right._Addr; } +#endif // !defined(__cpp_lib_concepts) error_category(const error_category&) = delete; error_category& operator=(const error_category&) = delete; @@ -173,6 +186,21 @@ public: return _System_error_equal(_Left, _Right); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); + } +#endif // !defined(__cpp_lib_concepts) +#if !_HAS_CXX20 _NODISCARD friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { return _System_error_equal(_Right, _Left); } @@ -188,11 +216,7 @@ public: _NODISCARD friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept { return !_System_error_equal(_Right, _Left); } - - _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); - } +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -249,22 +273,36 @@ public: return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } - _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { - return !(_Left == _Right); +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>( + const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); } - +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD friend bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() < _Right.category() || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); } +#endif // !defined(__cpp_lib_concepts) +#if !_HAS_CXX20 + _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { + return !(_Left == _Right); + } +#endif // !_HAS_CXX20 // We grant friendship to the operators from error_code here to allow is_error_code_enum_v but not // is_error_condition_enum_v enums to be compared directly with error_condition; for example: // io_errc::stream == make_error_condition(errc::out_of_memory) friend bool operator==(const error_code& _Left, const error_condition& _Right) noexcept; +#if !_HAS_CXX20 friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept; friend bool operator!=(const error_code& _Left, const error_condition& _Right) noexcept; friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept; +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -285,14 +323,42 @@ _NODISCARD inline bool operator==(const error_code& _Left, const error_condition return _Left.category().equivalent(_Left.value(), _Right) || _Right.category().equivalent(_Left, _Right.value()); } -_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { - return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); -} - _NODISCARD inline bool operator==(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts +_NODISCARD inline strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} + +_NODISCARD inline strong_ordering operator<=>(const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} + +_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} +#endif // !defined(__cpp_lib_concepts) + +#if !_HAS_CXX20 +_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { + return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); +} + _NODISCARD inline bool operator!=(const error_code& _Left, const error_code& _Right) noexcept { return !(_Left == _Right); } @@ -308,16 +374,7 @@ _NODISCARD inline bool operator!=(const error_condition& _Left, const error_code _NODISCARD inline bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { return !(_Left == _Right); } - -_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} - -_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS // VIRTUALS FOR error_category diff --git a/tests/std/tests/P1614R2_spaceship/env.lst b/tests/std/tests/P1614R2_spaceship/env.lst index f3ccc8613c6..20ea5fe3426 100644 --- a/tests/std/tests/P1614R2_spaceship/env.lst +++ b/tests/std/tests/P1614R2_spaceship/env.lst @@ -2,3 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception RUNALL_INCLUDE ..\concepts_matrix.lst +RUNALL_CROSSLIST +PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=0" +PM_CL="/D_STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS=1" diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 7c76cee6b79..84160c9ece7 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -121,6 +122,15 @@ namespace std { }; } // namespace std +struct dummy_diagnostic : std::error_category { + const char* name() const noexcept override { + return "dummy"; + } + std::string message(int) const override { + return ""; + } +}; + template void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { assert(smaller == smaller_equal); @@ -165,6 +175,36 @@ void unordered_containers_test( assert(something != different); } +template +void diagnostics_test() { + dummy_diagnostic c_mem[2]; + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_equal(0, c_mem[0]); + ErrorType e_larger(1, c_mem[1]); + + spaceship_test(e_smaller, e_equal, e_larger); + } + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_larger(0, c_mem[1]); + + assert(e_smaller < e_larger); + assert(!(e_larger < e_smaller)); + assert((e_smaller <=> e_larger) < 0); + assert((e_larger <=> e_smaller) > 0); + } + { + ErrorType e_smaller(0, c_mem[0]); + ErrorType e_larger(1, c_mem[0]); + + assert(e_smaller < e_larger); + assert(!(e_larger < e_smaller)); + assert((e_smaller <=> e_larger) < 0); + assert((e_larger <=> e_smaller) > 0); + } +} + void ordering_test_cases() { { // constexpr array constexpr std::array a0{{2, 8, 9, 1, 9}}; @@ -412,6 +452,35 @@ void ordering_test_cases() { static_assert(std::is_same_v, std::weak_ordering>); static_assert(std::is_same_v, std::partial_ordering>); } + { // Diagnostics Library + diagnostics_test(); + diagnostics_test(); + + dummy_diagnostic c_mem[2]; + { + std::error_code e1(0, c_mem[0]); + std::error_condition e2(0, c_mem[0]); + + assert(e1 == e2); + assert(e2 == e1); + } + { + std::error_code e1(0, c_mem[0]); + std::error_condition e2(0, c_mem[1]); + + assert(e1 != e2); + assert(e2 != e1); + } + { + std::error_code e1(1, c_mem[0]); + std::error_condition e2(0, c_mem[0]); + + assert(e1 != e2); + assert(e2 != e1); + } + + spaceship_test(c_mem[0], c_mem[0], c_mem[1]); + } } template From 82855581d1badad54edf5ff32a6c97b12d1867cf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 21 Jan 2021 17:42:20 -0800 Subject: [PATCH 04/18] `if constexpr` is always available now. --- stl/inc/vector | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 05f6adc3a4c..252601aae62 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1769,13 +1769,10 @@ _NODISCARD bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _ return false; } -#if _HAS_IF_CONSTEXPR if constexpr (is_same_v<_Ty, bool>) { return _STD equal( _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); - } else -#endif // _HAS_IF_CONSTEXPR - { + } else { return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } } @@ -1787,7 +1784,6 @@ _NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _ } #endif // !_HAS_CXX20 -#if _HAS_IF_CONSTEXPR // Optimize vector lexicographical comparisons. // There are several endianness/ordering issues to consider here. @@ -1829,7 +1825,6 @@ struct _Vbase_compare_three_way { #endif // __cpp_lib_concepts } }; -#endif // _HAS_IF_CONSTEXPR #ifdef __cpp_lib_concepts template @@ -1859,7 +1854,6 @@ _NODISCARD _Synth_three_way_result<_Ty> operator<=>( #else // __cpp_lib_concepts template _NODISCARD bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { -#if _HAS_IF_CONSTEXPR if constexpr (is_same_v<_Ty, bool>) { // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. auto _First = _Left._Myvec._Unchecked_begin(); @@ -1879,9 +1873,7 @@ _NODISCARD bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _A } return _Left.size() < _Right.size(); - } else -#endif // _HAS_IF_CONSTEXPR - { + } else { return _STD lexicographical_compare( _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); } From 091958d07423fdf460ef26968ce4a6a77cde1a3c Mon Sep 17 00:00:00 2001 From: statementreply Date: Thu, 28 Jan 2021 10:33:40 +0800 Subject: [PATCH 05/18] feature/spaceship: Clause 32: Thread support (#1590) Co-authored-by: Stephan T. Lavavej --- stl/inc/thread | 11 +++++++++++ tests/std/tests/P1614R2_spaceship/test.cpp | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/stl/inc/thread b/stl/inc/thread index 2d46b78936d..60c41a0fb6d 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -14,6 +14,7 @@ #include #include #if _HAS_CXX20 +#include #include #endif // _HAS_CXX20 @@ -215,7 +216,11 @@ private: friend thread::id thread::get_id() const noexcept; friend thread::id this_thread::get_id() noexcept; friend bool operator==(thread::id _Left, thread::id _Right) noexcept; +#if _HAS_CXX20 + friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv friend bool operator<(thread::id _Left, thread::id _Right) noexcept; +#endif // !_HAS_CXX20 template friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id); friend hash; @@ -237,6 +242,11 @@ _NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept return _Left._Id == _Right._Id; } +#if _HAS_CXX20 +_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept { + return _Left._Id <=> _Right._Id; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept { return !(_Left == _Right); } @@ -256,6 +266,7 @@ _NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept { _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept { return !(_Left < _Right); } +#endif // !_HAS_CXX20 template basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) { diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 84160c9ece7..734bf99f7de 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -481,6 +482,14 @@ void ordering_test_cases() { spaceship_test(c_mem[0], c_mem[0], c_mem[1]); } + { // thread::id + std::thread::id id1; + std::thread::id id1_equal; + std::thread::id id2 = std::this_thread::get_id(); + + // Implementation-specific assumption: std::thread::id{} occurs first in the unspecified total ordering. + spaceship_test(id1, id1_equal, id2); + } } template From e68b14a2724f096ab4e3c7857fee5960ae853930 Mon Sep 17 00:00:00 2001 From: statementreply Date: Wed, 3 Feb 2021 15:57:54 +0800 Subject: [PATCH 06/18] feature/spaceship: Clause 28: Localization, Clause 29: Input/output (#1593) --- stl/inc/filesystem | 34 +++++++++++++-- stl/inc/xlocale | 2 + tests/std/tests/P1614R2_spaceship/test.cpp | 49 ++++++++++++++++++++++ 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/stl/inc/filesystem b/stl/inc/filesystem index c0444a29bd9..075a08b1587 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -26,6 +26,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1399,6 +1403,11 @@ namespace filesystem { return _Left.compare(_Right) == 0; } +#if _HAS_CXX20 + _NODISCARD friend strong_ordering operator<=>(const path& _Left, const path& _Right) noexcept { + return _Left.compare(_Right) <=> 0; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD friend bool operator!=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) != 0; } @@ -1418,6 +1427,7 @@ namespace filesystem { _NODISCARD friend bool operator>=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) >= 0; } +#endif // !_HAS_CXX20 _NODISCARD friend path operator/(const path& _Left, const path& _Right) { // append a pair of paths together return path(_Left) /= _Right; @@ -1967,6 +1977,12 @@ namespace filesystem { return _Myperms; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const file_status& _Lhs, const file_status& _Rhs) noexcept { + return _Lhs._Myftype == _Rhs._Myftype && _Lhs._Myperms == _Rhs._Myperms; + } +#endif // _HAS_CXX20 + void _Refresh(const __std_win_error _Error, const __std_fs_stats& _Stats) noexcept { if (_Error == __std_win_error::_Success) { const auto _Attrs = _Stats._Attributes; @@ -2445,18 +2461,23 @@ namespace filesystem { return _Result._Status; } - _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { - return _Path < _Rhs._Path; - } - _NODISCARD bool operator==(const directory_entry& _Rhs) const noexcept { return _Path == _Rhs._Path; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const directory_entry& _Rhs) const noexcept { + return _Path <=> _Rhs._Path; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const directory_entry& _Rhs) const noexcept { return _Path != _Rhs._Path; } + _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { + return _Path < _Rhs._Path; + } + _NODISCARD bool operator<=(const directory_entry& _Rhs) const noexcept { return _Path <= _Rhs._Path; } @@ -2468,6 +2489,7 @@ namespace filesystem { _NODISCARD bool operator>=(const directory_entry& _Rhs) const noexcept { return _Path >= _Rhs._Path; } +#endif // !_HAS_CXX20 // [fs.dir.entry.io], inserter template @@ -3649,6 +3671,10 @@ namespace filesystem { uintmax_t capacity; uintmax_t free; uintmax_t available; + +#if _HAS_CXX20 + _NODISCARD friend constexpr bool operator==(const space_info&, const space_info&) noexcept = default; +#endif // _HAS_CXX20 }; _NODISCARD inline space_info space(const path& _Target) { diff --git a/stl/inc/xlocale b/stl/inc/xlocale index fcdbf63bb17..5fe52ccd945 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -389,9 +389,11 @@ public: return _Ptr == _Loc._Ptr || (name().compare("*") != 0 && name().compare(_Loc.name()) == 0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const locale& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 static _MRTIMP2_PURE const locale& __CLRCALL_PURE_OR_CDECL classic(); // classic "C" locale diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 734bf99f7de..c488238b56f 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -482,6 +483,54 @@ void ordering_test_cases() { spaceship_test(c_mem[0], c_mem[0], c_mem[1]); } + { // filesystem::space_info + constexpr std::filesystem::space_info si1{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; + constexpr std::filesystem::space_info si2{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; + constexpr std::filesystem::space_info si3{4'000'000'000'000, 2'000'000'000'000, 2'000'000'000'000}; + constexpr std::filesystem::space_info si4{4'000'000'000'000, 3'000'000'000'000, 1'000'000'000'000}; + constexpr std::filesystem::space_info si5{3'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; + + static_assert(si1 == si2); + static_assert(si1 != si3); + static_assert(si1 != si4); + static_assert(si1 != si5); + + assert(si1 == si2); + assert(si1 != si3); + assert(si1 != si4); + assert(si1 != si5); + } + { // filesystem::path + const std::filesystem::path p1{R"(a/b/c)"}; + const std::filesystem::path p2{LR"(a\b\c)"}; + const std::filesystem::path p3{R"(a/b/d)"}; + + spaceship_test(p1, p2, p3); + } + { // filesystem::file_status + std::filesystem::file_status s1; + s1.type(std::filesystem::file_type::regular); + s1.permissions(std::filesystem::perms{0755}); + + std::filesystem::file_status s2 = s1; + + std::filesystem::file_status s3 = s1; + s3.type(std::filesystem::file_type::directory); + + std::filesystem::file_status s4 = s1; + s4.permissions(std::filesystem::perms{0600}); + + assert(s1 == s2); + assert(s1 != s3); + assert(s1 != s4); + } + { // filesystem::directory_entry + const std::filesystem::directory_entry de1{u8R"(a/b/c)"}; + const std::filesystem::directory_entry de2{uR"(a\b\c)"}; + const std::filesystem::directory_entry de3{u8R"(a/b/d)"}; + + spaceship_test(de1, de2, de3); + } { // thread::id std::thread::id id1; std::thread::id id1_equal; From 5443c4a6241289e9f57194499e66deebd43b35dc Mon Sep 17 00:00:00 2001 From: Anju del Moral Gonzalez Date: Thu, 11 Feb 2021 20:24:41 -0800 Subject: [PATCH 07/18] feature/spaceship: Clause 26: Numerics (#1627) Co-authored-by: Stephan T. Lavavej --- stl/inc/complex | 4 ++++ stl/inc/valarray | 6 ++++++ tests/std/tests/P1614R2_spaceship/test.cpp | 12 ++++++++++++ 3 files changed, 22 insertions(+) diff --git a/stl/inc/complex b/stl/inc/complex index e0aff1ed4ee..5e935cf52d0 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -1514,12 +1514,15 @@ _NODISCARD constexpr bool operator==(const complex<_Ty>& _Left, const _Ty& _Righ return _Left.real() == _Right && _Left.imag() == 0; } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator==(const _Ty& _Left, const complex<_Ty>& _Right) { return _Left == _Right.real() && 0 == _Right.imag(); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE operator!= +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const complex<_Ty>& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); @@ -1534,6 +1537,7 @@ template _NODISCARD constexpr bool operator!=(const _Ty& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE imag template diff --git a/stl/inc/valarray b/stl/inc/valarray index 63b69f34fa4..5f5a6ab33fc 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -1373,6 +1373,12 @@ public: return _Stride; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const slice& _Left, const slice& _Right) noexcept /* strengthened */ { + return _Left.start() == _Right.start() && _Left.size() == _Right.size() && _Left.stride() == _Right.stride(); + } +#endif // _HAS_CXX20 + protected: size_t _Start = 0; // the starting offset size_t _Len = 0; // the number of elements diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index c488238b56f..1775d4b97f3 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include template @@ -483,6 +484,17 @@ void ordering_test_cases() { spaceship_test(c_mem[0], c_mem[0], c_mem[1]); } + { // slice + std::slice a1(2, 3, 4); + std::slice a2(2, 3, 4); + std::slice a3(3, 3, 4); + std::slice a4(2, 4, 4); + std::slice a5(2, 3, 3); + assert(a1 == a2); + assert(a1 != a3); + assert(a1 != a4); + assert(a1 != a5); + } { // filesystem::space_info constexpr std::filesystem::space_info si1{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; constexpr std::filesystem::space_info si2{4'000'000'000'000, 2'000'000'000'000, 1'000'000'000'000}; From 250aa736762959370ea435f3938339c056c39c8a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 17 Feb 2021 19:16:54 -0800 Subject: [PATCH 08/18] feature/spaceship: Clause 23: Iterators (#1645) --- stl/inc/iterator | 4 ++++ tests/libcxx/expected_results.txt | 3 +++ tests/libcxx/skipped_tests.txt | 3 +++ 3 files changed, 10 insertions(+) diff --git a/stl/inc/iterator b/stl/inc/iterator index ca74d58917c..94210f0eeb7 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -302,11 +302,13 @@ _NODISCARD bool operator==(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _ return _Left._Equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left, const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostream_iterator template > @@ -488,11 +490,13 @@ _NODISCARD bool operator==( return _Left.equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const istreambuf_iterator<_Elem, _Traits>& _Left, const istreambuf_iterator<_Elem, _Traits>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostreambuf_iterator template diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a2496d683f3..24c698b3d62 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -671,6 +671,9 @@ std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_lay # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano std/input.output/filesystems/fs.filesystem.synopsis/file_time_type_resolution.compile.pass.cpp FAIL +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +std/iterators/stream.iterators/istream.iterator/istream.iterator.ops/equal.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 2760e3e0b3c..4556745fa3e 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -671,6 +671,9 @@ utilities\memory\util.smartptr\util.smartptr.shared\libcxx.control_block_layout. # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano input.output\filesystems\fs.filesystem.synopsis\file_time_type_resolution.compile.pass.cpp +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +iterators\stream.iterators\istream.iterator\istream.iterator.ops\equal.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. From bbedd7ad845b73c4c4c140ceb20b1838012bd660 Mon Sep 17 00:00:00 2001 From: Anju del Moral Gonzalez Date: Wed, 17 Feb 2021 22:31:06 -0800 Subject: [PATCH 09/18] feature/spaceship: Clause 21: Strings (#1635) Co-authored-by: Anju Del Moral Gonzalez Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/xstring | 65 ++++++++- tests/std/tests/P1614R2_spaceship/test.cpp | 145 +++++++++++++++++++-- 2 files changed, 192 insertions(+), 18 deletions(-) diff --git a/stl/inc/xstring b/stl/inc/xstring index 80355987c2c..c5fc7e1c89f 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -39,6 +39,9 @@ struct _Char_traits { // properties of a string or stream element using pos_type = streampos; using off_type = streamoff; using state_type = _Mbstatet; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. @@ -217,6 +220,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -355,6 +361,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -1659,11 +1668,13 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } +#if !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { return _Lhs._Equal(_Rhs); } +#endif // !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( @@ -1671,7 +1682,7 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } - +#if !_HAS_CXX20 // FUNCTION TEMPLATES operator!= FOR basic_string_view template _NODISCARD constexpr bool operator!=( @@ -1770,7 +1781,37 @@ _NODISCARD constexpr bool operator>=( const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { return _Lhs.compare(_Rhs) >= 0; } +#endif // !_HAS_CXX20 + +#if _HAS_CXX20 +template +struct _Get_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_comparison_category<_Traits, void_t> { + using type = typename _Traits::comparison_category; + + static_assert(_Is_any_of_v, + "N4878 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); +}; + +template +using _Get_comparison_category_t = typename _Get_comparison_category<_Traits>::type; + +template +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +template // TRANSITION, VSO-409326 +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +#endif // _HAS_CXX20 // TYPEDEFS FOR basic_string_view using string_view = basic_string_view; @@ -4512,13 +4553,26 @@ _NODISCARD bool operator==( } template -_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { - return _Right._Equal(_Left); +_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return _Left._Equal(_Right); } +#if _HAS_CXX20 template -_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { - return _Left._Equal(_Right); +_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} + +template +_NODISCARD _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { + return _Right._Equal(_Left); } template @@ -4600,6 +4654,7 @@ template _NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return !(_Left < _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ using string = basic_string, allocator>; using wstring = basic_string, allocator>; diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 1775d4b97f3..e0788e989c0 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Covers: -// * spaceship for containers - #include #include #include @@ -21,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -434,14 +432,13 @@ void ordering_test_cases() { 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); + 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>; @@ -449,12 +446,134 @@ void ordering_test_cases() { 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::strong_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>); } + { // char_traits + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); +#ifdef __cpp_char8_t + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); +#endif // __cpp_char8_t + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); + static_assert(std::is_same_v::comparison_category, std::strong_ordering>); + } + { // Strings library + const std::string a1 = "abcdef"; + const std::string a2 = "abcdef"; + const std::string a3 = "abcdefg"; + const std::string a4 = "abcde"; + const std::string a5 = "abddef"; + const std::string a6 = "abbdef"; + + assert((a1 <=> a2) == std::strong_ordering::equivalent); + assert((a1 <=> a3) == std::strong_ordering::less); + assert((a1 <=> a4) == std::strong_ordering::greater); + assert((a1 <=> a5) == std::strong_ordering::less); + assert((a1 <=> a6) == std::strong_ordering::greater); + + assert(a1 == a2); + assert(a1 >= a2); + assert(a1 <= a2); + assert(a1 < a3); + assert(a1 <= a3); + assert(a1 != a3); + assert(a1 > a4); + assert(a1 >= a4); + assert(a1 != a4); + assert(a1 < a5); + assert(a1 <= a5); + assert(a1 != a5); + assert(a1 > a6); + assert(a1 >= a6); + assert(a1 != a6); + + assert((a1 <=> "aardvark") == std::strong_ordering::greater); + assert((a1 <=> "abcdef") == std::strong_ordering::equivalent); + assert((a1 <=> "zebra") == std::strong_ordering::less); + + assert(("aardvark" <=> a1) == std::strong_ordering::less); + assert(("abcdef" <=> a1) == std::strong_ordering::equivalent); + assert(("zebra" <=> a1) == std::strong_ordering::greater); + } + { // string_view + const std::string_view a1 = "abcdef"; + const std::string_view a2 = "abcdef"; + const std::string_view a3 = "abcdefg"; + const std::string_view a4 = "abcde"; + const std::string_view a5 = "abddef"; + const std::string_view a6 = "abbdef"; + + assert((a1 <=> a2) == std::strong_ordering::equivalent); + assert((a1 <=> a3) == std::strong_ordering::less); + assert((a1 <=> a4) == std::strong_ordering::greater); + assert((a1 <=> a5) == std::strong_ordering::less); + assert((a1 <=> a6) == std::strong_ordering::greater); + + assert(a1 == a2); + assert(a1 >= a2); + assert(a1 <= a2); + assert(a1 < a3); + assert(a1 <= a3); + assert(a1 != a3); + assert(a1 > a4); + assert(a1 >= a4); + assert(a1 != a4); + assert(a1 < a5); + assert(a1 <= a5); + assert(a1 != a5); + assert(a1 > a6); + assert(a1 >= a6); + assert(a1 != a6); + + assert((a1 <=> "aardvark") == std::strong_ordering::greater); + assert((a1 <=> "abcdef") == std::strong_ordering::equivalent); + assert((a1 <=> "zebra") == std::strong_ordering::less); + + assert(("aardvark" <=> a1) == std::strong_ordering::less); + assert(("abcdef" <=> a1) == std::strong_ordering::equivalent); + assert(("zebra" <=> a1) == std::strong_ordering::greater); + } + { // constexpr string_view + constexpr std::string_view a1 = "abcdef"; + constexpr std::string_view a2 = "abcdef"; + constexpr std::string_view a3 = "abcdefg"; + constexpr std::string_view a4 = "abcde"; + constexpr std::string_view a5 = "abddef"; + constexpr std::string_view a6 = "abbdef"; + + static_assert((a1 <=> a2) == std::strong_ordering::equivalent); + static_assert((a1 <=> a3) == std::strong_ordering::less); + static_assert((a1 <=> a4) == std::strong_ordering::greater); + static_assert((a1 <=> a5) == std::strong_ordering::less); + static_assert((a1 <=> a6) == std::strong_ordering::greater); + + static_assert(a1 == a2); + static_assert(a1 >= a2); + static_assert(a1 <= a2); + static_assert(a1 < a3); + static_assert(a1 <= a3); + static_assert(a1 != a3); + static_assert(a1 > a4); + static_assert(a1 >= a4); + static_assert(a1 != a4); + static_assert(a1 < a5); + static_assert(a1 <= a5); + static_assert(a1 != a5); + static_assert(a1 > a6); + static_assert(a1 >= a6); + static_assert(a1 != a6); + + static_assert((a1 <=> "aardvark") == std::strong_ordering::greater); + static_assert((a1 <=> "abcdef") == std::strong_ordering::equivalent); + static_assert((a1 <=> "zebra") == std::strong_ordering::less); + + static_assert(("aardvark" <=> a1) == std::strong_ordering::less); + static_assert(("abcdef" <=> a1) == std::strong_ordering::equivalent); + static_assert(("zebra" <=> a1) == std::strong_ordering::greater); + } { // Diagnostics Library diagnostics_test(); diagnostics_test(); From 55c2af9f1dc0d738ba96ee55e93fb7344138d87a Mon Sep 17 00:00:00 2001 From: d-winsor Date: Thu, 18 Feb 2021 17:38:38 -0800 Subject: [PATCH 10/18] feature/spaceship: Clause 20: variant, monostate (#1657) Co-authored-by: Stephan T. Lavavej --- stl/inc/variant | 49 +++++++++++++++++----- tests/std/tests/P1614R2_spaceship/test.cpp | 41 +++++++++++++++++- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/stl/inc/variant b/stl/inc/variant index bccf5add82a..7389fbee200 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -1365,13 +1365,14 @@ _NODISCARD constexpr add_pointer_t get_if( } // RELATIONAL OPERATORS [variant.relops] -template -struct _Variant_relop_visitor { // evaluate _Op with the contained value of two variants that hold the same alternative +template +struct _Variant_relop_visitor2 { // evaluate _Op with the contained value of two variants that hold the same alternative const _Variant_storage<_Types...>& _Left; template - _NODISCARD constexpr bool operator()(_Tagged _Right) const noexcept( - disjunction_v, is_nothrow_invocable_r>) { + _NODISCARD constexpr _Result operator()(_Tagged _Right) const + noexcept(disjunction_v, + is_nothrow_invocable_r<_Result, _Op, const _Ty&, const _Ty&>>) { // determine the relationship between the stored values of _Left and _Right // pre: _Left.index() == _Idx && _Right.index() == _Idx if constexpr (_Idx != variant_npos) { @@ -1387,7 +1388,7 @@ template _NODISCARD constexpr bool operator==(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments are both valueless or contain equal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() == _Right_index && _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1397,7 +1398,7 @@ template _NODISCARD constexpr bool operator!=(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments have different active alternatives or contain unequal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() != _Right_index || _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1408,7 +1409,7 @@ _NODISCARD constexpr bool operator<(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a lesser index(), or equal index() and lesser // contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1421,7 +1422,7 @@ _NODISCARD constexpr bool operator>(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a greater index(), or equal index() and // greater contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1434,7 +1435,7 @@ _NODISCARD constexpr bool operator<=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is less than _Right's, or equal and // _Left contains a value less than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1447,7 +1448,7 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is greater than _Right's, or equal and // _Left contains a value greater than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1455,6 +1456,27 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari && _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()})); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (three_way_comparable<_Types> && ...) +_NODISCARD constexpr common_comparison_category_t...> + operator<=>(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( + conjunction_v...>, + compare_three_way, const _Types&, const _Types&>...>) /* strengthened */ { + // clang-format on + // determine the three-way comparison of _Left's and _Right's index, if equal + // return the three-way comparison of the contained values of _Left and _Right + using _Visitor = _Variant_relop_visitor2...>, _Types...>; + const size_t _Left_offset = _Left.index() + 1; + const size_t _Right_offset = _Right.index() + 1; + const auto _Offset_order = _Left_offset <=> _Right_offset; + return _Offset_order != 0 ? _Offset_order + : _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()}); +} +#endif // __cpp_lib_concepts + // VISITATION [variant.visit] template inline constexpr size_t _Variant_total_states = @@ -1698,6 +1720,12 @@ struct monostate {}; _NODISCARD constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#if _HAS_CXX20 +_NODISCARD constexpr strong_ordering operator<=>(monostate, monostate) noexcept { + return strong_ordering::equal; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(monostate, monostate) noexcept { return false; } @@ -1713,6 +1741,7 @@ _NODISCARD constexpr bool operator<=(monostate, monostate) noexcept { _NODISCARD constexpr bool operator>=(monostate, monostate) noexcept { return true; } +#endif // !_HAS_CXX20 // SPECIALIZED ALGORITHMS [variant.specalg] template #include #include +#include #include template @@ -133,7 +134,7 @@ struct dummy_diagnostic : std::error_category { }; template -void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { +constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { assert(smaller == smaller_equal); assert(smaller_equal == smaller); assert(smaller != larger); @@ -151,6 +152,8 @@ void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, co assert((smaller <=> smaller_equal) == 0); static_assert(std::is_same_v larger), ReturnType>); + + return true; } template @@ -603,6 +606,42 @@ void ordering_test_cases() { spaceship_test(c_mem[0], c_mem[0], c_mem[1]); } + { // variant + using V = std::variant; + constexpr V v0_0(std::in_place_index<0>, 0); + constexpr V v0_1(std::in_place_index<0>, 1); + constexpr V v1_0(std::in_place_index<1>, 0); + constexpr V v1_1(std::in_place_index<1>, 1); + + spaceship_test(v0_0, v0_0, v0_1); + spaceship_test(v0_1, v0_1, v1_0); + spaceship_test(v1_0, v1_0, v1_1); + + static_assert(spaceship_test(v0_0, v0_0, v0_1)); + static_assert(spaceship_test(v0_1, v0_1, v1_0)); + static_assert(spaceship_test(v1_0, v1_0, v1_1)); + + struct ThrowException { + operator int() { + throw "woof"; + } + }; + V valueless(std::in_place_index<1>, 1729L); + try { + valueless.emplace<0>(ThrowException{}); + } catch (...) { + // ignore exception + } + assert(valueless.valueless_by_exception()); + spaceship_test(valueless, valueless, v0_0); + spaceship_test(valueless, valueless, v1_1); + + using M = std::monostate; + constexpr M m1{}; + constexpr M m2{}; + assert((m1 <=> m2) == 0); + static_assert((m1 <=> m2) == 0); + } { // slice std::slice a1(2, 3, 4); std::slice a2(2, 3, 4); From eeed690e2b77c8acf31a98a40738fef451500173 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Fri, 19 Feb 2021 06:18:56 +0100 Subject: [PATCH 11/18] feature/spaceship: Clause 22: Container Iterators (#1648) Co-authored-by: Casey Carter Co-authored-by: Stephan T. Lavavej --- stl/inc/array | 15 +++- stl/inc/deque | 13 ++++ stl/inc/filesystem | 6 ++ stl/inc/forward_list | 6 ++ stl/inc/iterator | 14 ++++ stl/inc/list | 4 + stl/inc/vector | 39 +++++++--- stl/inc/xhash | 2 + stl/inc/xstring | 19 +++++ stl/inc/xtree | 6 ++ tests/std/tests/P1614R2_spaceship/test.cpp | 90 ++++++++++++++++++++++ 11 files changed, 202 insertions(+), 12 deletions(-) diff --git a/stl/inc/array b/stl/inc/array index 23c892f3a15..f8dae919a60 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -111,6 +111,11 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + return _Ptr <=> _Right._Ptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -130,6 +135,7 @@ public: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 using _Prevent_inheriting_unwrap = _Array_const_iterator; @@ -235,6 +241,12 @@ private: return _Idx == _Right._Idx; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Idx <=> _Right._Idx; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -255,6 +267,7 @@ private: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR17 void _Compat(const _Array_const_iterator& _Right) const noexcept { // test for compatible iterator pair _STL_VERIFY(_Ptr == _Right._Ptr, "array iterators incompatible"); @@ -784,7 +797,7 @@ _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const ar #if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20 bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +_NODISCARD bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left == _Right); } #endif // !_HAS_CXX20 diff --git a/stl/inc/deque b/stl/inc/deque index fe0b478bda0..f1580180c88 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -107,6 +107,11 @@ public: return _Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_unchecked_const_iterator& _Right) const noexcept { + return _Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -126,6 +131,7 @@ public: _NODISCARD bool operator>=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 const _Container_base12* _Getcont() const noexcept { // get container pointer return _Mycont; @@ -345,6 +351,12 @@ public: return this->_Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_const_iterator& _Right) const noexcept { + _Compat(_Right); + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -365,6 +377,7 @@ public: _NODISCARD bool operator>=(const _Deque_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _Deque_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 075a08b1587..05341d65c4c 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -1603,9 +1603,11 @@ namespace filesystem { return _Lhs._Position == _Rhs._Position; } +#if !_HAS_CXX20 _NODISCARD friend bool operator!=(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { return _Lhs._Position != _Rhs._Position; } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend void _Verify_range(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { @@ -2728,9 +2730,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const directory_iterator& _Rhs) const noexcept /* strengthened */ { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -2977,9 +2981,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const recursive_directory_iterator& _Rhs) const noexcept { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 935574d1004..468f6e9c139 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -63,17 +63,21 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return _Ptr == nullptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return _Ptr != nullptr; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -161,9 +165,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Flist_const_iterator& _First, const _Flist_const_iterator& _Last) noexcept { diff --git a/stl/inc/iterator b/stl/inc/iterator index 94210f0eeb7..6fd2233e372 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1496,6 +1496,13 @@ public: return _Myindex == _Right._Myindex; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const checked_array_iterator& _Right) const noexcept { + _STL_VERIFY(_Myarray == _Right._Myarray && _Mysize == _Right._Mysize, + "cannot compare incompatible checked_array_iterators"); + return _Myindex <=> _Right._Myindex; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const checked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1517,6 +1524,7 @@ public: _NODISCARD constexpr bool operator>=(const checked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 friend constexpr void _Verify_range( const checked_array_iterator& _First, const checked_array_iterator& _Last) noexcept { @@ -1650,6 +1658,11 @@ public: return _Myptr == _Right._Myptr; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const unchecked_array_iterator& _Right) const noexcept { + return _Myptr <=> _Right._Myptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const unchecked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1669,6 +1682,7 @@ public: _NODISCARD constexpr bool operator>=(const unchecked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend constexpr void _Verify_range( diff --git a/stl/inc/list b/stl/inc/list index c8d81b1897e..812c81f6c61 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -74,9 +74,11 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -199,9 +201,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _List_const_iterator& _First, const _List_const_iterator& _Last) noexcept { diff --git a/stl/inc/vector b/stl/inc/vector index 5ee01bc5ff2..a5b750af657 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -152,26 +152,33 @@ public: return _Ptr == _Right._Ptr; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const _Vector_const_iterator& _Right) const noexcept { +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vector_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr < _Right._Ptr; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vector_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vector_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vector_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vector_const_iterator& _Right) const noexcept { // test for compatible iterator pair @@ -1851,7 +1858,7 @@ _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const vector<_Ty, _Alloc>& _Le #if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } #endif // !_HAS_CXX20 @@ -2204,26 +2211,36 @@ public: return this->_Myptr == _Right._Myptr && this->_Myoff == _Right._Myoff; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const _Vb_const_iterator& _Right) const noexcept { +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vb_const_iterator& _Right) const noexcept { + _Compat(_Right); + if (const auto _CmpResult = this->_Myptr <=> _Right._Myptr; _CmpResult != 0) { + return _CmpResult; + } + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vb_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vb_const_iterator& _Right) const noexcept { _Compat(_Right); return this->_Myptr < _Right._Myptr || (this->_Myptr == _Right._Myptr && this->_Myoff < _Right._Myoff); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vb_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vb_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vb_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vb_const_iterator& _Right) const noexcept { // test for compatible iterator pair diff --git a/stl/inc/xhash b/stl/inc/xhash index 3c8a7bbaba2..a7fc07566a0 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -196,12 +196,14 @@ struct _Reinterpret_move_iter { return _Lhs._Base == _Rhs._Base; } +#if !_HAS_CXX20 #ifndef __CUDACC__ // TRANSITION, VSO-568006 _NODISCARD #endif // TRANSITION, VSO-568006 friend bool operator!=(const _Reinterpret_move_iter& _Lhs, const _Reinterpret_move_iter& _Rhs) { return _Lhs._Base != _Rhs._Base; } +#endif // !_HAS_CXX20 }; // STRUCT TEMPLATE _List_head_construct_ptr diff --git a/stl/inc/xstring b/stl/inc/xstring index c5fc7e1c89f..8551a274080 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1124,6 +1124,17 @@ public: #endif // _ITERATOR_DEBUG_LEVEL } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff <=> _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr <=> _Right._Myptr; +#endif // _ITERATOR_DEBUG_LEVEL + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1149,6 +1160,7 @@ public: _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL >= 1 friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { @@ -2009,6 +2021,12 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -2029,6 +2047,7 @@ public: _NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 diff --git a/stl/inc/xtree b/stl/inc/xtree index aba7f46baee..3d9e6786ee0 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -98,18 +98,22 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return !!_Ptr->_Isnil; // TRANSITION, avoid warning C4800: // "Implicit conversion from 'char' to bool. Possible information loss" (/Wall) } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return !_Ptr->_Isnil; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -232,9 +236,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Tree_const_iterator& _First, const _Tree_const_iterator& _Last) noexcept { diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 01b773ad200..2bd59ee057b 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,27 @@ void unordered_containers_test( assert(something != different); } +template +void ordered_iterator_test(const Iter& smaller, const Iter& smaller_equal, const Iter& larger, + const ConstIter& const_smaller, const ConstIter& const_smaller_equal, const ConstIter& const_larger) { + spaceship_test(smaller, smaller_equal, larger); + spaceship_test(const_smaller, const_smaller_equal, const_larger); + spaceship_test(const_smaller, smaller_equal, larger); +} + +template +void unordered_iterator_test(const Iter& something, const Iter& something_equal, const Iter& different, + const ConstIter& const_something, const ConstIter& const_something_equal, const ConstIter& const_different) { + assert(something == something_equal); + assert(something != different); + + assert(const_something == const_something_equal); + assert(const_something != const_different); + + assert(something == const_something_equal); + assert(something != const_different); +} + template void diagnostics_test() { dummy_diagnostic c_mem[2]; @@ -233,61 +255,72 @@ void ordering_test_cases() { std::array a2 = {100, 100, 100}; std::array b1 = {200, 200}; ordered_containers_test(a1, a2, b1); + ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // array SynthOrdered std::array a = {10, 20, 30}; std::array b = {10, 20, 40}; ordered_containers_test(a, a, b); + ordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // deque std::deque a1(3, 100); std::deque a2(3, 100); std::deque b1(2, 200); ordered_containers_test(a1, a2, b1); + ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // deque SynthOrdered std::deque a = {10, 20, 30}; std::deque b = {10, 20, 40}; ordered_containers_test(a, a, b); + ordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // list std::list a1(3, 100); std::list a2(3, 100); std::list b1(2, 200); ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // list SynthOrdered std::list a = {10, 20, 30}; std::list b = {10, 20, 40}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // vector std::vector a1(3, 100); std::vector a2(3, 100); std::vector b1(2, 200); ordered_containers_test(a1, a2, b1); + ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // vector SynthOrdered std::vector a = {10, 20, 30}; std::vector b = {10, 20, 40}; ordered_containers_test(a, a, b); + ordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // vector std::vector c1 = {false, true, false}; std::vector c2 = {false, true, false}; std::vector d1 = {true, false}; ordered_containers_test(c1, c2, d1); + ordered_iterator_test(c1.begin(), c1.begin(), c1.end(), c1.cbegin(), c1.cbegin(), c1.cend()); } { // forward_list std::forward_list a1(3, 100); std::forward_list a2(3, 100); std::forward_list b1(2, 200); ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // forward_list SynthOrdered std::forward_list a = {10, 20, 30}; std::forward_list b = {10, 20, 40}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // map std::map a1; @@ -300,22 +333,26 @@ void ordering_test_cases() { b1["zoe"] = 3; b1["koala"] = 4; ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // map SynthOrdered std::map a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; std::map b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // multimap std::multimap a1 = {{'a', 1}, {'b', 2}, {'a', 3}}; std::multimap a2 = {{'a', 1}, {'a', 3}, {'b', 2}}; std::multimap b1 = {{'z', 4}, {'y', 90}, {'z', 12}}; ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // multimap SynthOrdered std::multimap a = {{10, 'z'}, {20, 'z'}, {30, 'z'}}; std::multimap b = {{10, 'z'}, {20, 'z'}, {40, 'z'}}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // set std::set a1; @@ -330,11 +367,13 @@ void ordering_test_cases() { b1.insert(30); b1.insert(40); ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // set SynthOrdered std::set a = {10, 20, 30}; std::set b = {10, 20, 40}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // multiset std::multiset a1; @@ -352,11 +391,13 @@ void ordering_test_cases() { b1.insert(40); b1.insert(40); ordered_containers_test(a1, a2, b1); + unordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } { // multiset SynthOrdered std::multiset a = {10, 20, 30}; std::multiset b = {10, 20, 40}; ordered_containers_test(a, a, b); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // unordered_map using stringmap = std::unordered_map; @@ -364,6 +405,7 @@ void ordering_test_cases() { stringmap b = {{"dog", "poodle"}, {"bear", "grizzly"}, {"cat", "tabby"}}; stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; unordered_containers_test(a, b, c); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // unordered_multimap using stringmap = std::unordered_multimap; @@ -371,18 +413,21 @@ void ordering_test_cases() { stringmap b = {{"dog", "poodle"}, {"cat", "siamese"}, {"cat", "tabby"}, {"dog", "poodle"}}; stringmap c = {{"cat", "siamese"}, {"dog", "lab"}, {"bear", "polar"}}; unordered_containers_test(a, b, c); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // unordered_set std::unordered_set a = {"cat", "dog", "bear"}; std::unordered_set b = {"bear", "cat", "dog"}; std::unordered_set c = {"mouse", "cat", "bear", "dog"}; unordered_containers_test(a, b, c); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // unordered_multiset std::unordered_multiset a = {"cat", "dog", "cat"}; std::unordered_multiset b = {"cat", "cat", "dog"}; std::unordered_multiset c = {"mouse", "cat", "bear", "dog"}; unordered_containers_test(a, b, c); + unordered_iterator_test(a.begin(), a.begin(), a.end(), a.cbegin(), a.cbegin(), a.cend()); } { // queue std::deque deq1(3, 100); @@ -414,6 +459,36 @@ void ordering_test_cases() { std::stack b{std::deque{10, 20, 40}}; ordered_containers_test(a, a, b); } + { // checked_array_iterator + int arr[] = {11, 22, 33}; + constexpr auto N = std::size(arr); + + using I = stdext::checked_array_iterator; + using CI = stdext::checked_array_iterator; // TRANSITION, GH-943, should be + + I first{arr, N}; + I last{arr, N, N}; + + CI cfirst{arr, N}; + CI clast{arr, N, N}; + + ordered_iterator_test(first, first, last, cfirst, cfirst, clast); + } + { // unchecked_array_iterator + int arr[] = {11, 22, 33}; + constexpr auto N = std::size(arr); + + using I = stdext::unchecked_array_iterator; + using CI = stdext::unchecked_array_iterator; // TRANSITION, GH-943, should be + + I first{arr}; + I last{arr + N}; + + CI cfirst{arr}; + CI clast{arr + N}; + + ordered_iterator_test(first, first, last, cfirst, cfirst, clast); + } { // sub_match const std::string s1{"cats"}; const std::string s2{"meow"}; @@ -501,6 +576,13 @@ void ordering_test_cases() { assert(("abcdef" <=> a1) == std::strong_ordering::equivalent); assert(("zebra" <=> a1) == std::strong_ordering::greater); } + { // string iterators + std::string a1 = "aaa"; + std::string a2 = "aaa"; + std::string b1 = "bb"; + ordered_containers_test(a1, a2, b1); + ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); + } { // string_view const std::string_view a1 = "abcdef"; const std::string_view a2 = "abcdef"; @@ -577,6 +659,13 @@ void ordering_test_cases() { static_assert(("abcdef" <=> a1) == std::strong_ordering::equivalent); static_assert(("zebra" <=> a1) == std::strong_ordering::greater); } + { // string_view iterators + std::string_view a1 = "aaa"; + std::string_view a2 = "aaa"; + std::string_view b1 = "bb"; + ordered_containers_test(a1, a2, b1); + ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); + } { // Diagnostics Library diagnostics_test(); diagnostics_test(); @@ -676,6 +765,7 @@ void ordering_test_cases() { const std::filesystem::path p3{R"(a/b/d)"}; spaceship_test(p1, p2, p3); + unordered_containers_test(p1.begin(), p1.begin(), p1.end()); } { // filesystem::file_status std::filesystem::file_status s1; From 88feca8055d6ae9c91037f0c8424d410c836ceb3 Mon Sep 17 00:00:00 2001 From: d-winsor Date: Fri, 19 Feb 2021 15:47:42 -0800 Subject: [PATCH 12/18] feature/spaceship: Clause 20: various synthesized equality operators (#1665) Co-authored-by: Stephan T. Lavavej --- stl/inc/functional | 2 + stl/inc/scoped_allocator | 2 + stl/inc/xmemory | 4 +- stl/inc/xpolymorphic_allocator.h | 4 + tests/std/tests/P1614R2_spaceship/test.cpp | 100 +++++++++++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) diff --git a/stl/inc/functional b/stl/inc/functional index 4a46439d871..a8aee539270 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,6 +1281,7 @@ _NODISCARD bool operator==(const function<_Fty>& _Other, nullptr_t) noexcept { return !_Other; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const function<_Fty>& _Other) noexcept { return !_Other; @@ -1295,6 +1296,7 @@ template _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { return static_cast(_Other); } +#endif // !_HAS_CXX20 // PLACEHOLDERS template diff --git a/stl/inc/scoped_allocator b/stl/inc/scoped_allocator index 76f95f24f36..e1defb79aab 100644 --- a/stl/inc/scoped_allocator +++ b/stl/inc/scoped_allocator @@ -259,12 +259,14 @@ _NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1>& _Left, return _Left.outer_allocator() == _Right.outer_allocator(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const scoped_allocator_adaptor<_Outer1, _Inner...>& _Left, const scoped_allocator_adaptor<_Outer2, _Inner...>& _Right) noexcept { // compare scoped_allocator_adaptors for equality return !(_Left == _Right); } +#endif // !_HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xmemory b/stl/inc/xmemory index c3fdd774d14..e83b30e271b 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -887,10 +887,12 @@ _NODISCARD _CONSTEXPR20_DYNALLOC bool operator==(const allocator<_Ty>&, const al return true; } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_DYNALLOC bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return false; } +#endif // !_HAS_CXX20 #if _HAS_CXX17 // ALIAS TEMPLATE _Guide_size_type_t FOR DEDUCTION GUIDES, N4687 26.5.4.1 [unord.map.overview]/4 diff --git a/stl/inc/xpolymorphic_allocator.h b/stl/inc/xpolymorphic_allocator.h index 47b1bdc68ae..481cc533313 100644 --- a/stl/inc/xpolymorphic_allocator.h +++ b/stl/inc/xpolymorphic_allocator.h @@ -168,9 +168,11 @@ namespace pmr { return &_Left == &_Right || _Left.is_equal(_Right); } +#if !_HAS_CXX20 _NODISCARD inline bool operator!=(const memory_resource& _Left, const memory_resource& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION get_default_resource extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Aligned_get_default_resource() noexcept; @@ -299,11 +301,13 @@ namespace pmr { return *_Left.resource() == *_Right.resource(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const polymorphic_allocator<_Ty1>& _Left, const polymorphic_allocator<_Ty2>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 } // namespace pmr diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 2bd59ee057b..6c8ce035a33 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -13,9 +13,13 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include #include @@ -231,6 +235,38 @@ void diagnostics_test() { } } +template +struct compare_resource final : std::pmr::memory_resource { +private: + void* do_allocate(size_t, size_t) override { + throw std::bad_alloc{}; + } + void do_deallocate(void*, size_t, size_t) override {} + bool do_is_equal(const memory_resource&) const noexcept override { + return Equal; + } +}; + +template +struct basic_compare_allocator : std::allocator { + template + struct rebind { + using other = basic_compare_allocator; + }; + + basic_compare_allocator() = default; + template + basic_compare_allocator(const basic_compare_allocator&) {} +}; + +template +bool operator==(const basic_compare_allocator&, const basic_compare_allocator&) { + return Equal; +} + +template +using compare_allocator = basic_compare_allocator; + void ordering_test_cases() { { // constexpr array constexpr std::array a0{{2, 8, 9, 1, 9}}; @@ -666,6 +702,70 @@ void ordering_test_cases() { ordered_containers_test(a1, a2, b1); ordered_iterator_test(a1.begin(), a1.begin(), a1.end(), a1.cbegin(), a1.cbegin(), a1.cend()); } + { // allocator + constexpr std::allocator a1; + constexpr std::allocator a2; + + static_assert(a1 == a1); + static_assert(a1 == a2); + static_assert(!(a1 != a1)); + static_assert(!(a1 != a2)); + } + { // memory_resource + compare_resource t1; + compare_resource& t2 = t1; + compare_resource f1; + compare_resource& f2 = f1; + + // same address + assert(t1 == t1); + assert(t1 == t2); + assert(f1 == f1); + assert(f1 == f2); + + // dependent on is_equal(__) + assert(t1 == f1); + assert(f1 != t1); + } + { // polymorphic_allocator + compare_resource rFalse; + compare_resource rTrue; + std::pmr::polymorphic_allocator p1 = &rFalse; + std::pmr::polymorphic_allocator p2 = p1; + std::pmr::polymorphic_allocator p3 = &rTrue; + + // same resource address + assert(p1 == p1); + assert(p1 == p2); + + // dependent on resource()::is_equal(__) + assert(p1 != p3); + assert(p3 == p1); + } + { // scoped_allocator_adaptor + // note: compare_allocator equality is based on the T of the RHS + std::scoped_allocator_adaptor> s1; + std::scoped_allocator_adaptor> s2; + std::scoped_allocator_adaptor> s3; + + assert(s1 == s1); + assert(s1 == s2); + assert(s1 != s3); + + std::scoped_allocator_adaptor, compare_allocator> s4{}; + std::scoped_allocator_adaptor, compare_allocator> s5{}; + + assert(s4 == s4); + assert(s5 != s5); + } + { // function + std::function f{}; + + assert(f == nullptr); + assert(nullptr == f); + assert(!(f != nullptr)); + assert(!(nullptr != f)); + } { // Diagnostics Library diagnostics_test(); diagnostics_test(); From aad4417612021d1456985c9ce5e42b54843446ab Mon Sep 17 00:00:00 2001 From: Anju del Moral Gonzalez Date: Fri, 19 Feb 2021 17:09:38 -0800 Subject: [PATCH 13/18] feature/spaceship: Clause 20: bitset, charconv, and typeindex (#1660) Co-authored-by: Stephan T. Lavavej --- stl/inc/bitset | 2 ++ stl/inc/charconv | 3 ++ stl/inc/typeindex | 8 ++++++ stl/inc/xcharconv.h | 3 ++ tests/std/tests/P1614R2_spaceship/test.cpp | 33 ++++++++++++++++++++++ 5 files changed, 49 insertions(+) diff --git a/stl/inc/bitset b/stl/inc/bitset index 8d21a2676ce..ff75bdd526b 100644 --- a/stl/inc/bitset +++ b/stl/inc/bitset @@ -370,9 +370,11 @@ public: return _CSTD memcmp(&_Array[0], &_Right._Array[0], sizeof(_Array)) == 0; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const bitset& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool test(size_t _Pos) const { if (_Bits <= _Pos) { diff --git a/stl/inc/charconv b/stl/inc/charconv index 9ab5d01ec81..bdb31a1b857 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -209,6 +209,9 @@ to_chars_result to_chars(char* _First, char* _Last, bool _Value, int _Base = 10) struct from_chars_result { const char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const from_chars_result&, const from_chars_result&) = default; +#endif // _HAS_CXX20 }; // FUNCTION from_chars (STRING TO INTEGER) diff --git a/stl/inc/typeindex b/stl/inc/typeindex index 0aa47537679..68d02e809f7 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -38,9 +38,17 @@ public: return *_Tptr == *_Right._Tptr; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const type_index& _Right) const noexcept { + return *_Tptr == *_Right._Tptr ? strong_ordering::equal + : _Tptr->before(*_Right._Tptr) ? strong_ordering::less + : strong_ordering::greater; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { return !(*this == _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD bool operator<(const type_index& _Right) const noexcept { return _Tptr->before(*_Right._Tptr); diff --git a/stl/inc/xcharconv.h b/stl/inc/xcharconv.h index ecd69b6d02e..fff630353ae 100644 --- a/stl/inc/xcharconv.h +++ b/stl/inc/xcharconv.h @@ -39,6 +39,9 @@ _BITMASK_OPS(chars_format) struct to_chars_result { char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const to_chars_result&, const to_chars_result&) = default; +#endif // _HAS_CXX20 }; _STD_END diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 6c8ce035a33..dd19714b162 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -574,6 +577,36 @@ void ordering_test_cases() { static_assert(std::is_same_v::comparison_category, std::strong_ordering>); static_assert(std::is_same_v::comparison_category, std::strong_ordering>); } + { // charconv + char c[7] = "123456"; + + std::from_chars_result a1{c + 6, std::errc{}}; + std::from_chars_result a2{c + 6, std::errc{}}; + std::from_chars_result a3{c + 6, std::errc::result_out_of_range}; + std::from_chars_result a4{c + 4, std::errc{}}; + + assert(a1 == a2); + assert(a1 != a3); + assert(a1 != a4); + + std::to_chars_result b1{c + 6, std::errc{}}; + std::to_chars_result b2{c + 6, std::errc{}}; + std::to_chars_result b3{c + 6, std::errc::value_too_large}; + std::to_chars_result b4{c + 4, std::errc{}}; + + assert(b1 == b2); + assert(b1 != b3); + assert(b1 != b4); + } + { // typeindex + std::type_index a1 = typeid(int); + std::type_index a2 = typeid(char); + std::type_index a3 = typeid(bool); + std::type_index a4 = typeid(int); + assert((a1 <=> a4) == std::strong_ordering::equal); + assert((a1 <=> a2) == std::strong_ordering::greater); // Implementation-specific assumption + assert((a1 <=> a3) == std::strong_ordering::less); // Implementation-specific assumption + } { // Strings library const std::string a1 = "abcdef"; const std::string a2 = "abcdef"; From d22f91693edf4b14f0771e2d355ddeb9bdcc6648 Mon Sep 17 00:00:00 2001 From: d-winsor Date: Mon, 22 Feb 2021 12:25:50 -0800 Subject: [PATCH 14/18] feature/spaceship: Clause 20: unique_ptr/shared_ptr (#1666) Co-authored-by: Stephan T. Lavavej --- stl/inc/memory | 44 ++++++++++++++++++++++ tests/std/tests/P1614R2_spaceship/test.cpp | 20 ++++++++++ tests/tr1/tests/memory1/test.cpp | 3 ++ 3 files changed, 67 insertions(+) diff --git a/stl/inc/memory b/stl/inc/memory index ae479ece7bb..8f84f1bc861 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -1795,6 +1795,13 @@ _NODISCARD bool operator==(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2> return _Left.get() == _Right.get(); } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { + return _Left.get() <=> _Right.get(); +} + +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() != _Right.get(); @@ -1819,12 +1826,20 @@ template _NODISCARD bool operator<=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template _NODISCARD bool operator==(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { return _Left.get() == nullptr; } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { + return _Left.get() <=> static_cast::element_type*>(nullptr); +} + +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return nullptr == _Right.get(); @@ -1879,6 +1894,7 @@ template _NODISCARD bool operator<=(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return static_cast::element_type*>(nullptr) <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Out, const shared_ptr<_Ty>& _Px) { @@ -3419,10 +3435,12 @@ _NODISCARD bool operator==(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return _Left.get() == _Right.get(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { @@ -3447,11 +3465,25 @@ _NODISCARD bool operator<=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable_with::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> +_NODISCARD compare_three_way_result_t::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> + operator<=>(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { + // clang-format on + return _Left.get() <=> _Right.get(); +} +#endif // __cpp_lib_concepts + template _NODISCARD bool operator==(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) noexcept { return !_Left; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !_Right; @@ -3466,6 +3498,7 @@ template _NODISCARD bool operator!=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t _Right) { @@ -3509,6 +3542,17 @@ _NODISCARD bool operator<=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable::pointer> +_NODISCARD compare_three_way_result_t::pointer> operator<=>( + const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) { + // clang-format on + return _Left.get() <=> static_cast::pointer>(nullptr); +} +#endif // __cpp_lib_concepts + template struct _Can_stream_unique_ptr : false_type {}; template diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index ed9ceceed4c..4d88d87262b 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -872,6 +872,26 @@ void ordering_test_cases() { assert((m1 <=> m2) == 0); static_assert((m1 <=> m2) == 0); } + { // unique_ptr + std::unique_ptr p1{nullptr}; + std::unique_ptr& p2 = p1; + std::unique_ptr p3{new int}; + + spaceship_test(p1, p2, p3); + spaceship_test(p1, nullptr, p3); + } + { // shared_ptr + std::shared_ptr p1{nullptr}; + std::shared_ptr& p2 = p1; // same managed ptr + std::shared_ptr p3 = p2; // same stored ptr + + std::shared_ptr p4{new int}; + std::shared_ptr p5 = p4; // same stored ptr + + spaceship_test(p1, p2, p4); + spaceship_test(p1, p3, p5); + spaceship_test(p1, nullptr, p4); + } { // slice std::slice a1(2, 3, 4); std::slice a2(2, 3, 4); diff --git a/tests/tr1/tests/memory1/test.cpp b/tests/tr1/tests/memory1/test.cpp index c27161919eb..695d941c98a 100644 --- a/tests/tr1/tests/memory1/test.cpp +++ b/tests/tr1/tests/memory1/test.cpp @@ -13,6 +13,9 @@ #include #include +// TRANSITION, VSO-1281038: False C6287 for C++20 synthesized operators +#pragma warning(disable : 6287) // Redundant code: the left and right sub-expressions are identical. + static void t_bad_weak_ptr() { // test bad_weak_ptr STD bad_weak_ptr ptr; STD exception* eptr = &ptr; From 0bed9f5a280a81e50a24f96dbbeacddf22aa2328 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 22 Feb 2021 13:23:47 -0800 Subject: [PATCH 15/18] feature/spaceship: Clause 17: Feature-test macro, yvals_core.h comment (#1672) --- stl/inc/yvals_core.h | 3 ++- tests/libcxx/expected_results.txt | 3 --- tests/libcxx/skipped_tests.txt | 3 --- .../VSO_0157762_feature_test_macros/test.compile.pass.cpp | 6 +++--- 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 988fb6bcfdd..a5e2e7b509e 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -211,6 +211,7 @@ // P1456R1 Move-Only Views // P1474R1 Helpful Pointers For contiguous_iterator // P1612R1 Relocating endian To +// P1614R2 Adding Spaceship <=> To The Library // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers @@ -1252,7 +1253,7 @@ #define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_three_way_comparison 201711L +#define __cpp_lib_three_way_comparison 201907L #endif // __cpp_lib_concepts #define __cpp_lib_to_address 201711L diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 24c698b3d62..c0d3f0eac74 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -320,9 +320,6 @@ std/language.support/support.limits/support.limits.general/functional.version.pa std/language.support/support.limits/support.limits.general/iterator.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -std/language.support/support.limits/support.limits.general/compare.version.pass.cpp FAIL - # C++23 P1048R1 "is_scoped_enum" std/utilities/meta/meta.unary/meta.unary.prop/is_scoped_enum.pass.cpp FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 4556745fa3e..0bbc0048e30 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -320,9 +320,6 @@ language.support\support.limits\support.limits.general\functional.version.pass.c language.support\support.limits\support.limits.general\iterator.version.pass.cpp language.support\support.limits\support.limits.general\memory.version.pass.cpp -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -language.support\support.limits\support.limits.general\compare.version.pass.cpp - # C++23 P1048R1 "is_scoped_enum" utilities\meta\meta.unary\meta.unary.prop\is_scoped_enum.pass.cpp diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index f6d8967ade9..d9b94d93205 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1420,10 +1420,10 @@ STATIC_ASSERT(__cpp_lib_syncbuf == 201803L); #if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_three_way_comparison #error __cpp_lib_three_way_comparison is not defined -#elif __cpp_lib_three_way_comparison != 201711L -#error __cpp_lib_three_way_comparison is not 201711L +#elif __cpp_lib_three_way_comparison != 201907L +#error __cpp_lib_three_way_comparison is not 201907L #else -STATIC_ASSERT(__cpp_lib_three_way_comparison == 201711L); +STATIC_ASSERT(__cpp_lib_three_way_comparison == 201907L); #endif #else #ifdef __cpp_lib_three_way_comparison From 5547f0f3389999dcd732d3316d621386df5a0531 Mon Sep 17 00:00:00 2001 From: Curtis J Bezault Date: Mon, 22 Feb 2021 13:59:45 -0800 Subject: [PATCH 16/18] feature/spaceship: Clause 20: pair, tuple, optional (#1206) Co-authored-by: Ahana Mukhopadhyay Co-authored-by: Casey Carter Co-authored-by: Curtis Bezault Co-authored-by: Stephan T. Lavavej --- stl/inc/compare | 29 +++ stl/inc/optional | 40 ++++ stl/inc/regex | 233 --------------------- stl/inc/tuple | 36 +++- stl/inc/utility | 44 ++-- tests/std/tests/P1614R2_spaceship/test.cpp | 160 +++++++++++--- 6 files changed, 251 insertions(+), 291 deletions(-) diff --git a/stl/inc/compare b/stl/inc/compare index 46bdfd0371f..ab47a879606 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -342,6 +342,35 @@ struct compare_three_way { }; // clang-format on +// STRUCT _Synth_three_way +struct _Synth_three_way { + // clang-format off + template + _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const + requires requires { + { _Left < _Right } -> _Boolean_testable; + { _Right < _Left } -> _Boolean_testable; + } + // clang-format on + { + if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { + return _Left <=> _Right; + } else { + if (_Left < _Right) { + return weak_ordering::less; + } else if (_Right < _Left) { + return weak_ordering::greater; + } else { + return weak_ordering::equivalent; + } + } + } +}; + +// ALIAS TEMPLATE _Synth_three_way_result +template +using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); + // Note: The following CPOs are passing arguments as lvalues; see GH-1374. // CUSTOMIZATION POINT OBJECT strong_order diff --git a/stl/inc/optional b/stl/inc/optional index 04e36c0c10b..81d2639a164 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -11,6 +11,9 @@ #if !_HAS_CXX17 #pragma message("The contents of are available only with C++17 or later.") #else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #include #include #include @@ -480,11 +483,30 @@ _NODISCARD constexpr bool operator>=(const optional<_Ty1>& _Left, const optional return !_Right.has_value() || (_Left.has_value() && *_Left >= *_Right); } +#ifdef __cpp_lib_concepts +template _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> operator<=>( + const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) { + if (_Left && _Right) { + return *_Left <=> *_Right; + } + + return _Left.has_value() <=> _Right.has_value(); +} +#endif // __cpp_lib_concepts + // COMPARISONS WITH nullopt [optional.nullops] template _NODISCARD constexpr bool operator==(const optional<_Ty>& _Left, nullopt_t) noexcept { return !_Left.has_value(); } + +#if _HAS_CXX20 +template +_NODISCARD constexpr strong_ordering operator<=>(const optional<_Ty>& _Left, nullopt_t) noexcept { + return _Left.has_value() <=> false; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD constexpr bool operator==(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); @@ -534,6 +556,7 @@ template _NODISCARD constexpr bool operator>=(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); } +#endif // !_HAS_CXX20 // COMPARISONS WITH T [optional.comp_with_t] template @@ -567,6 +590,7 @@ template = _NODISCARD constexpr bool operator==(const optional<_Ty1>& _Left, const _Ty2& _Right) { return _Left ? *_Left == _Right : false; } + template = 0> _NODISCARD constexpr bool operator==(const _Ty1& _Left, const optional<_Ty2>& _Right) { return _Right ? _Left == *_Right : false; @@ -617,6 +641,22 @@ _NODISCARD constexpr bool operator>=(const _Ty1& _Left, const optional<_Ty2>& _R return _Right ? _Left >= *_Right : true; } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (!_Is_specialization_v<_Ty2, optional>) // TRANSITION, GH-1674 + && three_way_comparable_with<_Ty1, _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> + operator<=>(const optional<_Ty1>& _Left, const _Ty2& _Right) { + // clang-format on + if (_Left) { + return *_Left <=> _Right; + } + + return strong_ordering::less; +} +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE swap [optional.specalg] template && is_swappable_v<_Ty>, int> = 0> void swap(optional<_Ty>& _Left, optional<_Ty>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/regex b/stl/inc/regex index 84ff2ce169d..2f2949e2683 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -730,35 +730,6 @@ 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) { @@ -797,70 +768,6 @@ 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) { @@ -930,70 +837,6 @@ _NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t< 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) { @@ -1064,82 +907,6 @@ _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) { - 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) < 0; -} - -template -_NODISCARD bool operator>( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - 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) <= 0; -} - -template -_NODISCARD bool operator>=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return (_Left <=> _Right) >= 0; -} -#endif // TRANSITION, VSO-900973 #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==( diff --git a/stl/inc/tuple b/stl/inc/tuple index d0734801e84..7fb6e3402fc 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -8,6 +8,9 @@ #define _TUPLE_ #include #if _STL_COMPILER_PREPROCESSOR +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts #include #include @@ -233,9 +236,15 @@ public: return true; } - constexpr bool _Less(const tuple&) const noexcept { +#ifdef __cpp_lib_concepts + _NODISCARD constexpr strong_ordering _Three_way_compare(const tuple&) const noexcept { + return strong_ordering::equal; + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD constexpr bool _Less(const tuple&) const noexcept { return false; } +#endif // !defined(__cpp_lib_concepts) }; template @@ -673,11 +682,23 @@ public: return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()); } +#ifdef __cpp_lib_concepts + template , // TRANSITION, DevCom-1344701 + _Synth_three_way_result<_Rest, _Other>...>> // (should be a normal or trailing return type) + _NODISCARD constexpr _Ret _Three_way_compare(const tuple<_First, _Other...>& _Right) const { + if (auto _Result = _Synth_three_way{}(_Myfirst._Val, _Right._Myfirst._Val); _Result != 0) { + return _Result; + } + return _Mybase::_Three_way_compare(_Right._Get_rest()); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template - constexpr bool _Less(const tuple<_Other...>& _Right) const { + _NODISCARD constexpr bool _Less(const tuple<_Other...>& _Right) const { return _Myfirst._Val < _Right._Myfirst._Val || (!(_Right._Myfirst._Val < _Myfirst._Val) && _Mybase::_Less(_Right._Get_rest())); } +#endif // !defined(__cpp_lib_concepts) template friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; @@ -733,10 +754,20 @@ _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple return _Left._Equals(_Right); } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Types1, _Types2>...> operator<=>( + const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { + static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); + return _Left._Three_way_compare(_Right); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { @@ -758,6 +789,7 @@ template _NODISCARD constexpr bool operator<=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Right < _Left); } +#endif // !defined(__cpp_lib_concepts) template ...>, int> = 0> _CONSTEXPR20 void swap(tuple<_Types...>& _Left, tuple<_Types...>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/utility b/stl/inc/utility index f14d1cf9bf5..54f6c0f149a 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -347,10 +347,22 @@ _NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_ return _Left.first == _Right.first && _Left.second == _Right.second; } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Ty1>, _Synth_three_way_result<_Ty2>> + operator<=>(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { + if (auto _Result = _Synth_three_way{}(_Left.first, _Right.first); _Result != 0) { + return _Result; + } + return _Synth_three_way{}(_Left.second, _Right.second); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { @@ -371,6 +383,7 @@ template _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left < _Right); } +#endif // !defined(__cpp_lib_concepts) // ALIAS TEMPLATE _Unrefwrap_t template @@ -700,37 +713,6 @@ _NODISCARD constexpr bool cmp_greater_equal(const _Ty1 _Left, const _Ty2 _Right) return !_STD cmp_less(_Left, _Right); } -#ifdef __cpp_lib_concepts -// STRUCT _Synth_three_way -struct _Synth_three_way { - // clang-format off - template - _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const - requires requires { - { _Left < _Right } -> _Boolean_testable; - { _Right < _Left } -> _Boolean_testable; - } - // clang-format on - { - if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { - return _Left <=> _Right; - } else { - if (_Left < _Right) { - return weak_ordering::less; - } else if (_Right < _Left) { - return weak_ordering::greater; - } else { - return weak_ordering::equivalent; - } - } - } -}; - -// ALIAS TEMPLATE _Synth_three_way_result -template -using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); -#endif // __cpp_lib_concepts - // FUNCTION TEMPLATE in_range template _NODISCARD _CONSTEVAL _Ty _Min_limit() noexcept { // same as (numeric_limits<_Ty>::min)(), less throughput cost diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 4d88d87262b..2b2e73691e5 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -40,9 +42,6 @@ #include #include -template -using SpaceshipType = decltype(std::declval() <=> std::declval()); - template concept HasSpaceshipWith = requires { std::declval() <=> std::declval(); @@ -173,15 +172,17 @@ constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller } template -inline constexpr bool is_pair = false; -template -inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented +inline constexpr bool has_synth_ordered = false; +template +inline constexpr bool has_synth_ordered> = true; +template <> +inline constexpr bool has_synth_ordered = true; 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) { + + if constexpr (has_synth_ordered) { spaceship_test(smaller, smaller_equal, larger); } else { spaceship_test(smaller, smaller_equal, larger); @@ -246,6 +247,83 @@ void diagnostics_test() { } } +template