diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h index 62a8bc53e23f3..5cdaf38a1b600 100644 --- a/libcxx/include/__algorithm/inplace_merge.h +++ b/libcxx/include/__algorithm/inplace_merge.h @@ -68,7 +68,7 @@ template -_LIBCPP_HIDE_FROM_ABI void __half_inplace_merge( +_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __half_inplace_merge( _InputIterator1 __first1, _Sent1 __last1, _InputIterator2 __first2, @@ -93,7 +93,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_CXX23 void __buffered_inplace_merge( _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, @@ -124,7 +124,7 @@ _LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge( } template -void __inplace_merge( +_LIBCPP_CONSTEXPR_SINCE_CXX23 void __inplace_merge( _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h index 0b2137dee2f77..a90d39ac6b926 100644 --- a/libcxx/include/__algorithm/sort.h +++ b/libcxx/include/__algorithm/sort.h @@ -283,7 +283,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_CXX23 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 ec556aad82e8d..af97035544ca5 100644 --- a/libcxx/include/__algorithm/stable_sort.h +++ b/libcxx/include/__algorithm/stable_sort.h @@ -24,6 +24,7 @@ #include <__utility/move.h> #include <__utility/pair.h> #include +#include #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header @@ -35,7 +36,7 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_STD template -_LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( _BidirectionalIterator __first1, _BidirectionalIterator __last1, typename iterator_traits<_BidirectionalIterator>::value_type* __first2, @@ -68,7 +69,7 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( } template -_LIBCPP_HIDE_FROM_ABI void __merge_move_construct( +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI void __merge_move_construct( _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, @@ -106,7 +107,7 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct( } template -_LIBCPP_HIDE_FROM_ABI void __merge_move_assign( +_LIBCPP_CONSTEXPR_SINCE_CXX23 _LIBCPP_HIDE_FROM_ABI void __merge_move_assign( _InputIterator1 __first1, _InputIterator1 __last1, _InputIterator2 __first2, @@ -134,19 +135,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_CXX23 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_CXX23 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; @@ -190,12 +193,13 @@ struct __stable_sort_switch { }; 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_CXX23 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) { @@ -235,7 +239,7 @@ void __stable_sort(_RandomAccessIterator __first, } template -inline _LIBCPP_HIDE_FROM_ABI void +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 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; @@ -243,10 +247,22 @@ __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, difference_type __len = __last - __first; __unique_temporary_buffer __unique_buf; pair __buf(0, 0); + std::vector __h_vec; if (__len > static_cast(__stable_sort_switch::value)) { +#if _LIBCPP_STD_VER >= 23 + if consteval { + __h_vec.reserve(__len); + __buf.first = __h_vec.data(); + __buf.second = __h_vec.size(); + } else { +#else __unique_buf = std::__allocate_unique_temporary_buffer(__len); __buf.first = __unique_buf.get(); __buf.second = __unique_buf.get_deleter().__count_; +#endif +#if _LIBCPP_STD_VER >= 23 + } +#endif } std::__stable_sort<_AlgPolicy, __comp_ref_type<_Compare> >(__first, __last, __comp, __len, __buf.first, __buf.second); @@ -254,13 +270,14 @@ __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last, } template -inline _LIBCPP_HIDE_FROM_ABI void +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 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) { +inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void +stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last) { std::stable_sort(__first, __last, __less<>()); } diff --git a/libcxx/include/__memory/destruct_n.h b/libcxx/include/__memory/destruct_n.h index 78635ad0af04b..f15c076e70a83 100644 --- a/libcxx/include/__memory/destruct_n.h +++ b/libcxx/include/__memory/destruct_n.h @@ -25,25 +25,25 @@ 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_CXX23 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_CXX23 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_CXX23 void __incr(false_type) _NOEXCEPT { ++__size_; } + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 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 {} public: - _LIBCPP_HIDE_FROM_ABI explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {} + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 explicit __destruct_n(size_t __s) _NOEXCEPT : __size_(__s) {} template - _LIBCPP_HIDE_FROM_ABI void __incr() _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __incr() _NOEXCEPT { __incr(integral_constant::value>()); } @@ -53,7 +53,7 @@ struct __destruct_n { } template - _LIBCPP_HIDE_FROM_ABI void operator()(_Tp* __p) _NOEXCEPT { + _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void operator()(_Tp* __p) _NOEXCEPT { __process(__p, integral_constant::value>()); } }; 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 4301d22027de8..3cc71c44d573f 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 @@ -14,7 +14,9 @@ // void // stable_sort(Iter first, Iter last); +#include <__config> #include +#include #include #include #include @@ -23,8 +25,6 @@ #include "count_new.h" #include "test_macros.h" -std::mt19937 randomness; - template void test_sort_helper(RI f, RI l) @@ -80,66 +80,178 @@ test_sort_() } } -void -test_larger_sorts(int N, int M) -{ - assert(N != 0); - assert(M != 0); - // create array length N filled with M different numbers - int* array = new int[N]; - int x = 0; - for (int i = 0; i < N; ++i) - { - array[i] = x; - if (++x == M) - x = 0; +template +_LIBCPP_CONSTEXPR_SINCE_CXX23 std::array init_saw_tooth_pattern() { + std::array array; + for (int i = 0, x = 0; i < N; ++i) { + array[i] = x; + if (++x == M) + x = 0; + } + return array; +} + +template +_LIBCPP_CONSTEXPR_SINCE_CXX23 std::array sort_saw_tooth_pattern() { + std::array array = init_saw_tooth_pattern(); + std::stable_sort(array.begin(), array.end()); + return array; +} + +template +_LIBCPP_CONSTEXPR_SINCE_CXX23 std::array sort_already_sorted() { + std::array array = sort_saw_tooth_pattern(); + std::stable_sort(array.begin(), array.end()); + return array; +} + +template +std::array sort_reversely_sorted() { + std::array array = sort_saw_tooth_pattern(); + std::reverse(array.begin(), array.end()); + std::stable_sort(array.begin(), array.end()); + return array; +} + +template +_LIBCPP_CONSTEXPR_SINCE_CXX23 std::array sort_swapped_sorted_ranges() { + std::array array = sort_saw_tooth_pattern(); + std::swap_ranges(array.begin(), array.begin() + N / 2, array.begin() + N / 2); + std::stable_sort(array.begin(), array.end()); + return array; +} + +template +std::array sort_reversely_swapped_sorted_ranges() { + std::array array = sort_saw_tooth_pattern(); + std::reverse(array.begin(), array.end()); + std::swap_ranges(array.begin(), array.begin() + N / 2, array.begin() + N / 2); + std::stable_sort(array.begin(), array.end()); + return array; +} + +#if _LIBCPP_STD_VER >= 23 +# define COMPILE_OR_RUNTIME_ASSERT(func) \ + if consteval { \ + static_assert(func); \ + } else { \ + assert(func); \ } - // test saw tooth pattern - std::stable_sort(array, array+N); - assert(std::is_sorted(array, array+N)); - // test random pattern - std::shuffle(array, array+N, randomness); - std::stable_sort(array, array+N); - assert(std::is_sorted(array, array+N)); - // test sorted pattern - std::stable_sort(array, array+N); - assert(std::is_sorted(array, array+N)); - // test reverse sorted pattern - std::reverse(array, array+N); - std::stable_sort(array, array+N); - assert(std::is_sorted(array, array+N)); - // 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)); - // 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; +#else +# define COMPILE_OR_RUNTIME_ASSERT(func) assert(func); +#endif + +template +_LIBCPP_CONSTEXPR_SINCE_CXX23 void test_larger_sorts() { + static_assert(N > 0, ""); + static_assert(M > 0, ""); + + { // test saw tooth pattern + _LIBCPP_CONSTEXPR_SINCE_CXX23 std::array array = sort_saw_tooth_pattern(); + COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end())) + } + +#if _LIBCPP_STD_VER >= 23 + if !consteval +#endif + { // test random pattern + // random-number generators not constexpr-friendly + static std::mt19937 randomness; + std::array array = init_saw_tooth_pattern(); + std::shuffle(array.begin(), array.end(), randomness); + std::stable_sort(array.begin(), array.end()); + assert(std::is_sorted(array.begin(), array.end())); + } + + { // test sorted pattern + _LIBCPP_CONSTEXPR_SINCE_CXX23 std::array array = sort_already_sorted(); + COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end())) + } + +#if _LIBCPP_STD_VER >= 23 + if !consteval +#endif + { // test reverse sorted pattern + // consteval error: "constexpr evaluation hit maximum step limit" + std::array array = sort_reversely_sorted(); + assert(std::is_sorted(array.begin(), array.end())); + } + + { // test swap ranges 2 pattern + _LIBCPP_CONSTEXPR_SINCE_CXX23 std::array array = sort_swapped_sorted_ranges(); + COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end())) + } + +#if _LIBCPP_STD_VER >= 23 + if !consteval +#endif + { // test reverse swap ranges 2 pattern + // consteval error: "constexpr evaluation hit maximum step limit" + std::array array = sort_reversely_swapped_sorted_ranges(); + assert(std::is_sorted(array.begin(), array.end())); + } } -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 +_LIBCPP_CONSTEXPR_SINCE_CXX23 void test_larger_sorts() { + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); + test_larger_sorts(); } -int main(int, char**) -{ - // test null range +namespace stability_test { +struct Element { + int key; + int value; + _LIBCPP_CONSTEXPR_SINCE_CXX23 Element(int key, int value) : key(key), value(value) {} + _LIBCPP_CONSTEXPR_SINCE_CXX23 bool operator==(const Element other) const { + return (key == other.key) and (value == other.value); + } +}; + +struct Comparer_by_key { + _LIBCPP_CONSTEXPR_SINCE_CXX23 bool operator()(const Element lhs, const Element rhs) { return lhs.key < rhs.key; } +}; + +_LIBCPP_CONSTEXPR_SINCE_CXX23 std::array get_by_key_sorted_array() { + std::array a = {Element(1, 0), Element(1, 1), Element(0, 0), Element(0, 1), Element(0, 2)}; + std::stable_sort(a.begin(), a.end(), Comparer_by_key()); + return a; +} + +_LIBCPP_CONSTEXPR_SINCE_CXX23 void run() { + _LIBCPP_CONSTEXPR_SINCE_CXX23 std::array a = get_by_key_sorted_array(); + COMPILE_OR_RUNTIME_ASSERT(a[0] == Element(0, 0)); + COMPILE_OR_RUNTIME_ASSERT(a[1] == Element(0, 1)); + COMPILE_OR_RUNTIME_ASSERT(a[2] == Element(0, 2)); + COMPILE_OR_RUNTIME_ASSERT(a[3] == Element(1, 0)); + COMPILE_OR_RUNTIME_ASSERT(a[4] == Element(1, 1)); +} +} // namespace stability_test + +#if _LIBCPP_STD_VER >= 23 +# define COMPILE_AND_RUNTIME_CALL(func) \ + func; \ + static_assert((func, true)); +#else +# define COMPILE_AND_RUNTIME_CALL(func) func; +#endif + +int main(int, char**) { + { // test null range int d = 0; std::stable_sort(&d, &d); - // exhaustively test all possibilities up to length 8 + COMPILE_AND_RUNTIME_CALL(std::stable_sort(&d, &d)) + } + + { // exhaustively test all possibilities up to length 8 + test_sort_<1>(); test_sort_<1>(); test_sort_<2>(); test_sort_<3>(); @@ -148,22 +260,32 @@ int main(int, char**) 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); - -#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) + { // larger sorts + // compile- and runtime tests + COMPILE_AND_RUNTIME_CALL(test_larger_sorts<256>()) + COMPILE_AND_RUNTIME_CALL(test_larger_sorts<257>()) + + // only runtime tests bc. error: "constexpr evaluation hit maximum step limit" + test_larger_sorts<499>(); + test_larger_sorts<500>(); + test_larger_sorts<997>(); + test_larger_sorts<1000>(); + test_larger_sorts<1009>(); + } + + { // test "stable" aka leaving already sorted elements in relative order + COMPILE_AND_RUNTIME_CALL(stability_test::run()); + } + +#ifndef 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 return 0; }