diff --git a/source/exec.tex b/source/exec.tex index 8fa3c30a24..2aed38436c 100644 --- a/source/exec.tex +++ b/source/exec.tex @@ -372,6 +372,9 @@ The type of the receiver does not affect an asynchronous operation's completion signatures, only the type of the receiver's environment. +A \defnadj{non-dependent}{sender} is a sender type +whose completion signatures are knowable +independent of an execution environment. \pnum A sender algorithm is a function that takes and/or returns a sender. @@ -528,23 +531,18 @@ template concept @\libconcept{sender}@ = @\seebelow@; - template> + template concept @\libconcept{sender_in}@ = @\seebelow@; + template + concept @\libconcept{dependent_sender}@ = @\seebelow@; + template concept @\libconcept{sender_to}@ = @\seebelow@; template struct @\exposidnc{type-list}@; // \expos - // \ref{exec.getcomplsigs}, completion signatures - struct get_completion_signatures_t; - inline constexpr get_completion_signatures_t get_completion_signatures {}; - - template> - requires @\libconcept{sender_in}@ - using completion_signatures_of_t = @\exposid{call-result-t}@; - template using @\exposidnc{decayed-tuple}@ = tuple...>; // \expos @@ -566,10 +564,10 @@ requires @\libconcept{sender_in}@ constexpr bool sends_stopped = @\seebelow@; - template + template using @\exposidnc{single-sender-value-type}@ = @\seebelownc@; // \expos - template + template concept @\exposconcept{single-sender}@ = @\seebelow@; // \expos template<@\libconcept{sender}@ Sndr> @@ -636,6 +634,8 @@ struct @\libglobal{stopped_as_optional_t}@ { @\unspec@ }; struct @\libglobal{stopped_as_error_t}@ { @\unspec@ }; + inline constexpr @\unspec@ @\libglobal{write_env}@{}; + inline constexpr @\unspec@ @\libglobal{unstoppable}@{}; inline constexpr starts_on_t @\libglobal{starts_on}@{}; inline constexpr continues_on_t @\libglobal{continues_on}@{}; inline constexpr on_t @\libglobal{on}@{}; @@ -654,38 +654,25 @@ inline constexpr stopped_as_optional_t @\libglobal{stopped_as_optional}@{}; inline constexpr stopped_as_error_t @\libglobal{stopped_as_error}@{}; - // \ref{exec.util}, sender and receiver utilities - // \ref{exec.util.cmplsig} + // \ref{exec.cmplsig}, completion signatures template concept @\exposconceptnc{completion-signature}@ = @\seebelownc@; // \expos template<@\exposconcept{completion-signature}@... Fns> - struct @\libglobal{completion_signatures}@ {}; + struct @\libglobal{completion_signatures}@; template concept @\exposconceptnc{valid-completion-signatures}@ = @\seebelownc@; // \expos - // \ref{exec.util.cmplsig.trans} - template< - @\exposconcept{valid-completion-signatures}@ InputSignatures, - @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, - template class SetValue = @\seebelow@, - template class SetError = @\seebelow@, - @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> - using transform_completion_signatures = completion_signatures<@\seebelow@>; - - template< - @\libconcept{sender}@ Sndr, - class Env = env<>, - @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, - template class SetValue = @\seebelow@, - template class SetError = @\seebelow@, - @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> - requires @\libconcept{sender_in}@ - using transform_completion_signatures_of = - transform_completion_signatures< - completion_signatures_of_t, - AdditionalSignatures, SetValue, SetError, SetStopped>; + struct dependent_sender_error : exception {}; + + // \ref{exec.getcomplsigs} + template + consteval auto get_completion_signatures() -> @\exposconcept{valid-completion-signatures}@ auto; + + template + requires @\libconcept{sender_in}@ + using completion_signatures_of_t = decltype(get_completion_signatures()); // \ref{exec.run.loop}, run_loop class run_loop; @@ -733,35 +720,44 @@ \end{itemize} \pnum -For types \tcode{Sndr} and \tcode{Env}, -\tcode{\exposid{single-sender-value-type}} is an alias for: +For type \tcode{Sndr} and pack of types \tcode{Env}, +let \tcode{CS} be \tcode{completion_signatures_of_t}. +Then \tcode{\exposid{single-sender-value-type}} is ill-formed +if \tcode{CS} is ill-formed or +if \tcode{sizeof...(Env) > 1} is \tcode{true}; +otherwise, it is an alias for: \begin{itemize} \item -\tcode{value_types_of_t} +\tcode{\exposid{gather-signatures}} if that type is well-formed, \item Otherwise, \tcode{void} -if \tcode{value_types_of_t} is -\tcode{variant>} or \tcode{vari\-ant<>}, +if \tcode{\exposid{gather-signatures}} is +\tcode{variant>} or \tcode{variant<>}, \item -Otherwise, \tcode{value_types_of_t} -if that type is well-formed, +Otherwise, \tcode{\exposid{gather-signatures}} +if that\linebreak{} type is well-formed, \item -Otherwise, \tcode{\exposid{single-sender-value-type}} is ill-formed. +Otherwise, \tcode{\exposid{single-sender-value-type}} is ill-formed. \end{itemize} \pnum The exposition-only concept \exposconcept{single-sender} is defined as follows: \begin{codeblock} namespace std::execution { - template - concept @\defexposconcept{single-sender}@ = @\libconcept{sender_in}@ && + template + concept @\defexposconcept{single-sender}@ = @\libconcept{sender_in}@ && requires { - typename @\exposid{single-sender-value-type}@; + typename @\exposid{single-sender-value-type}@; }; } \end{codeblock} +\pnum +A type satisfies and models the exposition-only concept +\defexposconcept{valid-completion-signatures} if +it is a specialization of the \tcode{completion_signatures} class template. + \rSec1[exec.queries]{Queries} \rSec2[exec.fwd.env]{\tcode{forwarding_query}} @@ -1251,26 +1247,42 @@ a set of completion operations whose first argument is a subexpression equal to \tcode{rcvr}. Let \tcode{Sigs} be a pack of completion signatures corresponding to -this set of completion operations. -Then the type of the expression \tcode{get_completion_signatures(sndr, env)} is +this set of completion operations, and +let \tcode{CS} be +the type of the expression \tcode{get_completion_signatures(Sndr, Env)()}. +Then \tcode{CS} is a specialization of -the class template \tcode{completion_signatures}\iref{exec.util.cmplsig}, +the class template \tcode{completion_signatures}\iref{exec.cmplsig}, the set of whose template arguments is \tcode{Sigs}. +If none of the types in \tcode{Sigs} are dependent on the type \tcode{Env}, then +the expression \tcode{get_completion_signatures()} is well-formed and +its type is \tcode{CS}. If a user-provided implementation of the algorithm -that produced \tcode{sndr} is selected instead of the default, -any completion signature +that produced \tcode{sndr} is selected instead of the default: + +\begin{itemize} +\item +Any completion signature that is in the set of types denoted by \tcode{completion_signatures_of_t} and that is not part of \tcode{Sigs} shall correspond to error or stopped completion operations, unless otherwise specified. +\item +If none of the types in \tcode{Sigs} are dependent on the type \tcode{Env}, then +\tcode{completion_signatures_of_t} and +\tcode{completion_signatures_of_t} +shall denote the same type. +\end{itemize} + \rSec2[exec.snd.expos]{Exposition-only entities} \pnum Subclause \ref{exec.snd} makes use of the following exposition-only entities. \pnum +\indexlibraryglobal{\exposid{FWD-ENV}}% For a queryable object \tcode{env}, \tcode{\exposid{FWD-ENV}(env)} is an expression whose type satisfies \exposconcept{queryable} @@ -1279,6 +1291,9 @@ the expression \tcode{\exposid{FWD-ENV}(env).query(q, as...)} is ill-formed if \tcode{forwarding_query(q)} is \tcode{false}; otherwise, it is expression-equivalent to \tcode{env.query(q, as...)}. +\indexlibraryglobal{\exposid{FWD-ENV-T}}% +The type \tcode{\exposid{FWD-ENV-T}(Env)} is +\tcode{decltype(\exposid{FWD-ENV}(declval()))}. \pnum For a query object \tcode{q} and \tcode{a} subexpression \tcode{v}, @@ -1568,6 +1583,16 @@ otherwise, \tcode{false}. \end{itemdescr} +\pnum +Let \exposconcept{valid-specialization} be the following concept: +\begin{codeblock} +namespace std::execution { + template class T, class... Args> + concept @\defexposconceptnc{valid-specialization}@ = // \expos + requires { typename T; }; +} +\end{codeblock} + \begin{itemdecl} template constexpr auto @\exposid{make-sender}@(Tag tag, Data&& data, Child&&... child); @@ -1581,6 +1606,18 @@ \item \tcode{\libconcept{semiregular}} \item \tcode{\exposconcept{movable-value}} \item \tcode{(\libconcept{sender} \&\& ...)} +\item% +\tcode{\libconcept{dependent_sender} || \libconcept{sender_in}}, +where \tcode{Sndr} is \tcode{\exposid{basic-sender}} +as defined below. + +%%FIXME: OK to have \recommended inside an itemized list like this? + \recommended +If evaluation of \tcode{\libconcept{sender_in}} results in +an uncaught exception from +the evaluation of \tcode{get_completion_signatures()}, +the implementation should include information about that exception in +the resulting diagnostic. \end{itemize} \pnum @@ -1597,16 +1634,15 @@ concept @\defexposconceptnc{completion-tag}@ = // \expos @\libconcept{same_as}@ || @\libconcept{same_as}@ || @\libconcept{same_as}@; - template class T, class... Args> - concept @\defexposconceptnc{valid-specialization}@ = // \expos - requires { typename T; }; - struct @\exposidnc{default-impls}@ { // \expos static constexpr auto @\exposidnc{get-attrs}@ = @\seebelownc@; // \expos static constexpr auto @\exposidnc{get-env}@ = @\seebelownc@; // \expos static constexpr auto @\exposidnc{get-state}@ = @\seebelownc@; // \expos static constexpr auto @\exposidnc{start}@ = @\seebelownc@; // \expos static constexpr auto @\exposidnc{complete}@ = @\seebelownc@; // \expos + + template + static consteval void @\exposidnc{check-types}@(); // \expos }; template @@ -1621,6 +1657,9 @@ decltype(@\exposid{impls-for}@>::@\exposid{get-env}@), Index, @\exposid{state-type}@&, const Rcvr&>; + template + using @\exposidnc{data-type}@ = decltype(declval().template @\exposidnc{get}@<1>()); // \expos + template using @\exposidnc{child-type}@ = decltype(declval().template @\exposidnc{get}@()); // \expos @@ -1696,9 +1735,6 @@ } }; - template - using @\exposidnc{completion-signatures-for}@ = @\seebelownc@; // \expos - template struct @\exposidnc{basic-sender}@ : @\exposidnc{product-type}@ { // \expos using sender_concept = sender_t; @@ -1715,11 +1751,8 @@ return {std::forward(self), std::move(rcvr)}; } - template<@\exposconcept{decays-to}@<@\exposid{basic-sender}@> Self, class Env> - auto get_completion_signatures(this Self&& self, Env&& env) noexcept - -> @\exposid{completion-signatures-for}@ { - return {}; - } + template<@\exposconcept{decays-to}@<@\exposid{basic-sender}@> Self, class... Env> + static constexpr auto get_completion_signatures(); }; } \end{codeblock} @@ -1844,26 +1877,86 @@ } \end{codeblock} +\indexlibrarymember{\exposid{check-types}}{\exposid{default-impls}}% +\begin{itemdecl} +template + static consteval void @\exposid{default-impls}@::@\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} \pnum -For a subexpression \tcode{sndr} let \tcode{Sndr} be \tcode{decltype((sndr))}. -Let \tcode{rcvr} be a receiver -with an associated environment of type \tcode{Env} -such that \tcode{\libconcept{sender_in}} is \tcode{true}. -\tcode{\exposid{completion-signatures-for}} denotes -a specialization of \tcode{completion_signatures}, -the set of whose template arguments correspond to -the set of completion operations that are potentially evaluated -as a result of starting\iref{exec.async.ops} -the operation state that results from connecting \tcode{sndr} and \tcode{rcvr}. -When \tcode{\libconcept{sender_in}} is \tcode{false}, -the type denoted by \tcode{\exposid{completion-signatures-for}}, -if any, is not a specialization of \tcode{completion_signatures}. +Let \tcode{Is} be the pack of integral template arguments of +the \tcode{integer_sequence} specialization denoted by +\tcode{\exposid{ndices-for}}. -\recommended -When \tcode{\libconcept{sender_in}} is \tcode{false}, -implementations are encouraged to use the type -denoted by \tcode{\exposid{completion-signatures-for}} -to communicate to users why. +\pnum +\effects +Equivalent to: +%%FIXME: Should the following end with a ";"? +\begin{codeblock} +(get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(), ...) +\end{codeblock} + +\pnum +%%FIXME: There's no "Note" clause macro; should this be \remarks? +\Fundesc{Note} +For any types \tcode{T} and \tcode{S}, and pack \tcode{E}, +let \tcode{e} be the expression +\tcode{\exposid{impls-for}::\exposid{check-types}()}. +Then exactly one of the following is \tcode{true}: +\begin{itemize} +\item \tcode{e} is ill-formed, or +\item the evaluation of \tcode{e} exits with an exception, or +\item \tcode{e} is a core constant expression. +\end{itemize} +When \tcode{e} is a core constant expression, +%%FIXME: Can "S, E..." be a "pack"? +the pack \tcode{S, E...} uniquely determines a set of completion signatures. +\end{itemdescr} + +\indexlibrarymember{get_completion_signatures}{\exposid{basic-sender}}% +\begin{itemdecl} +template + template + constexpr auto @\exposid{basic-sender}@::get_completion_signatures(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{Rcvr} be the type of a receiver whose +environment has type \tcode{E}, where +\tcode{E} is the first type in the list \tcode{Env..., env<>}. +Let \tcode{\placeholder{CHECK-TYPES}()} be the expression +\tcode{\exposid{impls-for}::template \exposid{check-types}<\linebreak{}Sndr, E>()}, and +let \tcode{CS} be a type determined as follows: + +\begin{itemize} +\item% +If \tcode{\exposid{CHECK-TYPES}()} is a core constant expression, +let \tcode{op} be an lvalue subexpression +whose type is \tcode{connect_result_t}. +Then \tcode{CS} is the specialization of \tcode{completion_signatures} +the set of whose template arguments +correspond to the set of completion operations +that are potentially evaluated\iref{basic.def.odr} +as a result of evaluating \tcode{op.start()}. + +\item% +Otherwise, \tcode{CS} is \tcode{completion_signatures<>}. +\end{itemize} + +\pnum +\constraints +\tcode{\exposid{CHECK-TYPES}()} is a well-formed expression. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +@\exposid{CHECK-TYPES}@(); +return CS(); +\end{codeblock} +\end{itemdescr} \begin{itemdecl} template<@\libconcept{sender}@ Sndr, @\exposconcept{queryable}@ Env> @@ -1916,6 +2009,47 @@ \end{itemize} \end{itemdescr} +\pnum +\indexlibraryglobal{\exposid{overload-set}} +%%FIXME: Should this be in a namespace? +\begin{codeblock} +template +struct @\exposid{overload-set}@ : Fns... { + using Fns::operator()...; +}; +\end{codeblock} + +\pnum +\indexlibraryglobal{\exposid{not-a-sender}} +\indexlibrarymember{get_completion_signatures}{\exposid{not-a-sender}} +%%FIXME: Should this be in a namespace? +\begin{codeblock} +struct @\exposid{not-a-sender}@ { + using sender_concept = sender_t; + + template + static consteval auto get_completion_signatures() -> completion_signatures<> { + throw @\placeholder{unspecified-exception}@(); + } +}; +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. + +\pnum +\indexlibraryglobal{\exposid{decay-copyable-result-datums}} +%%FIXME: Should this be in a namespace? +\begin{codeblock} +constexpr void @\exposid{decay-copyable-result-datums}@(auto cs) { + cs.@\exposid{for-each}@([](Tag(*)(Ts...)) { + if constexpr (!(is_constructible_v, Ts> &&...)) + throw @\placeholder{unspecified-exception}@(); + }); +} +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. + \rSec2[exec.snd.concepts]{Sender concepts} \pnum @@ -1932,10 +2066,11 @@ The connect customization point object is used to connect\iref{exec.async.ops} a sender and a receiver to produce an operation state. +\indexlibraryglobal{\exposid{is-dependent-sender-helper}}% \begin{codeblock} namespace std::execution { - template - concept @\exposconcept{valid-completion-signatures}@ = @\seebelow@; // \expos + template + concept @\defexposconcept{is-constant}@ = true; // \expos template concept @\defexposconcept{is-sender}@ = // \expos @@ -1946,6 +2081,14 @@ @\exposconcept{is-sender}@ || @\exposconcept{is-awaitable}@>>; // \ref{exec.awaitable} + template + consteval bool @\exposid{is-dependent-sender-helper}@() try { // \expos + get_completion_signatures(); + return false; + } catch (dependent_sender_error&) { + return true; + } + template concept @\deflibconcept{sender}@ = bool(@\exposconcept{enable-sender}@>) && @@ -1955,14 +2098,16 @@ @\libconcept{move_constructible}@> && @\libconcept{constructible_from}@, Sndr>; - template> + template concept @\deflibconcept{sender_in}@ = @\libconcept{sender}@ && - @\exposconcept{queryable}@ && - requires (Sndr&& sndr, Env&& env) { - { get_completion_signatures(std::forward(sndr), std::forward(env)) } - -> @\exposconcept{valid-completion-signatures}@; - }; + (sizeof...(Env) <= 1) && + (@\exposconcept{queryable}@ &&...) && + @\exposconcept{is-constant}@()>; + + template + concept @\deflibconcept{dependent_sender}@ = + @\libconcept{sender}@ && bool_constant<@\exposid{is-dependent-sender-helper}@()>::value; template concept @\deflibconcept{sender_to}@ = @@ -1974,6 +2119,12 @@ } \end{codeblock} +\pnum +For a type \tcode{Sndr}, if +\tcode{\libconcept{sender}} is \tcode{true} and +\tcode{\libconcept{dependent_sender}} is \tcode{false}, +then \tcode{Sndr} is a non-dependent sender\iref{exec.async.ops}. + \pnum Given a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))} and @@ -1989,12 +2140,6 @@ starting the resulting operation state are permissible completions for \tcode{Sndr} and \tcode{Env}. -\pnum -A type models -the exposition-only concept \defexposconcept{valid-completion-signatures} -if it denotes a specialization of -the \tcode{completion_signatures} class template. - \pnum The exposition-only concepts \exposconcept{sender-of} and \exposconcept{sender-in-of} @@ -2005,15 +2150,22 @@ template using @\exposid{value-signature}@ = set_value_t(As...); // \expos + template + concept @\defexposconcept{sender-in-of-impl}@ = // \expos + @\libconcept{sender_in}@ && + @\exposid{MATCHING-SIG}@(SetValue, // see \ref{exec.general} + @\exposid{gather-signatures}@, + @\exposid{value-signature}@, + type_identity_t>); + template - concept @\defexposconcept{sender-in-of}@ = - @\libconcept{sender_in}@ && - @\exposid{MATCHING-SIG}@( // see \ref{exec.general} - set_value_t(Values...), - value_types_of_t); + concept @\defexposconcept{sender-in-of}@ = // \expos + @\exposconcept{sender-in-of-impl}@; template - concept @\defexposconcept{sender-of}@ = @\exposconcept{sender-in-of}@, Values...>; + concept @\defexposconcept{sender-of}@ = // \expos + @\exposconcept{sender-in-of-impl}@; } \end{codeblock} @@ -2085,6 +2237,14 @@ any necessary implicit conversions and materializations. \end{note} +Let \tcode{\exposid{GET-AWAITER}(c)} be +expression-equivalent to \tcode{\exposid{GET-AWAITER}(c, q)} +where \tcode{q} is an lvalue of +an unspecified empty class type \tcode{\placeholder{none-such}} that +lacks an \tcode{await_transform} member, and +where \tcode{coroutine_handle<\placeholder{none-such}>} behaves as +\tcode{coroutine_handle}. + \pnum Let \exposconcept{is-awaitable} be the following exposition-only concept: \begin{codeblock} @@ -2092,18 +2252,18 @@ template concept @\exposconcept{await-suspend-result}@ = @\seebelow@; // \expos - template + template concept @\defexposconcept{is-awaiter}@ = // \expos - requires (A& a, coroutine_handle h) { + requires (A& a, coroutine_handle h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> @\exposconcept{await-suspend-result}@; a.await_resume(); }; - template + template concept @\defexposconcept{is-awaitable}@ = // \expos - requires (C (*fc)() noexcept, Promise& p) { - { @\exposid{GET-AWAITER}@(fc(), p) } -> @\exposconcept{is-awaiter}@; + requires (C (*fc)() noexcept, Promise&... p) { + { @\exposid{GET-AWAITER}@(fc(), p...) } -> @\exposconcept{is-awaiter}@; }; } \end{codeblock} @@ -2121,7 +2281,9 @@ such that \tcode{decltype((c))} is type \tcode{C}, and an lvalue \tcode{p} of type \tcode{Promise}, \tcode{\exposid{await-result-\newline type}} denotes -the type \tcode{decltype(\exposid{GET-AWAITER}(c, p).await_resume())}. +the type \tcode{decltype(\exposid{GET-AWAITER}(c, p).await_resume())} and +\tcode{\exposid{await-re\-sult-type}} denotes +the type \tcode{decltype(\exposid{GET-AWAITER}(c).await_resume())}. \pnum Let \exposid{with-await-transform} be the exposition-only class template: @@ -2374,44 +2536,93 @@ \rSec2[exec.getcomplsigs]{\tcode{execution::get_completion_signatures}} +\indexlibraryglobal{get_completion_signatures}% +%%FIXME: Should this be in namespace std::execution? +\begin{itemdecl} +template + consteval auto get_completion_signatures() -> @\exposconcept{valid-completion-signatures}@ auto; +\end{itemdecl} + +\begin{itemdescr} \pnum -\tcode{get_completion_signatures} is a customization point object. -Let \tcode{sndr} be an expression -such that \tcode{decltype((sndr))} is \tcode{Sndr}, and -let \tcode{env} be an expression -such that \tcode{decltype((env))} is \tcode{Env}. -Let \tcode{new_sndr} be the expression -\tcode{transform_sender(decltype(\exposid{get-domain-late}(sndr, env))\{\}, sndr, env)}, and -let \tcode{NewSndr} be \tcode{decltype((new_sndr))}. -Then \tcode{get_completion_signatures(sndr, env)} is expression-equiva\-lent to -\tcode{(void(sndr), void(env), CS())} -except that \tcode{void(sndr)} and \tcode{void(env)} are -indeterminately sequenced, -where \tcode{CS} is: +Let $except$ be an rvalue subexpression of +an unspecified class type $Except$ such that +\tcode{\libconceptx{move_construc\-tible}{move_constructible}<$Except$> \&\& \libconcept{derived_from}<$Except$, exception>} +is \tcode{true}. +Let \tcode{\placeholder{CHECKED-COMPLSIGS}($e$)} be $e$ +if $e$ is a core constant expression whose +type satisfies \exposconcept{valid-completion-signatures}; +otherwise, it is the following expression: +\begin{codeblock} +(@$e$@, throw @$except$@, completion_signatures()) +\end{codeblock} +Let \tcode{\placeholder{get-complsigs}()} +be expression-equivalent to +\tcode{remove_reference_t::tem\-plate get_completion_signatures()}. +Let \tcode{NewSndr} be \tcode{Sndr} +if \tcode{sizeof...(Env) == 0} is \tcode{true}; +otherwise, \tcode{decltype($s$)} +where $s$ is the following expression: +\begin{codeblock} +transform_sender( + @\exposid{get-domain-late}@(declval(), declval()...), + declval(), + declval()...) +\end{codeblock} + +\pnum +\constraints +\tcode{sizeof...(Env) <= 1} is \tcode{true}. + +\pnum +\effects +Equivalent to: \tcode{return $e$;} +where $e$ is expression-equivalent to the following: \begin{itemize} \item -\tcode{decltype(new_sndr.get_completion_signatures(env))} -if that type is well-formed, +\tcode{\placeholder{CHECKED-COMPLSIGS}(\placeholder{get-complsigs}())} +if \tcode{\placeholder{get-complsigs}()} +is a well-formed expression. \item -Otherwise, \tcode{remove_cvref_t::completion_signatures} -if that type is well-formed, +Otherwise, +\tcode{\placeholder{CHECKED-COMPLSIGS}(\placeholder{get-complsigs}())} +if \tcode{\placeholder{get-complsigs}()} +is a well-formed expression. \item Otherwise, -if \tcode{\exposconcept{is-awaitable}>} is \tcode{true}, -then: \begin{codeblock} completion_signatures< - @\exposid{SET-VALUE-SIG}@(@\exposid{await-result-type}@>), // \iref{exec.snd.concepts} + @\exposid{SET-VALUE-SIG}@(@\exposid{await-result-type}@...>), // \iref{exec.snd.concepts} set_error_t(exception_ptr), set_stopped_t()> \end{codeblock} +if \tcode{\exposconcept{is-awaitable}...>} +is \tcode{true}. + +\item +Otherwise, +\tcode{(throw \placeholder{dependent-sender-error}(), completion_signatures())} +if \tcode{sizeof...(\linebreak{}Env) == 0} is \tcode{true}, +where \tcode{\placeholder{dependent-sender-error}} is +\tcode{dependent_sender_error} or +an unspecified type derived publicly and unambiguously from +\tcode{dependent_sender_error}. \item -Otherwise, \tcode{CS} is ill-formed. +Otherwise, +\tcode{(throw $except$, completion_signatures())}. \end{itemize} +\pnum +Given a type \tcode{Env}, if +\tcode{completion_signatures_of_t} and +\tcode{completion_signatures_of_t} +are both well-formed, +they shall denote the same type. +\end{itemdescr} + \pnum Let \tcode{rcvr} be an rvalue whose type \tcode{Rcvr} models \libconcept{receiver}, and @@ -2563,7 +2774,13 @@ \end{itemize} \mandates -\tcode{\libconcept{sender} \&\& \libconcept{receiver}} is \tcode{true}. +The following are \tcode{true}: +\begin{itemize} +\item +\tcode{\libconcept{sender_in}>} +\item +\tcode{\libconcept{receiver_of}>>} +\end{itemize} \rSec2[exec.factories]{Sender factories} @@ -2657,6 +2874,7 @@ \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{read_env} as follows: +\indexlibraryglobal{\exposid{impls-for}<\exposid{decayed-typeof}>} \begin{codeblock} namespace std::execution { template<> @@ -2666,9 +2884,29 @@ @\exposid{TRY-SET-VALUE}@(rcvr, query(get_env(rcvr))); }; }; + + template + static consteval void @\exposid{check-types}@(); } \end{codeblock} +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}<\exposid{decayed-typeof}>} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{Q} be \tcode{decay_t<\exposid{data-type}>}. + +\pnum +\throws +An exception of an unspecified type derived from \tcode{exception} if +the expression \tcode{Q()(env)} is ill-formed or has type \tcode{void}, where +\tcode{env} is an lvalue subexpression whose type is \tcode{Env}. +\end{itemdescr} + \rSec2[exec.adapt]{Sender adaptors} \rSec3[exec.adapt.general]{General} @@ -2701,8 +2939,17 @@ any receiver used to connect a child sender has an associated environment equal to \tcode{\exposid{FWD-ENV}(get_env(rcvr))}. \item +An adaptor whose child senders are all non-dependent\iref{exec.async.ops} +is itself non-dependent. +\item These requirements apply to any function that is selected by the implementation of the sender adaptor. +\item + \recommended +Implementations should use +the completion signatures of the adaptors +to communicate type errors to users and +to propagate any such type errors from child senders. \end{itemize} \pnum @@ -2803,6 +3050,84 @@ the initializations of the bound argument entities of the result, as specified above, are all well-formed. +\rSec3[exec.write.env]{\tcode{execution::write_env}} + +\pnum +\tcode{write_env} is a sender adaptor +that accepts a sender and a queryable object, and +that returns a sender that, +when connected with a receiver \tcode{rcvr}, +connects the adapted sender with a receiver +whose execution environment is the result of +joining the \exposconcept{queryable} object +to the result of \tcode{get_env(rcvr)}. + +\pnum +\tcode{write_env} is a customization point object. +For some subexpressions \tcode{sndr} and \tcode{env}, +if \tcode{decltype((sndr))} does not satisfy \libconcept{sender} or +if \tcode{decltype((env))} does not satisfy \exposconcept{queryable}, +the expression \tcode{write_env(sndr, env)} is ill-formed. +Otherwise, it is expression-equivalent to +\tcode{\exposid{make-sender}(write_env, env, sndr)}. + +\pnum +Let \exposid{write-env-t} denote the type \tcode{decltype(auto(write_env))}. +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} +is specialized for \exposid{write-env-t} as follows: +\indexlibraryglobal{\exposid{impls-for}<\exposid{write-env-t}>} +\begin{codeblock} +template<> +struct @\exposid{impls-for}@<@\exposid{write-env-t}@> : @\exposid{default-impls}@ { + static constexpr auto @\exposid{join-env}@(const auto& state, const auto& env) noexcept { + return @\seebelow@; + } + + static constexpr auto @\exposid{get-env}@ = + [](auto, const auto& state, const auto& rcvr) noexcept { + return @\exposid{join-env}@(state, @\exposid{FWD-ENV}@(get_env(rcvr))); + }; + + template + static consteval void @\exposid{check-types}@(); +}; +\end{codeblock} +Invocation of +\tcode{\exposid{impls-for}<\exposid{write-env-t}>::\exposid{join-env}} +returns an object \tcode{e} such that +\begin{itemize} +\item +\tcode{decltype(e)} models \exposconcept{queryable} and +\item +given a query object \tcode{q}, +the expression \tcode{e.query(q)} is expression-equivalent +to \tcode{state.query(q)} if that expression is valid, +otherwise, \tcode{e.query(q)} is expression-equivalent +to \tcode{env.query(q)}. +\item +For a type \tcode{Sndr} and a pack of types \tcode{Env}, +let \tcode{State} be \tcode{\exposid{data-type}} and +let \tcode{JoinEnv} be the pack +\tcode{decltype(\exposid{join-env}(declval(), \exposid{FWD-ENV}(declval())))}. +Then \tcode{\exposid{impls-for}<\exposid{write-\linebreak{}env-t}>::\exposid{check-types}()} +is expression-equivalent to +\tcode{get_completion_signatures<\linebreak{}\exposid{child-type}, JoinEnv...>()}. +\end{itemize} + +\rSec3[exec.unstoppable]{\tcode{execution::unstoppable}} + +\pnum +\tcode{unstoppable} is a sender adaptor +that connects its inner sender +with a receiver that has the execution environment of the outer receiver +but with an object of type \tcode{never_stop_token} +as the result of the \tcode{get_stop_token query}. + +\pnum +For a subexpression \tcode{sndr}, +\tcode{unstoppable(sndr)} is expression-equivalent to +\tcode{write_env(sndr, prop(get_stop_token, never_stop_token\{\}))}. + \rSec3[exec.starts.on]{\tcode{execution::starts_on}} \pnum @@ -2970,6 +3295,7 @@ \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{schedule_from_t} as follows: +\indexlibraryglobal{\exposid{impls-for}}% \begin{codeblock} namespace std::execution { template<> @@ -2978,6 +3304,9 @@ static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; }; + + template + static consteval void @\exposid{check-types}@(); } \end{codeblock} @@ -2995,7 +3324,7 @@ is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept(@\seebelow@) - requires @\libconcept{sender_in}@<@\exposid{child-type}@, env_of_t> { + requires @\libconcept{sender_in}@<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(env_of_t)> { auto& [_, sch, child] = sndr; @@ -3018,6 +3347,23 @@ } \end{codeblock} +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +get_completion_signatures>, @\exposid{FWD-ENV-T}@(Env)...>(); +auto cs = get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(); +@\exposid{decay-copyable-result-datums}@(cs); // see \ref{exec.snd.expos} +\end{codeblock} +\end{itemdescr} + \pnum Objects of the local class \exposid{state-type} can be used to initialize a structured binding. @@ -3025,7 +3371,7 @@ \pnum Let \tcode{Sigs} be a pack of the arguments to the \tcode{completion_signatures} specialization -named by \tcode{completion_signatures_of_t<\exposid{child-type}, env_of_t>}. +named by \tcode{completion_signatures_of_t<\exposid{child-type}, \exposid{FWD-ENV-T}(env_of_t)>}. Let \exposid{as-tuple} be an alias template that transforms a completion signature \tcode{Tag(Args...)} into the tuple specialization \tcode{\exposid{decayed-tuple}}. @@ -3190,20 +3536,8 @@ \pnum Otherwise: -Let \exposid{not-a-scheduler} be an unspecified empty class type, and -let \exposid{not-a-sender} be the exposition-only type: -\begin{codeblock} -struct @\exposid{not-a-sender}@ { - using sender_concept = sender_t; - - auto get_completion_signatures(auto&&) const { - return @\seebelow@; - } -}; -\end{codeblock} -where the member function \tcode{get_completion_signatures} returns -an object of a type that is not -a specialization of the \tcode{completion_signatures} class template. +%%FIXME: What is not-a-scheduler if this "Otherwise" case does not apply? +Let \exposid{not-a-scheduler} be an unspecified empty class type. \pnum The expression \tcode{on.transform_env(out_sndr, env)} @@ -3243,11 +3577,11 @@ if constexpr (@\libconcept{same_as}@) { return @\exposid{not-a-sender}@{}; } else { - return @\exposid{write-env}@( + return write_env( continues_on( std::forward_like(closure)( continues_on( - @\exposid{write-env}@(std::forward_like(child), @\exposid{SCHED-ENV}@(orig_sch)), + write_env(std::forward_like(child), @\exposid{SCHED-ENV}@(orig_sch)), sch)), orig_sch), @\exposid{SCHED-ENV}@(sch)); @@ -3255,13 +3589,6 @@ } \end{codeblock} -\pnum -\recommended -Implementations should use -the return type of \tcode{\exposid{not-a-sender}::get_completion_signatures} -to inform users that their usage of \tcode{on} is incorrect -because there is no available scheduler onto which to restore execution. - \pnum Let \tcode{out_sndr} be a subexpression denoting a sender returned from \tcode{on(sch, sndr)} or one equal to such, and @@ -3361,6 +3688,7 @@ \tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{then-cpo} as follows: +\indexlibraryglobal{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{then-cpo}>>} \begin{codeblock} namespace std::execution { template<> @@ -3375,10 +3703,35 @@ Tag()(std::move(rcvr), std::forward(args)...); } }; + + template + static consteval void @\exposid{check-types}@(); }; } \end{codeblock} +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{then-cpo}>>} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto cs = get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(); +auto fn = [](set_value_t(*)(Ts...)) { + if constexpr (!@\libconcept{invocable}@>, Ts...>) + throw @\placeholder{unspecified-exception}@(); +}; +cs.@\exposid{for-each}@(@\exposid{overload-set}@{fn, [](auto){}}); +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. +\end{itemdescr} + \pnum The expression \tcode{\exposid{then-cpo}(sndr, f)} has undefined behavior unless it returns a sender \tcode{out_sndr} that @@ -3441,6 +3794,7 @@ \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \exposid{let-cpo} as follows: +\indexlibraryglobal{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>} \begin{codeblock} namespace std::execution { template @@ -3450,6 +3804,9 @@ struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{let-cpo}@>> : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; + + template + static consteval void @\exposid{check-types}@(); }; } \end{codeblock} @@ -3493,10 +3850,48 @@ \item given a query object \tcode{q}, the expression \tcode{e.query(q)} is expression-equivalent -to \tcode{\exposid{env}.query(q)} if that expression is valid, -otherwise \tcode{e.query(q)} is expression-equivalent -to \tcode{get_env(\exposid{rcvr}).query(q)}. +to \tcode{\exposid{env}.query(q)} if that expression is valid; +otherwise, +if the type of \tcode{q} satisfies \exposconcept{forwarding-query}, +\tcode{e.query(q)} is expression-equivalent +to \tcode{get_env(\exposid{rcvr}).query(q)}; +otherwise, +\tcode{e.query(q)} is ill-formed. +\end{itemize} + +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +using LetFn = remove_cvref_t<@\exposid{data-type}@>; +auto cs = get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(); +auto fn = [](@\exposid{decayed-typeof}@<@\exposid{set-cpo}@>(*)(Ts...)) { + if constexpr (!@\placeholder{is-valid-let-sender}@) // \seebelow + throw @\placeholder{unspecified-exception}@(); +}; +cs.@\exposid{for-each}@(@\exposid{overload-set}@(fn, [](auto){})); +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}, and +where \tcode{\placeholder{is-valid-let-sender}} is \tcode{true} if and only if +all of the following are \tcode{true}: +\begin{itemize} +\item \tcode{(\libconcept{constructible_from}, Ts> \&\&...)} +\item \tcode{\libconcept{invocable}\&...>} +\item \tcode{\libconcept{sender}\&...>>} +\item% +\tcode{sizeof...(Env) == 0 || \libconcept{sender_in}\&...>, \placeholder{env-t}\linebreak{}...>} \end{itemize} +where \tcode{\placeholder{env-t}} is the pack +\tcode{decltype(\exposid{let-cpo}.transform_env(declval(), declval()))}. +\end{itemdescr} \pnum \tcode{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}} @@ -3522,11 +3917,11 @@ \pnum Let \tcode{Sigs} be a pack of the arguments to the \tcode{completion_signatures} specialization named by -\tcode{completion_signatures_of_t<\exposid{child-type}, env_of_t>}. +\tcode{completion_signatures_of_t<\exposid{child-type}, \exposid{FWD-ENV-T}(env_of_t)>}. Let \tcode{LetSigs} be a pack of those types in \tcode{Sigs} with a return type of \tcode{\exposid{decayed-typeof}<\exposid{set-cpo}>}. Let \exposid{as-tuple} be an alias template -such that \tcode{\exposid{as-tuple}<\linebreak Tag(Args...)>} denotes +such that \tcode{\exposid{as-tuple}} denotes the type \tcode{\exposid{decayed-tuple}}. Then \tcode{args_variant_t} denotes the type \tcode{variant...>} @@ -3540,7 +3935,7 @@ Then \tcode{ops2_variant_t} denotes the type \begin{codeblock} -variant, @\exposid{receiver2}@>...> +variant, @\exposid{receiver2}@>...> \end{codeblock} except with duplicate types removed. @@ -3586,7 +3981,10 @@ then the expression \tcode{\exposid{let-cpo}.transform_env(sndr, env)} is ill-formed. Otherwise, it is equal to -\tcode{\exposid{JOIN-ENV}(\exposid{let-env}(sndr), \exposid{FWD-ENV}(env))}. +\begin{codeblock} +auto& [_, _, child] = sndr; +return @\exposid{JOIN-ENV}@(@\exposid{let-env}@(child), @\exposid{FWD-ENV}@(env)); +\end{codeblock} \pnum Let the subexpression \tcode{out_sndr} denote @@ -3639,11 +4037,15 @@ \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{bulk_t} as follows: +\indexlibraryglobal{\exposid{impls-for}} \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{complete}@ = @\seebelow@; + + template + static consteval void @\exposid{check-types}@(); }; } \end{codeblock} @@ -3675,6 +4077,28 @@ if \tcode{Tag} denotes a type other than \tcode{set_value_t} or if the expression \tcode{f(auto(shape), args...)} is well-formed. +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto cs = get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(); +auto fn = [](set_value_t(*)(Ts...)) { + if constexpr (!@\libconcept{invocable}@>, Ts&...>) + throw @\placeholder{unspecified-exception}@(); +}; +cs.@\exposid{for-each}@(@\exposid{overload-set}@(fn, [](auto){})); +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. +\end{itemdescr} + \pnum Let the subexpression \tcode{out_sndr} denote the result of the invocation \tcode{bulk(sndr, shape, f)} or @@ -4088,6 +4512,8 @@ let \tcode{Sndrs} be a pack of the types \tcode{decltype((sndrs))...}, and let \tcode{CD} be the type \tcode{common_type_t}. +Let \tcode{CD2} be \tcode{CD} if \tcode{CD} is well-formed, and +\tcode{default_domain} otherwise. The expressions \tcode{when_all(sndrs...)} and \tcode{when_all_with_variant(sndrs...)} are ill-formed if any of the following is \tcode{true}: @@ -4095,20 +4521,19 @@ \item \tcode{sizeof...(sndrs)} is \tcode{0}, or \item -\tcode{(\libconcept{sender} \&\& ...)} is \tcode{false}, or -\item -\tcode{CD} is ill-formed. +\tcode{(\libconcept{sender} \&\& ...)} is \tcode{false}. \end{itemize} \pnum The expression \tcode{when_all(sndrs...)} is expression-equivalent to: \begin{codeblock} -transform_sender(CD(), @\exposid{make-sender}@(when_all, {}, sndrs...)) +transform_sender(CD2(), @\exposid{make-sender}@(when_all, {}, sndrs...)) \end{codeblock} \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{when_all_t} as follows: +\indexlibraryglobal{\exposid{impls-for}} \begin{codeblock} namespace std::execution { template<> @@ -4118,10 +4543,78 @@ static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{start}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; + + template + static consteval void @\exposid{check-types}@(); }; } \end{codeblock} +\pnum +Let \exposid{make-when-all-env} be +the following exposition-only function template: +\indexlibraryglobal{\exposid{make-when-all-env}} +%%FIXME: Should this be in namespace std::execution? +\begin{codeblock} +template + constexpr auto @\exposid{make-when-all-env}@(inplace_stop_source& stop_src, // \expos + Env&& env) noexcept { + return @\seebelow@; +} +\end{codeblock} +Returns an object \tcode{e} such that +\begin{itemize} +\item +\tcode{decltype(e)} models \exposconcept{queryable}, and +\item +\tcode{e.query(get_stop_token)} is expression-equivalent to +\tcode{state.\exposid{stop-src}.get_token()}, and +\item +given a query object \tcode{q} +with type other than \cv{} \tcode{stop_token_t} and +whose type satisfies \exposconceptx{forwarding-que\-ry}{forwarding-query}, +\tcode{e.query(q)} is expression-equivalent to \tcode{get_env(rcvr).query(q)}. +\end{itemize} + +\pnum +Let \tcode{\placeholder{when-all-env}} be an alias template such that +\tcode{\placeholder{when-all-env}} denotes the type +\tcode{decltype(\exposid{mmake-\linebreak{}when-all-env}(declval(), declval()))}. + +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}} +\begin{itemdecl} +template + static consteval void @\exposid{check-types}@(); +\end{itemdecl} + +\begin{itemdescr} +\pnum +Let \tcode{Is} be the pack of integral template arguments of +the \tcode{integer_sequence} specialization denoted by +\tcode{\exposid{indices-for}}. + +\pnum +\effects +Equivalent to: +\begin{codeblock} +auto fn = []() { + auto cs = get_completion_signatures...>(); + if constexpr (cs.@\exposid{count-of}@(set_value) >= 2) + throw @\placeholder{unspecified-exception}@(); + @\exposid{decay-copyable-result-datums}@(cs); // see \ref{exec.snd.expos} +}; +(fn.template operator()<@\exposid{child-type}@>(), ...); +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. + +\pnum +\throws +Any exception thrown as a result of evaluating the \Fundescx{Effects}, or +an exception of an unspecified type +derived from \tcode{exception} when \tcode{CD} is ill-formed. +\end{itemdescr} + \pnum The member \tcode{\exposid{impls-for}::\exposid{get-attrs}} is initialized with a callable object @@ -4142,20 +4635,9 @@ equivalent to the following lambda expression: \begin{codeblock} [](auto&&, State& state, const Receiver& rcvr) noexcept { - return @\seebelow@; + return @\exposid{make-when-all-env}@(state.@\exposid{stop-src}@, get_env(rcvr)); } \end{codeblock} -Returns an object \tcode{e} such that -\begin{itemize} -\item -\tcode{decltype(e)} models \exposconcept{queryable}, and -\item -\tcode{e.query(get_stop_token)} is expression-equivalent to -\tcode{state.\exposid{stop-src}.get_token()}, and -\item -given a query object \tcode{q} with type other than \cv{} \tcode{stop_token_t}, -\tcode{e.query(q)} is expression-equivalent to \tcode{get_env(rcvr).query(q)}. -\end{itemize} \pnum The member \tcode{\exposid{impls-for}::\exposid{get-state}} @@ -4172,15 +4654,11 @@ \end{codeblock} and where \exposid{make-state} is the following exposition-only class template: \begin{codeblock} -template -concept @\defexposconcept{max-1-sender-in}@ = @\libconcept{sender_in}@ && // \expos - (tuple_size_v> <= 1); - enum class @\exposid{disposition}@ { @\exposid{started}@, @\exposid{error}@, @\exposid{stopped}@ }; // \expos template struct @\exposid{make-state}@ { - template<@\exposconcept{max-1-sender-in}@>... Sndrs> + template auto operator()(auto, auto, Sndrs&&... sndrs) const { using values_tuple = @\seebelow@; using errors_variant = @\seebelow@; @@ -4211,19 +4689,19 @@ \pnum Let \exposid{copy-fail} be \tcode{exception_ptr} if decay-copying any of the child senders' result datums can potentially throw; -otherwise, \exposid{none-such}, -where \exposid{none-such} is an unspecified empty class type. +otherwise, \tcode{\placeholder{none-such}}, +where \tcode{\placeholder{none-such}} is an unspecified empty class type. \pnum The alias \tcode{values_tuple} denotes the type \begin{codeblock} -tuple, @\exposid{decayed-tuple}@, optional>...> +tuple), @\exposid{decayed-tuple}@, optional>...> \end{codeblock} if that type is well-formed; otherwise, \tcode{tuple<>}. \pnum The alias \tcode{errors_variant} denotes -the type \tcode{variant<\exposid{none-such}, \exposid{copy-fail}, Es...>} +the type \tcode{variant<\placeholder{none-such}, \exposid{copy-fail}, Es...>} with duplicate types removed, where \tcode{Es} is the pack of the decayed types of all the child senders' possible error result datums. @@ -4255,7 +4733,7 @@ @\exposid{on_stop}@.reset(); visit( [&](Error& error) noexcept { - if constexpr (!@\libconcept{same_as}@) { + if constexpr (!@\libconcept{same_as}@) { set_error(std::move(rcvr), std::move(error)); } }, @@ -4344,7 +4822,7 @@ The expression \tcode{when_all_with_variant(sndrs...)} is expression-equivalent to: \begin{codeblock} -transform_sender(CD(), @\exposid{make-sender}@(when_all_with_variant, {}, sndrs...)); +transform_sender(CD2(), @\exposid{make-sender}@(when_all_with_variant, {}, sndrs...)); \end{codeblock} \pnum @@ -4390,12 +4868,20 @@ \pnum The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} is specialized for \tcode{into_variant} as follows: +\indexlibraryglobal{\exposid{impls-for}} +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}} \begin{codeblock} namespace std::execution { template<> struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { static constexpr auto @\exposid{get-state}@ = @\seebelow@; static constexpr auto @\exposid{complete}@ = @\seebelow@; + + template + static consteval void @\exposid{check-types}@() { + auto cs = get_completion_signatures<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)...>(); + @\exposid{decay-copyable-result-datums}@(cs); // see \ref{exec.snd.expos} + } }; } \end{codeblock} @@ -4405,7 +4891,7 @@ is initialized with a callable object equivalent to the following lambda: \begin{codeblock} [](Sndr&& sndr, Rcvr& rcvr) noexcept - -> type_identity, env_of_t>> { + -> type_identity, @\exposid{FWD-ENV-T}@(env_of_t)>> { return {}; } \end{codeblock} @@ -4444,20 +4930,48 @@ \end{codeblock} except that \tcode{sndr} is only evaluated once. +\pnum +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} +is specialized for \tcode{stopped_as_optional_t} as follows: +\indexlibraryglobal{\exposid{impls-for}} +\indexlibrarymember{\exposid{check-types}}{\exposid{impls-for}} +\begin{codeblock} +namespace std::execution { + template<> + struct @\exposid{impls-for}@ : @\exposid{default-impls}@ { + template + static consteval void @\exposid{check-types}@() { + @\exposid{default-impls}@::@\exposid{check-types}@(); + if constexpr (!requires { + requires (!@\libconcept{same_as}@, + @\exposid{FWD-ENV-T}@(Env)...>>); }) + throw @\placeholder{unspecified-exception}@(); + } + }; +} +\end{codeblock} +where \tcode{\placeholder{unspecified-exception}} is +a type derived from \tcode{exception}. + \pnum Let \tcode{sndr} and \tcode{env} be subexpressions such that \tcode{Sndr} is \tcode{decltype((sndr))} and \tcode{Env} is \tcode{decltype((env))}. If \tcode{\exposconcept{sender-for}} -is \tcode{false}, or -if the type \tcode{\exposid{single-sender-value-type}} -is ill-formed or \tcode{void}, -then the expression \tcode{stopped_as_optional.transform_sender(sndr, env)} +is \tcode{false} +then the expression \tcode{stopped_as_optional.trans\-form_sender(sndr, env)} is ill-formed; -otherwise, it is equivalent to: +otherwise, +if \tcode{\libconcept{sender_in}<\exposid{child-type}, \exposid{FWD-ENV-T}(Env)>} +is \tcode{false}, +the expression \tcode{stopped_as_optional.transform_sender(sndr, env)} +is equivalent to \tcode{\exposid{not-a-sen\-der}()}; +otherwise, +%%FIXME: What's "it"? Replace with "the expression \tcode{stopped_as_optional.transform_sender(sndr, env)}" +it is equivalent to: \begin{codeblock} auto&& [_, _, child] = sndr; -using V = @\exposid{single-sender-value-type}@; +using V = @\exposid{single-sender-value-type}@<@\exposid{child-type}@, @\exposid{FWD-ENV-T}@(Env)>; return let_stopped( then(std::forward_like(child), [](Ts&&... ts) noexcept(is_nothrow_constructible_v) { @@ -4558,10 +5072,8 @@ \pnum The name \tcode{this_thread::sync_wait} denotes a customization point object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype((sndr))}. -If \tcode{\libconcept{sender_in}} -is \tcode{false}, -the expression \tcode{this_thread::sync_wait(sndr)} is ill-formed. -Otherwise, it is expression-equivalent to the following, +The expression \tcode{this_thread::sync_wait(sndr)} +is expression-equivalent to the following, except that \tcode{sndr} is evaluated only once: \begin{codeblock} apply_sender(@\exposid{get-domain-early}@(sndr), sync_wait, sndr) @@ -4569,6 +5081,8 @@ \mandates \begin{itemize} \item +\tcode{\libconcept{sender_in}} is \tcode{true}. +\item The type \tcode{\exposid{sync-wait-result-type}} is well-formed. \item \tcode{\libconcept{same_as}>} @@ -4702,10 +5216,8 @@ a customization point object. For a subexpression \tcode{sndr}, let \tcode{Sndr} be \tcode{decltype(into_variant(sndr))}. -If \tcode{\libconcept{sender_in}} -is \tcode{false}, -\tcode{this_thread::sync_wait_with_variant(sndr)} is ill-formed. -Otherwise, it is expression-equivalent to the following, +The expression \tcode{this_thread::sync_wait_with_variant(sndr)} +is expression-equivalent to the following, except \tcode{sndr} is evaluated only once: \begin{codeblock} apply_sender(@\exposid{get-domain-early}@(sndr), sync_wait_with_variant, sndr) @@ -4713,6 +5225,8 @@ \mandates \begin{itemize} \item +\tcode{\libconcept{sender_in}} is \tcode{true}. +\item The type \tcode{\exposid{sync-wait-with-variant-result-type}} is well-formed. \item @@ -4721,8 +5235,7 @@ \end{itemize} \pnum -If \tcode{\exposconcept{callable}} is \tcode{false}, -the expression \tcode{sync_wait_with_variant.apply_sender(\linebreak sndr)} is ill-formed. +The expression \tcode{sync_wait_with_variant.apply_sender(sndr)} is ill-formed. Otherwise, it is equivalent to: \begin{codeblock} using result_type = @\exposid{sync-wait-with-variant-result-type}@; @@ -4759,9 +5272,7 @@ \end{itemize} \end{itemize} -\rSec1[exec.util]{Sender/receiver utilities} - -\rSec2[exec.util.cmplsig]{\tcode{execution::completion_signatures}} +\rSec1[exec.cmplsig]{Completion signatures} \pnum \tcode{completion_signatures} is a type @@ -4862,10 +5373,23 @@ \end{note} \pnum +%%FIXME: Should we be qualifying indexed ids with "execution::" like this? +%% We're inconsistent. +\indexlibraryglobal{execution::completion_signatures}% +\indexlibrarymember{\exposid{for-each}}{execution::completion_signatures}% +\indexlibrarymember{\exposid{count-of}}{execution::completion_signatures}% \begin{codeblock} namespace std::execution { template<@\exposconcept{completion-signature}@... Fns> - struct completion_signatures {}; + struct completion_signatures { + template + static constexpr size_t @\exposid{count-of}@(Tag) { return @\seebelow@; } + + template + static constexpr void @\exposid{for-each}@(Fn&& fn) { // \expos + (std::forward(fn)(static_cast(nullptr)), ...); + } + }; template, template class Tuple = @\exposid{decayed-tuple}@, @@ -4890,104 +5414,12 @@ } \end{codeblock} -\rSec2[exec.util.cmplsig.trans]{\tcode{execution::transform_completion_signatures}} - -\pnum -\tcode{transform_completion_signatures} is an alias template -used to transform one set of completion signatures into another. -It takes a set of completion signatures and -several other template arguments -that apply modifications to each completion signature in the set -to generate a new specialization of \tcode{completion_signature}s. -\pnum -\begin{example} -Given a sender \tcode{Sndr} and an environment \tcode{Env}, -adapt the completion signatures of \tcode{Sndr} by -lvalue-ref qualifying the values, -adding an additional \tcode{exception_ptr} error completion -if it is not already there, and -leaving the other completion signatures alone. -\begin{codeblock} -template - using my_set_value_t = - completion_signatures; - -using my_completion_signatures = - transform_completion_signatures< - completion_signatures_of_t, - completion_signatures, - my_set_value_t>; -\end{codeblock} -\end{example} - \pnum -This subclause makes use of the following exposition-only entities: -\begin{codeblock} -template - using @\exposid{default-set-value}@ = - completion_signatures; - -template - using @\exposid{default-set-error}@ = - completion_signatures; -\end{codeblock} - -\pnum -\begin{codeblock} -namespace std::execution { - template<@\exposconcept{valid-completion-signatures}@ InputSignatures, - @\exposconcept{valid-completion-signatures}@ AdditionalSignatures = completion_signatures<>, - template class SetValue = @\exposid{default-set-value}@, - template class SetError = @\exposid{default-set-error}@, - @\exposconcept{valid-completion-signatures}@ SetStopped = completion_signatures> - using transform_completion_signatures = completion_signatures<@\seebelow@>; -} -\end{codeblock} - -\pnum -\tcode{SetValue} shall name an alias template -such that for any pack of types \tcode{As}, -the type \tcode{SetValue} is either ill-formed or else -\tcode{\exposconcept{valid-completion-signatures}>} is satisfied. -\tcode{SetError} shall name an alias template -such that for any type \tcode{Err}, -\tcode{SetError} is either ill-formed or else -\tcode{\exposconcept{valid-completion-signatures}>} is satisfied. - -\pnum -Let \tcode{Vs} be a pack of the types in the \exposid{type-list} named by -\tcode{\exposid{gather-signatures}}. - -\pnum -Let \tcode{Es} be a pack of the types in the \exposid{type-list} named by -\tcode{\exposid{gather-signatures}}, -where \exposid{error-list} is an alias template -such that \tcode{\exposid{error-list}<\linebreak Ts...>} is -\tcode{\exposid{type-list}...>}. - -\pnum -Let \tcode{Ss} name the type \tcode{completion_signatures<>} if -\tcode{\exposid{gather-signatures}} -is an alias for the type \tcode{\exposid{type-list}<>}; -otherwise, \tcode{SetStopped}. - -\pnum -If any of the above types are ill-formed, -then -\begin{codeblock} -transform_completion_signatures -\end{codeblock} -is ill-formed. -Otherwise, -\begin{codeblock} -transform_completion_signatures -\end{codeblock} -is the type \tcode{completion_signatures} -where \tcode{Sigs...} is the unique set of types in all the template arguments -of all the \tcode{completion_signatures} specializations in the set -\tcode{AdditionalSignatures}, \tcode{Vs...}, \tcode{Es...}, \tcode{Ss}. +For a subexpression \tcode{tag}, +let \tcode{Tag} be the decayed type of \tcode{tag}. +\tcode{completion_signatures::\exposid{count-of}(\linebreak{}tag)} +returns the count of function types in \tcode{Fns...} that +are of the form \tcode{Tag(Ts...)} where \tcode{Ts} is a pack of types. \rSec1[exec.envs]{Queryable utilities} @@ -5227,8 +5659,7 @@ \pnum \exposid{run-loop-sender} is an exposition-only type that satisfies \libconcept{sender}. -For any type \tcode{Env}, -\tcode{completion_signatures_of_t<\exposid{run-loop-sender}, Env>} is +\tcode{completion_signatures_of_t<\exposid{run-\linebreak{}loop-sender}>} is \begin{codeblock} completion_signatures \end{codeblock}