[libcxx-commits] [libcxx] 053a714 - [libc++] Implement part of P2562R1: constexpr `ranges::inplace_merge` (#131947)

via libcxx-commits libcxx-commits at lists.llvm.org
Wed Mar 19 10:59:36 PDT 2025


Author: A. Jiang
Date: 2025-03-20T01:59:32+08:00
New Revision: 053a714adda3e1d3d506f417c7fbc6eb3fa75405

URL: https://github.com/llvm/llvm-project/commit/053a714adda3e1d3d506f417c7fbc6eb3fa75405
DIFF: https://github.com/llvm/llvm-project/commit/053a714adda3e1d3d506f417c7fbc6eb3fa75405.diff

LOG: [libc++] Implement part of P2562R1: constexpr `ranges::inplace_merge` (#131947)

Drive-by changes:
- Consistently mark `std::__inplace_merge::__inplace_merge_impl`
`_LIBCPP_CONSTEXPR_SINCE_CXX26`.
- This function template is only called by other functions that becomes
constexpr since C++26, and it itself calls `std::__inplace_merge` that
is constexpr since C++26.
- Unblock related test coverage in constant evaluation for
`stable_partition`, `ranges::stable_sort`, `std::stable_sort`,
`std::stable_partition`, and `std::inplace_merge`.

Added: 
    

Modified: 
    libcxx/include/__algorithm/ranges_inplace_merge.h
    libcxx/include/algorithm
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
    libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
    libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
    libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__algorithm/ranges_inplace_merge.h b/libcxx/include/__algorithm/ranges_inplace_merge.h
index 5879d0e7ef0fb..54581aff396ea 100644
--- a/libcxx/include/__algorithm/ranges_inplace_merge.h
+++ b/libcxx/include/__algorithm/ranges_inplace_merge.h
@@ -41,7 +41,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace ranges {
 struct __inplace_merge {
   template <class _Iter, class _Sent, class _Comp, class _Proj>
-  _LIBCPP_HIDE_FROM_ABI static constexpr auto
+  _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_SINCE_CXX26 auto
   __inplace_merge_impl(_Iter __first, _Iter __middle, _Sent __last, _Comp&& __comp, _Proj&& __proj) {
     auto __last_iter = ranges::next(__middle, __last);
     std::__inplace_merge<_RangeAlgPolicy>(
@@ -51,7 +51,7 @@ struct __inplace_merge {
 
   template <bidirectional_iterator _Iter, sentinel_for<_Iter> _Sent, class _Comp = ranges::less, class _Proj = identity>
     requires sortable<_Iter, _Comp, _Proj>
-  _LIBCPP_HIDE_FROM_ABI _Iter
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter
   operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
     return __inplace_merge_impl(
         std::move(__first), std::move(__middle), std::move(__last), std::move(__comp), std::move(__proj));
@@ -59,7 +59,7 @@ struct __inplace_merge {
 
   template <bidirectional_range _Range, class _Comp = ranges::less, class _Proj = identity>
     requires sortable<iterator_t<_Range>, _Comp, _Proj>
-  _LIBCPP_HIDE_FROM_ABI borrowed_iterator_t<_Range>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 borrowed_iterator_t<_Range>
   operator()(_Range&& __range, iterator_t<_Range> __middle, _Comp __comp = {}, _Proj __proj = {}) const {
     return __inplace_merge_impl(
         ranges::begin(__range), std::move(__middle), ranges::end(__range), std::move(__comp), std::move(__proj));

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index aea24e53019cc..6ba903ad3ce1e 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1031,13 +1031,14 @@ namespace ranges {
   template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
            class Proj = identity>
     requires sortable<I, Comp, Proj>
-    I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {});                    // since C++20
+    constexpr I                                                                            // constexpr since C++26
+      inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {});            // since C++20
 
   template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
     requires sortable<iterator_t<R>, Comp, Proj>
-    borrowed_iterator_t<R>
+    constexpr borrowed_iterator_t<R>                                                       // constexpr since C++26
       inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
-                    Proj proj = {});                                                               // since C++20
+                    Proj proj = {});                                                       // since C++20
 
   template<permutable I, sentinel_for<I> S, class Proj = identity,
            indirect_equivalence_relation<projected<I, Proj>> C = ranges::equal_to>

diff  --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
index 5a8640a8fc035..ee3619e0e6bcc 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
@@ -150,8 +150,15 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::is_sorted(a, Less(&copies)); assert(copies == 0);
     (void)std::ranges::is_sorted_until(first, last, Less(&copies)); assert(copies == 0);
     (void)std::ranges::is_sorted_until(a, Less(&copies)); assert(copies == 0);
-    if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+    if (!std::is_constant_evaluated())
+#endif
+    {
+      (void)std::ranges::inplace_merge(first, mid, last, Less(&copies));
+      assert(copies == 0);
+      (void)std::ranges::inplace_merge(a, mid, Less(&copies));
+      assert(copies == 0);
+    }
     (void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(&copies)); assert(copies == 0);
     (void)std::ranges::lexicographical_compare(a, b, Less(&copies)); assert(copies == 0);
     (void)std::ranges::lower_bound(first, last, value, Less(&copies)); assert(copies == 0);
@@ -223,10 +230,19 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::sort(a, Less(&copies)); assert(copies == 0);
     (void)std::ranges::sort_heap(first, last, Less(&copies)); assert(copies == 0);
     (void)std::ranges::sort_heap(a, Less(&copies)); assert(copies == 0);
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+    if (!std::is_constant_evaluated())
+#endif
+    {
+      (void)std::ranges::stable_partition(first, last, UnaryTrue(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_partition(a, UnaryTrue(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_sort(first, last, Less(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_sort(a, Less(&copies));
+      assert(copies == 0);
+    }
 #if TEST_STD_VER > 20
     (void)std::ranges::starts_with(first, last, first2, last2, Equal(&copies)); assert(copies == 0);
     (void)std::ranges::starts_with(a, b, Equal(&copies)); assert(copies == 0);

diff  --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
index 8cdf53c183512..1a85d1f33cf10 100644
--- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
@@ -147,8 +147,15 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::is_sorted(a, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::is_sorted_until(first, last, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::is_sorted_until(a, Less(), Proj(&copies)); assert(copies == 0);
-    if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+    if (!std::is_constant_evaluated())
+#endif
+    {
+      (void)std::ranges::inplace_merge(first, mid, last, Less(), Proj(&copies));
+      assert(copies == 0);
+      (void)std::ranges::inplace_merge(a, mid, Less(), Proj(&copies));
+      assert(copies == 0);
+    }
     (void)std::ranges::lexicographical_compare(first, last, first2, last2, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::lexicographical_compare(a, b, Less(), Proj(&copies), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::lower_bound(first, last, value, Less(), Proj(&copies)); assert(copies == 0);
@@ -228,10 +235,19 @@ constexpr bool all_the_algorithms()
     (void)std::ranges::sort(a, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::sort_heap(first, last, Less(), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::sort_heap(a, Less(), Proj(&copies)); assert(copies == 0);
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies)); assert(copies == 0); }
-    if (!std::is_constant_evaluated()) { (void)std::ranges::stable_sort(a, Less(), Proj(&copies)); assert(copies == 0); }
+#if TEST_STD_VER < 26
+    if (!std::is_constant_evaluated())
+#endif
+    {
+      (void)std::ranges::stable_partition(first, last, UnaryTrue(), Proj(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_partition(a, UnaryTrue(), Proj(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_sort(first, last, Less(), Proj(&copies));
+      assert(copies == 0);
+      (void)std::ranges::stable_sort(a, Less(), Proj(&copies));
+      assert(copies == 0);
+    }
 #if TEST_STD_VER > 20
     (void)std::ranges::starts_with(first, last, first2, last2, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);
     (void)std::ranges::starts_with(a, b, Equal(), Proj(&copies), Proj(&copies)); assert(copies == 0);

diff  --git a/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
index 1602490893d75..827e6c1d20868 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.merge/ranges_inplace_merge.pass.cpp
@@ -13,13 +13,14 @@
 // template<bidirectional_iterator I, sentinel_for<I> S, class Comp = ranges::less,
 //          class Proj = identity>
 //   requires sortable<I, Comp, Proj>
-//   I inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {});                    // Since C++20
+//   constexpr I                                                                            // constexpr since C++26
+//     inplace_merge(I first, I middle, S last, Comp comp = {}, Proj proj = {});            // Since C++20
 //
 // template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
 //   requires sortable<iterator_t<R>, Comp, Proj>
-//   borrowed_iterator_t<R>
+//   constexpr borrowed_iterator_t<R>                                                       // constexpr since C++26
 //     inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
-//                   Proj proj = {});                                                               // Since C++20
+//                   Proj proj = {});                                                       // Since C++20
 
 #include <algorithm>
 #include <array>
@@ -86,7 +87,7 @@ static_assert(!HasInplaceMergeRange<R<int*>, int*, ComparatorNotCopyable<int*>>)
 static_assert(!HasInplaceMergeIter<R<const int*>, const int*>);
 
 template <class In, template <class> class SentWrapper, std::size_t N1, std::size_t N2>
-void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
+TEST_CONSTEXPR_CXX26 void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int, N2> expected) {
   assert(std::is_sorted(input.begin(), input.begin() + midIdx));
   assert(std::is_sorted(input.begin() + midIdx, input.end()));
   assert(std::is_sorted(expected.begin(), expected.end()));
@@ -113,7 +114,7 @@ void testInplaceMergeImpl(std::array<int, N1> input, int midIdx, std::array<int,
 }
 
 template <class In, template <class> class SentWrapper>
-void testImpl() {
+TEST_CONSTEXPR_CXX26 void testImpl() {
   // sorted range
   {
     std::array in{0, 1, 5, 6, 9, 10};
@@ -193,14 +194,14 @@ void testImpl() {
 }
 
 template < template <class> class SentWrapper>
-void withAllPermutationsOfIter() {
+TEST_CONSTEXPR_CXX26 void withAllPermutationsOfIter() {
   testImpl<bidirectional_iterator<int*>, SentWrapper>();
   testImpl<random_access_iterator<int*>, SentWrapper>();
   testImpl<contiguous_iterator<int*>, SentWrapper>();
   testImpl<int*, SentWrapper>();
 }
 
-bool test() {
+TEST_CONSTEXPR_CXX26 bool test() {
   withAllPermutationsOfIter<std::type_identity_t>();
   withAllPermutationsOfIter<sentinel_wrapper>();
 
@@ -334,7 +335,9 @@ bool test() {
 
 int main(int, char**) {
   test();
-  // inplace_merge is not constexpr in the latest finished Standard (C++20)
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
 
   return 0;
 }

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
index e4316095f28bf..12743a08e3192 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -212,8 +212,12 @@ constexpr bool test_all() {
   }
   dangling_1st(std::ranges::partial_sort, in, mid);
   dangling_1st(std::ranges::nth_element, in, mid);
+#if TEST_STD_VER < 26
   if (!std::is_constant_evaluated())
+#endif
+  {
     dangling_1st(std::ranges::inplace_merge, in, mid);
+  }
   dangling_1st(std::ranges::make_heap, in);
   dangling_1st(std::ranges::push_heap, in);
   dangling_1st(std::ranges::pop_heap, in);

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
index aec07e9626f73..914e31d133805 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_omitting_invoke.pass.cpp
@@ -179,8 +179,12 @@ constexpr bool test_all() {
   }
   test_mid(std::ranges::partial_sort, in, mid, &Foo::binary_pred, &Bar::val);
   test_mid(std::ranges::nth_element, in, mid, &Foo::binary_pred, &Bar::val);
+#if TEST_STD_VER < 26
   if (!std::is_constant_evaluated())
+#endif
+  {
     test_mid(std::ranges::inplace_merge, in, mid, &Foo::binary_pred, &Bar::val);
+  }
   test(std::ranges::make_heap, in, &Foo::binary_pred, &Bar::val);
   test(std::ranges::push_heap, in, &Foo::binary_pred, &Bar::val);
   test(std::ranges::pop_heap, in, &Foo::binary_pred, &Bar::val);

diff  --git a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
index 675c2d114b3ac..2ed2cb54788a6 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_proxy_iterators.pass.cpp
@@ -182,8 +182,12 @@ constexpr void run_tests() {
   }
   test_mid(std::ranges::partial_sort, in, mid);
   test_mid(std::ranges::nth_element, in, mid);
+#if TEST_STD_VER < 26
   if (!std::is_constant_evaluated())
+#endif
+  {
     test_mid(std::ranges::inplace_merge, in, mid);
+  }
   test(std::ranges::make_heap, in);
   test(std::ranges::push_heap, in);
   test(std::ranges::pop_heap, in);

diff  --git a/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp b/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
index 6bd2abded2463..32154f9eda8f6 100644
--- a/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
+++ b/libcxx/test/std/algorithms/robust_against_adl_on_new.pass.cpp
@@ -15,23 +15,36 @@
 
 struct A {
     int i = 0;
-    bool operator<(const A& rhs) const { return i < rhs.i; }
-    static bool isEven(const A& a) { return a.i % 2 == 0; }
+    TEST_CONSTEXPR bool operator<(const A& rhs) const { return i < rhs.i; }
+    static TEST_CONSTEXPR bool isEven(const A& a) { return a.i % 2 == 0; }
 };
 
 void *operator new(std::size_t, A*) = delete;
 
-int main(int, char**)
-{
-    A a[4] = {};
-    std::sort(a, a+4);
-    std::sort(a, a+4, std::less<A>());
-    std::partition(a, a+4, A::isEven);
-    std::stable_sort(a, a+4);
-    std::stable_sort(a, a+4, std::less<A>());
-    std::stable_partition(a, a+4, A::isEven);
-    std::inplace_merge(a, a+2, a+4);
-    std::inplace_merge(a, a+2, a+4, std::less<A>());
-
-    return 0;
+TEST_CONSTEXPR_CXX20 bool test() {
+  A a[4] = {};
+  std::sort(a, a + 4);
+  std::sort(a, a + 4, std::less<A>());
+  std::partition(a, a + 4, A::isEven);
+#if TEST_STD_VER < 26
+  if (!TEST_IS_CONSTANT_EVALUATED)
+#endif
+  {
+    std::stable_sort(a, a + 4);
+    std::stable_sort(a, a + 4, std::less<A>());
+    std::stable_partition(a, a + 4, A::isEven);
+    std::inplace_merge(a, a + 2, a + 4);
+    std::inplace_merge(a, a + 2, a + 4, std::less<A>());
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+
+  return 0;
 }


        


More information about the libcxx-commits mailing list