[libcxx-commits] [libcxx] 8a61749 - [libc++][ranges] implement `std::ranges::inplace_merge`

Hui Xie via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 28 00:39:57 PDT 2022


Author: Hui Xie
Date: 2022-07-28T08:37:48+01:00
New Revision: 8a61749f767e9af773051fc4f6dc99276fe189e3

URL: https://github.com/llvm/llvm-project/commit/8a61749f767e9af773051fc4f6dc99276fe189e3
DIFF: https://github.com/llvm/llvm-project/commit/8a61749f767e9af773051fc4f6dc99276fe189e3.diff

LOG: [libc++][ranges] implement `std::ranges::inplace_merge`

Differential Revision: https://reviews.llvm.org/D130627

Added: 
    libcxx/include/__algorithm/algorithm_family.h

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__algorithm/inplace_merge.h
    libcxx/include/__algorithm/ranges_inplace_merge.h
    libcxx/include/__algorithm/stable_sort.h
    libcxx/include/algorithm
    libcxx/include/module.modulemap.in
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp
    libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp
    libcxx/test/libcxx/private_headers.verify.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_nonbool_predicates.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/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index ecbfb3329c536..3ae87225c3eb9 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -77,7 +77,7 @@ Permutation,sort,Konstantin Varlamov,`D127557 <https://llvm.org/D127557>`_,✅
 Permutation,stable_sort,Konstantin Varlamov,`D127834 <https://llvm.org/D127834>`_,✅
 Permutation,nth_element,Konstantin Varlamov,`D128149 <https://llvm.org/D128149>`_,✅
 Permutation,partial_sort,Konstantin Varlamov,`D128744 <https://llvm.org/D128744>`_,✅
-Permutation,inplace_merge,Not assigned,n/a,Not started
+Permutation,inplace_merge,Hui Xie,`D130627 <https://llvm.org/D130627>`_,✅
 Permutation,make_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅
 Permutation,push_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅
 Permutation,pop_heap,Konstantin Varlamov,`D128115 <https://llvm.org/D128115>`_,✅

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 970b4683d37b6..371f0c0cf223b 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1,5 +1,6 @@
 set(files
   __algorithm/adjacent_find.h
+  __algorithm/algorithm_family.h
   __algorithm/all_of.h
   __algorithm/any_of.h
   __algorithm/binary_search.h

diff  --git a/libcxx/include/__algorithm/algorithm_family.h b/libcxx/include/__algorithm/algorithm_family.h
new file mode 100644
index 0000000000000..30ffff832ebc3
--- /dev/null
+++ b/libcxx/include/__algorithm/algorithm_family.h
@@ -0,0 +1,52 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H
+#define _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H
+
+#include <__algorithm/iterator_operations.h>
+#include <__algorithm/move.h>
+#include <__algorithm/ranges_move.h>
+#include <__config>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _AlgPolicy>
+struct _AlgFamily;
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+template <>
+struct _AlgFamily<_RangeAlgPolicy> {
+  static constexpr auto __move = ranges::move;
+};
+
+#endif
+
+template <>
+struct _AlgFamily<_ClassicAlgPolicy> {
+
+  // move
+  template <class _InputIterator, class _OutputIterator>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17 static _OutputIterator
+  __move(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
+    return std::move(
+        std::move(__first),
+        std::move(__last),
+        std::move(__result));
+  }
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ALGORITHM_ALGORITHM_FAMILY_H

diff  --git a/libcxx/include/__algorithm/inplace_merge.h b/libcxx/include/__algorithm/inplace_merge.h
index cb662e791872b..e33894eada3f2 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_INPLACE_MERGE_H
 #define _LIBCPP___ALGORITHM_INPLACE_MERGE_H
 
+#include <__algorithm/algorithm_family.h>
 #include <__algorithm/comp.h>
 #include <__algorithm/comp_ref_type.h>
 #include <__algorithm/iterator_operations.h>
@@ -54,18 +55,17 @@ class __invert // invert the sense of a comparison
     bool operator()(const _T1& __x, const _T2& __y) {return __p_(__y, __x);}
 };
 
-template <class _AlgPolicy, class _Compare, class _InputIterator1, class _InputIterator2,
-          class _OutputIterator>
-void __half_inplace_merge(_InputIterator1 __first1, _InputIterator1 __last1,
-                          _InputIterator2 __first2, _InputIterator2 __last2,
-                          _OutputIterator __result, _Compare __comp)
+template <class _AlgPolicy, class _Compare, class _InputIterator1, class _Sent1,
+          class _InputIterator2, class _Sent2, class _OutputIterator>
+void __half_inplace_merge(_InputIterator1 __first1, _Sent1 __last1,
+                          _InputIterator2 __first2, _Sent2 __last2,
+                          _OutputIterator __result, _Compare&& __comp)
 {
     for (; __first1 != __last1; ++__result)
     {
         if (__first2 == __last2)
         {
-            // TODO(alg-policy): pass `_AlgPolicy` once it's supported by `move`.
-            _VSTD::move(__first1, __last1, __result);
+            _AlgFamily<_AlgPolicy>::__move(__first1, __last1, __result);
             return;
         }
 
@@ -84,13 +84,15 @@ void __half_inplace_merge(_InputIterator1 __first1, _InputIterator1 __last1,
 }
 
 template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-void
-__buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
-                _Compare __comp, typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len1,
-                                 typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len2,
-                typename iterator_traits<_BidirectionalIterator>::value_type* __buff)
-{
-    typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
+void __buffered_inplace_merge(
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __middle,
+    _BidirectionalIterator __last,
+    _Compare&& __comp,
+    typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len1,
+    typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len2,
+    typename iterator_traits<_BidirectionalIterator>::value_type* __buff) {
+  typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
     __destruct_n __d(0);
     unique_ptr<value_type, __destruct_n&> __h2(__buff, __d);
     if (__len1 <= __len2)
@@ -98,7 +100,7 @@ __buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator
         value_type* __p = __buff;
         for (_BidirectionalIterator __i = __first; __i != __middle; __d.template __incr<value_type>(), (void) ++__i, (void) ++__p)
             ::new ((void*)__p) value_type(_IterOps<_AlgPolicy>::__iter_move(__i));
-        std::__half_inplace_merge<_AlgPolicy, _Compare>(__buff, __p, __middle, __last, __first, __comp);
+        std::__half_inplace_merge<_AlgPolicy>(__buff, __p, __middle, __last, __first, __comp);
     }
     else
     {
@@ -108,19 +110,22 @@ __buffered_inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator
         typedef __unconstrained_reverse_iterator<_BidirectionalIterator> _RBi;
         typedef __unconstrained_reverse_iterator<value_type*> _Rv;
         typedef __invert<_Compare> _Inverted;
-        std::__half_inplace_merge<_AlgPolicy, _Inverted>(_Rv(__p), _Rv(__buff),
+        std::__half_inplace_merge<_AlgPolicy>(_Rv(__p), _Rv(__buff),
                                     _RBi(__middle), _RBi(__first),
                                     _RBi(__last), _Inverted(__comp));
     }
 }
 
 template <class _AlgPolicy, class _Compare, class _BidirectionalIterator>
-void
-__inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
-                _Compare __comp, typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len1,
-                                 typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len2,
-                typename iterator_traits<_BidirectionalIterator>::value_type* __buff, ptr
diff _t __buff_size)
-{
+void __inplace_merge(
+    _BidirectionalIterator __first,
+    _BidirectionalIterator __middle,
+    _BidirectionalIterator __last,
+    _Compare&& __comp,
+    typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len1,
+    typename iterator_traits<_BidirectionalIterator>::
diff erence_type __len2,
+    typename iterator_traits<_BidirectionalIterator>::value_type* __buff,
+    ptr
diff _t __buff_size) {
     using _Ops = _IterOps<_AlgPolicy>;
 
     typedef typename iterator_traits<_BidirectionalIterator>::
diff erence_type 
diff erence_type;
@@ -130,7 +135,7 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
         if (__len2 == 0)
             return;
         if (__len1 <= __buff_size || __len2 <= __buff_size)
-            return std::__buffered_inplace_merge<_AlgPolicy, _Compare>
+            return std::__buffered_inplace_merge<_AlgPolicy>
                    (__first, __middle, __last, __comp, __len1, __len2, __buff);
         // shrink [__first, __middle) as much as possible (with no moves), returning if it shrinks to 0
         for (; true; ++__first, (void) --__len1)
@@ -158,8 +163,7 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
             __len21 = __len2 / 2;
             __m2 = __middle;
             _Ops::advance(__m2, __len21);
-            // TODO: replace _ClassicAlgPolicy and __identity with _AlgPolicy and projection
-            __m1 = std::__upper_bound<_ClassicAlgPolicy>(__first, __middle, *__m2, __comp, std::__identity());
+            __m1 = std::__upper_bound<_AlgPolicy>(__first, __middle, *__m2, __comp, std::__identity());
             __len11 = _Ops::distance(__first, __m1);
         }
         else
@@ -187,9 +191,8 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
         // merge smaller range with recursive call and larger with tail recursion elimination
         if (__len11 + __len21 < __len12 + __len22)
         {
-            std::__inplace_merge<_AlgPolicy, _Compare>(
+            std::__inplace_merge<_AlgPolicy>(
                 __first, __m1, __middle, __comp, __len11, __len21, __buff, __buff_size);
-//          _VSTD::__inplace_merge<_Compare>(__middle, __m2, __last, __comp, __len12, __len22, __buff, __buff_size);
             __first = __middle;
             __middle = __m2;
             __len1 = __len12;
@@ -197,9 +200,8 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
         }
         else
         {
-            std::__inplace_merge<_AlgPolicy, _Compare>(
+            std::__inplace_merge<_AlgPolicy>(
                 __middle, __m2, __last, __comp, __len12, __len22, __buff, __buff_size);
-//          _VSTD::__inplace_merge<_Compare>(__first, __m1, __middle, __comp, __len11, __len21, __buff, __buff_size);
             __last = __middle;
             __middle = __m1;
             __len1 = __len11;
@@ -208,33 +210,40 @@ __inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle,
     }
 }
 
-template <class _BidirectionalIterator, class _Compare>
-inline _LIBCPP_INLINE_VISIBILITY
+template <class _AlgPolicy, class _BidirectionalIterator, class _Compare>
+_LIBCPP_HIDE_FROM_ABI
 void
-inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
-              _Compare __comp)
+__inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last,
+              _Compare&& __comp)
 {
     typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
     typedef typename iterator_traits<_BidirectionalIterator>::
diff erence_type 
diff erence_type;
-    
diff erence_type __len1 = _VSTD::distance(__first, __middle);
-    
diff erence_type __len2 = _VSTD::distance(__middle, __last);
+    
diff erence_type __len1 = _IterOps<_AlgPolicy>::distance(__first, __middle);
+    
diff erence_type __len2 = _IterOps<_AlgPolicy>::distance(__middle, __last);
     
diff erence_type __buf_size = _VSTD::min(__len1, __len2);
 // TODO: Remove the use of std::get_temporary_buffer
 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
     pair<value_type*, ptr
diff _t> __buf = _VSTD::get_temporary_buffer<value_type>(__buf_size);
 _LIBCPP_SUPPRESS_DEPRECATED_POP
     unique_ptr<value_type, __return_temporary_buffer> __h(__buf.first);
-    typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
-    return _VSTD::__inplace_merge<_ClassicAlgPolicy, _Comp_ref>(__first, __middle, __last, __comp, __len1, __len2,
-                                            __buf.first, __buf.second);
+    return std::__inplace_merge<_AlgPolicy>(
+        std::move(__first), std::move(__middle), std::move(__last), __comp, __len1, __len2, __buf.first, __buf.second);
+}
+
+template <class _BidirectionalIterator, class _Compare>
+inline _LIBCPP_HIDE_FROM_ABI void inplace_merge(
+    _BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last, _Compare __comp) {
+  typedef typename __comp_ref_type<_Compare>::type _Comp_ref;
+  std::__inplace_merge<_ClassicAlgPolicy>(
+      std::move(__first), std::move(__middle), std::move(__last), static_cast<_Comp_ref>(__comp));
 }
 
 template <class _BidirectionalIterator>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_HIDE_FROM_ABI
 void
 inplace_merge(_BidirectionalIterator __first, _BidirectionalIterator __middle, _BidirectionalIterator __last)
 {
-    _VSTD::inplace_merge(__first, __middle, __last,
+    std::inplace_merge(std::move(__first), std::move(__middle), std::move(__last),
                         __less<typename iterator_traits<_BidirectionalIterator>::value_type>());
 }
 

diff  --git a/libcxx/include/__algorithm/ranges_inplace_merge.h b/libcxx/include/__algorithm/ranges_inplace_merge.h
index a0867e486c3a0..2152e6648c350 100644
--- a/libcxx/include/__algorithm/ranges_inplace_merge.h
+++ b/libcxx/include/__algorithm/ranges_inplace_merge.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ALGORITHM_RANGES_INPLACE_MERGE_H
 
 #include <__algorithm/inplace_merge.h>
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/make_projected.h>
 #include <__config>
 #include <__functional/identity.h>
@@ -17,6 +18,7 @@
 #include <__functional/ranges_operations.h>
 #include <__iterator/concepts.h>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
 #include <__iterator/projected.h>
 #include <__iterator/sortable.h>
 #include <__ranges/access.h>
@@ -36,28 +38,38 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 namespace ranges {
 namespace __inplace_merge {
 
-struct __fn {
+  struct __fn {
+    template <class _Iter, class _Sent, class _Comp, class _Proj>
+    _LIBCPP_HIDE_FROM_ABI static constexpr 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>(
+          std::move(__first), std::move(__middle), __last_iter, ranges::__make_projected_comp(__comp, __proj));
+      return __last_iter;
+    }
 
-  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 operator()(_Iter __first, _Iter __middle, _Sent __last, _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__first; (void)__middle; (void)__last; (void)__comp; (void)__proj;
-    return {};
-  }
+    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
+    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));
+    }
 
-  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> operator()(_Range&& __range, iterator_t<_Range> __middle,
-                                            _Comp __comp = {}, _Proj __proj = {}) const {
-    // TODO: implement
-    (void)__range; (void)__middle; (void)__comp; (void)__proj;
-    return {};
-  }
-
-};
+    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>
+      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));
+    }
+  };
 
 } // namespace __inplace_merge
 

diff  --git a/libcxx/include/__algorithm/stable_sort.h b/libcxx/include/__algorithm/stable_sort.h
index 6122758bdefe8..fb149eeb65dc0 100644
--- a/libcxx/include/__algorithm/stable_sort.h
+++ b/libcxx/include/__algorithm/stable_sort.h
@@ -203,7 +203,7 @@ __stable_sort(_RandomAccessIterator __first, _RandomAccessIterator __last, _Comp
     }
     std::__stable_sort<_AlgPolicy, _Compare>(__first, __m, __comp, __l2, __buff, __buff_size);
     std::__stable_sort<_AlgPolicy, _Compare>(__m, __last, __comp, __len - __l2, __buff, __buff_size);
-    std::__inplace_merge<_AlgPolicy, _Compare>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
+    std::__inplace_merge<_AlgPolicy>(__first, __m, __last, __comp, __l2, __len - __l2, __buff, __buff_size);
 }
 
 template <class _AlgPolicy, class _RandomAccessIterator, class _Compare>

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 197b3b1043bbe..4fc353a8ce5dd 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -803,7 +803,7 @@ namespace ranges {
       set_symmetric_
diff erence(I1 first1, S1 last1, I2 first2, S2 last2, O result,
                                Comp comp = {}, Proj1 proj1 = {},
                                Proj2 proj2 = {});                                                   // since C++20
-  
+
   template<input_range R1, input_range R2, weakly_incrementable O,
            class Comp = ranges::less, class Proj1 = identity, class Proj2 = identity>
     requires mergeable<iterator_t<R1>, iterator_t<R2>, O, Comp, Proj1, Proj2>
@@ -816,13 +816,13 @@ namespace ranges {
            indirect_strict_weak_order<const T*, projected<I, Proj>> Comp = ranges::less>
     constexpr subrange<I>
       equal_range(I first, S last, const T& value, Comp comp = {}, Proj proj = {});                 // since C++20
-  
+
   template<forward_range R, class T, class Proj = identity,
            indirect_strict_weak_order<const T*, projected<iterator_t<R>, Proj>> Comp =
              ranges::less>
     constexpr borrowed_subrange_t<R>
       equal_range(R&& r, const T& value, Comp comp = {}, Proj proj = {});                           // since C++20
-  
+
   template<class I1, class I2, class O>
     using set_union_result = in_in_out_result<I1, I2, O>;                                           // since C++20
 
@@ -847,13 +847,24 @@ namespace ranges {
              ranges::less>
     constexpr bool includes(I1 first1, S1 last1, I2 first2, S2 last2, Comp comp = {},
                             Proj1 proj1 = {}, Proj2 proj2 = {});                                   // Since C++20
-  
+
   template<input_range R1, input_range R2, class Proj1 = identity,
            class Proj2 = identity,
            indirect_strict_weak_order<projected<iterator_t<R1>, Proj1>,
                                       projected<iterator_t<R2>, Proj2>> Comp = ranges::less>
     constexpr bool includes(R1&& r1, R2&& r2, Comp comp = {},
                             Proj1 proj1 = {}, Proj2 proj2 = {});                                   // Since C++20
+
+  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
+
+  template<bidirectional_range R, class Comp = ranges::less, class Proj = identity>
+    requires sortable<iterator_t<R>, Comp, Proj>
+    borrowed_iterator_t<R>
+      inplace_merge(R&& r, iterator_t<R> middle, Comp comp = {},
+                    Proj proj = {});                                                               // Since C++20
 }
 
     constexpr bool     // constexpr in C++20
@@ -1607,6 +1618,7 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/ranges_generate.h>
 #include <__algorithm/ranges_generate_n.h>
 #include <__algorithm/ranges_includes.h>
+#include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_is_heap.h>
 #include <__algorithm/ranges_is_heap_until.h>
 #include <__algorithm/ranges_is_partitioned.h>

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 98485bcd93ab3..1cfd4ae3bfef4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -239,6 +239,7 @@ module std [system] {
 
     module __algorithm {
       module adjacent_find                   { private header "__algorithm/adjacent_find.h" }
+      module algorithm_family                { private header "__algorithm/algorithm_family.h" }
       module all_of                          { private header "__algorithm/all_of.h" }
       module any_of                          { private header "__algorithm/any_of.h" }
       module binary_search                   { private header "__algorithm/binary_search.h" }

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 48eeb01b57587..669fa9c1842ef 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
@@ -138,8 +138,8 @@ 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 (!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); }
     (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);

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 80b8a5ec4d836..22fdf09888af7 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
@@ -121,8 +121,8 @@ 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 (!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); }
     (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);

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index f9cb581c16b83..5b3e65be251d9 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -37,6 +37,7 @@ END-SCRIPT
 // DO NOT MANUALLY EDIT ANYTHING BETWEEN THE MARKERS BELOW
 // GENERATED-MARKER
 #include <__algorithm/adjacent_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/adjacent_find.h'}}
+#include <__algorithm/algorithm_family.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/algorithm_family.h'}}
 #include <__algorithm/all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/all_of.h'}}
 #include <__algorithm/any_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/any_of.h'}}
 #include <__algorithm/binary_search.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/binary_search.h'}}

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 fff2a67004557..12863b8b9e01f 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
@@ -27,23 +27,315 @@
 #include <concepts>
 #include <functional>
 #include <ranges>
+#include <type_traits>
 
 #include "almost_satisfies_types.h"
+#include "counting_predicates.h"
+#include "counting_projection.h"
 #include "test_iterators.h"
 
-// TODO: SFINAE tests.
+template < class Iter,
+           class Middle = Iter,
+           class Sent   = sentinel_wrapper<std::remove_cvref_t<Iter>>,
+           class Comp   = std::ranges::less,
+           class Proj   = std::identity>
+concept HasInplaceMergeIter =
+    requires(Iter&& iter, Middle&& mid, Sent&& sent, Comp&& comp, Proj&& proj) {
+      std::ranges::inplace_merge(
+          std::forward<Iter>(iter),
+          std::forward<Middle>(mid),
+          std::forward<Sent>(sent),
+          std::forward<Comp>(comp),
+          std::forward<Proj>(proj));
+    };
 
-constexpr bool test() {
-  // TODO: main tests.
-  // TODO: A custom comparator works.
-  // TODO: A custom projection works.
+static_assert(HasInplaceMergeIter<int*, int*, int*>);
 
+// !bidirectional_­iterator<I>
+static_assert(!HasInplaceMergeIter<BidirectionalIteratorNotDerivedFrom>);
+static_assert(!HasInplaceMergeIter<cpp20_input_iterator<int*>>);
+
+// !sentinel_for<S, I>
+static_assert(!HasInplaceMergeIter<int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasInplaceMergeIter<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// !sortable<I, Comp, Proj>
+static_assert(!HasInplaceMergeIter<int*, int*, int*, ComparatorNotCopyable<int*>>);
+static_assert(!HasInplaceMergeIter<const int*, const int*, const int*>);
+
+template < class Range,
+           class Middle = std::ranges::iterator_t<Range>,
+           class Comp   = std::ranges::less,
+           class Proj   = std::identity>
+concept HasInplaceMergeRange =
+    requires(Range&& r, Middle&& mid, Comp&& comp, Proj&& proj) {
+      std::ranges::inplace_merge(
+          std::forward<Range>(r), std::forward<Middle>(mid), std::forward<Comp>(comp), std::forward<Proj>(proj));
+    };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasInplaceMergeRange<R<int*>, int*>);
+
+// !bidirectional_range<R>
+static_assert(!HasInplaceMergeRange<R<cpp20_input_iterator<int*>>>);
+static_assert(!HasInplaceMergeRange<R<BidirectionalIteratorNotDecrementable>>);
+
+// !sortable<iterator_t<R>, Comp, Proj>
+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) {
+  std::is_sorted(input.begin(), input.begin() + midIdx);
+  std::is_sorted(input.begin() + midIdx, input.end());
+  std::is_sorted(expected.begin(), expected.end());
+
+  using Sent = SentWrapper<In>;
+
+  // iterator overload
+  {
+    auto in = input;
+    std::same_as<In> decltype(auto) result =
+        std::ranges::inplace_merge(In{in.data()}, In{in.data() + midIdx}, Sent{In{in.data() + in.size()}});
+    assert(std::ranges::equal(in, expected));
+    assert(base(result) == in.data() + in.size());
+  }
+
+  // range overload
+  {
+    auto in = input;
+    std::ranges::subrange r{In{in.data()}, Sent{In{in.data() + in.size()}}};
+    std::same_as<In> decltype(auto) result = std::ranges::inplace_merge(r, In{in.data() + midIdx});
+    assert(std::ranges::equal(in, expected));
+    assert(base(result) == in.data() + in.size());
+  }
+}
+
+template <class In, template <class> class SentWrapper>
+void testImpl() {
+  // sorted range
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    std::array expected = in;
+    testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
+  }
+
+  // [first, mid) is longer
+  {
+    std::array in{0, 5, 9, 15, 18, 22, 2, 4, 6, 10};
+    std::array expected = {0, 2, 4, 5, 6, 9, 10, 15, 18, 22};
+    testInplaceMergeImpl<In, SentWrapper>(in, 6, expected);
+  }
+
+  // [first, mid) is shorter
+  {
+    std::array in{0, 5, 9, 2, 4, 6, 10};
+    std::array expected = {0, 2, 4, 5, 6, 9, 10};
+    testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
+  }
+
+  // [first, mid) == [mid, last)
+  {
+    std::array in{0, 5, 9, 0, 5, 9};
+    std::array expected = {0, 0, 5, 5, 9, 9};
+    testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
+  }
+
+  // duplicates within each range
+  {
+    std::array in{1, 5, 5, 2, 9, 9, 9};
+    std::array expected = {1, 2, 5, 5, 9, 9, 9};
+    testInplaceMergeImpl<In, SentWrapper>(in, 3, expected);
+  }
+
+  // all the same
+  {
+    std::array in{5, 5, 5, 5, 5, 5, 5, 5};
+    std::array expected = in;
+    testInplaceMergeImpl<In, SentWrapper>(in, 5, expected);
+  }
+
+  // [first, mid) is empty (mid == begin)
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    std::array expected = in;
+    testInplaceMergeImpl<In, SentWrapper>(in, 0, expected);
+  }
+
+  // [mid, last] is empty (mid == end)
+  {
+    std::array in{0, 1, 5, 6, 9, 10};
+    std::array expected = in;
+    testInplaceMergeImpl<In, SentWrapper>(in, 6, expected);
+  }
+
+  // both empty
+  {
+    std::array<int, 0> in{};
+    std::array expected = in;
+    testInplaceMergeImpl<In, SentWrapper>(in, 0, expected);
+  }
+
+  // mid == first + 1
+  {
+    std::array in{9, 2, 5, 7, 10};
+    std::array expected{2, 5, 7, 9, 10};
+    testInplaceMergeImpl<In, SentWrapper>(in, 1, expected);
+  }
+
+  // mid == last - 1
+  {
+    std::array in{2, 5, 7, 10, 9};
+    std::array expected{2, 5, 7, 9, 10};
+    testInplaceMergeImpl<In, SentWrapper>(in, 4, expected);
+  }
+}
+
+template < template <class> class SentWrapper>
+void withAllPermutationsOfIter() {
+  testImpl<bidirectional_iterator<int*>, SentWrapper>();
+  testImpl<random_access_iterator<int*>, SentWrapper>();
+  testImpl<contiguous_iterator<int*>, SentWrapper>();
+  testImpl<int*, SentWrapper>();
+}
+
+bool test() {
+  withAllPermutationsOfIter<std::type_identity_t>();
+  withAllPermutationsOfIter<sentinel_wrapper>();
+
+  struct Data {
+    int data;
+  };
+
+  const auto equal = [](const Data& x, const Data& y) { return x.data == y.data; };
+  // Test custom comparator
+  {
+    std::array<Data, 4> input{{{4}, {8}, {2}, {5}}};
+    std::array<Data, 4> expected{{{2}, {4}, {5}, {8}}};
+    const auto comp = [](const Data& x, const Data& y) { return x.data < y.data; };
+
+    // iterator overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 2, in.end(), comp);
+      assert(std::ranges::equal(in, expected, equal));
+      assert(result == in.end());
+    }
+
+    // range overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in, in.begin() + 2, comp);
+      assert(std::ranges::equal(in, expected, equal));
+      assert(result == in.end());
+    }
+  }
+
+  // Test custom projection
+  {
+    std::array<Data, 4> input{{{4}, {8}, {2}, {5}}};
+    std::array<Data, 4> expected{{{2}, {4}, {5}, {8}}};
+
+    const auto proj = &Data::data;
+
+    // iterator overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 2, in.end(), {}, proj);
+      assert(std::ranges::equal(in, expected, equal));
+      assert(result == in.end());
+    }
+
+    // range overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in, in.begin() + 2, {}, proj);
+      assert(std::ranges::equal(in, expected, equal));
+      assert(result == in.end());
+    }
+  }
+
+  // Remarks: Stable.
+  {
+    struct IntAndID {
+      int data;
+      int id;
+      constexpr auto operator<=>(const IntAndID& rhs) const { return data <=> rhs.data; }
+      constexpr auto operator==(const IntAndID& rhs) const { return data == rhs.data; }
+    };
+    std::array<IntAndID, 6> input{{{0, 0}, {1, 0}, {2, 0}, {0, 1}, {1, 1}, {2, 1}}};
+
+    // iterator overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in.begin(), in.begin() + 3, in.end());
+      assert(std::ranges::equal(in, std::array{0, 0, 1, 1, 2, 2}, {}, &IntAndID::data));
+      assert(std::ranges::equal(in, std::array{0, 1, 0, 1, 0, 1}, {}, &IntAndID::id));
+      assert(result == in.end());
+    }
+
+    // range overload
+    {
+      auto in     = input;
+      auto result = std::ranges::inplace_merge(in, in.begin() + 3);
+      assert(std::ranges::equal(in, std::array{0, 0, 1, 1, 2, 2}, {}, &IntAndID::data));
+      assert(std::ranges::equal(in, std::array{0, 1, 0, 1, 0, 1}, {}, &IntAndID::id));
+      assert(result == in.end());
+    }
+  }
+
+  // Complexity: Let N = last - first :
+  //   - For the overloads with no ExecutionPolicy, and if enough
+  //     additional memory is available, exactly N − 1 comparisons.
+  //   - Otherwise, O(NlogN) comparisons.
+  // In either case, twice as many projections as comparisons.
+  {
+    std::array input{1, 2, 3, 3, 3, 7, 7, 2, 2, 5, 5, 6, 6};
+    std::array expected{1, 2, 2, 2, 3, 3, 3, 5, 5, 6, 6, 7, 7};
+    auto mid = 7;
+    // iterator overload
+    {
+      auto in          = input;
+      int numberOfComp = 0;
+      int numberOfProj = 0;
+      auto result      = std::ranges::inplace_merge(
+          in.begin(),
+          in.begin() + mid,
+          in.end(),
+          counting_predicate{std::ranges::less{}, numberOfComp},
+          counting_projection{numberOfProj});
+      assert(std::ranges::equal(in, expected));
+      assert(result == in.end());
+
+      // the spec specifies exactly N-1 comparison but we actually
+      // do not invoke as many times as specified
+      assert(numberOfComp <= static_cast<int>(in.size() - 1));
+      assert(numberOfProj <= 2 * numberOfComp);
+    }
+    // range overload
+    {
+      auto in          = input;
+      int numberOfComp = 0;
+      int numberOfProj = 0;
+      auto result      = std::ranges::inplace_merge(
+          in,
+          in.begin() + mid,
+          counting_predicate{std::ranges::less{}, numberOfComp},
+          counting_projection{numberOfProj});
+      assert(std::ranges::equal(in, expected));
+      assert(result == in.end());
+      assert(numberOfComp <= static_cast<int>(in.size() - 1));
+      assert(numberOfProj <= 2 * numberOfComp);
+    }
+  }
   return true;
 }
 
 int main(int, char**) {
   test();
-  static_assert(test());
+  // inplace_merge is not constexpr in the latest finished Standard (C++20)
 
   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 40d1043d4b1b4..e147c875902c2 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -193,8 +193,8 @@ constexpr bool test_all() {
     dangling_1st(std::ranges::stable_sort, in);
   dangling_1st(std::ranges::partial_sort, in, mid);
   dangling_1st(std::ranges::nth_element, in, mid);
-  //if (!std::is_constant_evaluated())
-  //  dangling_1st(std::ranges::inplace_merge, in, mid);
+  if (!std::is_constant_evaluated())
+    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_nonbool_predicates.pass.cpp b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
index f5f7160869368..eeeec9c1f57f3 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_nonbool_predicates.pass.cpp
@@ -35,36 +35,36 @@ static_assert(std::convertible_to<decltype(binary_pred(1, 2)), bool>);
 // Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid.
 
 // (in, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test(Func&& func, Input& in, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test(Func&& func, Input& in, Args&&... args) {
   func(in.begin(), in.end(), std::forward<Args>(args)...);
   func(in, std::forward<Args>(args)...);
 }
 
 // (in1, in2, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test(Func&& func, Input& in1, Input& in2, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) {
   func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward<Args>(args)...);
   func(in1, in2, std::forward<Args>(args)...);
 }
 
 // (in, mid, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
   func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
   func(in, mid, std::forward<Args>(args)...);
 }
 
 constexpr bool test_all() {
-  std::array in = {1, 2, 3};
+  std::array in  = {1, 2, 3};
   std::array in2 = {4, 5, 6};
-  auto mid = in.begin() + 1;
+  auto mid       = in.begin() + 1;
 
   std::array output = {7, 8, 9, 10, 11, 12};
-  auto out = output.begin();
-  auto out2 = output.begin() + 1;
+  auto out          = output.begin();
+  auto out2         = output.begin() + 1;
 
-  int x = 2;
+  int x     = 2;
   int count = 1;
 
   test(std::ranges::any_of, in, unary_pred);
@@ -133,7 +133,8 @@ constexpr bool test_all() {
     test(std::ranges::stable_sort, in, binary_pred);
   test_mid(std::ranges::partial_sort, in, mid, binary_pred);
   test_mid(std::ranges::nth_element, in, mid, binary_pred);
-  //test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
+  if (!std::is_constant_evaluated())
+    test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
   test(std::ranges::make_heap, in, binary_pred);
   test(std::ranges::push_heap, in, binary_pred);
   test(std::ranges::pop_heap, in, binary_pred);

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 2da3db405c324..aa0b15db6dee6 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
@@ -36,34 +36,34 @@ struct Bar {
 // Invokes both the (iterator, sentinel, ...) and the (range, ...) overloads of the given niebloid.
 
 // (in, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test(Func&& func, Input& in, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test(Func&& func, Input& in, Args&&... args) {
   func(in.begin(), in.end(), std::forward<Args>(args)...);
   func(in, std::forward<Args>(args)...);
 }
 
 // (in1, in2, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test(Func&& func, Input& in1, Input& in2, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test(Func&& func, Input& in1, Input& in2, Args&&... args) {
   func(in1.begin(), in1.end(), in2.begin(), in2.end(), std::forward<Args>(args)...);
   func(in1, in2, std::forward<Args>(args)...);
 }
 
 // (in, mid, ...)
-template <class Func, std::ranges::range Input, class ...Args>
-constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&& ...args) {
+template <class Func, std::ranges::range Input, class... Args>
+constexpr void test_mid(Func&& func, Input& in, std::ranges::iterator_t<Input> mid, Args&&... args) {
   func(in.begin(), mid, in.end(), std::forward<Args>(args)...);
   func(in, mid, std::forward<Args>(args)...);
 }
 
 constexpr bool test_all() {
-  std::array in = {Bar{Foo{1}}, Bar{Foo{2}}, Bar{Foo{3}}};
+  std::array in  = {Bar{Foo{1}}, Bar{Foo{2}}, Bar{Foo{3}}};
   std::array in2 = {Bar{Foo{4}}, Bar{Foo{5}}, Bar{Foo{6}}};
-  auto mid = in.begin() + 1;
+  auto mid       = in.begin() + 1;
 
   std::array output = {Bar{Foo{7}}, Bar{Foo{8}}, Bar{Foo{9}}, Bar{Foo{10}}, Bar{Foo{11}}, Bar{Foo{12}}};
-  auto out = output.begin();
-  auto out2 = output.begin() + 1;
+  auto out          = output.begin();
+  auto out2         = output.begin() + 1;
 
   Bar a{Foo{1}};
   Bar b{Foo{2}};
@@ -162,7 +162,8 @@ constexpr bool test_all() {
     test(std::ranges::stable_sort, in, &Foo::binary_pred, &Bar::val);
   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);
-  //test_mid(std::ranges::inplace_merge, in, mid, binary_pred);
+  if (!std::is_constant_evaluated())
+    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 aba570160899a..4013065eb42a4 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
@@ -164,6 +164,7 @@ constexpr void run_tests() {
   //  test(std::ranges::stable_sort, in);
   test_mid(std::ranges::partial_sort, in, mid);
   test_mid(std::ranges::nth_element, in, mid);
+  // TODO(ranges): `inplace_merge` requires `ranges::rotate` to be implemented.
   //if (!std::is_constant_evaluated())
   //  test_mid(std::ranges::inplace_merge, in, mid);
   test(std::ranges::make_heap, in);

diff  --git a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
index ebe78c1742216..1fc6dfb89c4b9 100644
--- a/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
+++ b/libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp
@@ -87,7 +87,7 @@ static_assert(test(std::ranges::for_each_n, a, 10, odd));
 static_assert(test(std::ranges::generate, a, gen));
 static_assert(test(std::ranges::generate_n, a, 10, gen));
 static_assert(test(std::ranges::includes, a, a));
-//static_assert(test(std::ranges::inplace_merge, a, a+5));
+static_assert(test(std::ranges::inplace_merge, a, a+5));
 static_assert(test(std::ranges::is_heap, a));
 static_assert(test(std::ranges::is_heap_until, a));
 static_assert(test(std::ranges::is_partitioned, a, odd));


        


More information about the libcxx-commits mailing list