-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libc++][C++26] P2562R1: constexpr
Stable Sorting
#110320
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-libcxx Author: None (PaulXiCao) ChangesCloses #105360. Full diff: https://github.com/llvm/llvm-project/pull/110320.diff 4 Files Affected:
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 <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-_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..4f7f8b7615781c 100644
--- a/libcxx/include/__algorithm/stable_sort.h
+++ b/libcxx/include/__algorithm/stable_sort.h
@@ -68,7 +68,7 @@ _LIBCPP_HIDE_FROM_ABI void __insertion_sort_move(
}
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2>
-_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,
@@ -106,7 +106,7 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_construct(
}
template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2, class _OutputIterator>
-_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 +134,21 @@ _LIBCPP_HIDE_FROM_ABI void __merge_move_assign(
}
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-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 <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-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;
@@ -190,12 +192,13 @@ struct __stable_sort_switch {
};
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-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 +238,7 @@ void __stable_sort(_RandomAccessIterator __first,
}
template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
-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,13 +257,14 @@ __stable_sort_impl(_RandomAccessIterator __first, _RandomAccessIterator __last,
}
template <class _RandomAccessIterator, class _Compare>
-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 <class _RandomAccessIterator>
-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<>());
}
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 <class _Tp>
- _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 <class _Tp>
- _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 <class _Tp>
- _LIBCPP_HIDE_FROM_ABI void __incr() _NOEXCEPT {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __incr() _NOEXCEPT {
__incr(integral_constant<bool, is_trivially_destructible<_Tp>::value>());
}
template <class _Tp>
- _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<bool, is_trivially_destructible<_Tp>::value>());
}
template <class _Tp>
- _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<bool, is_trivially_destructible<_Tp>::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 4301d22027de85..92803c89dbcb7c 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 <algorithm>
+#include <array>
#include <cassert>
#include <iterator>
#include <random>
@@ -23,8 +25,6 @@
#include "count_new.h"
#include "test_macros.h"
-std::mt19937 randomness;
-
template <class RI>
void
test_sort_helper(RI f, RI l)
@@ -80,66 +80,149 @@ 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 <int N, int M>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> init_saw_tooth_pattern() {
+ std::array<int, N> array;
+ for (int i = 0, x = 0; i < N; ++i) {
+ array[i] = x;
+ if (++x == M)
+ x = 0;
+ }
+ return array;
+}
+
+template <int N, int M>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> sort_saw_tooth_pattern() {
+ std::array<int, N> array = init_saw_tooth_pattern<N, M>();
+ std::stable_sort(array.begin(), array.end());
+ return array;
+}
+
+template <int N, int M>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> sort_already_sorted() {
+ std::array<int, N> array = sort_saw_tooth_pattern<N, M>();
+ std::stable_sort(array.begin(), array.end());
+ return array;
+}
+
+template <int N, int M>
+std::array<int, N> sort_reversely_sorted() {
+ std::array<int, N> array = sort_saw_tooth_pattern<N, M>();
+ std::reverse(array.begin(), array.end());
+ std::stable_sort(array.begin(), array.end());
+ return array;
+}
+
+template <int N, int M>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> sort_swapped_sorted_ranges() {
+ std::array<int, N> array = sort_saw_tooth_pattern<N, M>();
+ std::swap_ranges(array.begin(), array.begin() + N / 2, array.begin() + N / 2);
+ std::stable_sort(array.begin(), array.end());
+ return array;
+}
+
+template <int N, int M>
+std::array<int, N> sort_reversely_swapped_sorted_ranges() {
+ std::array<int, N> array = sort_saw_tooth_pattern<N, M>();
+ 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 >= 26
+# 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 <int N, int M>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void test_larger_sorts() {
+ static_assert(N > 0, "");
+ static_assert(M > 0, "");
+
+ { // test saw tooth pattern
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> array = sort_saw_tooth_pattern<N, M>();
+ COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end()))
+ }
+
+#if _LIBCPP_STD_VER >= 26
+ if !consteval
+#endif
+ { // test random pattern
+ // random-number generators not constexpr-friendly
+ static std::mt19937 randomness;
+ std::array<int, N> array = init_saw_tooth_pattern<N, M>();
+ 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_CXX26 std::array<int, N> array = sort_already_sorted<N, M>();
+ COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end()))
+ }
+
+#if _LIBCPP_STD_VER >= 26
+ if !consteval
+#endif
+ { // test reverse sorted pattern
+ // consteval error: "constexpr evaluation hit maximum step limit"
+ std::array<int, N> array = sort_reversely_sorted<N, M>();
+ assert(std::is_sorted(array.begin(), array.end()));
+ }
+
+ { // test swap ranges 2 pattern
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 std::array<int, N> array = sort_swapped_sorted_ranges<N, M>();
+ COMPILE_OR_RUNTIME_ASSERT(std::is_sorted(array.begin(), array.end()))
+ }
+
+#if _LIBCPP_STD_VER >= 26
+ if !consteval
+#endif
+ { // test reverse swap ranges 2 pattern
+ // consteval error: "constexpr evaluation hit maximum step limit"
+ std::array<int, N> array = sort_reversely_swapped_sorted_ranges<N, M>();
+ 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 <int N>
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void test_larger_sorts() {
+ 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>();
}
-int main(int, char**)
-{
- // test null range
+#if _LIBCPP_STD_VER >= 26
+# 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
+#if _LIBCPP_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 +231,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<int> vec(150, 3);
- getGlobalMemCounter()->throw_after = 0;
- std::stable_sort(vec.begin(), vec.end());
- }
-#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
+ { // larger sorts
+ // run- and conditionally compile-time tests
+ test_larger_sorts<256>();
+ test_larger_sorts<257>();
+#if _LIBCPP_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<int> vec(150, 3);
+ getGlobalMemCounter()->throw_after = 0;
+ std::stable_sort(vec.begin(), vec.end());
+ }
+#endif
return 0;
}
|
This comment by Jiang An on the discord channel for reference:
|
@PaulXiCao If this PR addresses the paper only partially, maybe you shouldn't use "Closes #", because it will close the issue? Is that so? |
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
Outdated
Show resolved
Hide resolved
✅ With the latest revision this PR passed the C/C++ code formatter. |
Co-authored-by: A. Jiang <[email protected]>
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
Outdated
Show resolved
Hide resolved
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
Show resolved
Hide resolved
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
Outdated
Show resolved
Hide resolved
array[i] = x; | ||
if (++x == M) | ||
x = 0; | ||
template <int N, int M> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't move the individual test into different functions. If you really want to you can do
{ // description
<test code>
}
but I'd just keep it as-is if there isn't a reason to change things here.
Implementing
constexpr std::stable_sort
.This is part of P2562R1, tracked via issue #105360.
(EDIT: This comment has been edited.)