diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv index 8864b1ebe28891..8bdf7464e22e40 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.0","" "`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.0","" diff --git a/libcxx/include/__algorithm/sort.h b/libcxx/include/__algorithm/sort.h index 0b2137dee2f77e..2e858fd15022d8 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_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 ec556aad82e8d8..d9428d6672b086 100644 --- a/libcxx/include/__algorithm/stable_sort.h +++ b/libcxx/include/__algorithm/stable_sort.h @@ -34,6 +34,16 @@ _LIBCPP_PUSH_MACROS _LIBCPP_BEGIN_NAMESPACE_STD +// Workaround for "constexpr placement new" bug in gcc (fixed in 14.2). +// See https://github.com/llvm/llvm-project/pull/110320#discussion_r1788557715. +#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 <= 140100) +# define _LIBCPP_MOVING_PLACEMENT_NEW(__ptr, __type, __move_func, __iter) \ + [__ptr, &__iter] { ::new ((void*)__ptr) __type(__move_func(__iter)); }() +#else +# define _LIBCPP_MOVING_PLACEMENT_NEW(__ptr, __type, __move_func, __iter) \ + ::new ((void*)__ptr) __type(__move_func(__iter)) +#endif + template _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move( _BidirectionalIterator __first1, @@ -47,19 +57,22 @@ _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)); + _LIBCPP_MOVING_PLACEMENT_NEW(__last2, value_type, _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)); + { + value_type& __tmp = *__i2; + _LIBCPP_MOVING_PLACEMENT_NEW(__j2, value_type, std::move, __tmp); + } __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)); + _LIBCPP_MOVING_PLACEMENT_NEW(__j2, value_type, _Ops::__iter_move, __first1); __d.template __incr(); } } @@ -68,7 +81,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, @@ -83,22 +96,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)); + _LIBCPP_MOVING_PLACEMENT_NEW(__result, value_type, _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)); + _LIBCPP_MOVING_PLACEMENT_NEW(__result, value_type, _Ops::__iter_move, __first1); __h.release(); return; } if (__comp(*__first2, *__first1)) { - ::new ((void*)__result) value_type(_Ops::__iter_move(__first2)); + _LIBCPP_MOVING_PLACEMENT_NEW(__result, value_type, _Ops::__iter_move, __first2); __d.template __incr(); ++__first2; } else { - ::new ((void*)__result) value_type(_Ops::__iter_move(__first1)); + _LIBCPP_MOVING_PLACEMENT_NEW(__result, value_type, _Ops::__iter_move, __first1); __d.template __incr(); ++__first1; } @@ -106,7 +119,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, @@ -134,19 +147,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; @@ -154,21 +169,21 @@ void __stable_sort_move(_RandomAccessIterator __first1, case 0: return; case 1: - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + _LIBCPP_MOVING_PLACEMENT_NEW(__first2, value_type, _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)); + _LIBCPP_MOVING_PLACEMENT_NEW(__first2, value_type, _Ops::__iter_move, __last1); __d.template __incr(); ++__first2; - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + _LIBCPP_MOVING_PLACEMENT_NEW(__first2, value_type, _Ops::__iter_move, __first1); } else { - ::new ((void*)__first2) value_type(_Ops::__iter_move(__first1)); + _LIBCPP_MOVING_PLACEMENT_NEW(__first2, value_type, _Ops::__iter_move, __first1); __d.template __incr(); ++__first2; - ::new ((void*)__first2) value_type(_Ops::__iter_move(__last1)); + _LIBCPP_MOVING_PLACEMENT_NEW(__first2, value_type, _Ops::__iter_move, __last1); } __h2.release(); return; @@ -190,12 +205,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_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) { @@ -235,7 +251,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; @@ -254,18 +270,19 @@ __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<>()); } +#undef _LIBCPP_MOVING_PLACEMENT_NEW _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 78635ad0af04bd..1445a909162d32 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 17d63ce0cf1c0f..fd80bd7150829d 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/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 4301d22027de85..8302c59ef0ef54 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,13 +8,14 @@ // -// template -// requires ShuffleIterator -// && LessThanComparable -// void -// stable_sort(Iter first, Iter last); +// template +// constexpr void // constexpr in C++26 +// stable_sort(RandomAccessIterator first, RandomAccessIterator last); + +// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=20000000 #include +#include #include #include #include @@ -23,8 +24,6 @@ #include "count_new.h" #include "test_macros.h" -std::mt19937 randomness; - template void test_sort_helper(RI f, RI l) @@ -80,66 +79,122 @@ 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; - } - // 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; +template +TEST_CONSTEXPR_CXX26 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; } -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 std::array sort_saw_tooth_pattern() { + std::array array = init_saw_tooth_pattern(); + std::stable_sort(array.begin(), array.end()); + return array; } -int main(int, char**) -{ - // test null range +template +TEST_CONSTEXPR_CXX26 std::array sort_already_sorted() { + std::array array = sort_saw_tooth_pattern(); + std::stable_sort(array.begin(), array.end()); + return array; +} + +template +TEST_CONSTEXPR_CXX26 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 +TEST_CONSTEXPR_CXX26 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 +TEST_CONSTEXPR_CXX26 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; +} + +template +TEST_CONSTEXPR_CXX26 void test_larger_sorts() { + static_assert(N > 0, ""); + static_assert(M > 0, ""); + + { // test saw tooth pattern + std::array array = sort_saw_tooth_pattern(); + assert(std::is_sorted(array.begin(), array.end())); + } + +#if TEST_STD_VER >= 26 + 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 + std::array array = sort_already_sorted(); + assert(std::is_sorted(array.begin(), array.end())); + } + + { // test reverse sorted pattern + std::array array = sort_reversely_sorted(); + assert(std::is_sorted(array.begin(), array.end())); + } + + { // test swap ranges 2 pattern + std::array array = sort_swapped_sorted_ranges(); + assert(std::is_sorted(array.begin(), array.end())); + } + + { // test reverse swap ranges 2 pattern + std::array array = sort_reversely_swapped_sorted_ranges(); + assert(std::is_sorted(array.begin(), array.end())); + } +} + +template +TEST_CONSTEXPR_CXX26 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 int d = 0; std::stable_sort(&d, &d); - // exhaustively test all possibilities up to length 8 +#if TEST_STD_VER >= 26 + static_assert((std::stable_sort(&d, &d), true)); +#endif + } + + { // exhaustively test all possibilities up to length 8 test_sort_<1>(); test_sort_<2>(); test_sort_<3>(); @@ -148,22 +203,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 + // run- and compile-time tests + test_larger_sorts<256>(); + test_larger_sorts<257>(); +#if TEST_STD_VER >= 26 + static_assert((test_larger_sorts<256>(), true)); + static_assert((test_larger_sorts<257>(), true)); +#endif + + // 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>(); + } + +#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; }