From Jason Turner

[exec.adapt]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmp10hz0qea/{from.md → to.md} +2117 -0
tmp/tmp10hz0qea/{from.md → to.md} RENAMED
@@ -0,0 +1,2117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Sender adaptors <a id="exec.adapt">[[exec.adapt]]</a>
2
+
3
+ #### General <a id="exec.adapt.general">[[exec.adapt.general]]</a>
4
+
5
+ Subclause [[exec.adapt]] specifies a set of sender adaptors.
6
+
7
+ The bitwise inclusive operator is overloaded for the purpose of creating
8
+ sender chains. The adaptors also support function call syntax with
9
+ equivalent semantics.
10
+
11
+ Unless otherwise specified:
12
+
13
+ - A sender adaptor is prohibited from causing observable effects, apart
14
+ from moving and copying its arguments, before the returned sender is
15
+ connected with a receiver using `connect`, and `start` is called on
16
+ the resulting operation state.
17
+ - A parent sender [[exec.async.ops]] with a single child sender `sndr`
18
+ has an associated attribute object equal to `FWD-ENV(get_env(sndr))`
19
+ [[exec.fwd.env]].
20
+ - A parent sender with more than one child sender has an associated
21
+ attributes object equal to `env<>{}`.
22
+ - When a parent sender is connected to a receiver `rcvr`, any receiver
23
+ used to connect a child sender has an associated environment equal to
24
+ `FWD-ENV(get_env(rcvr))`.
25
+ - An adaptor whose child senders are all non-dependent
26
+ [[exec.async.ops]] is itself non-dependent.
27
+ - These requirements apply to any function that is selected by the
28
+ implementation of the sender adaptor.
29
+ - *Recommended practice:* Implementations should use the completion
30
+ signatures of the adaptors to communicate type errors to users and to
31
+ propagate any such type errors from child senders.
32
+
33
+ If a sender returned from a sender adaptor specified in [[exec.adapt]]
34
+ is specified to include `set_error_t(Err)` among its set of completion
35
+ signatures where `decay_t<Err>` denotes the type `exception_ptr`, but
36
+ the implementation does not potentially evaluate an error completion
37
+ operation with an `exception_ptr` argument, the implementation is
38
+ allowed to omit the `exception_ptr` error completion signature from the
39
+ set.
40
+
41
+ #### Closure objects <a id="exec.adapt.obj">[[exec.adapt.obj]]</a>
42
+
43
+ A *pipeable sender adaptor closure object* is a function object that
44
+ accepts one or more `sender` arguments and returns a `sender`. For a
45
+ pipeable sender adaptor closure object `c` and an expression `sndr` such
46
+ that `decltype((sndr))` models `sender`, the following expressions are
47
+ equivalent and yield a `sender`:
48
+
49
+ ``` cpp
50
+ c(sndr)
51
+ sndr | c
52
+ ```
53
+
54
+ Given an additional pipeable sender adaptor closure object `d`, the
55
+ expression `c | d` produces another pipeable sender adaptor closure
56
+ object `e`:
57
+
58
+ `e` is a perfect forwarding call wrapper [[func.require]] with the
59
+ following properties:
60
+
61
+ - Its target object is an object `d2` of type `decltype(auto(d))`
62
+ direct-non-list-initialized with `d`.
63
+ - It has one bound argument entity, an object `c2` of type
64
+ `decltype(auto(c))` direct-non-list-initialized with `c`.
65
+ - Its call pattern is `d2(c2(arg))`, where arg is the argument used in a
66
+ function call expression of `e`.
67
+
68
+ The expression `c | d` is well-formed if and only if the initializations
69
+ of the state entities [[func.def]] of `e` are all well-formed.
70
+
71
+ An object `t` of type `T` is a pipeable sender adaptor closure object if
72
+ `T` models `derived_from<sender_adaptor_closure<T>>`, `T` has no other
73
+ base classes of type `sender_adaptor_closure<U>` for any other type `U`,
74
+ and `T` does not satisfy `sender`.
75
+
76
+ The template parameter `D` for `sender_adaptor_closure` can be an
77
+ incomplete type. Before any expression of type cv `D` appears as an
78
+ operand to the `|` operator, `D` shall be complete and model
79
+ `derived_from<sender_adaptor_closure<D>>`. The behavior of an expression
80
+ involving an object of type cv `D` as an operand to the `|` operator is
81
+ undefined if overload resolution selects a program-defined `operator|`
82
+ function.
83
+
84
+ A *pipeable sender adaptor object* is a customization point object that
85
+ accepts a `sender` as its first argument and returns a `sender`. If a
86
+ pipeable sender adaptor object accepts only one argument, then it is a
87
+ pipeable sender adaptor closure object.
88
+
89
+ If a pipeable sender adaptor object adaptor accepts more than one
90
+ argument, then let `sndr` be an expression such that `decltype((sndr))`
91
+ models `sender`, let `args...` be arguments such that
92
+ `adaptor(sndr, args...)` is a well-formed expression as specified below,
93
+ and let `BoundArgs` be a pack that denotes `decltype(auto(args))...`.
94
+ The expression `adaptor(args...)` produces a pipeable sender adaptor
95
+ closure object `f` that is a perfect forwarding call wrapper with the
96
+ following properties:
97
+
98
+ - Its target object is a copy of adaptor.
99
+ - Its bound argument entities `bound_args` consist of objects of types
100
+ `BoundArgs...` direct-non-list-initialized with
101
+ `std::forward<decltype((args))>(args)...`, respectively.
102
+ - Its call pattern is `adaptor(rcvr, bound_args...)`, where `rcvr` is
103
+ the argument used in a function call expression of `f`.
104
+
105
+ The expression `adaptor(args...)` is well-formed if and only if the
106
+ initializations of the bound argument entities of the result, as
107
+ specified above, are all well-formed.
108
+
109
+ #### `execution::write_env` <a id="exec.write.env">[[exec.write.env]]</a>
110
+
111
+ `write_env` is a sender adaptor that accepts a sender and a queryable
112
+ object, and that returns a sender that, when connected with a receiver
113
+ `rcvr`, connects the adapted sender with a receiver whose execution
114
+ environment is the result of joining the `queryable` object to the
115
+ result of `get_env(rcvr)`.
116
+
117
+ `write_env` is a customization point object. For some subexpressions
118
+ `sndr` and `env`, if `decltype((sndr))` does not satisfy `sender` or if
119
+ `decltype((env))` does not satisfy `queryable`, the expression
120
+ `write_env(sndr, env)` is ill-formed. Otherwise, it is
121
+ expression-equivalent to `make-sender(write_env, env, sndr)`.
122
+
123
+ Let *`write-env-t`* denote the type `decltype(auto(write_env))`. The
124
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
125
+ specialized for *`write-env-t`* as follows:
126
+
127
+ ``` cpp
128
+ template<>
129
+ struct impls-for<write-env-t> : default-impls {
130
+ static constexpr auto join-env(const auto& state, const auto& env) noexcept {
131
+ return see below;
132
+ }
133
+
134
+ static constexpr auto get-env =
135
+ [](auto, const auto& state, const auto& rcvr) noexcept {
136
+ return join-env(state, FWD-ENV(get_env(rcvr)));
137
+ };
138
+
139
+ template<class Sndr, class... Env>
140
+ static consteval void check-types();
141
+ };
142
+ ```
143
+
144
+ Invocation of `impls-for<write-env-t>::join-env` returns an object `e`
145
+ such that
146
+
147
+ - `decltype(e)` models `queryable` and
148
+ - given a query object `q`, the expression `e.query(q)` is
149
+ expression-equivalent to `state.query(q)` if that expression is valid,
150
+ otherwise, `e.query(q)` is expression-equivalent to `env.query(q)`.
151
+
152
+ For a type `Sndr` and a pack of types `Env`, let `State` be
153
+ `data-type<Sndr>` and let `JoinEnv` be the pack
154
+ `decltype(join-env(declval<State>(), FWD-ENV(declval<Env>())))`. Then
155
+ `impls-for<write-env-{t}>::check-types<Sndr, Env...>()` is
156
+ expression-equivalent to
157
+ `get_completion_signatures<child-{type}<Sndr>, JoinEnv...>()`.
158
+
159
+ #### `execution::unstoppable` <a id="exec.unstoppable">[[exec.unstoppable]]</a>
160
+
161
+ `unstoppable` is a sender adaptor that connects its inner sender with a
162
+ receiver that has the execution environment of the outer receiver but
163
+ with an object of type `never_stop_token` as the result of the
164
+ `get_stop_token query`.
165
+
166
+ For a subexpression `sndr`, `unstoppable(sndr)` is expression-equivalent
167
+ to `write_env(sndr, prop(get_stop_token, never_stop_token{}))`.
168
+
169
+ #### `execution::starts_on` <a id="exec.starts.on">[[exec.starts.on]]</a>
170
+
171
+ `starts_on` adapts an input sender into a sender that will start on an
172
+ execution agent belonging to a particular scheduler’s associated
173
+ execution resource.
174
+
175
+ The name `starts_on` denotes a customization point object. For
176
+ subexpressions `sch` and `sndr`, if `decltype(( sch))` does not satisfy
177
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
178
+ `starts_on(sch, sndr)` is ill-formed.
179
+
180
+ Otherwise, the expression `starts_on(sch, sndr)` is
181
+ expression-equivalent to:
182
+
183
+ ``` cpp
184
+ transform_sender(
185
+ query-with-default(get_domain, sch, default_domain()),
186
+ make-sender(starts_on, sch, sndr))
187
+ ```
188
+
189
+ except that `sch` is evaluated only once.
190
+
191
+ Let `out_sndr` and `env` be subexpressions such that `OutSndr` is
192
+ `decltype((out_sndr))`. If `sender-for<OutSndr, starts_on_t>` is
193
+ `false`, then the expressions `starts_on.transform_env(out_sndr, env)`
194
+ and `starts_on.transform_sender(out_sndr, env)` are ill-formed;
195
+ otherwise
196
+
197
+ - `starts_on.transform_env(out_sndr, env)` is equivalent to:
198
+ ``` cpp
199
+ auto&& [_, sch, _] = out_sndr;
200
+ return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
201
+ ```
202
+ - `starts_on.transform_sender(out_sndr, env)` is equivalent to:
203
+ ``` cpp
204
+ auto&& [_, sch, sndr] = out_sndr;
205
+ return let_value(
206
+ schedule(sch),
207
+ [sndr = std::forward_like<OutSndr>(sndr)]() mutable
208
+ noexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {
209
+ return std::move(sndr);
210
+ });
211
+ ```
212
+
213
+ Let `out_sndr` be a subexpression denoting a sender returned from
214
+ `starts_on(sch, sndr)` or one equal to such, and let `OutSndr` be the
215
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
216
+ a receiver that has an environment of type `Env` such that
217
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
218
+ the operation state that results from connecting `out_sndr` with
219
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on an execution agent
220
+ of the associated execution resource of `sch`. If scheduling onto `sch`
221
+ fails, an error completion on `out_rcvr` shall be executed on an
222
+ unspecified execution agent.
223
+
224
+ #### `execution::continues_on` <a id="exec.continues.on">[[exec.continues.on]]</a>
225
+
226
+ `continues_on` adapts a sender into one that completes on the specified
227
+ scheduler.
228
+
229
+ The name `continues_on` denotes a pipeable sender adaptor object. For
230
+ subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
231
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
232
+ `continues_on(sndr, sch)` is ill-formed.
233
+
234
+ Otherwise, the expression `continues_on(sndr, sch)` is
235
+ expression-equivalent to:
236
+
237
+ ``` cpp
238
+ transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
239
+ ```
240
+
241
+ except that `sndr` is evaluated only once.
242
+
243
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
244
+ specialized for `continues_on_t` as follows:
245
+
246
+ ``` cpp
247
+ namespace std::execution {
248
+ template<>
249
+ struct impls-for<continues_on_t> : default-impls {
250
+ static constexpr auto get-attrs =
251
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
252
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
253
+ };
254
+ };
255
+ }
256
+ ```
257
+
258
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
259
+ `decltype((sndr))`. If `sender-for<Sndr, continues_on_t>` is `false`,
260
+ then the expression `continues_on.transform_sender(sndr, env)` is
261
+ ill-formed; otherwise, it is equal to:
262
+
263
+ ``` cpp
264
+ auto [_, data, child] = sndr;
265
+ return schedule_from(std::move(data), std::move(child));
266
+ ```
267
+
268
+ [*Note 1*: This causes the `continues_on(sndr, sch)` sender to become
269
+ `schedule_from(sch, sndr)` when it is connected with a receiver whose
270
+ execution domain does not customize `continues_on`. — *end note*]
271
+
272
+ Let `out_sndr` be a subexpression denoting a sender returned from
273
+ `continues_on(sndr, sch)` or one equal to such, and let `OutSndr` be the
274
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
275
+ a receiver that has an environment of type `Env` such that
276
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
277
+ the operation state that results from connecting `out_sndr` with
278
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
279
+ execution agent and execute completion operations on `out_rcvr` on an
280
+ execution agent of the execution resource associated with `sch`. If
281
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
282
+ executed on an unspecified execution agent.
283
+
284
+ #### `execution::schedule_from` <a id="exec.schedule.from">[[exec.schedule.from]]</a>
285
+
286
+ `schedule_from` schedules work dependent on the completion of a sender
287
+ onto a scheduler’s associated execution resource.
288
+
289
+ [*Note 1*: `schedule_from` is not meant to be used in user code; it is
290
+ used in the implementation of `continues_on`. — *end note*]
291
+
292
+ The name `schedule_from` denotes a customization point object. For some
293
+ subexpressions `sch` and `sndr`, let `Sch` be `decltype((sch))` and
294
+ `Sndr` be `decltype((sndr))`. If `Sch` does not satisfy `scheduler`, or
295
+ `Sndr` does not satisfy `sender`, `schedule_from(sch, sndr)` is
296
+ ill-formed.
297
+
298
+ Otherwise, the expression `schedule_from(sch, sndr)` is
299
+ expression-equivalent to:
300
+
301
+ ``` cpp
302
+ transform_sender(
303
+ query-with-default(get_domain, sch, default_domain()),
304
+ make-sender(schedule_from, sch, sndr))
305
+ ```
306
+
307
+ except that `sch` is evaluated only once.
308
+
309
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
310
+ specialized for `schedule_from_t` as follows:
311
+
312
+ ``` cpp
313
+ namespace std::execution {
314
+ template<>
315
+ struct impls-for<schedule_from_t> : default-impls {
316
+ static constexpr auto get-attrs = see below;
317
+ static constexpr auto get-state = see below;
318
+ static constexpr auto complete = see below;
319
+
320
+ template<class Sndr, class... Env>
321
+ static consteval void check-types();
322
+ };
323
+ }
324
+ ```
325
+
326
+ The member `impls-for<schedule_from_t>::get-attrs` is initialized with a
327
+ callable object equivalent to the following lambda:
328
+
329
+ ``` cpp
330
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
331
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
332
+ }
333
+ ```
334
+
335
+ The member `impls-for<schedule_from_t>::get-state` is initialized with a
336
+ callable object equivalent to the following lambda:
337
+
338
+ ``` cpp
339
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
340
+ requires sender_in<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)> {
341
+
342
+ auto& [_, sch, child] = sndr;
343
+
344
+ using sched_t = decltype(auto(sch));
345
+ using variant_t = see below;
346
+ using receiver_t = see below;
347
+ using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>;
348
+ constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr}));
349
+
350
+ struct state-type {
351
+ Rcvr& rcvr; // exposition only
352
+ variant_t async-result; // exposition only
353
+ operation_t op-state; // exposition only
354
+
355
+ explicit state-type(sched_t sch, Rcvr& rcvr) noexcept(nothrow)
356
+ : rcvr(rcvr), op-state(connect(schedule(sch), receiver_t{this})) {}
357
+ };
358
+
359
+ return state-type{sch, rcvr};
360
+ }
361
+ ```
362
+
363
+ ``` cpp
364
+ template<class Sndr, class... Env>
365
+ static consteval void check-types();
366
+ ```
367
+
368
+ *Effects:* Equivalent to:
369
+
370
+ ``` cpp
371
+ get_completion_signatures<schedule_result_t<data-type<Sndr>>, FWD-ENV-T(Env)...>();
372
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
373
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
374
+ ```
375
+
376
+ Objects of the local class *`state-type`* can be used to initialize a
377
+ structured binding.
378
+
379
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
380
+ specialization named by
381
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
382
+ Let *`as-tuple`* be an alias template such that `as-tuple<Tag(Args...)>`
383
+ denotes the type `decayed-tuple<Tag, Args...>`, and let
384
+ *`is-nothrow-decay-copy-sig`* be a variable template such that
385
+ `auto(is-nothrow-decay-copy-sig<Tag(Args...{})>)` is a constant
386
+ expression of type `bool` and equal to
387
+ `(is_nothrow_constructible_v<decay_t<Args>, Args> && ...)`. Let
388
+ *`error-completion`* be a pack consisting of the type
389
+ `set_error_t(exception_ptr)` if
390
+ `(is-nothrow-decay-copy-sig<Sigs> &&...)` is `false`, and an empty pack
391
+ otherwise. Then `variant_t` denotes the type
392
+ `variant<monostate, as-tuple<Sigs>..., error-completion...>`, except
393
+ with duplicate types removed.
394
+
395
+ `receiver_t` is an alias for the following exposition-only class:
396
+
397
+ ``` cpp
398
+ namespace std::execution {
399
+ struct receiver-type {
400
+ using receiver_concept = receiver_t;
401
+ state-type* state; // exposition only
402
+
403
+ void set_value() && noexcept {
404
+ visit(
405
+ [this]<class Tuple>(Tuple& result) noexcept -> void {
406
+ if constexpr (!same_as<monostate, Tuple>) {
407
+ auto& [tag, ...args] = result;
408
+ tag(std::move(state->rcvr), std::move(args)...);
409
+ }
410
+ },
411
+ state->async-result);
412
+ }
413
+
414
+ template<class Error>
415
+ void set_error(Error&& err) && noexcept {
416
+ execution::set_error(std::move(state->rcvr), std::forward<Error>(err));
417
+ }
418
+
419
+ void set_stopped() && noexcept {
420
+ execution::set_stopped(std::move(state->rcvr));
421
+ }
422
+
423
+ decltype(auto) get_env() const noexcept {
424
+ return FWD-ENV(execution::get_env(state->rcvr));
425
+ }
426
+ };
427
+ }
428
+ ```
429
+
430
+ The expression in the `noexcept` clause of the lambda is `true` if the
431
+ construction of the returned *`state-type`* object is not potentially
432
+ throwing; otherwise, `false`.
433
+
434
+ The member `impls-for<schedule_from_t>::complete` is initialized with a
435
+ callable object equivalent to the following lambda:
436
+
437
+ ``` cpp
438
+ []<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept
439
+ -> void {
440
+ using result_t = decayed-tuple<Tag, Args...>;
441
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...);
442
+
443
+ try {
444
+ state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
445
+ } catch (...) {
446
+ if constexpr (!nothrow)
447
+ state.async-result.template emplace<tuple<set_error_t,
448
+ exception_ptr>>(set_error, current_exception());
449
+ }
450
+ start(state.op-state);
451
+ };
452
+ ```
453
+
454
+ Let `out_sndr` be a subexpression denoting a sender returned from
455
+ `schedule_from(sch, sndr)` or one equal to such, and let `OutSndr` be
456
+ the type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression
457
+ denoting a receiver that has an environment of type `Env` such that
458
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
459
+ the operation state that results from connecting `out_sndr` with
460
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
461
+ execution agent and execute completion operations on `out_rcvr` on an
462
+ execution agent of the execution resource associated with `sch`. If
463
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
464
+ executed on an unspecified execution agent.
465
+
466
+ #### `execution::on` <a id="exec.on">[[exec.on]]</a>
467
+
468
+ The `on` sender adaptor has two forms:
469
+
470
+ - `on(sch, sndr)`, which starts a sender `sndr` on an execution agent
471
+ belonging to a scheduler `sch`’s associated execution resource and
472
+ that, upon `sndr`’s completion, transfers execution back to the
473
+ execution resource on which the `on` sender was started.
474
+ - `on(sndr, sch, closure)`, which upon completion of a sender `sndr`,
475
+ transfers execution to an execution agent belonging to a scheduler
476
+ `sch`’s associated execution resource, then executes a sender adaptor
477
+ closure `closure` with the async results of the sender, and that then
478
+ transfers execution back to the execution resource on which `sndr`
479
+ completed.
480
+
481
+ The name `on` denotes a pipeable sender adaptor object. For
482
+ subexpressions `sch` and `sndr`, `on(sch, sndr)` is ill-formed if any of
483
+ the following is `true`:
484
+
485
+ - `decltype((sch))` does not satisfy `scheduler`, or
486
+ - `decltype((sndr))` does not satisfy `sender` and `sndr` is not a
487
+ pipeable sender adaptor closure object [[exec.adapt.obj]], or
488
+ - `decltype((sndr))` satisfies `sender` and `sndr `is also a pipeable
489
+ sender adaptor closure object.
490
+
491
+ Otherwise, if `decltype((sndr))` satisfies `sender`, the expression
492
+ `on(sch, sndr)` is expression-equivalent to:
493
+
494
+ ``` cpp
495
+ transform_sender(
496
+ query-with-default(get_domain, sch, default_domain()),
497
+ make-sender(on, sch, sndr))
498
+ ```
499
+
500
+ except that `sch` is evaluated only once.
501
+
502
+ For subexpressions `sndr`, `sch`, and `closure`, if
503
+
504
+ - `decltype((sch))` does not satisfy `scheduler`, or
505
+ - `decltype((sndr))` does not satisfy `sender`, or
506
+ - `closure` is not a pipeable sender adaptor closure object
507
+ [[exec.adapt.obj]],
508
+
509
+ the expression `on(sndr, sch, closure)` is ill-formed; otherwise, it is
510
+ expression-equivalent to:
511
+
512
+ ``` cpp
513
+ transform_sender(
514
+ get-domain-early(sndr),
515
+ make-sender(on, product-type{sch, closure}, sndr))
516
+ ```
517
+
518
+ except that `sndr` is evaluated only once.
519
+
520
+ Let `out_sndr` and `env` be subexpressions, let `OutSndr` be
521
+ `decltype((out_sndr))`, and let `Env` be `decltype((env))`. If
522
+ `sender-for<OutSndr, on_t>` is `false`, then the expressions
523
+ `on.transform_env(out_sndr, env)` and
524
+ `on.transform_sender(out_sndr, env)` are ill-formed.
525
+
526
+ Otherwise: Let *`not-a-scheduler`* be an unspecified empty class type.
527
+
528
+ The expression `on.transform_env(out_sndr, env)` has effects equivalent
529
+ to:
530
+
531
+ ``` cpp
532
+ auto&& [_, data, _] = out_sndr;
533
+ if constexpr (scheduler<decltype(data)>) {
534
+ return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
535
+ } else {
536
+ return std::forward<Env>(env);
537
+ }
538
+ ```
539
+
540
+ The expression `on.transform_sender(out_sndr, env)` has effects
541
+ equivalent to:
542
+
543
+ ``` cpp
544
+ auto&& [_, data, child] = out_sndr;
545
+ if constexpr (scheduler<decltype(data)>) {
546
+ auto orig_sch =
547
+ query-with-default(get_scheduler, env, not-a-scheduler());
548
+
549
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
550
+ return not-a-sender{};
551
+ } else {
552
+ return continues_on(
553
+ starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
554
+ std::move(orig_sch));
555
+ }
556
+ } else {
557
+ auto& [sch, closure] = data;
558
+ auto orig_sch = query-with-default(
559
+ get_completion_scheduler<set_value_t>,
560
+ get_env(child),
561
+ query-with-default(get_scheduler, env, not-a-scheduler()));
562
+
563
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
564
+ return not-a-sender{};
565
+ } else {
566
+ return write_env(
567
+ continues_on(
568
+ std::forward_like<OutSndr>(closure)(
569
+ continues_on(
570
+ write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
571
+ sch)),
572
+ orig_sch),
573
+ SCHED-ENV(sch));
574
+ }
575
+ }
576
+ ```
577
+
578
+ Let `out_sndr` be a subexpression denoting a sender returned from
579
+ `on(sch, sndr)` or one equal to such, and let `OutSndr` be the type
580
+ `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting a
581
+ receiver that has an environment of type `Env` such that
582
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
583
+ the operation state that results from connecting `out_sndr` with
584
+ `out_rcvr`. Calling `start(op)` shall
585
+
586
+ - remember the current scheduler, `get_scheduler(get_env(rcvr))`;
587
+ - start `sndr` on an execution agent belonging to `sch`’s associated
588
+ execution resource;
589
+ - upon `sndr`’s completion, transfer execution back to the execution
590
+ resource associated with the scheduler remembered in step 1; and
591
+ - forward `sndr`’s async result to `out_rcvr`.
592
+
593
+ If any scheduling operation fails, an error completion on `out_rcvr`
594
+ shall be executed on an unspecified execution agent.
595
+
596
+ Let `out_sndr` be a subexpression denoting a sender returned from
597
+ `on(sndr, sch, closure)` or one equal to such, and let `OutSndr` be the
598
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
599
+ a receiver that has an environment of type `Env` such that
600
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
601
+ the operation state that results from connecting `out_sndr` with
602
+ `out_rcvr`. Calling `start(op)` shall
603
+
604
+ - remember the current scheduler, which is the first of the following
605
+ expressions that is well-formed:
606
+ - `get_completion_scheduler<set_value_t>(get_env(sndr))`
607
+ - `get_scheduler(get_env(rcvr))`;
608
+ - start `sndr` on the current execution agent;
609
+ - upon `sndr`’s completion, transfer execution to an agent owned by
610
+ `sch`’s associated execution resource;
611
+ - forward `sndr`’s async result as if by connecting and starting a
612
+ sender `closure(S)`, where `S` is a sender that completes
613
+ synchronously with `sndr`’s async result; and
614
+ - upon completion of the operation started in the previous step,
615
+ transfer execution back to the execution resource associated with the
616
+ scheduler remembered in step 1 and forward the operation’s async
617
+ result to `out_rcvr`.
618
+
619
+ If any scheduling operation fails, an error completion on `out_rcvr`
620
+ shall be executed on an unspecified execution agent.
621
+
622
+ #### `execution::then`, `execution::upon_error`, `execution::upon_stopped` <a id="exec.then">[[exec.then]]</a>
623
+
624
+ `then` attaches an invocable as a continuation for an input sender’s
625
+ value completion operation. `upon_error` and `upon_stopped` do the same
626
+ for the error and stopped completion operations, respectively, sending
627
+ the result of the invocable as a value completion.
628
+
629
+ The names `then`, `upon_error`, and `upon_stopped` denote pipeable
630
+ sender adaptor objects. Let the expression *`then-cpo`* be one of
631
+ `then`, `upon_error`, or `upon_stopped`. For subexpressions `sndr` and
632
+ `f`, if `decltype((sndr))` does not satisfy `sender`, or `decltype((f))`
633
+ does not satisfy `movable-value`, `then-cpo(sndr, f) `is ill-formed.
634
+
635
+ Otherwise, the expression `then-cpo(sndr, f)` is expression-equivalent
636
+ to:
637
+
638
+ ``` cpp
639
+ transform_sender(get-domain-early(sndr), make-sender(then-cpo, f, sndr))
640
+ ```
641
+
642
+ except that `sndr` is evaluated only once.
643
+
644
+ For `then`, `upon_error`, and `upon_stopped`, let *`set-cpo`* be
645
+ `set_value`, `set_error`, and `set_stopped`, respectively. The
646
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
647
+ specialized for *`then-cpo`* as follows:
648
+
649
+ ``` cpp
650
+ namespace std::execution {
651
+ template<>
652
+ struct impls-for<decayed-typeof<then-cpo>> : default-impls {
653
+ static constexpr auto complete =
654
+ []<class Tag, class... Args>
655
+ (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
656
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
657
+ TRY-SET-VALUE(rcvr,
658
+ invoke(std::move(fn), std::forward<Args>(args)...));
659
+ } else {
660
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
661
+ }
662
+ };
663
+
664
+ template<class Sndr, class... Env>
665
+ static consteval void check-types();
666
+ };
667
+ }
668
+ ```
669
+
670
+ ``` cpp
671
+ template<class Sndr, class... Env>
672
+ static consteval void check-types();
673
+ ```
674
+
675
+ *Effects:* Equivalent to:
676
+
677
+ ``` cpp
678
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
679
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
680
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>)
681
+ throw unspecified-exception();
682
+ };
683
+ cs.for-each(overload-set{fn, [](auto){}});
684
+ ```
685
+
686
+ where *`unspecified-exception`* is a type derived from `exception`.
687
+
688
+ The expression `then-cpo(sndr, f)` has undefined behavior unless it
689
+ returns a sender `out_sndr` that
690
+
691
+ - invokes `f` or a copy of such with the value, error, or stopped result
692
+ datums of `sndr` for `then`, `upon_error`, and `upon_stopped`,
693
+ respectively, using the result value of `f` as `out_sndr`’s value
694
+ completion, and
695
+ - forwards all other completion operations unchanged.
696
+
697
+ #### `execution::let_value`, `execution::let_error`, `execution::let_stopped` <a id="exec.let">[[exec.let]]</a>
698
+
699
+ `let_value`, `let_error`, and `let_stopped` transform a sender’s value,
700
+ error, and stopped completions, respectively, into a new child
701
+ asynchronous operation by passing the sender’s result datums to a
702
+ user-specified callable, which returns a new sender that is connected
703
+ and started.
704
+
705
+ For `let_value`, `let_error`, and `let_stopped`, let *`set-cpo`* be
706
+ `set_value`, `set_error`, and `set_stopped`, respectively. Let the
707
+ expression *`let-cpo`* be one of `let_value`, `let_error`, or
708
+ `let_stopped`. For a subexpression `sndr`, let `let-env(sndr)` be
709
+ expression-equivalent to the first well-formed expression below:
710
+
711
+ - `\exposid{SCHED-ENV}(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))`
712
+ - `\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))`
713
+ - `(void(sndr), env<>{})`
714
+
715
+ The names `let_value`, `let_error`, and `let_stopped` denote pipeable
716
+ sender adaptor objects. For subexpressions `sndr` and `f`, let `F` be
717
+ the decayed type of `f`. If `decltype((sndr))` does not satisfy `sender`
718
+ or if `decltype((f))` does not satisfy `movable-value`, the expression
719
+ `let-cpo(sndr, f)` is ill-formed. If `F` does not satisfy `invocable`,
720
+ the expression `let_stopped(sndr, f)` is ill-formed.
721
+
722
+ Otherwise, the expression `let-cpo(sndr, f)` is expression-equivalent
723
+ to:
724
+
725
+ ``` cpp
726
+ transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
727
+ ```
728
+
729
+ except that `sndr` is evaluated only once.
730
+
731
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
732
+ specialized for *`let-cpo`* as follows:
733
+
734
+ ``` cpp
735
+ namespace std::execution {
736
+ template<class State, class Rcvr, class... Args>
737
+ void let-bind(State& state, Rcvr& rcvr, Args&&... args); // exposition only
738
+
739
+ template<>
740
+ struct impls-for<decayed-typeof<let-cpo>> : default-impls {
741
+ static constexpr auto get-state = see below;
742
+ static constexpr auto complete = see below;
743
+
744
+ template<class Sndr, class... Env>
745
+ static consteval void check-types();
746
+ };
747
+ }
748
+ ```
749
+
750
+ Let *`receiver2`* denote the following exposition-only class template:
751
+
752
+ ``` cpp
753
+ namespace std::execution {
754
+ template<class Rcvr, class Env>
755
+ struct receiver2 {
756
+ using receiver_concept = receiver_t;
757
+
758
+ template<class... Args>
759
+ void set_value(Args&&... args) && noexcept {
760
+ execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
761
+ }
762
+
763
+ template<class Error>
764
+ void set_error(Error&& err) && noexcept {
765
+ execution::set_error(std::move(rcvr), std::forward<Error>(err));
766
+ }
767
+
768
+ void set_stopped() && noexcept {
769
+ execution::set_stopped(std::move(rcvr));
770
+ }
771
+
772
+ decltype(auto) get_env() const noexcept {
773
+ return see below;
774
+ }
775
+
776
+ Rcvr& rcvr; // exposition only
777
+ Env env; // exposition only
778
+ };
779
+ }
780
+ ```
781
+
782
+ Invocation of the function `receiver2::get_env` returns an object `e`
783
+ such that
784
+
785
+ - `decltype(e)` models `queryable` and
786
+ - given a query object `q`, the expression `e.query(q)` is
787
+ expression-equivalent to `env.query(q)` if that expression is valid;
788
+ otherwise, if the type of `q` satisfies `forwarding-query`,
789
+ `e.query(q)` is expression-equivalent to `get_env(rcvr).query(q)`;
790
+ otherwise, `e.query(q)` is ill-formed.
791
+
792
+ ``` cpp
793
+ template<class Sndr, class... Env>
794
+ static consteval void check-types();
795
+ ```
796
+
797
+ *Effects:* Equivalent to:
798
+
799
+ ``` cpp
800
+ using LetFn = remove_cvref_t<data-type<Sndr>>;
801
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
802
+ auto fn = []<class... Ts>(decayed-typeof<set-cpo>(*)(Ts...)) {
803
+ if constexpr (!is-valid-let-sender) // see below
804
+ throw unspecified-exception();
805
+ };
806
+ cs.for-each(overload-set(fn, [](auto){}));
807
+ ```
808
+
809
+ where *`unspecified-exception`* is a type derived from `exception`, and
810
+ where *`is-valid-let-sender`* is `true` if and only if all of the
811
+ following are `true`:
812
+
813
+ - `(constructible_from<decay_t<Ts>, Ts> &&...)`
814
+ - `invocable<LetFn, decay_t<Ts>&...>`
815
+ - `sender<invoke_result_t<LetFn, decay_t<Ts>&...>>`
816
+ - `sizeof...(Env) == 0 || sender_in<invoke_result_t<LetFn, decay_t<Ts>&...>, `*`env-t`*`...>`
817
+
818
+ where *`env-t`* is the pack
819
+ `decltype(`*`let-cpo`*`.transform_env(declval<Sndr>(), declval<Env>()))`.
820
+
821
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}`
822
+
823
+ is initialized with a callable object equivalent to the following:
824
+
825
+ ``` cpp
826
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
827
+ auto& [_, fn, child] = sndr;
828
+ using fn_t = decay_t<decltype(fn)>;
829
+ using env_t = decltype(let-env(child));
830
+ using args_variant_t = see below;
831
+ using ops2_variant_t = see below;
832
+
833
+ struct state-type {
834
+ fn_t fn; // exposition only
835
+ env_t env; // exposition only
836
+ args_variant_t args; // exposition only
837
+ ops2_variant_t ops2; // exposition only
838
+ };
839
+ return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
840
+ let-env(child), {}, {}};
841
+ }
842
+ ```
843
+
844
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
845
+ specialization named by
846
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
847
+ Let `LetSigs` be a pack of those types in `Sigs` with a return type of
848
+ `decayed-typeof<set-cpo>`. Let *`as-tuple`* be an alias template such
849
+ that `as-tuple<Tag(Args...)>` denotes the type `decayed-tuple<Args...>`.
850
+ Then `args_variant_t` denotes the type
851
+ `variant<monostate, as-tuple<LetSigs>...>` except with duplicate types
852
+ removed.
853
+
854
+ Given a type `Tag` and a pack `Args`, let *`as-sndr2`* be an alias
855
+ template such that `as-sndr2<Tag(Args...)>` denotes the type
856
+ `call-result-t<F, decay_t<Args>&...>`. Then `ops2_variant_t` denotes the
857
+ type
858
+
859
+ ``` cpp
860
+ variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, env_t>>...>
861
+ ```
862
+
863
+ except with duplicate types removed.
864
+
865
+ The *requires-clause* constraining the above lambda is satisfied if and
866
+ only if the types `args_variant_t` and `ops2_variant_t` are well-formed.
867
+
868
+ The exposition-only function template *`let-bind`* has effects
869
+ equivalent to:
870
+
871
+ ``` cpp
872
+ using args_t = decayed-tuple<Args...>;
873
+ auto mkop2 = [&] {
874
+ return connect(
875
+ apply(std::move(state.fn),
876
+ state.args.template emplace<args_t>(std::forward<Args>(args)...)),
877
+ receiver2{rcvr, std::move(state.env)});
878
+ };
879
+ start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
880
+ ```
881
+
882
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{complete}`
883
+
884
+ is initialized with a callable object equivalent to the following:
885
+
886
+ ``` cpp
887
+ []<class Tag, class... Args>
888
+ (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
889
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
890
+ TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args>(args)...));
891
+ } else {
892
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
893
+ }
894
+ }
895
+ ```
896
+
897
+ Let `sndr` and `env` be subexpressions, and let `Sndr` be
898
+ `decltype((sndr))`. If `sender-for<Sndr, decayed-typeof<let-cpo>>` is
899
+ `false`, then the expression `let-cpo.transform_env(sndr, env)` is
900
+ ill-formed. Otherwise, it is equal to:
901
+
902
+ ``` cpp
903
+ auto& [_, _, child] = sndr;
904
+ return JOIN-ENV(let-env(child), FWD-ENV(env));
905
+ ```
906
+
907
+ Let the subexpression `out_sndr` denote the result of the invocation
908
+ `let-cpo(sndr, f)` or an object equal to such, and let the subexpression
909
+ `rcvr` denote a receiver such that the expression
910
+ `connect(out_sndr, rcvr)` is well-formed. The expression
911
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
912
+ asynchronous operation [[exec.async.ops]] that, when started:
913
+
914
+ - invokes `f` when *`set-cpo`* is called with `sndr`’s result datums,
915
+ - makes its completion dependent on the completion of a sender returned
916
+ by `f`, and
917
+ - propagates the other completion operations sent by `sndr`.
918
+
919
+ #### `execution::bulk`, `execution::bulk_chunked`, and `execution::bulk_unchunked` <a id="exec.bulk">[[exec.bulk]]</a>
920
+
921
+ `bulk`, `bulk_chunked`, and `bulk_unchunked` run a task repeatedly for
922
+ every index in an index space.
923
+
924
+ The names `bulk`, `bulk_chunked`, and `bulk_unchunked` denote pipeable
925
+ sender adaptor objects. Let `bulk-algo` be either `bulk`,
926
+ `bulk_chunked`, or `bulk_unchunked`. For subexpressions `sndr`,
927
+ `policy`, `shape`, and `f`, let `Policy` be
928
+ `remove_cvref_t<decltype(policy)>`, `Shape` be `decltype(auto(shape))`,
929
+ and `Func` be `decay_t<decltype((f))>`. If
930
+
931
+ - `decltype((sndr))` does not satisfy `sender`, or
932
+ - `is_execution_policy_v<Policy>` is `false`, or
933
+ - `Shape` does not satisfy `integral`, or
934
+ - `Func` does not model `copy_constructible`,
935
+
936
+ `bulk-algo(sndr, policy, shape, f)` is ill-formed.
937
+
938
+ Otherwise, the expression `bulk-algo(sndr, policy, shape, f)` is
939
+ expression-equivalent to:
940
+
941
+ ``` cpp
942
+ transform_sender(get-domain-early(sndr), make-sender(
943
+ bulk-algo, product-type<see below, Shape, Func>{policy, shape, f}, sndr))
944
+ ```
945
+
946
+ except that `sndr` is evaluated only once. The first template argument
947
+ of *`product-type`* is `Policy` if `Policy` models `copy_constructible`,
948
+ and `const Policy&` otherwise.
949
+
950
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
951
+ `decltype((sndr))`. If `sender-for<Sndr, bulk_t>` is `false`, then the
952
+ expression `bulk.transform_sender(sndr, env)` is ill-formed; otherwise,
953
+ it is equivalent to:
954
+
955
+ ``` cpp
956
+ auto [_, data, child] = sndr;
957
+ auto& [policy, shape, f] = data;
958
+ auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)
959
+ noexcept(noexcept(f(begin, vs...))) {
960
+ while (begin != end) func(begin++, vs...);
961
+ }
962
+ return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
963
+ ```
964
+
965
+ [*Note 1*: This causes the `bulk(sndr, policy, shape, f)` sender to be
966
+ expressed in terms of `bulk_chunked(sndr, policy, shape, f)` when it is
967
+ connected to a receiver whose execution domain does not customize
968
+ `bulk`. — *end note*]
969
+
970
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
971
+ specialized for `bulk_chunked_t` as follows:
972
+
973
+ ``` cpp
974
+ namespace std::execution {
975
+ template<>
976
+ struct impls-for<bulk_chunked_t> : default-impls {
977
+ static constexpr auto complete = see below;
978
+
979
+ template<class Sndr, class... Env>
980
+ static consteval void check-types();
981
+ };
982
+ }
983
+ ```
984
+
985
+ The member `impls-for<bulk_chunked_t>::complete` is initialized with a
986
+ callable object equivalent to the following lambda:
987
+
988
+ ``` cpp
989
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
990
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
991
+ -> void requires see below {
992
+ if constexpr (same_as<Tag, set_value_t>) {
993
+ auto& [policy, shape, f] = state;
994
+ constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
995
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
996
+ f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
997
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
998
+ }());
999
+ } else {
1000
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
1001
+ }
1002
+ }
1003
+ ```
1004
+
1005
+ The expression in the *requires-clause* of the lambda above is `true` if
1006
+ and only if `Tag` denotes a type other than `set_value_t` or if the
1007
+ expression `f(auto(shape), auto(shape), args...)` is well-formed.
1008
+
1009
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1010
+ specialized for `bulk_unchunked_t` as follows:
1011
+
1012
+ ``` cpp
1013
+ namespace std::execution {
1014
+ template<>
1015
+ struct impls-for<bulk_unchunked_t> : default-impls {
1016
+ static constexpr auto complete = see below;
1017
+ };
1018
+ }
1019
+ ```
1020
+
1021
+ The member `impls-for<bulk_unchunked_t>::complete` is initialized with a
1022
+ callable object equivalent to the following lambda:
1023
+
1024
+ ``` cpp
1025
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
1026
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
1027
+ -> void requires see below {
1028
+ if constexpr (same_as<Tag, set_value_t>) {
1029
+ auto& [policy, shape, f] = state;
1030
+ constexpr bool nothrow = noexcept(f(auto(shape), args...));
1031
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
1032
+ for (decltype(auto(shape)) i = 0; i < shape; ++i) {
1033
+ f(auto(i), args...);
1034
+ }
1035
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
1036
+ }());
1037
+ } else {
1038
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
1039
+ }
1040
+ }
1041
+ ```
1042
+
1043
+ The expression in the *requires-clause* of the lambda above is `true` if
1044
+ and only if `Tag` denotes a type other than `set_value_t` or if the
1045
+ expression `f(auto(shape), args...)` is well-formed.
1046
+
1047
+ ``` cpp
1048
+ template<class Sndr, class... Env>
1049
+ static consteval void check-types();
1050
+ ```
1051
+
1052
+ *Effects:* Equivalent to:
1053
+
1054
+ ``` cpp
1055
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
1056
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
1057
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts&...>)
1058
+ throw unspecified-exception();
1059
+ };
1060
+ cs.for-each(overload-set(fn, [](auto){}));
1061
+ ```
1062
+
1063
+ where *`unspecified-exception`* is a type derived from `exception`.
1064
+
1065
+ Let the subexpression `out_sndr` denote the result of the invocation
1066
+ `bulk-algo(sndr, policy, shape, f)` or an object equal to such, and let
1067
+ the subexpression `rcvr` denote a receiver such that the expression
1068
+ `connect(out_sndr, rcvr)` is well-formed. The expression
1069
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
1070
+ asynchronous operation [[exec.async.ops]] that, when started:
1071
+
1072
+ - If `sndr` has a successful completion, where `args` is a pack of
1073
+ lvalue subexpressions referring to the value completion result datums
1074
+ of `sndr`, or decayed copies of those values if they model
1075
+ `copy_constructible`, then:
1076
+ - If `out_sndr` also completes successfully, then:
1077
+ - for `bulk`, invokes `f(i, args...)` for every i of type `Shape`
1078
+ from `0` to `shape`;
1079
+ - for `bulk_unchunked`, invokes `f(i, args...)` for every i of type
1080
+ `Shape` from `0` to `shape`; *Recommended practice:* The
1081
+ underlying scheduler should execute each iteration on a distinct
1082
+ execution agent.
1083
+ - for `bulk_chunked`, invokes `f(b, e, args...)` zero or more times
1084
+ with pairs of b and e of type `Shape` in range \[`0`, `shape`\],
1085
+ such that b < e and for every i of type `Shape` from `0` to
1086
+ `shape`, there is exactly one invocation with a pair b and e, such
1087
+ that i is in the range \[b, e).
1088
+ - If `out_sndr` completes with `set_error(rcvr, eptr)`, then the
1089
+ asynchronous operation may invoke a subset of the invocations of `f`
1090
+ before the error completion handler is called, and `eptr` is an
1091
+ `exception_ptr` containing either:
1092
+ - an exception thrown by an invocation of `f`, or
1093
+ - a `bad_alloc` exception if the implementation fails to allocate
1094
+ required resources, or
1095
+ - an exception derived from `runtime_error`.
1096
+ - If `out_sndr` completes with `set_stopped(rcvr)`, then the
1097
+ asynchronous operation may invoke a subset of the invocations of `f`
1098
+ before the stopped completion handler.
1099
+ - If `sndr` does not complete with `set_value`, then the completion is
1100
+ forwarded to `recv`.
1101
+ - For `bulk-algo`, the parameter `policy` describes the manner in which
1102
+ the execution of the asynchronous operations corresponding to these
1103
+ algorithms may be parallelized and the manner in which they apply `f`.
1104
+ Permissions and requirements on parallel algorithm element access
1105
+ functions [[algorithms.parallel.exec]] apply to `f`.
1106
+
1107
+ [*Note 2*: The asynchronous operation corresponding to
1108
+ `bulk-algo(sndr, policy, shape, f)` can complete with `set_stopped` if
1109
+ cancellation is requested or ignore cancellation
1110
+ requests. — *end note*]
1111
+
1112
+ #### `execution::when_all` <a id="exec.when.all">[[exec.when.all]]</a>
1113
+
1114
+ `when_all` and `when_all_with_variant` both adapt multiple input senders
1115
+ into a sender that completes when all input senders have completed.
1116
+ `when_all` only accepts senders with a single value completion signature
1117
+ and on success concatenates all the input senders’ value result datums
1118
+ into its own value completion operation.
1119
+ `when_all_with_variant(sndrs...)` is semantically equivalent to
1120
+ w`hen_all(into_variant(sndrs)...)`, where `sndrs` is a pack of
1121
+ subexpressions whose types model `sender`.
1122
+
1123
+ The names `when_all` and `when_all_with_variant` denote customization
1124
+ point objects. Let `sndrs` be a pack of subexpressions, let `Sndrs` be a
1125
+ pack of the types `decltype((sndrs))...`, and let `CD` be the type
1126
+ `common_type_t<decltype(get-domain-early(sndrs))...>`. Let `CD2` be `CD`
1127
+ if `CD` is well-formed, and `default_domain` otherwise. The expressions
1128
+ `when_all(sndrs...)` and `when_all_with_variant(sndrs...)` are
1129
+ ill-formed if any of the following is `true`:
1130
+
1131
+ - `sizeof...(sndrs)` is `0`, or
1132
+ - `(sender<Sndrs> && ...)` is `false`.
1133
+
1134
+ The expression `when_all(sndrs...)` is expression-equivalent to:
1135
+
1136
+ ``` cpp
1137
+ transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
1138
+ ```
1139
+
1140
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1141
+ specialized for `when_all_t` as follows:
1142
+
1143
+ ``` cpp
1144
+ namespace std::execution {
1145
+ template<>
1146
+ struct impls-for<when_all_t> : default-impls {
1147
+ static constexpr auto get-attrs = see below;
1148
+ static constexpr auto get-env = see below;
1149
+ static constexpr auto get-state = see below;
1150
+ static constexpr auto start = see below;
1151
+ static constexpr auto complete = see below;
1152
+
1153
+ template<class Sndr, class... Env>
1154
+ static consteval void check-types();
1155
+ };
1156
+ }
1157
+ ```
1158
+
1159
+ Let *`make-when-all-env`* be the following exposition-only function
1160
+ template:
1161
+
1162
+ ``` cpp
1163
+ template<class Env>
1164
+ constexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only
1165
+ Env&& env) noexcept {
1166
+ return see below;
1167
+ }
1168
+ ```
1169
+
1170
+ Returns an object `e` such that
1171
+
1172
+ - `decltype(e)` models `queryable`, and
1173
+ - `e.query(get_stop_token)` is expression-equivalent to
1174
+ `state.stop-src.get_token()`, and
1175
+ - given a query object `q` with type other than cv `get_stop_token_t`
1176
+ and whose type satisfies *`forwarding-query`*, `e.query(q)` is
1177
+ expression-equivalent to `get_env(rcvr).query(q)`.
1178
+
1179
+ Let `when-all-env` be an alias template such that `when-all-env<Env>`
1180
+ denotes the type
1181
+ `decltype(make-{when-all-env}(declval<inplace_stop_source&>(), declval<Env>()))`.
1182
+
1183
+ ``` cpp
1184
+ template<class Sndr, class... Env>
1185
+ static consteval void check-types();
1186
+ ```
1187
+
1188
+ Let `Is` be the pack of integral template arguments of the
1189
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
1190
+
1191
+ *Effects:* Equivalent to:
1192
+
1193
+ ``` cpp
1194
+ auto fn = []<class Child>() {
1195
+ auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
1196
+ if constexpr (cs.count-of(set_value) >= 2)
1197
+ throw unspecified-exception();
1198
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
1199
+ };
1200
+ (fn.template operator()<child-type<Sndr, Is>>(), ...);
1201
+ ```
1202
+
1203
+ where *`unspecified-exception`* is a type derived from `exception`.
1204
+
1205
+ *Throws:* Any exception thrown as a result of evaluating the *Effects*,
1206
+ or an exception of an unspecified type derived from `exception` when
1207
+ `CD` is ill-formed.
1208
+
1209
+ The member `impls-for<when_all_t>::get-attrs` is initialized with a
1210
+ callable object equivalent to the following lambda expression:
1211
+
1212
+ ``` cpp
1213
+ [](auto&&, auto&&... child) noexcept {
1214
+ if constexpr (same_as<CD, default_domain>) {
1215
+ return env<>();
1216
+ } else {
1217
+ return MAKE-ENV(get_domain, CD());
1218
+ }
1219
+ }
1220
+ ```
1221
+
1222
+ The member `impls-for<when_all_t>::get-env` is initialized with a
1223
+ callable object equivalent to the following lambda expression:
1224
+
1225
+ ``` cpp
1226
+ []<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
1227
+ return make-when-all-env(state.stop-src, get_env(rcvr));
1228
+ }
1229
+ ```
1230
+
1231
+ The member `impls-for<when_all_t>::get-state` is initialized with a
1232
+ callable object equivalent to the following lambda expression:
1233
+
1234
+ ``` cpp
1235
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
1236
+ return e;
1237
+ }
1238
+ ```
1239
+
1240
+ where e is the expression
1241
+
1242
+ ``` cpp
1243
+ std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
1244
+ ```
1245
+
1246
+ and where *`make-state`* is the following exposition-only class
1247
+ template:
1248
+
1249
+ ``` cpp
1250
+ enum class disposition { started, error, stopped }; // exposition only
1251
+
1252
+ template<class Rcvr>
1253
+ struct make-state {
1254
+ template<class... Sndrs>
1255
+ auto operator()(auto, auto, Sndrs&&... sndrs) const {
1256
+ using values_tuple = see below;
1257
+ using errors_variant = see below;
1258
+ using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
1259
+
1260
+ struct state-type {
1261
+ void arrive(Rcvr& rcvr) noexcept { // exposition only
1262
+ if (0 == --count) {
1263
+ complete(rcvr);
1264
+ }
1265
+ }
1266
+
1267
+ void complete(Rcvr& rcvr) noexcept; // exposition only
1268
+
1269
+ atomic<size_t> count{sizeof...(sndrs)}; // exposition only
1270
+ inplace_stop_source stop_src{}; // exposition only
1271
+ atomic<disposition> disp{disposition::started}; // exposition only
1272
+ errors_variant errors{}; // exposition only
1273
+ values_tuple values{}; // exposition only
1274
+ optional<stop_callback> on_stop{nullopt}; // exposition only
1275
+ };
1276
+
1277
+ return state-type{};
1278
+ }
1279
+ };
1280
+ ```
1281
+
1282
+ Let *`copy-fail`* be `exception_ptr` if decay-copying any of the child
1283
+ senders’ result datums can potentially throw; otherwise, `none-such`,
1284
+ where `none-such` is an unspecified empty class type.
1285
+
1286
+ The alias `values_tuple` denotes the type
1287
+
1288
+ ``` cpp
1289
+ tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
1290
+ ```
1291
+
1292
+ if that type is well-formed; otherwise, `tuple<>`.
1293
+
1294
+ The alias `errors_variant` denotes the type
1295
+ `variant<none-such, copy-fail, Es...>` with duplicate types removed,
1296
+ where `Es` is the pack of the decayed types of all the child senders’
1297
+ possible error result datums.
1298
+
1299
+ The member `void state-type::complete(Rcvr& rcvr) noexcept` behaves as
1300
+ follows:
1301
+
1302
+ - If `disp` is equal to `disposition::started`, evaluates:
1303
+ ``` cpp
1304
+ auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
1305
+ auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
1306
+
1307
+ on_stop.reset();
1308
+ apply(
1309
+ [&](auto&... opts) noexcept {
1310
+ apply(set, tuple_cat(tie(*opts)...));
1311
+ },
1312
+ values);
1313
+ ```
1314
+ - Otherwise, if `disp` is equal to `disposition::error`, evaluates:
1315
+ ``` cpp
1316
+ on_stop.reset();
1317
+ visit(
1318
+ [&]<class Error>(Error& error) noexcept {
1319
+ if constexpr (!same_as<Error, none-such>) {
1320
+ set_error(std::move(rcvr), std::move(error));
1321
+ }
1322
+ },
1323
+ errors);
1324
+ ```
1325
+ - Otherwise, evaluates:
1326
+ ``` cpp
1327
+ on_stop.reset();
1328
+ set_stopped(std::move(rcvr));
1329
+ ```
1330
+
1331
+ The member `impls-for<when_all_t>::start` is initialized with a callable
1332
+ object equivalent to the following lambda expression:
1333
+
1334
+ ``` cpp
1335
+ []<class State, class Rcvr, class... Ops>(
1336
+ State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
1337
+ state.on_stop.emplace(
1338
+ get_stop_token(get_env(rcvr)),
1339
+ on-stop-request{state.stop_src});
1340
+ if (state.stop_src.stop_requested()) {
1341
+ state.on_stop.reset();
1342
+ set_stopped(std::move(rcvr));
1343
+ } else {
1344
+ (start(ops), ...);
1345
+ }
1346
+ }
1347
+ ```
1348
+
1349
+ The member `impls-for<when_all_t>::complete` is initialized with a
1350
+ callable object equivalent to the following lambda expression:
1351
+
1352
+ ``` cpp
1353
+ []<class Index, class State, class Rcvr, class Set, class... Args>(
1354
+ this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
1355
+ if constexpr (same_as<Set, set_error_t>) {
1356
+ if (disposition::error != state.disp.exchange(disposition::error)) {
1357
+ state.stop_src.request_stop();
1358
+ TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
1359
+ }
1360
+ } else if constexpr (same_as<Set, set_stopped_t>) {
1361
+ auto expected = disposition::started;
1362
+ if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
1363
+ state.stop_src.request_stop();
1364
+ }
1365
+ } else if constexpr (!same_as<decltype(State::values), tuple<>>) {
1366
+ if (state.disp == disposition::started) {
1367
+ auto& opt = get<Index::value>(state.values);
1368
+ TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
1369
+ }
1370
+ }
1371
+ state.arrive(rcvr);
1372
+ }
1373
+ ```
1374
+
1375
+ where `TRY-EMPLACE-ERROR(v, e)`, for subexpressions `v` and `e`, is
1376
+ equivalent to:
1377
+
1378
+ ``` cpp
1379
+ try {
1380
+ v.template emplace<decltype(auto(e))>(e);
1381
+ } catch (...) {
1382
+ v.template emplace<exception_ptr>(current_exception());
1383
+ }
1384
+ ```
1385
+
1386
+ if the expression `decltype(auto(e))(e)` is potentially throwing;
1387
+ otherwise, `v.template emplace<decltype(auto(e))>(e)`; and where
1388
+ `TRY-EMPLACE-VALUE(c, o, as...)`, for subexpressions `c`, `o`, and pack
1389
+ of subexpressions `as`, is equivalent to:
1390
+
1391
+ ``` cpp
1392
+ try {
1393
+ o.emplace(as...);
1394
+ } catch (...) {
1395
+ c(Index(), state, rcvr, set_error, current_exception());
1396
+ return;
1397
+ }
1398
+ ```
1399
+
1400
+ if the expression `decayed-tuple<decltype(as)...>{as...}` is potentially
1401
+ throwing; otherwise, `o.emplace(as...)`.
1402
+
1403
+ The expression `when_all_with_variant(sndrs...)` is
1404
+ expression-equivalent to:
1405
+
1406
+ ``` cpp
1407
+ transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
1408
+ ```
1409
+
1410
+ Given subexpressions `sndr` and `env`, if
1411
+ `sender-for<decltype((sndr)), when_all_with_variant_t>` is `false`, then
1412
+ the expression `when_all_with_variant.transform_sender(sndr, env)` is
1413
+ ill-formed; otherwise, it is equivalent to:
1414
+
1415
+ ``` cpp
1416
+ auto&& [_, _, ...child] = sndr;
1417
+ return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
1418
+ ```
1419
+
1420
+ [*Note 1*: This causes the `when_all_with_variant(sndrs...)` sender to
1421
+ become `when_all(into_variant(sndrs)...)` when it is connected with a
1422
+ receiver whose execution domain does not customize
1423
+ `when_all_with_variant`. — *end note*]
1424
+
1425
+ #### `execution::into_variant` <a id="exec.into.variant">[[exec.into.variant]]</a>
1426
+
1427
+ `into_variant` adapts a sender with multiple value completion signatures
1428
+ into a sender with just one value completion signature consisting of a
1429
+ `variant` of `tuple`s.
1430
+
1431
+ The name `into_variant` denotes a pipeable sender adaptor object. For a
1432
+ subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If `Sndr` does
1433
+ not satisfy `sender`, `into_variant(sndr)` is ill-formed.
1434
+
1435
+ Otherwise, the expression `into_variant(sndr)` is expression-equivalent
1436
+ to:
1437
+
1438
+ ``` cpp
1439
+ transform_sender(get-domain-early(sndr), make-sender(into_variant, {}, sndr))
1440
+ ```
1441
+
1442
+ except that `sndr` is only evaluated once.
1443
+
1444
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1445
+ specialized for `into_variant` as follows:
1446
+
1447
+ ``` cpp
1448
+ namespace std::execution {
1449
+ template<>
1450
+ struct impls-for<into_variant_t> : default-impls {
1451
+ static constexpr auto get-state = see below;
1452
+ static constexpr auto complete = see below;
1453
+
1454
+ template<class Sndr, class... Env>
1455
+ static consteval void check-types() {
1456
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
1457
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
1458
+ }
1459
+ };
1460
+ }
1461
+ ```
1462
+
1463
+ The member `impls-for<into_variant_t>::get-state` is initialized with a
1464
+ callable object equivalent to the following lambda:
1465
+
1466
+ ``` cpp
1467
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
1468
+ -> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
1469
+ return {};
1470
+ }
1471
+ ```
1472
+
1473
+ The member `impls-for<into_variant_t>::complete` is initialized with a
1474
+ callable object equivalent to the following lambda:
1475
+
1476
+ ``` cpp
1477
+ []<class State, class Rcvr, class Tag, class... Args>(
1478
+ auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
1479
+ if constexpr (same_as<Tag, set_value_t>) {
1480
+ using variant_type = State::type;
1481
+ TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
1482
+ } else {
1483
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
1484
+ }
1485
+ }
1486
+ ```
1487
+
1488
+ #### `execution::stopped_as_optional` <a id="exec.stopped.opt">[[exec.stopped.opt]]</a>
1489
+
1490
+ `stopped_as_optional` maps a sender’s stopped completion operation into
1491
+ a value completion operation as a disengaged `optional`. The sender’s
1492
+ value completion operation is also converted into an `optional`. The
1493
+ result is a sender that never completes with stopped, reporting
1494
+ cancellation by completing with a disengaged `optional`.
1495
+
1496
+ The name `stopped_as_optional` denotes a pipeable sender adaptor object.
1497
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
1498
+ expression `stopped_as_optional(sndr)` is expression-equivalent to:
1499
+
1500
+ ``` cpp
1501
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_optional, {}, sndr))
1502
+ ```
1503
+
1504
+ except that `sndr` is only evaluated once.
1505
+
1506
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1507
+ specialized for `stopped_as_optional_t` as follows:
1508
+
1509
+ ``` cpp
1510
+ namespace std::execution {
1511
+ template<>
1512
+ struct impls-for<stopped_as_optional_t> : default-impls {
1513
+ template<class Sndr, class... Env>
1514
+ static consteval void check-types() {
1515
+ default-impls::check-types<Sndr, Env...>();
1516
+ if constexpr (!requires {
1517
+ requires (!same_as<void, single-sender-value-type<child-type<Sndr>,
1518
+ FWD-ENV-T(Env)...>>); })
1519
+ throw unspecified-exception();
1520
+ }
1521
+ };
1522
+ }
1523
+ ```
1524
+
1525
+ where `unspecified-exception` is a type derived from `exception`.
1526
+
1527
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
1528
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
1529
+ `sender-for<Sndr, stopped_as_optional_t>` is `false` then the expression
1530
+ `stopped_as_optional.transform_sender(sndr, env)` is ill-formed;
1531
+ otherwise, if `sender_in<child-type<Sndr>, FWD-ENV-T(Env)>` is `false`,
1532
+ the expression `stopped_as_optional.transform_sender(sndr, env)` is
1533
+ equivalent to `not-a-sender()`; otherwise, it is equivalent to:
1534
+
1535
+ ``` cpp
1536
+ auto&& [_, _, child] = sndr;
1537
+ using V = single-sender-value-type<child-type<Sndr>, FWD-ENV-T(Env)>;
1538
+ return let_stopped(
1539
+ then(std::forward_like<Sndr>(child),
1540
+ []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {
1541
+ return optional<V>(in_place, std::forward<Ts>(ts)...);
1542
+ }),
1543
+ []() noexcept { return just(optional<V>()); });
1544
+ ```
1545
+
1546
+ #### `execution::stopped_as_error` <a id="exec.stopped.err">[[exec.stopped.err]]</a>
1547
+
1548
+ `stopped_as_error` maps an input sender’s stopped completion operation
1549
+ into an error completion operation as a custom error type. The result is
1550
+ a sender that never completes with stopped, reporting cancellation by
1551
+ completing with an error.
1552
+
1553
+ The name `stopped_as_error` denotes a pipeable sender adaptor object.
1554
+ For some subexpressions `sndr` and `err`, let `Sndr` be
1555
+ `decltype((sndr))` and let `Err` be `decltype((err))`. If the type
1556
+ `Sndr` does not satisfy `sender` or if the type `Err` does not satisfy
1557
+ `movable-value`, `stopped_as_error(sndr, err)` is ill-formed. Otherwise,
1558
+ the expression `stopped_as_error(sndr, err)` is expression-equivalent
1559
+ to:
1560
+
1561
+ ``` cpp
1562
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_error, err, sndr))
1563
+ ```
1564
+
1565
+ except that `sndr` is only evaluated once.
1566
+
1567
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
1568
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
1569
+ `sender-for<Sndr, stopped_as_error_t>` is `false`, then the expression
1570
+ `stopped_as_error.transform_sender(sndr, env)` is ill-formed; otherwise,
1571
+ it is equivalent to:
1572
+
1573
+ ``` cpp
1574
+ auto&& [_, err, child] = sndr;
1575
+ using E = decltype(auto(err));
1576
+ return let_stopped(
1577
+ std::forward_like<Sndr>(child),
1578
+ [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {
1579
+ return just_error(std::move(err));
1580
+ });
1581
+ ```
1582
+
1583
+ #### `execution::associate` <a id="exec.associate">[[exec.associate]]</a>
1584
+
1585
+ `associate` tries to associate a sender with an async scope such that
1586
+ the scope can track the lifetime of any asynchronous operations created
1587
+ with the sender.
1588
+
1589
+ Let *`associate-data`* be the following exposition-only class template:
1590
+
1591
+ ``` cpp
1592
+ namespace std::execution {
1593
+ template<scope_token Token, sender Sender>
1594
+ struct associate-data { // exposition only
1595
+ using wrap-sender = // exposition only
1596
+ remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>;
1597
+
1598
+ explicit associate-data(Token t, Sender&& s)
1599
+ : sndr(t.wrap(std::forward<Sender>(s))),
1600
+ token(t) {
1601
+ if (!token.try_associate())
1602
+ sndr.reset();
1603
+ }
1604
+
1605
+ associate-data(const associate-data& other)
1606
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
1607
+ noexcept(other.token.try_associate()));
1608
+
1609
+ associate-data(associate-data&& other)
1610
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
1611
+
1612
+ ~associate-data();
1613
+
1614
+ optional<pair<Token, wrap-sender>>
1615
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
1616
+
1617
+ private:
1618
+ optional<wrap-sender> sndr; // exposition only
1619
+ Token token; // exposition only
1620
+ };
1621
+
1622
+ template<scope_token Token, sender Sender>
1623
+ associate-data(Token, Sender&&) -> associate-data<Token, Sender>;
1624
+ }
1625
+ ```
1626
+
1627
+ For an *`associate-data`* object `a`, `a.sndr.has_value()` is `true` if
1628
+ and only if an association was successfully made and is owned by `a`.
1629
+
1630
+ ``` cpp
1631
+ associate-data(const associate-data& other)
1632
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
1633
+ noexcept(other.token.try_associate()));
1634
+ ```
1635
+
1636
+ *Constraints:* `copy_constructible<`*`wrap-sender`*`>` is `true`.
1637
+
1638
+ *Effects:* Value-initializes *sndr* and initializes *token* with
1639
+ `other.`*`token`*. If `other.`*`sndr`*`.has_value()` is `false`, no
1640
+ further effects; otherwise, calls *`token`*`.try_associate()` and, if
1641
+ that returns `true`, calls *`sndr`*`.emplace(*other.`*`sndr`*`)` and, if
1642
+ that exits with an exception, calls *`token`*`.disassociate()` before
1643
+ propagating the exception.
1644
+
1645
+ ``` cpp
1646
+ associate-data(associate-data&& other)
1647
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
1648
+ ```
1649
+
1650
+ *Effects:* Initializes *sndr* with `std::move(other.`*`sndr`*`)` and
1651
+ initializes *token* with `std::move(other.`*`token`*`)` and then calls
1652
+ `other.`*`sndr`*`.reset()`.
1653
+
1654
+ ``` cpp
1655
+ ~associate-data();
1656
+ ```
1657
+
1658
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then no effect;
1659
+ otherwise, invokes *`sndr`*`.reset()` before invoking
1660
+ *`token`*`.disassociate()`.
1661
+
1662
+ ``` cpp
1663
+ optional<pair<Token, wrap-sender>>
1664
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
1665
+ ```
1666
+
1667
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then returns an
1668
+ `optional` that does not contain a value; otherwise returns an
1669
+ `optional` containing a value of type `pair<Token, `*`wrap-sender`*`>`
1670
+ as if by:
1671
+
1672
+ ``` cpp
1673
+ return optional(pair(token, std::move(*sndr)));
1674
+ ```
1675
+
1676
+ *Ensures:* *sndr* does not contain a value.
1677
+
1678
+ The name `associate` denotes a pipeable sender adaptor object. For
1679
+ subexpressions `sndr` and `token`:
1680
+
1681
+ - If `decltype((sndr))` does not satisfy `sender`, or
1682
+ `remove_cvref_t<decltype((token))>` does not satisfy `scope_token`,
1683
+ then `associate(sndr, token)` is ill-formed.
1684
+ - Otherwise, the expression `associate(sndr, token)` is
1685
+ expression-equivalent to:
1686
+ ``` cpp
1687
+ transform_sender(get-domain-early(sndr),
1688
+ make-sender(associate, associate-data(token, sndr)))
1689
+ ```
1690
+
1691
+ except that `sndr` is evaluated only once.
1692
+
1693
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1694
+ specialized for `associate_t` as follows:
1695
+
1696
+ ``` cpp
1697
+ namespace std::execution {
1698
+ template<>
1699
+ struct impls-for<associate_t> : default-impls {
1700
+ static constexpr auto get-state = see below; // exposition only
1701
+ static constexpr auto start = see below; // exposition only
1702
+
1703
+ template<class Sndr, class... Env>
1704
+ static consteval void check-types() { // exposition only
1705
+ using associate_data_t = remove_cvref_t<data-type<Sndr>>;
1706
+ using child_type_t = associate_data_t::wrap-sender;
1707
+ (void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>();
1708
+ }
1709
+ };
1710
+ }
1711
+ ```
1712
+
1713
+ The member `impls-for<associate_t>::get-state` is initialized with a
1714
+ callable object equivalent to the following lambda:
1715
+
1716
+ ``` cpp
1717
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below) {
1718
+ auto [_, data] = std::forward<Sndr>(sndr);
1719
+ auto dataParts = std::move(data).release();
1720
+
1721
+ using scope_tkn = decltype(dataParts->first);
1722
+ using wrap_sender = decltype(dataParts->second);
1723
+ using op_t = connect_result_t<wrap_sender, Rcvr>;
1724
+
1725
+ struct op_state {
1726
+ bool associated = false; // exposition only
1727
+ union {
1728
+ Rcvr* rcvr; // exposition only
1729
+ struct {
1730
+ scope_tkn token; // exposition only
1731
+ op_t op; // exposition only
1732
+ } assoc; // exposition only
1733
+ };
1734
+
1735
+ explicit op_state(Rcvr& r) noexcept
1736
+ : rcvr(addressof(r)) {}
1737
+
1738
+ explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try
1739
+ : associated(true),
1740
+ assoc(tkn, connect(std::move(sndr), std::move(r))) {
1741
+ }
1742
+ catch (...) {
1743
+ tkn.disassociate();
1744
+ throw;
1745
+ }
1746
+
1747
+ op_state(op_state&&) = delete;
1748
+
1749
+ ~op_state() {
1750
+ if (associated) {
1751
+ assoc.op.~op_t();
1752
+ assoc.token.disassociate();
1753
+ assoc.token.~scope_tkn();
1754
+ }
1755
+ }
1756
+
1757
+ void run() noexcept { // exposition only
1758
+ if (associated)
1759
+ start(assoc.op);
1760
+ else
1761
+ set_stopped(std::move(*rcvr));
1762
+ }
1763
+ };
1764
+
1765
+ if (dataParts)
1766
+ return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr};
1767
+ else
1768
+ return op_state{rcvr};
1769
+ }
1770
+ ```
1771
+
1772
+ The expression in the `noexcept` clause of
1773
+ `impls-for<associate_t>::get-state` is
1774
+
1775
+ ``` cpp
1776
+ is_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> &&
1777
+ is_nothrow_move_constructible_v<wrap-sender> &&
1778
+ nothrow-callable<connect_t, wrap-sender, Rcvr>
1779
+ ```
1780
+
1781
+ where *`wrap-sender`* is the type
1782
+ `remove_cvref_t<data-type<Sndr>>::wrap-sender`.
1783
+
1784
+ The member `impls-for<associate_t>::start` is initialized with a
1785
+ callable object equivalent to the following lambda:
1786
+
1787
+ ``` cpp
1788
+ [](auto& state, auto&) noexcept -> void {
1789
+ state.run();
1790
+ }
1791
+ ```
1792
+
1793
+ The evaluation of `associate(sndr, token)` may cause side effects
1794
+ observable via `token`'s associated async scope object.
1795
+
1796
+ #### Exposition-only `execution::stop-when` <a id="exec.stop.when">[[exec.stop.when]]</a>
1797
+
1798
+ *`stop-when`* fuses an additional stop token `t` into a sender so that,
1799
+ upon connecting to a receiver `r`, the resulting operation state
1800
+ receives stop requests from both `t` and the token returned from
1801
+ `get_stop_token(get_env(r))`.
1802
+
1803
+ The name *`stop-when`* denotes an exposition-only sender adaptor. For
1804
+ subexpressions `sndr` and `token`:
1805
+
1806
+ - If `decltype((sndr))` does not satisfy `sender`, or
1807
+ `remove_cvref_t<decltype((token))>` does not satisfy
1808
+ `stoppable_token`, then `stop-when(sndr, token)` is ill-formed.
1809
+ - Otherwise, if `remove_cvref_t<decltype((token))>` models
1810
+ `unstoppable_token` then `stop-when({}sndr, token)` is
1811
+ expression-equivalent to `sndr`.
1812
+ - Otherwise, `stop-when(sndr, token)` returns a sender `osndr`. If
1813
+ `osndr` is connected to a receiver `r`, let `rtoken` be the result of
1814
+ `get_stop_token(get_env(r))`.
1815
+ - If the type of `rtoken` models `unstoppable_token` then the effects
1816
+ of connecting `osndr` to `r` are equivalent to
1817
+ `connect(write_env(sndr, prop(get_stop_token, token)), r)`.
1818
+ - Otherwise, the effects of connecting `osndr` to `r` are equivalent
1819
+ to `connect(write_env(sndr, prop(get_stop_token, stoken)), r)` where
1820
+ `stoken` is an object of an exposition-only type *`stoken-t`* such
1821
+ that:
1822
+ - *`stoken-t`* models `stoppable_token`;
1823
+ - `stoken.stop_requested()` returns
1824
+ `token.stop_requested() || rtoken.stop_reques-{}ted()`;
1825
+ - `stoken.stop_possible()` returns
1826
+ `token.stop_possible() || rtoken.stop_possible()`; and
1827
+ - for types `Fn` and `Init` such that both `invocable<Fn>` and
1828
+ `constructible_from<Fn, Init>` are modeled,
1829
+ `stoken-t::callback_type<Fn>` models
1830
+ `stoppable-callback-for<Fn, stoken-t, Init>`.\begin{tailnote}
1831
+ For an object \texttt{fn} of type \texttt{Fn}
1832
+ constructed from a value, \texttt{init}, of type \texttt{Init},
1833
+ registering \texttt{fn} using
1834
+ \texttt{\exposid{stoken-t}::callback_type\<Fn\>(stoken, init)}
1835
+ results in an invocation of \texttt{fn} when
1836
+ a callback registered with \texttt{token} or \texttt{rtoken} would be invoked.
1837
+ \texttt{fn} is invoked at most once.
1838
+ \end{tailnote}
1839
+
1840
+ #### `execution::spawn_future` <a id="exec.spawn.future">[[exec.spawn.future]]</a>
1841
+
1842
+ `spawn_future` attempts to associate the given input sender with the
1843
+ given token’s async scope and, on success, eagerly starts the input
1844
+ sender; the return value is a sender that, when connected and started,
1845
+ completes with either the result of the eagerly-started input sender or
1846
+ with `set_stopped` if the input sender was not started.
1847
+
1848
+ The name `spawn_future` denotes a customization point object. For
1849
+ subexpressions `sndr`, `token`, and `env`,
1850
+
1851
+ - let `Sndr` be `decltype((sndr))`,
1852
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
1853
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
1854
+
1855
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
1856
+ not satisfied, the expression `spawn_future(sndr, token, env)` is
1857
+ ill-formed.
1858
+
1859
+ Let *`spawn-future-state-base`* be the exposition-only class template:
1860
+
1861
+ ``` cpp
1862
+ namespace std::execution {
1863
+ template<class Completions>
1864
+ struct spawn-future-state-base; // exposition only
1865
+
1866
+ template<class... Sigs>
1867
+ struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only
1868
+ using variant-t = see below; // exposition only
1869
+ variant-t result; // exposition only
1870
+ virtual void complete() noexcept = 0; // exposition only
1871
+ };
1872
+ }
1873
+ ```
1874
+
1875
+ Let `Sigs` be the pack of arguments to the `completion_signatures`
1876
+ specialization provided as a parameter to the
1877
+ *`spawn-future-state-base`* class template. Let *`as-tuple`* be an alias
1878
+ template that transforms a completion signature `Tag(Args...)` into the
1879
+ tuple specialization `decayed-tuple<Tag, Args...>`.
1880
+
1881
+ - If `is_nothrow_constructible_v<decay_t<Arg>, Arg>` is `true` for every
1882
+ type `Arg` in every parameter pack `Args` in every completion
1883
+ signature `Tag(Args...)` in `Sigs` then *`variant-t`* denotes the type
1884
+ `variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>`, except
1885
+ with duplicate types removed.
1886
+ - Otherwise *`variant-t`* denotes the type
1887
+ `variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>`,
1888
+ except with duplicate types removed.
1889
+
1890
+ Let *`spawn-future-receiver`* be the exposition-only class template:
1891
+
1892
+ ``` cpp
1893
+ namespace std::execution {
1894
+ template<class Completions>
1895
+ struct spawn-future-receiver { // exposition only
1896
+ using receiver_concept = receiver_t;
1897
+
1898
+ spawn-future-state-base<Completions>* state; // exposition only
1899
+
1900
+ template<class... T>
1901
+ void set_value(T&&... t) && noexcept {
1902
+ set-complete<set_value_t>(std::forward<T>(t)...);
1903
+ }
1904
+
1905
+ template<class E>
1906
+ void set_error(E&& e) && noexcept {
1907
+ set-complete<set_error_t>(std::forward<E>(e));
1908
+ }
1909
+
1910
+ void set_stopped() && noexcept {
1911
+ set-complete<set_stopped_t>();
1912
+ }
1913
+
1914
+ private:
1915
+ template<class CPO, class... T>
1916
+ void set-complete(T&&... t) noexcept { // exposition only
1917
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
1918
+ try {
1919
+ state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
1920
+ std::forward<T>(t)...);
1921
+ }
1922
+ catch (...) {
1923
+ if constexpr (!nothrow) {
1924
+ using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
1925
+ state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
1926
+ }
1927
+ }
1928
+ state->complete();
1929
+ }
1930
+ };
1931
+ }
1932
+ ```
1933
+
1934
+ Let `ssource-t` be an unspecified type that models `stoppable-source`
1935
+ and let `ssource` be an lvalue of type `ssource-t`. Let `stoken-t` be
1936
+ `decltype(ssource.get_token())`. Let *`future-spawned-sender`* be the
1937
+ alias template:
1938
+
1939
+ ``` cpp
1940
+ template<sender Sender, class Env>
1941
+ using future-spawned-sender = // exposition only
1942
+ decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
1943
+ ```
1944
+
1945
+ Let *`spawn-future-state`* be the exposition-only class template:
1946
+
1947
+ ``` cpp
1948
+ namespace std::execution {
1949
+ template<class Alloc, scope_token Token, sender Sender, class Env>
1950
+ struct spawn-future-state // exposition only
1951
+ : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
1952
+ using sigs-t = // exposition only
1953
+ completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
1954
+ using receiver-t = // exposition only
1955
+ spawn-future-receiver<sigs-t>;
1956
+ using op-t = // exposition only
1957
+ connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
1958
+
1959
+ spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env) // exposition only
1960
+ : alloc(std::move(alloc)),
1961
+ op(connect(
1962
+ write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
1963
+ receiver-t(this))),
1964
+ token(std::move(token)),
1965
+ associated(token.try_associate()) {
1966
+ if (associated)
1967
+ start(op);
1968
+ else
1969
+ set_stopped(receiver-t(this));
1970
+ }
1971
+
1972
+ void complete() noexcept override; // exposition only
1973
+ void consume(receiver auto& rcvr) noexcept; // exposition only
1974
+ void abandon() noexcept; // exposition only
1975
+
1976
+ private:
1977
+ using alloc-t = // exposition only
1978
+ allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
1979
+
1980
+ alloc-t alloc; // exposition only
1981
+ ssource-t ssource; // exposition only
1982
+ op-t op; // exposition only
1983
+ Token token; // exposition only
1984
+ bool associated; // exposition only
1985
+
1986
+ void destroy() noexcept; // exposition only
1987
+ };
1988
+ }
1989
+ ```
1990
+
1991
+ For purposes of determining the existence of a data race, *`complete`*,
1992
+ *`consume`*, and *`abandon`* behave as atomic operations
1993
+ [[intro.multithread]]. These operations on a single object of a type
1994
+ that is a specialization of *`spawn-future-state`* appear to occur in a
1995
+ single total order.
1996
+
1997
+ ``` cpp
1998
+ void complete() noexcept;
1999
+ ```
2000
+
2001
+ *Effects:*
2002
+
2003
+ - No effects if this invocation of *complete* happens before an
2004
+ invocation of *consume* or *abandon* on `*this`;
2005
+ - otherwise, if an invocation of *consume* on `*this` happens before
2006
+ this invocation of *complete* then there is a receiver, `rcvr`,
2007
+ registered and that receiver is completed as if by
2008
+ *`consume`*`(rcvr)`;
2009
+ - otherwise, *destroy* is invoked.
2010
+
2011
+ ``` cpp
2012
+ void consume(receiver auto& rcvr) noexcept;
2013
+ ```
2014
+
2015
+ *Effects:*
2016
+
2017
+ - If this invocation of *consume* happens before an invocation of
2018
+ *complete* on `*this` then `rcvr` is registered to be completed when
2019
+ *complete* is subsequently invoked on `*this`;
2020
+ - otherwise, `rcvr` is completed as if by:
2021
+ ``` cpp
2022
+ std::move(this->result).visit(
2023
+ [&rcvr](auto&& tuple) noexcept {
2024
+ if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
2025
+ apply([&rcvr](auto cpo, auto&&... vals) {
2026
+ cpo(std::move(rcvr), std::move(vals)...);
2027
+ }, std::move(tuple));
2028
+ }
2029
+ });
2030
+ ```
2031
+
2032
+ ``` cpp
2033
+ void abandon() noexcept;
2034
+ ```
2035
+
2036
+ *Effects:*
2037
+
2038
+ - If this invocation of *abandon* happens before an invocation of
2039
+ *complete* on `*this` then equivalent to:
2040
+ ``` cpp
2041
+ ssource.request_stop();
2042
+ ```
2043
+ - otherwise, *destroy* is invoked.
2044
+
2045
+ ``` cpp
2046
+ void destroy() noexcept;
2047
+ ```
2048
+
2049
+ *Effects:* Equivalent to:
2050
+
2051
+ ``` cpp
2052
+ auto token = std::move(this->token);
2053
+ bool associated = this->associated;
2054
+
2055
+ {
2056
+ auto alloc = std::move(this->alloc);
2057
+
2058
+ allocator_traits<alloc-t>::destroy(alloc, this);
2059
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
2060
+ }
2061
+
2062
+ if (associated)
2063
+ token.disassociate();
2064
+ ```
2065
+
2066
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2067
+ specialized for `spawn_future_t` as follows:
2068
+
2069
+ ``` cpp
2070
+ namespace std::execution {
2071
+ template<>
2072
+ struct impls-for<spawn_future_t> : default-impls {
2073
+ static constexpr auto start = see below; // exposition only
2074
+ };
2075
+ }
2076
+ ```
2077
+
2078
+ The member `impls-for<spawn_future_t>::start` is initialized with a
2079
+ callable object equivalent to the following lambda:
2080
+
2081
+ ``` cpp
2082
+ [](auto& state, auto& rcvr) noexcept -> void {
2083
+ state->consume(rcvr);
2084
+ }
2085
+ ```
2086
+
2087
+ For the expression `spawn_future(sndr, token, env)` let `new_sender` be
2088
+ the expression `token.wrap(sndr)` and let `alloc` and `senv` be defined
2089
+ as follows:
2090
+
2091
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
2092
+ the result of `get_allocator(env)` and `senv` is the expression `env`;
2093
+ - otherwise, if the expression `get_allocator(get_env(new_sender))` is
2094
+ well-formed, then `alloc` is the result of
2095
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
2096
+ `JOIN-ENV(prop(get_allocator, alloc), env)`;
2097
+ - otherwise, `alloc` is `allocator<void>()` and `senv` is the expression
2098
+ `env`.
2099
+
2100
+ The expression `spawn_future(sndr, token, env)` has the following
2101
+ effects:
2102
+
2103
+ - Uses `alloc` to allocate and construct an object `s` of a type that is
2104
+ a specialization of *`spawn-future-{}state`* from `alloc`,
2105
+ `token.wrap(sndr)`, `token`, and `senv`. If an exception is thrown
2106
+ then any constructed objects are destroyed and any allocated memory is
2107
+ deallocated.
2108
+ - Constructs an object `u` of a type that is a specialization of
2109
+ `unique_ptr` such that:
2110
+ - `u.get()` is equal to the address of `s`, and
2111
+ - `u.get_deleter()(u.release())` is equivalent to
2112
+ `u.release()->abandon()`.
2113
+ - Returns `make-sender(spawn_future, std::move(u))`.
2114
+
2115
+ The expression `spawn_future(sndr, token)` is expression-equivalent to
2116
+ `spawn_future(sndr, token, execution::env<>())`.
2117
+