[libcxx-commits] [libcxx] 438e2cc - [libc++] Make std::stable_sort constexpr friendly (#110320)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Jan 14 07:24:40 PST 2025
Author: PaulXiCao
Date: 2025-01-14T10:24:35-05:00
New Revision: 438e2ccd4ad18d23fc800d0ad9f4f667a547f868
URL: https://github.com/llvm/llvm-project/commit/438e2ccd4ad18d23fc800d0ad9f4f667a547f868
DIFF: https://github.com/llvm/llvm-project/commit/438e2ccd4ad18d23fc800d0ad9f4f667a547f868.diff
LOG: [libc++] Make std::stable_sort constexpr friendly (#110320)
Implementing `constexpr std::stable_sort`. This is part of P2562R1,
tracked via issue #105360.
Closes #119394
Co-authored-by: A. Jiang <de34 at live.cn>
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
Added:
Modified:
libcxx/docs/Status/Cxx2cPapers.csv
libcxx/include/__algorithm/inplace_merge.h
libcxx/include/__algorithm/sort.h
libcxx/include/__algorithm/stable_sort.h
libcxx/include/__memory/destruct_n.h
libcxx/include/algorithm
libcxx/include/module.modulemap
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp
libcxx/test/std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort_comp.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index aa896e85fcb1fe..65fd335a0309f0 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -2,7 +2,7 @@
"`P2497R0 <https://wg21.link/P2497R0>`__","Testing for success or failure of ``<charconv>`` functions","2023-06 (Varna)","|Complete|","18",""
"`P2592R3 <https://wg21.link/P2592R3>`__","Hashing support for ``std::chrono`` value classes","2023-06 (Varna)","","",""
"`P2587R3 <https://wg21.link/P2587R3>`__","``to_string`` or not ``to_string``","2023-06 (Varna)","","",""
-"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","","",""
+"`P2562R1 <https://wg21.link/P2562R1>`__","``constexpr`` Stable Sorting","2023-06 (Varna)","|Partial|","20.0",""
"`P2545R4 <https://wg21.link/P2545R4>`__","Read-Copy Update (RCU)","2023-06 (Varna)","","",""
"`P2530R3 <https://wg21.link/P2530R3>`__","Hazard Pointers for C++26","2023-06 (Varna)","","",""
"`P2538R1 <https://wg21.link/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 69213cc1457be9..1fc31b66f4bd62 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 <class _T1>
- _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 <class _T1, class _T2>
- _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 <class _AlgPolicy,
class _InputIterator2,
class _Sent2,
class _OutputIterator>
-_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 <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-_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 <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-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 5c60b23931ccae..8dd0721f2c65f5 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 <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 70a85023a17f0d..3cfbcf08d2c5c4 100644
--- a/libcxx/include/__algorithm/stable_sort.h
+++ b/libcxx/include/__algorithm/stable_sort.h
@@ -19,6 +19,7 @@
#include <__cstddef/ptr
diff _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 <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-_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<value_type, __destruct_n&> __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<value_type>();
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<value_type>();
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<value_type>();
}
}
@@ -74,7 +75,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,
@@ -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<value_type>())
- ::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<value_type>())
- ::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<value_type>();
++__first2;
} else {
- ::new ((void*)__result) value_type(_Ops::__iter_move(__first1));
+ std::__construct_at(__result, _Ops::__iter_move(__first1));
__d.template __incr<value_type>();
++__first1;
}
@@ -112,7 +113,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,
@@ -140,19 +141,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>::
diff erence_type __len,
- typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
- ptr
diff _t __buff_size);
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
+ _RandomAccessIterator __first,
+ _RandomAccessIterator __last,
+ _Compare __comp,
+ typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len,
+ typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
+ ptr
diff _t __buff_size);
template <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-void __stable_sort_move(_RandomAccessIterator __first1,
- _RandomAccessIterator __last1,
- _Compare __comp,
- typename iterator_traits<_RandomAccessIterator>::
diff erence_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>::
diff erence_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<value_type, __destruct_n&> __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<value_type>();
++__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<value_type>();
++__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 <class _AlgPolicy, class _Compare, class _RandomAccessIterator>
-void __stable_sort(_RandomAccessIterator __first,
- _RandomAccessIterator __last,
- _Compare __comp,
- typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len,
- typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
- ptr
diff _t __buff_size) {
+_LIBCPP_CONSTEXPR_SINCE_CXX26 void __stable_sort(
+ _RandomAccessIterator __first,
+ _RandomAccessIterator __last,
+ _Compare __comp,
+ typename iterator_traits<_RandomAccessIterator>::
diff erence_type __len,
+ typename iterator_traits<_RandomAccessIterator>::value_type* __buff,
+ ptr
diff _t __buff_size) {
typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
typedef typename iterator_traits<_RandomAccessIterator>::
diff erence_type
diff erence_type;
switch (__len) {
@@ -279,7 +283,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
diff erence_type = typename iterator_traits<_RandomAccessIterator>::
diff erence_type;
@@ -298,18 +302,18 @@ __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<>());
}
_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 66adefb0f51fc7..db227a4ea1dc77 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/include/algorithm b/libcxx/include/algorithm
index e593ae26ed6e24..7b4cb8e4961969 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1530,11 +1530,11 @@ template <class RandomAccessIterator, class Compare>
sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
template <class RandomAccessIterator>
- void
+ constexpr void // constexpr in C++26
stable_sort(RandomAccessIterator first, RandomAccessIterator last);
template <class RandomAccessIterator, class Compare>
- void
+ constexpr void // constexpr in C++26
stable_sort(RandomAccessIterator first, RandomAccessIterator last, Compare comp);
template <class RandomAccessIterator>
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 69f1b7d094ada1..cdac9c883ecabf 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 621234354092d3..b3b458808c44ad 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 @@
// <algorithm>
-// template<RandomAccessIterator Iter>
-// requires ShuffleIterator<Iter>
-// && LessThanComparable<Iter::value_type>
-// void
-// stable_sort(Iter first, Iter last);
+// template <class RandomAccessIterator>
+// 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 <algorithm>
#include <cassert>
@@ -23,156 +24,181 @@
#include "count_new.h"
#include "test_macros.h"
-std::mt19937 randomness;
-
-template <class RI>
-void
-test_sort_helper(RI f, RI l)
-{
- typedef typename std::iterator_traits<RI>::value_type value_type;
- typedef typename std::iterator_traits<RI>::
diff erence_type
diff erence_type;
-
- if (f != l)
- {
-
diff erence_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 <class Iterator>
+TEST_CONSTEXPR_CXX26 void test_all_permutations(Iterator first, Iterator last) {
+ using T = typename std::iterator_traits<Iterator>::value_type;
+
+ do {
+ std::vector<T> 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 <class RI>
-void
-test_sort_driver_driver(RI f, RI l, int start, RI real_last)
-{
- using value_type = typename std::iterator_traits<RI>::value_type;
+template <class Iterator>
+TEST_CONSTEXPR_CXX26 void test_sort_exhaustive_impl(Iterator first, Iterator last, int start, Iterator real_last) {
+ using T = typename std::iterator_traits<Iterator>::value_type;
- for (RI i = l; i > f + start;) {
- *--i = static_cast<value_type>(start);
- if (f == i) {
- test_sort_helper(f, real_last);
+ for (Iterator i = last; i > first + start;) {
+ *--i = static_cast<T>(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 <class RI>
-void
-test_sort_driver(RI f, RI l, int start)
-{
- test_sort_driver_driver(f, l, start, l);
-}
-
-template <int sa, class V>
-void test_sort_() {
- V ia[sa];
- for (int i = 0; i < sa; ++i) {
- test_sort_driver(ia, ia + sa, i);
+template <class T>
+TEST_CONSTEXPR_CXX26 void test_sort_exhaustive(int N) {
+ std::vector<T> vec;
+ vec.resize(N);
+ for (int i = 0; i < N; ++i) {
+ test_sort_exhaustive_impl(vec.begin(), vec.end(), i, vec.end());
}
}
-template <int sa>
-void test_sort_() {
- test_sort_<sa, int>();
- test_sort_<sa, float>();
-}
-
-template <class V>
-void test_larger_sorts(int N, int M) {
- assert(N != 0);
- assert(M != 0);
- // create array length N filled with M
diff erent numbers
- V* array = new V[N];
- int x = 0;
+template <class T>
+TEST_CONSTEXPR_CXX26 std::vector<T> generate_sawtooth(int N, int M) {
+ // Populate a sequence of length N with M
diff erent numbers
+ std::vector<T> v;
+ T x = 0;
for (int i = 0; i < N; ++i) {
- array[i] = static_cast<V>(x);
+ v.push_back(x);
if (++x == M)
x = 0;
}
+ return v;
+}
+
+template <class T>
+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<T>(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<T>(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<T>(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<T>(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<T>(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<T>(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<int>(N, M);
- test_larger_sorts<float>(N, M);
+template <class T>
+TEST_CONSTEXPR_CXX26 void test_larger_sorts(int N) {
+ test_larger_sorts<T>(N, 1);
+ test_larger_sorts<T>(N, 2);
+ test_larger_sorts<T>(N, 3);
+ test_larger_sorts<T>(N, N / 2 - 1);
+ test_larger_sorts<T>(N, N / 2);
+ test_larger_sorts<T>(N, N / 2 + 1);
+ test_larger_sorts<T>(N, N - 2);
+ test_larger_sorts<T>(N, N - 1);
+ test_larger_sorts<T>(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 <class T>
+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<int> 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<T>(1);
+ test_sort_exhaustive<T>(2);
+ test_sort_exhaustive<T>(3);
+ test_sort_exhaustive<T>(4);
+ test_sort_exhaustive<T>(5);
+ test_sort_exhaustive<T>(6);
+ test_sort_exhaustive<T>(7);
+ test_sort_exhaustive<T>(8);
+ }
+
+ test_larger_sorts<T>(256);
+ test_larger_sorts<T>(257);
+ if (!TEST_IS_CONSTANT_EVALUATED) { // avoid blowing past constexpr evaluation limit
+ test_larger_sorts<T>(499);
+ test_larger_sorts<T>(500);
+ test_larger_sorts<T>(997);
+ test_larger_sorts<T>(1000);
+ test_larger_sorts<T>(1009);
+ test_larger_sorts<T>(1024);
+ test_larger_sorts<T>(1031);
+ test_larger_sorts<T>(2053);
+ }
+
+ // check that the algorithm works without memory
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ if (!TEST_IS_CONSTANT_EVALUATED) {
+ std::vector<T> vec(150, T(3));
+ getGlobalMemCounter()->throw_after = 0;
+ std::stable_sort(vec.begin(), vec.end());
+ }
+#endif
+
+ return true;
+}
+int main(int, char**) {
+ test<int>();
+ test<float>();
+#if TEST_STD_VER >= 26
+ static_assert(test<int>());
+ static_assert(test<float>());
+#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 5ee6d89064941f..a2c0ca747d0390 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 @@
// <algorithm>
// template<RandomAccessIterator Iter, StrictWeakOrder<auto, Iter::value_type> Compare>
-// requires ShuffleIterator<Iter>
-// && CopyConstructible<Compare>
-// void
-// stable_sort(Iter first, Iter last, Compare comp);
+// requires ShuffleIterator<Iter> && CopyConstructible<Compare>
+// 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 <algorithm>
#include <functional>
@@ -27,57 +28,79 @@
struct indirect_less {
template <class P>
- 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<int, int>& x, const std::pair<int, int>& y) const { return x.first < y.first; }
+ TEST_CONSTEXPR_CXX26 bool operator()(const std::pair<int, int>& x, const std::pair<int, int>& y) const {
+ return x.first < y.first;
+ }
};
-void test()
-{
- typedef std::pair<int, int> P;
- const int N = 1000;
- const int M = 10;
- std::vector<P> 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<int, int>;
+
+TEST_CONSTEXPR_CXX26 std::vector<Pair> generate_sawtooth(int N, int M) {
+ std::vector<Pair> 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<std::unique_ptr<int> > v(1000);
for (int i = 0; static_cast<std::size_t>(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;
More information about the libcxx-commits
mailing list