[libcxx-commits] [libcxx] 551af79 - [libc++][pstl] Implementation of parallel std::uninitialized_(copy|move)(_n) based on parallel __for_each (#223229)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Sep 18 12:56:51 PDT 2026


Author: Michael G. Kazakov
Date: 2026-09-18T20:56:45+01:00
New Revision: 551af79c903169d0ee3ac35222a5af175424205d

URL: https://github.com/llvm/llvm-project/commit/551af79c903169d0ee3ac35222a5af175424205d
DIFF: https://github.com/llvm/llvm-project/commit/551af79c903169d0ee3ac35222a5af175424205d.diff

LOG: [libc++][pstl] Implementation of parallel std::uninitialized_(copy|move)(_n) based on parallel __for_each (#223229)

This PR adds an implementation of parallel versions of
`std::uninitialized_copy`, `std::uninitialized_copy_n`,
`std::uninitialized_move` and `std::uninitialized_move_n`.

Implementations are mostly one-liners under the hood, but require
dealing with iterator ranges, hence `std::uninitialized_copy` and
`std::uninitialized_move` are implemented in `cpu_algos`.
`std::uninitialized_copy_n` and `std::uninitialized_move_n` are
implemented in the `default` backend.

Fixes #103640
Fixes #134592
Fixes #134593
Part of #99938

Added: 
    libcxx/include/__pstl/cpu_algos/uninitialized_algorithms.h
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes/24.rst
    libcxx/docs/Status/Cxx17Papers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__memory/pstl.h
    libcxx/include/__pstl/backend_fwd.h
    libcxx/include/__pstl/backends/default.h
    libcxx/include/__pstl/backends/libdispatch.h
    libcxx/include/__pstl/backends/serial.h
    libcxx/include/__pstl/backends/std_thread.h
    libcxx/include/__pstl/cpu_algos/for_each.h
    libcxx/include/module.modulemap.in
    libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
    libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes/24.rst b/libcxx/docs/ReleaseNotes/24.rst
index 80c437e868bcb..c7678c5978006 100644
--- a/libcxx/docs/ReleaseNotes/24.rst
+++ b/libcxx/docs/ReleaseNotes/24.rst
@@ -53,6 +53,7 @@ Implemented Papers
 - 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>`__)
 - P3793R2: Better shifting (`Github <https://llvm.org/PR219389>`__) (no ``std::simd`` overloads)
+- 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 378071f8b60a3..9d2ee92bc173f 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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 99c3bd8b72aa8..caf90b0cbbafa 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -712,6 +712,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 c06f53bed7206..6c1fd6f10ee23 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
 
@@ -82,6 +83,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 +167,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 40a335ae1f2ad..570afef8a8a34 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 b5d7045bbb9b8..dd67d5fcc84e4 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_
diff erence<__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 b39691ee5a2bb..cab5577bbc545 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -52,6 +52,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>
@@ -440,6 +441,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 85c73b0f15db5..c779d583089d2 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 bc7fc020867f8..bdbddb9d36300 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 908f82f01e6c8..91ec0dfdc59f3 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
@@ -37,6 +38,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 0000000000000..861868c6ae6fa
--- /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 = __pstl::__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 = __pstl::__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 3e2b446780a43..29af487336365 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2476,6 +2476,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 f12d5bdcb3121..bbc669bfdc554 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 b85cc984d969d..f6aeaf68eb96b 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 0000000000000..b4dd55f1d2926
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy.pass.cpp
@@ -0,0 +1,144 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#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()                      = 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_ = 0;
+};
+
+// 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;
+
+    // Source is Src(1), Src(2), Src(3), ...
+    std::array<Src, n> source;
+    for (size_t i = 0; i < n; ++i) {
+      source[i].set_value(static_cast<int>(i + 1));
+    }
+
+    std::allocator<Dst> alloc_dst;
+    Dst* dest = alloc_dst.allocate(n);
+
+    // Copy-construct 
diff erent 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.data()), Iter1(source.data() + 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
+    });
+
+    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::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.data()), Iter1(source.data() + 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(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 0000000000000..c03be9445e6e3
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.copy/pstl.uninitialized_copy_n.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 Size,
+//           class ForwardIterator>
+//   ForwardIterator uninitialized_copy_n(ExecutionPolicy&& exec,
+//                                        InputIterator first, Size n,
+//                                        ForwardIterator result);
+
+#include <array>
+#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()                      = 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_ = 0;
+};
+
+// 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;
+
+    // Source is Src(1), Src(2), Src(3), ...
+    std::array<Src, n> source;
+    for (size_t i = 0; i < n; ++i) {
+      source[i].set_value(static_cast<int>(i + 1));
+    }
+
+    std::allocator<Dst> alloc_dst;
+    Dst* dest = alloc_dst.allocate(n);
+
+    // Copy-construct 
diff erent 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.data()), 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
+    });
+
+    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::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.data()), 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(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 0000000000000..5c3bf9ec7e207
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <array>
+#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()                      = 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_ = 0;
+  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::array<Src, n> source;
+
+    std::allocator<Dst> alloc_dst;
+    Dst* dest = alloc_dst.allocate(n);
+
+    // Move-construct 
diff erent 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) {
+        source[i].set_value(static_cast<int>(i + 1));
+      }
+
+      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));
+
+      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(dest, size); // Clear dest for the next iteration
+    });
+
+    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::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.data()), Iter1(source.data() + 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(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 0000000000000..9fdb551c459d0
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.move/pstl.uninitialized_move_n.pass.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+//   pair<InputIterator, ForwardIterator> uninitialized_move_n(ExecutionPolicy&& exec,
+//                                                             InputIterator first, Size n,
+//                                                             ForwardIterator result);
+
+#include <array>
+#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()                      = 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_ = 0;
+  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::array<Src, n> source;
+
+    std::allocator<Dst> alloc_dst;
+    Dst* dest = alloc_dst.allocate(n);
+
+    // Move-construct 
diff erent 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) {
+        source[i].set_value(static_cast<int>(i + 1));
+      }
+
+      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));
+
+      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(dest, size); // Clear dest for the next iteration
+    });
+
+    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::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.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));
+
+        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(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;
+}


        


More information about the libcxx-commits mailing list