From c2d5a62faf0fe95d65c3c86923473b6c1c635593 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 16 Nov 2024 17:17:48 +0800 Subject: [PATCH 1/4] Fix bogus pointer arithmetic with integer-class --- stl/inc/algorithm | 47 +++++---- tests/std/include/range_algorithm_support.hpp | 4 +- tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 95 +++++++++++++++++++ 5 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 tests/std/tests/GH_002885_stable_sort_difference_type/env.lst create mode 100644 tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 40f3f965d2f..883564e5b71 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -6600,7 +6600,9 @@ namespace ranges { if (_Count1 <= _Count2 && _Count1 <= _Capacity) { // buffer left range, then move parts _Uninitialized_backout*> _Backout{ - _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_First, _Mid, _Temp_ptr, _Temp_ptr + _Count1).out}; + _Temp_ptr, _RANGES _Uninitialized_move_unchecked( + _First, _Mid, _Temp_ptr, _Temp_ptr + static_cast(_Count1)) + .out}; const _It _New_mid = _RANGES _Move_unchecked(_STD move(_Mid), _STD move(_Last), _STD move(_First)).out; _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _New_mid); return _New_mid; @@ -6608,7 +6610,9 @@ namespace ranges { if (_Count2 <= _Capacity) { // buffer right range, then move parts _Uninitialized_backout*> _Backout{ - _Temp_ptr, _RANGES _Uninitialized_move_unchecked(_Mid, _Last, _Temp_ptr, _Temp_ptr + _Count2).out}; + _Temp_ptr, _RANGES _Uninitialized_move_unchecked( + _Mid, _Last, _Temp_ptr, _Temp_ptr + static_cast(_Count2)) + .out}; _RANGES _Move_backward_common(_First, _STD move(_Mid), _STD move(_Last)); return _RANGES _Move_unchecked(_Backout._First, _Backout._Last, _STD move(_First)).out; } @@ -8888,8 +8892,10 @@ namespace ranges { const iter_difference_t<_It> _Half_count_ceil = _Count - _Half_count; const _It _Mid = _First + _Half_count_ceil; if (_Half_count_ceil <= _Capacity) { // temp buffer big enough, sort each half using buffer - _Buffered_merge_sort_common(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Pred, _Proj); - _Buffered_merge_sort_common(_Mid, _Last, _Half_count, _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common( + _First, _Mid, static_cast(_Half_count_ceil), _Temp_ptr, _Pred, _Proj); + _Buffered_merge_sort_common( + _Mid, _Last, static_cast(_Half_count), _Temp_ptr, _Pred, _Proj); } else { // temp buffer not big enough, divide and conquer _Stable_sort_common_buffered(_First, _Mid, _Half_count_ceil, _Temp_ptr, _Capacity, _Pred, _Proj); _Stable_sort_common_buffered(_Mid, _Last, _Half_count, _Temp_ptr, _Capacity, _Pred, _Proj); @@ -8901,7 +8907,7 @@ namespace ranges { } template - static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const iter_difference_t<_It> _Count, + static void _Buffered_merge_sort_common(const _It _First, const _It _Last, ptrdiff_t _Count, iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) { // sort using temp buffer for merges // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t @@ -8911,14 +8917,14 @@ namespace ranges { _Insertion_sort_isort_max_chunks(_First, _Last, _Count, _Pred, _Proj); // merge adjacent pairs of chunks to and from temp buffer - if (_Count <= _Isort_max<_It>) { + if (_Count <= _ISORT_MAX) { return; } // do the first merge, constructing elements in the temporary buffer _Uninitialized_chunked_merge_common(_First, _Last, _Temp_ptr, _Count, _Pred, _Proj); _Uninitialized_backout*> _Backout{_Temp_ptr, _Temp_ptr + _Count}; - iter_difference_t<_It> _Chunk_size = _Isort_max<_It>; + ptrdiff_t _Chunk_size = _ISORT_MAX; for (;;) { // unconditionally merge elements back into the source buffer _Chunk_size <<= 1; @@ -8934,14 +8940,13 @@ namespace ranges { } template - static void _Insertion_sort_isort_max_chunks( - _It _First, _It _Last, iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + static void _Insertion_sort_isort_max_chunks(_It _First, _It _Last, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // insertion sort every chunk of distance _Isort_max<_It> in [_First, _Last) _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_RANGES distance(_First, _Last) == _Count); - for (; _Isort_max<_It> < _Count; _Count -= _Isort_max<_It>) { // sort chunks + for (; _ISORT_MAX < _Count; _Count -= _ISORT_MAX) { // sort chunks _First = _RANGES _Insertion_sort_common(_First, _First + _Isort_max<_It>, _Pred, _Proj); } @@ -8950,8 +8955,8 @@ namespace ranges { } template - static void _Uninitialized_chunked_merge_common(_It _First, const _It _Last, iter_value_t<_It>* const _Dest, - iter_difference_t<_It> _Count, _Pr _Pred, _Pj _Proj) { + static void _Uninitialized_chunked_merge_common( + _It _First, const _It _Last, iter_value_t<_It>* const _Dest, ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // move to uninitialized merging adjacent chunks of distance _Isort_max<_It> _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); @@ -8960,14 +8965,14 @@ namespace ranges { _Uninitialized_backout*> _Backout{_Dest}; const auto _Backout_end = _Dest + _Count; - while (_Isort_max<_It> < _Count) { - _Count -= _Isort_max<_It>; - const auto _Chunk2 = (_STD min)(_Isort_max<_It>, _Count); + while (_ISORT_MAX < _Count) { + _Count -= _ISORT_MAX; + const auto _Chunk2 = (_STD min)(static_cast(_ISORT_MAX), _Count); _Count -= _Chunk2; auto _Mid1 = _First + _Isort_max<_It>; - auto _Last1 = _Mid1 + _Chunk2; - auto _Last2 = _Backout._Last + _Isort_max<_It> + _Chunk2; + auto _Last1 = _Mid1 + static_cast>(_Chunk2); + auto _Last2 = _Backout._Last + _ISORT_MAX + _Chunk2; _Backout._Last = _Uninitialized_merge_move( _STD move(_First), _STD move(_Mid1), _Last1, _Backout._Last, _Last2, _Pred, _Proj); _First = _STD move(_Last1); @@ -9047,8 +9052,8 @@ namespace ranges { } template - static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, - const iter_difference_t<_It1> _Chunk_size, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj _Proj) { + static void _Chunked_merge_common(_It1 _First, const _It1 _Last, _It2 _Dest, const ptrdiff_t _Chunk_size, + ptrdiff_t _Count, _Pr _Pred, _Pj _Proj) { // move merging adjacent chunks of distance _Chunk_size _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It1, _Pr, _Pj>); @@ -9061,8 +9066,8 @@ namespace ranges { const auto _Right_chunk_size = (_STD min)(_Chunk_size, _Count); _Count -= _Right_chunk_size; - auto _Mid1 = _First + _Chunk_size; - auto _Last1 = _Mid1 + _Right_chunk_size; + auto _Mid1 = _First + static_cast>(_Chunk_size); + auto _Last1 = _Mid1 + static_cast>(_Right_chunk_size); _Dest = _Merge_move_common(_STD move(_First), _STD move(_Mid1), _Last1, _Dest, _Pred, _Proj); _First = _STD move(_Last1); } diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d4f430bfbbb..056b84b2982 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1163,10 +1163,10 @@ namespace test { if constexpr (is_sized) { const auto sz = to_unsigned(static_cast(ranges::distance(r))); return ranges::subrange{ - rediff_iter{r.begin()}, rediff_sent{r.end()}, sz}; + rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}, sz}; } else { return ranges::subrange{ - rediff_iter{r.begin()}, rediff_sent{r.end()}}; + rediff_iter{ranges::begin(r)}, rediff_sent{ranges::end(r)}}; } } } // namespace test diff --git a/tests/std/test.lst b/tests/std/test.lst index 94f80f9dce5..42163db9973 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -221,6 +221,7 @@ tests\GH_002711_Zc_alignedNew- tests\GH_002760_syncstream_memory_leak tests\GH_002769_handle_deque_block_pointers tests\GH_002789_Hash_vec_Tidy +tests\GH_002885_stable_sort_difference_type tests\GH_002989_nothrow_unwrappable tests\GH_002992_unwrappable_iter_sent_pairs tests\GH_003003_format_decimal_point diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst b/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst new file mode 100644 index 00000000000..351a8293d9d --- /dev/null +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_20_matrix.lst diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp new file mode 100644 index 00000000000..5a23cd2f55a --- /dev/null +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp @@ -0,0 +1,95 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include "range_algorithm_support.hpp" + +using namespace std; + +constexpr auto pred = [](int i) { return i <= 42; }; + +template +void test_iota_transform() { + constexpr int orig[]{42, 1729}; + int a[]{42, 1729}; + auto vw = views::iota(I{}, static_cast(ranges::size(a))) | views::transform([&a](I i) -> auto& { return a[i]; }); + + ranges::stable_sort(vw); + assert(ranges::equal(a, orig)); + + ranges::stable_sort(vw.begin(), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::inplace_merge(vw, ranges::next(vw.begin())); + assert(ranges::equal(a, orig)); + ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::stable_partition(vw, pred); + assert(ranges::equal(a, orig)); + ranges::stable_partition(vw.begin(), vw.end(), pred); + assert(ranges::equal(a, orig)); +} + +void test_iota_transform_all() { + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); + + test_iota_transform(); +#ifdef __cpp_char8_t + test_iota_transform(); +#endif // defined(__cpp_char8_t) + test_iota_transform(); + test_iota_transform(); + test_iota_transform(); +} + +template +void test_redifference() { + constexpr int orig[]{42, 1729}; + int a[]{42, 1729}; + auto vw = test::make_redifference_subrange(a); + + ranges::stable_sort(vw); + assert(ranges::equal(a, orig)); + + ranges::stable_sort(vw.begin(), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::inplace_merge(vw, ranges::next(vw.begin())); + assert(ranges::equal(a, orig)); + ranges::inplace_merge(vw.begin(), ranges::next(vw.begin()), vw.end()); + assert(ranges::equal(a, orig)); + + ranges::stable_partition(vw, pred); + assert(ranges::equal(a, orig)); + ranges::stable_partition(vw.begin(), vw.end(), pred); + assert(ranges::equal(a, orig)); +} + +void test_redifference_all() { + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference(); + test_redifference<_Signed128>(); +} + +int main() { + test_iota_transform_all(); + test_redifference_all(); +} From 8a4d4afd1fdc71f336396141cb078d13a844f6ce Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 16 Nov 2024 20:11:35 +0800 Subject: [PATCH 2/4] Make Clang happy --- .../std/tests/GH_002885_stable_sort_difference_type/test.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp index 5a23cd2f55a..ae626e22b10 100644 --- a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -16,7 +17,8 @@ template void test_iota_transform() { constexpr int orig[]{42, 1729}; int a[]{42, 1729}; - auto vw = views::iota(I{}, static_cast(ranges::size(a))) | views::transform([&a](I i) -> auto& { return a[i]; }); + auto vw = views::iota(I{}, static_cast(ranges::size(a))) + | views::transform([&a](I i) -> auto& { return a[static_cast(i)]; }); ranges::stable_sort(vw); assert(ranges::equal(a, orig)); From 0b41e8877d3c4951c52f7da1f70a949fa3b6b136 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 16 Nov 2024 20:55:24 +0800 Subject: [PATCH 3/4] Make `/permissive` happy --- tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp index ae626e22b10..34fe497513c 100644 --- a/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp +++ b/tests/std/tests/GH_002885_stable_sort_difference_type/test.cpp @@ -20,6 +20,8 @@ void test_iota_transform() { auto vw = views::iota(I{}, static_cast(ranges::size(a))) | views::transform([&a](I i) -> auto& { return a[static_cast(i)]; }); + static_assert(three_way_comparable>>); // TRANSITION, /permissive + ranges::stable_sort(vw); assert(ranges::equal(a, orig)); From 0a8f7ca906529f5efc9597f973d2630144904adc Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Sat, 16 Nov 2024 17:53:49 -0800 Subject: [PATCH 4/4] Casey's review comments --- stl/inc/algorithm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 883564e5b71..b999ad605c9 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -8907,10 +8907,10 @@ namespace ranges { } template - static void _Buffered_merge_sort_common(const _It _First, const _It _Last, ptrdiff_t _Count, + static void _Buffered_merge_sort_common(const _It _First, const _It _Last, const ptrdiff_t _Count, iter_value_t<_It>* const _Temp_ptr, _Pr _Pred, _Pj _Proj) { // sort using temp buffer for merges - // pre: _Count <= capacity of buffer at _Temp_ptr; also allows safe narrowing to ptrdiff_t + // pre: _Count <= capacity of buffer at _Temp_ptr _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_Last - _First == _Count);