[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