[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel uninitialized_default_construct, uninitialized_value_construct and uninitialized_fill (PR #214580)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Aug 13 01:14:28 PDT 2026
https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/214580
>From 470a198912352f13e40be4f23a9d814636f0b715 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 6 Aug 2026 21:58:11 +0100
Subject: [PATCH 1/5] Added uninitialized_fill() and uninitialized_fill_n()
---
libcxx/include/__memory/pstl.h | 28 ++++++
libcxx/include/__pstl/backend_fwd.h | 12 +++
libcxx/include/__pstl/backends/default.h | 30 ++++++
.../pstl.iterator-requirements.verify.cpp | 7 ++
.../pstl.exception_handling.pass.cpp | 10 ++
.../pstl.uninitialized_fill_n.pass.cpp | 93 +++++++++++++++++++
.../pstl.uninitialized_fill.pass.cpp | 93 +++++++++++++++++++
7 files changed, 273 insertions(+)
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
index a4fc727f3bf72..40bbc070b3fed 100644
--- a/libcxx/include/__memory/pstl.h
+++ b/libcxx/include/__memory/pstl.h
@@ -55,6 +55,34 @@ _LIBCPP_HIDE_FROM_ABI void destroy_n(_ExecutionPolicy&& __policy, _ForwardIterat
__pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
}
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Tp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_fill(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_fill requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__uninitialized_fill, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), __value);
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Size,
+ class _Tp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_fill_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, const _Tp& __value) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_fill_n requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_fill_n, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n, __value);
+}
+
_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 c5a219fb59db2..e424127fad8ac 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -377,6 +377,18 @@ struct __destroy_n;
// optional<__empty>
// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_fill;
+// template <class _Policy, class _ForwardIterator, class _Tp>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp &__value) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_fill_n;
+// template <class _Policy, class _ForwardIterator, class _Size, class _Tp>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n, const _Tp &__value) const noexcept;
+
} // namespace __pstl
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 43bac357efd95..23c98fb0c94c9 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -89,6 +89,8 @@ namespace __pstl {
// - replace_if
// - generate
// - generate_n
+// - uninitialized_fill
+// - uninitialized_fill_n
//
// merge family
// ------------
@@ -492,6 +494,34 @@ struct __generate_n<__default_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __uninitialized_fill<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Tp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, const _Tp& __value) const noexcept {
+ using _ForEach = __dispatch<__for_each, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEach()(__policy, std::move(__first), std::move(__last), [&__value](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType(__value);
+ });
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_fill_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Size, class _Tp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n, const _Tp& __value) const noexcept {
+ using _ForEachN = __dispatch<__for_each_n, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEachN()(__policy, std::move(__first), __n, [&__value](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType(__value);
+ });
+ }
+};
+
//////////////////////////////////////////////////////////////
// stable_sort family
//////////////////////////////////////////////////////////////
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 2d510be58e164..35ac4d54b7dfd 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -277,4 +277,11 @@ void f(non_forward_iterator non_fwd,
(void)std::adjacent_difference(
pol, non_fwd, non_fwd, it, func); // expected-error@*:* {{static assertion failed: adjacent_difference}}
}
+
+ {
+ std::uninitialized_fill(
+ pol, non_fwd, non_fwd, val); // expected-error@*:* {{static assertion failed: uninitialized_fill}}
+ std::uninitialized_fill_n(
+ pol, non_fwd, n, val); // expected-error@*:* {{static assertion failed: uninitialized_fill_n}}
+ }
}
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 4998b02fd0650..164948aa104e5 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -454,6 +454,16 @@ int main(int, char**) {
(void)std::adjacent_difference(policy, std::move(first1), std::move(last1), std::move(dest), op);
});
}
+
+ {
+ // uninitialized_fill(first, last, val)
+ assert_non_throwing([=, &policy] {
+ std::uninitialized_fill(policy, std::move(first1), std::move(last1), val);
+ });
+
+ // uninitialized_fill_n(first, n, val)
+ assert_non_throwing([=, &policy] { std::uninitialized_fill_n(policy, std::move(first1), n, val); });
+ }
}
});
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
new file mode 100644
index 0000000000000..971434dad990b
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator,
+// class Size,
+// class T>
+// void uninitialized_fill_n(ExecutionPolicy&& exec, ForwardIterator first, Size n, const T& value);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#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_fill_n);
+
+static_assert(sfinae_test_uninitialized_fill_n<int, int*, int, int>);
+static_assert(!sfinae_test_uninitialized_fill_n<std::execution::parallel_policy, int*, int, int>);
+
+// Init payload to initialize each Counted object with a pointer to its dedicated counter
+struct CountedInit {
+ struct Counted* first_;
+ std::atomic_int* counters_;
+};
+
+// Each Counted object has a dedicated external atomic counter
+struct Counted {
+ std::atomic_int* counter_;
+ Counted(CountedInit init) : counter_(init.counters_ + (this - init.first_)) { counter_->fetch_add(1); }
+ Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); }
+ ~Counted() { counter_->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test uninitialized_fill_n() with a range of Counted objects.
+ // Ranges vary in size from 0 to 1073.
+ // Each object has its own counter.
+ std::atomic_int counters[1073];
+ std::fill_n(std::begin(counters), std::size(counters), 0); // initialize all counters to 0
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Construct the Counted object in range [0, size).
+ std::uninitialized_fill_n(policy, Iter(pool), size, CountedInit{pool, counters});
+ ASSERT_SAME_TYPE(
+ decltype(std::uninitialized_fill_n(policy, Iter(pool), size, CountedInit{pool, counters})), void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(Iter(pool), Iter(pool + size));
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
new file mode 100644
index 0000000000000..9c1812cb795d7
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator,
+// class T>
+// void uninitialized_fill(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last, const T& value);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <iterator>
+#include <limits>
+#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_fill);
+
+static_assert(sfinae_test_uninitialized_fill<int, int*, int*, int>);
+static_assert(!sfinae_test_uninitialized_fill<std::execution::parallel_policy, int*, int*, int>);
+
+// Init payload to initialize each Counted object with a pointer to its dedicated counter
+struct CountedInit {
+ struct Counted* first_;
+ std::atomic_int* counters_;
+};
+
+// Each Counted object has a dedicated external atomic counter
+struct Counted {
+ std::atomic_int* counter_;
+ Counted(CountedInit init) : counter_(init.counters_ + (this - init.first_)) { counter_->fetch_add(1); }
+ Counted(Counted const& other) : counter_(other.counter_) { counter_->fetch_add(1); }
+ ~Counted() { counter_->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ // Test uninitialized_fill() with a range of Counted objects.
+ // Ranges vary in size from 0 to 1073.
+ // Each object has its own counter.
+ std::atomic_int counters[1073];
+ std::fill_n(std::begin(counters), std::size(counters), 0); // initialize all counters to 0
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Construct the Counted object in range [0, size).
+ std::uninitialized_fill(policy, Iter(pool), Iter(pool + size), CountedInit{pool, counters});
+ ASSERT_SAME_TYPE(
+ decltype(std::uninitialized_fill(policy, Iter(pool), Iter(pool + size), CountedInit{pool, counters})),
+ void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(Iter(pool), Iter(pool + size));
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ return 0;
+}
>From 7f5d2949640360973286e503084efd3f95f015bb Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 8 Aug 2026 13:41:29 +0100
Subject: [PATCH 2/5] Added std::uninitialized_default_construct()
---
libcxx/include/__memory/pstl.h | 27 +++++
libcxx/include/__pstl/backend_fwd.h | 12 ++
libcxx/include/__pstl/backends/default.h | 30 +++++
.../pstl.iterator-requirements.verify.cpp | 4 +
.../pstl.exception_handling.pass.cpp | 8 ++
...l.uninitialized_default_construct.pass.cpp | 111 +++++++++++++++++
...uninitialized_default_construct_n.pass.cpp | 112 ++++++++++++++++++
7 files changed, 304 insertions(+)
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
index 40bbc070b3fed..0c60ed730f7a6 100644
--- a/libcxx/include/__memory/pstl.h
+++ b/libcxx/include/__memory/pstl.h
@@ -55,6 +55,33 @@ _LIBCPP_HIDE_FROM_ABI void destroy_n(_ExecutionPolicy&& __policy, _ForwardIterat
__pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
}
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_default_construct(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_default_construct requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_default_construct, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Size,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_default_construct_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(
+ _ForwardIterator, "uninitialized_default_construct_n requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_default_construct_n, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator,
class _Tp,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index e424127fad8ac..a44c5b74ee6b3 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -377,6 +377,18 @@ struct __destroy_n;
// optional<__empty>
// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_default_construct;
+// template <class _Policy, class _ForwardIterator>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_default_construct_n;
+// template <class _Policy, class _ForwardIterator, class _Size>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __uninitialized_fill;
// template <class _Policy, class _ForwardIterator, class _Tp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 23c98fb0c94c9..22ef018476776 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -89,6 +89,8 @@ namespace __pstl {
// - replace_if
// - generate
// - generate_n
+// - uninitialized_default_construct
+// - uninitialized_default_construct_n
// - uninitialized_fill
// - uninitialized_fill_n
//
@@ -494,6 +496,34 @@ struct __generate_n<__default_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __uninitialized_default_construct<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept {
+ using _ForEach = __dispatch<__for_each, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEach()(__policy, std::move(__first), std::move(__last), [](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType;
+ });
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_default_construct_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Size>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept {
+ using _ForEachN = __dispatch<__for_each_n, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEachN()(__policy, std::move(__first), __n, [](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType;
+ });
+ }
+};
+
template <class _ExecutionPolicy>
struct __uninitialized_fill<__default_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Tp>
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 35ac4d54b7dfd..a4d4cfb570fb4 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -279,6 +279,10 @@ void f(non_forward_iterator non_fwd,
}
{
+ std::uninitialized_default_construct(
+ pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_default_construct}}
+ std::uninitialized_default_construct_n(
+ pol, non_fwd, n); // expected-error@*:* {{static assertion failed: uninitialized_default_construct_n}}
std::uninitialized_fill(
pol, non_fwd, non_fwd, val); // expected-error@*:* {{static assertion failed: uninitialized_fill}}
std::uninitialized_fill_n(
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 164948aa104e5..ed0a501e4ca5d 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -456,6 +456,14 @@ int main(int, char**) {
}
{
+ // uninitialized_default_construct(first, last)
+ assert_non_throwing([=, &policy] {
+ std::uninitialized_default_construct(policy, std::move(first1), std::move(last1));
+ });
+
+ // uninitialized_default_construct_n(first, n)
+ assert_non_throwing([=, &policy] { std::uninitialized_default_construct_n(policy, std::move(first1), n); });
+
// uninitialized_fill(first, last, val)
assert_non_throwing([=, &policy] {
std::uninitialized_fill(policy, std::move(first1), std::move(last1), val);
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
new file mode 100644
index 0000000000000..68ebac86d2dcb
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator>
+// void uninitialized_default_construct(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#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_default_construct);
+
+static_assert(sfinae_test_uninitialized_default_construct<int, int*, int*>);
+static_assert(!sfinae_test_uninitialized_default_construct<std::execution::parallel_policy, int*, int*>);
+
+// Each Counted object has a dedicated external atomic counter.
+// To be able to connect with that counter, each Counted object knows that it is located inside a single pool.
+struct Counted {
+ static std::atomic_int counters[1073];
+ static Counted* pool;
+ std::atomic_int* counter;
+ Counted() : counter(counters + (this - pool)) { counter->fetch_add(1); }
+ Counted(Counted const& other) : counter(other.counter) { counter->fetch_add(1); }
+ ~Counted() { counter->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+std::atomic_int Counted::counters[1073];
+Counted* Counted::pool = nullptr;
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ std::atomic_int(&counters)[1073] = Counted::counters;
+ Counted*(&pool) = Counted::pool;
+
+ // initialize all Counted counters to 0
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Default-construct the Counted objects in range [0, size).
+ std::uninitialized_default_construct(policy, Iter(pool), Iter(pool + size));
+ ASSERT_SAME_TYPE(decltype(std::uninitialized_default_construct(policy, Iter(pool), Iter(pool + size))), void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(pool, pool + size);
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_default_construct(policy, Iter(data), Iter(last));
+ for (int i = 0; i != n; ++i) {
+ data[i] = -i;
+ assert(data[i] == -i);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
new file mode 100644
index 0000000000000..61409997ab422
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator,
+// class Size>
+// void uninitialized_default_construct_n(ExecutionPolicy&& exec, ForwardIterator first, Size n);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#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_default_construct_n);
+
+static_assert(sfinae_test_uninitialized_default_construct_n<int, int*, int>);
+static_assert(!sfinae_test_uninitialized_default_construct_n<std::execution::parallel_policy, int*, int>);
+
+// Each Counted object has a dedicated external atomic counter.
+// To be able to connect with that counter, each Counted object knows that it is located inside a single pool.
+struct Counted {
+ static std::atomic_int counters[1073];
+ static Counted* pool;
+ std::atomic_int* counter;
+ Counted() : counter(counters + (this - pool)) { counter->fetch_add(1); }
+ Counted(Counted const& other) : counter(other.counter) { counter->fetch_add(1); }
+ ~Counted() { counter->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+std::atomic_int Counted::counters[1073];
+Counted* Counted::pool = nullptr;
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ std::atomic_int(&counters)[1073] = Counted::counters;
+ Counted*(&pool) = Counted::pool;
+
+ // initialize all Counted counters to 0
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Default-construct the Counted objects in range [0, size).
+ std::uninitialized_default_construct_n(policy, Iter(pool), size);
+ ASSERT_SAME_TYPE(decltype(std::uninitialized_default_construct_n(policy, Iter(pool), size)), void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(pool, pool + size);
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_default_construct_n(policy, Iter(data), n);
+ for (int i = 0; i != n; ++i) {
+ data[i] = -i;
+ assert(data[i] == -i);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
+ return 0;
+}
>From 88b94e4fe7155096a9f2fa8d46d9a6eda14711ab Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 8 Aug 2026 14:17:46 +0100
Subject: [PATCH 3/5] Added std::uninitialized_value_construct()
---
libcxx/include/__memory/pstl.h | 26 ++++
libcxx/include/__pstl/backend_fwd.h | 12 ++
libcxx/include/__pstl/backends/default.h | 28 +++++
.../pstl.iterator-requirements.verify.cpp | 4 +
.../pstl.exception_handling.pass.cpp | 8 ++
...l.uninitialized_default_construct.pass.cpp | 2 +-
...uninitialized_default_construct_n.pass.cpp | 2 +-
...stl.uninitialized_value_construct.pass.cpp | 110 +++++++++++++++++
...l.uninitialized_value_construct_n.pass.cpp | 111 ++++++++++++++++++
9 files changed, 301 insertions(+), 2 deletions(-)
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct.pass.cpp
create mode 100644 libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct_n.pass.cpp
diff --git a/libcxx/include/__memory/pstl.h b/libcxx/include/__memory/pstl.h
index 0c60ed730f7a6..c06f53bed7206 100644
--- a/libcxx/include/__memory/pstl.h
+++ b/libcxx/include/__memory/pstl.h
@@ -82,6 +82,32 @@ uninitialized_default_construct_n(_ExecutionPolicy&& __policy, _ForwardIterator
__pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
}
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_value_construct(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_value_construct requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_value_construct, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Size,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI void
+uninitialized_value_construct_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "uninitialized_value_construct_n requires ForwardIterators");
+ using _Implementation =
+ __pstl::__dispatch<__pstl::__uninitialized_value_construct_n, __pstl::__current_configuration, _RawPolicy>;
+ __pstl::__handle_exception<_Implementation>(std::forward<_ExecutionPolicy>(__policy), std::move(__first), __n);
+}
+
template <class _ExecutionPolicy,
class _ForwardIterator,
class _Tp,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a44c5b74ee6b3..7f570cf865386 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -389,6 +389,18 @@ struct __uninitialized_default_construct_n;
// optional<__empty>
// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_value_construct;
+// template <class _Policy, class _ForwardIterator>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept;
+
+template <class _Backend, class _ExecutionPolicy>
+struct __uninitialized_value_construct_n;
+// template <class _Policy, class _ForwardIterator, class _Size>
+// optional<__empty>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept;
+
template <class _Backend, class _ExecutionPolicy>
struct __uninitialized_fill;
// template <class _Policy, class _ForwardIterator, class _Tp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 22ef018476776..e60e9116292fc 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -524,6 +524,34 @@ struct __uninitialized_default_construct_n<__default_backend_tag, _ExecutionPoli
}
};
+template <class _ExecutionPolicy>
+struct __uninitialized_value_construct<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last) const noexcept {
+ using _ForEach = __dispatch<__for_each, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEach()(__policy, std::move(__first), std::move(__last), [](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType();
+ });
+ }
+};
+
+template <class _ExecutionPolicy>
+struct __uninitialized_value_construct_n<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Size>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<__empty>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n) const noexcept {
+ using _ForEachN = __dispatch<__for_each_n, __current_configuration, _ExecutionPolicy>;
+ using _ValueType = __iterator_value_type<_ForwardIterator>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _ForEachN()(__policy, std::move(__first), __n, [](_Ref __element) {
+ ::new (static_cast<void*>(std::addressof(__element))) _ValueType();
+ });
+ }
+};
+
template <class _ExecutionPolicy>
struct __uninitialized_fill<__default_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Tp>
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index a4d4cfb570fb4..52a46ed324642 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -283,6 +283,10 @@ void f(non_forward_iterator non_fwd,
pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_default_construct}}
std::uninitialized_default_construct_n(
pol, non_fwd, n); // expected-error@*:* {{static assertion failed: uninitialized_default_construct_n}}
+ std::uninitialized_value_construct(
+ pol, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: uninitialized_value_construct}}
+ std::uninitialized_value_construct_n(
+ pol, non_fwd, n); // expected-error@*:* {{static assertion failed: uninitialized_value_construct_n}}
std::uninitialized_fill(
pol, non_fwd, non_fwd, val); // expected-error@*:* {{static assertion failed: uninitialized_fill}}
std::uninitialized_fill_n(
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index ed0a501e4ca5d..6a983ae6cf4b5 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -464,6 +464,14 @@ int main(int, char**) {
// uninitialized_default_construct_n(first, n)
assert_non_throwing([=, &policy] { std::uninitialized_default_construct_n(policy, std::move(first1), n); });
+ // uninitialized_value_construct(first, last)
+ assert_non_throwing([=, &policy] {
+ std::uninitialized_value_construct(policy, std::move(first1), std::move(last1));
+ });
+
+ // uninitialized_value_construct_n(first, n)
+ assert_non_throwing([=, &policy] { std::uninitialized_value_construct_n(policy, std::move(first1), n); });
+
// uninitialized_fill(first, last, val)
assert_non_throwing([=, &policy] {
std::uninitialized_fill(policy, std::move(first1), std::move(last1), val);
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
index 68ebac86d2dcb..ec4837d131a6f 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct.pass.cpp
@@ -53,7 +53,7 @@ struct TestCounted {
void operator()(ExecutionPolicy&& policy) {
{
std::atomic_int(&counters)[1073] = Counted::counters;
- Counted*(&pool) = Counted::pool;
+ Counted*& pool = Counted::pool;
// initialize all Counted counters to 0
std::fill_n(std::begin(counters), std::size(counters), 0);
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
index 61409997ab422..875e6a9907fe5 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.default/pstl.uninitialized_default_construct_n.pass.cpp
@@ -54,7 +54,7 @@ struct TestCounted {
void operator()(ExecutionPolicy&& policy) {
{
std::atomic_int(&counters)[1073] = Counted::counters;
- Counted*(&pool) = Counted::pool;
+ Counted*& pool = Counted::pool;
// initialize all Counted counters to 0
std::fill_n(std::begin(counters), std::size(counters), 0);
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct.pass.cpp
new file mode 100644
index 0000000000000..6b9e77bc48067
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct.pass.cpp
@@ -0,0 +1,110 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator>
+// void uninitialized_value_construct(ExecutionPolicy&& exec, ForwardIterator first, ForwardIterator last);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#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_value_construct);
+
+static_assert(sfinae_test_uninitialized_value_construct<int, int*, int*>);
+static_assert(!sfinae_test_uninitialized_value_construct<std::execution::parallel_policy, int*, int*>);
+
+// Each Counted object has a dedicated external atomic counter.
+// To be able to connect with that counter, each Counted object knows that it is located inside a single pool.
+struct Counted {
+ static std::atomic_int counters[1073];
+ static Counted* pool;
+ std::atomic_int* counter;
+ Counted() : counter(counters + (this - pool)) { counter->fetch_add(1); }
+ Counted(Counted const& other) : counter(other.counter) { counter->fetch_add(1); }
+ ~Counted() { counter->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+std::atomic_int Counted::counters[1073];
+Counted* Counted::pool = nullptr;
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ std::atomic_int(&counters)[1073] = Counted::counters;
+ Counted*& pool = Counted::pool;
+
+ // initialize all Counted counters to 0
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Default-construct the Counted objects in range [0, size).
+ std::uninitialized_value_construct(policy, Iter(pool), Iter(pool + size));
+ ASSERT_SAME_TYPE(decltype(std::uninitialized_value_construct(policy, Iter(pool), Iter(pool + size))), void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(pool, pool + size);
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_value_construct(policy, Iter(data), Iter(last));
+ for (int i = 0; i != n; ++i) {
+ assert(data[i] == 0);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct_n.pass.cpp
new file mode 100644
index 0000000000000..eb93631c45446
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.construct.value/pstl.uninitialized_value_construct_n.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class ExecutionPolicy,
+// class ForwardIterator,
+// class Size>
+// void uninitialized_value_construct_n(ExecutionPolicy&& exec, ForwardIterator first, Size n);
+
+#include <atomic>
+#include <algorithm>
+#include <cassert>
+#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_value_construct_n);
+
+static_assert(sfinae_test_uninitialized_value_construct_n<int, int*, int>);
+static_assert(!sfinae_test_uninitialized_value_construct_n<std::execution::parallel_policy, int*, int>);
+
+// Each Counted object has a dedicated external atomic counter.
+// To be able to connect with that counter, each Counted object knows that it is located inside a single pool.
+struct Counted {
+ static std::atomic_int counters[1073];
+ static Counted* pool;
+ std::atomic_int* counter;
+ Counted() : counter(counters + (this - pool)) { counter->fetch_add(1); }
+ Counted(Counted const& other) : counter(other.counter) { counter->fetch_add(1); }
+ ~Counted() { counter->fetch_sub(1); }
+ friend void operator&(Counted) = delete;
+};
+std::atomic_int Counted::counters[1073];
+Counted* Counted::pool = nullptr;
+
+template <class Iter>
+struct TestCounted {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ std::atomic_int(&counters)[1073] = Counted::counters;
+ Counted*& pool = Counted::pool;
+
+ // initialize all Counted counters to 0
+ std::fill_n(std::begin(counters), std::size(counters), 0);
+
+ // Allocate memory for the Counted objects
+ using Alloc = std::allocator<Counted>;
+ Alloc alloc;
+ pool = std::allocator_traits<Alloc>::allocate(alloc, std::size(counters));
+
+ runway_sample(std::size(counters) + 1, [&](size_t size) {
+ // Default-construct the Counted objects in range [0, size).
+ std::uninitialized_value_construct_n(policy, Iter(pool), size);
+ ASSERT_SAME_TYPE(decltype(std::uninitialized_value_construct_n(policy, Iter(pool), size)), void);
+
+ // Verify that inside this range the counters are all 1 and outside the range they are all 0.
+ assert(std::all_of(std::begin(counters), std::begin(counters) + size, [](auto& x) { return x == 1; }));
+ assert(std::all_of(std::begin(counters) + size, std::end(counters), [](auto& x) { return x == 0; }));
+
+ // Destroy the constructed objects and verify that all counters are 0.
+ std::destroy(pool, pool + size);
+ assert(std::all_of(std::begin(counters), std::end(counters), [](auto& x) { return x == 0; }));
+ });
+
+ std::allocator_traits<Alloc>::deallocate(alloc, pool, std::size(counters));
+ }
+ }
+};
+
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_value_construct_n(policy, Iter(data), n);
+ for (int i = 0; i != n; ++i) {
+ assert(data[i] == 0);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
+ return 0;
+}
>From 6371df01ca8812b6efaafa84917b4ecf634d2aae Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 8 Aug 2026 14:23:35 +0100
Subject: [PATCH 4/5] Added tests with plain Ints into uninitialized_fill and
uninitialized_fill_n tests
---
.../pstl.uninitialized_fill_n.pass.cpp | 22 +++++++++++++++++++
.../pstl.uninitialized_fill.pass.cpp | 22 +++++++++++++++++++
2 files changed, 44 insertions(+)
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
index 971434dad990b..1dc64cddb1d56 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill.n/pstl.uninitialized_fill_n.pass.cpp
@@ -87,7 +87,29 @@ struct TestCounted {
}
};
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_fill_n(policy, Iter(data), n, 42);
+ for (int i = 0; i != n; ++i) {
+ assert(data[i] == 42);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
int main(int, char**) {
types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
return 0;
}
diff --git a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
index 9c1812cb795d7..102407504bcca 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/uninitialized.fill/pstl.uninitialized_fill.pass.cpp
@@ -87,7 +87,29 @@ struct TestCounted {
}
};
+template <class Iter>
+struct TestInt {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ constexpr int n = 1073;
+ std::allocator<int> alloc;
+ int* data = alloc.allocate(n);
+ int* last = data + n;
+
+ std::uninitialized_fill(policy, Iter(data), Iter(last), 42);
+ for (int i = 0; i != n; ++i) {
+ assert(data[i] == 42);
+ }
+
+ std::destroy(data, last);
+ alloc.deallocate(data, n);
+ }
+ }
+};
+
int main(int, char**) {
types::for_each(types::forward_iterator_list<Counted*>{}, TestIteratorWithPolicies<TestCounted>{});
+ types::for_each(types::forward_iterator_list<int*>{}, TestIteratorWithPolicies<TestInt>{});
return 0;
}
>From 46d385bdb9085a70e6ec37d8aaa0b097fb676d0a Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 8 Aug 2026 14:24:56 +0100
Subject: [PATCH 5/5] Updated the comment about the for_each family
---
libcxx/include/__pstl/backends/default.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index e60e9116292fc..1163409d31774 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -91,6 +91,8 @@ namespace __pstl {
// - generate_n
// - uninitialized_default_construct
// - uninitialized_default_construct_n
+// - uninitialized_value_construct
+// - uninitialized_value_construct_n
// - uninitialized_fill
// - uninitialized_fill_n
//
More information about the libcxx-commits
mailing list