[libcxx-commits] [libcxx] [libc++][pstl] Add serial implementations of set operations to PSTL (PR #233783)

Michael G. Kazakov via libcxx-commits libcxx-commits at lists.llvm.org
Tue Oct 20 08:49:44 PDT 2026


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

>From 7b70cd00e8907505f3881001abd7c44232951a5c Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 11:44:37 +0100
Subject: [PATCH 1/6] Added a serial implementation of std::includes to PSTL

---
 libcxx/include/__algorithm/pstl.h             |  48 ++++++
 libcxx/include/__pstl/backend_fwd.h           |   8 +
 libcxx/include/__pstl/backends/serial.h       |  16 ++
 .../pstl.iterator-requirements.verify.cpp     |   6 +
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../includes/pstl.includes.pass.cpp           | 127 +++++++++++++++
 .../includes/pstl.includes_comp.pass.cpp      | 149 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  15 ++
 8 files changed, 373 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 66fa8b3cdd8a00..deb3455d5e5dfa 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -1210,6 +1210,54 @@ is_sorted_until(_ExecutionPolicy&& __policy, _ForwardIterator __first, _ForwardI
       std::forward<_ExecutionPolicy>(__policy), std::move(__first), std::move(__last), std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] bool
+includes(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "includes requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "includes requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__includes, __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),
+      less{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] bool
+includes(_ExecutionPolicy&& __policy,
+         _ForwardIterator1 __first1,
+         _ForwardIterator1 __last1,
+         _ForwardIterator2 __first2,
+         _ForwardIterator2 __last2,
+         _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "includes requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "includes requires ForwardIterators");
+  using _Implementation = __pstl::__dispatch<__pstl::__includes, __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(__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 8bb23f352c9235..953e833e142eca 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -501,6 +501,14 @@ struct __uninitialized_fill_n;
 // optional<_ForwardIterator>
 // operator()(_Policy&& __policy, _ForwardIterator __first, _Size __n, const _Tp &__value) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __includes;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Compare>
+// optional<bool>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _Compare __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index c06e4d31ff3cba..827e7bf5ecaec0 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -13,6 +13,7 @@
 #include <__algorithm/find_end.h>
 #include <__algorithm/find_if.h>
 #include <__algorithm/for_each.h>
+#include <__algorithm/includes.h>
 #include <__algorithm/is_heap_until.h>
 #include <__algorithm/merge.h>
 #include <__algorithm/min_element.h>
@@ -313,6 +314,21 @@ struct __uninitialized_move<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __includes<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _Compare>
+  optional<bool>
+  operator()(_Policy&&,
+             _ForwardIterator1 __first1,
+             _ForwardIterator1 __last1,
+             _ForwardIterator2 __first2,
+             _ForwardIterator2 __last2,
+             _Compare __comp) const noexcept {
+    return std::includes(
+        std::move(__first1), std::move(__last1), std::move(__first2), std::move(__last2), std::move(__comp));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index d99869ce73f103..ac093a441c663f 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -152,6 +152,12 @@ void f(non_forward_iterator non_fwd,
     (void)std::reverse_copy(pol, it, it, non_fwd); // expected-error@*:* {{static assertion failed: reverse_copy}}
   }
 
+  {
+    (void)std::includes(pol, non_fwd, non_fwd, it, it); // expected-error@*:* {{static assertion failed: includes}}
+    (void)std::includes(
+        pol, it, it, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: includes}}
+  }
+
   {
     (void)std::is_partitioned(
         pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: is_partitioned}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index f36f12a135fbaf..54c7ac4c0f5d75 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -56,6 +56,10 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::find_first_of(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::includes(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::includes(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::is_heap(std::execution::par, std::begin(a), std::end(a));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::is_heap(std::execution::par, std::begin(a), std::end(a), pred2);
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
new file mode 100644
index 00000000000000..a7f69c4faa5922
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
@@ -0,0 +1,127 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, ForwardIterator2>
+//   bool
+//   includes(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1,
+//            ForwardIterator1 last1,
+//            ForwardIterator2 first2,
+//            ForwardIterator2 last2);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(includes);
+
+static_assert(sfinae_test_includes<int, int*, int*, int*, int*>);
+static_assert(!sfinae_test_includes<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[] = {1};
+      auto res =
+          std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::end(a)));
+      static_assert(std::is_same_v<decltype(res), bool>);
+    }
+    { // Both ranges are empty
+      int a[] = {1};
+      int b[] = {1};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::begin(b))));
+    }
+    { // Empty haystack, non-empty needly
+      int a[] = {1};
+      int b[] = {1};
+      assert(
+          !std::includes(policy, Iter1(std::begin(a)), Iter1(std::begin(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Non-empty haystack, empty needle
+      int a[] = {1};
+      int b[] = {1};
+      assert(
+          std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::begin(b))));
+    }
+    { // Haystack and needle are the same
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::end(a))));
+    }
+    { // Needle is a sparse subset
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      int b[] = {2, 4};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Superset is not included in its subset
+      int a[] = {2, 4};
+      int b[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      assert(
+          !std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Haystack and needle are equal
+      int a[] = {1, 2};
+      int b[] = {1, 2};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Partial overlap
+      int a[] = {1, 2};
+      int b[] = {2, 4};
+      assert(
+          !std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Needle is one element present multiple times
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      int b[] = {3};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Needle is two-element present multiple times
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      int b[] = {3, 3};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Needle is three-element duplicate present once
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      int b[] = {3, 3, 3};
+      assert(std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+    { // Needle is three-element duplicate not present
+      int a[] = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
+      int b[] = {3, 3, 3, 3};
+      assert(
+          !std::includes(policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b))));
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const int*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
new file mode 100644
index 00000000000000..79bddbbd2f1a05
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, ForwardIterator2, class Comp>
+//   bool
+//   includes(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1,
+//            ForwardIterator1 last1,
+//            ForwardIterator2 first2,
+//            ForwardIterator2 last2,
+//            Comp compare);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+
+EXECUTION_POLICY_SFINAE_TEST(includes);
+
+static_assert(sfinae_test_includes<int, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_includes<std::execution::parallel_policy, int*, int*, int*, int*, bool (*)(int, int)>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    { // Check the return type
+      int a[]  = {1};
+      auto res = std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::end(a)), std::greater{});
+      static_assert(std::is_same_v<decltype(res), bool>);
+    }
+    { // Both ranges are empty
+      int a[] = {1};
+      int b[] = {1};
+      assert(std::includes(
+          policy,
+          Iter1(std::begin(a)),
+          Iter1(std::begin(a)),
+          Iter2(std::begin(b)),
+          Iter2(std::begin(b)),
+          std::greater{}));
+    }
+    { // Empty haystack, non-empty needly
+      int a[] = {1};
+      int b[] = {1};
+      assert(!std::includes(
+          policy,
+          Iter1(std::begin(a)),
+          Iter1(std::begin(a)),
+          Iter2(std::begin(b)),
+          Iter2(std::end(b)),
+          std::greater{}));
+    }
+    { // Non-empty haystack, empty needle
+      int a[] = {1};
+      int b[] = {1};
+      assert(std::includes(
+          policy,
+          Iter1(std::begin(a)),
+          Iter1(std::end(a)),
+          Iter2(std::begin(b)),
+          Iter2(std::begin(b)),
+          std::greater{}));
+    }
+    { // Haystack and needle are the same
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(a)), Iter2(std::end(a)), std::greater{}));
+    }
+    { // Needle is a sparse subset
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      int b[] = {4, 2};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Superset is not included in its subset
+      int a[] = {4, 2};
+      int b[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      assert(!std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Haystack and needle are equal
+      int a[] = {2, 1};
+      int b[] = {2, 1};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Partial overlap
+      int a[] = {2, 1};
+      int b[] = {4, 2};
+      assert(!std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Needle is one element present multiple times
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      int b[] = {3};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Needle is two-element present multiple times
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      int b[] = {3, 3};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Needle is three-element duplicate present once
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      int b[] = {3, 3, 3};
+      assert(std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+    { // Needle is three-element duplicate not present
+      int a[] = {4, 4, 4, 4, 3, 3, 3, 2, 2, 1};
+      int b[] = {3, 3, 3, 3};
+      assert(!std::includes(
+          policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::greater{}));
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const int*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const 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 64f7b396813055..04f22c495daf5d 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -595,6 +595,21 @@ int main(int, char**) {
           (void)std::swap_ranges(policy, std::move(first1), std::move(last1), std::move(first2));
         });
       }
+
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // includes(first1, last1, first2, last2)
+        assert_non_throwing([=, &policy] {
+          (void)std::includes(policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2));
+        });
+
+        // includes(first1, last1, first2, last2, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::includes(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
+        });
+      }
     }
   });
 

>From d3e652272b052278988a6eeafd38490288fac909 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 12:06:25 +0100
Subject: [PATCH 2/6] Missing include

---
 .../alg.set.operations/includes/pstl.includes_comp.pass.cpp      | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
index 79bddbbd2f1a05..09837fed5e7c6c 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
@@ -24,6 +24,7 @@
 #include <cstddef>
 #include <algorithm>
 #include <cassert>
+#include <functional>
 #include <iterator>
 #include <numeric>
 #include <random>

>From 6ed74e7363ae43238ed183c97ab282aa7964215a Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 13:27:37 +0100
Subject: [PATCH 3/6] Added std::set_union to the serial backend

---
 libcxx/include/__algorithm/pstl.h             |  58 +++++++++
 libcxx/include/__pstl/backend_fwd.h           |   9 ++
 libcxx/include/__pstl/backends/serial.h       |  22 ++++
 .../pstl.iterator-requirements.verify.cpp     |  15 +++
 .../algorithms/pstl.nodiscard.verify.cpp      |   4 +
 .../includes/pstl.includes.pass.cpp           |   2 +-
 .../includes/pstl.includes_comp.pass.cpp      |   2 +-
 .../set.union/pstl.set_union.pass.cpp         |  88 ++++++++++++++
 .../set.union/pstl.set_union_comp.pass.cpp    | 111 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  22 ++++
 10 files changed, 331 insertions(+), 2 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union_comp.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index deb3455d5e5dfa..2d72e5e4445129 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -1258,6 +1258,64 @@ includes(_ExecutionPolicy&& __policy,
       std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator
+set_union(_ExecutionPolicy&& __policy,
+          _ForwardIterator1 __first1,
+          _ForwardIterator1 __last1,
+          _ForwardIterator2 __first2,
+          _ForwardIterator2 __last2,
+          _ForwardOutIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_union requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_union requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_union requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_union, __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(__result),
+      less{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator
+set_union(_ExecutionPolicy&& __policy,
+          _ForwardIterator1 __first1,
+          _ForwardIterator1 __last1,
+          _ForwardIterator2 __first2,
+          _ForwardIterator2 __last2,
+          _ForwardOutIterator __result,
+          _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_union requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_union requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_union requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_union, __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(__result),
+      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 953e833e142eca..4f48a343377a00 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -509,6 +509,15 @@ struct __includes;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Compare __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __set_union;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+// optional<_ForwardOutIterator>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _ForwardOutIterator __result,
+//                                _Compare __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 827e7bf5ecaec0..468fa5c8891e0e 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -22,6 +22,7 @@
 #include <__algorithm/reverse.h>
 #include <__algorithm/search.h>
 #include <__algorithm/search_n.h>
+#include <__algorithm/set_union.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/swap_ranges.h>
 #include <__algorithm/transform.h>
@@ -329,6 +330,27 @@ struct __includes<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __set_union<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+  optional<_ForwardOutIterator> operator()(
+      _Policy&&,
+      _ForwardIterator1 __first1,
+      _ForwardIterator1 __last1,
+      _ForwardIterator2 __first2,
+      _ForwardIterator2 __last2,
+      _ForwardOutIterator __result,
+      _Compare __comp) const noexcept {
+    return std::set_union(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::move(__result),
+        std::move(__comp));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index ac093a441c663f..33ea8813039a7a 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -275,6 +275,21 @@ void f(non_forward_iterator non_fwd,
     (void)std::search_n(pol, non_fwd, non_fwd, 1, 1, pred); // expected-error@*:* {{static assertion failed: search_n}}
   }
 
+  {
+    (void)std::set_union(
+        pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_union}}
+    (void)std::set_union(
+        pol, it, it, non_fwd, non_fwd, it);                // expected-error@*:* {{static assertion failed: set_union}}
+    (void)std::set_union(pol, it, it, it, it, non_output); // expected-error@*:* {{static assertion failed: set_union}}
+
+    (void)std::set_union(
+        pol, non_fwd, non_fwd, it, it, it, pred); // expected-error@*:* {{static assertion failed: set_union}}
+    (void)std::set_union(
+        pol, it, it, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: set_union}}
+    (void)std::set_union(
+        pol, it, it, it, it, non_output, pred); // expected-error@*:* {{static assertion failed: set_union}}
+  }
+
   {
     (void)std::sort(pol, non_fwd, non_fwd);       // expected-error@*:* {{static assertion failed: sort}}
     (void)std::sort(pol, non_fwd, non_fwd, pred); // expected-error@*:* {{static assertion failed: sort}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 54c7ac4c0f5d75..7e8e635b1bbcd7 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -119,4 +119,8 @@ void test() {
   std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1, pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_union(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_union(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
 }
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
index a7f69c4faa5922..1c6e339e0179ed 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes.pass.cpp
@@ -12,7 +12,7 @@
 
 // <algorithm>
 
-// template<class ExecutionPolicy, class ForwardIterator1, ForwardIterator2>
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2>
 //   bool
 //   includes(ExecutionPolicy&& exec,
 //            ForwardIterator1 first1,
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
index 09837fed5e7c6c..742c3c7aa1ef35 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/includes/pstl.includes_comp.pass.cpp
@@ -12,7 +12,7 @@
 
 // <algorithm>
 
-// template<class ExecutionPolicy, class ForwardIterator1, ForwardIterator2, class Comp>
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2, class Comp>
 //   bool
 //   includes(ExecutionPolicy&& exec,
 //            ForwardIterator1 first1,
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
new file mode 100644
index 00000000000000..4a697c7a41eff5
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardOutIterator>
+//   ForwardOutIterator
+//   set_union(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1,
+//            ForwardIterator1 last1,
+//            ForwardIterator2 first2,
+//            ForwardIterator2 last2,
+//            ForwardOutIterator result);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_union);
+
+static_assert(sfinae_test_set_union<int, int*, int*, int*, int*, int*>);
+static_assert(!sfinae_test_set_union<std::execution::parallel_policy, int*, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      { // Check the result type
+        TrivialSortable a[]  = {0};
+        TrivialSortable b[]  = {0};
+        TrivialSortable c[2] = {};
+        auto res             = std::set_union(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        TrivialSortable a[]                    = {11, 33, 31, 41};
+        TrivialSortable b[]                    = {22, 32, 43, 42, 52};
+        TrivialSortable expected[]             = {11, 22, 33, 31, 41, 42, 52};
+        TrivialSortable c[std::size(expected)] = {};
+        auto res                               = std::set_union(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+      { // First is longer
+        TrivialSortable a[]                    = {22, 32, 43, 42, 52};
+        TrivialSortable b[]                    = {11, 33, 31, 41};
+        TrivialSortable expected[]             = {11, 22, 32, 31, 43, 42, 52};
+        TrivialSortable c[std::size(expected)] = {};
+        auto res                               = std::set_union(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const TrivialSortable*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const TrivialSortable*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union_comp.pass.cpp
new file mode 100644
index 00000000000000..4760fbe7c2a66d
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union_comp.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+//          class ForwardOutIterator, class Compare>
+//   ForwardOutIterator
+//   set_union(ExecutionPolicy&& exec,
+//            ForwardIterator1 first1,
+//            ForwardIterator1 last1,
+//            ForwardIterator2 first2,
+//            ForwardIterator2 last2,
+//            ForwardOutIterator result,
+//            Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_union);
+
+static_assert(sfinae_test_set_union<int, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(
+    !sfinae_test_set_union<std::execution::parallel_policy, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T   = TrivialSortableWithComp;
+      auto comp = T::Comparator{};
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_union(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 22, 33, 31, 41, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_union(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {11, 22, 32, 31, 43, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_union(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::forward_iterator_list<const TrivialSortableWithComp*>{}, types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<const TrivialSortableWithComp*>{},
+                        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 04f22c495daf5d..7167aad293ec6a 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -610,6 +610,28 @@ int main(int, char**) {
               policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), compare);
         });
       }
+
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // set_union(first1, last1, first2, last2, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_union(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
+        });
+
+        // set_union(first1, last1, first2, last2, dest, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_union(
+              policy,
+              std::move(first1),
+              std::move(last1),
+              std::move(first2),
+              std::move(last2),
+              std::move(dest),
+              compare);
+        });
+      }
     }
   });
 

>From 5881801b584798864ebc224a8087c0f58598b5d8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 15:28:14 +0100
Subject: [PATCH 4/6] Added std::set_intersection

---
 libcxx/include/__algorithm/pstl.h             |  58 +++++++++
 libcxx/include/__pstl/backend_fwd.h           |   9 ++
 libcxx/include/__pstl/backends/serial.h       |  22 ++++
 .../pstl.iterator-requirements.verify.cpp     |  16 +++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.set_intersection.pass.cpp            |  89 ++++++++++++++
 .../pstl.set_intersection_comp.pass.cpp       | 111 ++++++++++++++++++
 .../set.union/pstl.set_union.pass.cpp         |  29 ++---
 .../pstl.exception_handling.pass.cpp          |  22 ++++
 9 files changed, 347 insertions(+), 14 deletions(-)
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection_comp.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 2d72e5e4445129..db2cc42c040149 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -1316,6 +1316,64 @@ set_union(_ExecutionPolicy&& __policy,
       std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_intersection(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_intersection requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_intersection requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_intersection requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_intersection, __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(__result),
+      less{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_intersection(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result,
+    _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_intersection requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_intersection requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_intersection requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_intersection, __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(__result),
+      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 4f48a343377a00..24d9dab8153a24 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -518,6 +518,15 @@ struct __set_union;
 //                                _ForwardOutIterator __result,
 //                                _Compare __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __set_intersection;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+// optional<_ForwardOutIterator>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _ForwardOutIterator __result,
+//                                _Compare __comp) const noexcept;
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 468fa5c8891e0e..4605250dff7333 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -22,6 +22,7 @@
 #include <__algorithm/reverse.h>
 #include <__algorithm/search.h>
 #include <__algorithm/search_n.h>
+#include <__algorithm/set_intersection.h>
 #include <__algorithm/set_union.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/swap_ranges.h>
@@ -351,6 +352,27 @@ struct __set_union<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __set_intersection<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+  optional<_ForwardOutIterator> operator()(
+      _Policy&&,
+      _ForwardIterator1 __first1,
+      _ForwardIterator1 __last1,
+      _ForwardIterator2 __first2,
+      _ForwardIterator2 __last2,
+      _ForwardOutIterator __result,
+      _Compare __comp) const noexcept {
+    return std::set_intersection(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::move(__result),
+        std::move(__comp));
+  }
+};
+
 } // namespace __pstl
 _LIBCPP_END_NAMESPACE_STD
 
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 33ea8813039a7a..e5c8e5a23b97a4 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -275,6 +275,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::search_n(pol, non_fwd, non_fwd, 1, 1, pred); // expected-error@*:* {{static assertion failed: search_n}}
   }
 
+  {
+    (void)std::set_intersection(
+        pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_intersection}}
+    (void)std::set_intersection(
+        pol, it, it, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: set_intersection}}
+    (void)std::set_intersection(
+        pol, it, it, it, it, non_output); // expected-error@*:* {{static assertion failed: set_intersection}}
+
+    (void)std::set_intersection(
+        pol, non_fwd, non_fwd, it, it, it, pred); // expected-error@*:* {{static assertion failed: set_intersection}}
+    (void)std::set_intersection(
+        pol, it, it, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: set_intersection}}
+    (void)std::set_intersection(
+        pol, it, it, it, it, non_output, pred); // expected-error@*:* {{static assertion failed: set_intersection}}
+  }
+
   {
     (void)std::set_union(
         pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_union}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 7e8e635b1bbcd7..51c6d58d354aeb 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -120,6 +120,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1, pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_intersection(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_intersection(
+      std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_union(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_union(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection.pass.cpp
new file mode 100644
index 00000000000000..374c8e8dc59af1
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardOutIterator>
+//   ForwardOutIterator
+//   set_intersection(ExecutionPolicy&& exec,
+//                    ForwardIterator1 first1,
+//                    ForwardIterator1 last1,
+//                    ForwardIterator2 first2,
+//                    ForwardIterator2 last2,
+//                    ForwardOutIterator result);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_intersection);
+
+static_assert(sfinae_test_set_intersection<int, int*, int*, int*, int*, int*>);
+static_assert(!sfinae_test_set_intersection<std::execution::parallel_policy, int*, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T = TrivialSortable;
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_intersection(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {33, 41};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_intersection(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {32, 42};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_intersection(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const TrivialSortable*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const TrivialSortable*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection_comp.pass.cpp
new file mode 100644
index 00000000000000..ca3e6c6d4ee8d3
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.intersection/pstl.set_intersection_comp.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+//          class ForwardOutIterator, class Compare>
+//   ForwardOutIterator
+//   set_intersection(ExecutionPolicy&& exec,
+//                    ForwardIterator1 first1,
+//                    ForwardIterator1 last1,
+//                    ForwardIterator2 first2,
+//                    ForwardIterator2 last2,
+//                    ForwardOutIterator result,
+//                    Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_intersection);
+
+static_assert(sfinae_test_set_intersection<int, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(
+    !sfinae_test_set_intersection<std::execution::parallel_policy, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T   = TrivialSortableWithComp;
+      auto comp = T::Comparator{};
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_intersection(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {33, 41};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_intersection(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {32, 42};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_intersection(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::forward_iterator_list<const TrivialSortableWithComp*>{}, types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<const TrivialSortableWithComp*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+      }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
index 4a697c7a41eff5..9fcb9584deb6ed 100644
--- a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.union/pstl.set_union.pass.cpp
@@ -45,30 +45,31 @@ struct Test {
   template <class ExecutionPolicy>
   void operator()(ExecutionPolicy&& policy) {
     {
+      using T = TrivialSortable;
       { // Check the result type
-        TrivialSortable a[]  = {0};
-        TrivialSortable b[]  = {0};
-        TrivialSortable c[2] = {};
-        auto res             = std::set_union(
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_union(
             policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
         static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
       }
       { // First is shorter
-        TrivialSortable a[]                    = {11, 33, 31, 41};
-        TrivialSortable b[]                    = {22, 32, 43, 42, 52};
-        TrivialSortable expected[]             = {11, 22, 33, 31, 41, 42, 52};
-        TrivialSortable c[std::size(expected)] = {};
-        auto res                               = std::set_union(
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 22, 33, 31, 41, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_union(
             policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
         assert(res == std::end(c));
         assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
       }
       { // First is longer
-        TrivialSortable a[]                    = {22, 32, 43, 42, 52};
-        TrivialSortable b[]                    = {11, 33, 31, 41};
-        TrivialSortable expected[]             = {11, 22, 32, 31, 43, 42, 52};
-        TrivialSortable c[std::size(expected)] = {};
-        auto res                               = std::set_union(
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {11, 22, 32, 31, 43, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_union(
             policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
         assert(res == std::end(c));
         assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
diff --git a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
index 7167aad293ec6a..5cb62edae04a64 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -611,6 +611,28 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // set_intersection(first1, last1, first2, last2, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_intersection(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
+        });
+
+        // set_intersection(first1, last1, first2, last2, dest, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_intersection(
+              policy,
+              std::move(first1),
+              std::move(last1),
+              std::move(first2),
+              std::move(last2),
+              std::move(dest),
+              compare);
+        });
+      }
+
       {
         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
 

>From 0fc3b2f40e1245e1fad1f27f7505be16f72e5cb1 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 15:55:14 +0100
Subject: [PATCH 5/6] Added std::set_difference

---
 libcxx/include/__algorithm/pstl.h             |  58 +++++++++
 libcxx/include/__pstl/backend_fwd.h           |   9 ++
 libcxx/include/__pstl/backends/serial.h       |  22 ++++
 .../pstl.iterator-requirements.verify.cpp     |  16 +++
 .../algorithms/pstl.nodiscard.verify.cpp      |   5 +
 .../pstl.set_difference.pass.cpp              |  89 ++++++++++++++
 .../pstl.set_difference_comp.pass.cpp         | 111 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  22 ++++
 8 files changed, 332 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference_comp.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index db2cc42c040149..61b7fc8cff014f 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -1374,6 +1374,64 @@ template <class _ExecutionPolicy,
       std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_difference requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_difference, __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(__result),
+      less{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result,
+    _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_difference requires result to be an OutputIterator");
+  using _Implementation = __pstl::__dispatch<__pstl::__set_difference, __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(__result),
+      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 24d9dab8153a24..3ac07b9676c432 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -509,6 +509,15 @@ struct __includes;
 //                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
 //                                _Compare __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __set_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+// optional<_ForwardOutIterator>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _ForwardOutIterator __result,
+//                                _Compare __comp) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __set_union;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 4605250dff7333..8fffc006b1c902 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -22,6 +22,7 @@
 #include <__algorithm/reverse.h>
 #include <__algorithm/search.h>
 #include <__algorithm/search_n.h>
+#include <__algorithm/set_difference.h>
 #include <__algorithm/set_intersection.h>
 #include <__algorithm/set_union.h>
 #include <__algorithm/stable_sort.h>
@@ -331,6 +332,27 @@ struct __includes<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __set_difference<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+  optional<_ForwardOutIterator> operator()(
+      _Policy&&,
+      _ForwardIterator1 __first1,
+      _ForwardIterator1 __last1,
+      _ForwardIterator2 __first2,
+      _ForwardIterator2 __last2,
+      _ForwardOutIterator __result,
+      _Compare __comp) const noexcept {
+    return std::set_difference(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::move(__result),
+        std::move(__comp));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __set_union<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index e5c8e5a23b97a4..0bab9532153d28 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -275,6 +275,22 @@ void f(non_forward_iterator non_fwd,
     (void)std::search_n(pol, non_fwd, non_fwd, 1, 1, pred); // expected-error@*:* {{static assertion failed: search_n}}
   }
 
+  {
+    (void)std::set_difference(
+        pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_difference}}
+    (void)std::set_difference(
+        pol, it, it, non_fwd, non_fwd, it); // expected-error@*:* {{static assertion failed: set_difference}}
+    (void)std::set_difference(
+        pol, it, it, it, it, non_output); // expected-error@*:* {{static assertion failed: set_difference}}
+
+    (void)std::set_difference(
+        pol, non_fwd, non_fwd, it, it, it, pred); // expected-error@*:* {{static assertion failed: set_difference}}
+    (void)std::set_difference(
+        pol, it, it, non_fwd, non_fwd, it, pred); // expected-error@*:* {{static assertion failed: set_difference}}
+    (void)std::set_difference(
+        pol, it, it, it, it, non_output, pred); // expected-error@*:* {{static assertion failed: set_difference}}
+  }
+
   {
     (void)std::set_intersection(
         pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_intersection}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 51c6d58d354aeb..41d68a3153b6e0 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -120,6 +120,11 @@ void test() {
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::search_n(std::execution::par, std::begin(a), std::end(a), 1, 1, pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_difference(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_difference(
+      std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_intersection(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_intersection(
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference.pass.cpp
new file mode 100644
index 00000000000000..b1a246b1fcc3ff
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardOutIterator>
+//   ForwardOutIterator
+//   set_difference(ExecutionPolicy&& exec,
+//                    ForwardIterator1 first1,
+//                    ForwardIterator1 last1,
+//                    ForwardIterator2 first2,
+//                    ForwardIterator2 last2,
+//                    ForwardOutIterator result);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_difference);
+
+static_assert(sfinae_test_set_difference<int, int*, int*, int*, int*, int*>);
+static_assert(!sfinae_test_set_difference<std::execution::parallel_policy, int*, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T = TrivialSortable;
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 31};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {22, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const TrivialSortable*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const TrivialSortable*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference_comp.pass.cpp
new file mode 100644
index 00000000000000..be55d754aae9ba
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.difference/pstl.set_difference_comp.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// UNSUPPORTED: libcpp-has-no-incomplete-pstl
+
+// <algorithm>
+
+// template<class ExecutionPolicy, class ForwardIterator1, class ForwardIterator2,
+//          class ForwardOutIterator, class Compare>
+//   ForwardOutIterator
+//   set_difference(ExecutionPolicy&& exec,
+//                  ForwardIterator1 first1,
+//                  ForwardIterator1 last1,
+//                  ForwardIterator2 first2,
+//                  ForwardIterator2 last2,
+//                  ForwardOutIterator result,
+//                  Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_difference);
+
+static_assert(sfinae_test_set_difference<int, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(
+    !sfinae_test_set_difference<std::execution::parallel_policy, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T   = TrivialSortableWithComp;
+      auto comp = T::Comparator{};
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 31};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {22, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::forward_iterator_list<const TrivialSortableWithComp*>{}, types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<const TrivialSortableWithComp*>{},
+                        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 5cb62edae04a64..4f7ed3dee97cda 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -611,6 +611,28 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // set_difference(first1, last1, first2, last2, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_difference(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
+        });
+
+        // set_difference(first1, last1, first2, last2, dest, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_difference(
+              policy,
+              std::move(first1),
+              std::move(last1),
+              std::move(first2),
+              std::move(last2),
+              std::move(dest),
+              compare);
+        });
+      }
+
       {
         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
 

>From ca64836856c86b8a52a10328bd80642e347952a8 Mon Sep 17 00:00:00 2001
From: Michael Kazakov <mike.kazakov at gmail.com>
Date: Tue, 20 Oct 2026 16:49:22 +0100
Subject: [PATCH 6/6] Added std::symmetric_difference

---
 libcxx/include/__algorithm/pstl.h             |  60 +++++++++
 libcxx/include/__pstl/backend_fwd.h           |   9 ++
 libcxx/include/__pstl/backends/serial.h       |  22 ++++
 .../pstl.iterator-requirements.verify.cpp     |  16 +++
 .../algorithms/pstl.nodiscard.verify.cpp      |   6 +
 .../pstl.set_symmetric_difference.pass.cpp    |  89 +++++++++++++
 ...stl.set_symmetric_difference_comp.pass.cpp | 117 ++++++++++++++++++
 .../pstl.exception_handling.pass.cpp          |  22 ++++
 8 files changed, 341 insertions(+)
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference.pass.cpp
 create mode 100644 libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference_comp.pass.cpp

diff --git a/libcxx/include/__algorithm/pstl.h b/libcxx/include/__algorithm/pstl.h
index 61b7fc8cff014f..dc0e5d5a946b19 100644
--- a/libcxx/include/__algorithm/pstl.h
+++ b/libcxx/include/__algorithm/pstl.h
@@ -1432,6 +1432,66 @@ template <class _ExecutionPolicy,
       std::move(__comp));
 }
 
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_symmetric_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_symmetric_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_symmetric_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_symmetric_difference requires result to be an OutputIterator");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__set_symmetric_difference, __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(__result),
+      less{});
+}
+
+template <class _ExecutionPolicy,
+          class _ForwardIterator1,
+          class _ForwardIterator2,
+          class _ForwardOutIterator,
+          class _Compare,
+          class _RawPolicy                                    = __remove_cvref_t<_ExecutionPolicy>,
+          enable_if_t<is_execution_policy_v<_RawPolicy>, int> = 0>
+[[nodiscard]] _ForwardOutIterator set_symmetric_difference(
+    _ExecutionPolicy&& __policy,
+    _ForwardIterator1 __first1,
+    _ForwardIterator1 __last1,
+    _ForwardIterator2 __first2,
+    _ForwardIterator2 __last2,
+    _ForwardOutIterator __result,
+    _Compare __comp) {
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator1, "set_symmetric_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_FORWARD_ITERATOR(_ForwardIterator2, "set_symmetric_difference requires ForwardIterators");
+  _LIBCPP_REQUIRE_CPP17_OUTPUT_ITERATOR(
+      _ForwardOutIterator, decltype(*__first1), "set_symmetric_difference requires result to be an OutputIterator");
+  using _Implementation =
+      __pstl::__dispatch<__pstl::__set_symmetric_difference, __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(__result),
+      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 3ac07b9676c432..e1f26c2d2ea254 100644
--- a/libcxx/include/__pstl/backend_fwd.h
+++ b/libcxx/include/__pstl/backend_fwd.h
@@ -518,6 +518,15 @@ struct __set_difference;
 //                                _ForwardOutIterator __result,
 //                                _Compare __comp) const noexcept;
 
+template <class _Backend, class _ExecutionPolicy>
+struct __set_symmetric_difference;
+// template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+// optional<_ForwardOutIterator>
+// operator()(_Policy&& __policy, _ForwardIterator1 __first1, _ForwardIterator1 __last1,
+//                                _ForwardIterator2 __first2, _ForwardIterator2 __last2,
+//                                _ForwardOutIterator __result,
+//                                _Compare __comp) const noexcept;
+
 template <class _Backend, class _ExecutionPolicy>
 struct __set_union;
 // template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
diff --git a/libcxx/include/__pstl/backends/serial.h b/libcxx/include/__pstl/backends/serial.h
index 8fffc006b1c902..294927cbb70276 100644
--- a/libcxx/include/__pstl/backends/serial.h
+++ b/libcxx/include/__pstl/backends/serial.h
@@ -24,6 +24,7 @@
 #include <__algorithm/search_n.h>
 #include <__algorithm/set_difference.h>
 #include <__algorithm/set_intersection.h>
+#include <__algorithm/set_symmetric_difference.h>
 #include <__algorithm/set_union.h>
 #include <__algorithm/stable_sort.h>
 #include <__algorithm/swap_ranges.h>
@@ -353,6 +354,27 @@ struct __set_difference<__serial_backend_tag, _ExecutionPolicy> {
   }
 };
 
+template <class _ExecutionPolicy>
+struct __set_symmetric_difference<__serial_backend_tag, _ExecutionPolicy> {
+  template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
+  optional<_ForwardOutIterator> operator()(
+      _Policy&&,
+      _ForwardIterator1 __first1,
+      _ForwardIterator1 __last1,
+      _ForwardIterator2 __first2,
+      _ForwardIterator2 __last2,
+      _ForwardOutIterator __result,
+      _Compare __comp) const noexcept {
+    return std::set_symmetric_difference(
+        std::move(__first1),
+        std::move(__last1),
+        std::move(__first2),
+        std::move(__last2),
+        std::move(__result),
+        std::move(__comp));
+  }
+};
+
 template <class _ExecutionPolicy>
 struct __set_union<__serial_backend_tag, _ExecutionPolicy> {
   template <class _Policy, class _ForwardIterator1, class _ForwardIterator2, class _ForwardOutIterator, class _Compare>
diff --git a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
index 0bab9532153d28..7a006be5af72b1 100644
--- a/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.iterator-requirements.verify.cpp
@@ -291,6 +291,22 @@ void f(non_forward_iterator non_fwd,
         pol, it, it, it, it, non_output, pred); // expected-error@*:* {{static assertion failed: set_difference}}
   }
 
+  {
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, non_fwd, non_fwd, it, it, it);
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, it, it, non_fwd, non_fwd, it);
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, it, it, it, it, non_output);
+
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, non_fwd, non_fwd, it, it, it, pred);
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, it, it, non_fwd, non_fwd, it, pred);
+    // expected-error@*:* {{static assertion failed: set_symmetric_difference}}
+    (void)std::set_symmetric_difference(pol, it, it, it, it, non_output, pred);
+  }
+
   {
     (void)std::set_intersection(
         pol, non_fwd, non_fwd, it, it, it); // expected-error@*:* {{static assertion failed: set_intersection}}
diff --git a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
index 41d68a3153b6e0..03e324e502a4df 100644
--- a/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/algorithms/pstl.nodiscard.verify.cpp
@@ -125,6 +125,12 @@ void test() {
   std::set_difference(
       std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_symmetric_difference(
+      std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::set_symmetric_difference(
+      std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b), pred2);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_intersection(std::execution::par, std::begin(a), std::end(a), std::begin(a), std::end(a), std::begin(b));
   // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
   std::set_intersection(
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference.pass.cpp
new file mode 100644
index 00000000000000..5cec123ecc2465
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardOutIterator>
+//   ForwardOutIterator
+//   set_symmetric_difference(ExecutionPolicy&& exec,
+//                            ForwardIterator1 first1,
+//                            ForwardIterator1 last1,
+//                            ForwardIterator2 first2,
+//                            ForwardIterator2 last2,
+//                            ForwardOutIterator result);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_symmetric_difference);
+
+static_assert(sfinae_test_set_symmetric_difference<int, int*, int*, int*, int*, int*>);
+static_assert(!sfinae_test_set_symmetric_difference<std::execution::parallel_policy, int*, int*, int*, int*, int*>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T = TrivialSortable;
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_symmetric_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 22, 31, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_symmetric_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {11, 22, 31, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_symmetric_difference(
+            policy, Iter1(std::begin(a)), Iter1(std::end(a)), Iter2(std::begin(b)), Iter2(std::end(b)), std::begin(c));
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected)) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(types::forward_iterator_list<const TrivialSortable*>{}, types::apply_type_identity{[](auto v) {
+                    using Iter = typename decltype(v)::type;
+                    types::for_each(
+                        types::forward_iterator_list<const TrivialSortable*>{},
+                        TestIteratorWithPolicies<types::partial_instantiation<Test, Iter>::template apply>{});
+                  }});
+  return 0;
+}
diff --git a/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference_comp.pass.cpp b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference_comp.pass.cpp
new file mode 100644
index 00000000000000..bf9410e079082b
--- /dev/null
+++ b/libcxx/test/std/algorithms/alg.sorting/alg.set.operations/set.symmetric.difference/pstl.set_symmetric_difference_comp.pass.cpp
@@ -0,0 +1,117 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 ForwardOutIterator, class Compare>
+//   ForwardOutIterator
+//   set_symmetric_difference(ExecutionPolicy&& exec,
+//                            ForwardIterator1 first1,
+//                            ForwardIterator1 last1,
+//                            ForwardIterator2 first2,
+//                            ForwardIterator2 last2,
+//                            ForwardOutIterator result,
+//                            Compare comp);
+
+#include <cstddef>
+#include <algorithm>
+#include <cassert>
+#include <iterator>
+#include <numeric>
+#include <random>
+#include <vector>
+
+#include "test_execution_policies.h"
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "type_algorithms.h"
+#include "../../sortable_helpers.h"
+
+EXECUTION_POLICY_SFINAE_TEST(set_symmetric_difference);
+
+static_assert(sfinae_test_set_symmetric_difference<int, int*, int*, int*, int*, int*, bool (*)(int, int)>);
+static_assert(!sfinae_test_set_symmetric_difference<
+              std::execution::parallel_policy,
+              int*,
+              int*,
+              int*,
+              int*,
+              int*,
+              bool (*)(int, int)>);
+
+template <class Iter1, class Iter2>
+struct Test {
+  template <class ExecutionPolicy>
+  void operator()(ExecutionPolicy&& policy) {
+    {
+      using T   = TrivialSortableWithComp;
+      auto comp = T::Comparator{};
+      { // Check the result type
+        T a[]    = {0};
+        T b[]    = {0};
+        T c[2]   = {};
+        auto res = std::set_symmetric_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        static_assert(std::is_same_v<decltype(res), decltype(std::begin(c))>);
+      }
+      { // First is shorter
+        T a[]                    = {11, 33, 31, 41};
+        T b[]                    = {22, 32, 43, 42, 52};
+        T expected[]             = {11, 22, 31, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_symmetric_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+      { // First is longer
+        T a[]                    = {22, 32, 43, 42, 52};
+        T b[]                    = {11, 33, 31, 41};
+        T expected[]             = {11, 22, 31, 42, 52};
+        T c[std::size(expected)] = {};
+        auto res                 = std::set_symmetric_difference(
+            policy,
+            Iter1(std::begin(a)),
+            Iter1(std::end(a)),
+            Iter2(std::begin(b)),
+            Iter2(std::end(b)),
+            std::begin(c),
+            comp);
+        assert(res == std::end(c));
+        assert(std::lexicographical_compare(std::begin(c), res, std::begin(expected), std::end(expected), comp) == 0);
+      }
+    }
+  }
+};
+
+int main(int, char**) {
+  types::for_each(
+      types::forward_iterator_list<const TrivialSortableWithComp*>{}, types::apply_type_identity{[](auto v) {
+        using Iter = typename decltype(v)::type;
+        types::for_each(types::forward_iterator_list<const TrivialSortableWithComp*>{},
+                        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 4f7ed3dee97cda..ec8b60c2759d52 100644
--- a/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
+++ b/libcxx/test/std/algorithms/pstl.exception_handling.pass.cpp
@@ -633,6 +633,28 @@ int main(int, char**) {
         });
       }
 
+      {
+        auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
+
+        // set_symmetric_difference(first1, last1, first2, last2, dest)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_symmetric_difference(
+              policy, std::move(first1), std::move(last1), std::move(first2), std::move(last2), std::move(dest));
+        });
+
+        // set_symmetric_difference(first1, last1, first2, last2, dest, pred)
+        assert_non_throwing([=, &policy] {
+          (void)std::set_symmetric_difference(
+              policy,
+              std::move(first1),
+              std::move(last1),
+              std::move(first2),
+              std::move(last2),
+              std::move(dest),
+              compare);
+        });
+      }
+
       {
         auto compare = maybe_throw(tokens[5], [](int x, int y) -> bool { return x < y; });
 



More information about the libcxx-commits mailing list