[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