From c6ebf4f863902e8f6ac6819656070b249136cb7a Mon Sep 17 00:00:00 2001 From: ericniebler Date: Wed, 26 Jun 2024 06:58:55 +0000 Subject: [PATCH] Publish: Merge pull request #249 from cplusplus/LWG-feedback-2024-06-25 feedback from LWG wording review, 2024-06-25 119a1a22e02b80098396054b13e8f757ce5ab79c --- execution.html | 521 ++++++++++++++++++++++++++----------------------- 1 file changed, 279 insertions(+), 242 deletions(-) diff --git a/execution.html b/execution.html index b64aeca..5d23ded 100644 --- a/execution.html +++ b/execution.html @@ -8067,7 +8067,7 @@

simple-allocator* ([allocator.requirements.general]).

+ satisfies simple-allocator ([allocator.requirements.general]).

  • forwarding_query(get_allocator) is a core constant expression and has value true.

    @@ -8281,7 +8281,7 @@

    Let Sch be the type of a scheduler and let Env be the type of an execution environment for which sender_in<schedule_result_t<Sch>, Env> is -satisfied. Then sender-of-in<schedule_result_t<Sch>, Env> shall be modeled.

    +satisfied. Then sender-in-of<schedule_result_t<Sch>, Env> shall be modeled.

  • None of a scheduler’s copy constructor, destructor, equality comparison, or swap member functions shall exit via an exception. None of these member functions, nor a scheduler type’s schedule function, shall introduce data @@ -8436,15 +8436,15 @@

    env, FWD-ENV(env) is an -expression whose type satisfies *queryable* such that for a query object q and a pack of subexpressions as, the expression FWD-ENV(env).query(q, as...) is ill-formed if forwarding_query(q) is false; otherwise, it is expression-equivalent +expression whose type satisfies queryable such that for a query object q and a pack of subexpressions as, the expression FWD-ENV(env).query(q, as...) is ill-formed if forwarding_query(q) is false; otherwise, it is expression-equivalent to env.query(q, as...).

  • -

    For a query object q and a subexpression v, MAKE-ENV(q, v) is an expression env whose type satisfies *queryable* such +

    For a query object q and a subexpression v, MAKE-ENV(q, v) is an expression env whose type satisfies queryable such that the result of env.query(q) has a value equal to v ([concepts.equality]). Unless otherwise stated, the object to which env.query(q) refers remains valid while env remains valid.

  • For two queryable objects env1 and env2, a query object q and a pack of subexpressions as, JOIN-ENV(env1, env2) is -an expression env3 whose type satisfies *queryable* such that env3.query(q, as...) is expression-equivalent to:

    +an expression env3 whose type satisfies queryable such that env3.query(q, as...) is expression-equivalent to:

    • env1.query(q, as...) if that expression is well-formed,

      @@ -8460,9 +8460,9 @@

      sch, SCHED-ATTRS(sch) is an -expression o1 whose type satisfied *queryable* such that o1.query(get_completion_scheduler<Tag>) is a +expression o1 whose type satisfies queryable such that o1.query(get_completion_scheduler<Tag>) is a expression with the same type and value as sch where Tag is -one of set_value_t or set_stopped_t, and such that o1.query(get_domain) is expression-equivalent to sch.query(get_domain). SCHED-ENV(sch) is an expression o2 whose type satisfied *queryable* such that o1.query(get_scheduler) is a prvalue with the same type and +one of set_value_t or set_stopped_t, and such that o1.query(get_domain) is expression-equivalent to sch.query(get_domain). SCHED-ENV(sch) is an expression o2 whose type satisfies queryable such that o1.query(get_scheduler) is a prvalue with the same type and value as sch, and such that o2.query(get_domain) is expression-equivalent to sch.query(get_domain).

    • @@ -8512,7 +8512,7 @@

      return Domain(); where Domain is the decayed type of the first of the +

      Effects: Equivalent to: return Domain(); where Domain is the decayed type of the first of the following expressions that is well-formed:

      • @@ -8544,14 +8544,14 @@

        schedule_from ([exec.schedule.from])) to give scheduler authors a way to customize both how to transition onto (transfer) and off of (schedule_from) a given execution -context. Thus, transfer must ignore the domain of the -predecessor and use the domain of the destination scheduler to -select a customization, a property that is unique to transfer. -That is why it is given special treatment here.

        +context. Thus, transfer ignores the domain of the predecessor +and uses the domain of the destination scheduler to select a +customization, a property that is unique to transfer. That is +why it is given special treatment here.

      • -

        Otherwise, return Domain(); where Domain is +

        Otherwise, return Domain(); where Domain is the first of the following expressions that is well-formed and -its type is not void:

        +whose type is not void:

        • get_domain(get_env(sndr))

          @@ -8595,23 +8595,52 @@

          template<class... T> +
          template<class T0, class T1, ... class Tn>
           struct product-type {  // exposition only
          -  using type0 = T0;      // exposition only
          -  using type1 = T1;      // exposition only
          -    ...
          -  using typen-1 = Tn-1;   // exposition only
          -
             T0 t0;      // exposition only
             T1 t1;      // exposition only
               ...
          -  Tn-1 tn-1;   // exposition only
          +  Tn tn;      // exposition only
          +
          +  template<size_t I, class Self>
          +  constexpr decltype(auto) get(this Self&& self) noexcept; // exposition only
          +
          +  template<class Self, class Fn>
          +  constexpr decltype(auto) apply(this Self&& self, Fn&& fn) // exposition only
          +    noexcept(see below);
           };
           
          1. An expression of type product-type is usable as the initializer of a structured binding declaration [dcl.struct.bind].

            +
          2. +
            template<size_t I, class Self>
            +constexpr decltype(auto) get(this Self&& self) noexcept;
            +
            +
              +
            1. +

              Effects: Equivalent to:

              +
              auto& [...ts] = self;
              +return std::forward_like<Self>(ts...[I]);
              +
              +
            +
          3. +
            template<class Self, class Fn>
            +constexpr decltype(auto) apply(this Self&& self, Fn&& fn) noexcept(see below);
            +
            +
              +
            1. +

              Effects: Equivalent to:

              +
              auto& [...ts] = self;
              +return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
              +
              +
            2. +

              Requires: The expression in the return statement above is +well-formed.

              +
            3. +

              Remarks: The expression in the noexcept clause is true if the return statement above is not potentially throwing; otherwise, false.

              +
        • template<class Tag, class Data = see below, class... Child>
          @@ -8629,8 +8658,8 @@ 

          (sender<Child> &&...)

      • -

        Returns: A prvalue of type basic-sender<Tag, decay_t<Data>, decay_t<Child>...> where the tag member has been default-initialized and the data and childn... members have been direct -initialized from their respective forwarded arguments, where basic-sender is the following exposition-only +

        Returns: A prvalue of type basic-sender<Tag, decay_t<Data>, decay_t<Child>...> that has been +direct-list-initialized with the forwarded arguments, where basic-sender is the following exposition-only class template except as noted below:

        namespace std::execution {
           template<class Tag>
        @@ -8638,7 +8667,7 @@ 

        Data template parameter -denotes an unspecified empty trivial class type.

        +denotes an unspecified empty trivially copyable class type that models semiregular.

      • -

        It is unspecified whether instances of basic-sender can be -aggregate initialized.

        +

        It is unspecified whether a specialization of basic-sender is +an aggregate.

      • -

        An expression of type basic-sender is usable as the initializer of a -structured binding declaration [dcl.struct.bind].

        +

        An expression of type basic-sender is usable as the initializer of a +structured binding declaration [dcl.struct.bind].

      • The expression in the noexcept clause of the constructor of basic-state is:

        is_nothrow_move_constructible_v<Rcvr> &&
         nothrow-callable<decltype(impls-for<tag_of_t<Sndr>>::get-state), Sndr, Rcvr&>
         
      • -

        The object connect-all is initialized with the following -lambda:

        +

        The object connect-all is initialized with a callable object +equivalent to the following lambda:

        []<class Sndr, class Rcvr, size_t... Is>(
        -  basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(noexcept(E))
        -    -> decltype(E) {
        -    return E;
        +  basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
        +    -> decltype(auto) {
        +    auto& [_, data, ...child] = sndr;
        +    return product-type{connect(
        +      std::forward_like<Sndr>(child),
        +      basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
           }
         
        -

        where E is the following expression:

        -
        product-type{connect(
        -  std::forward<Sndr>(sndr).childIs,
        -  basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...}
        -
        +
          +
        1. +

          Requires: The expression in the return statement is well-formed.

          +
        2. +

          Remarks: The expression in the noexcept clause is true if + the return statement is not potentially throwing; otherwise, false.

          +
      • The expression in the noexcept clause of the constructor of basic-operation is:

        -
        is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
        +
        is_nothrow_constructible_v<basic-state<Self, Rcvr>, Self, Rcvr> &&
         noexcept(connect-all(this, std::forward<Sndr>(sndr), indices-for<Sndr>()))
         
      • The expression in the noexcept clause of the connect member function of basic-sender is:

        -
        is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
        +
        is_nothrow_constructible_v<basic-operation<Self, Rcvr>, Self, Rcvr>
         
      • The member default-impls::get-attrs is initialized with a callable object equivalent to the following lambda:

        -
        [](const auto& data, const auto&... child) noexcept -> decltype(auto) {
        +
        [](const auto&, const auto&... child) noexcept -> decltype(auto) {
           if constexpr (sizeof...(child) == 1)
             return (FWD-ENV(get_env(child)), ...);
           else
        @@ -8822,7 +8847,7 @@ 

        default-impls::get-env is initialized with a callable object equivalent to the following lambda:

        -
        []<class Rcvr>(auto index, auto& state, const Rcvr& rcvr) noexcept -> decltype(auto) {
        +
        [](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {
           return FWD-ENV(get_env(rcvr));
         }
         
        @@ -8830,13 +8855,14 @@

        default-impls::get-state is initialized with a callable object equivalent to the following lambda:

        []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {
        -  return (std::forward<Sndr>(sndr).data);
        +  auto& [_, data, ...child] = sndr;
        +  return std::forward_like<Sndr>(data);
         }
         
      • The member default-impls::start is initialized with a callable object equivalent to the following lambda:

        -
        [](auto& state, auto& rcvr, auto&... ops) noexcept -> void {
        +
        [](auto&, auto&, auto&... ops) noexcept -> void {
           (execution::start(ops), ...);
         }
         
        @@ -8889,12 +8915,12 @@

        template<class Sndr> concept sender = - bool(enable-sender<remove_cvref_t<Sndr>>) && // atomic constraint + bool(enable-sender<remove_cvref_t<Sndr>>) && // atomic constraint ([temp.constr.atomic]) requires (const remove_cvref_t<Sndr>& sndr) { { get_env(sndr) } -> queryable; } && - move_constructible<remove_cvref_t<Sndr>> && // rvalues are movable, and - constructible_from<remove_cvref_t<Sndr>, Sndr>; // lvalues are copyable + move_constructible<remove_cvref_t<Sndr>> && // senders are movable and + constructible_from<remove_cvref_t<Sndr>, Sndr>; // decay copyable template<class Sndr, class Env = empty_env> concept sender_in = @@ -8915,31 +8941,30 @@

        }

      • -

        Given a subexpression sndr, let Sndr be decltype((sndr)), let Env be -the type of an environment, and let rcvr be a receiver with an associated -environment Env. A completion operation is a permissible completion for Sndr and Env if its +

        Given a subexpression sndr, let Sndr be decltype((sndr)) and let rcvr be a receiver with an associated environment whose type is Env. +A completion operation is a permissible completion for Sndr and Env if its completion signature appears in the argument list of the specialization of completion_signatures denoted by completion_signatures_of_t<Sndr, Env>. Sndr and Env model sender_in<Sndr, Env> if all the completion operations that are potentially evaluated by connecting sndr to rcvr and starting the resulting operation state are permissible completions for Sndr and Env.

      • -

        A type Sigs satisfies and models the exposition-only concept valid-completion-signatures if it denotes a specialization +

        A type models the exposition-only concept valid-completion-signatures if it denotes a specialization of the completion_signatures class template.

      • -

        The exposition-only concepts sender-of and sender-of-in define the requirements for a sender +

        The exposition-only concepts sender-of and sender-in-of define the requirements for a sender type that completes with a given unique set of value result types.

        namespace std::execution {
           template<class... As>
             using value-signature = set_value_t(As...); // exposition only
         
           template<class Sndr, class Env, class... Values>
        -    concept sender-of-in =
        +    concept sender-in-of =
               sender_in<Sndr, Env> &&
               MATCHING-SIG( // see [exec.general]
                 set_value_t(Values...),
                 value_types_of_t<Sndr, Env, value-signature, type_identity_t>);
         
           template<class Sndr, class... Values>
        -    concept sender-of = sender-of-in<Sndr, empty_env, Values...>;
        +    concept sender-of = sender-in-of<Sndr, empty_env, Values...>;
         }
         
      • @@ -8976,22 +9001,19 @@

        Only expose an overload of a member connect that accepts an lvalue sender if they model copy_constructible.

        -
      • -

        Model copy_constructible if they satisfy copy_constructible.

      34.9.3. Awaitable helpers [exec.awaitables]

      1. -

        The sender concepts recognize awaitables as senders. For this clause -([exec]), an awaitable is an expression that would be +

        The sender concepts recognize awaitables as senders. For [exec], an awaitable is an expression that would be well-formed as the operand of a co_await expression within a given context.

      2. For a subexpression c, let GET-AWAITER(c, p) be expression-equivalent to the series of transformations and conversions applied to c as the operand of an await-expression in a coroutine, -resulting in lvalue e as described by [expr.await]/3.2-4, where p is an lvalue referring to the coroutine’s promise type, Promise. This includes the invocation of the promise type’s await_transform member if any, the invocation of the operator co_await picked by overload resolution if any, and any necessary implicit +resulting in lvalue e as described by [expr.await], where p is an lvalue referring to the coroutine’s promise, which has type Promise. This includes the invocation of the promise type’s await_transform member if any, the invocation of the operator co_await picked by overload resolution if any, and any necessary implicit conversions and materializations.

        I have opened cwg#250 to give these transformations a term-of-art so we can more easily refer to it here.

        @@ -9000,7 +9022,7 @@

        namespace std {
           template<class T>
        -  concept await-suspend-result = see below;
        +  concept await-suspend-result = see below; // exposition only
         
           template<class A, class Promise>
           concept is-awaiter = // exposition only
        @@ -9047,9 +9069,8 @@ 

        } template<has-as-awaitable<Derived> T> - auto await_transform(T&& value) - noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>()))) - -> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) { + decltype(auto) await_transform(T&& value) + noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>()))) { return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this)); } }; @@ -9187,27 +9208,26 @@

        get_completion_signatures is a customization point object. Let sndr be an expression such that decltype((sndr)) is Sndr, and let env be an -expression such that decltype((env)) is Env. Then get_completion_signatures(sndr, env) is expression-equivalent to:

        +expression such that decltype((env)) is Env. Then get_completion_signatures(sndr, env) is expression-equivalent to (void(sndr), void(env), CS()) except that void(sndr) and void(env) are indeterminately sequenced, where CS is:

        1. -

          decltype(sndr.get_completion_signatures(env)){} if that -expression is well-formed,

          +

          decltype(sndr.get_completion_signatures(env)) if that +type is well-formed,

        2. -

          Otherwise, remove_cvref_t<Sndr>::completion_signatures{} if that expression is well-formed,

          +

          Otherwise, remove_cvref_t<Sndr>::completion_signatures if that type is well-formed,

        3. Otherwise, if is-awaitable<Sndr, env-promise<Env>> is true, then:

          completion_signatures<
             SET-VALUE-SIG(await-result-type<Sndr,
                           env-promise<Env>>), // see [exec.snd.concepts]
             set_error_t(exception_ptr),
          -  set_stopped_t()>{}
          +  set_stopped_t()>
           
        4. -

          Otherwise, get_completion_signatures(sndr, env) is ill-formed.

          +

          Otherwise, CS is ill-formed.

      3. -

        Let rcvr be an rvalue receiver of type Rcvr, and let Sndr be the type of a -sender such that sender_in<Sndr, env_of_t<Rcvr>> is true. Let Sigs... be the +

        Let rcvr be an rvalue whose type Rcvr models receiver, and let Sndr be the type of a sender such that sender_in<Sndr, env_of_t<Rcvr>> is true. Let Sigs... be the template arguments of the completion_signatures specialization named by completion_signatures_of_t<Sndr, env_of_t<Rcvr>>. Let CSO be a completion function. If sender Sndr or its operation state cause the expression CSO(rcvr, args...) to be potentially evaluated @@ -9219,13 +9239,12 @@

        connect connects ([async.ops]) a sender with a receiver.

      4. -

        The name connect denotes a customization point object. For subexpressions sndr and rcvr, let Sndr be decltype((sndr)) and Rcvr be decltype((rcvr)), and let DS and DR be the decayed types of Sndr and Rcvr, respectively.

        +

        The name connect denotes a customization point object. For subexpressions sndr and rcvr, let Sndr be decltype((sndr)) and Rcvr be decltype((rcvr)), and let DS and DR be decay_t<Sndr> and decay_t<Rcvr>, respectively.

      5. -

        Let connect-awaitable-promise be the following class:

        +

        Let connect-awaitable-promise be the following exposition-only class:

        namespace std::execution {
           struct connect-awaitable-promise
             : with-await-transform<connect-awaitable-promise> {
        -    DR& rcvr; // exposition only
         
             connect-awaitable-promise(DS&, DR& rcvr) noexcept : rcvr(rcvr) {}
         
        @@ -9235,7 +9254,7 @@ 

        operation-state-task be the following class:

        +

        Let operation-state-task be the following exposition-only class:

        namespace std::execution {
           struct operation-state-task {
             using operation_state_concept = operation_state_t;
             using promise_type = connect-awaitable-promise;
        -    coroutine_handle<> coro; // exposition only
         
             explicit operation-state-task(coroutine_handle<> h) noexcept : coro(h) {}
             operation-state-task(operation-state-task&& o) noexcept
        @@ -9266,6 +9287,9 @@ 

        Sndr does not satisfy sender or if Rcvr does not satisfy receiver, connect(sndr, rcvr) is ill-formed. Otherwise, the expression connect(sndr, rcvr) is +

        The expression connect(sndr, rcvr) is expression-equivalent to:

        1. @@ -9320,31 +9344,25 @@

          operation_state.

  • -

    Otherwise, connect-awaitable(sndr, rcvr) if that expression is -well-formed.

    +

    Otherwise, connect-awaitable(sndr, rcvr).

  • -

    Otherwise, connect(sndr, rcvr) is ill-formed.

    +

    Mandates: sender<Sndr> && receiver<Rcvr> is true.

    34.9.10. Sender factories [exec.factories]

    34.9.10.1. execution::schedule [exec.schedule]
    1. -

      schedule obtains a schedule-sender ([async.ops]) from a scheduler.

      +

      schedule obtains a schedule sender ([async.ops]) from a scheduler.

    2. The name schedule denotes a customization point object. For a -subexpression sch, the expression schedule(sch) is expression-equivalent to:

      +subexpression sch, the expression schedule(sch) is expression-equivalent to sch.schedule().

      1. -

        sch.schedule() if that expression is valid. If sch.schedule() does -not return a sender whose set_value completion scheduler is equal -to sch, the behavior of calling schedule(sch) is undefined.

        -
          -
        • -

          Mandates: The type of sch.schedule() satisfies sender.

          -
        +

        If the expression get_completion_scheduler<set_value_t>( get_env(sch.schedule())) == sch is ill-formed or evaluates +to false, the behavior of calling schedule(sch) is undefined.

      2. -

        Otherwise, schedule(sch) is ill-formed.

        +

        Mandates: The type of sch.schedule() satisfies sender.

    34.9.10.2. execution::just, execution::just_error, execution::just_stopped [exec.just]
    @@ -9365,7 +9383,7 @@
    just-cpo is just_stopped and sizeof...(ts) == 0 is false;

    -

    Otherwise, it is expression-equivalent to make-sender(just-cpo, product-type{vs...}).

    +

    Otherwise, it is expression-equivalent to make-sender(just-cpo, product-type{ts...}).

  • For just, just_error, and just_stopped, let set-cpo be set_value, set_error, and set_stopped respectively. The exposition-only class template impls-for ([exec.snd.general]) is specialized for just-cpo as @@ -9409,31 +9427,33 @@

    34.9.11.1. General [exec.adapt.general]

    1. -

      Subclause [exec.adapt] specifies a set of sender adaptors.

      +

      [exec.adapt] specifies a set of sender adaptors.

    2. -

      The bitwise OR operator is overloaded for the purpose of creating sender +

      The bitwise inclusive OR operator is overloaded for the purpose of creating sender chains. The adaptors also support function call syntax with equivalent semantics.

    3. -

      Unless otherwise specified, a sender adaptor is prohibited from causing -observable effects, apart from moving and copying its arguments, before the -returned sender is connected with a receiver using connect, and start is -called on the resulting operation state. This requirement applies to any -function that is selected by the implementation of the sender adaptor.

      -
    4. -

      Unless otherwise specified, a parent sender ([async.ops]) with a single child -sender sndr has an associated attribute object equal to FWD-ENV(get_env(sndr)) ([exec.fwd.env]). Unless -otherwise specified, a parent sender with more than one child senders has an -associated attributes object equal to empty_env{}. These -requirements apply to any function that is selected by the implementation of -the sender adaptor.

      -
    5. -

      Unless otherwise specified, when a parent sender is connected to a receiver rcvr, any receiver used to connect a child sender has an associated -environment equal to FWD-ENV(get_env(rcvr)). This -requirement applies to any sender returned from a function that is selected -by the implementation of such sender adaptor.

      -
    6. -

      If a sender returned from a sender adaptor specified in this subclause is +

      Unless otherwise specified:

      +
        +
      1. +

        A sender adaptor is prohibited from causing observable effects, apart +from moving and copying its arguments, before the returned sender is +connected with a receiver using connect, and start is called on the +resulting operation state.

        +
      2. +

        A parent sender ([async.ops]) with a single child +sender sndr has an associated attribute object equal to FWD-ENV(get_env(sndr)) ([exec.fwd.env]).

        +
      3. +

        A parent sender with more than one child sender has an +associated attributes object equal to empty_env{}.

        +
      4. +

        When a parent sender is connected to a receiver rcvr, any receiver used +to connect a child sender has an associated environment equal to FWD-ENV(get_env(rcvr)).

        +
      +

      These requirement apply to any function that is selected by the implementation +of the sender adaptor.

      +
    7. +

      If a sender returned from a sender adaptor specified in [exec.adapt] is specified to include set_error_t(Err) among its set of completion signatures where decay_t<Err> denotes the type exception_ptr, but the implementation does not potentially evaluate an error completion operation with an exception_ptr argument, the implementation is allowed to omit the exception_ptr error completion signature from the set.

      @@ -9442,7 +9462,7 @@
      sender arguments and returns a sender. For a sender +accepts one or more sender arguments and returns a sender. For a pipeable sender adaptor closure object c and an expression sndr such that decltype((sndr)) models sender, the following expressions are equivalent and yield a sender:

      c(sndr)
      @@ -9454,20 +9474,20 @@ 
      d2 of type decay_t<decltype((d))> direct-non-list-initialized with d.

      +

      Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d.

    8. -

      It has one bound argument entity, an object c2 of type decay_t<decltype((c))> direct-non-list-initialized with C.

      +

      It has one bound argument entity, an object c2 of type decltype(auto(c)) direct-non-list-initialized with c.

    9. Its call pattern is d2(c2(arg)), where arg is the argument used in a function call expression of e.

    The expression c | d is well-formed if and only if the initializations of - the state entities of e are all well-formed.

    + the state entities ([func.def]) of e are all well-formed.

    1. An object t of type T is a pipeable sender adaptor closure object if T models derived_from<sender_adaptor_closure<T>>, T has no other base -classes of type sender_adaptor_closure<U> for any other type U, and T does not model sender.

      +classes of type sender_adaptor_closure<U> for any other type U, and T does not satisfy sender.

    2. The template parameter D for sender_adaptor_closure can be an incomplete type. Before any expression of type cv D appears as an @@ -9482,10 +9502,9 @@

      adaptor accepts more than one argument, then let sndr be an expression such that decltype((sndr)) models sender, let args... be arguments such that adaptor(sndr, args...) is a -well-formed expression as specified in the rest of this subclause -([exec.adapt.objects]), and let BoundArgs be a pack that denotes decay_t<decltype((args))>.... The expression adaptor(args...) produces a -pipeable sender adaptor closure object f that is a perfect forwarding call -wrapper with the following properties:

      +well-formed expression as specified below, and let BoundArgs be a pack +that denotes decltype(auto(args)).... The expression adaptor(args...) produces a pipeable sender adaptor closure object f that is a perfect +forwarding call wrapper with the following properties:

      • Its target object is a copy of adaptor.

        @@ -9505,53 +9524,55 @@
        on adapts an input sender into a sender that will start on an execution agent belonging to a particular scheduler’s associated execution resource.

      • -

        The name on denotes a customization point object. For some subexpressions sch and sndr, if decltype((sch)) does not satisfy scheduler, or decltype((sndr)) does not satisfy sender, on(sch, sndr) is ill-formed.

        +

        The name on denotes a customization point object. For subexpressions sch and sndr, if decltype((sch)) does not satisfy scheduler, or decltype((sndr)) does not satisfy sender, on(sch, sndr) is ill-formed.

      • Otherwise, the expression on(sch, sndr) is expression-equivalent to:

        transform_sender(
           query-or-default(get_domain, sch, default_domain()),
        -  make-sender(on, sch, sndr));
        +  make-sender(on, sch, sndr))
         
        +

        except that sch is evaluated only once.

      • Let out_sndr and env be subexpressions such that OutSndr is decltype((out_sndr)). If sender-for<OutSndr, on_t> is false, then the expressions on.transform_env(out_sndr, env) and on.transform_sender(out_sndr, env) are ill-formed; otherwise:

        • on.transform_env(out_sndr, env) is equivalent to:

          -
          auto&& [ign1, sch, ign2] = out_sndr;
          +
          auto&& [_, sch, _] = out_sndr;
           return JOIN-ENV(SCHED-ENV(sch), FWD-ENV(env));
           
        • on.transform_sender(out_sndr, env) is equivalent to:

          -
          auto&& [ign, sch, sndr] = out_sndr;
          +
          auto&& [_, sch, sndr] = out_sndr;
           return let_value(
             schedule(sch),
          -  [sndr = std::forward_like<OutSndr>(sndr)]() mutable {
          +  [sndr = std::forward_like<OutSndr>(sndr)]() mutable
          +    noexcept(is_nothrow_move_constructible_v) {
               return std::move(sndr);
             });
          -
          +
      • Let out_sndr be a subexpression denoting a sender returned from on(sch, sndr) or one equal to such, and let OutSndr be the type decltype((out_sndr)). Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that sender_in<OutSndr, Env> is true. Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr. Calling start(op) shall start sndr on an execution agent of the -associated execution resource of sch, or failing that, shall execute an -error completion on out_rcvr.

        +associated execution resource of sch. If scheduling onto sch fails, an error +completion on out_rcvr shall be executed on an unspecified execution agent.

    34.9.11.4. execution::transfer [exec.transfer]
    1. -

      transfer adapts a sender into one with a different associated set_value completion scheduler. It results in a transition -between different execution resources when executed.

      +

      transfer adapts a sender into one that completes on the specified scheduler.

    2. -

      The name transfer denotes a customization point object. For some +

      The name transfer denotes a customization point object. For subexpressions sch and sndr, if decltype((sch)) does not satisfy scheduler, or decltype((sndr)) does not satisfy sender, transfer(sndr, sch) is ill-formed.

    3. Otherwise, the expression transfer(sndr, sch) is expression-equivalent to:

      transform_sender(
         get-domain-early(sndr),
      -  make-sender(transfer, sch, sndr));
      +  make-sender(transfer, sch, sndr))
       
      +

      except that sndr is evaluated only once.

    4. The exposition-only class template impls-for is specialized for transfer_t as follows:

      @@ -9568,18 +9589,18 @@
      sndr and env be subexpressions such that Sndr is decltype((sndr)). If sender-for<Sndr, transfer_t> is false, then the expression transfer.transform_sender(sndr, env) is ill-formed; otherwise, it is equal to:

      -
      auto [tag, data, child] = sndr;
      +
      auto [_, data, child] = sndr;
       return schedule_from(std::move(data), std::move(child));
       
      -

      This causes the transfer(sndr, sch) sender to become schedule_from(sch, sndr) when it is connected with a receiver with an -execution domain that does not customize transfer.

      +

      This causes the transfer(sndr, sch) sender to become schedule_from(sch, sndr) when it is connected with a receiver whose +execution domain does not customize transfer.

    5. Let out_sndr be a subexpression denoting a sender returned from transfer(sndr, sch) or one equal to such, and let OutSndr be the type decltype((out_sndr)). Let out_rcvr be a subexpression denoting a receiver that has an environment of type Env such that sender_in<OutSndr, Env> is true. Let op be an lvalue referring to the operation state that results from connecting out_sndr with out_rcvr. Calling start(op) shall start sndr on the current execution agent and execute completion operations on out_rcvr on an execution agent of the execution resource -associated with sch. If scheduling onto sch fails, execute an error -completion on out_rcvr on an unspecified execution agent.

      +associated with sch. If scheduling onto sch fails, an error completion +on out_rcvr shall be executed on an unspecified execution agent.

    34.9.11.5. execution::schedule_from [exec.schedule.from]
      @@ -9590,14 +9611,15 @@
      schedule_from denotes a customization point object. For some subexpressions sch and sndr, let Sch be decltype((sch)) and Sndr be decltype((sndr)). If Sch does not satisfy scheduler, or Sndr does not -satisfy sender, schedule_from is ill-formed.

      +satisfy sender, schedule_from(sch, sndr) is ill-formed.

    1. Otherwise, the expression schedule_from(sch, sndr) is expression-equivalent to:

      transform_sender(
         query-or-default(get_domain, sch, default_domain()),
      -  make-sender(schedule_from, sch, sndr));
      +  make-sender(schedule_from, sch, sndr))
       
      +

      except that sch is evaluated only once.

    2. The exposition-only class template impls-for ([exec.snd.general]) is specialized for schedule_from_t as follows:

      @@ -9621,31 +9643,33 @@
      impls-for<schedule_from_t>::get-state is initialized with a callable object equivalent to the following lambda:

      -
      []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr)
      +
      []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(see below)
           requires sender_in<child-type<Sndr>, env_of_t<Rcvr>> {
      -  return apply(
      -    [&]<class Sch, class Child>(auto, Sch sch, Child&& child) {
      -      using variant-type = see below;
      -      using receiver-type = see below;
      -      using operation-type = connect_result_t<schedule_result_t<Sch>, receiver-type>;
      -
      -      struct state-type {
      -        Rcvr& rcvr;
      -        variant-type async-result;
      -        operation-type op-state;
      -
      -        explicit state-type(Sch sch, Rcvr& rcvr)
      -          : rcvr(rcvr), op-state(connect(schedule(sch), receiver-type{{}, this})) {}
      -      };
       
      -      return state-type{sch, rcvr};
      -    },
      -    std::forward<Sndr>(sndr));
      +  auto& [_, sch, child] = sndr;
      +
      +  using sched-t = decltype(auto(sch));
      +  using variant-type = see below;
      +  using receiver-type = see below;
      +  using operation-type = connect_result_t<schedule_result_t<sched-t>, receiver-type>;
      +  constexpr bool nothrow = noexcept(connect(schedule(sch), receiver-type{nullptr}));
      +
      +  struct state-type {
      +    Rcvr& rcvr;
      +    variant-type async-result;
      +    operation-type op-state;
      +
      +    explicit state-type(sched-t sch, Rcvr& rcvr) noexcept(nothrow)
      +      : rcvr(rcvr), op-state(connect(schedule(sch), receiver-type{this})) {}
      +  };
      +
      +  return state-type{sch, rcvr};
       }
       
      1. -

        The local class state-type is a structural type.

        +

        Objects of the local class state-type can be used to + initialize a structured binding.

      2. Let Sigs be a pack of the arguments to the completion_signatures specialization named by completion_signatures_of_t<Child, env_of_t<Rcvr>>. Let as-tuple be an alias template that transforms a completion signature Tag(Args...) into the tuple specialization decayed-tuple<Tag, Args...>. @@ -9659,9 +9683,6 @@

        noexcept clause of the lambda is true if + the construction of the returned state-type object is not + potentially throwing; otherwise, false.

    3. The member impls-for<schedule_from_t>::complete is initialized with a callable object equivalent to the following lambda:

      @@ -9709,16 +9734,12 @@
      out_sndr denote the result of the invocation schedule_from(sch, sndr) or an object copied or moved from such, and let -the subexpression rcvr denote a receiver such that the expression connect(out_sndr, rcvr) is well-formed. The expression connect(out_sndr, rcvr) has undefined behavior unless it creates an -asynchronous operation ([async.ops]) that, when started:

      -
        -
      • -

        eventually completes on an execution agent belonging to the associated -execution resource of sch, and

        -
      • -

        completes with the same async result as sndr.

        -
      +

      Let out_sndr be a subexpression denoting a sender returned from schedule_from(sch, sndr) or one equal to such, and let OutSndr be the type decltype((out_sndr)). Let out_rcvr be a subexpression denoting a +receiver that has an environment of type Env such that sender_in<OutSndr, Env> is true. Let op be an lvalue referring to the operation state that +results from connecting out_sndr with out_rcvr. Calling start(op) shall start sndr on the current execution agent and execute completion +operations on out_rcvr on an execution agent of the execution resource +associated with sch. If scheduling onto sch fails, an error completion +on out_rcvr shall be executed on an unspecified execution agent.

    34.9.11.6. execution::then, execution::upon_error, execution::upon_stopped [exec.then]
      @@ -9729,15 +9750,16 @@
      then, upon_error, and upon_stopped denote customization point -objects. Let the expression then-cpo be one of then, upon_error, or upon_stopped. For subexpressions sndr and f, let Sndr be decltype((sndr)) and let F be the decayed type of f. If Sndr does not -satisfy sender, or F does not satisfy movable-value, then-cpo(sndr, f) is ill-formed.

      +objects. Let the expression then-cpo be one of then, upon_error, or upon_stopped. For subexpressions sndr and f, if decltype(sndr) does not satisfy sender, or decltype(f) does not +satisfy movable-value, then-cpo(sndr, f) is ill-formed.

    1. Otherwise, the expression then-cpo(sndr, f) is expression-equivalent to:

      transform_sender(
         get-domain-early(sndr),
      -  make-sender(then-cpo, f, sndr));
      +  make-sender(then-cpo, f, sndr))
       
      +

      except that sndr is evaluated only once.

    2. For then, upon_error, and upon_stopped, let set-cpo be set_value, set_error, and set_stopped respectively. The exposition-only class template impls-for ([exec.snd.general]) is specialized for then-cpo as follows:

      @@ -9746,7 +9768,7 @@
      f or a copy of such with the value, error, or stopped result -datums of sndr (for then, upon_error, and upon_stopped respectively), using the result value of f as out_sndr’s value +datums of sndr for then, upon_error, and upon_stopped respectively, using the result value of f as out_sndr’s value completion, and

    3. Forwards all other completion operations unchanged.

      @@ -9777,8 +9799,8 @@
      let-cpo be one of let_value, let_error, or let_stopped and let set-cpo be the -completion function that corresponds to let-cpo (set_value for let_value, etc.). For a subexpression sndr, let let-env(sndr) be expression-equivalent to the first +

      For let_value, let_error, and let_stopped, let set-cpo be set_value, set_error, and set_stopped respectively. +Let the expression let-cpo be one of let_value, let_error, or let_stopped. For a subexpression sndr, let let-env(sndr) be expression-equivalent to the first well-formed expression below:

      • @@ -9786,19 +9808,20 @@
        MAKE-ENV(get_domain, get_domain(get_env(sndr)))

      • -

        empty_env{}

        +

        (void(sndr), empty_env{})

    4. The names let_value, let_error, and let_stopped denote customization -point objects. For subexpressions sndr and f, let Sndr be decltype((sndr)), -let F be the decayed type of f. If Sndr does not satisfy sender or if F does not satisfy movable-value, the expression let-cpo(sndr, f) is ill-formed. If F does not satisfy invocable, the expression let_stopped(sndr, f) is ill-formed.

      +point objects. For subexpressions sndr and f, let F be the decayed +type of f. If decltype(sndr) does not satisfy sender or if decltype(f) does not satisfy movable-value, the expression let-cpo(sndr, f) is ill-formed. If F does not satisfy invocable, the expression let_stopped(sndr, f) is ill-formed.

    5. Otherwise, the expression let-cpo(sndr, f) is expression-equivalent to:

      transform_sender(
         get-domain-early(sndr),
      -  make-sender(let-cpo, f, sndr));
      +  make-sender(let-cpo, f, sndr))
       
      +

      except that sndr is evaluated only once.

    6. The exposition-only class template impls-for ([exec.snd.general]) is specialized for let-cpo as follows:

      @@ -9818,56 +9841,70 @@
      receiver2 denote the following exposition-only class template:

      namespace std::execution {
         template<class Rcvr, class Env>
      -  struct receiver2 : Rcvr {
      -    explicit receiver2(Rcvr rcvr, Env env)
      -      : Rcvr(std::move(rcvr)), env(std::move(env)) {}
      +  struct receiver2 {
      +    using receiver_concept = receiver_t;
       
      -    auto get_env() const noexcept {
      -      const Rcvr& rcvr = *this;
      +    template<class... Args>
      +    void set_value(Args&&... args) && noexcept {
      +      execution::set_value(std::move(rcvr), std::forward<Args>(args)...);
      +    }
      +
      +    template<class Error>
      +    void set_error(Error&& err) && noexcept {
      +      execution::set_error(std::move(rcvr), std::forward<Error>(err));
      +    }
      +
      +    void set_stopped() && noexcept {
      +      execution::set_stopped(std::move(rcvr));
      +    }
      +
      +    decltype(auto) get_env() const noexcept {
             return JOIN-ENV(env, FWD-ENV(execution::get_env(rcvr)));
           }
       
      +    Rcvr& rcvr; // exposition only
           Env env; // exposition only
         };
       }
       
    7. impls-for<decayed-typeof<let-cpo>>::get-state is -is initialized with a callable object equivalent to the following:

      +initialized with a callable object equivalent to the following:

      []<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires see below {
      -  auto&& [tag, data, child] = std::forward<Sndr>(sndr);
      -  return [&]<class Fn, class Env>(Fn fn, Env env) {
      -    using args-variant-type = see below;
      -    using ops2-variant-type = see below;
      -
      -    struct state-type {
      -      Fn fn;
      -      Env env;
      -      args-variant-type args;
      -      ops2-variant-type ops2;
      -    };
      -    return state-type{std::move(fn), std::move(env), {}, {}};
      -  }(std::forward_like<Sndr>(data), let-env(child));
      +  auto& [_, fn, child] = sndr;
      +  using fn-t = decay_t<decltype(fn)>;
      +  using env-t = decltype(let-env(child));
      +  using args-variant-type = see below;
      +  using ops2-variant-type = see below;
      +
      +  struct state-type {
      +    Fn fn;
      +    Env env;
      +    args-variant-type args;
      +    ops2-variant-type ops2;
      +  };
      +  return state-type{std::forward_like<Sndr>(fn), let-env(child), {}, {}};
       }
       
      1. -

        Let Sigs be a pack of the arguments to the completion_signatures specialization named by completion_signatures_of_t<child-type<Sndr>, env_of_t<Rcvr>>. Let LetSigs be a pack of those types in Sigs with a return type of decayed-typeof<set-cpo>. Let as-tuple be an alias template such that as-tuple<Tag(Args...)> denotes the type decayed-tuple<Args...>. Then args-variant-type denotes the type variant<monostate, as-tuple<LetSigs>...>.

        +

        Let Sigs be a pack of the arguments to the completion_signatures specialization named by completion_signatures_of_t<child-type<Sndr>, env_of_t<Rcvr>>. Let LetSigs be a pack of those types in Sigs with a return type of decayed-typeof<set-cpo>. Let as-tuple be an alias template such that as-tuple<Tag(Args...)> denotes the type decayed-tuple<Args...>. Then args-variant-type denotes the type variant<monostate, as-tuple<LetSigs>...> except with duplicate types removed.

      2. Let as-sndr2 be an alias template such that as-sndr2<Tag(Args...)> denotes the type call-result-t<Fn, decay_t<Args>&...>. -Then ops2-variant-type denotes the type variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, Env>>...>.

        +Then ops2-variant-type denotes the type variant<monostate, connect_result_t<as-sndr2<LetSigs>, receiver2<Rcvr, Env>>...> except with duplicate types removed.

      3. The requires-clause constraining the above lambda is satisfied if and only if the types args-variant-type and ops2-variant-type are well-formed.

    8. -

      The exposition-only function template let-bind is equal to:

      -
      auto& args = state.args.emplace<decayed-tuple<Args...>>(std::forward<Args>(args)...);
      -auto sndr2 = apply(std::move(state.fn), args);
      -auto rcvr2 = receiver2{std::move(rcvr), std::move(state.env)};
      -auto mkop2 = [&] { return connect(std::move(sndr2), std::move(rcvr2)); };
      -auto& op2 = state.ops2.emplace<decltype(mkop2())>(emplace-from{mkop2});
      -start(op2);
      +         

      The exposition-only function template let-bind has effects equivalent to:

      +
      using args_t = decayed-tuple<Args...>;
      +auto mkop2 = [&] {
      +  return connect(
      +    apply(std::move(state.fn), state.args.emplace<args_t>(std::forward<Args>(args)...)),
      +    receiver2{rcvr, std::move(state.env)});
      +};
      +start(state.ops2.emplace<decltype(mkop2())>(emplace-from{mkop2}));
       
    9. impls-for<decayed-typeof<let-cpo>>::complete is @@ -9886,7 +9923,7 @@

      sndr and env be subexpressions, and let Sndr be decltype((sndr)). If sender-for<Sndr, decayed-typeof<let-cpo>> is false, then the expression let-cpo.transform_env(sndr, env) is ill-formed. Otherwise, it is equal to JOIN-ENV(let-env(sndr), FWD-ENV(env)).

    10. -

      Let the subexpression out_sndr denote the result of the invocation let-cpo(sndr, f) or an object copied or moved from such, +

      Let the subexpression out_sndr denote the result of the invocation let-cpo(sndr, f) or an object equal to such, and let the subexpression rcvr denote a receiver such that the expression connect(out_sndr, rcvr) is well-formed. The expression connect(out_sndr, rcvr) has undefined behavior unless it creates an asynchronous operation ([async.ops]) that, when started:

        @@ -9905,16 +9942,16 @@
        bulk runs a task repeatedly for every index in an index space.

      • -

        The name bulk denotes a customization point object. For subexpressions sndr, shape, and f, let Sndr be decltype((sndr)), let Shape be -the decayed type of shape, and let F be the decayed type of f. If Sndr does not satisfy sender, or if Shape does not satisfy integral, -or if F does not satisfy movable-value, bulk(sndr, shape, f) is ill-formed.

        +

        The name bulk denotes a customization point object. For subexpressions sndr, shape, and f, let Shape be decltype(auto(shape)). If decltype((sndr)) does not satisfy sender, or if Shape does not +satisfy integral, or if decltype((f)) does not satisfy movable-value, bulk(sndr, shape, f) is ill-formed.

      • Otherwise, the expression bulk(sndr, shape, f) is expression-equivalent to:

        transform_sender(
           get-domain-early(sndr),
        -  make-sender(bulk, product-type{shape, f}, sndr));
        +  make-sender(bulk, product-type{shape, f}, sndr))
         
        +

        except that sndr is evaluated only once.

      • The exposition-only class template impls-for ([exec.snd.general]) is specialized for bulk_t as follows:

        namespace std::execution {
        @@ -9934,7 +9971,7 @@ 
        out_sndr denote the result of the invocation bulk(sndr, shape, f) or an object copied or moved from such, +

        Let the subexpression out_sndr denote the result of the invocation bulk(sndr, shape, f) or an object equal to such, and let the subexpression rcvr denote a receiver such that the expression connect(out_sndr, rcvr) is well-formed. The expression connect(out_sndr, rcvr) has undefined behavior unless it creates an asynchronous operation ([async.ops]) that, when started: