[libcxx-commits] [libcxx] 1d83750 - [libc++] Implement ranges::copy{, _n, _if, _backward}

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Apr 15 04:44:21 PDT 2022


Author: Nikolas Klauser
Date: 2022-04-15T13:44:11+02:00
New Revision: 1d83750f631d60bf6f371fa3fd0efc0499470d3f

URL: https://github.com/llvm/llvm-project/commit/1d83750f631d60bf6f371fa3fd0efc0499470d3f
DIFF: https://github.com/llvm/llvm-project/commit/1d83750f631d60bf6f371fa3fd0efc0499470d3f.diff

LOG: [libc++] Implement ranges::copy{, _n, _if, _backward}

Reviewed By: Mordante, var-const, #libc

Spies: sstefan1, libcxx-commits, mgorny

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

Added: 
    libcxx/include/__algorithm/ranges_copy.h
    libcxx/include/__algorithm/ranges_copy_backward.h
    libcxx/include/__algorithm/ranges_copy_if.h
    libcxx/include/__algorithm/ranges_copy_n.h
    libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp
    libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp

Modified: 
    libcxx/docs/Status/RangesAlgorithms.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__algorithm/copy.h
    libcxx/include/__algorithm/copy_backward.h
    libcxx/include/__algorithm/unwrap_iter.h
    libcxx/include/algorithm
    libcxx/include/module.modulemap
    libcxx/test/libcxx/private_headers.verify.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv
index dd5a038395d48..419da0b17aa80 100644
--- a/libcxx/docs/Status/RangesAlgorithms.csv
+++ b/libcxx/docs/Status/RangesAlgorithms.csv
@@ -36,10 +36,10 @@ Read-only,clamp,Not assigned,n/a,Not started
 Read-only,is_permutation,Not assigned,n/a,Not started
 Read-only,for_each,Not assigned,n/a,Not started
 Read-only,for_each_n,Not assigned,n/a,Not started
-Write,copy,Not assigned,n/a,Not started
-Write,copy_if,Not assigned,n/a,Not started
-Write,copy_n,Not assigned,n/a,Not started
-Write,copy_backward,Not assigned,n/a,Not started
+Write,copy,Nikolas Klauser,`D122982 <https://llvm.org/D122982>`_,✅
+Write,copy_if,Nikolas Klauser,`D122982 <https://llvm.org/D122982>`_,✅
+Write,copy_n,Nikolas Klauser,`D122982 <https://llvm.org/D122982>`_,✅
+Write,copy_backward,Nikolas Klauser,`D122982 <https://llvm.org/D122982>`_,✅
 Write,move,Not assigned,n/a,Not started
 Write,move_backward,Not assigned,n/a,Not started
 Write,fill,Not assigned,n/a,Not started

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 0809a9a798881..e97e5fbfb67da 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -66,6 +66,10 @@ set(files
   __algorithm/pop_heap.h
   __algorithm/prev_permutation.h
   __algorithm/push_heap.h
+  __algorithm/ranges_copy.h
+  __algorithm/ranges_copy_backward.h
+  __algorithm/ranges_copy_if.h
+  __algorithm/ranges_copy_n.h
   __algorithm/ranges_count.h
   __algorithm/ranges_count_if.h
   __algorithm/ranges_find.h

diff  --git a/libcxx/include/__algorithm/copy.h b/libcxx/include/__algorithm/copy.h
index b4045cd06a2bb..00587ecb5be96 100644
--- a/libcxx/include/__algorithm/copy.h
+++ b/libcxx/include/__algorithm/copy.h
@@ -12,6 +12,9 @@
 #include <__algorithm/unwrap_iter.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_iterator.h>
+#include <__utility/move.h>
+#include <__utility/pair.h>
 #include <cstring>
 #include <type_traits>
 
@@ -23,53 +26,74 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 
 // copy
 
-template <class _InputIterator, class _OutputIterator>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-_OutputIterator
-__copy_constexpr(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
-{
-    for (; __first != __last; ++__first, (void) ++__result)
-        *__result = *__first;
-    return __result;
+template <class _InIter, class _Sent, class _OutIter>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_InIter, _OutIter> __copy_impl(_InIter __first, _Sent __last, _OutIter __result) {
+  while (__first != __last) {
+    *__result = *__first;
+    ++__first;
+    ++__result;
+  }
+  return pair<_InIter, _OutIter>(std::move(__first), std::move(__result));
 }
 
-template <class _InputIterator, class _OutputIterator>
-inline _LIBCPP_INLINE_VISIBILITY
-_OutputIterator
-__copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
-{
-    return _VSTD::__copy_constexpr(__first, __last, __result);
+template <class _InValueT,
+          class _OutValueT,
+          class = __enable_if_t<is_same<typename remove_const<_InValueT>::type, _OutValueT>::value
+                             && is_trivially_copy_assignable<_OutValueT>::value> >
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_InValueT*, _OutValueT*> __copy_impl(_InValueT* __first, _InValueT* __last, _OutValueT* __result) {
+  if (__libcpp_is_constant_evaluated()
+// TODO: Remove this once GCC supports __builtin_memmove during constant evaluation
+#ifndef _LIBCPP_COMPILER_GCC
+      && !is_trivially_copyable<_InValueT>::value
+#endif
+     )
+    return std::__copy_impl<_InValueT*, _InValueT*, _OutValueT*>(__first, __last, __result);
+  const size_t __n = static_cast<size_t>(__last - __first);
+  if (__n > 0)
+    ::__builtin_memmove(__result, __first, __n * sizeof(_OutValueT));
+  return std::make_pair(__first + __n, __result + __n);
+}
+
+template <class _InValueT,
+          class _OutValueT,
+          class = __enable_if_t<is_same<typename remove_const<_InValueT>::type, _OutValueT>::value
+                             && is_trivially_copy_assignable<_OutValueT>::value> >
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<reverse_iterator<_InValueT*>, reverse_iterator<_OutValueT*> >
+__copy_impl(reverse_iterator<_InValueT*> __first,
+            reverse_iterator<_InValueT*> __last,
+            reverse_iterator<_OutValueT*> __result) {
+  auto __first_base = __first.base();
+  auto __last_base = __last.base();
+  auto __result_base = __result.base();
+  auto __result_first = __result_base - (__first_base - __last_base);
+  std::__copy_impl(__last_base, __first_base, __result_first);
+  return std::make_pair(__last, reverse_iterator<_OutValueT*>(__result_first));
+}
+
+template <class _InIter, class _Sent, class _OutIter>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) {
+  return std::__copy_impl(std::move(__first), std::move(__last), std::move(__result));
 }
 
-template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_same<typename remove_const<_Tp>::type, _Up>::value &&
-    is_trivially_copy_assignable<_Up>::value,
-    _Up*
->::type
-__copy(_Tp* __first, _Tp* __last, _Up* __result)
-{
-    const size_t __n = static_cast<size_t>(__last - __first);
-    if (__n > 0)
-        _VSTD::memmove(__result, __first, __n * sizeof(_Up));
-    return __result + __n;
+template <class _InIter, class _Sent, class _OutIter,
+          __enable_if_t<is_copy_constructible<_InIter>::value
+                     && is_copy_constructible<_Sent>::value
+                     && is_copy_constructible<_OutIter>::value> >
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_InIter, _OutIter> __copy(_InIter __first, _Sent __last, _OutIter __result) {
+  auto __ret = std::__copy_impl(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 _InputIterator, class _OutputIterator>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 _OutputIterator
-copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
-{
-    if (__libcpp_is_constant_evaluated()) {
-        return _VSTD::__copy_constexpr(__first, __last, __result);
-    } else {
-        return _VSTD::__rewrap_iter(__result,
-            _VSTD::__copy(_VSTD::__unwrap_iter(__first),
-                          _VSTD::__unwrap_iter(__last),
-                          _VSTD::__unwrap_iter(__result)));
-    }
+copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result) {
+  return std::__copy(__first, __last, __result).second;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/copy_backward.h b/libcxx/include/__algorithm/copy_backward.h
index 9754f0c95b01f..dd43a91ffa873 100644
--- a/libcxx/include/__algorithm/copy_backward.h
+++ b/libcxx/include/__algorithm/copy_backward.h
@@ -9,9 +9,11 @@
 #ifndef _LIBCPP___ALGORITHM_COPY_BACKWARD_H
 #define _LIBCPP___ALGORITHM_COPY_BACKWARD_H
 
+#include <__algorithm/copy.h>
 #include <__algorithm/unwrap_iter.h>
 #include <__config>
 #include <__iterator/iterator_traits.h>
+#include <__iterator/reverse_iterator.h>
 #include <cstring>
 #include <type_traits>
 
@@ -21,57 +23,29 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _BidirectionalIterator, class _OutputIterator>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
-_OutputIterator
-__copy_backward_constexpr(_BidirectionalIterator __first, _BidirectionalIterator __last, _OutputIterator __result)
-{
-    while (__first != __last)
-        *--__result = *--__last;
-    return __result;
+template <class _Iter1, class _Sent1, class _Iter2>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_Iter1, _Iter2> __copy_backward_impl(_Iter1 __first, _Sent1 __last, _Iter2 __result) {
+  auto __ret = std::__copy(reverse_iterator<_Iter1>(__last),
+                           reverse_iterator<_Sent1>(__first),
+                           reverse_iterator<_Iter2>(__result));
+  return pair<_Iter1, _Iter2>(__ret.first.base(), __ret.second.base());
 }
 
-template <class _BidirectionalIterator, class _OutputIterator>
-inline _LIBCPP_INLINE_VISIBILITY
-_OutputIterator
-__copy_backward(_BidirectionalIterator __first, _BidirectionalIterator __last, _OutputIterator __result)
-{
-    return _VSTD::__copy_backward_constexpr(__first, __last, __result);
-}
-
-template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_same<typename remove_const<_Tp>::type, _Up>::value &&
-    is_trivially_copy_assignable<_Up>::value,
-    _Up*
->::type
-__copy_backward(_Tp* __first, _Tp* __last, _Up* __result)
-{
-    const size_t __n = static_cast<size_t>(__last - __first);
-    if (__n > 0)
-    {
-        __result -= __n;
-        _VSTD::memmove(__result, __first, __n * sizeof(_Up));
-    }
-    return __result;
+template <class _Iter1, class _Sent1, class _Iter2>
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+pair<_Iter1, _Iter2> __copy_backward(_Iter1 __first, _Sent1 __last, _Iter2 __result) {
+  auto __ret = std::__copy_backward_impl(std::__unwrap_iter(__first),
+                                         std::__unwrap_iter(__last),
+                                         std::__unwrap_iter(__result));
+  return pair<_Iter1, _Iter2>(std::__rewrap_iter(__first, __ret.first), std::__rewrap_iter(__result, __ret.second));
 }
 
 template <class _BidirectionalIterator1, class _BidirectionalIterator2>
-inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX17
 _BidirectionalIterator2
-copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
-              _BidirectionalIterator2 __result)
-{
-    if (__libcpp_is_constant_evaluated()) {
-        return _VSTD::__copy_backward_constexpr(__first, __last, __result);
-    } else {
-        return _VSTD::__rewrap_iter(__result,
-            _VSTD::__copy_backward(_VSTD::__unwrap_iter(__first),
-                                   _VSTD::__unwrap_iter(__last),
-                                   _VSTD::__unwrap_iter(__result)));
-    }
+copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last, _BidirectionalIterator2 __result) {
+  return std::__copy_backward(__first, __last, __result).second;
 }
 
 _LIBCPP_END_NAMESPACE_STD

diff  --git a/libcxx/include/__algorithm/ranges_copy.h b/libcxx/include/__algorithm/ranges_copy.h
new file mode 100644
index 0000000000000..f5d6d5cd139a4
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_copy.h
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_COPY_H
+#define _LIBCPP___ALGORITHM_RANGES_COPY_H
+
+#include <__algorithm/copy.h>
+#include <__algorithm/in_out_result.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__iterator/concepts.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.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 {
+
+template <class _InIter, class _OutIter>
+using copy_result = in_out_result<_InIter, _OutIter>;
+
+namespace __copy {
+struct __fn {
+
+  template <input_iterator _InIter, sentinel_for<_InIter> _Sent, weakly_incrementable _OutIter>
+    requires indirectly_copyable<_InIter, _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_result<_InIter, _OutIter> operator()(_InIter __first, _Sent __last, _OutIter __result) const {
+    auto __ret = std::__copy(std::move(__first), std::move(__last), std::move(__result));
+    return {std::move(__ret.first), std::move(__ret.second)};
+  }
+
+  template <input_range _Range, weakly_incrementable _OutIter>
+    requires indirectly_copyable<iterator_t<_Range>, _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_result<borrowed_iterator_t<_Range>, _OutIter> operator()(_Range&& __r, _OutIter __result) const {
+    auto __ret = std::__copy(ranges::begin(__r), ranges::end(__r), std::move(__result));
+    return {std::move(__ret.first), std::move(__ret.second)};
+  }
+};
+} // namespace __copy
+
+inline namespace __cpo {
+  inline constexpr auto copy = __copy::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COPY_H

diff  --git a/libcxx/include/__algorithm/ranges_copy_backward.h b/libcxx/include/__algorithm/ranges_copy_backward.h
new file mode 100644
index 0000000000000..49c1b26add6d1
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_copy_backward.h
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_COPY_BACKWARD_H
+#define _LIBCPP___ALGORITHM_RANGES_COPY_BACKWARD_H
+
+#include <__algorithm/copy_backward.h>
+#include <__algorithm/in_out_result.h>
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/reverse_iterator.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.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 {
+
+template<class _Ip, class _Op>
+using copy_backward_result = in_out_result<_Ip, _Op>;
+
+namespace __copy_backward {
+struct __fn {
+
+  template <bidirectional_iterator _InIter1, sentinel_for<_InIter1> _Sent1, bidirectional_iterator _InIter2>
+    requires indirectly_copyable<_InIter1, _InIter2>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_backward_result<_InIter1, _InIter2> operator()(_InIter1 __first, _Sent1 __last, _InIter2 __result) const {
+    auto __ret = std::__copy_backward(std::move(__first), std::move(__last), std::move(__result));
+    return {std::move(__ret.first), std::move(__ret.second)};
+  }
+
+  template <bidirectional_range _Range, bidirectional_iterator _Iter>
+    requires indirectly_copyable<iterator_t<_Range>, _Iter>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_backward_result<borrowed_iterator_t<_Range>, _Iter> operator()(_Range&& __r, _Iter __result) const {
+    auto __ret = std::__copy_backward(ranges::begin(__r),
+                                      ranges::end(__r),
+                                      std::move(__result));
+    return {std::move(__ret.first), std::move(__ret.second)};
+  }
+};
+} // namespace __copy_backward
+
+inline namespace __cpo {
+  inline constexpr auto copy_backward = __copy_backward::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COPY_BACKWARD_H

diff  --git a/libcxx/include/__algorithm/ranges_copy_if.h b/libcxx/include/__algorithm/ranges_copy_if.h
new file mode 100644
index 0000000000000..492104fbbfbab
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_copy_if.h
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_COPY_IF_H
+#define _LIBCPP___ALGORITHM_RANGES_COPY_IF_H
+
+#include <__algorithm/in_out_result.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.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 {
+
+template<class _Ip, class _Op>
+using copy_if_result = in_out_result<_Ip, _Op>;
+
+namespace __copy_if {
+struct __fn {
+
+  template <class _InIter, class _Sent, class _OutIter, class _Proj, class _Pred>
+  _LIBCPP_HIDE_FROM_ABI static constexpr
+  copy_if_result <_InIter, _OutIter>
+  __copy_if_impl(_InIter __first, _Sent __last, _OutIter __result, _Pred& __pred, _Proj& __proj) {
+    for (; __first != __last; ++__first) {
+      if (std::invoke(__pred, std::invoke(__proj, *__first))) {
+        *__result = *__first;
+        ++__result;
+      }
+    }
+    return {std::move(__first), std::move(__result)};
+  }
+
+  template <input_iterator _Iter, sentinel_for<_Iter> _Sent, weakly_incrementable _OutIter, class _Proj = identity,
+            indirect_unary_predicate<projected<_Iter, _Proj>> _Pred>
+    requires indirectly_copyable<_Iter, _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_if_result<_Iter, _OutIter>
+  operator()(_Iter __first, _Sent __last, _OutIter __result, _Pred __pred, _Proj __proj = {}) const {
+    return __copy_if_impl(std::move(__first), std::move(__last), std::move(__result), __pred, __proj);
+  }
+
+  template <input_range _Range, weakly_incrementable _OutIter, class _Proj = identity,
+            indirect_unary_predicate<projected<iterator_t<_Range>, _Proj>> _Pred>
+    requires indirectly_copyable<iterator_t<_Range>, _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_if_result<borrowed_iterator_t<_Range>, _OutIter>
+  operator()(_Range&& __r, _OutIter __result, _Pred __pred, _Proj __proj = {}) const {
+    return __copy_if_impl(ranges::begin(__r), ranges::end(__r), std::move(__result), __pred, __proj);
+  }
+};
+} // namespace __copy_if
+
+inline namespace __cpo {
+  inline constexpr auto copy_if = __copy_if::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COPY_IF_H

diff  --git a/libcxx/include/__algorithm/ranges_copy_n.h b/libcxx/include/__algorithm/ranges_copy_n.h
new file mode 100644
index 0000000000000..eaa05c9546869
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_copy_n.h
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_COPY_N_H
+#define _LIBCPP___ALGORITHM_RANGES_COPY_N_H
+
+#include <__algorithm/copy.h>
+#include <__algorithm/in_out_result.h>
+#include <__algorithm/ranges_copy.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/unreachable_sentinel.h>
+#include <__iterator/wrap_iter.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+namespace ranges {
+
+template <class _Ip, class _Op>
+using copy_n_result = in_out_result<_Ip, _Op>;
+
+namespace __copy_n {
+struct __fn {
+
+  template <class _InIter, class _DiffType, class _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr static
+  copy_n_result<_InIter, _OutIter> __go(_InIter __first, _DiffType __n, _OutIter __result) {
+    while (__n != 0) {
+      *__result = *__first;
+      ++__first;
+      ++__result;
+      --__n;
+    }
+    return {std::move(__first), std::move(__result)};
+  }
+
+  template <random_access_iterator _InIter, class _DiffType, random_access_iterator _OutIter>
+  _LIBCPP_HIDE_FROM_ABI constexpr static
+  copy_n_result<_InIter, _OutIter> __go(_InIter __first, _DiffType __n, _OutIter __result) {
+    auto __ret = std::__copy(__first, __first + __n, __result);
+    return {__ret.first, __ret.second};
+  }
+
+  template <input_iterator _Ip, weakly_incrementable _Op>
+    requires indirectly_copyable<_Ip, _Op>
+  _LIBCPP_HIDE_FROM_ABI constexpr
+  copy_n_result<_Ip, _Op> operator()(_Ip __first, iter_
diff erence_t<_Ip> __n, _Op __result) const {
+    return __go(std::move(__first), __n, std::move(__result));
+  }
+};
+} // namespace __copy_n
+
+inline namespace __cpo {
+  inline constexpr auto copy_n = __copy_n::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___ALGORITHM_RANGES_COPY_N_H

diff  --git a/libcxx/include/__algorithm/unwrap_iter.h b/libcxx/include/__algorithm/unwrap_iter.h
index e738cb26fcd89..2edf16b70ddc8 100644
--- a/libcxx/include/__algorithm/unwrap_iter.h
+++ b/libcxx/include/__algorithm/unwrap_iter.h
@@ -64,14 +64,14 @@ __unwrap_iter(_Iter __i) _NOEXCEPT
 }
 
 template<class _OrigIter>
-_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR
 _OrigIter __rewrap_iter(_OrigIter, _OrigIter __result)
 {
     return __result;
 }
 
 template<class _OrigIter, class _UnwrappedIter>
-_LIBCPP_HIDE_FROM_ABI
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR
 _OrigIter __rewrap_iter(_OrigIter __first, _UnwrappedIter __result)
 {
     // Precondition: __result is reachable from __first

diff  --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 919c7ccbb8216..9feb59b3a1a7e 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -191,6 +191,53 @@ namespace ranges {
            indirect_strict_weak_order<projected<iterator_t<R>, Proj>> Comp = ranges::less>
     constexpr ranges::minmax_element_result<borrowed_iterator_t<R>>
       minmax_element(R&& r, Comp comp = {}, Proj proj = {});                              // since C++20
+
+  template<class I, class O>
+      using copy_result = in_out_result<I, O>;                                              // since C++20
+
+    template<class I, class O>
+      using copy_n_result = in_out_result<I, O>;                                            // since C++20
+
+  template<class I, class O>
+    using copy_if_result = in_out_result<I, O>;                                             // since C++20
+
+  template<class I1, class I2>
+    using copy_backward_result = in_out_result<I1, I2>;                                     // since C++20
+
+  template<input_iterator I, sentinel_for<I> S, weakly_incrementable O>
+    requires indirectly_copyable<I, O>
+    constexpr ranges::copy_result<I, O> ranges::copy(I first, S last, O result);            // since C++20
+
+  template<input_range R, weakly_incrementable O>
+    requires indirectly_copyable<iterator_t<R>, O>
+    constexpr ranges::copy_result<borrowed_iterator_t<R>, O> ranges::copy(R&& r, O result); // since C++20
+
+  template<input_iterator I, weakly_incrementable O>
+    requires indirectly_copyable<I, O>
+    constexpr ranges::copy_n_result<I, O>
+      ranges::copy_n(I first, iter_
diff erence_t<I> n, O result);                            // since C++20
+
+  template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
+           indirect_unary_predicate<projected<I, Proj>> Pred>
+    requires indirectly_copyable<I, O>
+    constexpr ranges::copy_if_result<I, O>
+      ranges::copy_if(I first, S last, O result, Pred pred, Proj proj = {});                // since C++20
+
+  template<input_range R, weakly_incrementable O, class Proj = identity,
+           indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+    requires indirectly_copyable<iterator_t<R>, O>
+    constexpr ranges::copy_if_result<borrowed_iterator_t<R>, O>
+      ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {});                          // since C++20
+
+  template<bidirectional_iterator I1, sentinel_for<I1> S1, bidirectional_iterator I2>
+    requires indirectly_copyable<I1, I2>
+    constexpr ranges::copy_backward_result<I1, I2>
+      ranges::copy_backward(I1 first, S1 last, I2 result);                                  // since C++20
+
+  template<bidirectional_range R, bidirectional_iterator I>
+    requires indirectly_copyable<iterator_t<R>, I>
+    constexpr ranges::copy_backward_result<borrowed_iterator_t<R>, I>
+      ranges::copy_backward(R&& r, I result);                                               // since C++20
 }
 
     constexpr bool     // constexpr in C++20
@@ -908,6 +955,10 @@ template <class BidirectionalIterator, class Compare>
 #include <__algorithm/pop_heap.h>
 #include <__algorithm/prev_permutation.h>
 #include <__algorithm/push_heap.h>
+#include <__algorithm/ranges_copy.h>
+#include <__algorithm/ranges_copy_backward.h>
+#include <__algorithm/ranges_copy_if.h>
+#include <__algorithm/ranges_copy_n.h>
 #include <__algorithm/ranges_count.h>
 #include <__algorithm/ranges_count_if.h>
 #include <__algorithm/ranges_find.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 5bf3b34610dd4..cd4fc9148b1f1 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -294,6 +294,10 @@ module std [system] {
       module pop_heap                 { private header "__algorithm/pop_heap.h" }
       module prev_permutation         { private header "__algorithm/prev_permutation.h" }
       module push_heap                { private header "__algorithm/push_heap.h" }
+      module ranges_copy              { private header "__algorithm/ranges_copy.h" }
+      module ranges_copy_backward     { private header "__algorithm/ranges_copy_backward.h" }
+      module ranges_copy_if           { private header "__algorithm/ranges_copy_if.h" }
+      module ranges_copy_n            { private header "__algorithm/ranges_copy_n.h" }
       module ranges_count             { private header "__algorithm/ranges_count.h" }
       module ranges_count_if          { private header "__algorithm/ranges_count_if.h" }
       module ranges_find              { private header "__algorithm/ranges_find.h" }

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 49551d3378f70..8b4305cd0e9b9 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -103,6 +103,10 @@ END-SCRIPT
 #include <__algorithm/pop_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pop_heap.h'}}
 #include <__algorithm/prev_permutation.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/prev_permutation.h'}}
 #include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}}
+#include <__algorithm/ranges_copy.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy.h'}}
+#include <__algorithm/ranges_copy_backward.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_backward.h'}}
+#include <__algorithm/ranges_copy_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_if.h'}}
+#include <__algorithm/ranges_copy_n.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_copy_n.h'}}
 #include <__algorithm/ranges_count.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count.h'}}
 #include <__algorithm/ranges_count_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_count_if.h'}}
 #include <__algorithm/ranges_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_find.h'}}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp
new file mode 100644
index 0000000000000..e692dca54de47
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.pass.cpp
@@ -0,0 +1,208 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<input_iterator I, sentinel_for<I> S, weakly_incrementable O>
+//   requires indirectly_copyable<I, O>
+//   constexpr ranges::copy_result<I, O> ranges::copy(I first, S last, O result);
+// template<input_range R, weakly_incrementable O>
+//   requires indirectly_copyable<iterator_t<R>, O>
+//   constexpr ranges::copy_result<borrowed_iterator_t<R>, O> ranges::copy(R&& r, O result);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+template <class In, class Out = In, class Sent = sentinel_wrapper<In>>
+concept HasCopyIt = requires(In in, Sent sent, Out out) { std::ranges::copy(in, sent, out); };
+
+static_assert(HasCopyIt<int*>);
+static_assert(!HasCopyIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCopyIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCopyIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCopyIt<int*, WeaklyIncrementableNotMovable>);
+struct NotIndirectlyCopyable {};
+static_assert(!HasCopyIt<int*, NotIndirectlyCopyable*>);
+static_assert(!HasCopyIt<int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasCopyIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+template <class Range, class Out>
+concept HasCopyR = requires(Range range, Out out) { std::ranges::copy(range, out); };
+
+static_assert(HasCopyR<std::array<int, 10>, int*>);
+static_assert(!HasCopyR<InputRangeNotDerivedFrom, int*>);
+static_assert(!HasCopyR<InputRangeNotIndirectlyReadable, int*>);
+static_assert(!HasCopyR<InputRangeNotInputOrOutputIterator, int*>);
+static_assert(!HasCopyR<WeaklyIncrementableNotMovable, int*>);
+static_assert(!HasCopyR<UncheckedRange<NotIndirectlyCopyable*>, int*>);
+static_assert(!HasCopyR<InputRangeNotSentinelSemiregular, int*>);
+static_assert(!HasCopyR<InputRangeNotSentinelEqualityComparableWith, int*>);
+
+static_assert(std::is_same_v<std::ranges::copy_result<int, long>, std::ranges::in_out_result<int, long>>);
+
+template <class In, class Out, class Sent = In>
+constexpr void test_iterators() {
+  { // simple test
+    {
+      std::array in {1, 2, 3, 4};
+      std::array<int, 4> out;
+      std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data()));
+      assert(in == out);
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+    {
+      std::array in {1, 2, 3, 4};
+      std::array<int, 4> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      std::same_as<std::ranges::in_out_result<In, Out>> auto ret = std::ranges::copy(range, Out(out.data()));
+      assert(in == out);
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+  }
+
+  { // check that an empty range works
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto ret = std::ranges::copy(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data()));
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      auto ret = std::ranges::copy(range, Out(out.data()));
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+  }
+}
+
+template <class Out>
+constexpr void test_in_iterators() {
+  test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_iterators<forward_iterator<int*>, Out>();
+  test_iterators<bidirectional_iterator<int*>, Out>();
+  test_iterators<random_access_iterator<int*>, Out>();
+  test_iterators<contiguous_iterator<int*>, Out>();
+}
+
+constexpr bool test() {
+  test_in_iterators<cpp20_input_iterator<int*>>();
+  test_in_iterators<forward_iterator<int*>>();
+  test_in_iterators<bidirectional_iterator<int*>>();
+  test_in_iterators<random_access_iterator<int*>>();
+  test_in_iterators<contiguous_iterator<int*>>();
+
+  { // check that ranges::dangling is returned
+    std::array<int, 4> out;
+    std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
+      std::ranges::copy(std::array {1, 2, 3, 4}, out.data());
+    assert(ret.out == out.data() + 4);
+    assert((out == std::array{1, 2, 3, 4}));
+  }
+
+  { // check that an iterator is returned with a borrowing range
+    std::array in {1, 2, 3, 4};
+    std::array<int, 4> out;
+    std::same_as<std::ranges::in_out_result<int*, int*>> auto ret = std::ranges::copy(std::views::all(in), out.data());
+    assert(ret.in == in.data() + 4);
+    assert(ret.out == out.data() + 4);
+    assert(in == out);
+  }
+
+  { // check that every element is copied exactly once
+    struct CopyOnce {
+      bool copied = false;
+      constexpr CopyOnce() = default;
+      constexpr CopyOnce(const CopyOnce& other) = delete;
+      constexpr CopyOnce& operator=(const CopyOnce& other) {
+        assert(!other.copied);
+        copied = true;
+        return *this;
+      }
+    };
+    {
+      std::array<CopyOnce, 4> in {};
+      std::array<CopyOnce, 4> out {};
+      auto ret = std::ranges::copy(in.begin(), in.end(), out.begin());
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; }));
+    }
+    {
+      std::array<CopyOnce, 4> in {};
+      std::array<CopyOnce, 4> out {};
+      auto ret = std::ranges::copy(in, out.begin());
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; }));
+    }
+  }
+
+  { // check that the range is copied forwards
+    struct OnlyForwardsCopyable {
+      OnlyForwardsCopyable* next = nullptr;
+      bool canCopy = false;
+      OnlyForwardsCopyable() = default;
+      constexpr OnlyForwardsCopyable& operator=(const OnlyForwardsCopyable&) {
+        assert(canCopy);
+        if (next != nullptr)
+          next->canCopy = true;
+        return *this;
+      }
+    };
+    {
+      std::array<OnlyForwardsCopyable, 3> in {};
+      std::array<OnlyForwardsCopyable, 3> out {};
+      out[0].next = &out[1];
+      out[1].next = &out[2];
+      out[0].canCopy = true;
+      auto ret = std::ranges::copy(in.begin(), in.end(), out.begin());
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(out[0].canCopy);
+      assert(out[1].canCopy);
+      assert(out[2].canCopy);
+    }
+    {
+      std::array<OnlyForwardsCopyable, 3> in {};
+      std::array<OnlyForwardsCopyable, 3> out {};
+      out[0].next = &out[1];
+      out[1].next = &out[2];
+      out[0].canCopy = true;
+      auto ret = std::ranges::copy(in, out.begin());
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(out[0].canCopy);
+      assert(out[1].canCopy);
+      assert(out[2].canCopy);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
new file mode 100644
index 0000000000000..d5e67d5fdb691
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp
@@ -0,0 +1,210 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+
+// template<bidirectional_iterator I1, sentinel_for<I1> S1, bidirectional_iterator I2>
+//   requires indirectly_copyable<I1, I2>
+//   constexpr ranges::copy_backward_result<I1, I2>
+//     ranges::copy_backward(I1 first, S1 last, I2 result);
+// template<bidirectional_range R, bidirectional_iterator I>
+//   requires indirectly_copyable<iterator_t<R>, I>
+//   constexpr ranges::copy_backward_result<borrowed_iterator_t<R>, I>
+//     ranges::copy_backward(R&& r, I result);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+template <class In, class Out = In, class Sent = sentinel_wrapper<In>>
+concept HasCopyBackwardIt = requires(In in, Sent sent, Out out) { std::ranges::copy_backward(in, sent, out); };
+
+static_assert(HasCopyBackwardIt<int*>);
+static_assert(!HasCopyBackwardIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCopyBackwardIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCopyBackwardIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCopyBackwardIt<int*, WeaklyIncrementableNotMovable>);
+struct NotIndirectlyCopyable {};
+static_assert(!HasCopyBackwardIt<int*, NotIndirectlyCopyable*>);
+static_assert(!HasCopyBackwardIt<int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasCopyBackwardIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+template <class Range, class Out>
+concept HasCopyBackwardR = requires(Range range, Out out) { std::ranges::copy_backward(range, out); };
+
+static_assert(HasCopyBackwardR<std::array<int, 10>, int*>);
+static_assert(!HasCopyBackwardR<InputRangeNotDerivedFrom, int*>);
+static_assert(!HasCopyBackwardR<InputRangeNotIndirectlyReadable, int*>);
+static_assert(!HasCopyBackwardR<InputRangeNotInputOrOutputIterator, int*>);
+static_assert(!HasCopyBackwardR<WeaklyIncrementableNotMovable, int*>);
+static_assert(!HasCopyBackwardR<UncheckedRange<NotIndirectlyCopyable*>, int*>);
+static_assert(!HasCopyBackwardR<InputRangeNotSentinelSemiregular, int*>);
+static_assert(!HasCopyBackwardR<InputRangeNotSentinelEqualityComparableWith, int*>);
+
+static_assert(std::is_same_v<std::ranges::copy_result<int, long>, std::ranges::in_out_result<int, long>>);
+
+template <class In, class Out, class Sent = In>
+constexpr void test_iterators() {
+  { // simple test
+    {
+      std::array in {1, 2, 3, 4};
+      std::array<int, 4> out;
+      std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+        std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size()));
+      assert(in == out);
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+    {
+      std::array in {1, 2, 3, 4};
+      std::array<int, 4> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+          std::ranges::copy_backward(range, Out(out.data() + out.size()));
+      assert(in == out);
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+  }
+
+  { // check that an empty range works
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto ret =
+          std::ranges::copy_backward(In(in.data()), Sent(In(in.data() + in.size())), Out(out.data() + out.size()));
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      auto ret = std::ranges::copy_backward(range, Out(out.data()));
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+  }
+}
+
+template <class Out>
+constexpr void test_in_iterators() {
+  test_iterators<bidirectional_iterator<int*>, Out>();
+  test_iterators<random_access_iterator<int*>, Out>();
+  test_iterators<contiguous_iterator<int*>, Out>();
+}
+
+constexpr bool test() {
+  test_in_iterators<bidirectional_iterator<int*>>();
+  test_in_iterators<random_access_iterator<int*>>();
+  test_in_iterators<contiguous_iterator<int*>>();
+
+  { // check that ranges::dangling is returned
+    std::array<int, 4> out;
+    std::same_as<std::ranges::in_out_result<std::ranges::dangling, int*>> auto ret =
+      std::ranges::copy_backward(std::array {1, 2, 3, 4}, out.data() + out.size());
+    assert(ret.out == out.data());
+    assert((out == std::array{1, 2, 3, 4}));
+  }
+
+  { // check that an iterator is returned with a borrowing range
+    std::array in {1, 2, 3, 4};
+    std::array<int, 4> out;
+    std::same_as<std::ranges::in_out_result<int*, int*>> auto ret =
+        std::ranges::copy_backward(std::views::all(in), out.data() + out.size());
+    assert(ret.in == in.data());
+    assert(ret.out == out.data());
+    assert(in == out);
+  }
+
+  { // check that every element is copied exactly once
+    struct CopyOnce {
+      bool copied = false;
+      constexpr CopyOnce() = default;
+      constexpr CopyOnce(const CopyOnce& other) = delete;
+      constexpr CopyOnce& operator=(const CopyOnce& other) {
+        assert(!other.copied);
+        copied = true;
+        return *this;
+      }
+    };
+    {
+      std::array<CopyOnce, 4> in {};
+      std::array<CopyOnce, 4> out {};
+      auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end());
+      assert(ret.in == in.begin());
+      assert(ret.out == out.begin());
+      assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; }));
+    }
+    {
+      std::array<CopyOnce, 4> in {};
+      std::array<CopyOnce, 4> out {};
+      auto ret = std::ranges::copy_backward(in, out.end());
+      assert(ret.in == in.begin());
+      assert(ret.out == out.begin());
+      assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; }));
+    }
+  }
+
+  { // check that the range is copied backwards
+    struct OnlyBackwardsCopyable {
+      OnlyBackwardsCopyable* next = nullptr;
+      bool canCopy = false;
+      OnlyBackwardsCopyable() = default;
+      constexpr OnlyBackwardsCopyable& operator=(const OnlyBackwardsCopyable&) {
+        assert(canCopy);
+        if (next != nullptr)
+          next->canCopy = true;
+        return *this;
+      }
+    };
+    {
+      std::array<OnlyBackwardsCopyable, 3> in {};
+      std::array<OnlyBackwardsCopyable, 3> out {};
+      out[1].next = &out[0];
+      out[2].next = &out[1];
+      out[2].canCopy = true;
+      auto ret = std::ranges::copy_backward(in, out.end());
+      assert(ret.in == in.begin());
+      assert(ret.out == out.begin());
+      assert(out[0].canCopy);
+      assert(out[1].canCopy);
+      assert(out[2].canCopy);
+    }
+    {
+      std::array<OnlyBackwardsCopyable, 3> in {};
+      std::array<OnlyBackwardsCopyable, 3> out {};
+      out[1].next = &out[0];
+      out[2].next = &out[1];
+      out[2].canCopy = true;
+      auto ret = std::ranges::copy_backward(in.begin(), in.end(), out.end());
+      assert(ret.in == in.begin());
+      assert(ret.out == out.begin());
+      assert(out[0].canCopy);
+      assert(out[1].canCopy);
+      assert(out[2].canCopy);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp
new file mode 100644
index 0000000000000..07a366a29d652
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_if.pass.cpp
@@ -0,0 +1,220 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<input_iterator I, sentinel_for<I> S, weakly_incrementable O, class Proj = identity,
+//          indirect_unary_predicate<projected<I, Proj>> Pred>
+//   requires indirectly_copyable<I, O>
+//   constexpr ranges::copy_if_result<I, O>
+//     ranges::copy_if(I first, S last, O result, Pred pred, Proj proj = {});
+// template<input_range R, weakly_incrementable O, class Proj = identity,
+//          indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+//   requires indirectly_copyable<iterator_t<R>, O>
+//   constexpr ranges::copy_if_result<borrowed_iterator_t<R>, O>
+//     ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {});
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+struct Functor {
+  bool operator()(int);
+};
+
+template <class In, class Out = In, class Sent = sentinel_wrapper<In>, class Func = Functor>
+concept HasCopyIfIt = requires(In first, Sent last, Out result) { std::ranges::copy_if(first, last, result, Func{}); };
+
+static_assert(HasCopyIfIt<int*>);
+static_assert(!HasCopyIfIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCopyIfIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCopyIfIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCopyIfIt<int*, WeaklyIncrementableNotMovable>);
+struct NotIndirectlyCopyable {};
+static_assert(!HasCopyIfIt<int*, NotIndirectlyCopyable*>);
+static_assert(!HasCopyIfIt<int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasCopyIfIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+static_assert(!HasCopyIfIt<int*, int*, int*, IndirectUnaryPredicateNotCopyConstructible>);
+static_assert(!HasCopyIfIt<int*, int*, int*, IndirectUnaryPredicateNotPredicate>);
+
+template <class Range, class Out, class Func = Functor>
+concept HasCopyIfR = requires(Range range, Out out) { std::ranges::copy_if(range, out, Func{}); };
+
+static_assert(HasCopyIfR<std::array<int, 10>, int*>);
+static_assert(!HasCopyIfR<InputRangeNotDerivedFrom, int*>);
+static_assert(!HasCopyIfR<InputRangeNotIndirectlyReadable, int*>);
+static_assert(!HasCopyIfR<InputRangeNotInputOrOutputIterator, int*>);
+static_assert(!HasCopyIfR<WeaklyIncrementableNotMovable, int*>);
+static_assert(!HasCopyIfR<UncheckedRange<NotIndirectlyCopyable*>, int*>);
+static_assert(!HasCopyIfR<InputRangeNotSentinelSemiregular, int*>);
+static_assert(!HasCopyIfR<InputRangeNotSentinelEqualityComparableWith, int*>);
+
+static_assert(std::is_same_v<std::ranges::copy_if_result<int, long>, std::ranges::in_out_result<int, long>>);
+
+template <class In, class Out, class Sent = In>
+constexpr void test_iterators() {
+  { // simple test
+    {
+      std::array in = {1, 2, 3, 4};
+      std::array<int, 4> out;
+      std::same_as<std::ranges::copy_if_result<In, Out>> auto ret =
+          std::ranges::copy_if(In(in.data()),
+                               Sent(In(in.data() + in.size())),
+                               Out(out.data()),
+                               [](int) { return true; });
+      assert(in == out);
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+    {
+      std::array in = {1, 2, 3, 4};
+      std::array<int, 4> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      std::same_as<std::ranges::copy_if_result<In, Out>> auto ret =
+          std::ranges::copy_if(range, Out(out.data()), [](int) { return true; });
+      assert(in == out);
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+  }
+
+  { // check that an empty range works
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto ret = std::ranges::copy_if(In(in.data()), Sent(In(in.data())), Out(out.data()), [](int) { return true; });
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+    {
+      std::array<int, 0> in;
+      std::array<int, 0> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data())));
+      auto ret = std::ranges::copy_if(range, Out(out.data()), [](int) { return true; });
+      assert(base(ret.in) == in.data());
+      assert(base(ret.out) == out.data());
+    }
+  }
+
+  { // check that the predicate is used
+    {
+      std::array in = {4, 6, 87, 3, 88, 44, 45, 9};
+      std::array<int, 4> out;
+      auto ret = std::ranges::copy_if(In(in.data()),
+                                      Sent(In(in.data() + in.size())),
+                                      Out(out.data()),
+                                      [](int i) { return i % 2 == 0; });
+      assert((out == std::array{4, 6, 88, 44}));
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+    {
+      std::array in = {4, 6, 87, 3, 88, 44, 45, 9};
+      std::array<int, 4> out;
+      auto range = std::ranges::subrange(In(in.data()), Sent(In(in.data() + in.size())));
+      auto ret = std::ranges::copy_if(range, Out(out.data()), [](int i) { return i % 2 == 0; });
+      assert((out == std::array{4, 6, 88, 44}));
+      assert(base(ret.in) == in.data() + in.size());
+      assert(base(ret.out) == out.data() + out.size());
+    }
+  }
+}
+
+template <class Out>
+constexpr bool test_in_iterators() {
+  test_iterators<cpp17_input_iterator<int*>, Out, sentinel_wrapper<cpp17_input_iterator<int*>>>();
+  test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_iterators<forward_iterator<int*>, Out>();
+  test_iterators<bidirectional_iterator<int*>, Out>();
+  test_iterators<random_access_iterator<int*>, Out>();
+  test_iterators<contiguous_iterator<int*>, Out>();
+  test_iterators<int*, Out>();
+
+  return true;
+}
+
+constexpr bool test() {
+  test_in_iterators<cpp17_output_iterator<int*>>();
+  test_in_iterators<cpp20_output_iterator<int*>>();
+  test_in_iterators<forward_iterator<int*>>();
+  test_in_iterators<bidirectional_iterator<int*>>();
+  test_in_iterators<random_access_iterator<int*>>();
+  test_in_iterators<contiguous_iterator<int*>>();
+  test_in_iterators<int*>();
+
+  { // check that std::invoke is used
+    {
+      struct S { int val; int other; };
+      std::array<S, 4> in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}};
+      std::array<S, 2> out;
+      auto ret = std::ranges::copy_if(in.begin(), in.end(), out.begin(), [](int i) { return i == 3; }, &S::val);
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(out[0].val == 3);
+      assert(out[0].other == 4);
+      assert(out[1].val == 3);
+      assert(out[1].other == 5);
+    }
+    {
+      struct S { int val; int other; };
+      std::array<S, 4> in = {{{4, 2}, {1, 3}, {3, 4}, {3, 5}}};
+      std::array<S, 2> out;
+      auto ret = std::ranges::copy_if(in, out.begin(), [](int i) { return i == 3; }, &S::val);
+      assert(ret.in == in.end());
+      assert(ret.out == out.end());
+      assert(out[0].val == 3);
+      assert(out[0].other == 4);
+      assert(out[1].val == 3);
+      assert(out[1].other == 5);
+    }
+  }
+
+  { // check that the complexity requirements are met
+    {
+      int predicateCount = 0;
+      int projectionCount = 0;
+      auto pred = [&](int i) { ++predicateCount; return i != 0; };
+      auto proj = [&](int i) { ++projectionCount; return i; };
+
+      int a[] = {5, 4, 3, 2, 1};
+      int b[5];
+      std::ranges::copy_if(a, a + 5, b, pred, proj);
+      assert(predicateCount == 5);
+      assert(projectionCount == 5);
+    }
+    {
+      int predicateCount = 0;
+      int projectionCount = 0;
+      auto pred = [&](int i) { ++predicateCount; return i != 0; };
+      auto proj = [&](int i) { ++projectionCount; return i; };
+
+      int a[] = {5, 4, 3, 2, 1};
+      int b[5];
+      std::ranges::copy_if(a, b, pred, proj);
+      assert(predicateCount == 5);
+      assert(projectionCount == 5);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}

diff  --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
new file mode 100644
index 0000000000000..e5c2efef9c883
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.pass.cpp
@@ -0,0 +1,106 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-has-no-incomplete-ranges
+
+// template<input_iterator I, weakly_incrementable O>
+//   requires indirectly_copyable<I, O>
+//   constexpr ranges::copy_n_result<I, O>
+//     ranges::copy_n(I first, iter_
diff erence_t<I> n, O result);
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <ranges>
+
+#include "almost_satisfies_types.h"
+#include "test_iterators.h"
+
+template <class In, class Out = In, class Count = size_t>
+concept HasCopyNIt = requires(In in, Count count, Out out) { std::ranges::copy_n(in, count, out); };
+
+static_assert(HasCopyNIt<int*>);
+static_assert(!HasCopyNIt<InputIteratorNotDerivedFrom>);
+static_assert(!HasCopyNIt<InputIteratorNotIndirectlyReadable>);
+static_assert(!HasCopyNIt<InputIteratorNotInputOrOutputIterator>);
+static_assert(!HasCopyNIt<int*, WeaklyIncrementableNotMovable>);
+struct NotIndirectlyCopyable {};
+static_assert(!HasCopyNIt<int*, NotIndirectlyCopyable*>);
+static_assert(!HasCopyNIt<int*, int*, SentinelForNotSemiregular>);
+static_assert(!HasCopyNIt<int*, int*, SentinelForNotWeaklyEqualityComparableWith>);
+
+static_assert(std::is_same_v<std::ranges::copy_result<int, long>, std::ranges::in_out_result<int, long>>);
+
+template <class In, class Out, class Sent = In>
+constexpr void test_iterators() {
+  { // simple test
+    std::array in {1, 2, 3, 4};
+    std::array<int, 4> out;
+    std::same_as<std::ranges::in_out_result<In, Out>> auto ret =
+      std::ranges::copy_n(In(in.data()), in.size(), Out(out.data()));
+    assert(in == out);
+    assert(base(ret.in) == in.data() + in.size());
+    assert(base(ret.out) == out.data() + out.size());
+  }
+
+  { // check that an empty range works
+    std::array<int, 0> in;
+    std::array<int, 0> out;
+    auto ret = std::ranges::copy_n(In(in.data()), in.size(), Out(out.begin()));
+    assert(base(ret.in) == in.data());
+    assert(base(ret.out) == out.data());
+  }
+}
+
+template <class Out>
+constexpr void test_in_iterators() {
+  test_iterators<cpp20_input_iterator<int*>, Out, sentinel_wrapper<cpp20_input_iterator<int*>>>();
+  test_iterators<forward_iterator<int*>, Out>();
+  test_iterators<bidirectional_iterator<int*>, Out>();
+  test_iterators<random_access_iterator<int*>, Out>();
+  test_iterators<contiguous_iterator<int*>, Out>();
+}
+
+constexpr bool test() {
+  test_in_iterators<cpp20_input_iterator<int*>>();
+  test_in_iterators<forward_iterator<int*>>();
+  test_in_iterators<bidirectional_iterator<int*>>();
+  test_in_iterators<random_access_iterator<int*>>();
+  test_in_iterators<contiguous_iterator<int*>>();
+
+  { // check that every element is copied exactly once
+    struct CopyOnce {
+      bool copied = false;
+      constexpr CopyOnce() = default;
+      constexpr CopyOnce(const CopyOnce& other) = delete;
+      constexpr CopyOnce& operator=(const CopyOnce& other) {
+        assert(!other.copied);
+        copied = true;
+        return *this;
+      }
+    };
+    std::array<CopyOnce, 4> in {};
+    std::array<CopyOnce, 4> out {};
+    auto ret = std::ranges::copy_n(in.data(), in.size(), out.begin());
+    assert(ret.in == in.end());
+    assert(ret.out == out.end());
+    assert(std::all_of(out.begin(), out.end(), [](const auto& e) { return e.copied; }));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}


        


More information about the libcxx-commits mailing list