From Jason Turner

[exec.snd]

Large diff (135.6 KB) - rendering may be slow on some devices

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmphl0syw6h/{from.md → to.md} +3841 -0
tmp/tmphl0syw6h/{from.md → to.md} RENAMED
@@ -0,0 +1,3841 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## Senders <a id="exec.snd">[[exec.snd]]</a>
2
+
3
+ ### General <a id="exec.snd.general">[[exec.snd.general]]</a>
4
+
5
+ Subclauses [[exec.factories]] and [[exec.adapt]] define customizable
6
+ algorithms that return senders. Each algorithm has a default
7
+ implementation. Let `sndr` be the result of an invocation of such an
8
+ algorithm or an object equal to the result [[concepts.equality]], and
9
+ let `Sndr` be `decltype((sndr))`. Let `rcvr` be a receiver of type
10
+ `Rcvr` with associated environment `env` of type `Env` such that
11
+ `sender_to<Sndr, Rcvr>` is `true`. For the default implementation of the
12
+ algorithm that produced `sndr`, connecting `sndr` to `rcvr` and starting
13
+ the resulting operation state [[exec.async.ops]] necessarily results in
14
+ the potential evaluation [[basic.def.odr]] of a set of completion
15
+ operations whose first argument is a subexpression equal to `rcvr`. Let
16
+ `Sigs` be a pack of completion signatures corresponding to this set of
17
+ completion operations, and let `CS` be the type of the expression
18
+ `get_completion_signatures<Sndr, Env>()`. Then `CS` is a specialization
19
+ of the class template `completion_signatures` [[exec.cmplsig]], the set
20
+ of whose template arguments is `Sigs`. If none of the types in `Sigs`
21
+ are dependent on the type `Env`, then the expression
22
+ `get_completion_signatures<Sndr>()` is well-formed and its type is `CS`.
23
+ If a user-provided implementation of the algorithm that produced `sndr`
24
+ is selected instead of the default:
25
+
26
+ - Any completion signature that is in the set of types denoted by
27
+ `completion_signatures_of_t<Sndr, Env>` and that is not part of `Sigs`
28
+ shall correspond to error or stopped completion operations, unless
29
+ otherwise specified.
30
+ - If none of the types in `Sigs` are dependent on the type `Env`, then
31
+ `completion_signatures_of_t<Sndr>` and
32
+ `completion_signatures_of_t<Sndr, Env>` shall denote the same type.
33
+
34
+ ### Exposition-only entities <a id="exec.snd.expos">[[exec.snd.expos]]</a>
35
+
36
+ Subclause [[exec.snd]] makes use of the following exposition-only
37
+ entities.
38
+
39
+ For a queryable object `env`, `FWD-ENV(env)` is an expression whose type
40
+ satisfies `queryable` such that for a query object `q` and a pack of
41
+ subexpressions `as`, the expression `FWD-ENV(env).query(q, as...)` is
42
+ ill-formed if `forwarding_query(q)` is `false`; otherwise, it is
43
+ expression-equivalent to `env.query(q, as...)`. The type
44
+ `FWD-ENV-T(Env)` is `decltype(FWD-ENV(declval<Env>()))`.
45
+
46
+ For a query object `q` and a subexpression `v`, `MAKE-ENV(q, v)` is an
47
+ expression `env` whose type satisfies `queryable` such that the result
48
+ of `env.query(q)` has a value equal to `v` [[concepts.equality]]. Unless
49
+ otherwise stated, the object to which `env.query(q)` refers remains
50
+ valid while `env` remains valid.
51
+
52
+ For two queryable objects `env1` and `env2`, a query object `q`, and a
53
+ pack of subexpressions `as`, `JOIN-ENV(env1, env2)` is an expression
54
+ `env3` whose type satisfies `queryable` such that `env3.query(q, as...)`
55
+ is expression-equivalent to:
56
+
57
+ - `env1.query(q, as...)` if that expression is well-formed,
58
+ - otherwise, `env2.query(q, as...)` if that expression is well-formed,
59
+ - otherwise, `env3.query(q, as...)` is ill-formed.
60
+
61
+ The results of *`FWD-ENV`*, *`MAKE-ENV`*, and *`JOIN-ENV`* can be
62
+ context-dependent; i.e., they can evaluate to expressions with different
63
+ types and value categories in different contexts for the same arguments.
64
+
65
+ For a scheduler `sch`, `SCHED-ATTRS(sch)` is an expression `o1` whose
66
+ type satisfies `queryable` such that
67
+ `o1.query(get_completion_scheduler<Tag>)` is an expression with the same
68
+ type and value as `sch` where `Tag` is one of `set_value_t` or
69
+ `set_stopped_t`, and such that `o1.query(get_domain)` is
70
+ expression-equivalent to `sch.query(get_domain)`. `SCHED-ENV(sch)` is an
71
+ expression `o2` whose type satisfies `queryable` such that
72
+ `o2.query(get_scheduler)` is a prvalue with the same type and value as
73
+ `sch`, and such that `o2.query(get_domain)` is expression-equivalent to
74
+ `sch.query(get_domain)`.
75
+
76
+ For two subexpressions `rcvr` and `expr`, `SET-VALUE(rcvr, expr)` is
77
+ expression-equivalent to `(expr, set_value(std::move(rcvr)))` if the
78
+ type of `expr` is `void`; otherwise, `set_value(std::move(rcvr), expr)`.
79
+ `TRY-EVAL(rcvr, expr)` is equivalent to:
80
+
81
+ ``` cpp
82
+ try {
83
+ expr;
84
+ } catch(...) {
85
+ set_error(std::move(rcvr), current_exception());
86
+ }
87
+ ```
88
+
89
+ if `expr` is potentially-throwing; otherwise, `expr`.
90
+ `TRY-SET-VALUE(rcvr, expr)` is
91
+
92
+ ``` cpp
93
+ TRY-EVAL(rcvr, SET-VALUE(rcvr, expr))
94
+ ```
95
+
96
+ except that `rcvr` is evaluated only once.
97
+
98
+ ``` cpp
99
+ template<class Default = default_domain, class Sndr>
100
+ constexpr auto completion-domain(const Sndr& sndr) noexcept;
101
+ ```
102
+
103
+ *`COMPL-DOMAIN`*`(T)` is the type of the expression
104
+ `get_domain(get_completion_scheduler<T>(get_env(sndr)))`.
105
+
106
+ *Effects:* If all of the types *`COMPL-DOMAIN`*`(set_value_t)`,
107
+ *`COMPL-DOMAIN`*`(set_error_t)`, and *`COMPL-DOMAIN`*`(set_stopped_t)`
108
+ are ill-formed, `completion-domain<Default>(sndr)` is a
109
+ default-constructed prvalue of type `Default`. Otherwise, if they all
110
+ share a common type [[meta.trans.other]] (ignoring those types that are
111
+ ill-formed), then *`completion-domain`*`<Default>(sndr)` is a
112
+ default-constructed prvalue of that type. Otherwise,
113
+ *`completion-domain`*`<Default>(sndr)` is ill-formed.
114
+
115
+ ``` cpp
116
+ template<class Tag, class Env, class Default>
117
+ constexpr decltype(auto) query-with-default(
118
+ Tag, const Env& env, Default&& value) noexcept(see below);
119
+ ```
120
+
121
+ Let `e` be the expression `Tag()(env)` if that expression is
122
+ well-formed; otherwise, it is
123
+ `static_cast<Default>(std::forward<Default>(value))`.
124
+
125
+ *Returns:* `e`.
126
+
127
+ *Remarks:* The expression in the noexcept clause is `noexcept(e)`.
128
+
129
+ ``` cpp
130
+ template<class Sndr>
131
+ constexpr auto get-domain-early(const Sndr& sndr) noexcept;
132
+ ```
133
+
134
+ *Effects:* Equivalent to:
135
+
136
+ ``` cpp
137
+ return Domain();
138
+ ```
139
+
140
+ where `Domain` is the decayed type of the first of the following
141
+ expressions that is well-formed:
142
+
143
+ - `get_domain(get_env(sndr))`
144
+ - *`completion-domain`*`(sndr)`
145
+ - `default_domain()`
146
+
147
+ ``` cpp
148
+ template<class Sndr, class Env>
149
+ constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept;
150
+ ```
151
+
152
+ *Effects:* Equivalent to:
153
+
154
+ - If `sender-for<Sndr, continues_on_t>` is `true`, then
155
+ ``` cpp
156
+ return Domain();
157
+ ```
158
+
159
+ where `Domain` is the type of the following expression:
160
+ ``` cpp
161
+ [] {
162
+ auto [_, sch, _] = sndr;
163
+ return query-with-default(get_domain, sch, default_domain());
164
+ }();
165
+ ```
166
+
167
+ \[*Note 1*: The `continues_on` algorithm works in tandem with
168
+ `schedule_from`[[exec.schedule.from]] to give scheduler authors a
169
+ way to customize both how to transition onto (`continues_on`) and off
170
+ of (`schedule_from`) a given execution context. Thus, `continues_on`
171
+ ignores the domain of the predecessor and uses the domain of the
172
+ destination scheduler to select a customization, a property that is
173
+ unique to `continues_on`. That is why it is given special treatment
174
+ here. — *end note*]
175
+ - Otherwise,
176
+ ``` cpp
177
+ return Domain();
178
+ ```
179
+
180
+ where `Domain` is the first of the following expressions that is
181
+ well-formed and whose type is not `void`:
182
+ - `get_domain(get_env(sndr))`
183
+ - *`completion-domain`*`<void>(sndr)`
184
+ - `get_domain(env)`
185
+ - `get_domain(get_scheduler(env))`
186
+ - `default_domain()`
187
+
188
+ ``` cpp
189
+ template<callable Fun>
190
+ requires is_nothrow_move_constructible_v<Fun>
191
+ struct emplace-from {
192
+ Fun fun; // exposition only
193
+ using type = call-result-t<Fun>;
194
+
195
+ constexpr operator type() && noexcept(nothrow-callable<Fun>) {
196
+ return std::move(fun)();
197
+ }
198
+
199
+ constexpr type operator()() && noexcept(nothrow-callable<Fun>) {
200
+ return std::move(fun)();
201
+ }
202
+ };
203
+ ```
204
+
205
+ [*Note 2*: *`emplace-from`* is used to emplace non-movable types into
206
+ `tuple`, `optional`, `variant`, and similar types. — *end note*]
207
+
208
+ ``` cpp
209
+ struct on-stop-request {
210
+ inplace_stop_source& stop-src; // exposition only
211
+ void operator()() noexcept { stop-src.request_stop(); }
212
+ };
213
+ ```
214
+
215
+ ``` cpp
216
+ template<class T_0, class T_1, …, class T_n>
217
+ struct product-type { // exposition only
218
+ T_0 t_0; // exposition only
219
+ T_1 t_1; // exposition only
220
+
221
+ T_n t_n; // exposition only
222
+
223
+ template<size_t I, class Self>
224
+ constexpr decltype(auto) get(this Self&& self) noexcept; // exposition only
225
+
226
+ template<class Self, class Fn>
227
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) // exposition only
228
+ noexcept(see below);
229
+ };
230
+ ```
231
+
232
+ [*Note 3*: *`product-type`* is presented here in pseudo-code form for
233
+ the sake of exposition. It can be approximated in standard C++ with a
234
+ tuple-like implementation that takes care to keep the type an aggregate
235
+ that can be used as the initializer of a structured binding
236
+ declaration. — *end note*]
237
+
238
+ [*Note 4*: An expression of type *`product-type`* is usable as the
239
+ initializer of a structured binding declaration
240
+ [[dcl.struct.bind]]. — *end note*]
241
+
242
+ ``` cpp
243
+ template<size_t I, class Self>
244
+ constexpr decltype(auto) get(this Self&& self) noexcept;
245
+ ```
246
+
247
+ *Effects:* Equivalent to:
248
+
249
+ ``` cpp
250
+ auto& [...ts] = self;
251
+ return std::forward_like<Self>(ts...[I]);
252
+ ```
253
+
254
+ ``` cpp
255
+ template<class Self, class Fn>
256
+ constexpr decltype(auto) apply(this Self&& self, Fn&& fn) noexcept(see below);
257
+ ```
258
+
259
+ *Constraints:* The expression in the `return` statement below is
260
+ well-formed.
261
+
262
+ *Effects:* Equivalent to:
263
+
264
+ ``` cpp
265
+ auto& [...ts] = self;
266
+ return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
267
+ ```
268
+
269
+ *Remarks:* The expression in the `noexcept` clause is `true` if the
270
+ `return` statement above is not potentially throwing; otherwise,
271
+ `false`.
272
+
273
+ Let `valid-specialization` be the following concept:
274
+
275
+ ``` cpp
276
+ namespace std::execution {
277
+ template<template<class...> class T, class... Args>
278
+ concept valid-specialization = // exposition only
279
+ requires { typename T<Args...>; };
280
+ }
281
+ ```
282
+
283
+ ``` cpp
284
+ template<class Tag, class Data = see below, class... Child>
285
+ constexpr auto make-sender(Tag tag, Data&& data, Child&&... child);
286
+ ```
287
+
288
+ *Mandates:* The following expressions are `true`:
289
+
290
+ - `semiregular<Tag>`
291
+ - `movable-value<Data>`
292
+ - `(sender<Child> && ...)`
293
+ - `dependent_sender<Sndr> || sender_in<Sndr>`, where `Sndr` is
294
+ *`basic-sender`*`<Tag, Data,Child...>` as defined below. *Recommended
295
+ practice:* If evaluation of `sender_in<Sndr>` results in an uncaught
296
+ exception from the evaluation of `get_completion_signatures<Sndr>()`,
297
+ the implementation should include information about that exception in
298
+ the resulting diagnostic.
299
+
300
+ *Returns:* A prvalue of type
301
+ *`basic-sender`*`<Tag, decay_t<Data>, decay_t<Child>...>` that has been
302
+ direct-list-initialized with the forwarded arguments, where
303
+ *basic-sender* is the following exposition-only class template except as
304
+ noted below.
305
+
306
+ *Remarks:* The default template argument for the `Data` template
307
+ parameter denotes an unspecified empty trivially copyable class type
308
+ that models `semiregular`.
309
+
310
+ ``` cpp
311
+ namespace std::execution {
312
+ template<class Tag>
313
+ concept completion-tag = // exposition only
314
+ same_as<Tag, set_value_t> || same_as<Tag, set_error_t> || same_as<Tag, set_stopped_t>;
315
+
316
+ struct default-impls { // exposition only
317
+ static constexpr auto get-attrs = see belownc; // exposition only
318
+ static constexpr auto get-env = see belownc; // exposition only
319
+ static constexpr auto get-state = see belownc; // exposition only
320
+ static constexpr auto start = see belownc; // exposition only
321
+ static constexpr auto complete = see belownc; // exposition only
322
+
323
+ template<class Sndr, class... Env>
324
+ static consteval void check-types(); // exposition only
325
+ };
326
+
327
+ template<class Tag>
328
+ struct impls-for : default-impls {}; // exposition only
329
+
330
+ template<class Sndr, class Rcvr> // exposition only
331
+ using state-type = decay_t<call-result-t<
332
+ decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>>;
333
+
334
+ template<class Index, class Sndr, class Rcvr> // exposition only
335
+ using env-type = call-result-t<
336
+ decltype(impls-for<tag_of_t<Sndr>>::get-env), Index,
337
+ state-type<Sndr, Rcvr>&, const Rcvr&>;
338
+
339
+ template<class Sndr>
340
+ using data-type = decltype(declval<Sndr>().template get<1>()); // exposition only
341
+
342
+ template<class Sndr, size_t I = 0>
343
+ using child-type = decltype(declval<Sndr>().template get<I+2>()); // exposition only
344
+
345
+ template<class Sndr>
346
+ using indices-for = remove_reference_t<Sndr>::indices-for; // exposition only
347
+
348
+ template<class Sndr, class Rcvr>
349
+ struct basic-state { // exposition only
350
+ basic-state(Sndr&& sndr, Rcvr&& rcvr) noexcept(see below)
351
+ : rcvr(std::move(rcvr))
352
+ , state(impls-for<tag_of_t<Sndr>>::get-state(std::forward<Sndr>(sndr), rcvr)) { }
353
+
354
+ Rcvr rcvr; // exposition only
355
+ state-type<Sndr, Rcvr> state; // exposition only
356
+ };
357
+
358
+ template<class Sndr, class Rcvr, class Index>
359
+ requires valid-specialization<env-type, Index, Sndr, Rcvr>
360
+ struct basic-receiver { // exposition only
361
+ using receiver_concept = receiver_t;
362
+
363
+ using tag-t = tag_of_t<Sndr>; // exposition only
364
+ using state-t = state-type<Sndr, Rcvr>; // exposition only
365
+ static constexpr const auto& complete = impls-for<tag-t>::complete; // exposition only
366
+
367
+ template<class... Args>
368
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_value_t, Args...>
369
+ void set_value(Args&&... args) && noexcept {
370
+ complete(Index(), op->state, op->rcvr, set_value_t(), std::forward<Args>(args)...);
371
+ }
372
+
373
+ template<class Error>
374
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_error_t, Error>
375
+ void set_error(Error&& err) && noexcept {
376
+ complete(Index(), op->state, op->rcvr, set_error_t(), std::forward<Error>(err));
377
+ }
378
+
379
+ void set_stopped() && noexcept
380
+ requires callable<decltype(complete), Index, state-t&, Rcvr&, set_stopped_t> {
381
+ complete(Index(), op->state, op->rcvr, set_stopped_t());
382
+ }
383
+
384
+ auto get_env() const noexcept -> env-type<Index, Sndr, Rcvr> {
385
+ return impls-for<tag-t>::get-env(Index(), op->state, op->rcvr);
386
+ }
387
+
388
+ basic-state<Sndr, Rcvr>* op; // exposition only
389
+ };
390
+
391
+ constexpr auto connect-all = see belownc; // exposition only
392
+
393
+ template<class Sndr, class Rcvr>
394
+ using connect-all-result = call-result-t< // exposition only
395
+ decltype(connect-all), basic-state<Sndr, Rcvr>*, Sndr, indices-for<Sndr>>;
396
+
397
+ template<class Sndr, class Rcvr>
398
+ requires valid-specialization<state-type, Sndr, Rcvr> &&
399
+ valid-specialization<connect-all-result, Sndr, Rcvr>
400
+ struct basic-operation : basic-state<Sndr, Rcvr> { // exposition only
401
+ using operation_state_concept = operation_state_t;
402
+ using tag-t = tag_of_t<Sndr>; // exposition only
403
+
404
+ connect-all-result<Sndr, Rcvr> inner-ops; // exposition only
405
+
406
+ basic-operation(Sndr&& sndr, Rcvr&& rcvr) noexcept(see belownc) // exposition only
407
+ : basic-state<Sndr, Rcvr>(std::forward<Sndr>(sndr), std::move(rcvr)),
408
+ inner-ops(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
409
+ {}
410
+
411
+ void start() & noexcept {
412
+ auto& [...ops] = inner-ops;
413
+ impls-for<tag-t>::start(this->state, this->rcvr, ops...);
414
+ }
415
+ };
416
+
417
+ template<class Tag, class Data, class... Child>
418
+ struct basic-sender : product-type<Tag, Data, Child...> { // exposition only
419
+ using sender_concept = sender_t;
420
+ using indices-for = index_sequence_for<Child...>; // exposition only
421
+
422
+ decltype(auto) get_env() const noexcept {
423
+ auto& [_, data, ...child] = *this;
424
+ return impls-for<Tag>::get-attrs(data, child...);
425
+ }
426
+
427
+ template<decays-to<basic-sender> Self, receiver Rcvr>
428
+ auto connect(this Self&& self, Rcvr rcvr) noexcept(see below)
429
+ -> basic-operation<Self, Rcvr> {
430
+ return {std::forward<Self>(self), std::move(rcvr)};
431
+ }
432
+
433
+ template<decays-to<basic-sender> Self, class... Env>
434
+ static constexpr auto get_completion_signatures();
435
+ };
436
+ }
437
+ ```
438
+
439
+ It is unspecified whether a specialization of *`basic-sender`* is an
440
+ aggregate.
441
+
442
+ An expression of type *`basic-sender`* is usable as the initializer of a
443
+ structured binding declaration [[dcl.struct.bind]].
444
+
445
+ The expression in the `noexcept` clause of the constructor of
446
+ *`basic-state`* is
447
+
448
+ ``` cpp
449
+ is_nothrow_move_constructible_v<Rcvr> &&
450
+ nothrow-callable<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&> &&
451
+ (same_as<state-type<Sndr, Rcvr>, get-state-result> ||
452
+ is_nothrow_constructible_v<state-type<Sndr, Rcvr>, get-state-result>)
453
+ ```
454
+
455
+ where *`get-state-result`* is
456
+
457
+ ``` cpp
458
+ call-result-t<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>.
459
+ ```
460
+
461
+ The object *`connect-all`* is initialized with a callable object
462
+ equivalent to the following lambda:
463
+
464
+ ``` cpp
465
+ []<class Sndr, class Rcvr, size_t... Is>(
466
+ basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
467
+ -> decltype(auto) {
468
+ auto& [_, data, ...child] = sndr;
469
+ return product-type{connect(
470
+ std::forward_like<Sndr>(child),
471
+ basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
472
+ }
473
+ ```
474
+
475
+ *Constraints:* The expression in the `return` statement is well-formed.
476
+
477
+ *Remarks:* The expression in the `noexcept` clause is `true` if the
478
+ `return` statement is not potentially throwing; otherwise, `false`.
479
+
480
+ The expression in the `noexcept` clause of the constructor of
481
+ *`basic-operation`* is:
482
+
483
+ ``` cpp
484
+ is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
485
+ noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
486
+ ```
487
+
488
+ The expression in the `noexcept` clause of the `connect` member function
489
+ of *`basic-sender`* is:
490
+
491
+ ``` cpp
492
+ is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
493
+ ```
494
+
495
+ The member `default-impls::get-attrs` is initialized with a callable
496
+ object equivalent to the following lambda:
497
+
498
+ ``` cpp
499
+ [](const auto&, const auto&... child) noexcept -> decltype(auto) {
500
+ if constexpr (sizeof...(child) == 1)
501
+ return (FWD-ENV(get_env(child)), ...);
502
+ else
503
+ return env<>();
504
+ }
505
+ ```
506
+
507
+ The member `default-impls::get-env` is initialized with a callable
508
+ object equivalent to the following lambda:
509
+
510
+ ``` cpp
511
+ [](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {
512
+ return FWD-ENV(get_env(rcvr));
513
+ }
514
+ ```
515
+
516
+ The member `default-impls::get-state` is initialized with a callable
517
+ object equivalent to the following lambda:
518
+
519
+ ``` cpp
520
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {
521
+ auto& [_, data, ...child] = sndr;
522
+ return allocator-aware-forward(std::forward_like<Sndr>(data), rcvr);
523
+ }
524
+ ```
525
+
526
+ The member `default-impls::start` is initialized with a callable object
527
+ equivalent to the following lambda:
528
+
529
+ ``` cpp
530
+ [](auto&, auto&, auto&... ops) noexcept -> void {
531
+ (execution::start(ops), ...);
532
+ }
533
+ ```
534
+
535
+ The member `default-impls::complete` is initialized with a callable
536
+ object equivalent to the following lambda:
537
+
538
+ ``` cpp
539
+ []<class Index, class Rcvr, class Tag, class... Args>(
540
+ Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
541
+ -> void requires callable<Tag, Rcvr, Args...> {
542
+ static_assert(Index::value == 0);
543
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
544
+ }
545
+ ```
546
+
547
+ ``` cpp
548
+ template<class Sndr, class... Env>
549
+ static consteval void default-impls::check-types();
550
+ ```
551
+
552
+ Let `Is` be the pack of integral template arguments of the
553
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
554
+
555
+ *Effects:* Equivalent to:
556
+
557
+ ``` cpp
558
+ (get_completion_signatures<child-type<Sndr, Is>, FWD-ENV-T(Env)...>(), ...);
559
+ ```
560
+
561
+ [*Note 1*:
562
+
563
+ For any types `T` and `S`, and pack `E`, let `e` be the expression
564
+ *`impls-for`*`<T>::`*`check-types`*`<S, E...>()`. Then exactly one of
565
+ the following is `true`:
566
+
567
+ - `e` is ill-formed, or
568
+ - the evaluation of `e` exits with an exception, or
569
+ - `e` is a core constant expression.
570
+
571
+ When `e` is a core constant expression, the pack `S, E...` uniquely
572
+ determines a set of completion signatures.
573
+
574
+ — *end note*]
575
+
576
+ ``` cpp
577
+ template<class Tag, class Data, class... Child>
578
+ template<class Sndr, class... Env>
579
+ constexpr auto basic-sender<Tag, Data, Child...>::get_completion_signatures();
580
+ ```
581
+
582
+ Let `Rcvr` be the type of a receiver whose environment has type `E`,
583
+ where `E` is the first type in the list `Env..., env<>`. Let
584
+ *`CHECK-TYPES`*`()` be the expression
585
+ *`impls-for`*`<Tag>::template `*`check-types`*`<Sndr, E>()`, and let
586
+ `CS` be a type determined as follows:
587
+
588
+ - If *`CHECK-TYPES`*`()` is a core constant expression, let `op` be an
589
+ lvalue subexpression whose type is `connect_result_t<Sndr, Rcvr>`.
590
+ Then `CS` is the specialization of `completion_signatures` the set of
591
+ whose template arguments correspond to the set of completion
592
+ operations that are potentially evaluated [[basic.def.odr]] as a
593
+ result of evaluating `op.start()`.
594
+ - Otherwise, `CS` is `completion_signatures<>`.
595
+
596
+ *Constraints:* *`CHECK-TYPES`*`()` is a well-formed expression.
597
+
598
+ *Effects:* Equivalent to:
599
+
600
+ ``` cpp
601
+ CHECK-TYPES();
602
+ return CS();
603
+ ```
604
+
605
+ ``` cpp
606
+ template<class... Fns>
607
+ struct overload-set : Fns... {
608
+ using Fns::operator()...;
609
+ };
610
+ ```
611
+
612
+ ``` cpp
613
+ struct not-a-sender {
614
+ using sender_concept = sender_t;
615
+
616
+ template<class Sndr>
617
+ static consteval auto get_completion_signatures() -> completion_signatures<> {
618
+ throw unspecified-exception();
619
+ }
620
+ };
621
+ ```
622
+
623
+ where `unspecified-exception` is a type derived from `exception`.
624
+
625
+ ``` cpp
626
+ constexpr void decay-copyable-result-datums(auto cs) {
627
+ cs.for-each([]<class Tag, class... Ts>(Tag(*)(Ts...)) {
628
+ if constexpr (!(is_constructible_v<decay_t<Ts>, Ts> &&...))
629
+ throw unspecified-exception();
630
+ });
631
+ }
632
+ ```
633
+
634
+ where `unspecified-exception` is a type derived from `exception`.
635
+
636
+ ``` cpp
637
+ template<class T, class Context>
638
+ decltype(auto) allocator-aware-forward(T&& obj, Context&& context); // exposition only
639
+ ```
640
+
641
+ *allocator-aware-forward* is an exposition-only function template used
642
+ to either create a new object of type `remove_cvref_t<T>` from `obj` or
643
+ forward `obj` depending on whether an allocator is available. If the
644
+ environment associated with `context` provides an allocator (i.e., the
645
+ expression `get_allocator(get_env(context))` is valid), let *alloc* be
646
+ the result of this expression and let `P` be `remove_cvref_t<T>`.
647
+
648
+ *Returns:*
649
+
650
+ - If *alloc* is not defined, returns `std::forward<T>(obj)`,
651
+ - otherwise if `P` is a specialization of *product-type*, returns an
652
+ object of type `P` whose elements are initialized using
653
+ ``` cpp
654
+ make_obj_using_allocator<decltype(e)>(std::forward_like<T>(e), alloc)
655
+ ```
656
+
657
+ where `e` is the corresponding element of `obj`,
658
+ - otherwise, returns
659
+ `make_obj_using_allocator<P>(std::forward<T>(obj), `*`alloc`*`)`.
660
+
661
+ ### Sender concepts <a id="exec.snd.concepts">[[exec.snd.concepts]]</a>
662
+
663
+ The `sender` concept defines the requirements for a sender type
664
+ [[exec.async.ops]]. The `sender_in` concept defines the requirements for
665
+ a sender type that can create asynchronous operations given an
666
+ associated environment type. The `sender_to` concept defines the
667
+ requirements for a sender type that can connect with a specific receiver
668
+ type. The `get_env` customization point object is used to access a
669
+ sender’s associated attributes. The connect customization point object
670
+ is used to connect [[exec.async.ops]] a sender and a receiver to produce
671
+ an operation state.
672
+
673
+ ``` cpp
674
+ namespace std::execution {
675
+ template<auto>
676
+ concept is-constant = true; // exposition only
677
+
678
+ template<class Sndr>
679
+ concept is-sender = // exposition only
680
+ derived_from<typename Sndr::sender_concept, sender_t>;
681
+
682
+ template<class Sndr>
683
+ concept enable-sender = // exposition only
684
+ is-sender<Sndr> ||
685
+ is-awaitable<Sndr, env-promise<env<>>>; // [exec.awaitable]
686
+
687
+ template<class Sndr>
688
+ inline constexpr bool enable_sender = enable-sender<Sndr>;
689
+
690
+ template<class Sndr>
691
+ consteval bool is-dependent-sender-helper() try { // exposition only
692
+ get_completion_signatures<Sndr>();
693
+ return false;
694
+ } catch (dependent_sender_error&) {
695
+ return true;
696
+ }
697
+
698
+ template<class Sndr>
699
+ concept sender =
700
+ enable_sender<remove_cvref_t<Sndr>> &&
701
+ requires (const remove_cvref_t<Sndr>& sndr) {
702
+ { get_env(sndr) } -> queryable;
703
+ } &&
704
+ move_constructible<remove_cvref_t<Sndr>> &&
705
+ constructible_from<remove_cvref_t<Sndr>, Sndr>;
706
+
707
+ template<class Sndr, class... Env>
708
+ concept sender_in =
709
+ sender<Sndr> &&
710
+ (sizeof...(Env) <= 1) &&
711
+ (queryable<Env> &&...) &&
712
+ is-constant<get_completion_signatures<Sndr, Env...>()>;
713
+
714
+ template<class Sndr>
715
+ concept dependent_sender =
716
+ sender<Sndr> && bool_constant<is-dependent-sender-helper<Sndr>()>::value;
717
+
718
+ template<class Sndr, class Rcvr>
719
+ concept sender_to =
720
+ sender_in<Sndr, env_of_t<Rcvr>> &&
721
+ receiver_of<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&
722
+ requires (Sndr&& sndr, Rcvr&& rcvr) {
723
+ connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr));
724
+ };
725
+ }
726
+ ```
727
+
728
+ For a type `Sndr`, if `sender<Sndr>` is `true` and
729
+ `dependent_sender<Sndr>` is `false`, then `Sndr` is a non-dependent
730
+ sender [[exec.async.ops]].
731
+
732
+ Given a subexpression `sndr`, let `Sndr` be `decltype((sndr))` and let
733
+ `rcvr` be a receiver with an associated environment whose type is `Env`.
734
+ A completion operation is a *permissible completion* for `Sndr` and
735
+ `Env` if its completion signature appears in the argument list of the
736
+ specialization of `completion_signatures` denoted by
737
+ `completion_signatures_of_t<Sndr, Env>`. `Sndr` and `Env` model
738
+ `sender_in<Sndr, Env>` if all the completion operations that are
739
+ potentially evaluated by connecting `sndr` to `rcvr` and starting the
740
+ resulting operation state are permissible completions for `Sndr` and
741
+ `Env`.
742
+
743
+ *Remarks:* Pursuant to [[namespace.std]], users may specialize
744
+ `enable_sender` to `true` for cv-unqualified program-defined types that
745
+ model `sender`, and `false` for types that do not. Such specializations
746
+ shall be usable in constant expressions [[expr.const]] and have type
747
+ `const bool`.
748
+
749
+ The exposition-only concepts `sender-of` and `sender-in-of` define the
750
+ requirements for a sender type that completes with a given unique set of
751
+ value result types.
752
+
753
+ ``` cpp
754
+ namespace std::execution {
755
+ template<class... As>
756
+ using value-signature = set_value_t(As...); // exposition only
757
+
758
+ template<class Sndr, class SetValue, class... Env>
759
+ concept sender-in-of-impl = // exposition only
760
+ sender_in<Sndr, Env...> &&
761
+ MATCHING-SIG(SetValue, // see [exec.general]
762
+ gather-signatures<set_value_t, // see [exec.cmplsig]
763
+ completion_signatures_of_t<Sndr, Env...>,
764
+ value-signature,
765
+ type_identity_t>);
766
+
767
+ template<class Sndr, class Env, class... Values>
768
+ concept sender-in-of = // exposition only
769
+ sender-in-of-impl<Sndr, set_value_t(Values...), Env>;
770
+
771
+ template<class Sndr, class... Values>
772
+ concept sender-of = // exposition only
773
+ sender-in-of-impl<Sndr, set_value_t(Values...)>;
774
+ }
775
+ ```
776
+
777
+ Let `sndr` be an expression such that `decltype((sndr))` is `Sndr`. The
778
+ type `tag_of_t<Sndr>` is as follows:
779
+
780
+ - If the declaration
781
+ ``` cpp
782
+ auto&& [tag, data, ...children] = sndr;
783
+ ```
784
+
785
+ would be well-formed, `tag_of_t<Sndr>` is an alias for
786
+ `decltype(auto(tag))`.
787
+ - Otherwise, `tag_of_t<Sndr>` is ill-formed.
788
+
789
+ Let `sender-for` be an exposition-only concept defined as follows:
790
+
791
+ ``` cpp
792
+ namespace std::execution {
793
+ template<class Sndr, class Tag>
794
+ concept sender-for =
795
+ sender<Sndr> &&
796
+ same_as<tag_of_t<Sndr>, Tag>;
797
+ }
798
+ ```
799
+
800
+ For a type `T`, `SET-VALUE-SIG(T)` denotes the type `set_value_t()` if
801
+ `T` is cv `void`; otherwise, it denotes the type `set_value_t(T)`.
802
+
803
+ Library-provided sender types
804
+
805
+ - always expose an overload of a member `connect` that accepts an rvalue
806
+ sender and
807
+ - only expose an overload of a member `connect` that accepts an lvalue
808
+ sender if they model `copy_constructible`.
809
+
810
+ ### Awaitable helpers <a id="exec.awaitable">[[exec.awaitable]]</a>
811
+
812
+ The sender concepts recognize awaitables as senders. For [[exec]], an
813
+ *awaitable* is an expression that would be well-formed as the operand of
814
+ a `co_await` expression within a given context.
815
+
816
+ For a subexpression `c`, let `GET-AWAITER(c, p)` be
817
+ expression-equivalent to the series of transformations and conversions
818
+ applied to `c` as the operand of an *await-expression* in a coroutine,
819
+ resulting in lvalue `e` as described by [[expr.await]], where `p` is an
820
+ lvalue referring to the coroutine’s promise, which has type `Promise`.
821
+
822
+ [*Note 1*: This includes the invocation of the promise type’s
823
+ `await_transform` member if any, the invocation of the
824
+ `operator co_await` picked by overload resolution if any, and any
825
+ necessary implicit conversions and materializations. — *end note*]
826
+
827
+ Let `GET-AWAITER(c)` be expression-equivalent to `GET-AWAITER(c, q)`
828
+ where `q` is an lvalue of an unspecified empty class type `none-such`
829
+ that lacks an `await_transform` member, and where
830
+ `coroutine_handle<none-such>` behaves as `coroutine_handle<void>`.
831
+
832
+ Let `is-awaitable` be the following exposition-only concept:
833
+
834
+ ``` cpp
835
+ namespace std {
836
+ template<class T>
837
+ concept await-suspend-result = see below; // exposition only
838
+
839
+ template<class A, class... Promise>
840
+ concept is-awaiter = // exposition only
841
+ requires (A& a, coroutine_handle<Promise...> h) {
842
+ a.await_ready() ? 1 : 0;
843
+ { a.await_suspend(h) } -> await-suspend-result;
844
+ a.await_resume();
845
+ };
846
+
847
+ template<class C, class... Promise>
848
+ concept is-awaitable = // exposition only
849
+ requires (C (*fc)() noexcept, Promise&... p) {
850
+ { GET-AWAITER(fc(), p...) } -> is-awaiter<Promise...>;
851
+ };
852
+ }
853
+ ```
854
+
855
+ `\defexposconcept{await-suspend-result}<T>` is `true` if and only if one
856
+ of the following is `true`:
857
+
858
+ - `T` is `void`, or
859
+ - `T` is `bool`, or
860
+ - `T` is a specialization of `coroutine_handle`.
861
+
862
+ For a subexpression `c` such that `decltype((c))` is type `C`, and an
863
+ lvalue `p` of type `Promise`, `await-result- type<C, Promise>` denotes
864
+ the type `decltype(GET-AWAITER(c, p).await_resume())` and
865
+ `await-result-type<C>` denotes the type
866
+ `decltype(GET-AWAITER(c).await_resume())`.
867
+
868
+ Let *`with-await-transform`* be the exposition-only class template:
869
+
870
+ ``` cpp
871
+ namespace std::execution {
872
+ template<class T, class Promise>
873
+ concept has-as-awaitable = // exposition only
874
+ requires (T&& t, Promise& p) {
875
+ { std::forward<T>(t).as_awaitable(p) } -> is-awaitable<Promise&>;
876
+ };
877
+
878
+ template<class Derived>
879
+ struct with-await-transform { // exposition only
880
+ template<class T>
881
+ T&& await_transform(T&& value) noexcept {
882
+ return std::forward<T>(value);
883
+ }
884
+
885
+ template<has-as-awaitable<Derived> T>
886
+ auto await_transform(T&& value)
887
+ noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))
888
+ -> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {
889
+ return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this));
890
+ }
891
+ };
892
+ }
893
+ ```
894
+
895
+ Let *`env-promise`* be the exposition-only class template:
896
+
897
+ ``` cpp
898
+ namespace std::execution {
899
+ template<class Env>
900
+ struct env-promise : with-await-transform<env-promise<Env>> { // exposition only
901
+ unspecified get_return_object() noexcept;
902
+ unspecified initial_suspend() noexcept;
903
+ unspecified final_suspend() noexcept;
904
+ void unhandled_exception() noexcept;
905
+ void return_void() noexcept;
906
+ coroutine_handle<> unhandled_stopped() noexcept;
907
+
908
+ const Env& get_env() const noexcept;
909
+ };
910
+ }
911
+ ```
912
+
913
+ [*Note 2*: Specializations of *`env-promise`* are used only for the
914
+ purpose of type computation; its members need not be
915
+ defined. — *end note*]
916
+
917
+ ### `execution::default_domain` <a id="exec.domain.default">[[exec.domain.default]]</a>
918
+
919
+ ``` cpp
920
+ namespace std::execution {
921
+ struct default_domain {
922
+ template<sender Sndr, queryable... Env>
923
+ requires (sizeof...(Env) <= 1)
924
+ static constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
925
+ noexcept(see below);
926
+
927
+ template<sender Sndr, queryable Env>
928
+ static constexpr queryable decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
929
+
930
+ template<class Tag, sender Sndr, class... Args>
931
+ static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
932
+ noexcept(see below);
933
+ };
934
+ }
935
+ ```
936
+
937
+ ``` cpp
938
+ template<sender Sndr, queryable... Env>
939
+ requires (sizeof...(Env) <= 1)
940
+ constexpr sender decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
941
+ noexcept(see below);
942
+ ```
943
+
944
+ Let `e` be the expression
945
+
946
+ ``` cpp
947
+ tag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...)
948
+ ```
949
+
950
+ if that expression is well-formed; otherwise,
951
+ `std::forward<Sndr>(sndr)`.
952
+
953
+ *Returns:* `e`.
954
+
955
+ *Remarks:* The exception specification is equivalent to `noexcept(e)`.
956
+
957
+ ``` cpp
958
+ template<sender Sndr, queryable Env>
959
+ constexpr queryable decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
960
+ ```
961
+
962
+ Let `e` be the expression
963
+
964
+ ``` cpp
965
+ tag_of_t<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
966
+ ```
967
+
968
+ if that expression is well-formed; otherwise,
969
+ *`FWD-ENV`*`(std::forward<Env>(env))`.
970
+
971
+ *Mandates:* `noexcept(e)` is `true`.
972
+
973
+ *Returns:* `e`.
974
+
975
+ ``` cpp
976
+ template<class Tag, sender Sndr, class... Args>
977
+ constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
978
+ noexcept(see below);
979
+ ```
980
+
981
+ Let `e` be the expression
982
+
983
+ ``` cpp
984
+ Tag().apply_sender(std::forward<Sndr>(sndr), std::forward<Args>(args)...)
985
+ ```
986
+
987
+ *Constraints:* `e` is a well-formed expression.
988
+
989
+ *Returns:* `e`.
990
+
991
+ *Remarks:* The exception specification is equivalent to `noexcept(e)`.
992
+
993
+ ### `execution::transform_sender` <a id="exec.snd.transform">[[exec.snd.transform]]</a>
994
+
995
+ ``` cpp
996
+ namespace std::execution {
997
+ template<class Domain, sender Sndr, queryable... Env>
998
+ requires (sizeof...(Env) <= 1)
999
+ constexpr sender decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env)
1000
+ noexcept(see below);
1001
+ }
1002
+ ```
1003
+
1004
+ Let *transformed-sndr* be the expression
1005
+
1006
+ ``` cpp
1007
+ dom.transform_sender(std::forward<Sndr>(sndr), env...)
1008
+ ```
1009
+
1010
+ if that expression is well-formed; otherwise,
1011
+
1012
+ ``` cpp
1013
+ default_domain().transform_sender(std::forward<Sndr>(sndr), env...)
1014
+ ```
1015
+
1016
+ Let *final-sndr* be the expression *transformed-sndr* if
1017
+ *transformed-sndr* and *sndr* have the same type ignoring cv-qualifiers;
1018
+ otherwise, it is the expression
1019
+ `transform_sender(dom, `*`transformed-sndr`*`, env...)`.
1020
+
1021
+ *Returns:* *final-sndr*.
1022
+
1023
+ *Remarks:* The exception specification is equivalent to
1024
+ `noexcept(`*`final-sndr`*`)`.
1025
+
1026
+ ### `execution::transform_env` <a id="exec.snd.transform.env">[[exec.snd.transform.env]]</a>
1027
+
1028
+ ``` cpp
1029
+ namespace std::execution {
1030
+ template<class Domain, sender Sndr, queryable Env>
1031
+ constexpr queryable decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept;
1032
+ }
1033
+ ```
1034
+
1035
+ Let `e` be the expression
1036
+
1037
+ ``` cpp
1038
+ dom.transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
1039
+ ```
1040
+
1041
+ if that expression is well-formed; otherwise,
1042
+
1043
+ ``` cpp
1044
+ default_domain().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
1045
+ ```
1046
+
1047
+ *Mandates:* `noexcept(e)` is `true`.
1048
+
1049
+ *Returns:* `e`.
1050
+
1051
+ ### `execution::apply_sender` <a id="exec.snd.apply">[[exec.snd.apply]]</a>
1052
+
1053
+ ``` cpp
1054
+ namespace std::execution {
1055
+ template<class Domain, class Tag, sender Sndr, class... Args>
1056
+ constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args)
1057
+ noexcept(see below);
1058
+ }
1059
+ ```
1060
+
1061
+ Let e be the expression
1062
+
1063
+ ``` cpp
1064
+ dom.apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
1065
+ ```
1066
+
1067
+ if that expression is well-formed; otherwise,
1068
+
1069
+ ``` cpp
1070
+ default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
1071
+ ```
1072
+
1073
+ *Constraints:* The expression e is well-formed.
1074
+
1075
+ *Returns:* e.
1076
+
1077
+ *Remarks:* The exception specification is equivalent to `noexcept(`e`)`.
1078
+
1079
+ ### `execution::get_completion_signatures` <a id="exec.getcomplsigs">[[exec.getcomplsigs]]</a>
1080
+
1081
+ ``` cpp
1082
+ template<class Sndr, class... Env>
1083
+ consteval auto get_completion_signatures() -> valid-completion-signatures auto;
1084
+ ```
1085
+
1086
+ Let except be an rvalue subexpression of an unspecified class type
1087
+ Except such that `<`Except`> && derived_from<`Except`, exception>` is
1088
+ `true`. Let *`CHECKED-COMPLSIGS`*`(`e`)` be e if e is a core constant
1089
+ expression whose type satisfies `valid-completion-signatures`;
1090
+ otherwise, it is the following expression:
1091
+
1092
+ ``` cpp
1093
+ (e, throw except, completion_signatures())
1094
+ ```
1095
+
1096
+ Let *`get-complsigs`*`<Sndr, Env...>()` be expression-equivalent to
1097
+ `remove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>()`.
1098
+ Let `NewSndr` be `Sndr` if `sizeof...(Env) == 0` is `true`; otherwise,
1099
+ `decltype(`s`)` where s is the following expression:
1100
+
1101
+ ``` cpp
1102
+ transform_sender(
1103
+ get-domain-late(declval<Sndr>(), declval<Env>()...),
1104
+ declval<Sndr>(),
1105
+ declval<Env>()...)
1106
+ ```
1107
+
1108
+ *Constraints:* `sizeof...(Env) <= 1` is `true`.
1109
+
1110
+ *Effects:* Equivalent to: `return `e`;` where e is expression-equivalent
1111
+ to the following:
1112
+
1113
+ - *`CHECKED-COMPLSIGS`*`(`*`get-complsigs`*`<NewSndr, Env...>())` if
1114
+ *`get-complsigs`*`<NewSndr, Env...>()` is a well-formed expression.
1115
+ - Otherwise, *`CHECKED-COMPLSIGS`*`(`*`get-complsigs`*`<NewSndr>())` if
1116
+ *`get-complsigs`*`<NewSndr>()` is a well-formed expression.
1117
+ - Otherwise,
1118
+ ``` cpp
1119
+ completion_signatures<
1120
+ SET-VALUE-SIG(await-result-type<NewSndr, env-promise<Env>...>), // [exec.snd.concepts]
1121
+ set_error_t(exception_ptr),
1122
+ set_stopped_t()>
1123
+ ```
1124
+
1125
+ if `is-awaitable<NewSndr, `*`env-promise`*`<Env>...>` is `true`.
1126
+ - Otherwise,
1127
+ `(throw `*`dependent-sender-error`*`(), completion_signatures())` if
1128
+ `sizeof...(Env) == 0` is `true`, where *`dependent-sender-error`* is
1129
+ `dependent_sender_error` or an unspecified type derived publicly and
1130
+ unambiguously from `dependent_sender_error`.
1131
+ - Otherwise, `(throw `except`, completion_signatures())`.
1132
+
1133
+ Given a type `Env`, if `completion_signatures_of_t<Sndr>` and
1134
+ `completion_signatures_of_t<Sndr, Env>` are both well-formed, they shall
1135
+ denote the same type.
1136
+
1137
+ Let `rcvr` be an rvalue whose type `Rcvr` models `receiver`, and let
1138
+ `Sndr` be the type of a sender such that
1139
+ `sender_in<Sndr, env_of_t<Rcvr>>` is `true`. Let `Sigs...` be the
1140
+ template arguments of the `completion_signatures` specialization named
1141
+ by `completion_signatures_of_t<Sndr, env_of_t<Rcvr>>`. Let `CSO` be a
1142
+ completion function. If sender `Sndr` or its operation state cause the
1143
+ expression `CSO(rcvr, args...)` to be potentially evaluated
1144
+ [[basic.def.odr]] then there shall be a signature `Sig` in `Sigs...`
1145
+ such that
1146
+
1147
+ ``` cpp
1148
+ MATCHING-SIG(decayed-typeof<CSO>(decltype(args)...), Sig)
1149
+ ```
1150
+
1151
+ is `true` [[exec.general]].
1152
+
1153
+ ### `execution::connect` <a id="exec.connect">[[exec.connect]]</a>
1154
+
1155
+ `connect` connects [[exec.async.ops]] a sender with a receiver.
1156
+
1157
+ The name `connect` denotes a customization point object. For
1158
+ subexpressions `sndr` and `rcvr`, let `Sndr` be `decltype((sndr))` and
1159
+ `Rcvr` be `decltype((rcvr))`, let `new_sndr` be the expression
1160
+
1161
+ ``` cpp
1162
+ transform_sender(decltype(get-domain-late(sndr, get_env(rcvr))){}, sndr, get_env(rcvr))
1163
+ ```
1164
+
1165
+ and let `DS` and `DR` be `decay_t<decltype((new_sndr))>` and
1166
+ `decay_t<Rcvr>`, respectively.
1167
+
1168
+ Let *`connect-awaitable-promise`* be the following exposition-only
1169
+ class:
1170
+
1171
+ ``` cpp
1172
+ namespace std::execution {
1173
+ struct connect-awaitable-promise : with-await-transform<connect-awaitable-promise> {
1174
+
1175
+ connect-awaitable-promise(DS&, DR& rcvr) noexcept : rcvr(rcvr) {}
1176
+
1177
+ suspend_always initial_suspend() noexcept { return {}; }
1178
+ [[noreturn]] suspend_always final_suspend() noexcept { terminate(); }
1179
+ [[noreturn]] void unhandled_exception() noexcept { terminate(); }
1180
+ [[noreturn]] void return_void() noexcept { terminate(); }
1181
+
1182
+ coroutine_handle<> unhandled_stopped() noexcept {
1183
+ set_stopped(std::move(rcvr));
1184
+ return noop_coroutine();
1185
+ }
1186
+
1187
+ operation-state-task get_return_object() noexcept {
1188
+ return operation-state-task{
1189
+ coroutine_handle<connect-awaitable-promise>::from_promise(*this)};
1190
+ }
1191
+
1192
+ env_of_t<DR> get_env() const noexcept {
1193
+ return execution::get_env(rcvr);
1194
+ }
1195
+
1196
+ private:
1197
+ DR& rcvr; // exposition only
1198
+ };
1199
+ }
1200
+ ```
1201
+
1202
+ Let *`operation-state-task`* be the following exposition-only class:
1203
+
1204
+ ``` cpp
1205
+ namespace std::execution {
1206
+ struct operation-state-task { // exposition only
1207
+ using operation_state_concept = operation_state_t;
1208
+ using promise_type = connect-awaitable-promise;
1209
+
1210
+ explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
1211
+ operation-state-task(operation-state-task&&) = delete;
1212
+ ~operation-state-task() { coro.destroy(); }
1213
+
1214
+ void start() & noexcept {
1215
+ coro.resume();
1216
+ }
1217
+
1218
+ private:
1219
+ coroutine_handle<> coro; // exposition only
1220
+ };
1221
+ }
1222
+ ```
1223
+
1224
+ Let `V` name the type
1225
+ `await-result-type<DS, connect-awaitable-promise>`, let `Sigs` name the
1226
+ type
1227
+
1228
+ ``` cpp
1229
+ completion_signatures<
1230
+ SET-VALUE-SIG(V), // see~[exec.snd.concepts]
1231
+ set_error_t(exception_ptr),
1232
+ set_stopped_t()>
1233
+ ```
1234
+
1235
+ and let *`connect-awaitable`* be an exposition-only coroutine defined as
1236
+ follows:
1237
+
1238
+ ``` cpp
1239
+ namespace std::execution {
1240
+ template<class Fun, class... Ts>
1241
+ auto suspend-complete(Fun fun, Ts&&... as) noexcept { // exposition only
1242
+ auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); };
1243
+
1244
+ struct awaiter {
1245
+ decltype(fn) fn; // exposition only
1246
+
1247
+ static constexpr bool await_ready() noexcept { return false; }
1248
+ void await_suspend(coroutine_handle<>) noexcept { fn(); }
1249
+ [[noreturn]] void await_resume() noexcept { unreachable(); }
1250
+ };
1251
+ return awaiter{fn};
1252
+ }
1253
+
1254
+ operation-state-task connect-awaitable(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> {
1255
+ exception_ptr ep;
1256
+ try {
1257
+ if constexpr (same_as<V, void>) {
1258
+ co_await std::move(sndr);
1259
+ co_await suspend-complete(set_value, std::move(rcvr));
1260
+ } else {
1261
+ co_await suspend-complete(set_value, std::move(rcvr), co_await std::move(sndr));
1262
+ }
1263
+ } catch(...) {
1264
+ ep = current_exception();
1265
+ }
1266
+ co_await suspend-complete(set_error, std::move(rcvr), std::move(ep));
1267
+ }
1268
+ }
1269
+ ```
1270
+
1271
+ The expression `connect(sndr, rcvr)` is expression-equivalent to:
1272
+
1273
+ - `new_sndr.connect(rcvr)` if that expression is well-formed.
1274
+ *Mandates:* The type of the expression above satisfies
1275
+ `operation_state`.
1276
+ - Otherwise, `connect-awaitable(new_sndr, rcvr)`.
1277
+
1278
+ Except that `rcvr` is evaluated only once.
1279
+
1280
+ *Mandates:* The following are `true`:
1281
+
1282
+ - `\texttt{sender_in}<Sndr, env_of_t<Rcvr>>`
1283
+ - `\texttt{receiver_of}<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>`
1284
+
1285
+ ### Sender factories <a id="exec.factories">[[exec.factories]]</a>
1286
+
1287
+ #### `execution::schedule` <a id="exec.schedule">[[exec.schedule]]</a>
1288
+
1289
+ `schedule` obtains a schedule sender [[exec.async.ops]] from a
1290
+ scheduler.
1291
+
1292
+ The name `schedule` denotes a customization point object. For a
1293
+ subexpression `sch`, the expression `schedule(sch)` is
1294
+ expression-equivalent to `sch.schedule()`.
1295
+
1296
+ *Mandates:* The type of `sch.schedule()` satisfies `sender`.
1297
+
1298
+ If the expression
1299
+
1300
+ ``` cpp
1301
+ get_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch
1302
+ ```
1303
+
1304
+ is ill-formed or evaluates to `false`, the behavior of calling
1305
+ `schedule(sch)` is undefined.
1306
+
1307
+ #### `execution::just`, `execution::just_error`, `execution::just_stopped` <a id="exec.just">[[exec.just]]</a>
1308
+
1309
+ `just`, `just_error`, and `just_stopped` are sender factories whose
1310
+ asynchronous operations complete synchronously in their start operation
1311
+ with a value completion operation, an error completion operation, or a
1312
+ stopped completion operation, respectively.
1313
+
1314
+ The names `just`, `just_error`, and `just_stopped` denote customization
1315
+ point objects. Let *`just-cpo`* be one of `just`, `just_error`, or
1316
+ `just_stopped`. For a pack of subexpressions `ts`, let `Ts` be the pack
1317
+ of types `decltype((ts))`. The expression `just-cpo(ts...)` is
1318
+ ill-formed if
1319
+
1320
+ - `(movable-value<Ts> &&...)` is `false`, or
1321
+ - *`just-cpo`* is `just_error` and `sizeof...(ts) == 1` is `false`, or
1322
+ - *`just-cpo`* is `just_stopped` and `sizeof...(ts) == 0` is `false`.
1323
+
1324
+ Otherwise, it is expression-equivalent to
1325
+ `make-sender(just-cpo, product-type{ts...})`.
1326
+
1327
+ For `just`, `just_error`, and `just_stopped`, let *`set-cpo`* be
1328
+ `set_value`, `set_error`, and `set_stopped`, respectively. The
1329
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1330
+ specialized for *`just-cpo`* as follows:
1331
+
1332
+ ``` cpp
1333
+ namespace std::execution {
1334
+ template<>
1335
+ struct impls-for<decayed-typeof<just-cpo>> : default-impls {
1336
+ static constexpr auto start =
1337
+ [](auto& state, auto& rcvr) noexcept -> void {
1338
+ auto& [...ts] = state;
1339
+ set-cpo(std::move(rcvr), std::move(ts)...);
1340
+ };
1341
+ };
1342
+ }
1343
+ ```
1344
+
1345
+ #### `execution::read_env` <a id="exec.read.env">[[exec.read.env]]</a>
1346
+
1347
+ `read_env` is a sender factory for a sender whose asynchronous operation
1348
+ completes synchronously in its start operation with a value completion
1349
+ result equal to a value read from the receiver’s associated environment.
1350
+
1351
+ `read_env` is a customization point object. For some query object `q`,
1352
+ the expression `read_env(q)` is expression-equivalent to
1353
+ `make-sender(read_env, q)`.
1354
+
1355
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1356
+ specialized for `read_env` as follows:
1357
+
1358
+ ``` cpp
1359
+ namespace std::execution {
1360
+ template<>
1361
+ struct impls-for<decayed-typeof<read_env>> : default-impls {
1362
+ static constexpr auto start =
1363
+ [](auto query, auto& rcvr) noexcept -> void {
1364
+ TRY-SET-VALUE(rcvr, query(get_env(rcvr)));
1365
+ };
1366
+ };
1367
+
1368
+ template<class Sndr, class Env>
1369
+ static consteval void check-types();
1370
+ }
1371
+ ```
1372
+
1373
+ ``` cpp
1374
+ template<class Sndr, class Env>
1375
+ static consteval void check-types();
1376
+ ```
1377
+
1378
+ Let `Q` be `decay_t<`*`data-type`*`<Sndr>>`.
1379
+
1380
+ *Throws:* An exception of an unspecified type derived from `exception`
1381
+ if the expression `Q()(env)` is ill-formed or has type `void`, where
1382
+ `env` is an lvalue subexpression whose type is `Env`.
1383
+
1384
+ ### Sender adaptors <a id="exec.adapt">[[exec.adapt]]</a>
1385
+
1386
+ #### General <a id="exec.adapt.general">[[exec.adapt.general]]</a>
1387
+
1388
+ Subclause [[exec.adapt]] specifies a set of sender adaptors.
1389
+
1390
+ The bitwise inclusive operator is overloaded for the purpose of creating
1391
+ sender chains. The adaptors also support function call syntax with
1392
+ equivalent semantics.
1393
+
1394
+ Unless otherwise specified:
1395
+
1396
+ - A sender adaptor is prohibited from causing observable effects, apart
1397
+ from moving and copying its arguments, before the returned sender is
1398
+ connected with a receiver using `connect`, and `start` is called on
1399
+ the resulting operation state.
1400
+ - A parent sender [[exec.async.ops]] with a single child sender `sndr`
1401
+ has an associated attribute object equal to `FWD-ENV(get_env(sndr))`
1402
+ [[exec.fwd.env]].
1403
+ - A parent sender with more than one child sender has an associated
1404
+ attributes object equal to `env<>{}`.
1405
+ - When a parent sender is connected to a receiver `rcvr`, any receiver
1406
+ used to connect a child sender has an associated environment equal to
1407
+ `FWD-ENV(get_env(rcvr))`.
1408
+ - An adaptor whose child senders are all non-dependent
1409
+ [[exec.async.ops]] is itself non-dependent.
1410
+ - These requirements apply to any function that is selected by the
1411
+ implementation of the sender adaptor.
1412
+ - *Recommended practice:* Implementations should use the completion
1413
+ signatures of the adaptors to communicate type errors to users and to
1414
+ propagate any such type errors from child senders.
1415
+
1416
+ If a sender returned from a sender adaptor specified in [[exec.adapt]]
1417
+ is specified to include `set_error_t(Err)` among its set of completion
1418
+ signatures where `decay_t<Err>` denotes the type `exception_ptr`, but
1419
+ the implementation does not potentially evaluate an error completion
1420
+ operation with an `exception_ptr` argument, the implementation is
1421
+ allowed to omit the `exception_ptr` error completion signature from the
1422
+ set.
1423
+
1424
+ #### Closure objects <a id="exec.adapt.obj">[[exec.adapt.obj]]</a>
1425
+
1426
+ A *pipeable sender adaptor closure object* is a function object that
1427
+ accepts one or more `sender` arguments and returns a `sender`. For a
1428
+ pipeable sender adaptor closure object `c` and an expression `sndr` such
1429
+ that `decltype((sndr))` models `sender`, the following expressions are
1430
+ equivalent and yield a `sender`:
1431
+
1432
+ ``` cpp
1433
+ c(sndr)
1434
+ sndr | c
1435
+ ```
1436
+
1437
+ Given an additional pipeable sender adaptor closure object `d`, the
1438
+ expression `c | d` produces another pipeable sender adaptor closure
1439
+ object `e`:
1440
+
1441
+ `e` is a perfect forwarding call wrapper [[func.require]] with the
1442
+ following properties:
1443
+
1444
+ - Its target object is an object `d2` of type `decltype(auto(d))`
1445
+ direct-non-list-initialized with `d`.
1446
+ - It has one bound argument entity, an object `c2` of type
1447
+ `decltype(auto(c))` direct-non-list-initialized with `c`.
1448
+ - Its call pattern is `d2(c2(arg))`, where arg is the argument used in a
1449
+ function call expression of `e`.
1450
+
1451
+ The expression `c | d` is well-formed if and only if the initializations
1452
+ of the state entities [[func.def]] of `e` are all well-formed.
1453
+
1454
+ An object `t` of type `T` is a pipeable sender adaptor closure object if
1455
+ `T` models `derived_from<sender_adaptor_closure<T>>`, `T` has no other
1456
+ base classes of type `sender_adaptor_closure<U>` for any other type `U`,
1457
+ and `T` does not satisfy `sender`.
1458
+
1459
+ The template parameter `D` for `sender_adaptor_closure` can be an
1460
+ incomplete type. Before any expression of type cv `D` appears as an
1461
+ operand to the `|` operator, `D` shall be complete and model
1462
+ `derived_from<sender_adaptor_closure<D>>`. The behavior of an expression
1463
+ involving an object of type cv `D` as an operand to the `|` operator is
1464
+ undefined if overload resolution selects a program-defined `operator|`
1465
+ function.
1466
+
1467
+ A *pipeable sender adaptor object* is a customization point object that
1468
+ accepts a `sender` as its first argument and returns a `sender`. If a
1469
+ pipeable sender adaptor object accepts only one argument, then it is a
1470
+ pipeable sender adaptor closure object.
1471
+
1472
+ If a pipeable sender adaptor object adaptor accepts more than one
1473
+ argument, then let `sndr` be an expression such that `decltype((sndr))`
1474
+ models `sender`, let `args...` be arguments such that
1475
+ `adaptor(sndr, args...)` is a well-formed expression as specified below,
1476
+ and let `BoundArgs` be a pack that denotes `decltype(auto(args))...`.
1477
+ The expression `adaptor(args...)` produces a pipeable sender adaptor
1478
+ closure object `f` that is a perfect forwarding call wrapper with the
1479
+ following properties:
1480
+
1481
+ - Its target object is a copy of adaptor.
1482
+ - Its bound argument entities `bound_args` consist of objects of types
1483
+ `BoundArgs...` direct-non-list-initialized with
1484
+ `std::forward<decltype((args))>(args)...`, respectively.
1485
+ - Its call pattern is `adaptor(rcvr, bound_args...)`, where `rcvr` is
1486
+ the argument used in a function call expression of `f`.
1487
+
1488
+ The expression `adaptor(args...)` is well-formed if and only if the
1489
+ initializations of the bound argument entities of the result, as
1490
+ specified above, are all well-formed.
1491
+
1492
+ #### `execution::write_env` <a id="exec.write.env">[[exec.write.env]]</a>
1493
+
1494
+ `write_env` is a sender adaptor that accepts a sender and a queryable
1495
+ object, and that returns a sender that, when connected with a receiver
1496
+ `rcvr`, connects the adapted sender with a receiver whose execution
1497
+ environment is the result of joining the `queryable` object to the
1498
+ result of `get_env(rcvr)`.
1499
+
1500
+ `write_env` is a customization point object. For some subexpressions
1501
+ `sndr` and `env`, if `decltype((sndr))` does not satisfy `sender` or if
1502
+ `decltype((env))` does not satisfy `queryable`, the expression
1503
+ `write_env(sndr, env)` is ill-formed. Otherwise, it is
1504
+ expression-equivalent to `make-sender(write_env, env, sndr)`.
1505
+
1506
+ Let *`write-env-t`* denote the type `decltype(auto(write_env))`. The
1507
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1508
+ specialized for *`write-env-t`* as follows:
1509
+
1510
+ ``` cpp
1511
+ template<>
1512
+ struct impls-for<write-env-t> : default-impls {
1513
+ static constexpr auto join-env(const auto& state, const auto& env) noexcept {
1514
+ return see below;
1515
+ }
1516
+
1517
+ static constexpr auto get-env =
1518
+ [](auto, const auto& state, const auto& rcvr) noexcept {
1519
+ return join-env(state, FWD-ENV(get_env(rcvr)));
1520
+ };
1521
+
1522
+ template<class Sndr, class... Env>
1523
+ static consteval void check-types();
1524
+ };
1525
+ ```
1526
+
1527
+ Invocation of `impls-for<write-env-t>::join-env` returns an object `e`
1528
+ such that
1529
+
1530
+ - `decltype(e)` models `queryable` and
1531
+ - given a query object `q`, the expression `e.query(q)` is
1532
+ expression-equivalent to `state.query(q)` if that expression is valid,
1533
+ otherwise, `e.query(q)` is expression-equivalent to `env.query(q)`.
1534
+
1535
+ For a type `Sndr` and a pack of types `Env`, let `State` be
1536
+ `data-type<Sndr>` and let `JoinEnv` be the pack
1537
+ `decltype(join-env(declval<State>(), FWD-ENV(declval<Env>())))`. Then
1538
+ `impls-for<write-env-{t}>::check-types<Sndr, Env...>()` is
1539
+ expression-equivalent to
1540
+ `get_completion_signatures<child-{type}<Sndr>, JoinEnv...>()`.
1541
+
1542
+ #### `execution::unstoppable` <a id="exec.unstoppable">[[exec.unstoppable]]</a>
1543
+
1544
+ `unstoppable` is a sender adaptor that connects its inner sender with a
1545
+ receiver that has the execution environment of the outer receiver but
1546
+ with an object of type `never_stop_token` as the result of the
1547
+ `get_stop_token query`.
1548
+
1549
+ For a subexpression `sndr`, `unstoppable(sndr)` is expression-equivalent
1550
+ to `write_env(sndr, prop(get_stop_token, never_stop_token{}))`.
1551
+
1552
+ #### `execution::starts_on` <a id="exec.starts.on">[[exec.starts.on]]</a>
1553
+
1554
+ `starts_on` adapts an input sender into a sender that will start on an
1555
+ execution agent belonging to a particular scheduler’s associated
1556
+ execution resource.
1557
+
1558
+ The name `starts_on` denotes a customization point object. For
1559
+ subexpressions `sch` and `sndr`, if `decltype(( sch))` does not satisfy
1560
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
1561
+ `starts_on(sch, sndr)` is ill-formed.
1562
+
1563
+ Otherwise, the expression `starts_on(sch, sndr)` is
1564
+ expression-equivalent to:
1565
+
1566
+ ``` cpp
1567
+ transform_sender(
1568
+ query-with-default(get_domain, sch, default_domain()),
1569
+ make-sender(starts_on, sch, sndr))
1570
+ ```
1571
+
1572
+ except that `sch` is evaluated only once.
1573
+
1574
+ Let `out_sndr` and `env` be subexpressions such that `OutSndr` is
1575
+ `decltype((out_sndr))`. If `sender-for<OutSndr, starts_on_t>` is
1576
+ `false`, then the expressions `starts_on.transform_env(out_sndr, env)`
1577
+ and `starts_on.transform_sender(out_sndr, env)` are ill-formed;
1578
+ otherwise
1579
+
1580
+ - `starts_on.transform_env(out_sndr, env)` is equivalent to:
1581
+ ``` cpp
1582
+ auto&& [_, sch, _] = out_sndr;
1583
+ return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
1584
+ ```
1585
+ - `starts_on.transform_sender(out_sndr, env)` is equivalent to:
1586
+ ``` cpp
1587
+ auto&& [_, sch, sndr] = out_sndr;
1588
+ return let_value(
1589
+ schedule(sch),
1590
+ [sndr = std::forward_like<OutSndr>(sndr)]() mutable
1591
+ noexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {
1592
+ return std::move(sndr);
1593
+ });
1594
+ ```
1595
+
1596
+ Let `out_sndr` be a subexpression denoting a sender returned from
1597
+ `starts_on(sch, sndr)` or one equal to such, and let `OutSndr` be the
1598
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
1599
+ a receiver that has an environment of type `Env` such that
1600
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
1601
+ the operation state that results from connecting `out_sndr` with
1602
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on an execution agent
1603
+ of the associated execution resource of `sch`. If scheduling onto `sch`
1604
+ fails, an error completion on `out_rcvr` shall be executed on an
1605
+ unspecified execution agent.
1606
+
1607
+ #### `execution::continues_on` <a id="exec.continues.on">[[exec.continues.on]]</a>
1608
+
1609
+ `continues_on` adapts a sender into one that completes on the specified
1610
+ scheduler.
1611
+
1612
+ The name `continues_on` denotes a pipeable sender adaptor object. For
1613
+ subexpressions `sch` and `sndr`, if `decltype((sch))` does not satisfy
1614
+ `scheduler`, or `decltype((sndr))` does not satisfy `sender`,
1615
+ `continues_on(sndr, sch)` is ill-formed.
1616
+
1617
+ Otherwise, the expression `continues_on(sndr, sch)` is
1618
+ expression-equivalent to:
1619
+
1620
+ ``` cpp
1621
+ transform_sender(get-domain-early(sndr), make-sender(continues_on, sch, sndr))
1622
+ ```
1623
+
1624
+ except that `sndr` is evaluated only once.
1625
+
1626
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1627
+ specialized for `continues_on_t` as follows:
1628
+
1629
+ ``` cpp
1630
+ namespace std::execution {
1631
+ template<>
1632
+ struct impls-for<continues_on_t> : default-impls {
1633
+ static constexpr auto get-attrs =
1634
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
1635
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
1636
+ };
1637
+ };
1638
+ }
1639
+ ```
1640
+
1641
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
1642
+ `decltype((sndr))`. If `sender-for<Sndr, continues_on_t>` is `false`,
1643
+ then the expression `continues_on.transform_sender(sndr, env)` is
1644
+ ill-formed; otherwise, it is equal to:
1645
+
1646
+ ``` cpp
1647
+ auto [_, data, child] = sndr;
1648
+ return schedule_from(std::move(data), std::move(child));
1649
+ ```
1650
+
1651
+ [*Note 1*: This causes the `continues_on(sndr, sch)` sender to become
1652
+ `schedule_from(sch, sndr)` when it is connected with a receiver whose
1653
+ execution domain does not customize `continues_on`. — *end note*]
1654
+
1655
+ Let `out_sndr` be a subexpression denoting a sender returned from
1656
+ `continues_on(sndr, sch)` or one equal to such, and let `OutSndr` be the
1657
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
1658
+ a receiver that has an environment of type `Env` such that
1659
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
1660
+ the operation state that results from connecting `out_sndr` with
1661
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
1662
+ execution agent and execute completion operations on `out_rcvr` on an
1663
+ execution agent of the execution resource associated with `sch`. If
1664
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
1665
+ executed on an unspecified execution agent.
1666
+
1667
+ #### `execution::schedule_from` <a id="exec.schedule.from">[[exec.schedule.from]]</a>
1668
+
1669
+ `schedule_from` schedules work dependent on the completion of a sender
1670
+ onto a scheduler’s associated execution resource.
1671
+
1672
+ [*Note 1*: `schedule_from` is not meant to be used in user code; it is
1673
+ used in the implementation of `continues_on`. — *end note*]
1674
+
1675
+ The name `schedule_from` denotes a customization point object. For some
1676
+ subexpressions `sch` and `sndr`, let `Sch` be `decltype((sch))` and
1677
+ `Sndr` be `decltype((sndr))`. If `Sch` does not satisfy `scheduler`, or
1678
+ `Sndr` does not satisfy `sender`, `schedule_from(sch, sndr)` is
1679
+ ill-formed.
1680
+
1681
+ Otherwise, the expression `schedule_from(sch, sndr)` is
1682
+ expression-equivalent to:
1683
+
1684
+ ``` cpp
1685
+ transform_sender(
1686
+ query-with-default(get_domain, sch, default_domain()),
1687
+ make-sender(schedule_from, sch, sndr))
1688
+ ```
1689
+
1690
+ except that `sch` is evaluated only once.
1691
+
1692
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
1693
+ specialized for `schedule_from_t` as follows:
1694
+
1695
+ ``` cpp
1696
+ namespace std::execution {
1697
+ template<>
1698
+ struct impls-for<schedule_from_t> : default-impls {
1699
+ static constexpr auto get-attrs = see below;
1700
+ static constexpr auto get-state = see below;
1701
+ static constexpr auto complete = see below;
1702
+
1703
+ template<class Sndr, class... Env>
1704
+ static consteval void check-types();
1705
+ };
1706
+ }
1707
+ ```
1708
+
1709
+ The member `impls-for<schedule_from_t>::get-attrs` is initialized with a
1710
+ callable object equivalent to the following lambda:
1711
+
1712
+ ``` cpp
1713
+ [](const auto& data, const auto& child) noexcept -> decltype(auto) {
1714
+ return JOIN-ENV(SCHED-ATTRS(data), FWD-ENV(get_env(child)));
1715
+ }
1716
+ ```
1717
+
1718
+ The member `impls-for<schedule_from_t>::get-state` is initialized with a
1719
+ callable object equivalent to the following lambda:
1720
+
1721
+ ``` cpp
1722
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
1723
+ requires sender_in<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)> {
1724
+
1725
+ auto& [_, sch, child] = sndr;
1726
+
1727
+ using sched_t = decltype(auto(sch));
1728
+ using variant_t = see below;
1729
+ using receiver_t = see below;
1730
+ using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>;
1731
+ constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr}));
1732
+
1733
+ struct state-type {
1734
+ Rcvr& rcvr; // exposition only
1735
+ variant_t async-result; // exposition only
1736
+ operation_t op-state; // exposition only
1737
+
1738
+ explicit state-type(sched_t sch, Rcvr& rcvr) noexcept(nothrow)
1739
+ : rcvr(rcvr), op-state(connect(schedule(sch), receiver_t{this})) {}
1740
+ };
1741
+
1742
+ return state-type{sch, rcvr};
1743
+ }
1744
+ ```
1745
+
1746
+ ``` cpp
1747
+ template<class Sndr, class... Env>
1748
+ static consteval void check-types();
1749
+ ```
1750
+
1751
+ *Effects:* Equivalent to:
1752
+
1753
+ ``` cpp
1754
+ get_completion_signatures<schedule_result_t<data-type<Sndr>>, FWD-ENV-T(Env)...>();
1755
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
1756
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
1757
+ ```
1758
+
1759
+ Objects of the local class *`state-type`* can be used to initialize a
1760
+ structured binding.
1761
+
1762
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
1763
+ specialization named by
1764
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
1765
+ Let *`as-tuple`* be an alias template such that `as-tuple<Tag(Args...)>`
1766
+ denotes the type `decayed-tuple<Tag, Args...>`, and let
1767
+ *`is-nothrow-decay-copy-sig`* be a variable template such that
1768
+ `auto(is-nothrow-decay-copy-sig<Tag(Args...{})>)` is a constant
1769
+ expression of type `bool` and equal to
1770
+ `(is_nothrow_constructible_v<decay_t<Args>, Args> && ...)`. Let
1771
+ *`error-completion`* be a pack consisting of the type
1772
+ `set_error_t(exception_ptr)` if
1773
+ `(is-nothrow-decay-copy-sig<Sigs> &&...)` is `false`, and an empty pack
1774
+ otherwise. Then `variant_t` denotes the type
1775
+ `variant<monostate, as-tuple<Sigs>..., error-completion...>`, except
1776
+ with duplicate types removed.
1777
+
1778
+ `receiver_t` is an alias for the following exposition-only class:
1779
+
1780
+ ``` cpp
1781
+ namespace std::execution {
1782
+ struct receiver-type {
1783
+ using receiver_concept = receiver_t;
1784
+ state-type* state; // exposition only
1785
+
1786
+ void set_value() && noexcept {
1787
+ visit(
1788
+ [this]<class Tuple>(Tuple& result) noexcept -> void {
1789
+ if constexpr (!same_as<monostate, Tuple>) {
1790
+ auto& [tag, ...args] = result;
1791
+ tag(std::move(state->rcvr), std::move(args)...);
1792
+ }
1793
+ },
1794
+ state->async-result);
1795
+ }
1796
+
1797
+ template<class Error>
1798
+ void set_error(Error&& err) && noexcept {
1799
+ execution::set_error(std::move(state->rcvr), std::forward<Error>(err));
1800
+ }
1801
+
1802
+ void set_stopped() && noexcept {
1803
+ execution::set_stopped(std::move(state->rcvr));
1804
+ }
1805
+
1806
+ decltype(auto) get_env() const noexcept {
1807
+ return FWD-ENV(execution::get_env(state->rcvr));
1808
+ }
1809
+ };
1810
+ }
1811
+ ```
1812
+
1813
+ The expression in the `noexcept` clause of the lambda is `true` if the
1814
+ construction of the returned *`state-type`* object is not potentially
1815
+ throwing; otherwise, `false`.
1816
+
1817
+ The member `impls-for<schedule_from_t>::complete` is initialized with a
1818
+ callable object equivalent to the following lambda:
1819
+
1820
+ ``` cpp
1821
+ []<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept
1822
+ -> void {
1823
+ using result_t = decayed-tuple<Tag, Args...>;
1824
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...);
1825
+
1826
+ try {
1827
+ state.async-result.template emplace<result_t>(Tag(), std::forward<Args>(args)...);
1828
+ } catch (...) {
1829
+ if constexpr (!nothrow)
1830
+ state.async-result.template emplace<tuple<set_error_t,
1831
+ exception_ptr>>(set_error, current_exception());
1832
+ }
1833
+ start(state.op-state);
1834
+ };
1835
+ ```
1836
+
1837
+ Let `out_sndr` be a subexpression denoting a sender returned from
1838
+ `schedule_from(sch, sndr)` or one equal to such, and let `OutSndr` be
1839
+ the type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression
1840
+ denoting a receiver that has an environment of type `Env` such that
1841
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
1842
+ the operation state that results from connecting `out_sndr` with
1843
+ `out_rcvr`. Calling `start(op)` shall start `sndr` on the current
1844
+ execution agent and execute completion operations on `out_rcvr` on an
1845
+ execution agent of the execution resource associated with `sch`. If
1846
+ scheduling onto `sch` fails, an error completion on `out_rcvr` shall be
1847
+ executed on an unspecified execution agent.
1848
+
1849
+ #### `execution::on` <a id="exec.on">[[exec.on]]</a>
1850
+
1851
+ The `on` sender adaptor has two forms:
1852
+
1853
+ - `on(sch, sndr)`, which starts a sender `sndr` on an execution agent
1854
+ belonging to a scheduler `sch`’s associated execution resource and
1855
+ that, upon `sndr`’s completion, transfers execution back to the
1856
+ execution resource on which the `on` sender was started.
1857
+ - `on(sndr, sch, closure)`, which upon completion of a sender `sndr`,
1858
+ transfers execution to an execution agent belonging to a scheduler
1859
+ `sch`’s associated execution resource, then executes a sender adaptor
1860
+ closure `closure` with the async results of the sender, and that then
1861
+ transfers execution back to the execution resource on which `sndr`
1862
+ completed.
1863
+
1864
+ The name `on` denotes a pipeable sender adaptor object. For
1865
+ subexpressions `sch` and `sndr`, `on(sch, sndr)` is ill-formed if any of
1866
+ the following is `true`:
1867
+
1868
+ - `decltype((sch))` does not satisfy `scheduler`, or
1869
+ - `decltype((sndr))` does not satisfy `sender` and `sndr` is not a
1870
+ pipeable sender adaptor closure object [[exec.adapt.obj]], or
1871
+ - `decltype((sndr))` satisfies `sender` and `sndr `is also a pipeable
1872
+ sender adaptor closure object.
1873
+
1874
+ Otherwise, if `decltype((sndr))` satisfies `sender`, the expression
1875
+ `on(sch, sndr)` is expression-equivalent to:
1876
+
1877
+ ``` cpp
1878
+ transform_sender(
1879
+ query-with-default(get_domain, sch, default_domain()),
1880
+ make-sender(on, sch, sndr))
1881
+ ```
1882
+
1883
+ except that `sch` is evaluated only once.
1884
+
1885
+ For subexpressions `sndr`, `sch`, and `closure`, if
1886
+
1887
+ - `decltype((sch))` does not satisfy `scheduler`, or
1888
+ - `decltype((sndr))` does not satisfy `sender`, or
1889
+ - `closure` is not a pipeable sender adaptor closure object
1890
+ [[exec.adapt.obj]],
1891
+
1892
+ the expression `on(sndr, sch, closure)` is ill-formed; otherwise, it is
1893
+ expression-equivalent to:
1894
+
1895
+ ``` cpp
1896
+ transform_sender(
1897
+ get-domain-early(sndr),
1898
+ make-sender(on, product-type{sch, closure}, sndr))
1899
+ ```
1900
+
1901
+ except that `sndr` is evaluated only once.
1902
+
1903
+ Let `out_sndr` and `env` be subexpressions, let `OutSndr` be
1904
+ `decltype((out_sndr))`, and let `Env` be `decltype((env))`. If
1905
+ `sender-for<OutSndr, on_t>` is `false`, then the expressions
1906
+ `on.transform_env(out_sndr, env)` and
1907
+ `on.transform_sender(out_sndr, env)` are ill-formed.
1908
+
1909
+ Otherwise: Let *`not-a-scheduler`* be an unspecified empty class type.
1910
+
1911
+ The expression `on.transform_env(out_sndr, env)` has effects equivalent
1912
+ to:
1913
+
1914
+ ``` cpp
1915
+ auto&& [_, data, _] = out_sndr;
1916
+ if constexpr (scheduler<decltype(data)>) {
1917
+ return JOIN-ENV(SCHED-ENV(std::forward_like<OutSndr>(data)), FWD-ENV(std::forward<Env>(env)));
1918
+ } else {
1919
+ return std::forward<Env>(env);
1920
+ }
1921
+ ```
1922
+
1923
+ The expression `on.transform_sender(out_sndr, env)` has effects
1924
+ equivalent to:
1925
+
1926
+ ``` cpp
1927
+ auto&& [_, data, child] = out_sndr;
1928
+ if constexpr (scheduler<decltype(data)>) {
1929
+ auto orig_sch =
1930
+ query-with-default(get_scheduler, env, not-a-scheduler());
1931
+
1932
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
1933
+ return not-a-sender{};
1934
+ } else {
1935
+ return continues_on(
1936
+ starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
1937
+ std::move(orig_sch));
1938
+ }
1939
+ } else {
1940
+ auto& [sch, closure] = data;
1941
+ auto orig_sch = query-with-default(
1942
+ get_completion_scheduler<set_value_t>,
1943
+ get_env(child),
1944
+ query-with-default(get_scheduler, env, not-a-scheduler()));
1945
+
1946
+ if constexpr (same_as<decltype(orig_sch), not-a-scheduler>) {
1947
+ return not-a-sender{};
1948
+ } else {
1949
+ return write_env(
1950
+ continues_on(
1951
+ std::forward_like<OutSndr>(closure)(
1952
+ continues_on(
1953
+ write_env(std::forward_like<OutSndr>(child), SCHED-ENV(orig_sch)),
1954
+ sch)),
1955
+ orig_sch),
1956
+ SCHED-ENV(sch));
1957
+ }
1958
+ }
1959
+ ```
1960
+
1961
+ Let `out_sndr` be a subexpression denoting a sender returned from
1962
+ `on(sch, sndr)` or one equal to such, and let `OutSndr` be the type
1963
+ `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting a
1964
+ receiver that has an environment of type `Env` such that
1965
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
1966
+ the operation state that results from connecting `out_sndr` with
1967
+ `out_rcvr`. Calling `start(op)` shall
1968
+
1969
+ - remember the current scheduler, `get_scheduler(get_env(rcvr))`;
1970
+ - start `sndr` on an execution agent belonging to `sch`’s associated
1971
+ execution resource;
1972
+ - upon `sndr`’s completion, transfer execution back to the execution
1973
+ resource associated with the scheduler remembered in step 1; and
1974
+ - forward `sndr`’s async result to `out_rcvr`.
1975
+
1976
+ If any scheduling operation fails, an error completion on `out_rcvr`
1977
+ shall be executed on an unspecified execution agent.
1978
+
1979
+ Let `out_sndr` be a subexpression denoting a sender returned from
1980
+ `on(sndr, sch, closure)` or one equal to such, and let `OutSndr` be the
1981
+ type `decltype((out_sndr))`. Let `out_rcvr` be a subexpression denoting
1982
+ a receiver that has an environment of type `Env` such that
1983
+ `sender_in<OutSndr, Env>` is `true`. Let `op` be an lvalue referring to
1984
+ the operation state that results from connecting `out_sndr` with
1985
+ `out_rcvr`. Calling `start(op)` shall
1986
+
1987
+ - remember the current scheduler, which is the first of the following
1988
+ expressions that is well-formed:
1989
+ - `get_completion_scheduler<set_value_t>(get_env(sndr))`
1990
+ - `get_scheduler(get_env(rcvr))`;
1991
+ - start `sndr` on the current execution agent;
1992
+ - upon `sndr`’s completion, transfer execution to an agent owned by
1993
+ `sch`’s associated execution resource;
1994
+ - forward `sndr`’s async result as if by connecting and starting a
1995
+ sender `closure(S)`, where `S` is a sender that completes
1996
+ synchronously with `sndr`’s async result; and
1997
+ - upon completion of the operation started in the previous step,
1998
+ transfer execution back to the execution resource associated with the
1999
+ scheduler remembered in step 1 and forward the operation’s async
2000
+ result to `out_rcvr`.
2001
+
2002
+ If any scheduling operation fails, an error completion on `out_rcvr`
2003
+ shall be executed on an unspecified execution agent.
2004
+
2005
+ #### `execution::then`, `execution::upon_error`, `execution::upon_stopped` <a id="exec.then">[[exec.then]]</a>
2006
+
2007
+ `then` attaches an invocable as a continuation for an input sender’s
2008
+ value completion operation. `upon_error` and `upon_stopped` do the same
2009
+ for the error and stopped completion operations, respectively, sending
2010
+ the result of the invocable as a value completion.
2011
+
2012
+ The names `then`, `upon_error`, and `upon_stopped` denote pipeable
2013
+ sender adaptor objects. Let the expression *`then-cpo`* be one of
2014
+ `then`, `upon_error`, or `upon_stopped`. For subexpressions `sndr` and
2015
+ `f`, if `decltype((sndr))` does not satisfy `sender`, or `decltype((f))`
2016
+ does not satisfy `movable-value`, `then-cpo(sndr, f) `is ill-formed.
2017
+
2018
+ Otherwise, the expression `then-cpo(sndr, f)` is expression-equivalent
2019
+ to:
2020
+
2021
+ ``` cpp
2022
+ transform_sender(get-domain-early(sndr), make-sender(then-cpo, f, sndr))
2023
+ ```
2024
+
2025
+ except that `sndr` is evaluated only once.
2026
+
2027
+ For `then`, `upon_error`, and `upon_stopped`, let *`set-cpo`* be
2028
+ `set_value`, `set_error`, and `set_stopped`, respectively. The
2029
+ exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2030
+ specialized for *`then-cpo`* as follows:
2031
+
2032
+ ``` cpp
2033
+ namespace std::execution {
2034
+ template<>
2035
+ struct impls-for<decayed-typeof<then-cpo>> : default-impls {
2036
+ static constexpr auto complete =
2037
+ []<class Tag, class... Args>
2038
+ (auto, auto& fn, auto& rcvr, Tag, Args&&... args) noexcept -> void {
2039
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
2040
+ TRY-SET-VALUE(rcvr,
2041
+ invoke(std::move(fn), std::forward<Args>(args)...));
2042
+ } else {
2043
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2044
+ }
2045
+ };
2046
+
2047
+ template<class Sndr, class... Env>
2048
+ static consteval void check-types();
2049
+ };
2050
+ }
2051
+ ```
2052
+
2053
+ ``` cpp
2054
+ template<class Sndr, class... Env>
2055
+ static consteval void check-types();
2056
+ ```
2057
+
2058
+ *Effects:* Equivalent to:
2059
+
2060
+ ``` cpp
2061
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
2062
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
2063
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts...>)
2064
+ throw unspecified-exception();
2065
+ };
2066
+ cs.for-each(overload-set{fn, [](auto){}});
2067
+ ```
2068
+
2069
+ where *`unspecified-exception`* is a type derived from `exception`.
2070
+
2071
+ The expression `then-cpo(sndr, f)` has undefined behavior unless it
2072
+ returns a sender `out_sndr` that
2073
+
2074
+ - invokes `f` or a copy of such with the value, error, or stopped result
2075
+ datums of `sndr` for `then`, `upon_error`, and `upon_stopped`,
2076
+ respectively, using the result value of `f` as `out_sndr`’s value
2077
+ completion, and
2078
+ - forwards all other completion operations unchanged.
2079
+
2080
+ #### `execution::let_value`, `execution::let_error`, `execution::let_stopped` <a id="exec.let">[[exec.let]]</a>
2081
+
2082
+ `let_value`, `let_error`, and `let_stopped` transform a sender’s value,
2083
+ error, and stopped completions, respectively, into a new child
2084
+ asynchronous operation by passing the sender’s result datums to a
2085
+ user-specified callable, which returns a new sender that is connected
2086
+ and started.
2087
+
2088
+ For `let_value`, `let_error`, and `let_stopped`, let *`set-cpo`* be
2089
+ `set_value`, `set_error`, and `set_stopped`, respectively. Let the
2090
+ expression *`let-cpo`* be one of `let_value`, `let_error`, or
2091
+ `let_stopped`. For a subexpression `sndr`, let `let-env(sndr)` be
2092
+ expression-equivalent to the first well-formed expression below:
2093
+
2094
+ - `\exposid{SCHED-ENV}(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))`
2095
+ - `\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))`
2096
+ - `(void(sndr), env<>{})`
2097
+
2098
+ The names `let_value`, `let_error`, and `let_stopped` denote pipeable
2099
+ sender adaptor objects. For subexpressions `sndr` and `f`, let `F` be
2100
+ the decayed type of `f`. If `decltype((sndr))` does not satisfy `sender`
2101
+ or if `decltype((f))` does not satisfy `movable-value`, the expression
2102
+ `let-cpo(sndr, f)` is ill-formed. If `F` does not satisfy `invocable`,
2103
+ the expression `let_stopped(sndr, f)` is ill-formed.
2104
+
2105
+ Otherwise, the expression `let-cpo(sndr, f)` is expression-equivalent
2106
+ to:
2107
+
2108
+ ``` cpp
2109
+ transform_sender(get-domain-early(sndr), make-sender(let-cpo, f, sndr))
2110
+ ```
2111
+
2112
+ except that `sndr` is evaluated only once.
2113
+
2114
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2115
+ specialized for *`let-cpo`* as follows:
2116
+
2117
+ ``` cpp
2118
+ namespace std::execution {
2119
+ template<class State, class Rcvr, class... Args>
2120
+ void let-bind(State& state, Rcvr& rcvr, Args&&... args); // exposition only
2121
+
2122
+ template<>
2123
+ struct impls-for<decayed-typeof<let-cpo>> : default-impls {
2124
+ static constexpr auto get-state = see below;
2125
+ static constexpr auto complete = see below;
2126
+
2127
+ template<class Sndr, class... Env>
2128
+ static consteval void check-types();
2129
+ };
2130
+ }
2131
+ ```
2132
+
2133
+ Let *`receiver2`* denote the following exposition-only class template:
2134
+
2135
+ ``` cpp
2136
+ namespace std::execution {
2137
+ template<class Rcvr, class Env>
2138
+ struct receiver2 {
2139
+ using receiver_concept = receiver_t;
2140
+
2141
+ template<class... Args>
2142
+ void set_value(Args&&... args) && noexcept {
2143
+ execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
2144
+ }
2145
+
2146
+ template<class Error>
2147
+ void set_error(Error&& err) && noexcept {
2148
+ execution::set_error(std::move(rcvr), std::forward<Error>(err));
2149
+ }
2150
+
2151
+ void set_stopped() && noexcept {
2152
+ execution::set_stopped(std::move(rcvr));
2153
+ }
2154
+
2155
+ decltype(auto) get_env() const noexcept {
2156
+ return see below;
2157
+ }
2158
+
2159
+ Rcvr& rcvr; // exposition only
2160
+ Env env; // exposition only
2161
+ };
2162
+ }
2163
+ ```
2164
+
2165
+ Invocation of the function `receiver2::get_env` returns an object `e`
2166
+ such that
2167
+
2168
+ - `decltype(e)` models `queryable` and
2169
+ - given a query object `q`, the expression `e.query(q)` is
2170
+ expression-equivalent to `env.query(q)` if that expression is valid;
2171
+ otherwise, if the type of `q` satisfies `forwarding-query`,
2172
+ `e.query(q)` is expression-equivalent to `get_env(rcvr).query(q)`;
2173
+ otherwise, `e.query(q)` is ill-formed.
2174
+
2175
+ ``` cpp
2176
+ template<class Sndr, class... Env>
2177
+ static consteval void check-types();
2178
+ ```
2179
+
2180
+ *Effects:* Equivalent to:
2181
+
2182
+ ``` cpp
2183
+ using LetFn = remove_cvref_t<data-type<Sndr>>;
2184
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
2185
+ auto fn = []<class... Ts>(decayed-typeof<set-cpo>(*)(Ts...)) {
2186
+ if constexpr (!is-valid-let-sender) // see below
2187
+ throw unspecified-exception();
2188
+ };
2189
+ cs.for-each(overload-set(fn, [](auto){}));
2190
+ ```
2191
+
2192
+ where *`unspecified-exception`* is a type derived from `exception`, and
2193
+ where *`is-valid-let-sender`* is `true` if and only if all of the
2194
+ following are `true`:
2195
+
2196
+ - `(constructible_from<decay_t<Ts>, Ts> &&...)`
2197
+ - `invocable<LetFn, decay_t<Ts>&...>`
2198
+ - `sender<invoke_result_t<LetFn, decay_t<Ts>&...>>`
2199
+ - `sizeof...(Env) == 0 || sender_in<invoke_result_t<LetFn, decay_t<Ts>&...>, `*`env-t`*`...>`
2200
+
2201
+ where *`env-t`* is the pack
2202
+ `decltype(`*`let-cpo`*`.transform_env(declval<Sndr>(), declval<Env>()))`.
2203
+
2204
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}`
2205
+
2206
+ is initialized with a callable object equivalent to the following:
2207
+
2208
+ ``` cpp
2209
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
2210
+ auto& [_, fn, child] = sndr;
2211
+ using fn_t = decay_t<decltype(fn)>;
2212
+ using env_t = decltype(let-env(child));
2213
+ using args_variant_t = see below;
2214
+ using ops2_variant_t = see below;
2215
+
2216
+ struct state-type {
2217
+ fn_t fn; // exposition only
2218
+ env_t env; // exposition only
2219
+ args_variant_t args; // exposition only
2220
+ ops2_variant_t ops2; // exposition only
2221
+ };
2222
+ return state-type{allocator-aware-forward(std::forward_like<Sndr>(fn), rcvr),
2223
+ let-env(child), {}, {}};
2224
+ }
2225
+ ```
2226
+
2227
+ Let `Sigs` be a pack of the arguments to the `completion_signatures`
2228
+ specialization named by
2229
+ `completion_signatures_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>`.
2230
+ Let `LetSigs` be a pack of those types in `Sigs` with a return type of
2231
+ `decayed-typeof<set-cpo>`. Let *`as-tuple`* be an alias template such
2232
+ that `as-tuple<Tag(Args...)>` denotes the type `decayed-tuple<Args...>`.
2233
+ Then `args_variant_t` denotes the type
2234
+ `variant<monostate, as-tuple<LetSigs>...>` except with duplicate types
2235
+ removed.
2236
+
2237
+ Given a type `Tag` and a pack `Args`, let *`as-sndr2`* be an alias
2238
+ template such that `as-sndr2<Tag(Args...)>` denotes the type
2239
+ `call-result-t<F, decay_t<Args>&...>`. Then `ops2_variant_t` denotes the
2240
+ type
2241
+
2242
+ ``` cpp
2243
+ variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, env_t>>...>
2244
+ ```
2245
+
2246
+ except with duplicate types removed.
2247
+
2248
+ The *requires-clause* constraining the above lambda is satisfied if and
2249
+ only if the types `args_variant_t` and `ops2_variant_t` are well-formed.
2250
+
2251
+ The exposition-only function template *`let-bind`* has effects
2252
+ equivalent to:
2253
+
2254
+ ``` cpp
2255
+ using args_t = decayed-tuple<Args...>;
2256
+ auto mkop2 = [&] {
2257
+ return connect(
2258
+ apply(std::move(state.fn),
2259
+ state.args.template emplace<args_t>(std::forward<Args>(args)...)),
2260
+ receiver2{rcvr, std::move(state.env)});
2261
+ };
2262
+ start(state.ops2.template emplace<decltype(mkop2())>(emplace-from{mkop2}));
2263
+ ```
2264
+
2265
+ `\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{complete}`
2266
+
2267
+ is initialized with a callable object equivalent to the following:
2268
+
2269
+ ``` cpp
2270
+ []<class Tag, class... Args>
2271
+ (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
2272
+ if constexpr (same_as<Tag, decayed-typeof<set-cpo>>) {
2273
+ TRY-EVAL(rcvr, let-bind(state, rcvr, std::forward<Args>(args)...));
2274
+ } else {
2275
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2276
+ }
2277
+ }
2278
+ ```
2279
+
2280
+ Let `sndr` and `env` be subexpressions, and let `Sndr` be
2281
+ `decltype((sndr))`. If `sender-for<Sndr, decayed-typeof<let-cpo>>` is
2282
+ `false`, then the expression `let-cpo.transform_env(sndr, env)` is
2283
+ ill-formed. Otherwise, it is equal to:
2284
+
2285
+ ``` cpp
2286
+ auto& [_, _, child] = sndr;
2287
+ return JOIN-ENV(let-env(child), FWD-ENV(env));
2288
+ ```
2289
+
2290
+ Let the subexpression `out_sndr` denote the result of the invocation
2291
+ `let-cpo(sndr, f)` or an object equal to such, and let the subexpression
2292
+ `rcvr` denote a receiver such that the expression
2293
+ `connect(out_sndr, rcvr)` is well-formed. The expression
2294
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
2295
+ asynchronous operation [[exec.async.ops]] that, when started:
2296
+
2297
+ - invokes `f` when *`set-cpo`* is called with `sndr`’s result datums,
2298
+ - makes its completion dependent on the completion of a sender returned
2299
+ by `f`, and
2300
+ - propagates the other completion operations sent by `sndr`.
2301
+
2302
+ #### `execution::bulk`, `execution::bulk_chunked`, and `execution::bulk_unchunked` <a id="exec.bulk">[[exec.bulk]]</a>
2303
+
2304
+ `bulk`, `bulk_chunked`, and `bulk_unchunked` run a task repeatedly for
2305
+ every index in an index space.
2306
+
2307
+ The names `bulk`, `bulk_chunked`, and `bulk_unchunked` denote pipeable
2308
+ sender adaptor objects. Let `bulk-algo` be either `bulk`,
2309
+ `bulk_chunked`, or `bulk_unchunked`. For subexpressions `sndr`,
2310
+ `policy`, `shape`, and `f`, let `Policy` be
2311
+ `remove_cvref_t<decltype(policy)>`, `Shape` be `decltype(auto(shape))`,
2312
+ and `Func` be `decay_t<decltype((f))>`. If
2313
+
2314
+ - `decltype((sndr))` does not satisfy `sender`, or
2315
+ - `is_execution_policy_v<Policy>` is `false`, or
2316
+ - `Shape` does not satisfy `integral`, or
2317
+ - `Func` does not model `copy_constructible`,
2318
+
2319
+ `bulk-algo(sndr, policy, shape, f)` is ill-formed.
2320
+
2321
+ Otherwise, the expression `bulk-algo(sndr, policy, shape, f)` is
2322
+ expression-equivalent to:
2323
+
2324
+ ``` cpp
2325
+ transform_sender(get-domain-early(sndr), make-sender(
2326
+ bulk-algo, product-type<see below, Shape, Func>{policy, shape, f}, sndr))
2327
+ ```
2328
+
2329
+ except that `sndr` is evaluated only once. The first template argument
2330
+ of *`product-type`* is `Policy` if `Policy` models `copy_constructible`,
2331
+ and `const Policy&` otherwise.
2332
+
2333
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
2334
+ `decltype((sndr))`. If `sender-for<Sndr, bulk_t>` is `false`, then the
2335
+ expression `bulk.transform_sender(sndr, env)` is ill-formed; otherwise,
2336
+ it is equivalent to:
2337
+
2338
+ ``` cpp
2339
+ auto [_, data, child] = sndr;
2340
+ auto& [policy, shape, f] = data;
2341
+ auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)
2342
+ noexcept(noexcept(f(begin, vs...))) {
2343
+ while (begin != end) func(begin++, vs...);
2344
+ }
2345
+ return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
2346
+ ```
2347
+
2348
+ [*Note 1*: This causes the `bulk(sndr, policy, shape, f)` sender to be
2349
+ expressed in terms of `bulk_chunked(sndr, policy, shape, f)` when it is
2350
+ connected to a receiver whose execution domain does not customize
2351
+ `bulk`. — *end note*]
2352
+
2353
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2354
+ specialized for `bulk_chunked_t` as follows:
2355
+
2356
+ ``` cpp
2357
+ namespace std::execution {
2358
+ template<>
2359
+ struct impls-for<bulk_chunked_t> : default-impls {
2360
+ static constexpr auto complete = see below;
2361
+
2362
+ template<class Sndr, class... Env>
2363
+ static consteval void check-types();
2364
+ };
2365
+ }
2366
+ ```
2367
+
2368
+ The member `impls-for<bulk_chunked_t>::complete` is initialized with a
2369
+ callable object equivalent to the following lambda:
2370
+
2371
+ ``` cpp
2372
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
2373
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
2374
+ -> void requires see below {
2375
+ if constexpr (same_as<Tag, set_value_t>) {
2376
+ auto& [policy, shape, f] = state;
2377
+ constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...));
2378
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
2379
+ f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
2380
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2381
+ }());
2382
+ } else {
2383
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2384
+ }
2385
+ }
2386
+ ```
2387
+
2388
+ The expression in the *requires-clause* of the lambda above is `true` if
2389
+ and only if `Tag` denotes a type other than `set_value_t` or if the
2390
+ expression `f(auto(shape), auto(shape), args...)` is well-formed.
2391
+
2392
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2393
+ specialized for `bulk_unchunked_t` as follows:
2394
+
2395
+ ``` cpp
2396
+ namespace std::execution {
2397
+ template<>
2398
+ struct impls-for<bulk_unchunked_t> : default-impls {
2399
+ static constexpr auto complete = see below;
2400
+ };
2401
+ }
2402
+ ```
2403
+
2404
+ The member `impls-for<bulk_unchunked_t>::complete` is initialized with a
2405
+ callable object equivalent to the following lambda:
2406
+
2407
+ ``` cpp
2408
+ []<class Index, class State, class Rcvr, class Tag, class... Args>
2409
+ (Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept
2410
+ -> void requires see below {
2411
+ if constexpr (same_as<Tag, set_value_t>) {
2412
+ auto& [policy, shape, f] = state;
2413
+ constexpr bool nothrow = noexcept(f(auto(shape), args...));
2414
+ TRY-EVAL(rcvr, [&]() noexcept(nothrow) {
2415
+ for (decltype(auto(shape)) i = 0; i < shape; ++i) {
2416
+ f(auto(i), args...);
2417
+ }
2418
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2419
+ }());
2420
+ } else {
2421
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2422
+ }
2423
+ }
2424
+ ```
2425
+
2426
+ The expression in the *requires-clause* of the lambda above is `true` if
2427
+ and only if `Tag` denotes a type other than `set_value_t` or if the
2428
+ expression `f(auto(shape), args...)` is well-formed.
2429
+
2430
+ ``` cpp
2431
+ template<class Sndr, class... Env>
2432
+ static consteval void check-types();
2433
+ ```
2434
+
2435
+ *Effects:* Equivalent to:
2436
+
2437
+ ``` cpp
2438
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
2439
+ auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {
2440
+ if constexpr (!invocable<remove_cvref_t<data-type<Sndr>>, Ts&...>)
2441
+ throw unspecified-exception();
2442
+ };
2443
+ cs.for-each(overload-set(fn, [](auto){}));
2444
+ ```
2445
+
2446
+ where *`unspecified-exception`* is a type derived from `exception`.
2447
+
2448
+ Let the subexpression `out_sndr` denote the result of the invocation
2449
+ `bulk-algo(sndr, policy, shape, f)` or an object equal to such, and let
2450
+ the subexpression `rcvr` denote a receiver such that the expression
2451
+ `connect(out_sndr, rcvr)` is well-formed. The expression
2452
+ `connect(out_sndr, rcvr)` has undefined behavior unless it creates an
2453
+ asynchronous operation [[exec.async.ops]] that, when started:
2454
+
2455
+ - If `sndr` has a successful completion, where `args` is a pack of
2456
+ lvalue subexpressions referring to the value completion result datums
2457
+ of `sndr`, or decayed copies of those values if they model
2458
+ `copy_constructible`, then:
2459
+ - If `out_sndr` also completes successfully, then:
2460
+ - for `bulk`, invokes `f(i, args...)` for every i of type `Shape`
2461
+ from `0` to `shape`;
2462
+ - for `bulk_unchunked`, invokes `f(i, args...)` for every i of type
2463
+ `Shape` from `0` to `shape`; *Recommended practice:* The
2464
+ underlying scheduler should execute each iteration on a distinct
2465
+ execution agent.
2466
+ - for `bulk_chunked`, invokes `f(b, e, args...)` zero or more times
2467
+ with pairs of b and e of type `Shape` in range \[`0`, `shape`\],
2468
+ such that b < e and for every i of type `Shape` from `0` to
2469
+ `shape`, there is exactly one invocation with a pair b and e, such
2470
+ that i is in the range \[b, e).
2471
+ - If `out_sndr` completes with `set_error(rcvr, eptr)`, then the
2472
+ asynchronous operation may invoke a subset of the invocations of `f`
2473
+ before the error completion handler is called, and `eptr` is an
2474
+ `exception_ptr` containing either:
2475
+ - an exception thrown by an invocation of `f`, or
2476
+ - a `bad_alloc` exception if the implementation fails to allocate
2477
+ required resources, or
2478
+ - an exception derived from `runtime_error`.
2479
+ - If `out_sndr` completes with `set_stopped(rcvr)`, then the
2480
+ asynchronous operation may invoke a subset of the invocations of `f`
2481
+ before the stopped completion handler.
2482
+ - If `sndr` does not complete with `set_value`, then the completion is
2483
+ forwarded to `recv`.
2484
+ - For `bulk-algo`, the parameter `policy` describes the manner in which
2485
+ the execution of the asynchronous operations corresponding to these
2486
+ algorithms may be parallelized and the manner in which they apply `f`.
2487
+ Permissions and requirements on parallel algorithm element access
2488
+ functions [[algorithms.parallel.exec]] apply to `f`.
2489
+
2490
+ [*Note 2*: The asynchronous operation corresponding to
2491
+ `bulk-algo(sndr, policy, shape, f)` can complete with `set_stopped` if
2492
+ cancellation is requested or ignore cancellation
2493
+ requests. — *end note*]
2494
+
2495
+ #### `execution::when_all` <a id="exec.when.all">[[exec.when.all]]</a>
2496
+
2497
+ `when_all` and `when_all_with_variant` both adapt multiple input senders
2498
+ into a sender that completes when all input senders have completed.
2499
+ `when_all` only accepts senders with a single value completion signature
2500
+ and on success concatenates all the input senders’ value result datums
2501
+ into its own value completion operation.
2502
+ `when_all_with_variant(sndrs...)` is semantically equivalent to
2503
+ w`hen_all(into_variant(sndrs)...)`, where `sndrs` is a pack of
2504
+ subexpressions whose types model `sender`.
2505
+
2506
+ The names `when_all` and `when_all_with_variant` denote customization
2507
+ point objects. Let `sndrs` be a pack of subexpressions, let `Sndrs` be a
2508
+ pack of the types `decltype((sndrs))...`, and let `CD` be the type
2509
+ `common_type_t<decltype(get-domain-early(sndrs))...>`. Let `CD2` be `CD`
2510
+ if `CD` is well-formed, and `default_domain` otherwise. The expressions
2511
+ `when_all(sndrs...)` and `when_all_with_variant(sndrs...)` are
2512
+ ill-formed if any of the following is `true`:
2513
+
2514
+ - `sizeof...(sndrs)` is `0`, or
2515
+ - `(sender<Sndrs> && ...)` is `false`.
2516
+
2517
+ The expression `when_all(sndrs...)` is expression-equivalent to:
2518
+
2519
+ ``` cpp
2520
+ transform_sender(CD2(), make-sender(when_all, {}, sndrs...))
2521
+ ```
2522
+
2523
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2524
+ specialized for `when_all_t` as follows:
2525
+
2526
+ ``` cpp
2527
+ namespace std::execution {
2528
+ template<>
2529
+ struct impls-for<when_all_t> : default-impls {
2530
+ static constexpr auto get-attrs = see below;
2531
+ static constexpr auto get-env = see below;
2532
+ static constexpr auto get-state = see below;
2533
+ static constexpr auto start = see below;
2534
+ static constexpr auto complete = see below;
2535
+
2536
+ template<class Sndr, class... Env>
2537
+ static consteval void check-types();
2538
+ };
2539
+ }
2540
+ ```
2541
+
2542
+ Let *`make-when-all-env`* be the following exposition-only function
2543
+ template:
2544
+
2545
+ ``` cpp
2546
+ template<class Env>
2547
+ constexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only
2548
+ Env&& env) noexcept {
2549
+ return see below;
2550
+ }
2551
+ ```
2552
+
2553
+ Returns an object `e` such that
2554
+
2555
+ - `decltype(e)` models `queryable`, and
2556
+ - `e.query(get_stop_token)` is expression-equivalent to
2557
+ `state.stop-src.get_token()`, and
2558
+ - given a query object `q` with type other than cv `get_stop_token_t`
2559
+ and whose type satisfies *`forwarding-query`*, `e.query(q)` is
2560
+ expression-equivalent to `get_env(rcvr).query(q)`.
2561
+
2562
+ Let `when-all-env` be an alias template such that `when-all-env<Env>`
2563
+ denotes the type
2564
+ `decltype(make-{when-all-env}(declval<inplace_stop_source&>(), declval<Env>()))`.
2565
+
2566
+ ``` cpp
2567
+ template<class Sndr, class... Env>
2568
+ static consteval void check-types();
2569
+ ```
2570
+
2571
+ Let `Is` be the pack of integral template arguments of the
2572
+ `integer_sequence` specialization denoted by *`indices-for`*`<Sndr>`.
2573
+
2574
+ *Effects:* Equivalent to:
2575
+
2576
+ ``` cpp
2577
+ auto fn = []<class Child>() {
2578
+ auto cs = get_completion_signatures<Child, when-all-env<Env>...>();
2579
+ if constexpr (cs.count-of(set_value) >= 2)
2580
+ throw unspecified-exception();
2581
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
2582
+ };
2583
+ (fn.template operator()<child-type<Sndr, Is>>(), ...);
2584
+ ```
2585
+
2586
+ where *`unspecified-exception`* is a type derived from `exception`.
2587
+
2588
+ *Throws:* Any exception thrown as a result of evaluating the *Effects*,
2589
+ or an exception of an unspecified type derived from `exception` when
2590
+ `CD` is ill-formed.
2591
+
2592
+ The member `impls-for<when_all_t>::get-attrs` is initialized with a
2593
+ callable object equivalent to the following lambda expression:
2594
+
2595
+ ``` cpp
2596
+ [](auto&&, auto&&... child) noexcept {
2597
+ if constexpr (same_as<CD, default_domain>) {
2598
+ return env<>();
2599
+ } else {
2600
+ return MAKE-ENV(get_domain, CD());
2601
+ }
2602
+ }
2603
+ ```
2604
+
2605
+ The member `impls-for<when_all_t>::get-env` is initialized with a
2606
+ callable object equivalent to the following lambda expression:
2607
+
2608
+ ``` cpp
2609
+ []<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {
2610
+ return make-when-all-env(state.stop-src, get_env(rcvr));
2611
+ }
2612
+ ```
2613
+
2614
+ The member `impls-for<when_all_t>::get-state` is initialized with a
2615
+ callable object equivalent to the following lambda expression:
2616
+
2617
+ ``` cpp
2618
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {
2619
+ return e;
2620
+ }
2621
+ ```
2622
+
2623
+ where e is the expression
2624
+
2625
+ ``` cpp
2626
+ std::forward<Sndr>(sndr).apply(make-state<Rcvr>())
2627
+ ```
2628
+
2629
+ and where *`make-state`* is the following exposition-only class
2630
+ template:
2631
+
2632
+ ``` cpp
2633
+ enum class disposition { started, error, stopped }; // exposition only
2634
+
2635
+ template<class Rcvr>
2636
+ struct make-state {
2637
+ template<class... Sndrs>
2638
+ auto operator()(auto, auto, Sndrs&&... sndrs) const {
2639
+ using values_tuple = see below;
2640
+ using errors_variant = see below;
2641
+ using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t<Rcvr>>, on-stop-request>;
2642
+
2643
+ struct state-type {
2644
+ void arrive(Rcvr& rcvr) noexcept { // exposition only
2645
+ if (0 == --count) {
2646
+ complete(rcvr);
2647
+ }
2648
+ }
2649
+
2650
+ void complete(Rcvr& rcvr) noexcept; // exposition only
2651
+
2652
+ atomic<size_t> count{sizeof...(sndrs)}; // exposition only
2653
+ inplace_stop_source stop_src{}; // exposition only
2654
+ atomic<disposition> disp{disposition::started}; // exposition only
2655
+ errors_variant errors{}; // exposition only
2656
+ values_tuple values{}; // exposition only
2657
+ optional<stop_callback> on_stop{nullopt}; // exposition only
2658
+ };
2659
+
2660
+ return state-type{};
2661
+ }
2662
+ };
2663
+ ```
2664
+
2665
+ Let *`copy-fail`* be `exception_ptr` if decay-copying any of the child
2666
+ senders’ result datums can potentially throw; otherwise, `none-such`,
2667
+ where `none-such` is an unspecified empty class type.
2668
+
2669
+ The alias `values_tuple` denotes the type
2670
+
2671
+ ``` cpp
2672
+ tuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t<Rcvr>), decayed-tuple, optional>...>
2673
+ ```
2674
+
2675
+ if that type is well-formed; otherwise, `tuple<>`.
2676
+
2677
+ The alias `errors_variant` denotes the type
2678
+ `variant<none-such, copy-fail, Es...>` with duplicate types removed,
2679
+ where `Es` is the pack of the decayed types of all the child senders’
2680
+ possible error result datums.
2681
+
2682
+ The member `void state-type::complete(Rcvr& rcvr) noexcept` behaves as
2683
+ follows:
2684
+
2685
+ - If `disp` is equal to `disposition::started`, evaluates:
2686
+ ``` cpp
2687
+ auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };
2688
+ auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };
2689
+
2690
+ on_stop.reset();
2691
+ apply(
2692
+ [&](auto&... opts) noexcept {
2693
+ apply(set, tuple_cat(tie(*opts)...));
2694
+ },
2695
+ values);
2696
+ ```
2697
+ - Otherwise, if `disp` is equal to `disposition::error`, evaluates:
2698
+ ``` cpp
2699
+ on_stop.reset();
2700
+ visit(
2701
+ [&]<class Error>(Error& error) noexcept {
2702
+ if constexpr (!same_as<Error, none-such>) {
2703
+ set_error(std::move(rcvr), std::move(error));
2704
+ }
2705
+ },
2706
+ errors);
2707
+ ```
2708
+ - Otherwise, evaluates:
2709
+ ``` cpp
2710
+ on_stop.reset();
2711
+ set_stopped(std::move(rcvr));
2712
+ ```
2713
+
2714
+ The member `impls-for<when_all_t>::start` is initialized with a callable
2715
+ object equivalent to the following lambda expression:
2716
+
2717
+ ``` cpp
2718
+ []<class State, class Rcvr, class... Ops>(
2719
+ State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void {
2720
+ state.on_stop.emplace(
2721
+ get_stop_token(get_env(rcvr)),
2722
+ on-stop-request{state.stop_src});
2723
+ if (state.stop_src.stop_requested()) {
2724
+ state.on_stop.reset();
2725
+ set_stopped(std::move(rcvr));
2726
+ } else {
2727
+ (start(ops), ...);
2728
+ }
2729
+ }
2730
+ ```
2731
+
2732
+ The member `impls-for<when_all_t>::complete` is initialized with a
2733
+ callable object equivalent to the following lambda expression:
2734
+
2735
+ ``` cpp
2736
+ []<class Index, class State, class Rcvr, class Set, class... Args>(
2737
+ this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {
2738
+ if constexpr (same_as<Set, set_error_t>) {
2739
+ if (disposition::error != state.disp.exchange(disposition::error)) {
2740
+ state.stop_src.request_stop();
2741
+ TRY-EMPLACE-ERROR(state.errors, std::forward<Args>(args)...);
2742
+ }
2743
+ } else if constexpr (same_as<Set, set_stopped_t>) {
2744
+ auto expected = disposition::started;
2745
+ if (state.disp.compare_exchange_strong(expected, disposition::stopped)) {
2746
+ state.stop_src.request_stop();
2747
+ }
2748
+ } else if constexpr (!same_as<decltype(State::values), tuple<>>) {
2749
+ if (state.disp == disposition::started) {
2750
+ auto& opt = get<Index::value>(state.values);
2751
+ TRY-EMPLACE-VALUE(complete, opt, std::forward<Args>(args)...);
2752
+ }
2753
+ }
2754
+ state.arrive(rcvr);
2755
+ }
2756
+ ```
2757
+
2758
+ where `TRY-EMPLACE-ERROR(v, e)`, for subexpressions `v` and `e`, is
2759
+ equivalent to:
2760
+
2761
+ ``` cpp
2762
+ try {
2763
+ v.template emplace<decltype(auto(e))>(e);
2764
+ } catch (...) {
2765
+ v.template emplace<exception_ptr>(current_exception());
2766
+ }
2767
+ ```
2768
+
2769
+ if the expression `decltype(auto(e))(e)` is potentially throwing;
2770
+ otherwise, `v.template emplace<decltype(auto(e))>(e)`; and where
2771
+ `TRY-EMPLACE-VALUE(c, o, as...)`, for subexpressions `c`, `o`, and pack
2772
+ of subexpressions `as`, is equivalent to:
2773
+
2774
+ ``` cpp
2775
+ try {
2776
+ o.emplace(as...);
2777
+ } catch (...) {
2778
+ c(Index(), state, rcvr, set_error, current_exception());
2779
+ return;
2780
+ }
2781
+ ```
2782
+
2783
+ if the expression `decayed-tuple<decltype(as)...>{as...}` is potentially
2784
+ throwing; otherwise, `o.emplace(as...)`.
2785
+
2786
+ The expression `when_all_with_variant(sndrs...)` is
2787
+ expression-equivalent to:
2788
+
2789
+ ``` cpp
2790
+ transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));
2791
+ ```
2792
+
2793
+ Given subexpressions `sndr` and `env`, if
2794
+ `sender-for<decltype((sndr)), when_all_with_variant_t>` is `false`, then
2795
+ the expression `when_all_with_variant.transform_sender(sndr, env)` is
2796
+ ill-formed; otherwise, it is equivalent to:
2797
+
2798
+ ``` cpp
2799
+ auto&& [_, _, ...child] = sndr;
2800
+ return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);
2801
+ ```
2802
+
2803
+ [*Note 1*: This causes the `when_all_with_variant(sndrs...)` sender to
2804
+ become `when_all(into_variant(sndrs)...)` when it is connected with a
2805
+ receiver whose execution domain does not customize
2806
+ `when_all_with_variant`. — *end note*]
2807
+
2808
+ #### `execution::into_variant` <a id="exec.into.variant">[[exec.into.variant]]</a>
2809
+
2810
+ `into_variant` adapts a sender with multiple value completion signatures
2811
+ into a sender with just one value completion signature consisting of a
2812
+ `variant` of `tuple`s.
2813
+
2814
+ The name `into_variant` denotes a pipeable sender adaptor object. For a
2815
+ subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If `Sndr` does
2816
+ not satisfy `sender`, `into_variant(sndr)` is ill-formed.
2817
+
2818
+ Otherwise, the expression `into_variant(sndr)` is expression-equivalent
2819
+ to:
2820
+
2821
+ ``` cpp
2822
+ transform_sender(get-domain-early(sndr), make-sender(into_variant, {}, sndr))
2823
+ ```
2824
+
2825
+ except that `sndr` is only evaluated once.
2826
+
2827
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2828
+ specialized for `into_variant` as follows:
2829
+
2830
+ ``` cpp
2831
+ namespace std::execution {
2832
+ template<>
2833
+ struct impls-for<into_variant_t> : default-impls {
2834
+ static constexpr auto get-state = see below;
2835
+ static constexpr auto complete = see below;
2836
+
2837
+ template<class Sndr, class... Env>
2838
+ static consteval void check-types() {
2839
+ auto cs = get_completion_signatures<child-type<Sndr>, FWD-ENV-T(Env)...>();
2840
+ decay-copyable-result-datums(cs); // see [exec.snd.expos]
2841
+ }
2842
+ };
2843
+ }
2844
+ ```
2845
+
2846
+ The member `impls-for<into_variant_t>::get-state` is initialized with a
2847
+ callable object equivalent to the following lambda:
2848
+
2849
+ ``` cpp
2850
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept
2851
+ -> type_identity<value_types_of_t<child-type<Sndr>, FWD-ENV-T(env_of_t<Rcvr>)>> {
2852
+ return {};
2853
+ }
2854
+ ```
2855
+
2856
+ The member `impls-for<into_variant_t>::complete` is initialized with a
2857
+ callable object equivalent to the following lambda:
2858
+
2859
+ ``` cpp
2860
+ []<class State, class Rcvr, class Tag, class... Args>(
2861
+ auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {
2862
+ if constexpr (same_as<Tag, set_value_t>) {
2863
+ using variant_type = State::type;
2864
+ TRY-SET-VALUE(rcvr, variant_type(decayed-tuple<Args...>{std::forward<Args>(args)...}));
2865
+ } else {
2866
+ Tag()(std::move(rcvr), std::forward<Args>(args)...);
2867
+ }
2868
+ }
2869
+ ```
2870
+
2871
+ #### `execution::stopped_as_optional` <a id="exec.stopped.opt">[[exec.stopped.opt]]</a>
2872
+
2873
+ `stopped_as_optional` maps a sender’s stopped completion operation into
2874
+ a value completion operation as a disengaged `optional`. The sender’s
2875
+ value completion operation is also converted into an `optional`. The
2876
+ result is a sender that never completes with stopped, reporting
2877
+ cancellation by completing with a disengaged `optional`.
2878
+
2879
+ The name `stopped_as_optional` denotes a pipeable sender adaptor object.
2880
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
2881
+ expression `stopped_as_optional(sndr)` is expression-equivalent to:
2882
+
2883
+ ``` cpp
2884
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_optional, {}, sndr))
2885
+ ```
2886
+
2887
+ except that `sndr` is only evaluated once.
2888
+
2889
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
2890
+ specialized for `stopped_as_optional_t` as follows:
2891
+
2892
+ ``` cpp
2893
+ namespace std::execution {
2894
+ template<>
2895
+ struct impls-for<stopped_as_optional_t> : default-impls {
2896
+ template<class Sndr, class... Env>
2897
+ static consteval void check-types() {
2898
+ default-impls::check-types<Sndr, Env...>();
2899
+ if constexpr (!requires {
2900
+ requires (!same_as<void, single-sender-value-type<child-type<Sndr>,
2901
+ FWD-ENV-T(Env)...>>); })
2902
+ throw unspecified-exception();
2903
+ }
2904
+ };
2905
+ }
2906
+ ```
2907
+
2908
+ where `unspecified-exception` is a type derived from `exception`.
2909
+
2910
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
2911
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
2912
+ `sender-for<Sndr, stopped_as_optional_t>` is `false` then the expression
2913
+ `stopped_as_optional.transform_sender(sndr, env)` is ill-formed;
2914
+ otherwise, if `sender_in<child-type<Sndr>, FWD-ENV-T(Env)>` is `false`,
2915
+ the expression `stopped_as_optional.transform_sender(sndr, env)` is
2916
+ equivalent to `not-a-sender()`; otherwise, it is equivalent to:
2917
+
2918
+ ``` cpp
2919
+ auto&& [_, _, child] = sndr;
2920
+ using V = single-sender-value-type<child-type<Sndr>, FWD-ENV-T(Env)>;
2921
+ return let_stopped(
2922
+ then(std::forward_like<Sndr>(child),
2923
+ []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {
2924
+ return optional<V>(in_place, std::forward<Ts>(ts)...);
2925
+ }),
2926
+ []() noexcept { return just(optional<V>()); });
2927
+ ```
2928
+
2929
+ #### `execution::stopped_as_error` <a id="exec.stopped.err">[[exec.stopped.err]]</a>
2930
+
2931
+ `stopped_as_error` maps an input sender’s stopped completion operation
2932
+ into an error completion operation as a custom error type. The result is
2933
+ a sender that never completes with stopped, reporting cancellation by
2934
+ completing with an error.
2935
+
2936
+ The name `stopped_as_error` denotes a pipeable sender adaptor object.
2937
+ For some subexpressions `sndr` and `err`, let `Sndr` be
2938
+ `decltype((sndr))` and let `Err` be `decltype((err))`. If the type
2939
+ `Sndr` does not satisfy `sender` or if the type `Err` does not satisfy
2940
+ `movable-value`, `stopped_as_error(sndr, err)` is ill-formed. Otherwise,
2941
+ the expression `stopped_as_error(sndr, err)` is expression-equivalent
2942
+ to:
2943
+
2944
+ ``` cpp
2945
+ transform_sender(get-domain-early(sndr), make-sender(stopped_as_error, err, sndr))
2946
+ ```
2947
+
2948
+ except that `sndr` is only evaluated once.
2949
+
2950
+ Let `sndr` and `env` be subexpressions such that `Sndr` is
2951
+ `decltype((sndr))` and `Env` is `decltype((env))`. If
2952
+ `sender-for<Sndr, stopped_as_error_t>` is `false`, then the expression
2953
+ `stopped_as_error.transform_sender(sndr, env)` is ill-formed; otherwise,
2954
+ it is equivalent to:
2955
+
2956
+ ``` cpp
2957
+ auto&& [_, err, child] = sndr;
2958
+ using E = decltype(auto(err));
2959
+ return let_stopped(
2960
+ std::forward_like<Sndr>(child),
2961
+ [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {
2962
+ return just_error(std::move(err));
2963
+ });
2964
+ ```
2965
+
2966
+ #### `execution::associate` <a id="exec.associate">[[exec.associate]]</a>
2967
+
2968
+ `associate` tries to associate a sender with an async scope such that
2969
+ the scope can track the lifetime of any asynchronous operations created
2970
+ with the sender.
2971
+
2972
+ Let *`associate-data`* be the following exposition-only class template:
2973
+
2974
+ ``` cpp
2975
+ namespace std::execution {
2976
+ template<scope_token Token, sender Sender>
2977
+ struct associate-data { // exposition only
2978
+ using wrap-sender = // exposition only
2979
+ remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>;
2980
+
2981
+ explicit associate-data(Token t, Sender&& s)
2982
+ : sndr(t.wrap(std::forward<Sender>(s))),
2983
+ token(t) {
2984
+ if (!token.try_associate())
2985
+ sndr.reset();
2986
+ }
2987
+
2988
+ associate-data(const associate-data& other)
2989
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
2990
+ noexcept(other.token.try_associate()));
2991
+
2992
+ associate-data(associate-data&& other)
2993
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
2994
+
2995
+ ~associate-data();
2996
+
2997
+ optional<pair<Token, wrap-sender>>
2998
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
2999
+
3000
+ private:
3001
+ optional<wrap-sender> sndr; // exposition only
3002
+ Token token; // exposition only
3003
+ };
3004
+
3005
+ template<scope_token Token, sender Sender>
3006
+ associate-data(Token, Sender&&) -> associate-data<Token, Sender>;
3007
+ }
3008
+ ```
3009
+
3010
+ For an *`associate-data`* object `a`, `a.sndr.has_value()` is `true` if
3011
+ and only if an association was successfully made and is owned by `a`.
3012
+
3013
+ ``` cpp
3014
+ associate-data(const associate-data& other)
3015
+ noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
3016
+ noexcept(other.token.try_associate()));
3017
+ ```
3018
+
3019
+ *Constraints:* `copy_constructible<`*`wrap-sender`*`>` is `true`.
3020
+
3021
+ *Effects:* Value-initializes *sndr* and initializes *token* with
3022
+ `other.`*`token`*. If `other.`*`sndr`*`.has_value()` is `false`, no
3023
+ further effects; otherwise, calls *`token`*`.try_associate()` and, if
3024
+ that returns `true`, calls *`sndr`*`.emplace(*other.`*`sndr`*`)` and, if
3025
+ that exits with an exception, calls *`token`*`.disassociate()` before
3026
+ propagating the exception.
3027
+
3028
+ ``` cpp
3029
+ associate-data(associate-data&& other)
3030
+ noexcept(is_nothrow_move_constructible_v<wrap-sender>);
3031
+ ```
3032
+
3033
+ *Effects:* Initializes *sndr* with `std::move(other.`*`sndr`*`)` and
3034
+ initializes *token* with `std::move(other.`*`token`*`)` and then calls
3035
+ `other.`*`sndr`*`.reset()`.
3036
+
3037
+ ``` cpp
3038
+ ~associate-data();
3039
+ ```
3040
+
3041
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then no effect;
3042
+ otherwise, invokes *`sndr`*`.reset()` before invoking
3043
+ *`token`*`.disassociate()`.
3044
+
3045
+ ``` cpp
3046
+ optional<pair<Token, wrap-sender>>
3047
+ release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
3048
+ ```
3049
+
3050
+ *Effects:* If *`sndr`*`.has_value()` returns `false` then returns an
3051
+ `optional` that does not contain a value; otherwise returns an
3052
+ `optional` containing a value of type `pair<Token, `*`wrap-sender`*`>`
3053
+ as if by:
3054
+
3055
+ ``` cpp
3056
+ return optional(pair(token, std::move(*sndr)));
3057
+ ```
3058
+
3059
+ *Ensures:* *sndr* does not contain a value.
3060
+
3061
+ The name `associate` denotes a pipeable sender adaptor object. For
3062
+ subexpressions `sndr` and `token`:
3063
+
3064
+ - If `decltype((sndr))` does not satisfy `sender`, or
3065
+ `remove_cvref_t<decltype((token))>` does not satisfy `scope_token`,
3066
+ then `associate(sndr, token)` is ill-formed.
3067
+ - Otherwise, the expression `associate(sndr, token)` is
3068
+ expression-equivalent to:
3069
+ ``` cpp
3070
+ transform_sender(get-domain-early(sndr),
3071
+ make-sender(associate, associate-data(token, sndr)))
3072
+ ```
3073
+
3074
+ except that `sndr` is evaluated only once.
3075
+
3076
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3077
+ specialized for `associate_t` as follows:
3078
+
3079
+ ``` cpp
3080
+ namespace std::execution {
3081
+ template<>
3082
+ struct impls-for<associate_t> : default-impls {
3083
+ static constexpr auto get-state = see below; // exposition only
3084
+ static constexpr auto start = see below; // exposition only
3085
+
3086
+ template<class Sndr, class... Env>
3087
+ static consteval void check-types() { // exposition only
3088
+ using associate_data_t = remove_cvref_t<data-type<Sndr>>;
3089
+ using child_type_t = associate_data_t::wrap-sender;
3090
+ (void)get_completion_signatures<child_type_t, FWD-ENV-T(Env)...>();
3091
+ }
3092
+ };
3093
+ }
3094
+ ```
3095
+
3096
+ The member `impls-for<associate_t>::get-state` is initialized with a
3097
+ callable object equivalent to the following lambda:
3098
+
3099
+ ``` cpp
3100
+ []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below) {
3101
+ auto [_, data] = std::forward<Sndr>(sndr);
3102
+ auto dataParts = std::move(data).release();
3103
+
3104
+ using scope_tkn = decltype(dataParts->first);
3105
+ using wrap_sender = decltype(dataParts->second);
3106
+ using op_t = connect_result_t<wrap_sender, Rcvr>;
3107
+
3108
+ struct op_state {
3109
+ bool associated = false; // exposition only
3110
+ union {
3111
+ Rcvr* rcvr; // exposition only
3112
+ struct {
3113
+ scope_tkn token; // exposition only
3114
+ op_t op; // exposition only
3115
+ } assoc; // exposition only
3116
+ };
3117
+
3118
+ explicit op_state(Rcvr& r) noexcept
3119
+ : rcvr(addressof(r)) {}
3120
+
3121
+ explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try
3122
+ : associated(true),
3123
+ assoc(tkn, connect(std::move(sndr), std::move(r))) {
3124
+ }
3125
+ catch (...) {
3126
+ tkn.disassociate();
3127
+ throw;
3128
+ }
3129
+
3130
+ op_state(op_state&&) = delete;
3131
+
3132
+ ~op_state() {
3133
+ if (associated) {
3134
+ assoc.op.~op_t();
3135
+ assoc.token.disassociate();
3136
+ assoc.token.~scope_tkn();
3137
+ }
3138
+ }
3139
+
3140
+ void run() noexcept { // exposition only
3141
+ if (associated)
3142
+ start(assoc.op);
3143
+ else
3144
+ set_stopped(std::move(*rcvr));
3145
+ }
3146
+ };
3147
+
3148
+ if (dataParts)
3149
+ return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr};
3150
+ else
3151
+ return op_state{rcvr};
3152
+ }
3153
+ ```
3154
+
3155
+ The expression in the `noexcept` clause of
3156
+ `impls-for<associate_t>::get-state` is
3157
+
3158
+ ``` cpp
3159
+ is_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> &&
3160
+ is_nothrow_move_constructible_v<wrap-sender> &&
3161
+ nothrow-callable<connect_t, wrap-sender, Rcvr>
3162
+ ```
3163
+
3164
+ where *`wrap-sender`* is the type
3165
+ `remove_cvref_t<data-type<Sndr>>::wrap-sender`.
3166
+
3167
+ The member `impls-for<associate_t>::start` is initialized with a
3168
+ callable object equivalent to the following lambda:
3169
+
3170
+ ``` cpp
3171
+ [](auto& state, auto&) noexcept -> void {
3172
+ state.run();
3173
+ }
3174
+ ```
3175
+
3176
+ The evaluation of `associate(sndr, token)` may cause side effects
3177
+ observable via `token`'s associated async scope object.
3178
+
3179
+ #### Exposition-only `execution::stop-when` <a id="exec.stop.when">[[exec.stop.when]]</a>
3180
+
3181
+ *`stop-when`* fuses an additional stop token `t` into a sender so that,
3182
+ upon connecting to a receiver `r`, the resulting operation state
3183
+ receives stop requests from both `t` and the token returned from
3184
+ `get_stop_token(get_env(r))`.
3185
+
3186
+ The name *`stop-when`* denotes an exposition-only sender adaptor. For
3187
+ subexpressions `sndr` and `token`:
3188
+
3189
+ - If `decltype((sndr))` does not satisfy `sender`, or
3190
+ `remove_cvref_t<decltype((token))>` does not satisfy
3191
+ `stoppable_token`, then `stop-when(sndr, token)` is ill-formed.
3192
+ - Otherwise, if `remove_cvref_t<decltype((token))>` models
3193
+ `unstoppable_token` then `stop-when({}sndr, token)` is
3194
+ expression-equivalent to `sndr`.
3195
+ - Otherwise, `stop-when(sndr, token)` returns a sender `osndr`. If
3196
+ `osndr` is connected to a receiver `r`, let `rtoken` be the result of
3197
+ `get_stop_token(get_env(r))`.
3198
+ - If the type of `rtoken` models `unstoppable_token` then the effects
3199
+ of connecting `osndr` to `r` are equivalent to
3200
+ `connect(write_env(sndr, prop(get_stop_token, token)), r)`.
3201
+ - Otherwise, the effects of connecting `osndr` to `r` are equivalent
3202
+ to `connect(write_env(sndr, prop(get_stop_token, stoken)), r)` where
3203
+ `stoken` is an object of an exposition-only type *`stoken-t`* such
3204
+ that:
3205
+ - *`stoken-t`* models `stoppable_token`;
3206
+ - `stoken.stop_requested()` returns
3207
+ `token.stop_requested() || rtoken.stop_reques-{}ted()`;
3208
+ - `stoken.stop_possible()` returns
3209
+ `token.stop_possible() || rtoken.stop_possible()`; and
3210
+ - for types `Fn` and `Init` such that both `invocable<Fn>` and
3211
+ `constructible_from<Fn, Init>` are modeled,
3212
+ `stoken-t::callback_type<Fn>` models
3213
+ `stoppable-callback-for<Fn, stoken-t, Init>`.\begin{tailnote}
3214
+ For an object \texttt{fn} of type \texttt{Fn}
3215
+ constructed from a value, \texttt{init}, of type \texttt{Init},
3216
+ registering \texttt{fn} using
3217
+ \texttt{\exposid{stoken-t}::callback_type\<Fn\>(stoken, init)}
3218
+ results in an invocation of \texttt{fn} when
3219
+ a callback registered with \texttt{token} or \texttt{rtoken} would be invoked.
3220
+ \texttt{fn} is invoked at most once.
3221
+ \end{tailnote}
3222
+
3223
+ #### `execution::spawn_future` <a id="exec.spawn.future">[[exec.spawn.future]]</a>
3224
+
3225
+ `spawn_future` attempts to associate the given input sender with the
3226
+ given token’s async scope and, on success, eagerly starts the input
3227
+ sender; the return value is a sender that, when connected and started,
3228
+ completes with either the result of the eagerly-started input sender or
3229
+ with `set_stopped` if the input sender was not started.
3230
+
3231
+ The name `spawn_future` denotes a customization point object. For
3232
+ subexpressions `sndr`, `token`, and `env`,
3233
+
3234
+ - let `Sndr` be `decltype((sndr))`,
3235
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
3236
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
3237
+
3238
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
3239
+ not satisfied, the expression `spawn_future(sndr, token, env)` is
3240
+ ill-formed.
3241
+
3242
+ Let *`spawn-future-state-base`* be the exposition-only class template:
3243
+
3244
+ ``` cpp
3245
+ namespace std::execution {
3246
+ template<class Completions>
3247
+ struct spawn-future-state-base; // exposition only
3248
+
3249
+ template<class... Sigs>
3250
+ struct spawn-future-state-base<completion_signatures<Sigs...>> { // exposition only
3251
+ using variant-t = see below; // exposition only
3252
+ variant-t result; // exposition only
3253
+ virtual void complete() noexcept = 0; // exposition only
3254
+ };
3255
+ }
3256
+ ```
3257
+
3258
+ Let `Sigs` be the pack of arguments to the `completion_signatures`
3259
+ specialization provided as a parameter to the
3260
+ *`spawn-future-state-base`* class template. Let *`as-tuple`* be an alias
3261
+ template that transforms a completion signature `Tag(Args...)` into the
3262
+ tuple specialization `decayed-tuple<Tag, Args...>`.
3263
+
3264
+ - If `is_nothrow_constructible_v<decay_t<Arg>, Arg>` is `true` for every
3265
+ type `Arg` in every parameter pack `Args` in every completion
3266
+ signature `Tag(Args...)` in `Sigs` then *`variant-t`* denotes the type
3267
+ `variant<monostate, tuple<set_stopped_t>, as-tuple<Sigs>...>`, except
3268
+ with duplicate types removed.
3269
+ - Otherwise *`variant-t`* denotes the type
3270
+ `variant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, as-tuple<Sigs>...>`,
3271
+ except with duplicate types removed.
3272
+
3273
+ Let *`spawn-future-receiver`* be the exposition-only class template:
3274
+
3275
+ ``` cpp
3276
+ namespace std::execution {
3277
+ template<class Completions>
3278
+ struct spawn-future-receiver { // exposition only
3279
+ using receiver_concept = receiver_t;
3280
+
3281
+ spawn-future-state-base<Completions>* state; // exposition only
3282
+
3283
+ template<class... T>
3284
+ void set_value(T&&... t) && noexcept {
3285
+ set-complete<set_value_t>(std::forward<T>(t)...);
3286
+ }
3287
+
3288
+ template<class E>
3289
+ void set_error(E&& e) && noexcept {
3290
+ set-complete<set_error_t>(std::forward<E>(e));
3291
+ }
3292
+
3293
+ void set_stopped() && noexcept {
3294
+ set-complete<set_stopped_t>();
3295
+ }
3296
+
3297
+ private:
3298
+ template<class CPO, class... T>
3299
+ void set-complete(T&&... t) noexcept { // exposition only
3300
+ constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...);
3301
+ try {
3302
+ state->result.template emplace<decayed-tuple<CPO, T...>>(CPO{},
3303
+ std::forward<T>(t)...);
3304
+ }
3305
+ catch (...) {
3306
+ if constexpr (!nothrow) {
3307
+ using tuple_t = decayed-tuple<set_error_t, exception_ptr>;
3308
+ state->result.template emplace<tuple_t>(set_error_t{}, current_exception());
3309
+ }
3310
+ }
3311
+ state->complete();
3312
+ }
3313
+ };
3314
+ }
3315
+ ```
3316
+
3317
+ Let `ssource-t` be an unspecified type that models `stoppable-source`
3318
+ and let `ssource` be an lvalue of type `ssource-t`. Let `stoken-t` be
3319
+ `decltype(ssource.get_token())`. Let *`future-spawned-sender`* be the
3320
+ alias template:
3321
+
3322
+ ``` cpp
3323
+ template<sender Sender, class Env>
3324
+ using future-spawned-sender = // exposition only
3325
+ decltype(write_env(stop-when(declval<Sender>(), declval<stoken-t>()), declval<Env>()));
3326
+ ```
3327
+
3328
+ Let *`spawn-future-state`* be the exposition-only class template:
3329
+
3330
+ ``` cpp
3331
+ namespace std::execution {
3332
+ template<class Alloc, scope_token Token, sender Sender, class Env>
3333
+ struct spawn-future-state // exposition only
3334
+ : spawn-future-state-base<completion_signatures_of_t<future-spawned-sender<Sender, Env>>> {
3335
+ using sigs-t = // exposition only
3336
+ completion_signatures_of_t<future-spawned-sender<Sender, Env>>;
3337
+ using receiver-t = // exposition only
3338
+ spawn-future-receiver<sigs-t>;
3339
+ using op-t = // exposition only
3340
+ connect_result_t<future-spawned-sender<Sender, Env>, receiver-t>;
3341
+
3342
+ spawn-future-state(Alloc alloc, Sender&& sndr, Token token, Env env) // exposition only
3343
+ : alloc(std::move(alloc)),
3344
+ op(connect(
3345
+ write_env(stop-when(std::forward<Sender>(sndr), ssource.get_token()), std::move(env)),
3346
+ receiver-t(this))),
3347
+ token(std::move(token)),
3348
+ associated(token.try_associate()) {
3349
+ if (associated)
3350
+ start(op);
3351
+ else
3352
+ set_stopped(receiver-t(this));
3353
+ }
3354
+
3355
+ void complete() noexcept override; // exposition only
3356
+ void consume(receiver auto& rcvr) noexcept; // exposition only
3357
+ void abandon() noexcept; // exposition only
3358
+
3359
+ private:
3360
+ using alloc-t = // exposition only
3361
+ allocator_traits<Alloc>::template rebind_alloc<spawn-future-state>;
3362
+
3363
+ alloc-t alloc; // exposition only
3364
+ ssource-t ssource; // exposition only
3365
+ op-t op; // exposition only
3366
+ Token token; // exposition only
3367
+ bool associated; // exposition only
3368
+
3369
+ void destroy() noexcept; // exposition only
3370
+ };
3371
+ }
3372
+ ```
3373
+
3374
+ For purposes of determining the existence of a data race, *`complete`*,
3375
+ *`consume`*, and *`abandon`* behave as atomic operations
3376
+ [[intro.multithread]]. These operations on a single object of a type
3377
+ that is a specialization of *`spawn-future-state`* appear to occur in a
3378
+ single total order.
3379
+
3380
+ ``` cpp
3381
+ void complete() noexcept;
3382
+ ```
3383
+
3384
+ *Effects:*
3385
+
3386
+ - No effects if this invocation of *complete* happens before an
3387
+ invocation of *consume* or *abandon* on `*this`;
3388
+ - otherwise, if an invocation of *consume* on `*this` happens before
3389
+ this invocation of *complete* then there is a receiver, `rcvr`,
3390
+ registered and that receiver is completed as if by
3391
+ *`consume`*`(rcvr)`;
3392
+ - otherwise, *destroy* is invoked.
3393
+
3394
+ ``` cpp
3395
+ void consume(receiver auto& rcvr) noexcept;
3396
+ ```
3397
+
3398
+ *Effects:*
3399
+
3400
+ - If this invocation of *consume* happens before an invocation of
3401
+ *complete* on `*this` then `rcvr` is registered to be completed when
3402
+ *complete* is subsequently invoked on `*this`;
3403
+ - otherwise, `rcvr` is completed as if by:
3404
+ ``` cpp
3405
+ std::move(this->result).visit(
3406
+ [&rcvr](auto&& tuple) noexcept {
3407
+ if constexpr (!same_as<remove_reference_t<decltype(tuple)>, monostate>) {
3408
+ apply([&rcvr](auto cpo, auto&&... vals) {
3409
+ cpo(std::move(rcvr), std::move(vals)...);
3410
+ }, std::move(tuple));
3411
+ }
3412
+ });
3413
+ ```
3414
+
3415
+ ``` cpp
3416
+ void abandon() noexcept;
3417
+ ```
3418
+
3419
+ *Effects:*
3420
+
3421
+ - If this invocation of *abandon* happens before an invocation of
3422
+ *complete* on `*this` then equivalent to:
3423
+ ``` cpp
3424
+ ssource.request_stop();
3425
+ ```
3426
+ - otherwise, *destroy* is invoked.
3427
+
3428
+ ``` cpp
3429
+ void destroy() noexcept;
3430
+ ```
3431
+
3432
+ *Effects:* Equivalent to:
3433
+
3434
+ ``` cpp
3435
+ auto token = std::move(this->token);
3436
+ bool associated = this->associated;
3437
+
3438
+ {
3439
+ auto alloc = std::move(this->alloc);
3440
+
3441
+ allocator_traits<alloc-t>::destroy(alloc, this);
3442
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
3443
+ }
3444
+
3445
+ if (associated)
3446
+ token.disassociate();
3447
+ ```
3448
+
3449
+ The exposition-only class template *`impls-for`* [[exec.snd.expos]] is
3450
+ specialized for `spawn_future_t` as follows:
3451
+
3452
+ ``` cpp
3453
+ namespace std::execution {
3454
+ template<>
3455
+ struct impls-for<spawn_future_t> : default-impls {
3456
+ static constexpr auto start = see below; // exposition only
3457
+ };
3458
+ }
3459
+ ```
3460
+
3461
+ The member `impls-for<spawn_future_t>::start` is initialized with a
3462
+ callable object equivalent to the following lambda:
3463
+
3464
+ ``` cpp
3465
+ [](auto& state, auto& rcvr) noexcept -> void {
3466
+ state->consume(rcvr);
3467
+ }
3468
+ ```
3469
+
3470
+ For the expression `spawn_future(sndr, token, env)` let `new_sender` be
3471
+ the expression `token.wrap(sndr)` and let `alloc` and `senv` be defined
3472
+ as follows:
3473
+
3474
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
3475
+ the result of `get_allocator(env)` and `senv` is the expression `env`;
3476
+ - otherwise, if the expression `get_allocator(get_env(new_sender))` is
3477
+ well-formed, then `alloc` is the result of
3478
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
3479
+ `JOIN-ENV(prop(get_allocator, alloc), env)`;
3480
+ - otherwise, `alloc` is `allocator<void>()` and `senv` is the expression
3481
+ `env`.
3482
+
3483
+ The expression `spawn_future(sndr, token, env)` has the following
3484
+ effects:
3485
+
3486
+ - Uses `alloc` to allocate and construct an object `s` of a type that is
3487
+ a specialization of *`spawn-future-{}state`* from `alloc`,
3488
+ `token.wrap(sndr)`, `token`, and `senv`. If an exception is thrown
3489
+ then any constructed objects are destroyed and any allocated memory is
3490
+ deallocated.
3491
+ - Constructs an object `u` of a type that is a specialization of
3492
+ `unique_ptr` such that:
3493
+ - `u.get()` is equal to the address of `s`, and
3494
+ - `u.get_deleter()(u.release())` is equivalent to
3495
+ `u.release()->abandon()`.
3496
+ - Returns `make-sender(spawn_future, std::move(u))`.
3497
+
3498
+ The expression `spawn_future(sndr, token)` is expression-equivalent to
3499
+ `spawn_future(sndr, token, execution::env<>())`.
3500
+
3501
+ ### Sender consumers <a id="exec.consumers">[[exec.consumers]]</a>
3502
+
3503
+ #### `this_thread::sync_wait` <a id="exec.sync.wait">[[exec.sync.wait]]</a>
3504
+
3505
+ `this_thread::sync_wait` and `this_thread::sync_wait_with_variant` are
3506
+ used to block the current thread of execution until the specified sender
3507
+ completes and to return its async result. `sync_wait` mandates that the
3508
+ input sender has exactly one value completion signature.
3509
+
3510
+ Let *`sync-wait-env`* be the following exposition-only class type:
3511
+
3512
+ ``` cpp
3513
+ namespace std::this_thread {
3514
+ struct sync-wait-env {
3515
+ execution::run_loop* loop; // exposition only
3516
+
3517
+ auto query(execution::get_scheduler_t) const noexcept {
3518
+ return loop->get_scheduler();
3519
+ }
3520
+
3521
+ auto query(execution::get_delegation_scheduler_t) const noexcept {
3522
+ return loop->get_scheduler();
3523
+ }
3524
+ };
3525
+ }
3526
+ ```
3527
+
3528
+ Let *`sync-wait-result-type`* and *`sync-wait-with-variant-result-type`*
3529
+ be exposition-only alias templates defined as follows:
3530
+
3531
+ ``` cpp
3532
+ namespace std::this_thread {
3533
+ template<execution::sender_in<sync-wait-env> Sndr>
3534
+ using sync-wait-result-type =
3535
+ optional<execution::value_types_of_t<Sndr, sync-wait-env, decayed-tuple,
3536
+ type_identity_t>>;
3537
+
3538
+ template<execution::sender_in<sync-wait-env> Sndr>
3539
+ using sync-wait-with-variant-result-type =
3540
+ optional<execution::value_types_of_t<Sndr, sync-wait-env>>;
3541
+ }
3542
+ ```
3543
+
3544
+ The name `this_thread::sync_wait` denotes a customization point object.
3545
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. The
3546
+ expression `this_thread::sync_wait(sndr)` is expression-equivalent to
3547
+ the following, except that `sndr` is evaluated only once:
3548
+
3549
+ ``` cpp
3550
+ apply_sender(get-domain-early(sndr), sync_wait, sndr)
3551
+ ```
3552
+
3553
+ *Mandates:*
3554
+
3555
+ - `sender_in<Sndr, sync-wait-env>` is `true`.
3556
+ - The type `sync-wait-result-type<Sndr>` is well-formed.
3557
+ - `same_as<decltype(e), sync-wait-result-type<Sndr>>` is `true`, where e
3558
+ is the `apply_sender` expression above.
3559
+
3560
+ Let *`sync-wait-state`* and *`sync-wait-receiver`* be the following
3561
+ exposition-only class templates:
3562
+
3563
+ ``` cpp
3564
+ namespace std::this_thread {
3565
+ template<class Sndr>
3566
+ struct sync-wait-state { // exposition only
3567
+ execution::run_loop loop; // exposition only
3568
+ exception_ptr error; // exposition only
3569
+ sync-wait-result-type<Sndr> result; // exposition only
3570
+ };
3571
+
3572
+ template<class Sndr>
3573
+ struct sync-wait-receiver { // exposition only
3574
+ using receiver_concept = execution::receiver_t;
3575
+ sync-wait-state<Sndr>* state; // exposition only
3576
+
3577
+ template<class... Args>
3578
+ void set_value(Args&&... args) && noexcept;
3579
+
3580
+ template<class Error>
3581
+ void set_error(Error&& err) && noexcept;
3582
+
3583
+ void set_stopped() && noexcept;
3584
+
3585
+ sync-wait-env get_env() const noexcept { return {&state->loop}; }
3586
+ };
3587
+ }
3588
+ ```
3589
+
3590
+ ``` cpp
3591
+ template<class... Args>
3592
+ void set_value(Args&&... args) && noexcept;
3593
+ ```
3594
+
3595
+ *Effects:* Equivalent to:
3596
+
3597
+ ``` cpp
3598
+ try {
3599
+ state->result.emplace(std::forward<Args>(args)...);
3600
+ } catch (...) {
3601
+ state->error = current_exception();
3602
+ }
3603
+ state->loop.finish();
3604
+ ```
3605
+
3606
+ ``` cpp
3607
+ template<class Error>
3608
+ void set_error(Error&& err) && noexcept;
3609
+ ```
3610
+
3611
+ *Effects:* Equivalent to:
3612
+
3613
+ ``` cpp
3614
+ state->error = AS-EXCEPT-PTR(std::forward<Error>(err)); // see [exec.general]
3615
+ state->loop.finish();
3616
+ ```
3617
+
3618
+ ``` cpp
3619
+ void set_stopped() && noexcept;
3620
+ ```
3621
+
3622
+ *Effects:* Equivalent to *`state`*`->`*`loop`*`.finish()`.
3623
+
3624
+ For a subexpression `sndr`, let `Sndr` be `decltype((sndr))`. If
3625
+ `sender_to<Sndr, sync-wait-receiver<Sndr>>` is `false`, the expression
3626
+ `sync_wait.apply_sender(sndr)` is ill-formed; otherwise, it is
3627
+ equivalent to:
3628
+
3629
+ ``` cpp
3630
+ sync-wait-state<Sndr> state;
3631
+ auto op = connect(sndr, sync-wait-receiver<Sndr>{&state});
3632
+ start(op);
3633
+
3634
+ state.loop.run();
3635
+ if (state.error) {
3636
+ rethrow_exception(std::move(state.error));
3637
+ }
3638
+ return std::move(state.result);
3639
+ ```
3640
+
3641
+ The behavior of `this_thread::sync_wait(sndr)` is undefined unless:
3642
+
3643
+ - It blocks the current thread of execution [[defns.block]] with forward
3644
+ progress guarantee delegation [[intro.progress]] until the specified
3645
+ sender completes. \[*Note 1*: The default implementation of
3646
+ `sync_wait` achieves forward progress guarantee delegation by
3647
+ providing a `run_loop` scheduler via the `get_delegation_scheduler`
3648
+ query on the *`sync-wait-receiver`*’s environment. The `run_loop` is
3649
+ driven by the current thread of execution. — *end note*]
3650
+ - It returns the specified sender’s async results as follows:
3651
+ - For a value completion, the result datums are returned in a `tuple`
3652
+ in an engaged `optional` object.
3653
+ - For an error completion, an exception is thrown.
3654
+ - For a stopped completion, a disengaged `optional` object is
3655
+ returned.
3656
+
3657
+ #### `this_thread::sync_wait_with_variant` <a id="exec.sync.wait.var">[[exec.sync.wait.var]]</a>
3658
+
3659
+ The name `this_thread::sync_wait_with_variant` denotes a customization
3660
+ point object. For a subexpression `sndr`, let `Sndr` be
3661
+ `decltype(into_variant(sndr))`. The expression
3662
+ `this_thread::sync_wait_with_variant(sndr)` is expression-equivalent to
3663
+ the following, except `sndr` is evaluated only once:
3664
+
3665
+ ``` cpp
3666
+ apply_sender(get-domain-early(sndr), sync_wait_with_variant, sndr)
3667
+ ```
3668
+
3669
+ *Mandates:*
3670
+
3671
+ - `sender_in<Sndr, sync-wait-env>` is `true`.
3672
+ - The type `sync-wait-with-variant-result-type<Sndr>` is well-formed.
3673
+ - `same_as<decltype(e), sync-wait-with-variant-result-type<Sndr>>` is
3674
+ `true`, where e is the `apply_sender` expression above.
3675
+
3676
+ The expression `sync_wait_with_variant.apply_sender(sndr)` is equivalent
3677
+ to:
3678
+
3679
+ ``` cpp
3680
+ using result_type = sync-wait-with-variant-result-type<Sndr>;
3681
+ if (auto opt_value = sync_wait(into_variant(sndr))) {
3682
+ return result_type(std::move(get<0>(*opt_value)));
3683
+ }
3684
+ return result_type(nullopt);
3685
+ ```
3686
+
3687
+ The behavior of `this_thread::sync_wait_with_variant(sndr)` is undefined
3688
+ unless:
3689
+
3690
+ - It blocks the current thread of execution [[defns.block]] with forward
3691
+ progress guarantee delegation [[intro.progress]] until the specified
3692
+ sender completes. \[*Note 1*: The default implementation of
3693
+ `sync_wait_with_variant` achieves forward progress guarantee
3694
+ delegation by relying on the forward progress guarantee delegation
3695
+ provided by `sync_wait`. — *end note*]
3696
+ - It returns the specified sender’s async results as follows:
3697
+ - For a value completion, the result datums are returned in an engaged
3698
+ `optional` object that contains a `variant` of `tuple`s.
3699
+ - For an error completion, an exception is thrown.
3700
+ - For a stopped completion, a disengaged `optional` object is
3701
+ returned.
3702
+
3703
+ #### `execution::spawn` <a id="exec.spawn">[[exec.spawn]]</a>
3704
+
3705
+ `spawn` attempts to associate the given input sender with the given
3706
+ token’s async scope and, on success, eagerly starts the input sender.
3707
+
3708
+ The name `spawn` denotes a customization point object. For
3709
+ subexpressions `sndr`, `token`, and `env`,
3710
+
3711
+ - let `Sndr` be `decltype((sndr))`,
3712
+ - let `Token` be `remove_cvref_t<decltype((token))>`, and
3713
+ - let `Env` be `remove_cvref_t<decltype((env))>`.
3714
+
3715
+ If any of `sender<Sndr>`, `scope_token<Token>`, or `queryable<Env>` are
3716
+ not satisfied, the expression `spawn({}sndr, token, env)` is ill-formed.
3717
+
3718
+ Let *`spawn-state-base`* be the exposition-only class:
3719
+
3720
+ ``` cpp
3721
+ namespace std::execution {
3722
+ struct spawn-state-base { // exposition only
3723
+ virtual void complete() noexcept = 0; // exposition only
3724
+ };
3725
+ }
3726
+ ```
3727
+
3728
+ Let *`spawn-receiver`* be the exposition-only class:
3729
+
3730
+ ``` cpp
3731
+ namespace std::execution {
3732
+ struct spawn-receiver { // exposition only
3733
+ using receiver_concept = receiver_t;
3734
+
3735
+ spawn-state-base* state; // exposition only
3736
+ void set_value() && noexcept { state->complete(); }
3737
+ void set_stopped() && noexcept { state->complete(); }
3738
+ };
3739
+ }
3740
+ ```
3741
+
3742
+ Let *`spawn-state`* be the exposition-only class template:
3743
+
3744
+ ``` cpp
3745
+ namespace std::execution {
3746
+ template<class Alloc, scope_token Token, sender Sender>
3747
+ struct spawn-state : spawn-state-base { // exposition only
3748
+ using op-t = connect_result_t<Sender, spawn-receiver>; // exposition only
3749
+
3750
+ spawn-state(Alloc alloc, Sender&& sndr, Token token); // exposition only
3751
+ void complete() noexcept override; // exposition only
3752
+ void run(); // exposition only
3753
+
3754
+ private:
3755
+ using alloc-t = // exposition only
3756
+ allocator_traits<Alloc>::template rebind_alloc<spawn-state>;
3757
+
3758
+ alloc-t alloc; // exposition only
3759
+ op-t op; // exposition only
3760
+ Token token; // exposition only
3761
+
3762
+ void destroy() noexcept; // exposition only
3763
+ };
3764
+ }
3765
+ ```
3766
+
3767
+ ``` cpp
3768
+ spawn-state(Alloc alloc, Sender&& sndr, Token token);
3769
+ ```
3770
+
3771
+ *Effects:* Initializes *alloc* with `alloc`, *token* with `token`, and
3772
+ *op* with:
3773
+
3774
+ ``` cpp
3775
+ connect(std::move(sndr), spawn-receiver(this))
3776
+ ```
3777
+
3778
+ ``` cpp
3779
+ void run();
3780
+ ```
3781
+
3782
+ *Effects:* Equivalent to:
3783
+
3784
+ ``` cpp
3785
+ if (token.try_associate())
3786
+ start(op);
3787
+ else
3788
+ destroy();
3789
+ ```
3790
+
3791
+ ``` cpp
3792
+ void complete() noexcept override;
3793
+ ```
3794
+
3795
+ *Effects:* Equivalent to:
3796
+
3797
+ ``` cpp
3798
+ auto token = std::move(this->token);
3799
+
3800
+ destroy();
3801
+ token.disassociate();
3802
+ ```
3803
+
3804
+ ``` cpp
3805
+ void destroy() noexcept;
3806
+ ```
3807
+
3808
+ *Effects:* Equivalent to:
3809
+
3810
+ ``` cpp
3811
+ auto alloc = std::move(this->alloc);
3812
+
3813
+ allocator_traits<alloc-t>::destroy(alloc, this);
3814
+ allocator_traits<alloc-t>::deallocate(alloc, this, 1);
3815
+ ```
3816
+
3817
+ For the expression `spawn(sndr, token, env)` let `new_sender` be the
3818
+ expression `token.wrap(sndr)` and let `alloc` and `senv` be defined as
3819
+ follows:
3820
+
3821
+ - if the expression `get_allocator(env)` is well-formed, then `alloc` is
3822
+ the result of `get_allocator(env)` and `senv` is the expression `env`,
3823
+ - otherwise if the expression `get_allocator(get_env(new_sender))` is
3824
+ well-formed, then `alloc` is the result of
3825
+ `get_allocator(get_env(new_sender))` and `senv` is the expression
3826
+ `JOIN-ENV(prop(get_allocator, alloc), env)`,
3827
+ - otherwise `alloc` is `allocator<void>()` and `senv` is the expression
3828
+ `env`.
3829
+
3830
+ The expression `spawn(sndr, token, env)` is of type `void` and has the
3831
+ following effects:
3832
+
3833
+ - Uses `alloc` to allocate and construct an object `o` of type that is a
3834
+ specialization of `spawn-state` from `alloc`,
3835
+ `write_env(token.wrap(sndr), senv)`, and `token` and then invokes
3836
+ `o.run()`. If an exception is thrown then any constructed objects are
3837
+ destroyed and any allocated memory is deallocated.
3838
+
3839
+ The expression `spawn(sndr, token)` is expression-equivalent to
3840
+ `spawn(sndr, token, execution::env<>({}))`.
3841
+