[libcxx-commits] [libcxx] [libc++][pstl] Implementation of parallel std::find_end() based on __parallel_find() (PR #218321)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Mon Aug 24 07:27:49 PDT 2026


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

>From b40c373a515f4e7925630d9f94ef770ad3b2267c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Mon, 24 Aug 2026 11:47:18 +0700
Subject: [PATCH] Implementation of parallel std::find_end

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__algorithm/pstl.h             |  48 +++
 libcxx/include/__pstl/backend_fwd.h           |   7 +
 libcxx/include/__pstl/backends/default.h      |   4 +
 libcxx/include/__pstl/backends/libdispatch.h  |   5 +
 libcxx/include/__pstl/backends/serial.h       |  16 +
 libcxx/include/__pstl/backends/std_thread.h   |   5 +
 libcxx/include/__pstl/cpu_algos/find_end.h    | 102 +++++++
 libcxx/include/module.modulemap.in            |   3 +
 .../pstl.iterator-requirements.verify.cpp     |   9 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../alg.find.end/pstl.find_end.pass.cpp       | 268 +++++++++++++++++
 .../alg.find.end/pstl.find_end_pred.pass.cpp  | 276 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  14 +
 14 files changed, 762 insertions(+)
 create mode 100644 libcxx/include/__pstl/cpu_algos/find_end.h
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end_pred.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index a1d6a09aa6b20..0440abea75bc8 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -694,6 +694,7 @@ set(files
   __pstl/cpu_algos/any_of.h
   __pstl/cpu_algos/cpu_traits.h
   __pstl/cpu_algos/fill.h
+  __pstl/cpu_algos/find_end.h
   __pstl/cpu_algos/find_if.h
   __pstl/cpu_algos/for_each.h
   __pstl/cpu_algos/merge.h
diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index a8ef6ddae9b63..2aba027a0ea48 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -419,6 +419,54 @@ fill_n(_ExecutionPolicy&& __policy, _ForwardIterator __first, _Size __n, const _
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__n), __value);
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_end(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_end requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_end requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_end, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      equal_to<>{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _BinaryPredicate,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _ForwardIterator1 find_end(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _BinaryPredicate __pred) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "find_end requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "find_end requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__find_end, __pstl::__current_configuration, _RawPolicy>;
+  return __pstl::__handle_exception<_Implementation>(
+      std::forward<_ExecutionPolicy>(__policy),
+      std::move(__first1),
+      std::move(__last1),
+      std::move(__first2),
+      std::move(__last2),
+      std::move(__pred));
+}
+
 template <class _ExecutionPolicy,
           class _ForwardIterator,
           class _Predicate,
diff --git a/libcxx/include/__pstl/backend_fwd.h b/libcxx/include/__pstl/backend_fwd.h
index 45e570c51aed9..736de86ca0189 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -116,6 +116,13 @@ struct __find_first_of;
 // operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
 //                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __find_end;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Predicate>
+// optional<_ForwardIterator1>
+// operator()(_Policy&&, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                       _ForwardIterator2 __first2, _ForwardIterator2 __last2, _Predicate __pred) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __for_each;
 // template <class _Policy, class _ForwardIterator, class _Function>
diff --git a/libcxx/include/__pstl/backends/default.h b/libcxx/include/__pstl/backends/default.h
index ecf5b3bc3d9eb..1b434fdabfff2 100644
--- a/libcxx/include/__pstl/backends/default.h
+++ b/libcxx/include/__pstl/backends/default.h
@@ -60,6 +60,10 @@ namespace __pstl {
 //
 // This backend implements all the PSTL algorithms based on the following basis operations:
 //
+// find_end family
+// ------------------
+// No other algorithms based on find_end
+//
 // find_if family
 // --------------
 // - find
diff --git a/libcxx/include/__pstl/backends/libdispatch.h b/libcxx/include/__pstl/backends/libdispatch.h
index c5efbd46298fb..48970c8c3402d 100644
--- a/libcxx/include/__pstl/backends/libdispatch.h
+++ b/libcxx/include/__pstl/backends/libdispatch.h
@@ -37,6 +37,7 @@
 #include <__pstl/cpu_algos/any_of.h>
 #include <__pstl/cpu_algos/cpu_traits.h>
 #include <__pstl/cpu_algos/fill.h>
+#include <__pstl/cpu_algos/find_end.h>
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
@@ -362,6 +363,10 @@ struct __cpu_traits<__libdispatch_backend_tag> {
 };
 
 // Mandatory implementations of the computational basis
+template <class _ExecutionPolicy>
+struct __find_end<__libdispatch_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_find_end<__libdispatch_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __find_if<__libdispatch_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_find_if<__libdispatch_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index c67f0b3331ac2..b64dba7ec0d3a 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___PSTL_BACKENDS_SERIAL_H
 #define _LIBCPP___PSTL_BACKENDS_SERIAL_H
 
+#include <__algorithm/find_end.h>
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
 #include <__algorithm/merge.h>
@@ -50,6 +51,21 @@ namespace __pstl {
 //       if we end up running serially anyways.
 //
 
+template <class _ExecutionPolicy>
+struct __find_end<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _BinaryPredicate __pred) const noexcept {
+    return std::find_end(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __find_if<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator, class _Pred>
diff --git a/libcxx/include/__pstl/backends/std_thread.h b/libcxx/include/__pstl/backends/std_thread.h
index 06beb04e4d885..9beffc6c304f1 100644
--- a/libcxx/include/__pstl/backends/std_thread.h
+++ b/libcxx/include/__pstl/backends/std_thread.h
@@ -16,6 +16,7 @@
 #include <__pstl/cpu_algos/any_of.h>
 #include <__pstl/cpu_algos/cpu_traits.h>
 #include <__pstl/cpu_algos/fill.h>
+#include <__pstl/cpu_algos/find_end.h>
 #include <__pstl/cpu_algos/find_if.h>
 #include <__pstl/cpu_algos/for_each.h>
 #include <__pstl/cpu_algos/merge.h>
@@ -93,6 +94,10 @@ struct __cpu_traits<__std_thread_backend_tag> {
 };
 
 // Mandatory implementations of the computational basis
+template <class _ExecutionPolicy>
+struct __find_end<__std_thread_backend_tag, _ExecutionPolicy>
+    : __cpu_parallel_find_end<__std_thread_backend_tag, _ExecutionPolicy> {};
+
 template <class _ExecutionPolicy>
 struct __find_if<__std_thread_backend_tag, _ExecutionPolicy>
     : __cpu_parallel_find_if<__std_thread_backend_tag, _ExecutionPolicy> {};
diff --git a/libcxx/include/__pstl/cpu_algos/find_end.h b/libcxx/include/__pstl/cpu_algos/find_end.h
new file mode 100644
index 0000000000000..5beef0fc068e9
--- /dev/null
+++ b/libcxx/include/__pstl/cpu_algos/find_end.h
@@ -0,0 +1,102 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___PSTL_CPU_ALGOS_FIND_END_H
+#define _LIBCPP___PSTL_CPU_ALGOS_FIND_END_H
+
+#include <__algorithm/find_end.h>
+#include <__config>
+#include <__functional/operations.h>
+#include <__iterator/concepts.h>
+#include <__iterator/iterator_traits.h>
+#include <__optional/nullopt_t.h>
+#include <__optional/optional.h>
+#include <__pstl/backend_fwd.h>
+#include <__pstl/cpu_algos/cpu_traits.h>
+#include <__pstl/cpu_algos/find_if.h>
+#include <__type_traits/is_execution_policy.h>
+#include <__utility/convert_to_integral.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+#if _LIBCPP_STD_VER >= 17
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+namespace __pstl {
+
+template <class _Backend, class _RawExecutionPolicy>
+struct __cpu_parallel_find_end {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _BinaryPredicate>
+  _LIBCPP_HIDE_FROM_ABI optional<_ForwardIterator1>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _BinaryPredicate __pred) const noexcept {
+    if constexpr (__is_parallel_execution_policy_v<_RawExecutionPolicy> &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator1>::value &&
+                  __has_random_access_iterator_category_or_concept<_ForwardIterator2>::value) {
+      typedef typename std::iterator_traits<_ForwardIterator1>::difference_type _DifferenceType;
+      _DifferenceType __size2 = __last2 - __first2; // The length of the needle to search for.
+      if (__size2 == 0) {
+        return __last1; // If the needle length is zero, the last iterator is returned.
+      }
+      _DifferenceType __size1 = __last1 - __first1;
+      if (__size1 < __size2) {
+        return __last1; // The range is too small to contain the requested number of consecutive elements.
+      }
+      // Calculate the length of the tail where a potential match cannot start by definition.
+      _DifferenceType __crop = __size2 - 1;
+      // We're only interested in the range where a potential match can start: [first, last - crop)
+      _ForwardIterator1 __last1_cropped = __last1 - __crop;
+      // Run a parallel chunked find_if, covering the range where a potential match can start.
+      auto __res = __pstl::__parallel_find<_Backend>(
+          __first1,
+          __last1_cropped,
+          [__first2, __last2, __crop, &__pred](_ForwardIterator1 __brick_first, _ForwardIterator1 __brick_last) {
+            // Uncrop the range to allow std::find_end to find a full match, which can go beyond __brick_last.
+            _ForwardIterator1 __brick_last_uncropped = __brick_last + __crop;
+            // Run a serial std::find_end inside each of the chunks in parallel.
+            _ForwardIterator1 __ret = std::find_end(__brick_first, __brick_last_uncropped, __first2, __last2, __pred);
+            // The returned iterator is either a match inside [__brick_first, __brick_last) or a miss encoded as
+            // __brick_last_uncropped. Return the miss as __brick_last to conform to expectations of __parallel_find().
+            return __ret == __brick_last_uncropped ? __brick_last : __ret;
+          },
+          greater<>{}, // `greater` here means the highest index among the matches
+          false        // `false` here means we want the last match, not the first
+      );
+      if (!__res) {
+        return std::nullopt; // Failed to run the algorithm, propagate the error.
+      }
+      if (*__res == __last1_cropped) {
+        return __last1; // No match was found in the range.
+      }
+      return *__res; // Return the successful match.
+    } else {
+      // Non-random access iterators cannot be processed in parallel, fall back to the sequential implementation.
+      return std::find_end(
+          std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__pred));
+    }
+  }
+};
+
+} // namespace __pstl
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___PSTL_CPU_ALGOS_FIND_END_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 4512cc71153ca..72d5239fdd4da 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -2421,6 +2421,9 @@ module std {
         header "__pstl/cpu_algos/fill.h"
         export std_core.utility_core.empty
       }
+      module find_end {
+        header "__pstl/cpu_algos/find_end.h"
+      }
       module find_if {
         header "__pstl/cpu_algos/find_if.h"
       }
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 7e9fa5cfacc30..3c55c872a6e76 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -99,6 +99,15 @@ void f(non_forward_iterator non_fwd,
     (void)std::find_if_not(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: find_if_not}}
   }
 
+  {
+    (void)std::find_end(pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(
+        pol, it, it, non_fwd, non_fwd, pred);           // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: find_end}}
+    (void)std::find_end(
+        pol, non_fwd, non_fwd, it, it, pred); // expected-error@*:* {{static assertion failed: find_end}}
+  }
+
   {
     (void)std::find_first_of(
         pol, it, it, non_fwd, non_fwd); // expected-error@*:* {{static assertion failed: find_first_of}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 61758770102f3..8398bddade79f 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -42,6 +42,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::equal(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_end(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::find_end(std::execution::par, std::begin(a), std::end(a), std::begin(b), std::end(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find_if(std::execution::par, std::begin(a), std::end(a), pred);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find_if_not(std::execution::par, std::begin(a), std::end(a), pred);
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp
new file mode 100644
index 0000000000000..7b7c6acf3d95d
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end.pass.cpp
@@ -0,0 +1,268 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
+//   ForwardIterator1
+//   find_end(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1, ForwardIterator1 last1,
+//            ForwardIterator2 first2, ForwardIterator2 last2);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_end);
+
+static_assert(sfinae_test_find_end<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_find_end<std::execution::parallel_policy, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      static_assert(std::is_same_v<decltype(res), Iter1>);
+    }
+    { // Empty haystack, empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::begin(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Empty haystack, non-empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Non-empty haystack, empty needle
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::begin(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Both single element, same
+      int a[] = {0};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Both single element, different
+      int a[] = {0};
+      int b[] = {1};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle found at the beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Needle found in the middle
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {1};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 1));
+    }
+    { // Needle found at the end
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {5};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a) - 1));
+    }
+    { // Multiple element needle found at the beginning
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {0, 1, 2};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Multiple element needle found in the middle
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {2, 3, 4};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 2));
+    }
+    { // Multiple element needle found at the end
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {3, 4, 5};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Needle too long for haystack
+      int a[] = {0, 1, 2, 3, 4};
+      int b[] = {0, 1, 2, 3, 4, 5};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle much longer than haystack
+      int a[] = {0};
+      int b[] = {0, 1, 2, 3, 4, 5};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Multiple needles with size=1, the last one is found
+      int a[] = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[] = {1};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Multiple needles with size=2, the last one is found
+      int a[] = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[] = {1, 2};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Multiple needles with size=3, the last one is found
+      int a[] = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[] = {1, 2, 3};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Single needle with size=4, found at the end
+      int a[] = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[] = {1, 2, 3, 4};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Needle with repeated elements in the prefix
+      int a[] = {0, 1, 1, 1, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[] = {1, 1, 2};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Long needle, found at the end
+      int a[] = {0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0};
+      int b[] = {0, 0, 0, 0, 1, 1, 1, 1, 0, 0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 6));
+    }
+    { // Needle not found - no match exists
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {6};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle not found, size=3
+      int a[] = {0, 1, 2, 3, 4, 5};
+      int b[] = {4, 5, 6};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Partial match, fails at the last element
+      int a[] = {0, 1, 2, 3, 4, 5, 6};
+      int b[] = {3, 4, 6};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Partial match, fails in the middle
+      int a[] = {0, 1, 2, 4, 5};
+      int b[] = {1, 3, 4};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::end(a)));
+    }
+    { // All elements identical in haystack
+      int a[] = {7, 7, 7, 7, 7, 7};
+      int b[] = {7, 7};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 4));
+    }
+    { // Multiple partial matches before real match
+      int a[] = {1, 2, 1, 2, 1, 2, 3};
+      int b[] = {1, 2, 3};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 4));
+    }
+    { // Alternating pattern
+      int a[] = {0, 1, 0, 1, 0, 1, 0, 1};
+      int b[] = {1, 0, 1, 0};
+      auto res =
+          std::find_end(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)));
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Many elements, needles found at various sampled positions, last one is picked
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 0);
+      int needle1[] = {1};
+      int needle2[] = {1, 0, 0};
+      int needle3[] = {1, 0, 0, 0, 0};
+      runway_sample(std::size(a) - std::size(needle3) + 1, [&](std::size_t i) {
+        a[i] = 1;
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle1)),
+                             Iter2(std::end(needle1))) == Iter1(std::begin(a) + i));
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle2)),
+                             Iter2(std::end(needle2))) == Iter1(std::begin(a) + i));
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle3)),
+                             Iter2(std::end(needle3))) == Iter1(std::begin(a) + i));
+        // a[i] is not reverted back to ensure there're multiple possible matches
+      });
+    }
+  }
+};
+
+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/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end_pred.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end_pred.pass.cpp
new file mode 100644
index 0000000000000..55e4290b583c1
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.end/pstl.find_end_pred.pass.cpp
@@ -0,0 +1,276 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class BinaryPredicate>
+//   ForwardIterator1
+//   find_end(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1, ForwardIterator1 last1,
+//            ForwardIterator2 first2, ForwardIterator2 last2,
+//            BinaryPredicate p);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "runway_sample.h"
+
+EXECUTION_POLICY_SFINAE_TEST(find_end);
+
+static_assert(sfinae_test_find_end<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_find_end<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+struct Pred {
+  bool operator()(int lhs, int rhs) const { return lhs + 10 == rhs; }
+};
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[]  = {0};
+      int b[]  = {0};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      static_assert(std::is_same_v<decltype(res), Iter1>);
+    }
+    { // Empty haystack, empty needle
+      int a[]  = {0};
+      int b[]  = {0};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::begin(b)), Pred{});
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Empty haystack, non-empty needle
+      int a[]  = {0};
+      int b[]  = {0};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Non-empty haystack, empty needle
+      int a[]  = {0};
+      int b[]  = {0};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::begin(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Both single element, same
+      int a[]  = {0};
+      int b[]  = {10};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Both single element, different
+      int a[]  = {0};
+      int b[]  = {11};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle found at the beginning
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {10};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Needle found in the middle
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {11};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 1));
+    }
+    { // Needle found at the end
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {15};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a) - 1));
+    }
+    { // Multiple element needle found at the beginning
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {10, 11, 12};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a)));
+    }
+    { // Multiple element needle found in the middle
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {12, 13, 14};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 2));
+    }
+    { // Multiple element needle found at the end
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {13, 14, 15};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Needle too long for haystack
+      int a[]  = {0, 1, 2, 3, 4};
+      int b[]  = {10, 11, 12, 13, 14, 15};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle much longer than haystack
+      int a[]  = {0};
+      int b[]  = {10, 11, 12, 13, 14, 15};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Multiple needles with size=1, the last one is found
+      int a[]  = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[]  = {11};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Multiple needles with size=2, the last one is found
+      int a[]  = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[]  = {11, 12};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Multiple needles with size=3, the last one is found
+      int a[]  = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[]  = {11, 12, 13};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Single needle with size=4, found at the end
+      int a[]  = {0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[]  = {11, 12, 13, 14};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 8));
+    }
+    { // Needle with repeated elements in the prefix
+      int a[]  = {0, 1, 1, 1, 1, 2, 3, 0, 1, 2, 3, 4};
+      int b[]  = {11, 11, 12};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Long needle, found at the end
+      int a[]  = {0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0};
+      int b[]  = {10, 10, 10, 10, 11, 11, 11, 11, 10, 10};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 6));
+    }
+    { // Needle not found - no match exists
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {16};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Needle not found, size=3
+      int a[]  = {0, 1, 2, 3, 4, 5};
+      int b[]  = {14, 15, 16};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Partial match, fails at the last element
+      int a[]  = {0, 1, 2, 3, 4, 5, 6};
+      int b[]  = {13, 14, 16};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // Partial match, fails in the middle
+      int a[]  = {0, 1, 2, 4, 5};
+      int b[]  = {11, 13, 14};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::end(a)));
+    }
+    { // All elements identical in haystack
+      int a[]  = {7, 7, 7, 7, 7, 7};
+      int b[]  = {17, 17};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 4));
+    }
+    { // Multiple partial matches before real match
+      int a[]  = {1, 2, 1, 2, 1, 2, 3};
+      int b[]  = {11, 12, 13};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 4));
+    }
+    { // Alternating pattern
+      int a[]  = {0, 1, 0, 1, 0, 1, 0, 1};
+      int b[]  = {11, 10, 11, 10};
+      auto res = std::find_end(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), Pred{});
+      assert(res == Iter1(std::begin(a) + 3));
+    }
+    { // Many elements, needles found at various sampled positions, last one is picked
+      int a[1073];
+      std::fill(std::begin(a), std::end(a), 0);
+      int needle1[] = {11};
+      int needle2[] = {11, 10, 10};
+      int needle3[] = {11, 10, 10, 10, 10};
+      runway_sample(std::size(a) - std::size(needle3) + 1, [&](std::size_t i) {
+        a[i] = 1;
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle1)),
+                             Iter2(std::end(needle1)),
+                             Pred{}) == Iter1(std::begin(a) + i));
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle2)),
+                             Iter2(std::end(needle2)),
+                             Pred{}) == Iter1(std::begin(a) + i));
+        assert(std::find_end(policy,
+                             Iter1(std::begin(a)),
+                             Iter1(std::end(a)),
+                             Iter2(std::begin(needle3)),
+                             Iter2(std::end(needle3)),
+                             Pred{}) == Iter1(std::begin(a) + i));
+        // a[i] is not reverted back to ensure there're multiple possible matches
+      });
+    }
+  }
+};
+
+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/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index b1d6d831ef61f..9a7dd08905815 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -184,6 +184,20 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
+
+        // find_end(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_end(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // find_end(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::find_end(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), pred);
+        });
+      }
+
       {
         auto pred = maybe_throw(tokens[5], [](int x, int y) -> bool { return x == y; });
 



More information about the libcxx-commits mailing list