[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::uninitialized_(copy|move)(_n) based on parallel __for_each (PR #223229)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Sep 17 04:00:41 PDT 2026
https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/223229
>From e7db02892763bcc883430557d6bbe2a58bca0b85 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 13 Sep 2026 11:55:18 +0100
Subject: [PATCH 1/7] Implementation of parallel uninitialized_(copy|move)(_n)
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__memory/pstl.h | 60 +++++++
libcxx/include/__pstl/backend_fwd.h | 28 ++++
libcxx/include/__pstl/backends/default.h | 52 ++++++
libcxx/include/__pstl/backends/libdispatch.h | 9 ++
libcxx/include/__pstl/backends/serial.h | 19 +++
libcxx/include/__pstl/backends/std_thread.h | 9 ++
libcxx/include/__pstl/cpu_algos/for_each.h | 18 +++
.../cpu_algos/uninitialized_algorithms.h | 97 +++++++++++
libcxx/include/module.modulemap.in | 3 +
.../pstl.iterator-requirements.verify.cpp | 21 ++-
.../pstl.exception_handling.pass.cpp | 22 +++
.../pstl.uninitialized_copy.pass.cpp | 145 +++++++++++++++++
.../pstl.uninitialized_copy_n.pass.cpp | 146 +++++++++++++++++
.../pstl.uninitialized_move.pass.cpp | 150 +++++++++++++++++
.../pstl.uninitialized_move_n.pass.cpp | 153 ++++++++++++++++++
16 files changed, 932 insertions(+), 1 deletion(-)
create mode 100644 libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 113d4fde7ca59d..2ec3881db77742 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -711,6 +711,7 @@ set(files
__pstl/cpu_algos/stable_sort.h
__pstl/cpu_algos/transform.h
__pstl/cpu_algos/transform_reduce.h
+ __pstl/cpu_algos/uninitialized_algorithms.h
__pstl/dispatch.h
__pstl/handle_exception.h
__random/bernoulli_distribution.h
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
index c06f53bed7206e..2b4011bd158cb8 100644
--- a/libcxx/include/__memory/pstl.h
+++ b/libcxx/include/__memory/pstl.h
@@ -82,6 +82,36 @@ uninitialized_default_construct_n(_ExecutionPolicy&& __policy, _ForwardIterator
__pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
}
+template <class _ExecutionPolicy,
+ class _InputIterator,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_copy(
+ _ExecutionPolicy&& __policy, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) {
+ _LIBCPP_REQUIRE_CPP17_INPUT_ITERATOR(_InputIterator, "uninitialized_copy requires InputIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_copy requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__uninitialized_copy, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
+template <class _ExecutionPolicy,
+ class _InputIterator,
+ class _Size,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator
+uninitialized_copy_n(_ExecutionPolicy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) {
+ _LIBCPP_REQUIRE_CPP17_INPUT_ITERATOR(_InputIterator, "uninitialized_copy_n requires InputIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_copy_n requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_copy_n, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n, std::move(__result));
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator,
class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
@@ -136,6 +166,36 @@ uninitialized_fill_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Siz
std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n, __value);
}
+template <class _ExecutionPolicy,
+ class _InputIterator,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardIterator uninitialized_move(
+ _ExecutionPolicy&& __policy, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) {
+ _LIBCPP_REQUIRE_CPP17_INPUT_ITERATOR(_InputIterator, "uninitialized_move requires InputIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_move requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__uninitialized_move, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__result));
+}
+
+template <class _ExecutionPolicy,
+ class _InputIterator,
+ class _Size,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI pair<_InputIterator, _ForwardIterator>
+uninitialized_move_n(_ExecutionPolicy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) {
+ _LIBCPP_REQUIRE_CPP17_INPUT_ITERATOR(_InputIterator, "uninitialized_move_n requires InputIterators");
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_move_n requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_move_n, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n, std::move(__result));
+}
+
_LIBCPP_END_NAMESPACE_STD
#endif // _LIBCPP_HAS_EXPERIMENTAL_PSTL && _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 40a335ae1f2adb..570afef8a8a346 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -430,6 +430,20 @@ struct __destroy_n;
// optional<__empty>
// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_copy;
+// template <class _Policy, class _InputIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const
+// noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_copy_n;
+// template <class _Policy, class _InputIterator, class _Size, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) const
+// noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __uninitialized_default_construct;
// template <class _Policy, class _ForwardIterator>
@@ -442,6 +456,20 @@ struct __uninitialized_default_construct_n;
// optional<__empty>
// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_move;
+// template <class _Policy, class _InputIterator, class _ForwardIterator>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const
+// noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_move_n;
+// template <class _Policy, class _InputIterator, class _Size, class _ForwardIterator>
+// optional<pair<_InputIterator, _ForwardIterator>>
+// operator()(_Policy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) const
+// noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __uninitialized_value_construct;
// template <class _Policy, class _ForwardIterator>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index b5d7045bbb9b85..dd67d5fcc84e44 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -29,6 +29,7 @@
#include <__iterator/reverse_iterator.h>
#include <__memory/addressof.h>
#include <__memory/construct_at.h>
+#include <__memory/uninitialized_algorithms.h>
#include <__optional/comparison.h>
#include <__optional/nullopt_t.h>
#include <__optional/optional.h>
@@ -151,6 +152,14 @@ namespace __pstl {
// - reverse_copy
// - rotate_copy
//
+// uninitialized_copy family
+// -------------------------------------
+// - uninitialized_copy_n
+//
+// uninitialized_move family
+// -------------------------------------
+// - uninitialized_move_n
+//
//////////////////////////////////////////////////////////////
// find_if family
@@ -906,6 +915,49 @@ struct __adjacent_difference<__default_backend_tag, _ExecutionPolicy> {
}
};
+//////////////////////////////////////////////////////////////
+// uninitialized_copy family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __uninitialized_copy_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _InputIterator, class _Size, class _ForwardIterator>
+ optional<_ForwardIterator>
+ operator()(_Policy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) const noexcept {
+ if constexpr (__has_random_access_iterator_category_or_concept<_InputIterator>::value &&
+ __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+ using _UninitializedCopy = __dispatch<__uninitialized_copy, __current_configuration, _ExecutionPolicy>;
+ _InputIterator __last = __first + __n;
+ return _UninitializedCopy()(__policy, std::move(__first), std::move(__last), std::move(__result));
+ } else {
+ return std::uninitialized_copy_n(std::move(__first), __n, std::move(__result));
+ }
+ }
+};
+
+//////////////////////////////////////////////////////////////
+// uninitialized_move family
+//////////////////////////////////////////////////////////////
+
+template <class _ExecutionPolicy>
+struct __uninitialized_move_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _InputIterator, class _Size, class _ForwardIterator>
+ optional<pair<_InputIterator, _ForwardIterator>>
+ operator()(_Policy&& __policy, _InputIterator __first, _Size __n, _ForwardIterator __result) const noexcept {
+ if constexpr (__has_random_access_iterator_category_or_concept<_InputIterator>::value &&
+ __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+ using _UninitializedMove = __dispatch<__uninitialized_move, __current_configuration, _ExecutionPolicy>;
+ _InputIterator __last = __first + __n;
+ auto __res = _UninitializedMove()(__policy, std::move(__first), __last, std::move(__result));
+ if (!__res)
+ return nullopt; // Failed to run the parallel algorithm, propagate the failure
+ return pair{__last, *__res};
+ } else {
+ return std::uninitialized_move_n(std::move(__first), __n, std::move(__result));
+ }
+ }
+};
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index dbd9fa34e1f876..5b245373eb16b1 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -51,6 +51,7 @@
#include <__pstl/cpu_algos/stable_sort.h>
#include <__pstl/cpu_algos/transform.h>
#include <__pstl/cpu_algos/transform_reduce.h>
+#include <__pstl/cpu_algos/uninitialized_algorithms.h>
#include <__utility/empty.h>
#include <__utility/exception_guard.h>
#include <__utility/move.h>
@@ -439,6 +440,14 @@ template <class _ExecutionPolicy>
struct __fill<__libdispatch_backend_tag, _ExecutionPolicy>
: __cpu_parallel_fill<__libdispatch_backend_tag, _ExecutionPolicy> {};
+template <class _ExecutionPolicy>
+struct __uninitialized_copy<__libdispatch_backend_tag, _ExecutionPolicy>
+ : __cpu_parallel_uninitialized_copy<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_move<__libdispatch_backend_tag, _ExecutionPolicy>
+ : __cpu_parallel_uninitialized_move<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
} // namespace __pstl
_LIBCPP_END_EXPLICIT_ABI_ANNOTATIONS
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 85c73b0f15db57..c779d583089d2c 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -24,6 +24,7 @@
#include <__algorithm/stable_sort.h>
#include <__algorithm/transform.h>
#include <__config>
+#include <__memory/uninitialized_algorithms.h>
#include <__numeric/transform_reduce.h>
#include <__optional/optional.h>
#include <__pstl/backend_fwd.h>
@@ -284,6 +285,24 @@ struct __transform_reduce_binary<__serial_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __uninitialized_copy<__serial_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _InputIterator, class _ForwardIterator>
+ _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+ operator()(_Policy&&, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const noexcept {
+ return std::uninitialized_copy(std::move(__first), std::move(__last), std::move(__result));
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_move<__serial_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _InputIterator, class _ForwardIterator>
+ _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+ operator()(_Policy&&, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const noexcept {
+ return std::uninitialized_move(std::move(__first), std::move(__last), std::move(__result));
+ }
+};
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index bc7fc020867f8b..bdbddb9d36300e 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -30,6 +30,7 @@
#include <__pstl/cpu_algos/stable_sort.h>
#include <__pstl/cpu_algos/transform.h>
#include <__pstl/cpu_algos/transform_reduce.h>
+#include <__pstl/cpu_algos/uninitialized_algorithms.h>
#include <__utility/empty.h>
#include <__utility/move.h>
@@ -170,6 +171,14 @@ template <class _ExecutionPolicy>
struct __fill<__std_thread_backend_tag, _ExecutionPolicy>
: __cpu_parallel_fill<__std_thread_backend_tag, _ExecutionPolicy> {};
+template <class _ExecutionPolicy>
+struct __uninitialized_copy<__std_thread_backend_tag, _ExecutionPolicy>
+ : __cpu_parallel_uninitialized_copy<__std_thread_backend_tag, _ExecutionPolicy> {};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_move<__std_thread_backend_tag, _ExecutionPolicy>
+ : __cpu_parallel_uninitialized_move<__std_thread_backend_tag, _ExecutionPolicy> {};
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/cpu_algos/for_each.h b/libcxx/include/__pstl/cpu_algos/for_each.h
index 908f82f01e6c80..bc29fa4e3da44c 100644
--- a/libcxx/include/__pstl/cpu_algos/for_each.h
+++ b/libcxx/include/__pstl/cpu_algos/for_each.h
@@ -37,6 +37,24 @@ _LIBCPP_HIDE_FROM_ABI _Iterator __simd_for_each(_Iterator __first, _DifferenceTy
return __first + __n;
}
+// Applies the 3-legged function to sub-ranges of the input ranges in parallel:
+// f(sub_range1_first, sub_range1_last, sub_range2_first)
+template <class _Backend, class _RandomAccessIterator1, class _RandomAccessIterator2, class _BrickFunction>
+_LIBCPP_HIDE_FROM_ABI optional<__empty> __parallel_for_each_iter_pair(
+ _RandomAccessIterator1 __first1,
+ _RandomAccessIterator1 __last1,
+ _RandomAccessIterator2 __first2,
+ _BrickFunction __f) {
+ return __cpu_traits<_Backend>::__for_each(
+ __first1,
+ __last1,
+ [__first1, __first2, __f = std::move(__f)](
+ _RandomAccessIterator1 __brick_first1, _RandomAccessIterator1 __brick_last1) {
+ _RandomAccessIterator2 __brick_first2 = __first2 + (__brick_first1 - __first1);
+ __f(__brick_first1, __brick_last1, __brick_first2);
+ });
+}
+
template <class _Backend, class _RawExecutionPolicy>
struct __cpu_parallel_for_each {
template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h b/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
new file mode 100644
index 00000000000000..41ee8bc3b7a534
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___PSTL_CPU_ALGOS_UNINITIALIZED_ALGORITHMS_H
+#define _LIBCPP___PSTL_CPU_ALGOS_UNINITIALIZED_ALGORITHMS_H
+
+#include <__config>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/uninitialized_algorithms.h>
+#include <__optional/nullopt_t.h>
+#include <__optional/optional.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/for_each.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_uninitialized_copy {
+ template <class _Policy, class _InputIterator, class _ForwardIterator>
+ _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+ operator()(_Policy&&, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const noexcept {
+ if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+ __has_random_access_iterator_category_or_concept<_InputIterator>::value &&
+ __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+ auto __res = __parallel_for_each_iter_pair<_Backend>(
+ __first,
+ __last,
+ __result,
+ [](_InputIterator __brick_first, _InputIterator __brick_last, _ForwardIterator __brick_result) {
+ std::uninitialized_copy(std::move(__brick_first), std::move(__brick_last), std::move(__brick_result));
+ });
+ if (!__res) {
+ return std::nullopt; // Failed to run the algorithm, propagate the error.
+ }
+ return __result + (__last - __first);
+ } else {
+ // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+ return std::uninitialized_copy(std::move(__first), std::move(__last), std::move(__result));
+ }
+ }
+};
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_uninitialized_move {
+ template <class _Policy, class _InputIterator, class _ForwardIterator>
+ _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+ operator()(_Policy&&, _InputIterator __first, _InputIterator __last, _ForwardIterator __result) const noexcept {
+ if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+ __has_random_access_iterator_category_or_concept<_InputIterator>::value &&
+ __has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+ auto __res = __parallel_for_each_iter_pair<_Backend>(
+ __first,
+ __last,
+ __result,
+ [](_InputIterator __brick_first, _InputIterator __brick_last, _ForwardIterator __brick_result) {
+ std::uninitialized_move(std::move(__brick_first), std::move(__brick_last), std::move(__brick_result));
+ });
+ if (!__res) {
+ return std::nullopt; // Failed to run the algorithm, propagate the error.
+ }
+ return __result + (__last - __first);
+ } else {
+ // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+ return std::uninitialized_move(std::move(__first), std::move(__last), std::move(__result));
+ }
+ }
+};
+
+} // namespace __pstl
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_UNINITIALIZED_ALGORITHMS_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index d22a9ce36d02cd..ccd011a3c00e54 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2474,6 +2474,9 @@ module std {
module transform_reduce {
header "__pstl/cpu_algos/transform_reduce.h"
}
+ module uninitialized_algorithms {
+ header "__pstl/cpu_algos/uninitialized_algorithms.h"
+ }
}
module dispatch { header "__pstl/dispatch.h" }
module handle_exception { header "__pstl/handle_exception.h" }
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index f12d5bdcb31218..bbc669bfdc5548 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -32,6 +32,7 @@
#include "test_iterators.h"
+using non_input_iterator = cpp17_output_iterator<int*>;
using non_forward_iterator = cpp17_input_iterator<int*>;
using non_bidirectional_iterator = forward_iterator<int*>;
using non_randomaccess_iterator = bidirectional_iterator<int*>;
@@ -39,7 +40,8 @@ struct non_output_iterator : forward_iterator<int*> {
constexpr int const& operator*() const; // prevent it from being an output iterator
};
-void f(non_forward_iterator non_fwd,
+void f(non_input_iterator non_input,
+ non_forward_iterator non_fwd,
non_output_iterator non_output,
non_bidirectional_iterator non_bidir,
non_randomaccess_iterator non_random,
@@ -338,4 +340,21 @@ void f(non_forward_iterator non_fwd,
std::uninitialized_fill_n(
pol, non_fwd, n, val); // expected-error@*:* {{static assertion failed: uninitialized_fill_n}}
}
+
+ {
+ std::uninitialized_copy(
+ pol, non_input, non_input, it); // expected-error@*:* {{static assertion failed: uninitialized_copy}}
+ std::uninitialized_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_copy}}
+ std::uninitialized_copy_n(
+ pol, non_input, n, it); // expected-error@*:* {{static assertion failed: uninitialized_copy_n}}
+ std::uninitialized_copy_n(
+ pol, it, n, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_copy_n}}
+ std::uninitialized_move(
+ pol, non_input, non_input, it); // expected-error@*:* {{static assertion failed: uninitialized_move}}
+ std::uninitialized_move(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_move}}
+ std::uninitialized_move_n(
+ pol, non_input, n, it); // expected-error@*:* {{static assertion failed: uninitialized_move_n}}
+ std::uninitialized_move_n(
+ pol, it, n, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_move_n}}
+ }
}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index b85cc984d969d9..f6aeaf68eb96ba 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -566,6 +566,28 @@ int main(int, char**) {
// uninitialized_fill_n(first, n, val)
assert_non_throwing([=, &policy] { std::uninitialized_fill_n(policy, std::move(first1), n, val); });
}
+
+ {
+ // uninitialized_copy(first, last, dest)
+ assert_non_throwing([=, &policy] {
+ (void)std::uninitialized_copy(policy, std::move(first1), std::move(last1), std::move(dest));
+ });
+
+ // uninitialized_copy_n(first, n, dest)
+ assert_non_throwing([=, &policy] {
+ (void)std::uninitialized_copy_n(policy, std::move(first1), n, std::move(dest));
+ });
+
+ // uninitialized_move(first, last, dest)
+ assert_non_throwing([=, &policy] {
+ (void)std::uninitialized_move(policy, std::move(first1), std::move(last1), std::move(dest));
+ });
+
+ // uninitialized_move_n(first, n, dest)
+ assert_non_throwing([=, &policy] {
+ (void)std::uninitialized_move_n(policy, std::move(first1), n, std::move(dest));
+ });
+ }
}
});
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
new file mode 100644
index 00000000000000..d2f8c4cd66a476
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
@@ -0,0 +1,145 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <memory>
+
+// template <class ExecutionPolicy,
+// class InputIterator,
+// class ForwardIterator>
+// ForwardIterator uninitialized_copy(ExecutionPolicy&& exec,
+// InputIterator first, InputIterator last,
+// ForwardIterator result);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(uninitialized_copy);
+
+static_assert(sfinae_test_uninitialized_copy<int, int*, int*, int*>);
+static_assert(!sfinae_test_uninitialized_copy<std::execution::parallel_policy, int*, int*, int*>);
+
+// Source type
+struct Src {
+ Src(int v) : value_(v) {}
+ Src(const Src&) = delete;
+ Src& operator=(const Src&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+// Destination type
+struct Dst {
+ Dst(const Src& x) : value_(x.value()) {} // Can only be constructed from Src
+ Dst(const Dst&) = delete;
+ ~Dst() { value_ = 0; };
+ Dst& operator=(const Dst&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ constexpr size_t n = 1073;
+ std::allocator<Src> alloc_src;
+ std::allocator<Dst> alloc_dst;
+ Src* source = alloc_src.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
+
+ // Source is Src(1), Src(2), Src(3), ...
+ for (size_t i = 0; i < n; ++i) {
+ std::construct_at(source + i, static_cast<int>(i + 1));
+ }
+
+ // Copy-construct different ranges of Y [0..size) from the source X array
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_copy(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(dest[i].value() == source[i].value()); // Dest is Dst(1), Dst(2), Dst(3), ...
+ }
+
+ std::destroy_n(dest, size); // Clear dest for the next iteration
+ });
+
+ std::destroy_n(source, n);
+ alloc_src.deallocate(source, n);
+ alloc_dst.deallocate(dest, n);
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr size_t n = 1073;
+ std::allocator<int> alloc;
+ int* source = alloc.allocate(n);
+ int* dest = alloc.allocate(n);
+
+ std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_copy(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(dest[i] == source[i]); // Dest is 1 2 3 4 ...
+ }
+
+ std::fill(dest, dest + size, 0); // Clear dest for the next iteration
+ });
+
+ alloc.deallocate(source, n);
+ alloc.deallocate(dest, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::cpp17_input_iterator_list<const Src*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<Dst*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+ }});
+ types::for_each(types::cpp17_input_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
new file mode 100644
index 00000000000000..4a373fc1924fd6
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
@@ -0,0 +1,146 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <memory>
+
+// template <class ExecutionPolicy,
+// class InputIterator,
+// class Size,
+// class ForwardIterator>
+// ForwardIterator uninitialized_copy_n(ExecutionPolicy&& exec,
+// InputIterator first, Size n,
+// ForwardIterator result);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(uninitialized_copy_n);
+
+static_assert(sfinae_test_uninitialized_copy_n<int, int*, int, int*>);
+static_assert(!sfinae_test_uninitialized_copy_n<std::execution::parallel_policy, int*, int, int*>);
+
+// Source type
+struct Src {
+ Src(int v) : value_(v) {}
+ Src(const Src&) = delete;
+ Src& operator=(const Src&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+// Destination type
+struct Dst {
+ Dst(const Src& x) : value_(x.value()) {} // Can only be constructed from Src
+ Dst(const Dst&) = delete;
+ ~Dst() { value_ = 0; };
+ Dst& operator=(const Dst&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ constexpr size_t n = 1073;
+ std::allocator<Src> alloc_src;
+ std::allocator<Dst> alloc_dst;
+ Src* source = alloc_src.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
+
+ // Source is Src(1), Src(2), Src(3), ...
+ for (size_t i = 0; i < n; ++i) {
+ std::construct_at(source + i, static_cast<int>(i + 1));
+ }
+
+ // Copy-construct different ranges of Y [0..size) from the source X array
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_copy_n(policy, Iter1(source), size, Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(dest[i].value() == source[i].value()); // Dest is Dst(1), Dst(2), Dst(3), ...
+ }
+
+ std::destroy_n(dest, size); // Clear dest for the next iteration
+ });
+
+ std::destroy_n(source, n);
+ alloc_src.deallocate(source, n);
+ alloc_dst.deallocate(dest, n);
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr size_t n = 1073;
+ std::allocator<int> alloc;
+ int* source = alloc.allocate(n);
+ int* dest = alloc.allocate(n);
+
+ std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_copy_n(policy, Iter1(source), size, Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(dest[i] == source[i]); // Dest is 1 2 3 4 ...
+ }
+
+ std::fill(dest, dest + size, 0); // Clear dest for the next iteration
+ });
+
+ alloc.deallocate(source, n);
+ alloc.deallocate(dest, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::cpp17_input_iterator_list<const Src*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<Dst*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+ }});
+ types::for_each(types::cpp17_input_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
new file mode 100644
index 00000000000000..f90f06654835f5
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
@@ -0,0 +1,150 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <memory>
+
+// template <class ExecutionPolicy,
+// class InputIterator,
+// class ForwardIterator>
+// ForwardIterator uninitialized_move(ExecutionPolicy&& exec,
+// InputIterator first, InputIterator last,
+// ForwardIterator result);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(uninitialized_move);
+
+static_assert(sfinae_test_uninitialized_move<int, int*, int*, int*>);
+static_assert(!sfinae_test_uninitialized_move<std::execution::parallel_policy, int*, int*, int*>);
+
+// Source type
+struct Src {
+ Src(int v) : value_(v) {}
+ Src(const Src&) = delete;
+ Src& operator=(const Src&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+ friend struct Dst;
+};
+
+// Destination type
+struct Dst {
+ Dst(const Src&) { assert(0); }
+ Dst(Src&& x) : value_(x.value()) { x.value_ = 0; } // Destructive move-construct
+ Dst(const Dst&) = delete;
+ ~Dst() { value_ = 0; };
+ Dst& operator=(const Dst&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ constexpr size_t n = 1073;
+ std::allocator<Src> alloc_src;
+ std::allocator<Dst> alloc_dst;
+ Src* source = alloc_src.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
+
+ // Move-construct different ranges of Y [0..size) from the source X array
+ runway_sample(n + 1, [&](size_t size) {
+ // Construct the source array for this iteration: X(1), X(2), X(3), ...
+ for (size_t i = 0; i < size; ++i) {
+ std::construct_at(source + i, static_cast<int>(i + 1));
+ }
+
+ auto ret = std::uninitialized_move(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(source[i].value() == 0); // Source is X(0), X(0), X(0), ...
+ assert(dest[i].value() == static_cast<int>(i + 1)); // Dest is Y(1), Y(2), Y(3), ...
+ }
+
+ std::destroy_n(source, size); // Clear source for the next iteration
+ std::destroy_n(dest, size); // Clear dest for the next iteration
+ });
+
+ alloc_src.deallocate(source, n);
+ alloc_dst.deallocate(dest, n);
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr size_t n = 1073;
+ std::allocator<int> alloc;
+ int* source = alloc.allocate(n);
+ int* dest = alloc.allocate(n);
+
+ std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_move(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), Iter2);
+ assert(ret == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ // For trivial types, move is the same as copy
+ assert(dest[i] == static_cast<int>(i + 1));
+ assert(dest[i] == source[i]); // Both source and dest are 1 2 3 4 ...
+ }
+
+ std::fill(dest, dest + size, 0); // Clear dest for the next iteration
+ });
+
+ alloc.deallocate(source, n);
+ alloc.deallocate(dest, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::cpp17_input_iterator_list<Src*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<Dst*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+ }});
+ types::for_each(types::cpp17_input_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
new file mode 100644
index 00000000000000..df7eed353881c6
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -0,0 +1,153 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <memory>
+
+// template <class ExecutionPolicy,
+// class InputIterator,
+// class Size,
+// class ForwardIterator>
+// ForwardIterator uninitialized_move_n(ExecutionPolicy&& exec,
+// InputIterator first, Size n,
+// ForwardIterator result);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <cstring>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(uninitialized_move_n);
+
+static_assert(sfinae_test_uninitialized_move_n<int, int*, int, int*>);
+static_assert(!sfinae_test_uninitialized_move_n<std::execution::parallel_policy, int*, int, int*>);
+
+// Source type
+struct Src {
+ Src(int v) : value_(v) {}
+ Src(const Src&) = delete;
+ Src& operator=(const Src&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+ friend struct Dst;
+};
+
+// Destination type
+struct Dst {
+ Dst(const Src&) { assert(0); }
+ Dst(Src&& x) : value_(x.value()) { x.value_ = 0; } // Destructive move-construct
+ Dst(const Dst&) = delete;
+ ~Dst() { value_ = 0; };
+ Dst& operator=(const Dst&) = delete;
+ int value() const { return value_; }
+
+private:
+ int value_;
+};
+
+template <class Iter1, class Iter2>
+struct TestCustomTypes {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ constexpr size_t n = 1073;
+ std::allocator<Src> alloc_src;
+ std::allocator<Dst> alloc_dst;
+ Src* source = alloc_src.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
+
+ // Move-construct different ranges of Y [0..size) from the source X array
+ runway_sample(n + 1, [&](size_t size) {
+ // Construct the source array for this iteration: X(1), X(2), X(3), ...
+ for (size_t i = 0; i < size; ++i) {
+ std::construct_at(source + i, static_cast<int>(i + 1));
+ }
+
+ auto ret = std::uninitialized_move_n(policy, Iter1(source), size, Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), std::pair<Iter1, Iter2>);
+ assert(ret.first == Iter1(source + size));
+ assert(ret.second == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ assert(source[i].value() == 0); // Source is X(0), X(0), X(0), ...
+ assert(dest[i].value() == static_cast<int>(i + 1)); // Dest is Y(1), Y(2), Y(3), ...
+ }
+
+ std::destroy_n(source, size); // Clear source for the next iteration
+ std::destroy_n(dest, size); // Clear dest for the next iteration
+ });
+
+ alloc_src.deallocate(source, n);
+ alloc_dst.deallocate(dest, n);
+ }
+};
+
+template <class Iter1, class Iter2>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr size_t n = 1073;
+ std::allocator<int> alloc;
+ int* source = alloc.allocate(n);
+ int* dest = alloc.allocate(n);
+
+ std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+
+ runway_sample(n + 1, [&](size_t size) {
+ auto ret = std::uninitialized_move_n(policy, Iter1(source), size, Iter2(dest));
+ ASSERT_SAME_TYPE(decltype(ret), std::pair<Iter1, Iter2>);
+ assert(ret.first == Iter1(source + size));
+ assert(ret.second == Iter2(dest + size));
+
+ for (size_t i = 0; i < size; ++i) {
+ // For trivial types, move is the same as copy
+ assert(dest[i] == static_cast<int>(i + 1));
+ assert(dest[i] == source[i]); // Both source and dest are 1 2 3 4 ...
+ }
+
+ std::fill(dest, dest + size, 0); // Clear dest for the next iteration
+ });
+
+ alloc.deallocate(source, n);
+ alloc.deallocate(dest, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::cpp17_input_iterator_list<Src*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<Dst*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestCustomTypes, Iter>::template apply>{});
+ }});
+ types::for_each(types::cpp17_input_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<TestInt, Iter>::template apply>{});
+ }});
+ return 0;
+}
>From 2e9f4788985eb25a204cf0bb8a4de3781e1c46c0 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 13 Sep 2026 12:41:13 +0100
Subject: [PATCH 2/7] Missed includes
---
libcxx/include/__memory/pstl.h | 1 +
libcxx/include/__pstl/cpu_algos/for_each.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
index 2b4011bd158cb8..6c1fd6f10ee230 100644
--- a/libcxx/include/__memory/pstl.h
+++ b/libcxx/include/__memory/pstl.h
@@ -30,6 +30,7 @@ _LIBCPP_PUSH_MACROS
# include <__type_traits/remove_cvref.h>
# include <__utility/forward.h>
# include <__utility/move.h>
+# include <__utility/pair.h>
_LIBCPP_BEGIN_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/cpu_algos/for_each.h b/libcxx/include/__pstl/cpu_algos/for_each.h
index bc29fa4e3da44c..91ec0dfdc59f3a 100644
--- a/libcxx/include/__pstl/cpu_algos/for_each.h
+++ b/libcxx/include/__pstl/cpu_algos/for_each.h
@@ -18,6 +18,7 @@
#include <__pstl/cpu_algos/cpu_traits.h>
#include <__type_traits/is_execution_policy.h>
#include <__utility/empty.h>
+#include <__utility/move.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
>From a29ca54861d5674bc97d9ca22e92b18e5360e346 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 13 Sep 2026 13:24:52 +0100
Subject: [PATCH 3/7] ADL-proofing
---
libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h b/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
index 41ee8bc3b7a534..861868c6ae6fab 100644
--- a/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
+++ b/libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
@@ -42,7 +42,7 @@ struct __cpu_parallel_uninitialized_copy {
if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
__has_random_access_iterator_category_or_concept<_InputIterator>::value &&
__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
- auto __res = __parallel_for_each_iter_pair<_Backend>(
+ auto __res = __pstl::__parallel_for_each_iter_pair<_Backend>(
__first,
__last,
__result,
@@ -68,7 +68,7 @@ struct __cpu_parallel_uninitialized_move {
if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
__has_random_access_iterator_category_or_concept<_InputIterator>::value &&
__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
- auto __res = __parallel_for_each_iter_pair<_Backend>(
+ auto __res = __pstl::__parallel_for_each_iter_pair<_Backend>(
__first,
__last,
__result,
>From 9da11b1d9d2deaf2af77da9cd05066dddf6d3578 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sun, 13 Sep 2026 16:31:18 +0100
Subject: [PATCH 4/7] Can't use std::construct_at in the test - it appeared in
C++20
---
.../uninitialized.copy/pstl.uninitialized_copy.pass.cpp | 2 +-
.../uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp | 2 +-
.../uninitialized.move/pstl.uninitialized_move.pass.cpp | 2 +-
.../uninitialized.move/pstl.uninitialized_move_n.pass.cpp | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
index d2f8c4cd66a476..b9681744ca6260 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
@@ -74,7 +74,7 @@ struct TestCustomTypes {
// Source is Src(1), Src(2), Src(3), ...
for (size_t i = 0; i < n; ++i) {
- std::construct_at(source + i, static_cast<int>(i + 1));
+ std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
}
// Copy-construct different ranges of Y [0..size) from the source X array
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
index 4a373fc1924fd6..5bc8cdf94a08cb 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
@@ -75,7 +75,7 @@ struct TestCustomTypes {
// Source is Src(1), Src(2), Src(3), ...
for (size_t i = 0; i < n; ++i) {
- std::construct_at(source + i, static_cast<int>(i + 1));
+ std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
}
// Copy-construct different ranges of Y [0..size) from the source X array
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
index f90f06654835f5..5502e61ec429ef 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
@@ -78,7 +78,7 @@ struct TestCustomTypes {
runway_sample(n + 1, [&](size_t size) {
// Construct the source array for this iteration: X(1), X(2), X(3), ...
for (size_t i = 0; i < size; ++i) {
- std::construct_at(source + i, static_cast<int>(i + 1));
+ std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
}
auto ret = std::uninitialized_move(policy, Iter1(source), Iter1(source + size), Iter2(dest));
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
index df7eed353881c6..2143d05aaa840e 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -79,7 +79,7 @@ struct TestCustomTypes {
runway_sample(n + 1, [&](size_t size) {
// Construct the source array for this iteration: X(1), X(2), X(3), ...
for (size_t i = 0; i < size; ++i) {
- std::construct_at(source + i, static_cast<int>(i + 1));
+ std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
}
auto ret = std::uninitialized_move_n(policy, Iter1(source), size, Iter2(dest));
>From 0c4f9474e93103f675b040893911bf2d3fe47524 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 15 Sep 2026 16:45:28 +0100
Subject: [PATCH 5/7] Updated comments
---
libcxx/docs/ReleaseNotes/24.rst | 1 +
libcxx/docs/Status/Cxx17Papers.csv | 2 +-
.../uninitialized.move/pstl.uninitialized_move_n.pass.cpp | 6 +++---
3 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index a7b9e8731d555d..046f79a95e1242 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -52,6 +52,7 @@ Implemented Papers
- P2830R10: Standardized Constexpr Type Ordering (`Github <https://llvm.org/PR216616>`__)
- P3778R0: Fix for ``type_order`` template definition (`Github <https://llvm.org/PR216616>`__)
- P4140R0: Proposed resolution for US70-126: allow incomplete types in type_order (`Github <https://llvm.org/PR216616>`__)
+- P0040R3: Extending memory management tools (`Github <https://github.com/llvm/llvm-project/issues/103640>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv
index 378071f8b60a30..9d2ee92bc173fa 100644
--- a/libcxx/docs/Status/Cxx17Papers.csv
+++ b/libcxx/docs/Status/Cxx17Papers.csv
@@ -42,7 +42,7 @@
"`P0077R2 <https://wg21.link/P0077R2>`__","``is_callable``\ , the missing INVOKE related trait","2016-02 (Jacksonville)","|Complete|","3.9","`#103638 <https://github.com/llvm/llvm-project/issues/103638>`__",""
"","","","","","",""
"`P0032R3 <https://wg21.link/P0032R3>`__","Homogeneous interface for variant, any and optional","2016-06 (Oulu)","|Complete|","4","`#103639 <https://github.com/llvm/llvm-project/issues/103639>`__",""
-"`P0040R3 <https://wg21.link/P0040R3>`__","Extending memory management tools","2016-06 (Oulu)","|Partial|","","`#103640 <https://github.com/llvm/llvm-project/issues/103640>`__","The serial overloads have been available since v4, but the ``ExecutionPolicy`` overloads have not been implemented."
+"`P0040R3 <https://wg21.link/P0040R3>`__","Extending memory management tools","2016-06 (Oulu)","|Complete|","24","`#103640 <https://github.com/llvm/llvm-project/issues/103640>`__","The serial overloads have been available since v4."
"`P0063R3 <https://wg21.link/P0063R3>`__","C++17 should refer to C11 instead of C99","2016-06 (Oulu)","|Complete|","7","`#103642 <https://github.com/llvm/llvm-project/issues/103642>`__",""
"`P0067R3 <https://wg21.link/P0067R3>`__","Elementary string conversions","2016-06 (Oulu)","|Nothing To Do|","n/a","`#103643 <https://github.com/llvm/llvm-project/issues/103643>`__","Resolved by `P0067R5 <https://wg21.link/P0067R5>`__"
"`P0083R3 <https://wg21.link/P0083R3>`__","Splicing Maps and Sets","2016-06 (Oulu)","|Complete|","8","`#103645 <https://github.com/llvm/llvm-project/issues/103645>`__",""
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
index 2143d05aaa840e..2feaaa37bcaa2c 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -16,9 +16,9 @@
// class InputIterator,
// class Size,
// class ForwardIterator>
-// ForwardIterator uninitialized_move_n(ExecutionPolicy&& exec,
-// InputIterator first, Size n,
-// ForwardIterator result);
+// pair<InputIterator, ForwardIterator> uninitialized_move_n(ExecutionPolicy&& exec,
+// InputIterator first, Size n,
+// ForwardIterator result);
#include <atomic>
#include <algorithm>
>From 7f4c1935b2a8a49d8a0525b8366c3876b1e226e5 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 16 Sep 2026 20:36:44 +0100
Subject: [PATCH 6/7] Simplified the tests a bit
---
.../pstl.uninitialized_copy.pass.cpp | 35 ++++++++---------
.../pstl.uninitialized_copy_n.pass.cpp | 35 ++++++++---------
.../pstl.uninitialized_move.pass.cpp | 35 ++++++++---------
.../pstl.uninitialized_move_n.pass.cpp | 39 +++++++++----------
4 files changed, 70 insertions(+), 74 deletions(-)
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
index b9681744ca6260..b4dd55f1d29262 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
@@ -19,7 +19,7 @@
// InputIterator first, InputIterator last,
// ForwardIterator result);
-#include <atomic>
+#include <array>
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -41,13 +41,14 @@ static_assert(!sfinae_test_uninitialized_copy<std::execution::parallel_policy, i
// Source type
struct Src {
- Src(int v) : value_(v) {}
+ Src() = default;
Src(const Src&) = delete;
Src& operator=(const Src&) = delete;
int value() const { return value_; }
+ void set_value(int v) { value_ = v; }
private:
- int value_;
+ int value_ = 0;
};
// Destination type
@@ -67,19 +68,19 @@ struct TestCustomTypes {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
constexpr size_t n = 1073;
- std::allocator<Src> alloc_src;
- std::allocator<Dst> alloc_dst;
- Src* source = alloc_src.allocate(n);
- Dst* dest = alloc_dst.allocate(n);
// Source is Src(1), Src(2), Src(3), ...
+ std::array<Src, n> source;
for (size_t i = 0; i < n; ++i) {
- std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
+ source[i].set_value(static_cast<int>(i + 1));
}
+ std::allocator<Dst> alloc_dst;
+ Dst* dest = alloc_dst.allocate(n);
+
// Copy-construct different ranges of Y [0..size) from the source X array
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_copy(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ auto ret = std::uninitialized_copy(policy, Iter1(source.data()), Iter1(source.data() + size), Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -90,8 +91,6 @@ struct TestCustomTypes {
std::destroy_n(dest, size); // Clear dest for the next iteration
});
- std::destroy_n(source, n);
- alloc_src.deallocate(source, n);
alloc_dst.deallocate(dest, n);
}
};
@@ -102,15 +101,16 @@ struct TestInt {
void operator()(ExecutionPolicy&& policy) {
{
constexpr size_t n = 1073;
- std::allocator<int> alloc;
- int* source = alloc.allocate(n);
- int* dest = alloc.allocate(n);
- std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
- std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+ std::array<int, n> source;
+ std::iota(source.begin(), source.end(), 1); // Source is 1 2 3 4 ...
+
+ std::allocator<int> alloc;
+ int* dest = alloc.allocate(n);
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_copy(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ auto ret = std::uninitialized_copy(policy, Iter1(source.data()), Iter1(source.data() + size), Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -121,7 +121,6 @@ struct TestInt {
std::fill(dest, dest + size, 0); // Clear dest for the next iteration
});
- alloc.deallocate(source, n);
alloc.deallocate(dest, n);
}
}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
index 5bc8cdf94a08cb..c03be9445e6e39 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
@@ -20,7 +20,7 @@
// InputIterator first, Size n,
// ForwardIterator result);
-#include <atomic>
+#include <array>
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -42,13 +42,14 @@ static_assert(!sfinae_test_uninitialized_copy_n<std::execution::parallel_policy,
// Source type
struct Src {
- Src(int v) : value_(v) {}
+ Src() = default;
Src(const Src&) = delete;
Src& operator=(const Src&) = delete;
int value() const { return value_; }
+ void set_value(int v) { value_ = v; }
private:
- int value_;
+ int value_ = 0;
};
// Destination type
@@ -68,19 +69,19 @@ struct TestCustomTypes {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
constexpr size_t n = 1073;
- std::allocator<Src> alloc_src;
- std::allocator<Dst> alloc_dst;
- Src* source = alloc_src.allocate(n);
- Dst* dest = alloc_dst.allocate(n);
// Source is Src(1), Src(2), Src(3), ...
+ std::array<Src, n> source;
for (size_t i = 0; i < n; ++i) {
- std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
+ source[i].set_value(static_cast<int>(i + 1));
}
+ std::allocator<Dst> alloc_dst;
+ Dst* dest = alloc_dst.allocate(n);
+
// Copy-construct different ranges of Y [0..size) from the source X array
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_copy_n(policy, Iter1(source), size, Iter2(dest));
+ auto ret = std::uninitialized_copy_n(policy, Iter1(source.data()), size, Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -91,8 +92,6 @@ struct TestCustomTypes {
std::destroy_n(dest, size); // Clear dest for the next iteration
});
- std::destroy_n(source, n);
- alloc_src.deallocate(source, n);
alloc_dst.deallocate(dest, n);
}
};
@@ -103,15 +102,16 @@ struct TestInt {
void operator()(ExecutionPolicy&& policy) {
{
constexpr size_t n = 1073;
- std::allocator<int> alloc;
- int* source = alloc.allocate(n);
- int* dest = alloc.allocate(n);
- std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
- std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+ std::array<int, n> source;
+ std::iota(source.begin(), source.end(), 1); // Source is 1 2 3 4 ...
+
+ std::allocator<int> alloc;
+ int* dest = alloc.allocate(n);
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_copy_n(policy, Iter1(source), size, Iter2(dest));
+ auto ret = std::uninitialized_copy_n(policy, Iter1(source.data()), size, Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -122,7 +122,6 @@ struct TestInt {
std::fill(dest, dest + size, 0); // Clear dest for the next iteration
});
- alloc.deallocate(source, n);
alloc.deallocate(dest, n);
}
}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
index 5502e61ec429ef..5c3bf9ec7e2076 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
@@ -19,7 +19,7 @@
// InputIterator first, InputIterator last,
// ForwardIterator result);
-#include <atomic>
+#include <array>
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -41,13 +41,14 @@ static_assert(!sfinae_test_uninitialized_move<std::execution::parallel_policy, i
// Source type
struct Src {
- Src(int v) : value_(v) {}
+ Src() = default;
Src(const Src&) = delete;
Src& operator=(const Src&) = delete;
int value() const { return value_; }
+ void set_value(int v) { value_ = v; }
private:
- int value_;
+ int value_ = 0;
friend struct Dst;
};
@@ -69,19 +70,19 @@ struct TestCustomTypes {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
constexpr size_t n = 1073;
- std::allocator<Src> alloc_src;
+ std::array<Src, n> source;
+
std::allocator<Dst> alloc_dst;
- Src* source = alloc_src.allocate(n);
- Dst* dest = alloc_dst.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
// Move-construct different ranges of Y [0..size) from the source X array
runway_sample(n + 1, [&](size_t size) {
// Construct the source array for this iteration: X(1), X(2), X(3), ...
for (size_t i = 0; i < size; ++i) {
- std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
+ source[i].set_value(static_cast<int>(i + 1));
}
- auto ret = std::uninitialized_move(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ auto ret = std::uninitialized_move(policy, Iter1(source.data()), Iter1(source.data() + size), Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -90,11 +91,9 @@ struct TestCustomTypes {
assert(dest[i].value() == static_cast<int>(i + 1)); // Dest is Y(1), Y(2), Y(3), ...
}
- std::destroy_n(source, size); // Clear source for the next iteration
- std::destroy_n(dest, size); // Clear dest for the next iteration
+ std::destroy_n(dest, size); // Clear dest for the next iteration
});
- alloc_src.deallocate(source, n);
alloc_dst.deallocate(dest, n);
}
};
@@ -105,15 +104,16 @@ struct TestInt {
void operator()(ExecutionPolicy&& policy) {
{
constexpr size_t n = 1073;
- std::allocator<int> alloc;
- int* source = alloc.allocate(n);
- int* dest = alloc.allocate(n);
- std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
- std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+ std::array<int, n> source;
+ std::iota(source.begin(), source.end(), 1); // Source is 1 2 3 4 ...
+
+ std::allocator<int> alloc;
+ int* dest = alloc.allocate(n);
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_move(policy, Iter1(source), Iter1(source + size), Iter2(dest));
+ auto ret = std::uninitialized_move(policy, Iter1(source.data()), Iter1(source.data() + size), Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), Iter2);
assert(ret == Iter2(dest + size));
@@ -126,7 +126,6 @@ struct TestInt {
std::fill(dest, dest + size, 0); // Clear dest for the next iteration
});
- alloc.deallocate(source, n);
alloc.deallocate(dest, n);
}
}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
index 2feaaa37bcaa2c..efb21e59cd8873 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -20,7 +20,7 @@
// InputIterator first, Size n,
// ForwardIterator result);
-#include <atomic>
+#include <array>
#include <algorithm>
#include <cassert>
#include <cstring>
@@ -42,13 +42,14 @@ static_assert(!sfinae_test_uninitialized_move_n<std::execution::parallel_policy,
// Source type
struct Src {
- Src(int v) : value_(v) {}
+ Src() = default;
Src(const Src&) = delete;
Src& operator=(const Src&) = delete;
int value() const { return value_; }
+ void set_value(int v) { value_ = v; }
private:
- int value_;
+ int value_ = 0;
friend struct Dst;
};
@@ -70,21 +71,21 @@ struct TestCustomTypes {
template <class ExecutionPolicy>
void operator()(ExecutionPolicy&& policy) {
constexpr size_t n = 1073;
- std::allocator<Src> alloc_src;
+ std::array<Src, n> source;
+
std::allocator<Dst> alloc_dst;
- Src* source = alloc_src.allocate(n);
- Dst* dest = alloc_dst.allocate(n);
+ Dst* dest = alloc_dst.allocate(n);
// Move-construct different ranges of Y [0..size) from the source X array
runway_sample(n + 1, [&](size_t size) {
// Construct the source array for this iteration: X(1), X(2), X(3), ...
for (size_t i = 0; i < size; ++i) {
- std::allocator_traits<std::allocator<Src>>::construct(alloc_src, source + i, static_cast<int>(i + 1));
+ source[i].set_value(static_cast<int>(i + 1));
}
- auto ret = std::uninitialized_move_n(policy, Iter1(source), size, Iter2(dest));
+ auto ret = std::uninitialized_move_n(policy, Iter1(source.begin()), size, Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), std::pair<Iter1, Iter2>);
- assert(ret.first == Iter1(source + size));
+ assert(ret.first == Iter1(source.data() + size));
assert(ret.second == Iter2(dest + size));
for (size_t i = 0; i < size; ++i) {
@@ -92,11 +93,9 @@ struct TestCustomTypes {
assert(dest[i].value() == static_cast<int>(i + 1)); // Dest is Y(1), Y(2), Y(3), ...
}
- std::destroy_n(source, size); // Clear source for the next iteration
- std::destroy_n(dest, size); // Clear dest for the next iteration
+ std::destroy_n(dest, size); // Clear dest for the next iteration
});
- alloc_src.deallocate(source, n);
alloc_dst.deallocate(dest, n);
}
};
@@ -107,17 +106,18 @@ struct TestInt {
void operator()(ExecutionPolicy&& policy) {
{
constexpr size_t n = 1073;
- std::allocator<int> alloc;
- int* source = alloc.allocate(n);
- int* dest = alloc.allocate(n);
- std::iota(source, source + n, 1); // Source is 1 2 3 4 ...
- std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
+ std::array<int, n> source;
+ std::iota(source.begin(), source.end(), 1); // Source is 1 2 3 4 ...
+
+ std::allocator<int> alloc;
+ int* dest = alloc.allocate(n);
+ std::fill(dest, dest + n, 0); // Dest is 0 0 0 0 ...
runway_sample(n + 1, [&](size_t size) {
- auto ret = std::uninitialized_move_n(policy, Iter1(source), size, Iter2(dest));
+ auto ret = std::uninitialized_move_n(policy, Iter1(source.data()), size, Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), std::pair<Iter1, Iter2>);
- assert(ret.first == Iter1(source + size));
+ assert(ret.first == Iter1(source.data() + size));
assert(ret.second == Iter2(dest + size));
for (size_t i = 0; i < size; ++i) {
@@ -129,7 +129,6 @@ struct TestInt {
std::fill(dest, dest + size, 0); // Clear dest for the next iteration
});
- alloc.deallocate(source, n);
alloc.deallocate(dest, n);
}
}
>From b7217caa5a009e5e8015fedf844e99b960b89b44 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 17 Sep 2026 12:00:22 +0100
Subject: [PATCH 7/7] Fixed the test
---
.../uninitialized.move/pstl.uninitialized_move_n.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
index efb21e59cd8873..9fdb551c459d01 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -83,7 +83,7 @@ struct TestCustomTypes {
source[i].set_value(static_cast<int>(i + 1));
}
- auto ret = std::uninitialized_move_n(policy, Iter1(source.begin()), size, Iter2(dest));
+ auto ret = std::uninitialized_move_n(policy, Iter1(source.data()), size, Iter2(dest));
ASSERT_SAME_TYPE(decltype(ret), std::pair<Iter1, Iter2>);
assert(ret.first == Iter1(source.data() + size));
assert(ret.second == Iter2(dest + size));
More information about the libcxx-commits
mailing list