[libcxx-commits] [libcxx] [libc++][C++26] `constexpr std::stable_sort` (part of P2562) (PR #105379)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Sep 20 18:47:22 PDT 2024


https://github.com/PaulXiCao updated https://github.com/llvm/llvm-project/pull/105379

>From 26d5a32b4301426d1b5311da28c5edd2cee6021e Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 20 Aug 2024 22:21:57 +0200
Subject: [PATCH 1/2] constexpr stable sort; tests

---
 libcxx/include/__algorithm/inplace_merge.h    |   6 +-
 libcxx/include/__algorithm/sort.h             |   2 +-
 libcxx/include/__algorithm/stable_sort.h      |  63 +++--
 libcxx/include/__memory/destruct_n.h          |  14 +-
 .../alg.sort/stable.sort/stable_sort.pass.cpp | 228 ++++++++++++------
 5 files changed, 209 insertions(+), 104 deletions(-)

diff --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index 62a8bc53e23f3c..5cdaf38a1b600c 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -68,7 +68,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_CXX23 void __half_inplace_merge(
     _InputIterator1 __first1,
     _Sent1 __last1,
     _InputIterator2 __first2,
@@ -93,7 +93,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_CXX23 void __buffered_inplace_merge(
     _BidirectionalIterator __first,
     _BidirectionalIterator __middle,
     _BidirectionalIterator __last,
@@ -124,7 +124,7 @@ _LIBCPP_HIDE_FROM_ABI void __buffered_inplace_merge(
 }
 
 template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-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 0b2137dee2f77e..a90d39ac6b9262 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_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 ec556aad82e8d8..af97035544ca50 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 <new>
+#include <vector>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -35,7 +36,7 @@ _LIBCPP_PUSH_MACROS
 _LIBCPP_BEGIN_NAMESPACE_STD
 
 template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-_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 <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2>
-_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 <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2, class _OutputIterator>
-_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 <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_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 <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_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 <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_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 <class _AlgPolicy, class _RandomAccessIterator, class _Compare>
-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<value_type> __unique_buf;
   pair<value_type*, ptrdiff_t> __buf(0, 0);
+  std::vector<value_type> __h_vec;
   if (__len > static_cast<difference_type>(__stable_sort_switch<value_type>::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<value_type>(__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 <class _RandomAccessIterator, class _Compare>
-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 <class _RandomAccessIterator>
-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 78635ad0af04bd..f15c076e70a836 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 <class _Tp>
-  _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 <class _Tp>
-  _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 <class _Tp>
-  _LIBCPP_HIDE_FROM_ABI void __incr() _NOEXCEPT {
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void __incr() _NOEXCEPT {
     __incr(integral_constant<bool, is_trivially_destructible<_Tp>::value>());
   }
 
@@ -53,7 +53,7 @@ struct __destruct_n {
   }
 
   template <class _Tp>
-  _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<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..5f327c3d859452 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,148 @@ 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_CXX23 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_CXX23 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_CXX23 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_CXX23 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 >= 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 <int N, int M>
+_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<int, N> array = sort_saw_tooth_pattern<N, M>();
+    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<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_CXX23 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 >= 23
+  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_CXX23 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 >= 23
+  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_CXX23 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 >= 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 +230,28 @@ 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
+    // 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>();
+  }
+
+#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;
 }

>From 55c19d3fcacdbe49d086f9c8e20bd8a59204fa70 Mon Sep 17 00:00:00 2001
From: Paul <paulxicao7 at gmail.com>
Date: Tue, 20 Aug 2024 22:55:25 +0200
Subject: [PATCH 2/2] stable_sort: stability test (keep relative order)

---
 .../alg.sort/stable.sort/stable_sort.pass.cpp | 34 +++++++++++++++++++
 1 file changed, 34 insertions(+)

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 5f327c3d859452..3cc71c44d573f9 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
@@ -205,6 +205,36 @@ _LIBCPP_CONSTEXPR_SINCE_CXX23 void test_larger_sorts() {
   test_larger_sorts<N, N>();
 }
 
+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<Element, 5> get_by_key_sorted_array() {
+  std::array<Element, 5> 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<Element, 5> 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;                                                                                                              \
@@ -245,6 +275,10 @@ int main(int, char**) {
     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<int> vec(150, 3);



More information about the libcxx-commits mailing list