[libcxx-commits] [libcxx] [libc++][pstl] Generic implementation of parallel std::is_sorted_until (PR #178756)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 30 02:27:49 PST 2026


https://github.com/mikekazakov updated https://github.com/llvm/llvm-project/pull/178756

>From 05dc9334346b08ef79a48764f3d9764c20d9358a Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Sat, 24 Jan 2026 11:20:56 +0000
Subject: [PATCH 1/5] Implementation of std::is_sorted_until

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__algorithm/pstl.h             |  25 +++
 libcxx/include/__pstl/backend_fwd.h           |   5 +
 libcxx/include/__pstl/backends/default.h      |  32 +++
 libcxx/include/__pstl/index_iterator.h        | 135 +++++++++++++
 libcxx/include/module.modulemap.in            |   1 +
 .../algorithms/pstl.index_iterator.pass.cpp   |  95 +++++++++
 .../is.sorted/pstl.is_sorted_until.pass.cpp   | 188 ++++++++++++++++++
 .../pstl.is_sorted_until_comp.pass.cpp        | 187 +++++++++++++++++
 9 files changed, 669 insertions(+)
 create mode 100644 libcxx/include/__pstl/index_iterator.h
 create mode 100644 libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 494c21bd30019..c65167be97a44 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -669,6 +669,7 @@ set(files
   __pstl/cpu_algos/transform_reduce.h
   __pstl/dispatch.h
   __pstl/handle_exception.h
+  __pstl/index_iterator.h
   __random/bernoulli_distribution.h
   __random/binomial_distribution.h
   __random/cauchy_distribution.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 10625ea3f8e3d..178f5e9fa5400 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -654,6 +654,31 @@ _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 >
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+is_sorted_until(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted_until requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__is_sorted_until, __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>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator
+is_sorted_until(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator, "is_sorted_until requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__is_sorted_until, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index a52e6db954d0c..c6c00087105dd 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -297,6 +297,11 @@ struct __reduce;
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
 //                       _Tp __init, _BinaryOperation __op) const noexcept;
 
+struct __is_sorted_until;
+// template <class _Policy, class _ForwardIterator, class _Comp>
+// optional<_ForwardIterator>
+// operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __is_sorted;
 // template <class _Policy, class _ForwardIterator, class _Comp>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index be90715af13b5..c25a8fc2aa9b0 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -14,6 +14,7 @@
 #include <__algorithm/fill_n.h>
 #include <__algorithm/for_each_n.h>
 #include <__algorithm/is_sorted.h>
+#include <__algorithm/is_sorted_until.h>
 #include <__config>
 #include <__functional/identity.h>
 #include <__functional/not_fn.h>
@@ -23,6 +24,7 @@
 #include <__iterator/next.h>
 #include <__pstl/backend_fwd.h>
 #include <__pstl/dispatch.h>
+#include <__pstl/index_iterator.h>
 #include <__utility/empty.h>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -57,6 +59,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - is_sorted_until
 //
 // for_each family
 // ---------------
@@ -181,6 +184,35 @@ struct __is_partitioned<__default_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __is_sorted_until<__default_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator, class _Comp>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator>
+  operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept {
+    if constexpr (__has_random_access_iterator_category_or_concept<_ForwardIterator>::value) {
+      using _DifferenceType    = typename std::iterator_traits<_ForwardIterator>::difference_type;
+      using _IndexIteratorType = __index_iterator<_DifferenceType>;
+
+      _DifferenceType __n = __last - __first;
+      if (__n <= 1)
+        return __last; // Sorted by definition
+
+      _IndexIteratorType __index_first{_DifferenceType{}};
+      _IndexIteratorType __index_last{__n - 1};
+
+      using _FindIf = __dispatch<__find_if, __current_configuration, _ExecutionPolicy>;
+      auto __res    = _FindIf()(__policy, __index_first, __index_last, [&](_DifferenceType __index) -> bool {
+        return __comp(*(__first + __index + 1), *(__first + __index));
+      });
+      if (!__res)
+        return nullopt;
+      return __first + *(*__res) + 1;
+    } else {
+      return std::is_sorted_until(std::move(__first), std::move(__last), std::forward<_Comp>(__comp));
+    }
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
diff --git a/libcxx/include/__pstl/index_iterator.h b/libcxx/include/__pstl/index_iterator.h
new file mode 100644
index 0000000000000..e9e9af635e3bb
--- /dev/null
+++ b/libcxx/include/__pstl/index_iterator.h
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___PSTL_INDEX_ITERATOR_H
+#define _LIBCPP___PSTL_INDEX_ITERATOR_H
+
+#include <__config>
+#include <__iterator/iterator_traits.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+// This iterator type is a stopgap solution to use the existing backend algorithms in PSTL and be able to implement
+// position-sensitive algorithms on top. While providing an iterator interface, this type is essentially a wrapper over
+// an index type.
+// Once the backends start providing a less restrictive interface, e.g. working with chunks of iterator
+// ranges, and supporting forward iterators, any algorithms that were implemented using this index wrapper should be
+// reimplemented using iterators as-is.
+
+template <typename _Index>
+class __index_iterator {
+public:
+  using value_type        = _Index;
+  using difference_type   = _Index;
+  using iterator_category = std::random_access_iterator_tag;
+  using reference         = _Index;
+  using pointer           = void;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator() noexcept = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __index_iterator(_Index __index) noexcept : __index_(__index) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Index operator*() const noexcept { return __index_; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator& operator++() noexcept {
+    ++__index_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator operator++(int) noexcept {
+    auto __tmp = *this;
+    ++__index_;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator& operator--() noexcept {
+    --__index_;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator operator--(int) noexcept {
+    auto __tmp = *this;
+    --__index_;
+    return __tmp;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator operator+(_Index __n) const noexcept {
+    return __index_iterator{__index_ + __n};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr __index_iterator operator+(_Index __n, const __index_iterator& __x) noexcept {
+    return __x + __n;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator& operator+=(_Index __n) noexcept {
+    __index_ += __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator operator-(_Index __n) const noexcept {
+    return __index_iterator{__index_ - __n};
+  }
+
+  _LIBCPP_HIDE_FROM_ABI friend constexpr _Index
+  operator-(const __index_iterator& __lhs, const __index_iterator& __rhs) noexcept {
+    return __lhs.__index_ - __rhs.__index_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __index_iterator& operator-=(_Index __n) noexcept {
+    __index_ -= __n;
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Index operator[](_Index __n) const noexcept { return __index_ + __n; }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const __index_iterator& __other) const noexcept {
+    return __index_ == __other.__index_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const __index_iterator& __other) const noexcept {
+    return !(*this == __other);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const __index_iterator& __other) const noexcept {
+    return __index_ < __other.__index_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const __index_iterator& __other) const noexcept {
+    return __index_ <= __other.__index_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const __index_iterator& __other) const noexcept {
+    return __index_ > __other.__index_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const __index_iterator& __other) const noexcept {
+    return __index_ >= __other.__index_;
+  }
+
+private:
+  _Index __index_ = {};
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_INDEX_ITERATOR_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 58fc4941cb7d8..bfefa9c6ae174 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2356,6 +2356,7 @@ module std [system] {
     }
     module dispatch           { header "__pstl/dispatch.h" }
     module handle_exception   { header "__pstl/handle_exception.h" }
+    module index_iterator     { header "__pstl/index_iterator.h" }
   }
 
   // Miscellaneous modules for top-level headers
diff --git a/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp b/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
new file mode 100644
index 0000000000000..650b7423d0839
--- /dev/null
+++ b/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
@@ -0,0 +1,95 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// Checks the logic of the index wrapper iterator.
+
+#include <algorithm>
+#include <execution>
+#include <iterator>
+#include <type_traits>
+#include <cassert>
+#include <cstddef>
+
+int main(int, char**) {
+  using Index = std::ptrdiff_t;
+  using Iter  = std::__pstl::__index_iterator<Index>;
+  static_assert(std::is_nothrow_default_constructible_v<Iter>);
+  static_assert(std::is_nothrow_constructible_v<Iter, Index>);
+  static_assert(std::is_trivially_copy_constructible_v<Iter>);
+  static_assert(std::is_trivially_move_constructible_v<Iter>);
+  static_assert(std::is_trivially_assignable_v<Iter, Iter>);
+  static_assert(std::is_trivially_copy_assignable_v<Iter>);
+  static_assert(std::is_trivially_move_assignable_v<Iter>);
+  static_assert(std::is_trivially_destructible_v<Iter>);
+
+  {
+    assert((*Iter{}) == Index{});
+  }
+  {
+    assert((*Iter{7}) == 7);
+  }
+  {
+    Iter it{5};
+    assert(&(++it) == &it);
+    assert((*it) == 6);
+  }
+  {
+    Iter it{5};
+    assert(*(it++) == 5);
+    assert((*it) == 6);
+  }
+  {
+    Iter it{5};
+    assert(&(--it) == &it);
+    assert((*it) == 4);
+  }
+  {
+    Iter it{5};
+    assert(*(it--) == 5);
+    assert((*it) == 4);
+  }
+  {
+    assert(*(Iter{4} + 3) == 7);
+  }
+  {
+    assert(*(4 + Iter{3}) == 7);
+  }
+  {
+    Iter it{5};
+    assert(&(it += 3) == &it);
+    assert((*it) == 8);
+  }
+  {
+    assert((Iter{4} - 3) == Iter{1});
+  }
+  {
+    assert((Iter{4} - Iter{3}) == 1);
+  }
+  {
+    Iter it{5};
+    assert(&(it -= 3) == &it);
+    assert((*it) == 2);
+  }
+  {
+    assert(Iter{5}[3] == 8);
+  }
+  {
+    assert(Iter{5} == Iter{5});
+    assert(!(Iter{5} == Iter{6}));
+  }
+  {
+    assert(Iter{5} != Iter{6});
+    assert(!(Iter{5} != Iter{5}));
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
new file mode 100644
index 0000000000000..b6aea31dc5a74
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.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>
+// ForwardIterator is_sorted_until(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_until(policy, Iter(a), Iter(a)) == Iter(a));
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+
+    {
+      int a[]     = {0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+
+    {
+      int a[]     = {0, 0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 3));
+    }
+    {
+      int a[]     = {0, 0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 3));
+    }
+    {
+      int a[]     = {0, 1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 3));
+    }
+    {
+      int a[]     = {1, 1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == 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_until_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
new file mode 100644
index 0000000000000..74a073efd4451
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
@@ -0,0 +1,187 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+// ForwardIterator is_sorted_until(ExecutionPolicy&& exec,
+//                                 ForwardIterator first, ForwardIterator last,
+//                                 Comp comp);
+
+#include <algorithm>
+#include <functional>
+#include <cassert>
+
+#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_until(policy, Iter(a), Iter(a), std::greater<int>()) == Iter(a));
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+
+    {
+      int a[]     = {0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+
+    {
+      int a[]     = {0, 0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {0, 0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 3));
+    }
+    {
+      int a[]     = {0, 0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {0, 1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {0, 1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {0, 1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {0, 1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      int a[]     = {1, 0, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 0, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 3));
+    }
+    {
+      int a[]     = {1, 0, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 0, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 2));
+    }
+    {
+      int a[]     = {1, 1, 0, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 1, 0, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 3));
+    }
+    {
+      int a[]     = {1, 1, 1, 0};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      int a[]     = {1, 1, 1, 1};
+      unsigned sa = sizeof(a) / sizeof(a[0]);
+      assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == 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;
+}

>From 56b814a5af2744c7a81647c3329dc7995c87e995 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 29 Jan 2026 20:39:44 +0000
Subject: [PATCH 2/5] Rebased is_sorted on top of is_sorted_until, added more
 tests

---
 libcxx/include/__pstl/backend_fwd.h           |  9 ++--
 libcxx/include/__pstl/backends/default.h      | 44 ++++++-------------
 .../algorithms/pstl.nodiscard.verify.cpp      |  4 ++
 .../is.sorted/pstl.is_sorted_until.pass.cpp   | 41 +++++++++++++++++
 .../pstl.is_sorted_until_comp.pass.cpp        | 41 +++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  8 ++++
 6 files changed, 113 insertions(+), 34 deletions(-)

diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index c6c00087105dd..6a17e2a109091 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -297,15 +297,16 @@ struct __reduce;
 // operator()(_Policy&&, _ForwardIterator __first, _ForwardIterator __last,
 //                       _Tp __init, _BinaryOperation __op) const noexcept;
 
-struct __is_sorted_until;
+template <class _Backend, class _ExecutionPolicy>
+struct __is_sorted;
 // template <class _Policy, class _ForwardIterator, class _Comp>
-// optional<_ForwardIterator>
+// optional<bool>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
 template <class _Backend, class _ExecutionPolicy>
-struct __is_sorted;
+struct __is_sorted_until;
 // template <class _Policy, class _ForwardIterator, class _Comp>
-// optional<bool>
+// optional<_ForwardIterator>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _ForwardIterator __last, _Comp&& __comp) const noexcept;
 
 } // namespace __pstl
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index c25a8fc2aa9b0..5e005a96429a8 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -59,6 +59,7 @@ namespace __pstl {
 // - all_of
 // - none_of
 // - is_partitioned
+// - is_sorted
 // - is_sorted_until
 //
 // for_each family
@@ -85,7 +86,6 @@ namespace __pstl {
 // - count
 // - equal(3 legs)
 // - equal
-// - is_sorted
 // - reduce
 //
 // transform and transform_binary family
@@ -213,6 +213,19 @@ struct __is_sorted_until<__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 {
+    using _IsSortedUntil             = __dispatch<__is_sorted_until, __current_configuration, _ExecutionPolicy>;
+    optional<_ForwardIterator> __res = _IsSortedUntil()(__policy, __first, __last, std::forward<_Comp>(__comp));
+    if (!__res)
+      return nullopt;
+    return *__res == __last;
+  }
+};
+
 //////////////////////////////////////////////////////////////
 // for_each family
 //////////////////////////////////////////////////////////////
@@ -423,35 +436,6 @@ 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_bidirectional_iterator_category<_ForwardIterator>::value) {
-      if (__first == __last)
-        return true; // Empty, sorted by definition
-      _ForwardIterator __first2 = std::next(__first);
-      if (__first2 == __last)
-        return true; // Only one element, sorted by definition
-      --__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()(
-          __policy,
-          std::move(__first),
-          std::move(__last),
-          std::move(__first2),
-          true,
-          std::logical_and{},
-          [&](_Ref __left, _Ref __right) -> bool { return !__comp(__right, __left); });
-    } else {
-      // Currently anything outside bidirectional 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/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index b6554cef6f32a..fa6bf65aab5ff 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -52,4 +52,8 @@ void test() {
   std::is_sorted(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_sorted(std::execution::par, std::begin(a), std::end(a), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::is_sorted_until(std::execution::par, std::begin(a), std::end(a));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::is_sorted_until(std::execution::par, std::begin(a), std::end(a), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
index b6aea31dc5a74..98cfceea59deb 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
@@ -17,6 +17,7 @@
 #include <algorithm>
 #include <cassert>
 #include <functional>
+#include <limits>
 #include <numeric>
 
 #include "test_execution_policies.h"
@@ -175,6 +176,46 @@ struct Test {
       unsigned sa = sizeof(a) / sizeof(a[0]);
       assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa)) == 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_until(policy, Iter(a), Iter(a + sa)) == 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_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 2));
+    }
+    {
+      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_until(policy, Iter(a), Iter(a + sa)) == 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_until(policy, Iter(a), Iter(a + sa)) == Iter(a + 3));
+    }
   }
 };
 
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
index 74a073efd4451..e9bcdca288094 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
@@ -17,6 +17,7 @@
 
 #include <algorithm>
 #include <functional>
+#include <limits>
 #include <cassert>
 
 #include "test_execution_policies.h"
@@ -175,6 +176,46 @@ struct Test {
       unsigned sa = sizeof(a) / sizeof(a[0]);
       assert(std::is_sorted_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
     }
+    {
+      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_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == 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_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 1));
+    }
+    {
+      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_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + sa));
+    }
+    {
+      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_until(policy, Iter(a), Iter(a + sa), std::greater<int>()) == Iter(a + 3));
+    }
   }
 };
 
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index a3db159289393..bbb2d4e9e40d4 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -290,6 +290,14 @@ int main(int, char**) {
         assert_non_throwing([=, &policy] {
           (void)std::is_sorted(policy, std::move(first1), std::move(last1), compare);
         });
+
+        // is_sorted_until(first, last)
+        assert_non_throwing([=, &policy] { (void)std::is_sorted_until(policy, std::move(first1), std::move(last1)); });
+
+        // is_sorted_until(first, last, comp)
+        assert_non_throwing([=, &policy] {
+          (void)std::is_sorted_until(policy, std::move(first1), std::move(last1), compare);
+        });
       }
 
       {

>From b427a33a8a272240f12e89d1719a27d53a473f2a Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Thu, 29 Jan 2026 22:17:27 +0000
Subject: [PATCH 3/5] Include the internal header directly in the test so that
 the modules build can pass

---
 libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp b/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
index 650b7423d0839..bcdf009b2912d 100644
--- a/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.index_iterator.pass.cpp
@@ -12,8 +12,7 @@
 
 // Checks the logic of the index wrapper iterator.
 
-#include <algorithm>
-#include <execution>
+#include <__pstl/index_iterator.h>
 #include <iterator>
 #include <type_traits>
 #include <cassert>

>From d3684c7fd741063c0268c79597c7e997ba38d053 Mon Sep 17 00:00:00 2001
From: "Michael G. Kazakov" <mike.kazakov at gmail.com>
Date: Fri, 30 Jan 2026 10:24:05 +0000
Subject: [PATCH 4/5] Apply suggestion from @Zingam

Co-authored-by: Hristo Hristov <zingam at outlook.com>
---
 .../alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp            | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
index 98cfceea59deb..acf20bdccaa73 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14
+// REQUIRES: std-at-least-c++17
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 

>From 74ce3a63c72eb6e27854d75674032bd7a93b0875 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Fri, 30 Jan 2026 10:27:26 +0000
Subject: [PATCH 5/5] Use REQUIRES: std-at-least-c++17 in the test

---
 .../alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
index e9bcdca288094..fb438663f7cd4 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.sort/is.sorted/pstl.is_sorted_until_comp.pass.cpp
@@ -6,7 +6,7 @@
 //
 //===----------------------------------------------------------------------===//
 
-// UNSUPPORTED: c++03, c++11, c++14
+// REQUIRES: std-at-least-c++17
 
 // UNSUPPORTED: libcpp-has-no-incomplete-pstl
 



More information about the libcxx-commits mailing list