[libcxx-commits] [libcxx] [libc++][pstl] Generic implementation of parallel std::is_sorted (PR #176129)
Michael G. Kazakov via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Jan 24 02:18:01 PST 2026
https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/176129
>From dc01e44005393bb688a84c286f1cce30d1838898 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 15 Jan 2026 10:27:20 +0000
Subject: [PATCH 1/6] Generic implementation of parallel std::is_sorted based
on std::transform_reduce
---
libcxx/include/__algorithm/pstl.h | 24 +++
libcxx/include/__pstl/backend_fwd.h | 6 +
libcxx/include/__pstl/backends/default.h | 30 +++
.../is.sorted/pstl.is_sorted.pass.cpp | 188 +++++++++++++++++
.../is.sorted/pstl.is_sorted_comp.pass.cpp | 189 ++++++++++++++++++
5 files changed, 437 insertions(+)
create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 7169dd85df602..a79bac3200d6b 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -654,6 +654,30 @@ _LIBCPP_HIDE_FROM_ABI _ForwardOutIterator transform(
std::move(__op));
}
+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 bool is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__is_sorted, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), less{});
+}
+
+template <class _ExecutionPolicy,
+ class _ForwardIterator,
+ class _Comp,
+ class _RawPolicy = __remove_cvref_t<_ExecutionPolicy>,
+ enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI bool
+is_sorted(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp __comp) {
+ _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted requires ForwardIterators");
+ using _Implementation = __pstl::__dispatch<__pstl::__is_sorted, __pstl::__current_configuration, _RawPolicy>;
+ return __pstl::__handle_exception<_Implementation>(
+ std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
_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 a7d53b6a1c989..a52e6db954d0c 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -297,6 +297,12 @@ struct __reduce;
// operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
// _Tp __init, _BinaryOperation __op) const noexcept;
+template <class _Backend, class _ExecutionPolicy>
+struct __is_sorted;
+// template <class _Policy, class _ForwardIterator, class _Comp>
+// optional<bool>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) 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 43b1f1ce3870a..9fa642b080f64 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -13,6 +13,7 @@
#include <__algorithm/equal.h>
#include <__algorithm/fill_n.h>
#include <__algorithm/for_each_n.h>
+#include <__algorithm/is_sorted.h>
#include <__config>
#include <__functional/identity.h>
#include <__functional/not_fn.h>
@@ -388,6 +389,35 @@ struct __reduce<__default_backend_tag, _ExecutionPolicy> {
}
};
+template <class _ExecutionPolicy>
+struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
+ template <class _Policy, class _ForwardIterator, class _Comp>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
+ operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
+ if constexpr (__has_random_access_iterator_category<_ForwardIterator>::value) {
+ if (__first == __last)
+ return true; // Empty, sorted by definition
+ _ForwardIterator __first2 = __first + 1;
+ if (__first2 == __last)
+ return true; // Only one element, sorted by definition
+ --__last;
+ using _TransformReduce = __dispatch<__transform_reduce_binary, __current_configuration, _ExecutionPolicy>;
+ using _Ref = __iterator_reference<_ForwardIterator>;
+ return _TransformReduce()(
+ __policy,
+ std::move(__first),
+ std::move(__last),
+ std::move(__first2),
+ true,
+ std::logical_and{},
+ [&](_Ref __first, _Ref __second) -> bool { return !__comp(__second, __first); });
+ } else {
+ // Currently anything outside random access iterators has to be processed serially
+ return std::is_sorted(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
+ }
+ }
+};
+
//////////////////////////////////////////////////////////////
// transform family
//////////////////////////////////////////////////////////////
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
new file mode 100644
index 0000000000000..9bffa588f0858
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
@@ -0,0 +1,188 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class ExecutionPolicy, class ForwardIterator,
+// bool is_sorted(ExecutionPolicy&& exec,
+// ForwardIterator first, ForwardIterator last);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter>
+struct Test {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ int a[] = {0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a)));
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+
+ {
+ int a[] = {0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+
+ {
+ int a[] = {0, 0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {0, 1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {1, 1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+ types::bidirectional_iterator_list<int*>,
+ types::random_access_iterator_list<int*>>{},
+ TestIteratorWithPolicies<Test>{});
+
+ return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
new file mode 100644
index 0000000000000..aa57678a5ec2a
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
@@ -0,0 +1,189 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template<class ExecutionPolicy, class ForwardIterator, class Comp>
+// bool is_sorted(ExecutionPolicy&& exec,
+// ForwardIterator first, ForwardIterator last,
+// Comp comp);
+
+#include <algorithm>
+#include <cassert>
+#include <functional>
+#include <numeric>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+
+template <class Iter>
+struct Test {
+ template <class ExecutionPolicy>
+ void operator()(ExecutionPolicy&& policy) {
+ {
+ int a[] = {0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a)));
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+
+ {
+ int a[] = {0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+
+ {
+ int a[] = {0, 0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {0, 1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 0, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 0, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 0, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 1, 0};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {1, 1, 1, 1};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ }
+};
+
+int main(int, char**) {
+ types::for_each(types::concatenate_t<types::forward_iterator_list<int*>,
+ types::bidirectional_iterator_list<int*>,
+ types::random_access_iterator_list<int*>>{},
+ TestIteratorWithPolicies<Test>{});
+
+ return 0;
+}
>From 695b416e9aae9d7b24c8757699cdb2216bf8af91 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 15 Jan 2026 20:02:53 +0000
Subject: [PATCH 2/6] Changed the arguments names to prevent shadowing
---
libcxx/include/__pstl/backends/default.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 9fa642b080f64..a555a3f7878a7 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -410,7 +410,7 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
std::move(__first2),
true,
std::logical_and{},
- [&](_Ref __first, _Ref __second) -> bool { return !__comp(__second, __first); });
+ [&](_Ref __left, _Ref __right) -> bool { return !__comp(__right, __left); });
} else {
// Currently anything outside random access iterators has to be processed serially
return std::is_sorted(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
>From 4301d18c1df0f559811decfe8c6e175142c00f03 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Jan 2026 21:28:42 +0000
Subject: [PATCH 3/6] Relaxed the iterator requirement to bidirectional, added
more diverse test cases
---
libcxx/include/__pstl/backends/default.h | 8 ++--
.../is.sorted/pstl.is_sorted.pass.cpp | 43 ++++++++++++++++++-
.../is.sorted/pstl.is_sorted_comp.pass.cpp | 40 +++++++++++++++++
3 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index a555a3f7878a7..8c84225ad6c11 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -81,6 +81,7 @@ namespace __pstl {
// - count
// - equal(3 legs)
// - equal
+// - is_sorted
// - reduce
//
// transform and transform_binary family
@@ -394,13 +395,14 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
template <class _Policy, class _ForwardIterator, class _Comp>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<bool>
operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
- if constexpr (__has_random_access_iterator_category<_ForwardIterator>::value) {
+ if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
if (__first == __last)
return true; // Empty, sorted by definition
- _ForwardIterator __first2 = __first + 1;
+ _ForwardIterator __first2 = __first;
+ ++__first2; // __first2 = __first + 1
if (__first2 == __last)
return true; // Only one element, sorted by definition
- --__last;
+ --__last; // __last = __last - 1
using _TransformReduce = __dispatch<__transform_reduce_binary, __current_configuration, _ExecutionPolicy>;
using _Ref = __iterator_reference<_ForwardIterator>;
return _TransformReduce()(
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
index 9bffa588f0858..a7708721c6035 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
@@ -10,7 +10,7 @@
// UNSUPPORTED: libcpp-has-no-incomplete-pstl
-// template<class ExecutionPolicy, class ForwardIterator,
+// template<class ExecutionPolicy, class ForwardIterator>
// bool is_sorted(ExecutionPolicy&& exec,
// ForwardIterator first, ForwardIterator last);
@@ -18,6 +18,7 @@
#include <cassert>
#include <functional>
#include <numeric>
+#include <limits>
#include "test_execution_policies.h"
#include "test_iterators.h"
@@ -175,6 +176,46 @@ struct Test {
unsigned sa = sizeof(a) / sizeof(a[0]);
assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
}
+ {
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ -1,
+ 0,
+ 1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
+ {
+ int a[] = {
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min() / 2,
+ 1,
+ 0,
+ -1,
+ std::numeric_limits<int>::max() / 2,
+ std::numeric_limits<int>::max()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa)));
+ }
}
};
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
index aa57678a5ec2a..b7b01188d7f0b 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
@@ -176,6 +176,46 @@ struct Test {
unsigned sa = sizeof(a) / sizeof(a[0]);
assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
}
+ {
+ int a[] = {std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::min()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::min(),
+ std::numeric_limits<int>::max()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max() / 2,
+ 1,
+ 0,
+ -1,
+ std::numeric_limits<int>::min() / 2,
+ std::numeric_limits<int>::min()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
+ {
+ int a[] = {
+ std::numeric_limits<int>::max(),
+ std::numeric_limits<int>::max() / 2,
+ -1,
+ 0,
+ 1,
+ std::numeric_limits<int>::min() / 2,
+ std::numeric_limits<int>::min()};
+ unsigned sa = sizeof(a) / sizeof(a[0]);
+ assert(!std::is_sorted(policy, Iter(a), Iter(a + sa), std::greater<int>()));
+ }
}
};
>From 4904023e31caf1d9ff253421b77c2f1869c2aaf9 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Jan 2026 21:55:33 +0000
Subject: [PATCH 4/6] Updated the comment
---
libcxx/include/__pstl/backends/default.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 8c84225ad6c11..70ca0cec094c1 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -414,7 +414,7 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
std::logical_and{},
[&](_Ref __left, _Ref __right) -> bool { return !__comp(__right, __left); });
} else {
- // Currently anything outside random access iterators has to be processed serially
+ // Currently anything outside bidirectional iterators has to be processed serially
return std::is_sorted(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
}
}
>From 101830c975da5c252f0ccc7478540fe967807a43 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Wed, 21 Jan 2026 15:36:30 +0000
Subject: [PATCH 5/6] Added a missing include
---
.../alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp | 2 +-
.../alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
index a7708721c6035..5b1e5ce60c253 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted.pass.cpp
@@ -17,8 +17,8 @@
#include <algorithm>
#include <cassert>
#include <functional>
-#include <numeric>
#include <limits>
+#include <numeric>
#include "test_execution_policies.h"
#include "test_iterators.h"
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
index b7b01188d7f0b..8a6c24ca22af1 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_comp.pass.cpp
@@ -18,6 +18,7 @@
#include <algorithm>
#include <cassert>
#include <functional>
+#include <limits>
#include <numeric>
#include "test_execution_policies.h"
>From c705c8009480eb329c2c2a37f80b69435ab0fa84 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 24 Jan 2026 10:17:37 +0000
Subject: [PATCH 6/6] Updated iterator changes with a more clear intent, added
termination tests
---
libcxx/include/__pstl/backends/default.h | 6 +++---
.../test/std/algorithms/pstl.exception_handling.pass.cpp | 8 ++++++++
2 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index 70ca0cec094c1..be90715af13b5 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -20,6 +20,7 @@
#include <__functional/operations.h>
#include <__iterator/concepts.h>
#include <__iterator/iterator_traits.h>
+#include <__iterator/next.h>
#include <__pstl/backend_fwd.h>
#include <__pstl/dispatch.h>
#include <__utility/empty.h>
@@ -398,11 +399,10 @@ struct __is_sorted<__default_backend_tag, _ExecutionPolicy> {
if constexpr (__has_bidirectional_iterator_category<_ForwardIterator>::value) {
if (__first == __last)
return true; // Empty, sorted by definition
- _ForwardIterator __first2 = __first;
- ++__first2; // __first2 = __first + 1
+ _ForwardIterator __first2 = std::next(__first);
if (__first2 == __last)
return true; // Only one element, sorted by definition
- --__last; // __last = __last - 1
+ --__last; // Make two iterator ranges: [__first, __first + n - 1) and [__first + 1, __first + n)
using _TransformReduce = __dispatch<__transform_reduce_binary, __current_configuration, _ExecutionPolicy>;
using _Ref = __iterator_reference<_ForwardIterator>;
return _TransformReduce()(
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index e24ce66314197..a3db159289393 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -282,6 +282,14 @@ int main(int, char**) {
assert_non_throwing([=, &policy] {
(void)std::stable_sort(policy, std::move(first1), std::move(last1), compare);
});
+
+ // is_sorted(first, last)
+ assert_non_throwing([=, &policy] { (void)std::is_sorted(policy, std::move(first1), std::move(last1)); });
+
+ // is_sorted(first, last, comp)
+ assert_non_throwing([=, &policy] {
+ (void)std::is_sorted(policy, std::move(first1), std::move(last1), compare);
+ });
}
{
More information about the libcxx-commits
mailing list