diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index aa896e85fcb1f..65fd335a0309f 100644 --- a/libcxx/docs/Status/Cxx2cPapers.csv +++ b/libcxx/docs/Status/Cxx2cPapers.csv @@ -2,7 +2,7 @@ "`P2497R0 `__","Testing for success or failure of ```` functions","2023-06 (Varna)","|Complete|","18","" "`P2592R3 `__","Hashing support for ``std::chrono`` value classes","2023-06 (Varna)","","","" "`P2587R3 `__","``to_string`` or not ``to_string``","2023-06 (Varna)","","","" -"`P2562R1 `__","``constexpr`` Stable Sorting","2023-06 (Varna)","","","" +"`P2562R1 `__","``constexpr`` Stable Sorting","2023-06 (Varna)","|Partial|","20.0","" "`P2545R4 `__","Read-Copy Update (RCU)","2023-06 (Varna)","","","" "`P2530R3 `__","Hazard Pointers for C++26","2023-06 (Varna)","","","" "`P2538R1 `__","ADL-proof ``std::projected``","2023-06 (Varna)","|Complete|","18","" diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h index 69213cc1457be..1fc31b66f4bd6 100644 --- a/libcxx/include/__algorithm/inplace_merge.h +++ b/libcxx/include/__algorithm/inplace_merge.h @@ -44,17 +44,17 @@ class __invert // invert the sense of a comparison _Predicate __p_; public: - _LIBCPP_HIDE_FROM_ABI __invert() {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __invert() {} - _LIBCPP_HIDE_FROM_ABI explicit __invert(_Predicate __p) : __p_(__p) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __invert(_Predicate __p) : __p_(__p) {} template - _LIBCPP_HIDE_FROM_ABI bool operator()(const _T1& __x) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const _T1& __x) { return !__p_(__x); } template - _LIBCPP_HIDE_FROM_ABI bool operator()(const _T1& __x, const _T2& __y) { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool operator()(const _T1& __x, const _T2& __y) { return __p_(__y, __x); } }; @@ -66,7 +66,7 @@ template -_LIBCPP_HIDE_FROM_ABI void __half_inplace_merge( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __half_inplace_merge( _InputIterator1 __first1, _Sent1 __last1, _InputIterator2 __first2, @@ -91,7 +91,7 @@ _LIBCPP_HIDE_FROM_ABI void __half_inplace_merge( } template -_LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __buffered_inplace_merge( _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, @@ -122,7 +122,7 @@ _LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge( } template -void __inplace_merge( +_LIBCPP_CONSTEXPR_SINCE_CXX26 void __inplace_merge( _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h index 5c60b23931cca..8dd0721f2c65f 100644 --- a/libcxx/include/__algorithm/sort.h +++ b/libcxx/include/__algorithm/sort.h @@ -240,7 +240,7 @@ __selection_sort(_BidirectionalIterator __first, _BidirectionalIterator __last, // Sort the iterator range [__first, __last) using the comparator __comp using // the insertion sort algorithm. template -_LIBCPP_HIDE_FROM_ABI void +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insertion_sort(_BidirectionalIterator __first, _BidirectionalIterator __last, _Compare __comp) { using _Ops = _IterOps<_AlgPolicy>; diff --git a/libcxx/include/__algorithm/stable_sort.h b/libcxx/include/__algorithm/stable_sort.h index 70a85023a17f0..3cfbcf08d2c5c 100644 --- a/libcxx/include/__algorithm/stable_sort.h +++ b/libcxx/include/__algorithm/stable_sort.h @@ -19,6 +19,7 @@ #include <__cstddef/ptrdiff_t.h> #include <__debug_utils/strict_weak_ordering_check.h> #include <__iterator/iterator_traits.h> +#include <__memory/construct_at.h> #include <__memory/destruct_n.h> #include <__memory/unique_ptr.h> #include <__memory/unique_temporary_buffer.h> @@ -41,7 +42,7 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_STD template -_LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __insertion_sort_move( _BidirectionalIterator __first1, _BidirectionalIterator __last1, typename iterator_traits<_BidirectionalIterator>::value_type* __first2, @@ -53,19 +54,19 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( __destruct_n __d(0); unique_ptr __h(__first2, __d); value_type* __last2 = __first2; - ::new ((void*)__last2) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__last2, _Ops::__iter_move(__first1)); __d.template __incr(); for (++__last2; ++__first1 != __last1; ++__last2) { value_type* __j2 = __last2; value_type* __i2 = __j2; if (__comp(*__first1, *--__i2)) { - ::new ((void*)__j2) value_type(std::move(*__i2)); + std::__construct_at(__j2, std::move(*__i2)); __d.template __incr(); for (--__j2; __i2 != __first2 && __comp(*__first1, *--__i2); --__j2) *__j2 = std::move(*__i2); *__j2 = _Ops::__iter_move(__first1); } else { - ::new ((void*)__j2) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__j2, _Ops::__iter_move(__first1)); __d.template __incr(); } } @@ -74,7 +75,7 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( } template -_LIBCPP_HIDE_FROM_ABI void __merge_move_construct( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __merge_move_construct( _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, @@ -89,22 +90,22 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct( for (; true; ++__result) { if (__first1 == __last1) { for (; __first2 != __last2; ++__first2, (void)++__result, __d.template __incr()) - ::new ((void*)__result) value_type(_Ops::__iter_move(__first2)); + std::__construct_at(__result, _Ops::__iter_move(__first2)); __h.release(); return; } if (__first2 == __last2) { for (; __first1 != __last1; ++__first1, (void)++__result, __d.template __incr()) - ::new ((void*)__result) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__result, _Ops::__iter_move(__first1)); __h.release(); return; } if (__comp(*__first2, *__first1)) { - ::new ((void*)__result) value_type(_Ops::__iter_move(__first2)); + std::__construct_at(__result, _Ops::__iter_move(__first2)); __d.template __incr(); ++__first2; } else { - ::new ((void*)__result) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__result, _Ops::__iter_move(__first1)); __d.template __incr(); ++__first1; } @@ -112,7 +113,7 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct( } template -_LIBCPP_HIDE_FROM_ABI void __merge_move_assign( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __merge_move_assign( _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, @@ -140,19 +141,21 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_assign( } template -void __stable_sort(_RandomAccessIterator __first, - _RandomAccessIterator __last, - _Compare __comp, - typename iterator_traits<_RandomAccessIterator>::difference_type __len, - typename iterator_traits<_RandomAccessIterator>::value_type* __buff, - ptrdiff_t __buff_size); +_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort( + _RandomAccessIterator __first, + _RandomAccessIterator __last, + _Compare __comp, + typename iterator_traits<_RandomAccessIterator>::difference_type __len, + typename iterator_traits<_RandomAccessIterator>::value_type* __buff, + ptrdiff_t __buff_size); template -void __stable_sort_move(_RandomAccessIterator __first1, - _RandomAccessIterator __last1, - _Compare __comp, - typename iterator_traits<_RandomAccessIterator>::difference_type __len, - typename iterator_traits<_RandomAccessIterator>::value_type* __first2) { +_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort_move( + _RandomAccessIterator __first1, + _RandomAccessIterator __last1, + _Compare __comp, + typename iterator_traits<_RandomAccessIterator>::difference_type __len, + typename iterator_traits<_RandomAccessIterator>::value_type* __first2) { using _Ops = _IterOps<_AlgPolicy>; typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type; @@ -160,21 +163,21 @@ void __stable_sort_move(_RandomAccessIterator __first1, case 0: return; case 1: - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__first2, _Ops::__iter_move(__first1)); return; case 2: __destruct_n __d(0); unique_ptr __h2(__first2, __d); if (__comp(*--__last1, *__first1)) { - ::new ((void*)__first2) value_type(_Ops::__iter_move(__last1)); + std::__construct_at(__first2, _Ops::__iter_move(__last1)); __d.template __incr(); ++__first2; - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__first2, _Ops::__iter_move(__first1)); } else { - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + std::__construct_at(__first2, _Ops::__iter_move(__first1)); __d.template __incr(); ++__first2; - ::new ((void*)__first2) value_type(_Ops::__iter_move(__last1)); + std::__construct_at(__first2, _Ops::__iter_move(__last1)); } __h2.release(); return; @@ -218,12 +221,13 @@ _LIBCPP_HIDE_FROM_ABI constexpr unsigned __radix_sort_max_bound() { #endif // _LIBCPP_STD_VER >= 17 template -void __stable_sort(_RandomAccessIterator __first, - _RandomAccessIterator __last, - _Compare __comp, - typename iterator_traits<_RandomAccessIterator>::difference_type __len, - typename iterator_traits<_RandomAccessIterator>::value_type* __buff, - ptrdiff_t __buff_size) { +_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort( + _RandomAccessIterator __first, + _RandomAccessIterator __last, + _Compare __comp, + typename iterator_traits<_RandomAccessIterator>::difference_type __len, + typename iterator_traits<_RandomAccessIterator>::value_type* __buff, + ptrdiff_t __buff_size) { typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type; typedef typename iterator_traits<_RandomAccessIterator>::difference_type difference_type; switch (__len) { @@ -279,7 +283,7 @@ void __stable_sort(_RandomAccessIterator __first, } template -inline _LIBCPP_HIDE_FROM_ABI void +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare& __comp) { using value_type = typename iterator_traits<_RandomAccessIterator>::value_type; using difference_type = typename iterator_traits<_RandomAccessIterator>::difference_type; @@ -298,18 +302,18 @@ __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, } template -inline _LIBCPP_HIDE_FROM_ABI void +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Compare __comp) { std::__stable_sort_impl<_ClassicAlgPolicy>(std::move(__first), std::move(__last), __comp); } template -inline _LIBCPP_HIDE_FROM_ABI void stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void +stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { std::stable_sort(__first, __last, __less<>()); } _LIBCPP_END_NAMESPACE_STD - _LIBCPP_POP_MACROS #endif // _LIBCPP___ALGORITHM_STABLE_SORT_H diff --git a/libcxx/include/__memory/destruct_n.h b/libcxx/include/__memory/destruct_n.h index 66adefb0f51fc..db227a4ea1dc7 100644 --- a/libcxx/include/__memory/destruct_n.h +++ b/libcxx/include/__memory/destruct_n.h @@ -25,35 +25,35 @@ struct __destruct_n { size_t __size_; template - _LIBCPP_HIDE_FROM_ABI void __process(_Tp* __p, false_type) _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __process(_Tp* __p, false_type) _NOEXCEPT { for (size_t __i = 0; __i < __size_; ++__i, ++__p) __p->~_Tp(); } template - _LIBCPP_HIDE_FROM_ABI void __process(_Tp*, true_type) _NOEXCEPT {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __process(_Tp*, true_type) _NOEXCEPT {} - _LIBCPP_HIDE_FROM_ABI void __incr(false_type) _NOEXCEPT { ++__size_; } - _LIBCPP_HIDE_FROM_ABI void __incr(true_type) _NOEXCEPT {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr(false_type) _NOEXCEPT { ++__size_; } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr(true_type) _NOEXCEPT {} - _LIBCPP_HIDE_FROM_ABI void __set(size_t __s, false_type) _NOEXCEPT { __size_ = __s; } - _LIBCPP_HIDE_FROM_ABI void __set(size_t, true_type) _NOEXCEPT {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t __s, false_type) _NOEXCEPT { __size_ = __s; } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t, true_type) _NOEXCEPT {} public: - _LIBCPP_HIDE_FROM_ABI explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {} template - _LIBCPP_HIDE_FROM_ABI void __incr() _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr() _NOEXCEPT { __incr(integral_constant::value>()); } template - _LIBCPP_HIDE_FROM_ABI void __set(size_t __s, _Tp*) _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __set(size_t __s, _Tp*) _NOEXCEPT { __set(__s, integral_constant::value>()); } template - _LIBCPP_HIDE_FROM_ABI void operator()(_Tp* __p) _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void operator()(_Tp* __p) _NOEXCEPT { __process(__p, integral_constant::value>()); } }; diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index e593ae26ed6e2..7b4cb8e496196 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -1530,11 +1530,11 @@ template sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp); template - void + constexpr void // constexpr in C++26 stable_sort(RandomAccessIterator first, RandomAccessIterator last); template - void + constexpr void // constexpr in C++26 stable_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp); template diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 69f1b7d094ada..cdac9c883ecab 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -819,7 +819,10 @@ module std [system] { module sort_heap { header "__algorithm/sort_heap.h" } module sort { header "__algorithm/sort.h" } module stable_partition { header "__algorithm/stable_partition.h" } - module stable_sort { header "__algorithm/stable_sort.h" } + module stable_sort { + header "__algorithm/stable_sort.h" + export std.memory.unique_temporary_buffer // TODO: Workaround for https://github.com/llvm/llvm-project/issues/120108 + } module swap_ranges { header "__algorithm/swap_ranges.h" } module three_way_comp_ref_type { header "__algorithm/three_way_comp_ref_type.h" } module transform { header "__algorithm/transform.h" } diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp index 621234354092d..b3b458808c44a 100644 --- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp @@ -8,11 +8,12 @@ // -// template -// requires ShuffleIterator -// && LessThanComparable -// void -// stable_sort(Iter first, Iter last); +// template +// constexpr void // constexpr since C++26 +// stable_sort(RandomAccessIterator first, RandomAccessIterator last); + +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=200000000 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=200000000 #include #include @@ -23,156 +24,181 @@ #include "count_new.h" #include "test_macros.h" -std::mt19937 randomness; - -template -void -test_sort_helper(RI f, RI l) -{ - typedef typename std::iterator_traits::value_type value_type; - typedef typename std::iterator_traits::difference_type difference_type; - - if (f != l) - { - difference_type len = l - f; - value_type* save(new value_type[len]); - do - { - std::copy(f, l, save); - std::stable_sort(save, save+len); - assert(std::is_sorted(save, save+len)); - } while (std::next_permutation(f, l)); - delete [] save; - } +template +TEST_CONSTEXPR_CXX26 void test_all_permutations(Iterator first, Iterator last) { + using T = typename std::iterator_traits::value_type; + + do { + std::vector save(first, last); + std::stable_sort(save.begin(), save.end()); + assert(std::is_sorted(save.begin(), save.end())); + } while (std::next_permutation(first, last)); } -template -void -test_sort_driver_driver(RI f, RI l, int start, RI real_last) -{ - using value_type = typename std::iterator_traits::value_type; +template +TEST_CONSTEXPR_CXX26 void test_sort_exhaustive_impl(Iterator first, Iterator last, int start, Iterator real_last) { + using T = typename std::iterator_traits::value_type; - for (RI i = l; i > f + start;) { - *--i = static_cast(start); - if (f == i) { - test_sort_helper(f, real_last); + for (Iterator i = last; i > first + start;) { + *--i = static_cast(start); + if (first == i) { + test_all_permutations(first, real_last); } if (start > 0) - test_sort_driver_driver(f, i, start-1, real_last); + test_sort_exhaustive_impl(first, i, start - 1, real_last); } } -template -void -test_sort_driver(RI f, RI l, int start) -{ - test_sort_driver_driver(f, l, start, l); -} - -template -void test_sort_() { - V ia[sa]; - for (int i = 0; i < sa; ++i) { - test_sort_driver(ia, ia + sa, i); +template +TEST_CONSTEXPR_CXX26 void test_sort_exhaustive(int N) { + std::vector vec; + vec.resize(N); + for (int i = 0; i < N; ++i) { + test_sort_exhaustive_impl(vec.begin(), vec.end(), i, vec.end()); } } -template -void test_sort_() { - test_sort_(); - test_sort_(); -} - -template -void test_larger_sorts(int N, int M) { - assert(N != 0); - assert(M != 0); - // create array length N filled with M different numbers - V* array = new V[N]; - int x = 0; +template +TEST_CONSTEXPR_CXX26 std::vector generate_sawtooth(int N, int M) { + // Populate a sequence of length N with M different numbers + std::vector v; + T x = 0; for (int i = 0; i < N; ++i) { - array[i] = static_cast(x); + v.push_back(x); if (++x == M) x = 0; } + return v; +} + +template +TEST_CONSTEXPR_CXX26 void test_larger_sorts(int N, int M) { + assert(N != 0); + assert(M != 0); + // test saw tooth pattern - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); + { + auto v = generate_sawtooth(N, M); + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } + // test random pattern - std::shuffle(array, array + N, randomness); - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); + { + if (!TEST_IS_CONSTANT_EVALUATED) { + auto v = generate_sawtooth(N, M); + std::mt19937 randomness; + std::shuffle(v.begin(), v.end(), randomness); + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } + } + // test sorted pattern - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); + { + auto v = generate_sawtooth(N, M); + std::sort(v.begin(), v.end()); + + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } + // test reverse sorted pattern - std::reverse(array, array + N); - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); + { + auto v = generate_sawtooth(N, M); + std::sort(v.begin(), v.end()); + std::reverse(v.begin(), v.end()); + + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } + // test swap ranges 2 pattern - std::swap_ranges(array, array + N / 2, array + N / 2); - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); + { + auto v = generate_sawtooth(N, M); + std::sort(v.begin(), v.end()); + std::swap_ranges(v.begin(), v.begin() + (N / 2), v.begin() + (N / 2)); + + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } + // test reverse swap ranges 2 pattern - std::reverse(array, array + N); - std::swap_ranges(array, array + N / 2, array + N / 2); - std::stable_sort(array, array + N); - assert(std::is_sorted(array, array + N)); - delete[] array; + { + auto v = generate_sawtooth(N, M); + std::sort(v.begin(), v.end()); + std::reverse(v.begin(), v.end()); + std::swap_ranges(v.begin(), v.begin() + (N / 2), v.begin() + (N / 2)); + + std::stable_sort(v.begin(), v.end()); + assert(std::is_sorted(v.begin(), v.end())); + } } -void test_larger_sorts(int N, int M) { - test_larger_sorts(N, M); - test_larger_sorts(N, M); +template +TEST_CONSTEXPR_CXX26 void test_larger_sorts(int N) { + test_larger_sorts(N, 1); + test_larger_sorts(N, 2); + test_larger_sorts(N, 3); + test_larger_sorts(N, N / 2 - 1); + test_larger_sorts(N, N / 2); + test_larger_sorts(N, N / 2 + 1); + test_larger_sorts(N, N - 2); + test_larger_sorts(N, N - 1); + test_larger_sorts(N, N); } -void -test_larger_sorts(int N) -{ - test_larger_sorts(N, 1); - test_larger_sorts(N, 2); - test_larger_sorts(N, 3); - test_larger_sorts(N, N/2-1); - test_larger_sorts(N, N/2); - test_larger_sorts(N, N/2+1); - test_larger_sorts(N, N-2); - test_larger_sorts(N, N-1); - test_larger_sorts(N, N); -} +template +TEST_CONSTEXPR_CXX26 bool test() { + // test null range + { + T value = 0; + std::stable_sort(&value, &value); + } -int main(int, char**) -{ - // test null range - int d = 0; - std::stable_sort(&d, &d); - // exhaustively test all possibilities up to length 8 - test_sort_<1>(); - test_sort_<2>(); - test_sort_<3>(); - test_sort_<4>(); - test_sort_<5>(); - test_sort_<6>(); - test_sort_<7>(); - test_sort_<8>(); - - test_larger_sorts(256); - test_larger_sorts(257); - test_larger_sorts(499); - test_larger_sorts(500); - test_larger_sorts(997); - test_larger_sorts(1000); - test_larger_sorts(1009); - test_larger_sorts(1024); - test_larger_sorts(1031); - test_larger_sorts(2053); - -#if !defined(TEST_HAS_NO_EXCEPTIONS) - { // check that the algorithm works without memory - std::vector vec(150, 3); - getGlobalMemCounter()->throw_after = 0; - std::stable_sort(vec.begin(), vec.end()); - } -#endif // !defined(TEST_HAS_NO_EXCEPTIONS) + // exhaustively test all possibilities up to length 8 + if (!TEST_IS_CONSTANT_EVALUATED) { + test_sort_exhaustive(1); + test_sort_exhaustive(2); + test_sort_exhaustive(3); + test_sort_exhaustive(4); + test_sort_exhaustive(5); + test_sort_exhaustive(6); + test_sort_exhaustive(7); + test_sort_exhaustive(8); + } + + test_larger_sorts(256); + test_larger_sorts(257); + if (!TEST_IS_CONSTANT_EVALUATED) { // avoid blowing past constexpr evaluation limit + test_larger_sorts(499); + test_larger_sorts(500); + test_larger_sorts(997); + test_larger_sorts(1000); + test_larger_sorts(1009); + test_larger_sorts(1024); + test_larger_sorts(1031); + test_larger_sorts(2053); + } + + // check that the algorithm works without memory +#ifndef TEST_HAS_NO_EXCEPTIONS + if (!TEST_IS_CONSTANT_EVALUATED) { + std::vector vec(150, T(3)); + getGlobalMemCounter()->throw_after = 0; + std::stable_sort(vec.begin(), vec.end()); + } +#endif + + return true; +} +int main(int, char**) { + test(); + test(); +#if TEST_STD_VER >= 26 + static_assert(test()); + static_assert(test()); +#endif return 0; } diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort_comp.pass.cpp index 5ee6d89064941..a2c0ca747d039 100644 --- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort_comp.pass.cpp +++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort_comp.pass.cpp @@ -9,10 +9,11 @@ // // template Compare> -// requires ShuffleIterator -// && CopyConstructible -// void -// stable_sort(Iter first, Iter last, Compare comp); +// requires ShuffleIterator && CopyConstructible +// constexpr void stable_sort(Iter first, Iter last, Compare comp); // constexpr since C++26 +// +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=200000000 +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=200000000 #include #include @@ -27,57 +28,79 @@ struct indirect_less { template - bool operator()(const P& x, const P& y) const { + TEST_CONSTEXPR_CXX26 bool operator()(const P& x, const P& y) const { return *x < *y; } }; -std::mt19937 randomness; - struct first_only { - bool operator()(const std::pair& x, const std::pair& y) const { return x.first < y.first; } + TEST_CONSTEXPR_CXX26 bool operator()(const std::pair& x, const std::pair& y) const { + return x.first < y.first; + } }; -void test() -{ - typedef std::pair P; - const int N = 1000; - const int M = 10; - std::vector

v(N); - int x = 0; - int ver = 0; - for (int i = 0; i < N; ++i) - { - v[i] = P(x, ver); - if (++x == M) - { - x = 0; - ++ver; - } - } - for (int i = 0; i < N - M; i += M) - { - std::shuffle(v.begin() + i, v.begin() + i + M, randomness); +using Pair = std::pair; + +TEST_CONSTEXPR_CXX26 std::vector generate_sawtooth(int N, int M) { + std::vector v(N); + int x = 0; + int ver = 0; + for (int i = 0; i < N; ++i) { + v[i] = Pair(x, ver); + if (++x == M) { + x = 0; + ++ver; } + } + return v; +} + +TEST_CONSTEXPR_CXX26 bool test() { + int const N = 1000; + int const M = 10; + + // test sawtooth pattern + { + auto v = generate_sawtooth(N, M); std::stable_sort(v.begin(), v.end(), first_only()); assert(std::is_sorted(v.begin(), v.end())); -} + } -int main(int, char**) -{ - test(); + // Test sorting a sequence where subsequences of elements are not sorted with <, + // but everything is already sorted with respect to the first element. This ensures + // that we don't change the order of "equivalent" elements. + { + if (!TEST_IS_CONSTANT_EVALUATED) { + auto v = generate_sawtooth(N, M); + std::mt19937 randomness; + for (int i = 0; i < N - M; i += M) { + std::shuffle(v.begin() + i, v.begin() + i + M, randomness); + } + std::stable_sort(v.begin(), v.end(), first_only()); + assert(std::is_sorted(v.begin(), v.end())); + } + } #if TEST_STD_VER >= 11 - { + { std::vector > v(1000); for (int i = 0; static_cast(i) < v.size(); ++i) - v[i].reset(new int(i)); + v[i].reset(new int(i)); std::stable_sort(v.begin(), v.end(), indirect_less()); assert(std::is_sorted(v.begin(), v.end(), indirect_less())); assert(*v[0] == 0); assert(*v[1] == 1); assert(*v[2] == 2); - } + } +#endif + + return true; +} + +int main(int, char**) { + test(); +#if TEST_STD_VER >= 26 + static_assert(test()); #endif return 0;