Skip to content
Merged
Next Next commit
change specification of sender_traits and connect to make awaitables …
…senders
  • Loading branch information
ericniebler committed Aug 24, 2021
commit da8d5c8a42f6ad10549f4137f37b7f27df56b1f8
82 changes: 75 additions & 7 deletions std_execution.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2053,7 +2053,15 @@ enum class forward_progress_guarantee {

2. The class template `sender_traits` is used to query a sender type for facts associated with the signal it sends.

3. The primary class template `sender_traits<S>` is defined as if inheriting from an implementation-defined class template <code><i>sender-traits-base</i>&lt;S></code> defined as follows:
3. The primary class template `sender_traits<S>` also recognizes awaitables as typed senders. For this clause ([execution]):

- An <i>awaitable</i> is an expression that would be well-formed as the operand of a `co_await` expression within a coroutine that does not define an `await_transform` member in its promise type.

- For any type `T`, <code><i>is-awaitable</i>&lt;T></code> is `true` if an expression of that type is an awaitable as described above; otherwise, `false`.

- For an awaitable `a` such that `decltype((a))` is type `A`, <code><i>await-result-type</i>&lt;A></code> is an alias for <code>decltype(<i>e</i>)</code>, where <code><i>e</i></code> is `a`'s <i>await-resume</i> expression ([expr.await]).

4. The primary class template `sender_traits<S>` is defined as if inheriting from an implementation-defined class template <code><i>sender-traits-base</i>&lt;S></code> defined as follows:

1. If <code><i>has-sender-types</i>&lt;S></code> is `true`, then <code><i>sender-traits-base</i>&lt;S></code> is equivalent to:

Expand All @@ -2077,7 +2085,39 @@ enum class forward_progress_guarantee {
struct <i>sender-traits-base</i> {};
</pre>

3. Otherwise, <code><i>sender-traits-base</i>&lt;S></code> is equivalent to
3. Otherwise, if <code><i>is-awaitable</i>&lt;S></code> is `true`, then

- If <code><i>await-result-type</i>&lt;S></code> is <code><i>cv</i> void</code> then <code><i>sender-traits-base&lt;S></i></code> is equivalent to

<pre highlight=c++>
template&lt;class S>
struct <i>sender-traits-base</i> {
template&lt;template&lt;class...> class Tuple, template&lt;class...> class Variant>
using value_types = Variant&lt;Tuple&lt;>>;

template&lt;template&lt;class...> class Variant>
using error_types = Variant&lt;exception_ptr>;

static constexpr bool sends_done = false;
};
</pre>

- Otherwise, <code><i>sender-traits-base&lt;S></i></code> is equivalent to

<pre highlight=c++>
template&lt;class S>
struct <i>sender-traits-base</i> {
template&lt;template&lt;class...> class Tuple, template&lt;class...> class Variant>
using value_types = Variant&lt;Tuple&lt;<i>await-result-type</i>&lt;S>>;

template&lt;template&lt;class...> class Variant>
using error_types = Variant&lt;exception_ptr>;

static constexpr bool sends_done = false;
};
</pre>

4. Otherwise, <code><i>sender-traits-base</i>&lt;S></code> is equivalent to

<pre highlight=c++>
template&lt;class S>
Expand All @@ -2086,16 +2126,16 @@ enum class forward_progress_guarantee {
};
</pre>

4. If `sender_traits<S>::value_types<Tuple, Variant>` for some sender type `S` is well formed, it shall be a type `Variant<Tuple<Args0..., Args1..., ..., ArgsN...>>`, where the type packs `Args0` through `ArgsN` are the packs of types the sender `S` passes as
5. If `sender_traits<S>::value_types<Tuple, Variant>` for some sender type `S` is well formed, it shall be a type `Variant<Tuple<Args0..., Args1..., ..., ArgsN...>>`, where the type packs `Args0` through `ArgsN` are the packs of types the sender `S` passes as
arguments to `execution::set_value` after a receiver object. If such sender `S` invokes `execution::set_value(r, args...)` for some receiver `r`, where `decltype(args)` is not one of the type packs `Args0` through `ArgsN`, the program is ill-formed with no
diagnostic required.

5. If `sender_traits<S>::error_types<Variant>` for some sender type `S` is well formed, it shall be a type `Variant<E0, E1, ..., EN>`, where the types `E0` through `EN` are the types the sender `S` passes as arguments to `execution::set_error` after a receiver
6. If `sender_traits<S>::error_types<Variant>` for some sender type `S` is well formed, it shall be a type `Variant<E0, E1, ..., EN>`, where the types `E0` through `EN` are the types the sender `S` passes as arguments to `execution::set_error` after a receiver
object. If such sender `S` invokes `execution::set_error(r, e)` for some receiver `r`, where `decltype(e)` is not one of the types `E0` through `EN`, the program is ill-formed with no diagnostic required.

6. If `sender_traits<S>::sends_done` is well formed and `false`, and such sender `S` invokes `execution::set_done(r)` for some receiver `r`, the program is ill-formed with no diagnostic required.
7. If `sender_traits<S>::sends_done` is well formed and `false`, and such sender `S` invokes `execution::set_done(r)` for some receiver `r`, the program is ill-formed with no diagnostic required.

7. Users may specialize `sender_traits` on program-defined types.
8. Users may specialize `sender_traits` on program-defined types.

### `execution::connect` <b>[execution.senders.connect]</b> ### {#spec-execution.senders.connect}

Expand All @@ -2108,7 +2148,35 @@ enum class forward_progress_guarantee {
1. `tag_invoke(execution::connect, s, r)`, if that expression is valid and its type satisfies `execution::operation_state`. If the function selected by `tag_invoke` does not return an operation state for which `execution::start` starts work described by `s`, the program
is ill-formed with no diagnostic required.

2. Otherwise, `execution::connect(s, r)` is ill-formed.
2. Otherwise, <code><i>connect-awaitable</i>(s, r)</code> if <code><i>is-awaitable</i>&lt;S></code> is `true`, where <code><i>connect-awaitable</i></code> is a coroutine equivalent to the following:

<pre highlight="c++">
<i>operation-state-task</i> <i>connect-awaitable</i>(S&& s, R&& r) noexcept {
exception_ptr e;
try {
<i>set-value-expr</i>
} catch(...) {
e = current_exception();
}
<i>set-error-expr</i>
}
</pre>

where <code><i>connect-awaitable</i></code> starts suspended and:

- <i>set-value-expr</i> first evaluates `co_await (S&&) s`, then suspends the coroutine and evaluates `execution::set_value((R&&) r)` if <code><i>await-result-type</i>&lt;S></code> is <code><i>cv</i> void</code>; otherwise, it evaluates `auto&& res = co_await (S&&) s`, then suspends the coroutine and evaluates `execution::set_value((R&&) r, (decltype(res)) res)`.

[<i>Note</i>: If the call to `execution::set_value` exits normally, then the <code><i>connect-awaitable</i></code> coroutine is never resumed. --<i>end note</i>]

- <i>set-error-expr</i> first suspends the coroutine and then executes `execution::set_error((R&&) r, std::move(e))`.

[<i>Note</i>: The <code><i>connect-awaitable</i></code> coroutine is never resumed after the call to `execution::set_error`. --<i>end note</i>]

- <code><i>operation-state-task</i></code> is a type that models `operation_state`. Its `execution::start` resumes the <code><i>connect-awaitable</i></code> coroutine, advancing it past the initial suspend point.

- Let `p` be an lvalue reference to the promise of the <code><i>connect-awaitable</i></code> coroutine and let `c` be any `tag_invoke`-based customization point object. Then `c(p, as...)` is expression-equivalent to `c(r, as...)` for any set of arguments `as...`.

3. Otherwise, `execution::connect(s, r)` is ill-formed.

3. Standard sender types shall always expose an rvalue-qualified overload of a customization of `execution::connect`. Standard sender types shall only expose an lvalue-qualified overload of a customization of `execution::connect` if they are copyable.

Expand Down