[libcxx-commits] [libcxx] [libc++][execution] Implement C++26 std::execution set_value, set_error and set_stopped (PR #229095)

via libcxx-commits libcxx-commits at lists.llvm.org
Mon Oct 5 21:50:51 PDT 2026


https://github.com/emmett2020 updated https://github.com/llvm/llvm-project/pull/229095

>From 3d5a8c845547b7dc0742f9e957b20addf626e1fb Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Mon, 5 Oct 2026 21:44:28 +0800
Subject: [PATCH 1/2] [libc++][execution] Implement C++26
 std::execution::set_value, set_error, and set_stopped

---
 libcxx/docs/Status/Cxx26Papers.csv            |   2 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__configuration/experimental.h |   1 +
 .../__execution/completion_functions.h        |  84 +++++++++++++
 libcxx/include/execution                      |  10 ++
 libcxx/include/module.modulemap.in            |   1 +
 libcxx/modules/std/execution.inc              |  16 +++
 .../exec.recv/completion_functions.verify.cpp |  59 +++++++++
 .../exec.recv/completion_functions.pass.cpp   | 117 ++++++++++++++++++
 libcxx/utils/libcxx/test/params.py            |   1 +
 10 files changed, 291 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/include/__execution/completion_functions.h
 create mode 100644 libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
 create mode 100644 libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp

diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index f3cdf8c53bab4..3ed07bd6ce574 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -74,7 +74,7 @@
 "`P2968R2 <https://wg21.link/P2968R2>`__","Make ``std::ignore`` a first-class object","2024-06 (St. Louis)","|Complete|","19","`#105436 <https://github.com/llvm/llvm-project/issues/105436>`__",""
 "`P2075R6 <https://wg21.link/P2075R6>`__","Philox as an extension of the C++ RNG engines","2024-06 (St. Louis)","","","`#105437 <https://github.com/llvm/llvm-project/issues/105437>`__",""
 "`P2422R1 <https://wg21.link/P2422R1>`__","Remove ``nodiscard`` annotations from the standard library specification","2024-06 (St. Louis)","|Complete|","19","`#105438 <https://github.com/llvm/llvm-project/issues/105438>`__","``nodiscard`` attributes were kept as a conforming extension"
-"`P2300R10 <https://wg21.link/P2300R10>`__","``std::execution``","2024-06 (St. Louis)","","","`#105440 <https://github.com/llvm/llvm-project/issues/105440>`__",""
+"`P2300R10 <https://wg21.link/P2300R10>`__","``std::execution``","2024-06 (St. Louis)","|Partial|","","`#105440 <https://github.com/llvm/llvm-project/issues/105440>`__",""
 "","","","","","",""
 "`P3136R1 <https://wg21.link/P3136R1>`__","Retiring niebloids","2024-11 (Wrocław)","|Complete|","14","`#118133 <https://github.com/llvm/llvm-project/issues/118133>`__",""
 "`P3138R5 <https://wg21.link/P3138R5>`__","``views::cache_latest``","2024-11 (Wrocław)","","","`#118134 <https://github.com/llvm/llvm-project/issues/118134>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 1ee2081561fd9..d96cb3c116352 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -367,6 +367,7 @@ set(files
   __exception/nested_exception.h
   __exception/operations.h
   __exception/terminate.h
+  __execution/completion_functions.h
   __expected/bad_expected_access.h
   __expected/expected.h
   __expected/unexpect.h
diff --git a/libcxx/include/__configuration/experimental.h b/libcxx/include/__configuration/experimental.h
index bb38d8297c63d..7445cc7eb3b64 100644
--- a/libcxx/include/__configuration/experimental.h
+++ b/libcxx/include/__configuration/experimental.h
@@ -30,6 +30,7 @@
 #endif
 
 #define _LIBCPP_HAS_EXPERIMENTAL_PSTL _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
+#define _LIBCPP_HAS_EXPERIMENTAL_EXECUTION _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_TZDB _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_SYNCSTREAM _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
 #define _LIBCPP_HAS_EXPERIMENTAL_HARDENING_OBSERVE_SEMANTIC _LIBCPP_HAS_EXPERIMENTAL_LIBRARY
diff --git a/libcxx/include/__execution/completion_functions.h b/libcxx/include/__execution/completion_functions.h
new file mode 100644
index 0000000000000..3e0e24a3c76a1
--- /dev/null
+++ b/libcxx/include/__execution/completion_functions.h
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___EXECUTION_COMPLETION_FUNCTIONS_H
+#define _LIBCPP___EXECUTION_COMPLETION_FUNCTIONS_H
+
+#include <__config>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_same.h>
+#include <__utility/forward.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_EXECUTION
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace execution {
+
+// [exec.set.value]
+struct set_value_t {
+  template <class _Receiver, class... _Args>
+    requires(!is_lvalue_reference_v<_Receiver> && !is_const_v<_Receiver>) &&
+            requires(_Receiver&& __rcvr, _Args&&... __args) {
+              std::forward<_Receiver>(__rcvr).set_value(std::forward<_Args>(__args)...);
+            }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator()(_Receiver&& __rcvr, _Args&&... __args) const noexcept {
+    static_assert(noexcept(std::forward<_Receiver>(__rcvr).set_value(std::forward<_Args>(__args)...)),
+                  "set_value must be noexcept");
+    static_assert(is_same_v<decltype(std::forward<_Receiver>(__rcvr).set_value(std::forward<_Args>(__args)...)), void>,
+                  "set_value must return void");
+    std::forward<_Receiver>(__rcvr).set_value(std::forward<_Args>(__args)...);
+  }
+};
+
+// [exec.set.error]
+struct set_error_t {
+  template <class _Receiver, class _Error>
+    requires(!is_lvalue_reference_v<_Receiver> && !is_const_v<_Receiver>) &&
+            requires(_Receiver&& __rcvr, _Error&& __error) {
+              std::forward<_Receiver>(__rcvr).set_error(std::forward<_Error>(__error));
+            }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator()(_Receiver&& __rcvr, _Error&& __error) const noexcept {
+    static_assert(noexcept(std::forward<_Receiver>(__rcvr).set_error(std::forward<_Error>(__error))),
+                  "set_error must be noexcept");
+    static_assert(is_same_v<decltype(std::forward<_Receiver>(__rcvr).set_error(std::forward<_Error>(__error))), void>,
+                  "set_error must return void");
+    std::forward<_Receiver>(__rcvr).set_error(std::forward<_Error>(__error));
+  }
+};
+
+// [exec.set.stopped]
+struct set_stopped_t {
+  template <class _Receiver>
+    requires(!is_lvalue_reference_v<_Receiver> && !is_const_v<_Receiver>) && requires(_Receiver&& __rcvr) {
+      std::forward<_Receiver>(__rcvr).set_stopped();
+    }
+  _LIBCPP_HIDE_FROM_ABI constexpr void operator()(_Receiver&& __rcvr) const noexcept {
+    static_assert(noexcept(std::forward<_Receiver>(__rcvr).set_stopped()), "set_stopped must be noexcept");
+    static_assert(
+        is_same_v<decltype(std::forward<_Receiver>(__rcvr).set_stopped()), void>, "set_stopped must return void");
+    std::forward<_Receiver>(__rcvr).set_stopped();
+  }
+};
+
+inline constexpr set_value_t set_value{};
+inline constexpr set_error_t set_error{};
+inline constexpr set_stopped_t set_stopped{};
+
+} // namespace execution
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_EXECUTION
+
+#endif // _LIBCPP___EXECUTION_COMPLETION_FUNCTIONS_H
diff --git a/libcxx/include/execution b/libcxx/include/execution
index 49a901282fd9b..9e18989b5b0a1 100644
--- a/libcxx/include/execution
+++ b/libcxx/include/execution
@@ -21,6 +21,15 @@ namespace std::execution {
   inline constexpr parallel_policy par = implementation-defined;
   inline constexpr parallel_unsequenced_policy par_unseq = implementation-defined;
   inline constexpr unsequenced_policy unseq = implementation-defined; // since C++20
+
+  // completion functions, since C++26
+  struct set_value_t;
+  struct set_error_t;
+  struct set_stopped_t;
+
+  inline constexpr set_value_t set_value{};
+  inline constexpr set_error_t set_error{};
+  inline constexpr set_stopped_t set_stopped{};
 }
 
 namespace std {
@@ -36,6 +45,7 @@ namespace std {
 #  include <__cxx03/__config>
 #else
 #  include <__config>
+#  include <__execution/completion_functions.h>
 #  include <__type_traits/is_execution_policy.h>
 #  include <__type_traits/is_same.h>
 #  include <__type_traits/remove_cvref.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 28ae9ee8de46a..06b669c242d8b 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1266,6 +1266,7 @@ module std {
 
   module execution {
     header "execution"
+    header "__execution/completion_functions.h"
     export *
   }
 
diff --git a/libcxx/modules/std/execution.inc b/libcxx/modules/std/execution.inc
index 86c1cd7622b9b..928e0285f502e 100644
--- a/libcxx/modules/std/execution.inc
+++ b/libcxx/modules/std/execution.inc
@@ -34,3 +34,19 @@ export namespace std::execution {
   using std::execution::unseq;
 } // namespace std::execution
 #endif // _LIBCPP_ENABLE_EXPERIMENTAL
+
+#if _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_EXECUTION
+export namespace std::execution {
+  // [exec.set.value], value completion function
+  using std::execution::set_value;
+  using std::execution::set_value_t;
+
+  // [exec.set.error], error completion function
+  using std::execution::set_error;
+  using std::execution::set_error_t;
+
+  // [exec.set.stopped], stopped completion function
+  using std::execution::set_stopped;
+  using std::execution::set_stopped_t;
+} // namespace std::execution
+#endif // _LIBCPP_STD_VER >= 26 && _LIBCPP_HAS_EXPERIMENTAL_EXECUTION
diff --git a/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp b/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
new file mode 100644
index 0000000000000..7ad34c3db9f15
--- /dev/null
+++ b/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++26
+// UNSUPPORTED: libcpp-has-no-experimental-execution
+
+// The noexcept and void-return requirements are mandates, not constraints.
+
+#include <execution>
+
+namespace ex = std::execution;
+
+struct ThrowingReceiver {
+  void set_value() && noexcept(false);
+  void set_error(int) && noexcept(false);
+  void set_stopped() && noexcept(false);
+};
+
+struct NonVoidReceiver {
+  int set_value() && noexcept;
+  int set_error(int) && noexcept;
+  int set_stopped() && noexcept;
+};
+
+// Objects of this type can be converted to int, but the conversion may throw.
+struct ThrowingConversion {
+  operator int() noexcept(false);
+};
+
+struct ConversionReceiver {
+  void set_value(int) && noexcept;
+  void set_error(int) && noexcept;
+};
+
+void test() {
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_value must be noexcept}}
+  ex::set_value(ThrowingReceiver{});
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_error must be noexcept}}
+  ex::set_error(ThrowingReceiver{}, 0);
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_stopped must be noexcept}}
+  ex::set_stopped(ThrowingReceiver{});
+
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_value must return void}}
+  ex::set_value(NonVoidReceiver{});
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_error must return void}}
+  ex::set_error(NonVoidReceiver{}, 0);
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_stopped must return void}}
+  ex::set_stopped(NonVoidReceiver{});
+
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_value must be noexcept}}
+  ex::set_value(ConversionReceiver{}, ThrowingConversion{});
+  // expected-error-re@*:* {{static assertion failed{{.*}}set_error must be noexcept}}
+  ex::set_error(ConversionReceiver{}, ThrowingConversion{});
+}
diff --git a/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp b/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp
new file mode 100644
index 0000000000000..f6cbb51b728ec
--- /dev/null
+++ b/libcxx/test/std/exec/exec.recv/completion_functions.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++26
+// UNSUPPORTED: libcpp-has-no-experimental-execution
+
+// [exec.set.value], [exec.set.error], [exec.set.stopped]
+
+#include <execution>
+
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+namespace ex = std::execution;
+
+static_assert(std::is_same_v<decltype(ex::set_value), const ex::set_value_t>);
+static_assert(std::is_same_v<decltype(ex::set_error), const ex::set_error_t>);
+static_assert(std::is_same_v<decltype(ex::set_stopped), const ex::set_stopped_t>);
+
+// Reject receivers that have no set_xxx members.
+struct NoMemberReceiver {};
+static_assert(!std::is_invocable_v<ex::set_value_t, NoMemberReceiver, int>);
+static_assert(!std::is_invocable_v<ex::set_error_t, NoMemberReceiver, int>);
+static_assert(!std::is_invocable_v<ex::set_stopped_t, NoMemberReceiver>);
+
+struct CvrefTestReceiver {
+  void set_value(int) const noexcept;
+  void set_error(int) const noexcept;
+  void set_stopped() const noexcept;
+};
+
+static_assert(std::is_nothrow_invocable_v<ex::set_value_t, CvrefTestReceiver, int>);
+static_assert(std::is_nothrow_invocable_v<ex::set_error_t, CvrefTestReceiver, int>);
+static_assert(std::is_nothrow_invocable_v<ex::set_stopped_t, CvrefTestReceiver>);
+
+static_assert(!std::is_invocable_v<ex::set_value_t, CvrefTestReceiver&, int>);
+static_assert(!std::is_invocable_v<ex::set_error_t, CvrefTestReceiver&, int>);
+static_assert(!std::is_invocable_v<ex::set_stopped_t, CvrefTestReceiver&>);
+
+static_assert(!std::is_invocable_v<ex::set_value_t, const CvrefTestReceiver, int>);
+static_assert(!std::is_invocable_v<ex::set_error_t, const CvrefTestReceiver, int>);
+static_assert(!std::is_invocable_v<ex::set_stopped_t, const CvrefTestReceiver>);
+
+struct StaticReceiver {
+  static void set_value(int) noexcept;
+  static void set_error(int) noexcept;
+  static void set_stopped() noexcept;
+};
+
+static_assert(std::is_nothrow_invocable_v<ex::set_value_t, StaticReceiver, int>);
+static_assert(std::is_nothrow_invocable_v<ex::set_error_t, StaticReceiver, int>);
+static_assert(std::is_nothrow_invocable_v<ex::set_stopped_t, StaticReceiver>);
+
+struct RvalueReceiver {
+  int& result;
+
+  constexpr void set_value() && noexcept { result = 0; }
+  constexpr void set_value(int value) && noexcept { result = value; }
+  constexpr void set_value(int& output, int&& value) && noexcept {
+    output = value;
+    result = value;
+  }
+  constexpr void set_error(int& error) && noexcept { result = error; }
+  constexpr void set_error(int&& error) && noexcept { result = error; }
+  constexpr void set_stopped() && noexcept { result = 0; }
+};
+
+// This receiver accepts at most two values, one error, and no stopped arguments.
+static_assert(!std::is_invocable_v<ex::set_value_t, RvalueReceiver, int, int, int>);
+static_assert(!std::is_invocable_v<ex::set_error_t, RvalueReceiver>);
+static_assert(!std::is_invocable_v<ex::set_error_t, RvalueReceiver, int, int>);
+static_assert(!std::is_invocable_v<ex::set_stopped_t, RvalueReceiver, int>);
+
+constexpr bool test() {
+  int result = -1;
+  RvalueReceiver receiver{result};
+  ex::set_value(std::move(receiver));
+  assert(result == 0);
+
+  result = -1;
+  ex::set_value(RvalueReceiver{result}, 42);
+  assert(result == 42);
+
+  // Forward multiple arguments, preserving lvalue and rvalue references.
+  int output = 0;
+  result     = -1;
+  ex::set_value(RvalueReceiver{result}, output, 42);
+  assert(output == 42);
+  assert(result == 42);
+
+  result = -1;
+  ex::set_error(RvalueReceiver{result}, 42);
+  assert(result == 42);
+
+  int error = 42;
+  result    = -1;
+  ex::set_error(RvalueReceiver{result}, error);
+  assert(result == 42);
+
+  result = -1;
+  ex::set_stopped(RvalueReceiver{result});
+  assert(result == 0);
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}
diff --git a/libcxx/utils/libcxx/test/params.py b/libcxx/utils/libcxx/test/params.py
index 951b0a4dd9806..9795121c1c18d 100644
--- a/libcxx/utils/libcxx/test/params.py
+++ b/libcxx/utils/libcxx/test/params.py
@@ -366,6 +366,7 @@ def getSuitableClangTidy(cfg):
         if experimental
         else [
             AddFeature("libcpp-has-no-incomplete-pstl"),
+            AddFeature("libcpp-has-no-experimental-execution"),
             AddFeature("libcpp-has-no-experimental-optional-iterator"),
             AddFeature("libcpp-has-no-experimental-tzdb"),
             AddFeature("libcpp-has-no-experimental-syncstream"),

>From c2213d10278fa67a948ed69e8b2ea01a0c59332b Mon Sep 17 00:00:00 2001
From: Emmett <emmettzhang2020 at outlook.com>
Date: Tue, 6 Oct 2026 12:50:11 +0800
Subject: [PATCH 2/2] fix some review comments

---
 libcxx/include/execution                      | 15 ++++----
 .../exec.recv/completion_functions.verify.cpp | 35 +++++++++++++++++
 .../exec.recv/completion_functions.pass.cpp   | 38 +++++++++++++++++--
 3 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/libcxx/include/execution b/libcxx/include/execution
index 9e18989b5b0a1..ef960f7cfe827 100644
--- a/libcxx/include/execution
+++ b/libcxx/include/execution
@@ -22,14 +22,13 @@ namespace std::execution {
   inline constexpr parallel_unsequenced_policy par_unseq = implementation-defined;
   inline constexpr unsequenced_policy unseq = implementation-defined; // since C++20
 
-  // completion functions, since C++26
-  struct set_value_t;
-  struct set_error_t;
-  struct set_stopped_t;
-
-  inline constexpr set_value_t set_value{};
-  inline constexpr set_error_t set_error{};
-  inline constexpr set_stopped_t set_stopped{};
+  struct set_value_t;    // Since C++26
+  struct set_error_t;    // Since C++26
+  struct set_stopped_t;  // Since C++26
+
+  inline constexpr set_value_t set_value{};      // Since C++26
+  inline constexpr set_error_t set_error{};      // Since C++26
+  inline constexpr set_stopped_t set_stopped{};  // Since C++26
 }
 
 namespace std {
diff --git a/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp b/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
index 7ad34c3db9f15..567b63e26710f 100644
--- a/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
+++ b/libcxx/test/libcxx/exec/exec.recv/completion_functions.verify.cpp
@@ -9,6 +9,41 @@
 // REQUIRES: std-at-least-c++26
 // UNSUPPORTED: libcpp-has-no-experimental-execution
 
+// <execution>
+//
+// namespace std::execution {
+//   struct set_value_t {
+//     template<class Receiver, class... Args>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr, Args&&... args) {
+//                  std::forward<Receiver>(rcvr).set_value(std::forward<Args>(args)...);
+//                }
+//     constexpr void operator()(Receiver&& rcvr, Args&&... args) const noexcept;
+//   };
+//
+//   struct set_error_t {
+//     template<class Receiver, class Error>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr, Error&& error) {
+//                  std::forward<Receiver>(rcvr).set_error(std::forward<Error>(error));
+//                }
+//     constexpr void operator()(Receiver&& rcvr, Error&& error) const noexcept;
+//   };
+//
+//   struct set_stopped_t {
+//     template<class Receiver>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr) {
+//                  std::forward<Receiver>(rcvr).set_stopped();
+//                }
+//     constexpr void operator()(Receiver&& rcvr) const noexcept;
+//   };
+//
+//   inline constexpr set_value_t set_value{};
+//   inline constexpr set_error_t set_error{};
+//   inline constexpr set_stopped_t set_stopped{};
+// }
+
 // The noexcept and void-return requirements are mandates, not constraints.
 
 #include <execution>
diff --git a/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp b/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp
index f6cbb51b728ec..a66d6a6521a7b 100644
--- a/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp
+++ b/libcxx/test/std/exec/exec.recv/completion_functions.pass.cpp
@@ -9,11 +9,43 @@
 // REQUIRES: std-at-least-c++26
 // UNSUPPORTED: libcpp-has-no-experimental-execution
 
-// [exec.set.value], [exec.set.error], [exec.set.stopped]
-
-#include <execution>
+// <execution>
+//
+// namespace std::execution {
+//   struct set_value_t {
+//     template<class Receiver, class... Args>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr, Args&&... args) {
+//                  std::forward<Receiver>(rcvr).set_value(std::forward<Args>(args)...);
+//                }
+//     constexpr void operator()(Receiver&& rcvr, Args&&... args) const noexcept;
+//   };
+//
+//   struct set_error_t {
+//     template<class Receiver, class Error>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr, Error&& error) {
+//                  std::forward<Receiver>(rcvr).set_error(std::forward<Error>(error));
+//                }
+//     constexpr void operator()(Receiver&& rcvr, Error&& error) const noexcept;
+//   };
+//
+//   struct set_stopped_t {
+//     template<class Receiver>
+//       requires (!is_lvalue_reference_v<Receiver> && !is_const_v<Receiver>) &&
+//                requires(Receiver&& rcvr) {
+//                  std::forward<Receiver>(rcvr).set_stopped();
+//                }
+//     constexpr void operator()(Receiver&& rcvr) const noexcept;
+//   };
+//
+//   inline constexpr set_value_t set_value{};
+//   inline constexpr set_error_t set_error{};
+//   inline constexpr set_stopped_t set_stopped{};
+// }
 
 #include <cassert>
+#include <execution>
 #include <type_traits>
 #include <utility>
 



More information about the libcxx-commits mailing list