summaryrefslogtreecommitdiff
path: root/libcxx
diff options
context:
space:
mode:
authorNikolas Klauser <nikolasklauser@berlin.de>2023-05-15 07:07:45 -0700
committerNikolas Klauser <nikolasklauser@berlin.de>2023-05-15 09:57:20 -0700
commit6851d078c54eb091235cf8e2916eefc7882278dd (patch)
tree779860b10d68c6552c6dc5a8026343cf84fdb551 /libcxx
parentcc7dc90481d93734a56098470879189cb2e2c127 (diff)
downloadllvm-6851d078c54eb091235cf8e2916eefc7882278dd.tar.gz
[libc++][PSTL] Implement std::transform
Reviewed By: ldionne, #libc Spies: libcxx-commits Differential Revision: https://reviews.llvm.org/D149615
Diffstat (limited to 'libcxx')
-rw-r--r--libcxx/include/CMakeLists.txt2
-rw-r--r--libcxx/include/__algorithm/pstl_backend.h10
-rw-r--r--libcxx/include/__algorithm/pstl_backends/cpu_backend.h1
-rw-r--r--libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h132
-rw-r--r--libcxx/include/__algorithm/pstl_transform.h66
-rw-r--r--libcxx/include/__pstl/internal/glue_algorithm_defs.h23
-rw-r--r--libcxx/include/__pstl/internal/glue_algorithm_impl.h21
-rw-r--r--libcxx/include/algorithm1
-rw-r--r--libcxx/include/module.modulemap.in3
-rw-r--r--libcxx/test/libcxx/private_headers.verify.cpp1
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp79
-rw-r--r--libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp65
-rw-r--r--libcxx/test/support/type_algorithms.h28
13 files changed, 388 insertions, 44 deletions
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 4dd363de4d17..ead9693b5f7d 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -78,10 +78,12 @@ set(files
__algorithm/pstl_backends/cpu_backends/find_if.h
__algorithm/pstl_backends/cpu_backends/for_each.h
__algorithm/pstl_backends/cpu_backends/serial.h
+ __algorithm/pstl_backends/cpu_backends/transform.h
__algorithm/pstl_fill.h
__algorithm/pstl_find.h
__algorithm/pstl_for_each.h
__algorithm/pstl_frontend_dispatch.h
+ __algorithm/pstl_transform.h
__algorithm/push_heap.h
__algorithm/ranges_adjacent_find.h
__algorithm/ranges_all_of.h
diff --git a/libcxx/include/__algorithm/pstl_backend.h b/libcxx/include/__algorithm/pstl_backend.h
index 55f2dc4b9f4e..9ac8002a3d27 100644
--- a/libcxx/include/__algorithm/pstl_backend.h
+++ b/libcxx/include/__algorithm/pstl_backend.h
@@ -32,6 +32,16 @@ A PSTL parallel backend is a tag type to which the following functions are assoc
template <class _ExecutionPolicy, class _Iterator, class _Predicate>
_Iterator __pstl_find_if(_Backend, _Iterator __first, _Iterator __last, _Predicate __pred);
+ template <class _ExecutionPolicy, class _InIterator, class _OutIterator, class _UnaryOperation>
+ _OutIterator __pstl_transform(_InIterator __first, _InIterator __last, _OutIterator __result, _UnaryOperation __op);
+
+ template <class _ExecutionPolicy, class _InIterator1, class _InIterator2, class _OutIterator, class _BinaryOperation>
+ _OutIterator __pstl_transform(_InIterator1 __first1,
+ _InIterator1 __last1,
+ _InIterator2 __first2,
+ _OutIterator __result,
+ _BinaryOperation __op);
+
// TODO: Complete this list
The following functions are optional but can be provided. If provided, they are used by the corresponding
diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h
index 98e6eb76df4c..a15e7f8f1acd 100644
--- a/libcxx/include/__algorithm/pstl_backends/cpu_backend.h
+++ b/libcxx/include/__algorithm/pstl_backends/cpu_backend.h
@@ -27,5 +27,6 @@
#include <__algorithm/pstl_backends/cpu_backends/fill.h>
#include <__algorithm/pstl_backends/cpu_backends/find_if.h>
#include <__algorithm/pstl_backends/cpu_backends/for_each.h>
+#include <__algorithm/pstl_backends/cpu_backends/transform.h>
#endif // _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKEND_H
diff --git a/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h
new file mode 100644
index 000000000000..f1aac24607c2
--- /dev/null
+++ b/libcxx/include/__algorithm/pstl_backends/cpu_backends/transform.h
@@ -0,0 +1,132 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H
+#define _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H
+
+#include <__algorithm/pstl_backends/cpu_backends/backend.h>
+#include <__algorithm/transform.h>
+#include <__config>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/terminate_on_exception.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Iterator1, class _DifferenceType, class _Iterator2, class _Function>
+_LIBCPP_HIDE_FROM_ABI _Iterator2
+__simd_walk_2(_Iterator1 __first1, _DifferenceType __n, _Iterator2 __first2, _Function __f) noexcept {
+ _PSTL_PRAGMA_SIMD
+ for (_DifferenceType __i = 0; __i < __n; ++__i)
+ __f(__first1[__i], __first2[__i]);
+ return __first2 + __n;
+}
+
+template <class _ExecutionPolicy, class _ForwardIterator, class _ForwardOutIterator, class _UnaryOperation>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform(
+ __cpu_backend_tag,
+ _ForwardIterator __first,
+ _ForwardIterator __last,
+ _ForwardOutIterator __result,
+ _UnaryOperation __op) {
+ if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> &&
+ __is_cpp17_random_access_iterator<_ForwardIterator>::value &&
+ __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) {
+ std::__terminate_on_exception([&] {
+ std::__par_backend::__parallel_for(
+ __first, __last, [__op, __first, __result](_ForwardIterator __brick_first, _ForwardIterator __brick_last) {
+ return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>(
+ __cpu_backend_tag{}, __brick_first, __brick_last, __result + (__brick_first - __first), __op);
+ });
+ });
+ return __result + (__last - __first);
+ } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> &&
+ __is_cpp17_random_access_iterator<_ForwardIterator>::value &&
+ __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) {
+ return std::__simd_walk_2(
+ __first,
+ __last - __first,
+ __result,
+ [&](__iter_reference<_ForwardIterator> __in_value, __iter_reference<_ForwardOutIterator> __out_value) {
+ __out_value = __op(__in_value);
+ });
+ } else {
+ return std::transform(__first, __last, __result, __op);
+ }
+}
+
+template <class _Iterator1, class _DifferenceType, class _Iterator2, class _Iterator3, class _Function>
+_LIBCPP_HIDE_FROM_ABI _Iterator3 __simd_walk_3(
+ _Iterator1 __first1, _DifferenceType __n, _Iterator2 __first2, _Iterator3 __first3, _Function __f) noexcept {
+ _PSTL_PRAGMA_SIMD
+ for (_DifferenceType __i = 0; __i < __n; ++__i)
+ __f(__first1[__i], __first2[__i], __first3[__i]);
+ return __first3 + __n;
+}
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _ForwardOutIterator,
+ class _BinaryOperation,
+ enable_if_t<is_execution_policy_v<__remove_cvref_t<_ExecutionPolicy>>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator __pstl_transform(
+ __cpu_backend_tag,
+ _ForwardIterator1 __first1,
+ _ForwardIterator1 __last1,
+ _ForwardIterator2 __first2,
+ _ForwardOutIterator __result,
+ _BinaryOperation __op) {
+ if constexpr (__is_parallel_execution_policy_v<_ExecutionPolicy> &&
+ __is_cpp17_random_access_iterator<_ForwardIterator1>::value &&
+ __is_cpp17_random_access_iterator<_ForwardIterator2>::value &&
+ __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) {
+ std::__terminate_on_exception([&] {
+ std::__par_backend::__parallel_for(
+ __first1,
+ __last1,
+ [__op, __first1, __first2, __result](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) {
+ return std::__pstl_transform<__remove_parallel_policy_t<_ExecutionPolicy>>(
+ __cpu_backend_tag{},
+ __brick_first,
+ __brick_last,
+ __first2 + (__brick_first - __first1),
+ __result + (__brick_first - __first1),
+ __op);
+ });
+ });
+ return __result + (__last1 - __first1);
+ } else if constexpr (__is_unsequenced_execution_policy_v<_ExecutionPolicy> &&
+ __is_cpp17_random_access_iterator<_ForwardIterator1>::value &&
+ __is_cpp17_random_access_iterator<_ForwardIterator2>::value &&
+ __is_cpp17_random_access_iterator<_ForwardOutIterator>::value) {
+ return std::__simd_walk_3(
+ __first1,
+ __last1 - __first1,
+ __first2,
+ __result,
+ [&](__iter_reference<_ForwardIterator1> __in1,
+ __iter_reference<_ForwardIterator2> __in2,
+ __iter_reference<_ForwardOutIterator> __out_value) { __out_value = __op(__in1, __in2); });
+ } else {
+ return std::transform(__first1, __last1, __first2, __result, __op);
+ }
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
+
+#endif // _LIBCPP___ALGORITHM_PSTL_BACKENDS_CPU_BACKENDS_TRANSFORM_H
diff --git a/libcxx/include/__algorithm/pstl_transform.h b/libcxx/include/__algorithm/pstl_transform.h
new file mode 100644
index 000000000000..9d2d731eeb15
--- /dev/null
+++ b/libcxx/include/__algorithm/pstl_transform.h
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___ALGORITHM_PSTL_TRANSFORM_H
+#define _LIBCPP___ALGORITHM_PSTL_TRANSFORM_H
+
+#include <__algorithm/pstl_backend.h>
+#include <__config>
+#include <__iterator/iterator_traits.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/terminate_on_exception.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _ForwardOutIterator,
+ class _UnaryOperation,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform(
+ _ExecutionPolicy&&,
+ _ForwardIterator __first,
+ _ForwardIterator __last,
+ _ForwardOutIterator __result,
+ _UnaryOperation __op) {
+ using _Backend = typename __select_backend<_RawPolicy>::type;
+ return std::__pstl_transform<_RawPolicy>(
+ _Backend{}, std::move(__first), std::move(__last), std::move(__result), std::move(__op));
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator1,
+ class _ForwardIterator2,
+ class _ForwardOutIterator,
+ class _BinaryOperation,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform(
+ _ExecutionPolicy&&,
+ _ForwardIterator1 __first1,
+ _ForwardIterator1 __last1,
+ _ForwardIterator2 __first2,
+ _ForwardOutIterator __result,
+ _BinaryOperation __op) {
+ using _Backend = typename __select_backend<_RawPolicy>::type;
+ return std::__pstl_transform<_RawPolicy>(
+ _Backend{}, std::move(__first1), std::move(__last1), std::move(__first2), std::move(__result), std::move(__op));
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // !defined(_LIBCPP_HAS_NO_INCOMPLETE_PSTL) && _LIBCPP_STD_VER >= 17
+
+#endif // _LIBCPP___ALGORITHM_PSTL_TRANSFORM_H
diff --git a/libcxx/include/__pstl/internal/glue_algorithm_defs.h b/libcxx/include/__pstl/internal/glue_algorithm_defs.h
index 82bb3f508d5a..de4501e56b2c 100644
--- a/libcxx/include/__pstl/internal/glue_algorithm_defs.h
+++ b/libcxx/include/__pstl/internal/glue_algorithm_defs.h
@@ -134,29 +134,6 @@ template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterato
__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2> swap_ranges(
_ExecutionPolicy&& __exec, _ForwardIterator1 __first1, _ForwardIterator1 __last1, _ForwardIterator2 __first2);
-// [alg.transform]
-
-template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryOperation>
-__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2>
-transform(_ExecutionPolicy&& __exec,
- _ForwardIterator1 __first,
- _ForwardIterator1 __last,
- _ForwardIterator2 __result,
- _UnaryOperation __op);
-
-template <class _ExecutionPolicy,
- class _ForwardIterator1,
- class _ForwardIterator2,
- class _ForwardIterator,
- class _BinaryOperation>
-__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator>
-transform(_ExecutionPolicy&& __exec,
- _ForwardIterator1 __first1,
- _ForwardIterator1 __last1,
- _ForwardIterator2 __first2,
- _ForwardIterator __result,
- _BinaryOperation __op);
-
// [alg.replace]
template <class _ExecutionPolicy, class _ForwardIterator, class _UnaryPredicate, class _Tp>
diff --git a/libcxx/include/__pstl/internal/glue_algorithm_impl.h b/libcxx/include/__pstl/internal/glue_algorithm_impl.h
index db62705233b9..bae5efa7d057 100644
--- a/libcxx/include/__pstl/internal/glue_algorithm_impl.h
+++ b/libcxx/include/__pstl/internal/glue_algorithm_impl.h
@@ -251,27 +251,6 @@ __pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardItera
// [alg.transform]
-template <class _ExecutionPolicy, class _ForwardIterator1, class _ForwardIterator2, class _UnaryOperation>
-__pstl::__internal::__enable_if_execution_policy<_ExecutionPolicy, _ForwardIterator2>
-transform(_ExecutionPolicy&& __exec,
- _ForwardIterator1 __first,
- _ForwardIterator1 __last,
- _ForwardIterator2 __result,
- _UnaryOperation __op) {
- typedef typename iterator_traits<_ForwardIterator1>::reference _InputType;
- typedef typename iterator_traits<_ForwardIterator2>::reference _OutputType;
-
- auto __dispatch_tag = __pstl::__internal::__select_backend(__exec, __first, __result);
-
- return __pstl::__internal::__pattern_walk2(
- __dispatch_tag,
- std::forward<_ExecutionPolicy>(__exec),
- __first,
- __last,
- __result,
- [__op](_InputType __x, _OutputType __y) mutable { __y = __op(__x); });
-}
-
template <class _ExecutionPolicy,
class _ForwardIterator1,
class _ForwardIterator2,
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 469bf1706628..18a89eb1a4dc 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -1792,6 +1792,7 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/pstl_fill.h>
#include <__algorithm/pstl_find.h>
#include <__algorithm/pstl_for_each.h>
+#include <__algorithm/pstl_transform.h>
#include <__algorithm/push_heap.h>
#include <__algorithm/ranges_adjacent_find.h>
#include <__algorithm/ranges_all_of.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index fcf793f0e63f..d6419daff675 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -341,6 +341,9 @@ module std [system] {
module pstl_backends_cpu_backends_serial {
private header "__algorithm/pstl_backends/cpu_backends/serial.h"
}
+ module pstl_backends_cpu_backends_transform {
+ private header "__algorithm/pstl_backends/cpu_backends/transform.h"
+ }
module push_heap { private header "__algorithm/push_heap.h" }
module ranges_adjacent_find { private header "__algorithm/ranges_adjacent_find.h" }
module ranges_all_of { private header "__algorithm/ranges_all_of.h" }
diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 33cc828b9218..cb80edfd0694 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -121,6 +121,7 @@ END-SCRIPT
#include <__algorithm/pstl_backends/cpu_backends/find_if.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/find_if.h'}}
#include <__algorithm/pstl_backends/cpu_backends/for_each.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/for_each.h'}}
#include <__algorithm/pstl_backends/cpu_backends/serial.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/serial.h'}}
+#include <__algorithm/pstl_backends/cpu_backends/transform.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/pstl_backends/cpu_backends/transform.h'}}
#include <__algorithm/push_heap.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/push_heap.h'}}
#include <__algorithm/ranges_adjacent_find.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_adjacent_find.h'}}
#include <__algorithm/ranges_all_of.h> // expected-error@*:* {{use of private header from outside its module: '__algorithm/ranges_all_of.h'}}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp
new file mode 100644
index 000000000000..b2b98619fb96
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.binary.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+// class ForwardIterator, class BinaryOperation>
+// ForwardIterator
+// transform(ExecutionPolicy&& exec,
+// ForwardIterator1 first1, ForwardIterator1 last1,
+// ForwardIterator2 first2, ForwardIterator result,
+// BinaryOperation binary_op);
+
+#include <algorithm>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+
+EXECUTION_POLICY_SFINAE_TEST(transform);
+
+static_assert(sfinae_test_transform<int, int*, int*, int*, int*, bool (*)(int)>);
+static_assert(!sfinae_test_transform<std::execution::parallel_policy, int*, int*, int*, int*, int (*)(int, int)>);
+
+template <class Iter1, class Iter2, class Iter3>
+struct Test {
+ template <class Policy>
+ void operator()(Policy&& policy) {
+ // simple test
+ for (const int size : {0, 1, 2, 100, 350}) {
+ std::vector<int> a(size);
+ std::vector<int> b(size);
+ for (int i = 0; i != size; ++i) {
+ a[i] = i + 1;
+ b[i] = i - 3;
+ }
+
+ std::vector<int> out(std::size(a));
+ decltype(auto) ret = std::transform(
+ policy,
+ Iter1(std::data(a)),
+ Iter1(std::data(a) + std::size(a)),
+ Iter2(std::data(b)),
+ Iter3(std::data(out)),
+ [](int i, int j) { return i + j + 3; });
+ static_assert(std::is_same_v<decltype(ret), Iter3>);
+ assert(base(ret) == std::data(out) + std::size(out));
+ for (int i = 0; i != size; ++i) {
+ assert(out[i] == i * 2 + 1);
+ }
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(
+ types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v) {
+ using Iter3 = typename decltype(v)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{}, types::apply_type_identity{[](auto v2) {
+ using Iter2 = typename decltype(v2)::type;
+ types::for_each(
+ types::forward_iterator_list<int*>{},
+ TestIteratorWithPolicies<types::partial_instantiation<Test, Iter2, Iter3>::template apply>{});
+ }});
+ }});
+
+ return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp
new file mode 100644
index 000000000000..03985c7d3673
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.modifying.operations/alg.transform/pstl.transform.unary.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+// class UnaryOperation>
+// ForwardIterator2
+// transform(ExecutionPolicy&& exec,
+// ForwardIterator1 first1, ForwardIterator1 last1,
+// ForwardIterator2 result, UnaryOperation op);
+
+#include <algorithm>
+#include <cassert>
+#include <vector>
+
+#include "test_macros.h"
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+
+// We can't test the constraint on the execution policy, because that would conflict with the binary
+// transform algorithm that doesn't take an execution policy, which is not constrained at all.
+
+template <class Iter1, class Iter2>
+struct Test {
+ template <class Policy>
+ void operator()(Policy&& policy) {
+ // simple test
+ for (const int size : {0, 1, 2, 100, 350}) {
+ std::vector<int> a(size);
+ for (int i = 0; i != size; ++i)
+ a[i] = i + 1;
+
+ std::vector<int> out(std::size(a));
+ decltype(auto) ret = std::transform(
+ policy, Iter1(std::data(a)), Iter1(std::data(a) + std::size(a)), Iter2(std::data(out)), [](int i) {
+ return i + 3;
+ });
+ static_assert(std::is_same_v<decltype(ret), Iter2>);
+ assert(base(ret) == std::data(out) + std::size(out));
+ for (int i = 0; i != size; ++i)
+ assert(out[i] == i + 4);
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::forward_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<Test, Iter>::template apply>{});
+ }});
+
+ return 0;
+}
diff --git a/libcxx/test/support/type_algorithms.h b/libcxx/test/support/type_algorithms.h
index 95a282b7b0bc..46e658624f82 100644
--- a/libcxx/test/support/type_algorithms.h
+++ b/libcxx/test/support/type_algorithms.h
@@ -52,6 +52,34 @@ TEST_CONSTEXPR_CXX14 void for_each(type_list<Types...>, Functor f) {
swallow((f.template operator()<Types>(), 0)...);
}
+template <class T>
+struct type_identity {
+ using type = T;
+};
+
+#if TEST_STD_VER >= 17
+template <class Func>
+struct apply_type_identity {
+ Func func_;
+
+ apply_type_identity(Func func) : func_(func) {}
+
+ template <class... Args>
+ decltype(auto) operator()() const {
+ return func_(type_identity<Args>{}...);
+ }
+};
+
+template <class T>
+apply_type_identity(T) -> apply_type_identity<T>;
+
+#endif
+template <template <class...> class T, class... Args>
+struct partial_instantiation {
+ template <class Other>
+ using apply = T<Args..., Other>;
+};
+
// type categories defined in [basic.fundamental] plus extensions (without CV-qualifiers)
using character_types =