[llvm-branch-commits] [libcxx] 33e5f15 - [libc++][ranges] Implement `ranges::rotate`.

Tom Stellard via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Aug 8 15:38:21 PDT 2022


Author: Konstantin Varlamov
Date: 2022-08-08T13:29:17-07:00
New Revision: 33e5f15934e91586a316671e49dfe29ce529d1bf

URL: https://github.com/llvm/llvm-project/commit/33e5f15934e91586a316671e49dfe29ce529d1bf
DIFF: https://github.com/llvm/llvm-project/commit/33e5f15934e91586a316671e49dfe29ce529d1bf.diff

LOG: [libc++][ranges] Implement `ranges::rotate`.

Also fix `ranges::stable_sort` and `ranges::inplace_merge` to support
proxy iterators now that their internal implementations can correctly
dispatch `rotate`.

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

(cherry picked from commit 36c746ca2d5b325a7ac64135c1ff8774c06ab34c)

Added: 
    libcxx/include/__algorithm/ranges_rotate.h
    libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/ranges_rotate.pass.cpp

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__algorithm/inplace_merge.h
    libcxx/include/__algorithm/iterator_operations.h
    libcxx/include/__algorithm/move.h
    libcxx/include/__algorithm/move_backward.h
    libcxx/include/__algorithm/ranges_move.h
    libcxx/include/__algorithm/ranges_swap_ranges.h
    libcxx/include/__algorithm/rotate.h
    libcxx/include/__algorithm/stable_partition.h
    libcxx/include/__algorithm/swap_ranges.h
    libcxx/include/__iterator/reverse_iterator.h
    libcxx/include/algorithm
    libcxx/include/module.modulemap.in
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/algorithms/ranges_robust_against_dangling.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: 
    libcxx/include/__algorithm/algorithm_family.h


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index b1d35131c0f89..8938c99e0ccb6 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -68,7 +68,7 @@ Merge,set_union,Hui Xie,`D129657 <https://llvm.org/D129657>`_,✅
 Permutation,remove,Nikolas Klauser,`D128618 <https://llvm.org/D128618>`_,✅
 Permutation,remove_if,Nikolas Klauser,`D128618 <https://llvm.org/D128618>`_,✅
 Permutation,reverse,Nikolas Klauser,`D125752 <https://llvm.org/D125752>`_,✅
-Permutation,rotate,Nikolas Klauser,`D124122 <https://llvm.org/D124122>`_,Under review
+Permutation,rotate,Konstantin Varlamov and Nikolas Klauser,`D130758 <https://llvm.org/D130758>`_,✅
 Permutation,shuffle,Konstantin Varlamov,`D130321 <https://llvm.org/D130321>`_,✅
 Permutation,unique,Hui Xie,`D130404 <https://llvm.org/D130404>`_,✅
 Permutation,partition,Konstantin Varlamov,`D129624 <https://llvm.org/D129624>`_,✅

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index e4da38dfb82ba..957779d3bf729 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1,6 +1,5 @@
 set(files
   __algorithm/adjacent_find.h
-  __algorithm/algorithm_family.h
   __algorithm/all_of.h
   __algorithm/any_of.h
   __algorithm/binary_search.h
@@ -134,6 +133,7 @@ set(files
   __algorithm/ranges_replace_if.h
   __algorithm/ranges_reverse.h
   __algorithm/ranges_reverse_copy.h
+  __algorithm/ranges_rotate.h
   __algorithm/ranges_rotate_copy.h
   __algorithm/ranges_sample.h
   __algorithm/ranges_search.h

diff  --git a/libcxx/include/__algorithm/algorithm_family.h b/libcxx/include/__algorithm/algorithm_family.h
deleted file mode 100644
index 30ffff832ebc3..0000000000000
--- a/libcxx/include/__algorithm/algorithm_family.h
+++ /dev/null
@@ -1,52 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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 e33894eada3f2..0890639f49523 100644
--- a/libcxx/include/__algorithm/inplace_merge.h
+++ b/libcxx/include/__algorithm/inplace_merge.h
@@ -9,7 +9,6 @@
 #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>
@@ -65,7 +64,7 @@ void __half_inplace_merge(_InputIterator1 __first1, _Sent1 __last1,
     {
         if (__first2 == __last2)
         {
-            _AlgFamily<_AlgPolicy>::__move(__first1, __last1, __result);
+            std::__move<_AlgPolicy>(__first1, __last1, __result);
             return;
         }
 
@@ -185,8 +184,7 @@ void __inplace_merge(
         
diff erence_type __len22 = __len2 - __len21;  // distance(__m2, __last)
         // [__first, __m1) [__m1, __middle) [__middle, __m2) [__m2, __last)
         // swap middle two partitions
-        // TODO(alg-policy): pass `_AlgPolicy` once it's supported by `rotate`.
-        __middle = _VSTD::rotate(__m1, __middle, __m2);
+        __middle = std::__rotate<_AlgPolicy>(__m1, __middle, __m2).first;
         // __len12 and __len21 now have swapped meanings
         // merge smaller range with recursive call and larger with tail recursion elimination
         if (__len11 + __len21 < __len12 + __len22)

diff  --git a/libcxx/include/__algorithm/iterator_operations.h b/libcxx/include/__algorithm/iterator_operations.h
index 2222b9883d601..af461878737f9 100644
--- a/libcxx/include/__algorithm/iterator_operations.h
+++ b/libcxx/include/__algorithm/iterator_operations.h
@@ -19,6 +19,7 @@
 #include <__iterator/iter_swap.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/next.h>
+#include <__iterator/prev.h>
 #include <__iterator/readable_traits.h>
 #include <__utility/declval.h>
 #include <__utility/forward.h>
@@ -53,6 +54,7 @@ struct _IterOps<_RangeAlgPolicy> {
   static constexpr auto __iter_move = ranges::iter_move;
   static constexpr auto iter_swap = ranges::iter_swap;
   static constexpr auto next = ranges::next;
+  static constexpr auto prev = ranges::prev;
   static constexpr auto __advance_to = ranges::advance;
 };
 
@@ -146,10 +148,18 @@ struct _IterOps<_ClassicAlgPolicy> {
   template <class _Iter>
   _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
   __uncvref_t<_Iter> next(_Iter&& __it,
-                          typename iterator_traits<__uncvref_t<_Iter> >::
diff erence_type __n = 1){
+                          typename iterator_traits<__uncvref_t<_Iter> >::
diff erence_type __n = 1) {
     return std::next(std::forward<_Iter>(__it), __n);
   }
 
+  // prev
+  template <class _Iter>
+  _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
+  __uncvref_t<_Iter> prev(_Iter&& __iter,
+                 typename iterator_traits<__uncvref_t<_Iter> >::
diff erence_type __n = 1) {
+    return std::prev(std::forward<_Iter>(__iter), __n);
+  }
+
   template <class _Iter>
   _LIBCPP_HIDE_FROM_ABI static _LIBCPP_CONSTEXPR_AFTER_CXX11
   void __advance_to(_Iter& __first, _Iter __last) {

diff  --git a/libcxx/include/__algorithm/move.h b/libcxx/include/__algorithm/move.h
index 0b08d31c176ef..c090faf6b6dc3 100644
--- a/libcxx/include/__algorithm/move.h
+++ b/libcxx/include/__algorithm/move.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_MOVE_H
 #define _LIBCPP___ALGORITHM_MOVE_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
@@ -26,18 +27,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 // move
 
-template <class _InIter, class _Sent, class _OutIter>
+template <class _AlgPolicy, class _InIter, class _Sent, class _OutIter>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 pair<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
   while (__first != __last) {
-    *__result = std::move(*__first);
+    *__result = _IterOps<_AlgPolicy>::__iter_move(__first);
     ++__first;
     ++__result;
   }
   return std::make_pair(std::move(__first), std::move(__result));
 }
 
-template <class _InType,
+template <class _AlgPolicy,
+          class _InType,
           class _OutType,
           class = __enable_if_t<is_same<typename remove_const<_InType>::type, _OutType>::value
                              && is_trivially_move_assignable<_OutType>::value> >
@@ -49,7 +51,7 @@ pair<_InType*, _OutType*> __move_impl(_InType* __first, _InType* __last, _OutTyp
    && !is_trivially_copyable<_InType>::value
 #endif
      )
-    return std::__move_impl<_InType*, _InType*, _OutType*>(__first, __last, __result);
+    return std::__move_impl<_AlgPolicy, _InType*, _InType*, _OutType*>(__first, __last, __result);
   const size_t __n = static_cast<size_t>(__last - __first);
   ::__builtin_memmove(__result, __first, __n * sizeof(_OutType));
   return std::make_pair(__first + __n, __result + __n);
@@ -65,7 +67,8 @@ template <class _Iter>
 struct __is_trivially_move_assignable_unwrapped
     : __is_trivially_move_assignable_unwrapped_impl<decltype(std::__unwrap_iter<_Iter>(std::declval<_Iter>()))> {};
 
-template <class _InIter,
+template <class _AlgPolicy,
+          class _InIter,
           class _OutIter,
           __enable_if_t<is_same<typename remove_const<typename iterator_traits<_InIter>::value_type>::type,
                                 typename iterator_traits<_OutIter>::value_type>::value
@@ -81,33 +84,34 @@ __move_impl(reverse_iterator<_InIter> __first,
   auto __last_base = std::__unwrap_iter(__last.base());
   auto __result_base = std::__unwrap_iter(__result.base());
   auto __result_first = __result_base - (__first_base - __last_base);
-  std::__move_impl(__last_base, __first_base, __result_first);
+  std::__move_impl<_AlgPolicy>(__last_base, __first_base, __result_first);
   return std::make_pair(__last, reverse_iterator<_OutIter>(std::__rewrap_iter(__result.base(), __result_first)));
 }
 
-template <class _InIter, class _Sent, class _OutIter>
+template <class _AlgPolicy, class _InIter, class _Sent, class _OutIter>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
 __enable_if_t<is_copy_constructible<_InIter>::value
            && is_copy_constructible<_Sent>::value
            && is_copy_constructible<_OutIter>::value, pair<_InIter, _OutIter> >
 __move(_InIter __first, _Sent __last, _OutIter __result) {
-  auto __ret = std::__move_impl(std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result));
+  auto __ret = std::__move_impl<_AlgPolicy>(
+      std::__unwrap_iter(__first), std::__unwrap_iter(__last), std::__unwrap_iter(__result));
   return std::make_pair(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second));
 }
 
-template <class _InIter, class _Sent, class _OutIter>
+template <class _AlgPolicy, class _InIter, class _Sent, class _OutIter>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
 __enable_if_t<!is_copy_constructible<_InIter>::value
            || !is_copy_constructible<_Sent>::value
            || !is_copy_constructible<_OutIter>::value, pair<_InIter, _OutIter> >
 __move(_InIter __first, _Sent __last, _OutIter __result) {
-  return std::__move_impl(std::move(__first), std::move(__last), std::move(__result));
+  return std::__move_impl<_AlgPolicy>(std::move(__first), std::move(__last), std::move(__result));
 }
 
 template <class _InputIterator, class _OutputIterator>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 _OutputIterator move(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
-  return std::__move(__first, __last, __result).second;
+  return std::__move<_ClassicAlgPolicy>(__first, __last, __result).second;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/move_backward.h b/libcxx/include/__algorithm/move_backward.h
index a56f6b826ce33..626e250b6d413 100644
--- a/libcxx/include/__algorithm/move_backward.h
+++ b/libcxx/include/__algorithm/move_backward.h
@@ -9,6 +9,7 @@
 #ifndef _LIBCPP___ALGORITHM_MOVE_BACKWARD_H
 #define _LIBCPP___ALGORITHM_MOVE_BACKWARD_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__config>
 #include <__utility/move.h>
@@ -21,25 +22,25 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _InputIterator, class _OutputIterator>
+template <class _AlgPolicy, class _InputIterator, class _OutputIterator>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 _OutputIterator
 __move_backward_constexpr(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
 {
     while (__first != __last)
-        *--__result = _VSTD::move(*--__last);
+        *--__result = _IterOps<_AlgPolicy>::__iter_move(--__last);
     return __result;
 }
 
-template <class _InputIterator, class _OutputIterator>
+template <class _AlgPolicy, class _InputIterator, class _OutputIterator>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 _OutputIterator
-__move_backward(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
+__move_backward_impl(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
 {
-    return _VSTD::__move_backward_constexpr(__first, __last, __result);
+    return _VSTD::__move_backward_constexpr<_AlgPolicy>(__first, __last, __result);
 }
 
-template <class _Tp, class _Up>
+template <class _AlgPolicy, class _Tp, class _Up>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
 typename enable_if
 <
@@ -47,7 +48,7 @@ typename enable_if
     is_trivially_move_assignable<_Up>::value,
     _Up*
 >::type
-__move_backward(_Tp* __first, _Tp* __last, _Up* __result)
+__move_backward_impl(_Tp* __first, _Tp* __last, _Up* __result)
 {
     const size_t __n = static_cast<size_t>(__last - __first);
     if (__n > 0)
@@ -58,22 +59,31 @@ __move_backward(_Tp* __first, _Tp* __last, _Up* __result)
     return __result;
 }
 
-template <class _BidirectionalIterator1, class _BidirectionalIterator2>
+template <class _AlgPolicy, class _BidirectionalIterator1, class _BidirectionalIterator2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 _BidirectionalIterator2
-move_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
-              _BidirectionalIterator2 __result)
+__move_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
+                _BidirectionalIterator2 __result)
 {
     if (__libcpp_is_constant_evaluated()) {
-        return _VSTD::__move_backward_constexpr(__first, __last, __result);
+        return _VSTD::__move_backward_constexpr<_AlgPolicy>(__first, __last, __result);
     } else {
         return _VSTD::__rewrap_iter(__result,
-            _VSTD::__move_backward(_VSTD::__unwrap_iter(__first),
-                                   _VSTD::__unwrap_iter(__last),
-                                   _VSTD::__unwrap_iter(__result)));
+            _VSTD::__move_backward_impl<_AlgPolicy>(_VSTD::__unwrap_iter(__first),
+                                                    _VSTD::__unwrap_iter(__last),
+                                                    _VSTD::__unwrap_iter(__result)));
     }
 }
 
+template <class _BidirectionalIterator1, class _BidirectionalIterator2>
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+_BidirectionalIterator2
+move_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
+              _BidirectionalIterator2 __result)
+{
+  return std::__move_backward<_ClassicAlgPolicy>(std::move(__first), std::move(__last), std::move(__result));
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___ALGORITHM_MOVE_BACKWARD_H

diff  --git a/libcxx/include/__algorithm/ranges_move.h b/libcxx/include/__algorithm/ranges_move.h
index ad4342d7c989a..9e1d4c72fb585 100644
--- a/libcxx/include/__algorithm/ranges_move.h
+++ b/libcxx/include/__algorithm/ranges_move.h
@@ -10,6 +10,7 @@
 #define _LIBCPP___ALGORITHM_RANGES_MOVE_H
 
 #include <__algorithm/in_out_result.h>
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/move.h>
 #include <__config>
 #include <__iterator/concepts.h>
@@ -36,24 +37,12 @@ namespace __move {
 struct __fn {
 
   template <class _InIter, class _Sent, class _OutIter>
-    requires __iter_move::__move_deref<_InIter> // check that we are allowed to std::move() the value
   _LIBCPP_HIDE_FROM_ABI constexpr static
   move_result<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
-    auto __ret = std::__move(std::move(__first), std::move(__last), std::move(__result));
+    auto __ret = std::__move<_RangeAlgPolicy>(std::move(__first), std::move(__last), std::move(__result));
     return {std::move(__ret.first), std::move(__ret.second)};
   }
 
-  template <class _InIter, class _Sent, class _OutIter>
-  _LIBCPP_HIDE_FROM_ABI constexpr static
-  move_result<_InIter, _OutIter> __move_impl(_InIter __first, _Sent __last, _OutIter __result) {
-    while (__first != __last) {
-      *__result = ranges::iter_move(__first);
-      ++__first;
-      ++__result;
-    }
-    return {std::move(__first), std::move(__result)};
-  }
-
   template <input_iterator _InIter, sentinel_for<_InIter> _Sent, weakly_incrementable _OutIter>
     requires indirectly_movable<_InIter, _OutIter>
   _LIBCPP_HIDE_FROM_ABI constexpr

diff  --git a/libcxx/include/__algorithm/ranges_rotate.h b/libcxx/include/__algorithm/ranges_rotate.h
new file mode 100644
index 0000000000000..1cd69a141fe3b
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_rotate.h
@@ -0,0 +1,71 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_RANGES_ROTATE_H
+#define _LIBCPP___ALGORITHM_RANGES_ROTATE_H
+
+#include <__algorithm/iterator_operations.h>
+#include <__algorithm/ranges_iterator_concept.h>
+#include <__algorithm/rotate.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__iterator/permutable.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/subrange.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+namespace __rotate {
+
+struct __fn {
+
+  template <class _Iter, class _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  static subrange<_Iter> __rotate_fn_impl(_Iter __first, _Iter __middle, _Sent __last) {
+    auto __ret = std::__rotate<_RangeAlgPolicy>(
+      std::move(__first), std::move(__middle), std::move(__last));
+    return {std::move(__ret.first), std::move(__ret.second)};
+  }
+
+  template <permutable _Iter, sentinel_for<_Iter> _Sent>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  subrange<_Iter> operator()(_Iter __first, _Iter __middle, _Sent __last) const {
+    return __rotate_fn_impl(std::move(__first), std::move(__middle), std::move(__last));
+  }
+
+  template <forward_range _Range>
+  requires permutable<iterator_t<_Range>>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  borrowed_subrange_t<_Range> operator()(_Range&& __range, iterator_t<_Range> __middle) const {
+    return __rotate_fn_impl(ranges::begin(__range), std::move(__middle), ranges::end(__range));
+  }
+
+};
+
+} // namespace __rotate
+
+inline namespace __cpo {
+  inline constexpr auto rotate = __rotate::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_ROTATE_H

diff  --git a/libcxx/include/__algorithm/ranges_swap_ranges.h b/libcxx/include/__algorithm/ranges_swap_ranges.h
index 3254e1c60abbd..d980fdec2c491 100644
--- a/libcxx/include/__algorithm/ranges_swap_ranges.h
+++ b/libcxx/include/__algorithm/ranges_swap_ranges.h
@@ -10,6 +10,8 @@
 #define _LIBCPP___ALGORITHM_RANGES_SWAP_RANGES_H
 
 #include <__algorithm/in_in_result.h>
+#include <__algorithm/iterator_operations.h>
+#include <__algorithm/swap_ranges.h>
 #include <__config>
 #include <__iterator/concepts.h>
 #include <__iterator/iter_swap.h>
@@ -38,12 +40,9 @@ struct __fn {
     requires indirectly_swappable<_I1, _I2>
   _LIBCPP_HIDE_FROM_ABI constexpr swap_ranges_result<_I1, _I2>
   operator()(_I1 __first1, _S1 __last1, _I2 __first2, _S2 __last2) const {
-    while (__first1 != __last1 && __first2 != __last2) {
-      ranges::iter_swap(__first1, __first2);
-      ++__first1;
-      ++__first2;
-    }
-    return {_VSTD::move(__first1), _VSTD::move(__first2)};
+    auto __ret = std::__swap_ranges<_RangeAlgPolicy>(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2));
+    return {std::move(__ret.first), std::move(__ret.second)};
   }
 
   template <input_range _R1, input_range _R2>

diff  --git a/libcxx/include/__algorithm/rotate.h b/libcxx/include/__algorithm/rotate.h
index fcf8444a65a08..beb5409d5b886 100644
--- a/libcxx/include/__algorithm/rotate.h
+++ b/libcxx/include/__algorithm/rotate.h
@@ -15,10 +15,8 @@
 #include <__algorithm/swap_ranges.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
-#include <__iterator/next.h>
-#include <__iterator/prev.h>
 #include <__utility/move.h>
-#include <__utility/swap.h>
+#include <__utility/pair.h>
 #include <type_traits>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -32,9 +30,11 @@ _LIBCPP_CONSTEXPR_AFTER_CXX11 _ForwardIterator
 __rotate_left(_ForwardIterator __first, _ForwardIterator __last)
 {
     typedef typename iterator_traits<_ForwardIterator>::value_type value_type;
-    value_type __tmp = _IterOps<_AlgPolicy>::__iter_move(__first);
-    // TODO(ranges): pass `_AlgPolicy` to `move`.
-    _ForwardIterator __lm1 = _VSTD::move(_VSTD::next(__first), __last, __first);
+    using _Ops = _IterOps<_AlgPolicy>;
+
+    value_type __tmp = _Ops::__iter_move(__first);
+    _ForwardIterator __lm1 = std::__move<_AlgPolicy>(
+        _Ops::next(__first), __last, __first).second;
     *__lm1 = _VSTD::move(__tmp);
     return __lm1;
 }
@@ -44,11 +44,11 @@ _LIBCPP_CONSTEXPR_AFTER_CXX11 _BidirectionalIterator
 __rotate_right(_BidirectionalIterator __first, _BidirectionalIterator __last)
 {
     typedef typename iterator_traits<_BidirectionalIterator>::value_type value_type;
-    // TODO(ranges): pass `_AlgPolicy` to `prev`.
-    _BidirectionalIterator __lm1 = _VSTD::prev(__last);
-    value_type __tmp = _IterOps<_AlgPolicy>::__iter_move(__lm1);
-    // TODO(ranges): pass `_AlgPolicy` to `move_backward`.
-    _BidirectionalIterator __fp1 = _VSTD::move_backward(__first, __lm1, __last);
+    using _Ops = _IterOps<_AlgPolicy>;
+
+    _BidirectionalIterator __lm1 = _Ops::prev(__last);
+    value_type __tmp = _Ops::__iter_move(__lm1);
+    _BidirectionalIterator __fp1 = std::__move_backward<_AlgPolicy>(__first, __lm1, std::move(__last));
     *__first = _VSTD::move(__tmp);
     return __fp1;
 }
@@ -108,26 +108,26 @@ __rotate_gcd(_RandomAccessIterator __first, _RandomAccessIterator __middle, _Ran
 {
     typedef typename iterator_traits<_RandomAccessIterator>::
diff erence_type 
diff erence_type;
     typedef typename iterator_traits<_RandomAccessIterator>::value_type value_type;
+    using _Ops = _IterOps<_AlgPolicy>;
 
     const 
diff erence_type __m1 = __middle - __first;
-    const 
diff erence_type __m2 = __last - __middle;
+    const 
diff erence_type __m2 = _Ops::distance(__middle, __last);
     if (__m1 == __m2)
     {
-        // TODO(ranges): pass `_AlgPolicy` to `swap_ranges`.
-        _VSTD::swap_ranges(__first, __middle, __middle);
+        std::__swap_ranges<_AlgPolicy>(__first, __middle, __middle, __last);
         return __middle;
     }
     const 
diff erence_type __g = _VSTD::__algo_gcd(__m1, __m2);
     for (_RandomAccessIterator __p = __first + __g; __p != __first;)
     {
-        value_type __t(_IterOps<_AlgPolicy>::__iter_move(--__p));
+        value_type __t(_Ops::__iter_move(--__p));
         _RandomAccessIterator __p1 = __p;
         _RandomAccessIterator __p2 = __p1 + __m1;
         do
         {
-            *__p1 = _IterOps<_AlgPolicy>::__iter_move(__p2);
+            *__p1 = _Ops::__iter_move(__p2);
             __p1 = __p2;
-            const 
diff erence_type __d = __last - __p2;
+            const 
diff erence_type __d = _Ops::distance(__p2, __last);
             if (__m1 < __d)
                 __p2 += __m1;
             else
@@ -188,16 +188,23 @@ __rotate_impl(_RandomAccessIterator __first, _RandomAccessIterator __middle, _Ra
     return std::__rotate_forward<_AlgPolicy>(__first, __middle, __last);
 }
 
-template <class _AlgPolicy, class _RandomAccessIterator, class _IterCategory>
+template <class _AlgPolicy, class _Iterator, class _Sentinel>
 _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
-_RandomAccessIterator __rotate(_RandomAccessIterator __first, _RandomAccessIterator __middle,
-    _RandomAccessIterator __last, _IterCategory __iter_category) {
+pair<_Iterator, _Iterator>
+__rotate(_Iterator __first, _Iterator __middle, _Sentinel __last) {
+  using _Ret = pair<_Iterator, _Iterator>;
+  _Iterator __last_iter = _IterOps<_AlgPolicy>::next(__middle, __last);
+
   if (__first == __middle)
-      return __last;
+      return _Ret(__last_iter, __last_iter);
   if (__middle == __last)
-      return __first;
+      return _Ret(std::move(__first), std::move(__last_iter));
+
+  using _IterCategory = typename _IterOps<_AlgPolicy>::template __iterator_category<_Iterator>;
+  auto __result = std::__rotate_impl<_AlgPolicy>(
+      std::move(__first), std::move(__middle), __last_iter, _IterCategory());
 
-  return std::__rotate_impl<_AlgPolicy>(std::move(__first), std::move(__middle), std::move(__last), __iter_category);
+  return _Ret(std::move(__result), std::move(__last_iter));
 }
 
 template <class _ForwardIterator>
@@ -205,8 +212,8 @@ inline _LIBCPP_INLINE_VISIBILITY
 _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator
 rotate(_ForwardIterator __first, _ForwardIterator __middle, _ForwardIterator __last)
 {
-  return std::__rotate<_ClassicAlgPolicy>(__first, __middle, __last,
-                                          typename iterator_traits<_ForwardIterator>::iterator_category());
+  return std::__rotate<_ClassicAlgPolicy>(
+      std::move(__first), std::move(__middle), std::move(__last)).first;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/stable_partition.h b/libcxx/include/__algorithm/stable_partition.h
index e5ad48b2ed516..c7aa3f07a88ce 100644
--- a/libcxx/include/__algorithm/stable_partition.h
+++ b/libcxx/include/__algorithm/stable_partition.h
@@ -108,7 +108,7 @@ __stable_partition_impl(_ForwardIterator __first, _ForwardIterator __last, _Pred
 __second_half_done:
     // TTTFFFFFTTTTTFFFFF
     // f  ff   m    sf   l
-    return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false, __fit);
+    return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false).first;
     // TTTTTTTTFFFFFFFFFF
     //         |
 }
@@ -253,7 +253,7 @@ __stable_partition_impl(_BidirectionalIterator __first, _BidirectionalIterator _
 __second_half_done:
     // TTTFFFFFTTTTTFFFFF
     // f  ff   m    sf  l
-    return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false, __bit);
+    return std::__rotate<_AlgPolicy>(__first_false, __m, __second_false).first;
     // TTTTTTTTFFFFFFFFFF
     //         |
 }

diff  --git a/libcxx/include/__algorithm/swap_ranges.h b/libcxx/include/__algorithm/swap_ranges.h
index 0422265bb4be4..0cff519a8da94 100644
--- a/libcxx/include/__algorithm/swap_ranges.h
+++ b/libcxx/include/__algorithm/swap_ranges.h
@@ -9,8 +9,10 @@
 #ifndef _LIBCPP___ALGORITHM_SWAP_RANGES_H
 #define _LIBCPP___ALGORITHM_SWAP_RANGES_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__config>
-#include <__utility/swap.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -18,12 +20,39 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+// 2+2 iterators: the shorter size will be used.
+template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _ForwardIterator2, class _Sentinel2>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+pair<_ForwardIterator1, _ForwardIterator2>
+__swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2, _Sentinel2 __last2) {
+  while (__first1 != __last1 && __first2 != __last2) {
+    _IterOps<_AlgPolicy>::iter_swap(__first1, __first2);
+    ++__first1;
+    ++__first2;
+  }
+
+  return pair<_ForwardIterator1, _ForwardIterator2>(std::move(__first1), std::move(__first2));
+}
+
+// 2+1 iterators: size2 >= size1.
+template <class _AlgPolicy, class _ForwardIterator1, class _Sentinel1, class _ForwardIterator2>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
+pair<_ForwardIterator1, _ForwardIterator2>
+__swap_ranges(_ForwardIterator1 __first1, _Sentinel1 __last1, _ForwardIterator2 __first2) {
+  while (__first1 != __last1) {
+    _IterOps<_AlgPolicy>::iter_swap(__first1, __first2);
+    ++__first1;
+    ++__first2;
+  }
+
+  return pair<_ForwardIterator1, _ForwardIterator2>(std::move(__first1), std::move(__first2));
+}
+
 template <class _ForwardIterator1, class _ForwardIterator2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17 _ForwardIterator2
 swap_ranges(_ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2) {
-  for (; __first1 != __last1; ++__first1, (void)++__first2)
-    swap(*__first1, *__first2);
-  return __first2;
+  return std::__swap_ranges<_ClassicAlgPolicy>(
+      std::move(__first1), std::move(__last1), std::move(__first2)).second;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__iterator/reverse_iterator.h b/libcxx/include/__iterator/reverse_iterator.h
index abc8896c0adb2..7b002205f3337 100644
--- a/libcxx/include/__iterator/reverse_iterator.h
+++ b/libcxx/include/__iterator/reverse_iterator.h
@@ -363,7 +363,7 @@ class __unconstrained_reverse_iterator {
   _Iter __iter_;
 
 public:
-  static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value);
+  static_assert(__is_cpp17_bidirectional_iterator<_Iter>::value || bidirectional_iterator<_Iter>);
 
   using iterator_type = _Iter;
   using iterator_category =
@@ -391,6 +391,14 @@ class __unconstrained_reverse_iterator {
     }
   }
 
+  _LIBCPP_HIDE_FROM_ABI friend constexpr
+  iter_rvalue_reference_t<_Iter> iter_move(const __unconstrained_reverse_iterator& __i)
+    noexcept(is_nothrow_copy_constructible_v<_Iter> &&
+        noexcept(ranges::iter_move(--declval<_Iter&>()))) {
+    auto __tmp = __i.base();
+    return ranges::iter_move(--__tmp);
+  }
+
   _LIBCPP_HIDE_FROM_ABI constexpr __unconstrained_reverse_iterator& operator++() {
     --__iter_;
     return *this;

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 6da0146447785..ab5ad55e38759 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -745,6 +745,13 @@ namespace ranges {
     constexpr ranges::reverse_copy_result<borrowed_iterator_t<R>, O>
       ranges::reverse_copy(R&& r, O result);                                                        // since C++20
 
+  template<permutable I, sentinel_for<I> S>
+    constexpr subrange<I> rotate(I first, I middle, S last);                                        // since C++20
+
+  template<forward_range R>
+    requires permutable<iterator_t<R>>
+    constexpr borrowed_subrange_t<R> rotate(R&& r, iterator_t<R> middle);                           // Since C++20
+
   template <class _InIter, class _OutIter>
   using rotate_copy_result = in_out_result<_InIter, _OutIter>;                                      // since C++20
 
@@ -1817,6 +1824,7 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/ranges_replace_if.h>
 #include <__algorithm/ranges_reverse.h>
 #include <__algorithm/ranges_reverse_copy.h>
+#include <__algorithm/ranges_rotate.h>
 #include <__algorithm/ranges_rotate_copy.h>
 #include <__algorithm/ranges_sample.h>
 #include <__algorithm/ranges_search.h>

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index bcb3a0d760e02..897c2c8c583f5 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -239,7 +239,6 @@ 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" }
@@ -373,6 +372,7 @@ module std [system] {
       module ranges_replace_if               { private header "__algorithm/ranges_replace_if.h" }
       module ranges_reverse                  { private header "__algorithm/ranges_reverse.h" }
       module ranges_reverse_copy             { private header "__algorithm/ranges_reverse_copy.h" }
+      module ranges_rotate                   { private header "__algorithm/ranges_rotate.h" }
       module ranges_rotate_copy              { private header "__algorithm/ranges_rotate_copy.h" }
       module ranges_sample                   { private header "__algorithm/ranges_sample.h" }
       module ranges_search                   { private header "__algorithm/ranges_search.h" }

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 59abd9109ec0a..3c14d13de3498 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -37,7 +37,6 @@ 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'}}
@@ -171,6 +170,7 @@ END-SCRIPT
 #include <__algorithm/ranges_replace_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_replace_if.h'}}
 #include <__algorithm/ranges_reverse.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_reverse.h'}}
 #include <__algorithm/ranges_reverse_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_reverse_copy.h'}}
+#include <__algorithm/ranges_rotate.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_rotate.h'}}
 #include <__algorithm/ranges_rotate_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_rotate_copy.h'}}
 #include <__algorithm/ranges_sample.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_sample.h'}}
 #include <__algorithm/ranges_search.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_search.h'}}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/ranges_rotate.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/ranges_rotate.pass.cpp
new file mode 100644
index 0000000000000..4a8bfbe19d564
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.rotate/ranges_rotate.pass.cpp
@@ -0,0 +1,191 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// <algorithm>
+
+// template<permutable I, sentinel_for<I> S>
+//   constexpr subrange<I> rotate(I first, I middle, S last);                                        // since C++20
+//
+// template<forward_range R>
+//   requires permutable<iterator_t<R>>
+//   constexpr borrowed_subrange_t<R> rotate(R&& r, iterator_t<R> middle);                           // Since C++20
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+// Test constraints of the (iterator, sentinel) overload.
+// ======================================================
+
+template <class Iter = int*, class Sent = int*>
+concept HasRotateIter =
+    requires(Iter&& iter, Sent&& sent) {
+      std::ranges::rotate(std::forward<Iter>(iter), std::forward<Iter>(iter), std::forward<Sent>(sent));
+    };
+
+static_assert(HasRotateIter<int*, int*>);
+
+// !permutable<I>
+static_assert(!HasRotateIter<PermutableNotForwardIterator>);
+static_assert(!HasRotateIter<PermutableNotSwappable>);
+
+// !sentinel_for<S, I>
+static_assert(!HasRotateIter<int*, SentinelForNotSemiregular>);
+static_assert(!HasRotateIter<int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+// Test constraints of the (range) overload.
+// =========================================
+
+template <class Range>
+concept HasRotateRange =
+    requires(Range&& range, std::ranges::iterator_t<Range> iter) {
+      std::ranges::rotate(std::forward<Range>(range), iter);
+    };
+
+template <class T>
+using R = UncheckedRange<T>;
+
+static_assert(HasRotateRange<R<int*>>);
+
+// !forward_range<R>
+static_assert(!HasRotateRange<ForwardRangeNotDerivedFrom>);
+static_assert(!HasRotateRange<ForwardRangeNotIncrementable>);
+static_assert(!HasRotateRange<ForwardRangeNotSentinelSemiregular>);
+static_assert(!HasRotateRange<ForwardRangeNotSentinelEqualityComparableWith>);
+
+// !permutable<iterator_t<R>>
+static_assert(!HasRotateRange<PermutableRangeNotForwardIterator>);
+static_assert(!HasRotateRange<PermutableRangeNotSwappable>);
+
+template <class Iter, class Sent, size_t N>
+constexpr void test_one(const std::array<int, N> input, size_t mid_index, std::array<int, N> expected) {
+  assert(mid_index <= N);
+
+  { // (iterator, sentinel) overload.
+    auto in = input;
+    auto begin = Iter(in.data());
+    auto mid = Iter(in.data() + mid_index);
+    auto end = Sent(Iter(in.data() + in.size()));
+
+    std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::rotate(begin, mid, end);
+    assert(base(result.begin()) == in.data() + in.size() - mid_index);
+    assert(base(result.end()) == in.data() + in.size());
+    assert(in == expected);
+  }
+
+  { // (range) overload.
+    auto in = input;
+    auto begin = Iter(in.data());
+    auto mid = Iter(in.data() + mid_index);
+    auto end = Sent(Iter(in.data() + in.size()));
+    auto range = std::ranges::subrange(std::move(begin), std::move(end));
+
+    std::same_as<std::ranges::subrange<Iter>> decltype(auto) result = std::ranges::rotate(range, mid);
+    assert(base(result.begin()) == in.data() + in.size() - mid_index);
+    assert(base(result.end()) == in.data() + in.size());
+    assert(in == expected);
+  }
+}
+
+template <class Iter, class Sent>
+constexpr void test_iter_sent() {
+  // Empty sequence.
+  test_one<Iter, Sent, 0>({}, 0, {});
+
+  // 1-element sequence.
+  test_one<Iter, Sent, 1>({1}, 0, {1});
+
+  // 2-element sequence.
+  test_one<Iter, Sent, 2>({1, 2}, 1, {2, 1});
+
+  // 3-element sequence.
+  test_one<Iter, Sent, 3>({1, 2, 3}, 1, {2, 3, 1});
+  test_one<Iter, Sent, 3>({1, 2, 3}, 2, {3, 1, 2});
+
+  // Longer sequence.
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 2, {3, 4, 5, 6, 7, 1, 2});
+
+  // Rotate around the middle.
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 3, {4, 5, 6, 7, 1, 2, 3});
+
+  // Rotate around the 1st element (no-op).
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 0, {1, 2, 3, 4, 5, 6, 7});
+
+  // Rotate around the 2nd element.
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 1, {2, 3, 4, 5, 6, 7, 1});
+
+  // Rotate around the last element.
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 6, {7, 1, 2, 3, 4, 5, 6});
+
+  // Pass `end()` as `mid` (no-op).
+  test_one<Iter, Sent, 7>({1, 2, 3, 4, 5, 6, 7}, 7, {1, 2, 3, 4, 5, 6, 7});
+}
+
+template <class Iter>
+constexpr void test_iter() {
+  test_iter_sent<Iter, Iter>();
+  test_iter_sent<Iter, sentinel_wrapper<Iter>>();
+}
+
+constexpr void test_iterators() {
+  test_iter<forward_iterator<int*>>();
+  test_iter<bidirectional_iterator<int*>>();
+  test_iter<random_access_iterator<int*>>();
+  test_iter<contiguous_iterator<int*>>();
+  test_iter<int*>();
+}
+
+constexpr bool test() {
+  test_iterators();
+
+  { // Complexity: at most `last - first` swaps.
+    const std::array input = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+    auto expected = static_cast<int>(input.size());
+
+    {
+      auto in = input;
+      int swaps = 0;
+      auto begin = adl::Iterator::TrackSwaps(in.data(), swaps);
+      auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
+
+      for (size_t mid = 0; mid != input.size(); ++mid) {
+        std::ranges::rotate(begin, begin + mid, end);
+        assert(swaps <= expected);
+      }
+    }
+
+    {
+      auto in = input;
+      int swaps = 0;
+      auto begin = adl::Iterator::TrackSwaps(in.data(), swaps);
+      auto end = adl::Iterator::TrackSwaps(in.data() + in.size(), swaps);
+      auto range = std::ranges::subrange(begin, end);
+
+      for (size_t mid = 0; mid != input.size(); ++mid) {
+        std::ranges::rotate(range, begin + mid);
+        assert(swaps <= expected);
+      }
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  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 5335b842fc537..e42892d37abaa 100644
--- a/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
+++ b/libcxx/test/std/algorithms/ranges_robust_against_dangling.pass.cpp
@@ -181,7 +181,7 @@ constexpr bool test_all() {
   dangling_1st(std::ranges::remove, in, x);
   dangling_1st(std::ranges::remove_if, in, unary_pred);
   dangling_1st(std::ranges::reverse, in);
-  //dangling_1st(std::ranges::rotate, in, mid);
+  dangling_1st(std::ranges::rotate, in, mid);
   if (!std::is_constant_evaluated()) // `shuffle` isn't `constexpr`.
     dangling_1st(std::ranges::shuffle, in, rand_gen());
   dangling_1st(std::ranges::unique, in);

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 0dda0cf6ca7a7..709913d8af0fa 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
@@ -147,7 +147,7 @@ constexpr void run_tests() {
   test(std::ranges::remove, in, x);
   test(std::ranges::remove_if, in, unary_pred);
   test(std::ranges::reverse, in);
-  //test_mid(std::ranges::rotate, in, mid);
+  test_mid(std::ranges::rotate, in, mid);
   if (!std::is_constant_evaluated()) // `shuffle` isn't `constexpr`.
     test(std::ranges::shuffle, in, rand_gen());
   if (!std::is_constant_evaluated()) {
@@ -156,18 +156,15 @@ constexpr void run_tests() {
   }
   test(std::ranges::unique, in);
   test(std::ranges::partition, in, unary_pred);
-  // TODO(ranges): `stable_partition` requires `ranges::rotate` to be implemented.
-  //if (!std::is_constant_evaluated())
-  // test(std::ranges::stable_partition, in, unary_pred);
+  if (!std::is_constant_evaluated())
+    test(std::ranges::stable_partition, in, unary_pred);
   test(std::ranges::sort, in);
-  // TODO(ranges): `stable_sort` requires `ranges::rotate` to be implemented.
-  //if (!std::is_constant_evaluated())
-  //  test(std::ranges::stable_sort, in);
+  if (!std::is_constant_evaluated())
+    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);
+  if (!std::is_constant_evaluated())
+    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/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 2f137d07772a1..5abe476e63f63 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
@@ -128,7 +128,7 @@ static_assert(test(std::ranges::replace_copy_if, a, a, odd, 43));
 static_assert(test(std::ranges::replace_if, a, odd, 43));
 static_assert(test(std::ranges::reverse, a));
 static_assert(test(std::ranges::reverse_copy, a, a));
-//static_assert(test(std::ranges::rotate, a, a+5));
+static_assert(test(std::ranges::rotate, a, a+5));
 static_assert(test(std::ranges::rotate_copy, a, a+5, a));
 static_assert(test(std::ranges::sample, a, a, 5, g));
 static_assert(test(std::ranges::search, a, a));


        


More information about the llvm-branch-commits mailing list