<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="author" content="Eric Niebler" />
  <title>Early Diagnostics for Sender Expressions</title>
  <style>
</style>
  <style type="text/css">body {
max-width: 1000px;
text-align: justify;
margin: auto
}
h1.title {
text-align: center
}
p, li {
text-align: justify
}
blockquote.note {
background-color: #E0E0E0;
padding-left: 15px;
padding-right: 15px;
padding-top: 1px;
padding-bottom: 1px;
}
span.note:before {
content: "[Note: ";
font-style: italic;
}
span.note:after {
content: " -- end note]";
font-style: italic;
}
span.ednote:before {
content: "[Editorial note: ";
font-style: italic;
}
span.ednote:after {
content: " -- end note]";
font-style: italic;
}
span.ednote,
span.ednote * {
color: blue !important;
margin-top: 0em;
margin-bottom: 0em;
}
ins,
ins * {
color: #00A000 !important
}
del,
del * {
color: #A00000 !important
}
div.ins,
div.ins * {
color: #00A000 !important;
text-decoration-line: none;
}
div.del,
div.del * {
color: #A00000 !important;
text-decoration-line: none;
}
dfn {
font-style: italic;
font-weight: bold;
}
code:not(.sourceCode) {
white-space: normal;
font-size: 85%;
}
ins>code:not(.sourceCode) {
white-space: normal;
font-size: 85%;
}
div.sourceCode {
margin-left: 20pt;
}
pre.sourceCode {
white-space: pre-wrap;
font-size: 85%;
}
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
td, td * {
padding: 10px;
text-align: left;
}
th, th * {
background-color: #f2f2f2;
text-align: center;
}
ul.dash {
list-style-type: none;
text-indent: -2em;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
ul.dash>li:before {
content: "\2014";
position: absolute;
margin-left: 2em;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
span.marginalizedparent {
position: relative;
left: -5em;
}
</style>
</head>
<body>
<header id="title-block-header">
<h1 class="title">Early Diagnostics for Sender Expressions</h1>

<div>
<dl>

<dt><strong>Document:</strong></dt>
<dd>
<span class="document">P3164R3</span>
</dd>

<dt><strong>Authors:</strong></dt>
<dd>
<span class="author"><a href="mailto:eric.niebler@gmail.com">Eric
Niebler</a></span>
</dd>

<dt><strong>Date:</strong></dt>
<dd>
<span class="date">Jan 9, 2025</span>
</dd>

<dt><strong>Source:</strong></dt>
<dd>
<span class="source"><a href="https://github.com/ericniebler/wg21/blob/main/P3164/P3164.md">Github</a></span>
</dd>
  
<dt><strong>Issue tracking:</strong></dt>
<dd>
<span class="issues"><a href="https://github.com/ericniebler/wg21/issues">Github</a></span>
</dd>
  
<dt><strong>Project:</strong></dt>
<dd>
<span class="project">ISO/IEC JTC1/SC22/WG21 14882: Programming Language
— C++</span>
</dd>
  
<dt><strong>Audience:</strong></dt>
<dd>
<span class="audience">Library Evolution Working Group</span>
</dd>

</dl>
</div>
</header>
<nav id="TOC" role="doc-toc">
<ul>
<li><a href="#synopsis" id="toc-synopsis">Synopsis</a></li>
<li><a href="#executive-summary" id="toc-executive-summary">Executive
Summary</a></li>
<li><a href="#revision-history" id="toc-revision-history">Revision
History</a></li>
<li><a href="#improving-early-diagnostics" id="toc-improving-early-diagnostics">Improving early diagnostics</a>
<ul>
<li><a href="#problem-description" id="toc-problem-description">Problem
Description</a></li>
<li><a href="#non-dependent-senders" id="toc-non-dependent-senders">Non-dependent senders</a></li>
<li><a href="#suggested-solution" id="toc-suggested-solution">Suggested
Solution</a></li>
<li><a href="#comparison-table" id="toc-comparison-table">Comparison
Table</a></li>
<li><a href="#design-considerations" id="toc-design-considerations">Design Considerations</a></li>
</ul></li>
<li><a href="#proposed-wording" id="toc-proposed-wording">Proposed
Wording</a></li>
<li><a href="#acknowlegments" id="toc-acknowlegments">Acknowlegments</a></li>
</ul>
</nav>
<h2 id="synopsis">Synopsis</h2>
<p>This paper aims to improve the user experience of the sender
framework by giving users immediate feedback about incorrect sender
expressions.</p>
<p>A relatively minor change to how sender completion signatures are
computed, and a trivial change to the sender adaptor algorithms makes it
possible for the majority of sender expressions to be type-checked
immediately, when the expression is constructed, rather than when it is
connected to a receiver (the status quo).</p>
<h2 id="executive-summary">Executive Summary</h2>
<p>Below are the specific changes this paper proposes in order to
improve the diagnostics emitted by sender-based codes:</p>
<ol type="1">
<li><p>Define a &quot;non-dependent sender&quot; to be one whose completions are
knowable without an environment.</p></li>
<li><p>Add support for calling <code>get_completion_signatures</code>
without an environment argument.</p></li>
<li><p>Change the definition of the
<code>completion_signatures_of_t</code> alias template to support
querying a sender&#39;s non-dependent signatures, if such exist.</p></li>
<li><p>Extend the awaitable helper concepts to support querying a type
whether it is awaitable in an arbitrary coroutine (without knowing the
promise type). For example, anything that implements the awaiter
interface (<code>await_ready</code>, <code>await_suspend</code>,
<code>await_resume</code>) is awaitable in any coroutine, and should
function as a non-dependent sender.</p></li>
<li><p>Require the sender adaptor algorithms to preserve the
&quot;non-dependent sender&quot; property wherever possible.</p></li>
<li><p>Add &quot;Mandates:&quot; paragraphs to the sender adaptor algorithms to
require them to hard-error when passed non-dependent senders that fail
type-checking.</p></li>
<li><p>Extend the eager type checking of the <code>let_</code> family of
algorithms to hard-error if the user passes a lambda that does not
return a sender type.</p></li>
<li><p>For any algorithm that eagerly <code>connect</code>s a sender
(e.g., <code>sync_wait</code>, <code>split</code>), hard-error
(<em>i.e.</em> <code>static_assert</code>) if the sender fails to
type-check rather than SFINAE-ing the overload away.</p></li>
<li><p>Specify that <code>run_loop</code>&#39;s schedule sender is
non-dependent.</p></li>
</ol>
<h2 id="revision-history">Revision History</h2>
<p><strong>R3</strong>:</p>
<ul>
<li><p>Rebase the paper on the current standard working draft.</p></li>
<li><p>Remove section respecifying
<code>transform_completion_signatures</code> to propagate type errors. A
separate paper (<a href="https://isocpp.org/files/papers/P3557R0.html">P3557</a>) addresses
the issue of type errors during the computation of completion
signatures.</p></li>
<li><p>Specify <code>stopped_as_optional</code> to mandate that its
child sender satisfies the <em><code>single-sender</code></em> concept,
and change the <em><code>single-sender</code></em> concept so that it
works properly with non-dependent senders.</p></li>
<li><p>Add requirement to [exec.snd.general] that ensures user-defined
customizations of sender algorithms produce non-dependent senders when
the default implementation would.</p></li>
<li><p>Specify the exposition-only <em><code>basic-sender</code></em>
helper to support the creation of non-dependent senders. (This change
includes the proposed resolution for <a href="https://github.com/cplusplus/sender-receiver/issues/307">cplusplus/sender-receiver#307</a>.)</p></li>
<li><p>Update the exposition-only <em><code>sender-of</code></em>
concept to work with non-dependent senders (i.e.
<code><em>sender-of</em>&lt;Sndr, int&gt;</code> subsumes
<code>sender_in&lt;Sndr&gt;</code>).</p></li>
<li><p>Specify that the sender returned by calling <code>schedule</code>
on <code>run_loop</code>&#39;s scheduler is non-dependent.</p></li>
</ul>
<p><strong>R2</strong>:</p>
<ul>
<li><p>Remove the <code>sender_in&lt;Sndr, Env...&gt;</code> constraint
on the <code>completion_signatures_of_t&lt;Sndr, Env...&gt;</code>
alias.</p></li>
<li><p>Specify <code>get_completion_signatures(sndr, env)</code> to
dispatch to <code>get_completion_signatures(sndr)</code> as a last
resort, per suggestion from Lewis Baker.</p></li>
<li><p>Add encouragement for implementors to use the completion
signatures of the sender adaptor algorithms to propagate type
errors.</p></li>
<li><p>Add a <a href="#design-considerations-1">design discussion</a>
about the decision to <em>infer</em> that types returned from
<code>get_completion_signatures</code> represent errors if they are not
specializations of <code>completion_signatures&lt;&gt;</code>.</p></li>
</ul>
<p><strong>R1</strong>:</p>
<ul>
<li><p>Change the specification of
<code>transform_completion_signatures</code> to propagate types that are
not specialization of the <code>completion_signatures&lt;&gt;</code>
class template. This makes it easier to use an algorithm&#39;s completion
signatures to communicate type errors from child senders.</p></li>
<li><p>For the customization points <code>let_value</code>,
<code>let_error</code>, and <code>let_stopped</code>, mandate that the
callable&#39;s possible return types all satisfy
<code>sender</code>.</p></li>
<li><p>Change <em>Requires:</em> to <em>Mandates</em>: for algorithms
that eagerly connect senders.</p></li>
</ul>
<p><strong>R0</strong>:</p>
<ul>
<li>Original revision</li>
</ul>
<h2 id="improving-early-diagnostics">Improving early diagnostics</h2>
<h3 id="problem-description">Problem Description</h3>
<p>Type-checking a sender expression involves computing its completion
signatures. In the general case, a sender&#39;s completion signatures may
depend on the receiver&#39;s execution environment. For example, the
sender:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>read_env<span class="op">(</span>get_stop_token<span class="op">)</span></span></code></pre></div>
<p>... when connected to a receiver <code>rcvr</code> and started, will
fetch the stop token from the receiver&#39;s environment and then pass it
back to the receiver, as follows:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> st <span class="op">=</span> get_stop_token<span class="op">(</span>get_env<span class="op">(</span>rcvr<span class="op">));</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>set_value<span class="op">(</span>move<span class="op">(</span>rcvr<span class="op">),</span> move<span class="op">(</span>st<span class="op">));</span></span></code></pre></div>
<p>Without an execution environment, the sender
<code>read_env(get_stop_token)</code> doesn&#39;t know how it will
complete.</p>
<p>The type of the environment is known rather late, when the sender is
connected to a receiver. This is often far from where the sender
expression was constructed. If there are type errors in a sender
expression, those errors will be diagnosed far from where the error was
made, which makes it harder to know the source of the problem.</p>
<p>It would be far preferable to issue diagnostics while
<em>constructing</em> the sender rather than waiting until it is
connected to a receiver.</p>
<h3 id="non-dependent-senders">Non-dependent senders</h3>
<p>The majority of senders have completions that do not depend on the
receiver&#39;s environment. Consider <code>just(42)</code> -- it will
complete with the integer <code>42</code> no matter what receiver it is
connected to. If a so-called &quot;non-dependent&quot; sender advertised itself as
such, then sender algorithms could eagerly type-check the non-dependent
senders they are passed, giving immediate feedback to the developer.</p>
<p>For example, this expression should be immediately rejected:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>just<span class="op">(</span><span class="dv">42</span><span class="op">)</span> <span class="op">|</span> then<span class="op">([](</span><span class="dt">int</span><span class="op">*</span> p<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> <span class="op">*</span>p<span class="op">;</span> <span class="op">})</span></span></code></pre></div>
<p>The <code>then</code> algorithm can reject <code>just(42)</code> and
the above lambda because the arguments don&#39;t match: an integer cannot be
passed to a function expecting an <code>int*</code>. The
<code>then</code> algorithm can do that type-checking only when it knows
the input sender is non-dependent. It couldn&#39;t, for example, do any
type-checking if the input sender were
<code>read_env(get_stop_token)</code> instead of
<code>just(42)</code>.</p>
<p>And in fact, some senders <em>do</em> advertise themselves as
non-dependent, although the sender algorithms in ([exec]) do not
currently do anything with that extra information. A sender can declare
its completions signatures with a nested type alias, as follows:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> T<span class="op">&gt;</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> just_sender <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  T value<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">using</span> completion_signatures <span class="op">=</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>execution::completion_signatures<span class="op">&lt;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>      <span class="bu">std::</span>execution::set_value_t<span class="op">(</span>T<span class="op">)</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">&gt;;</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>  <span class="co">// ...</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>Senders whose completions depend on the execution environment cannot
declare their completion signatures this way. Instead, they must define
a <code>get_completion_signatures</code> customization that takes the
environment as an argument.</p>
<p>We can use this extra bit of information to define a
<code>non_dependent_sender</code> concept as follows:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> Sndr<span class="op">&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">concept</span> non_dependent_sender <span class="op">=</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  sender<span class="op">&lt;</span>Sndr<span class="op">&gt;</span> <span class="op">&amp;&amp;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> <span class="op">{</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">typename</span> <span class="dt">remove_reference_t</span><span class="op">&lt;</span>Sndr<span class="op">&gt;::</span>completion_signatures<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">};</span></span></code></pre></div>
<p>A sender algorithm can use this concept to conditionally dispatch to
code that does eager type-checking.</p>
<h3 id="suggested-solution">Suggested Solution</h3>
<p>The author suggests that this notion of non-dependent senders be
given fuller treatment in <code>std::execution</code>. Conditionally
defining the nested typedef in generic sender adaptors -- which may
adapt either dependent or non-dependent senders -- is awkward and
verbose. We suggest instead to support calling
<code>get_completion_signatures</code> either with <em>or without</em>
an execution environment. This makes it easier for authors of sender
adaptors to preserve the &quot;non-dependent&quot; property of the senders it
wraps.</p>
<p>We suggest that a similar change be made to the
<code>completion_signatures_of_t</code> alias template. When
instantiated with only a sender type, it should compute the
non-dependent completion signatures, or be ill-formed.</p>
<h3 id="comparison-table">Comparison Table</h3>
<p>Consider the following code, which contains a type error:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> work <span class="op">=</span> just<span class="op">(</span><span class="dv">42</span><span class="op">)</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> then<span class="op">([](</span><span class="dt">int</span><span class="op">*</span> p<span class="op">)</span> <span class="op">{</span> <span class="co">// &lt;&lt;&lt; ERROR here</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>              <span class="co">//...</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>            <span class="op">});</span></span></code></pre></div>
<p>The table below shows the result of compiling this code both before
the proposed change and after:</p>
<table style="table-layout: fixed; width: 100%;">
<thead>
<tr>
<th style="width:30%">

<p>Before</p>
</th>
<th>

<p>After</p>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>

<p><em>no error</em></p>
</td>
<td>

<pre class="sourceCode">
error: static_assert failed due to requirement &#39;_is_completion_signatures&lt;
ustdex::ERROR&lt;ustdex::WHERE (ustdex::IN_ALGORITHM, ustdex::then_t), ustdex
::WHAT (ustdex::FUNCTION_IS_NOT_CALLABLE), ustdex::WITH_FUNCTION ((lambda
at hello.cpp:57:18)), ustdex::WITH_ARGUMENTS (int)&gt;&gt;&#39;
    static_assert(_is_completion_signatures&lt;_completions&gt;);
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
</pre>

</td>
</tr>
</tbody>
</table>

<p>This error was generated with with <a href="https://github.com/ericniebler/ustdex">µstdex</a> library and
Clang-13.</p>
<h3 id="design-considerations">Design Considerations</h3>
<h4 id="why-have-two-ways-for-non-dependent-senders-to-publish-their-completion-signatures">Why
have two ways for non-dependent senders to publish their completion
signatures?</h4>
<p>The addition of support for a customization of
<code>get_completion_signatures</code> that does not take an environment
obviates the need to support the use of a nested
<code>::completion_signatures</code> alias. In a class, this:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> get_completion_signatures<span class="op">()</span> <span class="op">-&gt;</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>execution::completion_signatures<span class="op">&lt;</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">std::</span>execution::set_value_t<span class="op">(</span>T<span class="op">)</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">&gt;;</span></span></code></pre></div>
<p>... works just as well as this:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> completion_signatures <span class="op">=</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>execution::completion_signatures<span class="op">&lt;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">std::</span>execution::set_value_t<span class="op">(</span>T<span class="op">)</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">&gt;;</span></span></code></pre></div>
<p>Without a doubt, we could simplify the design by dropping support for
the latter. This paper suggests retaining it, though. For something like
the <code>just_sender</code>, providing type metadata with an alias is
more idiomatic and less surprising, in the author&#39;s opinion, than
defining a function and putting the metadata in the return type. That is
the case for keeping the
<code>typename Sndr::completion_signatures</code> form.</p>
<p>The case for adding the <code>sndr.get_completion_signatures()</code>
form is that it makes it simpler for sender adaptors such as
<code>then_sender</code> to preserve the &quot;non-dependent&quot; property of the
senders it adapts. For instance, one could define
<code>then_sender</code> like:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> Sndr<span class="op">,</span> <span class="kw">class</span> Fun<span class="op">&gt;</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> then_sender <span class="op">{</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    Sndr <span class="va">sndr_</span><span class="op">;</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    Fun <span class="va">fun_</span><span class="op">;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span><span class="op">...</span> Env<span class="op">&gt;</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> get_completion_signatures<span class="op">(</span>Env<span class="op">&amp;&amp;...</span> env<span class="op">)</span> <span class="at">const</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>      <span class="op">-&gt;</span> some<span class="op">-</span>computed<span class="op">-</span>type<span class="op">;</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">//....</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>... and with this one member function support both dependent and
non-dependent senders while preserving the &quot;non-dependent-ness&quot; of the
adapted sender.</p>
<h2 id="proposed-wording">Proposed Wording</h2>
<p><span class="ednote">This proposed wording is based on the current
working draft.</span></p>
<p><span class="ednote">Change [async.ops]/13 as follows:</span></p>
<blockquote>

<ol start="13" type="1">
<li>A completion signature is a function type that describes a
completion operation. An asychronous operation has a finite set of
possible completion signatures corresponding to the completion
operations that the asynchronous operation potentially evaluates
([basic.def.odr]). For a completion function <code>set</code>, receiver
<code>rcvr</code>, and pack of arguments <code>args</code>, let
<code>c</code> be the completion operation
<code>set(rcvr, args...)</code>, and let <code>F</code> be the function
type <code>decltype(auto(set))(decltype((args))...)</code>. A completion
signature <code>Sig</code> is associated with <code>c</code> if and only
if <code>MATCHING-SIG(Sig, F)</code> is <code>true</code>
([exec.general]). Together, a sender type and an environment type
<code>Env</code> determine the set of completion signatures of an
asynchronous operation that results from connecting the sender with a
receiver that has an environment of type <code>Env</code>. The type of
the receiver does not affect an asychronous operation’s completion
signatures, only the type of the receiver’s environment. <ins>A sender
type whose completion signatures are knowable independent of an
execution environment is known as a <dfn>non-dependent
sender</dfn>.</ins></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.syn] as follows:</span></p>
<blockquote>
<blockquote>
<pre class="sourceCode">
<span style="color:blue">...</span>
template&lt;class Sndr, class<ins>...</ins> Env <del>= env&lt;&gt;</del>&gt;
  concept sender_in = <em>see below</em>;
<span style="color:blue">...</span>
 
template&lt;class Sndr, class<ins>...</ins> Env <del>= env&lt;&gt;</del>&gt;
   requires sender_in&lt;Sndr, Env<ins>...</ins>&gt;
  using completion_signatures_of_t = <em>call-result-t</em>&lt;get_completion_signatures_t, Sndr, Env<ins>...</ins>&gt;;
<span style="color:blue">...</span>
 
template&lt;class Sndr, class<ins>...</ins> Env&gt;
  using <em>single-sender-value-type</em> = <em>see below</em>;                  <em>// exposition only</em>
 
template&lt;class Sndr, class<ins>...</ins> Env&gt;
  concept <em>single-sender</em> = <em>see below</em>;                           <em>// exposition only</em>
<span style="color:blue">...</span>
</pre>
</blockquote>

<ol type="1">
<li><p>The exposition-only type
<code><em>variant-or-empty</em>&lt;Ts...&gt;</code> is defined as
follows <span style="color:blue">... as before</span></p></li>
<li><p>For types <code>Sndr</code> and <ins>pack</ins> <code>Env</code>,
<ins>let <code>CS</code> be
<code>completion_signatures_of_t&lt;Sndr, Env...&gt;</code>. Then</ins>
<code><em>single-sender-value-type</em>&lt;Sndr,
Env<ins>...</ins>&gt;</code> is <ins>ill-formed if <code>CS</code> is
ill-formed or if <code>sizeof...(Env) &gt; 1</code> is
<code>true</code>; otherwise, it is</ins> an alias for:</p>
<ul>
<li><p><del><code>value_types_of_t&lt;Sndr,
Env</del><ins><em>gather-signatures</em>&lt;set_value_t, CS</ins>,
decay_t, type_identity_t&gt;</code> if that type is
well-formed,</p></li>
<li><p>Otherwise, <code>void</code> if
<code><del>value_types_of_t&lt;Sndr,
Env</del><ins><em>gather-signatures</em>&lt;set_value_t, CS</ins>,
tuple, variant&gt;</code> is <code>variant&lt;tuple&lt;&gt;&gt;</code>
or <code>variant&lt;&gt;</code>,</p></li>
<li><p>Otherwise, <code><del>value_types_of_t&lt;Sndr,
Env</del><ins><em>gather-signatures</em>&lt;set_value_t, CS</ins>,
<em>decayed-tuple</em>, type_identity_t&gt;</code> if that type is
well-formed,</p></li>
<li><p>Otherwise, <code><em>single-sender-value-type</em>&lt;Sndr,
Env<ins>...</ins>&gt;</code> is ill-formed.</p></li>
</ul></li>
<li><p>The exposition-only concept <em><code>single-sender</code></em>
is defined as follows:</p>
<blockquote><pre class="sourceCode">
namespace std::execution {
  template&lt;class Sndr, class<ins>...</ins> Env&gt;
    concept <em>single-sender</em> = sender_in&lt;Sndr, Env<ins>...</ins>&gt; &amp;&amp;
      requires {
        typename <em>single-sender-value-type</em>&lt;Sndr, Env<ins>...</ins>&gt;;
      };
}
</pre></blockquote>
</li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.snd.general] para 1 as
follows:</span></p>
<blockquote>

<ol type="1">
<li><p>Subclauses [exec.factories] and [exec.adapt] define customizable
algorithms that return senders. Each algorithm has a default
implementation. Let <code>sndr</code> be the result of an invocation of
such an algorithm or an object equal to the result
([concepts.equality]), and let <code>Sndr</code> be
<code>decltype((sndr))</code>. Let <code>rcvr</code> be a receiver of
type <code>Rcvr</code> with associated environment <code>env</code> of
type <code>Env</code> such that <code>sender_to&lt;Sndr, Rcvr&gt;</code>
is <code>true</code>. For the default implementation of the algorithm
that produced <code>sndr</code>, connecting <code>sndr</code> to
<code>rcvr</code> and starting the resulting operation state
([exec.async.ops]) necessarily results in the potential evaluation
([basic.def.odr]) of a set of completion operations whose first argument
is a subexpression equal to <code>rcvr</code>. Let <code>Sigs</code> be
a pack of completion signatures corresponding to this set of completion
operations<del>. Then</del> <ins>, and let <code>CS</code> be</ins> the
type of the expression <code>get_completion_signatures(sndr, env)</code>
<ins>. Then <code>CS</code></ins> is a specialization of the class
template <code>completion_signatures</code> ([exec.util.cmplsig]), the
set of whose template arguments is <code>Sigs</code>. <ins>If none of
the types in <code>Sigs</code> are dependent on the type
<code>Env</code>, then the expression
<code>get_completion_signatures(sndr)</code> is well-formed and its type
is <code>CS</code>.</ins> If a user-provided implementation of the
algorithm that produced <code>sndr</code> is selected instead of the
default: <span class="ednote">Reformatted into a list.</span></p>
<ul>
<li><p>Any completion signature that is in the set of types denoted by
<code>completion_signatures_of_t&lt;Sndr, Env&gt;</code> and that is not
part of <code>Sigs</code> shall correspond to error or stopped
completion operations, unless otherwise specified.</p></li>
<li><p><ins>If none of the types in <code>Sigs</code> are dependent on
the type <code>Env</code>, then
<code>completion_signatures_of_t&lt;Sndr&gt;</code> and
<code>completion_signatures_of_t&lt;Sndr, Env&gt;</code> shall denote
the same type.<ins></p></li>
</ul></li>
</ol>
</blockquote>

<p><span class="ednote">In [exec.snd.expos] para 24, change the
definition of the exposition-only templates
<em><code>completion-signatures-for</code></em> and
<em><code>basic-sender</code></em> as follows:</p>
<blockquote>
<blockquote>
<pre class="sourceCode">
  template&lt;class Sndr, class<ins>...</ins> Env&gt;
  using <em>completion-signatures-for</em> = <em>see below</em>;                   <em>// exposition only</em>
 
  template&lt;class Tag, class Data, class... Child&gt;
  struct <em>basic-sender</em> : <em>product-type</em>&lt;Tag, Data, Child...&gt; {    <em>// exposition only</em>
    using sender_concept = sender_t;
    using <em>indices-for</em> = index_sequence_for&lt;Child...&gt;;       <em>// exposition only</em>
 
    decltype(auto) get_env() const noexcept {
      auto&amp; [_, data, ...child] = *this;
      return <em>impls-for</em>&lt;Tag&gt;::<em>get-attrs</em>(data, child...);
    }
 
    template&lt;<em>decays-to</em>&lt;<em>basic-sender</em>&gt; Self, receiver Rcvr&gt;
    auto connect(this Self&amp;&amp; self, Rcvr rcvr) noexcept(<em>see below</em>)
      -&gt; <em>basic-operation</em>&lt;Self, Rcvr&gt; {
      return {std::forward&lt;Self&gt;(self), std::move(rcvr)};
    }
 
    template&lt;<em>decays-to</em>&lt;<em>basic-sender</em>&gt; Self, class<ins>...</ins> Env&gt;
    auto get_completion_signatures(this Self&amp;&amp; self, Env&amp;&amp;<ins>...</ins> env) noexcept
      -&gt; <em>completion-signatures-for</em>&lt;Self, Env<ins>...</ins>&gt; {
      return {};
    }
  };
</pre>
</blockquote>
</blockquote>

<p><span class="ednote">Change [exec.snd.expos] para 39 as follows (this
includes the proposed resolution of <a href="https://github.com/cplusplus/sender-receiver/issues/307">cplusplus/sender-receiver#307</a>):</span></p>
<blockquote>

<div class="del">

<ol start="39" type="1">
<li>For a subexpression <code>sndr</code> let <code>Sndr</code> be
<code>decltype((sndr))</code>. Let <code>rcvr</code> be a receiver with
an associated environment of type <code>Env</code> such that
<code>sender_in&lt;Sndr, Env&gt;</code> is <code>true</code>.
<code><em>completion-signatures-for</em>&lt;Sndr, Env&gt;</code> denotes
a specialization of <code>completion_signatures</code>, the set of whose
template arguments correspond to the set of completion operations that
are potentially evaluated as a result of starting ([exec.async.ops]) the
operation state that results from connecting <code>sndr</code> and
<code>rcvr</code>. When <code>sender_in&lt;Sndr, Env&gt;</code> is
<code>false</code>, the type denoted by
<code><em>completion-signatures-for</em>&lt;Sndr, Env&gt;</code>, if
any, is not a specialization of
<code>completion_signatures</code>.<br />
<br />
<em>Recommended practice</em>: When
<code>sender_in&lt;Sndr, Env&gt;</code> is <code>false</code>,
implementations are encouraged to use the type denoted by
<code><em>completion-signatures-for</em>&lt;Sndr, Env&gt;</code> to
communicate to users why.</li>
</ol>
</div>

<div class="ins">

<ol start="39" type="1">
<li>Let <code>Sndr</code> be a (possibly <code>const</code>-qualified)
specialization <code><em>basic-sender</em></code> or an lvalue reference
of such, let <code>Rcvr</code> be the type of a receiver with an
associated environment of type <code>Env</code>. If the type
<code><em>basic-operation</em>&lt;Sndr, Rcvr&gt;</code> is well-formed,
let <code>op</code> be an lvalue subexpression of that type. Then
<code><em>completion-signatures-for</em>&lt;Sndr, Env&gt;</code> denotes
a specialization of <code>completion_signatures</code>, the set of whose
template arguments corresponds to the set of completion operations that
are potentially evaluated ([basic.def.odr]) as a result of evaluating
<code>op.start()</code>. Otherwise,
<code><em>completion-signatures-for</em>&lt;Sndr, Env&gt;</code> is
ill-formed. If <code><em>completion-signatures-for</em>&lt;Sndr,
Env&gt;</code> is well-formed and its type is not dependent upon the
type <code>Env</code>,
<code><em>completion-signatures-for</em>&lt;Sndr&gt;</code> is
well-formed and denotes the same type; otherwise,
<code><em>completion-signatures-for</em>&lt;Sndr&gt;</code> is
ill-formed.</li>
</ol>
</div>

</blockquote>

<p><span class="ednote">Change the <code>sender_in</code> concept in
[exec.snd.concepts] para 1 as follows:</span></p>
<blockquote><pre class="sourceCode">
template&lt;class Sndr, class<ins>...</ins> Env <del>= env&lt;&gt;</del>&gt;
  concept sender_in =
    sender&lt;Sndr&gt; &amp;&amp;
    <ins>(sizeof...(Env) &lt;= 1)</ins>
    <ins>(</ins>queryable&lt;Env&gt;<ins> &amp;&amp;...)</ins> &amp;&amp;
    requires (Sndr&amp;&amp; sndr, Env&amp;&amp;<ins>...</ins> env) {
      { get_completion_signatures(std::forward&lt;Sndr&gt;(sndr), std::forward&lt;Env&gt;(env)<ins>...</ins>) }
        -&gt; <em>valid-completion-signatures</em>;
    };
</pre></blockquote>

<p><span class="ednote">This subtly changes the meaning of
<code>sender_in&lt;Sndr&gt;</code>. Before the change, it tests whether
a type is a sender when used specifically with the environment
<code>env&lt;&gt;</code>. After the change, it tests whether a type is a
non-dependent sender. This is a stronger assertion to make about the
type; it says that this type is a sender <em>regardless of the
environment</em>. One can still get the old behavior with
<code>sender_in&lt;Sndr, env&lt;&gt;&gt;</code>.</span></p>
<p><span class="ednote">Change [exec.snd.concepts] para 4 as follows (so
that the exposition-only <em><code>sender-of</code></em> concept tests
for sender-ness with no environment as opposed to the empty environment,
<code>env&lt;&gt;</code>):</span></p>
<blockquote>

<ol start="4" type="1">
<li>The exposition-only concepts <em><code>sender-of</code></em> and
<em><code>sender-in-of</code></em> define the requirements for a sender
type that completes with a given unique set of value result types.</li>
</ol>
<blockquote><pre class="sourceCode">
namespace std::execution {
  template&lt;class... As&gt;
    using <em>value-signature</em> = set_value_t(As...);      <em>// exposition only</em>
<div class="del">
  template&lt;class Sndr, class Env, class... Values&gt;
    concept <em>sender-in-of</em> =
      sender_in&lt;Sndr, Env&gt; &amp;&amp;
      <em>MATCHING-SIG</em>(                     <em>// see [exec.general]</em>
        set_value_t(Values...),
        value_types_of_t&lt;Sndr, Env, <em>value-signature</em>, type_identity_t&gt;);
 
  template&lt;class Sndr, class... Values&gt;
    concept <em>sender-of</em> = <em>sender-in-of</em>&lt;Sndr, env&lt;&gt;, Values...&gt;;
</div><div class="ins">
  template&lt;class Sndr, class SetValue, class... Env&gt;
    concept <em>sender-in-of-impl</em> =         <em>// exposition only</em>
      sender_in&lt;Sndr, Env...&gt; &amp;&amp;
      <em>MATCHING-SIG</em>(SetValue,                          <em>// see [exec.general]</em>        
                   <em>gather-signatures</em>&lt;set_value_t,     <em>// see [exec.util.cmplsig]</em>
                                     completion_signatures_of_t&lt;Sndr, Env...&gt;,
                                     <em>value-signature</em>,
                                     type_identity_t&gt;);
 
  template&lt;class Sndr, class Env, class... Values&gt;
    concept <em>sender-in-of</em> =              <em>// exposition only</em>
      <em>sender-in-of-impl</em>&lt;Sndr, set_value_t(Values...), Env&gt;;
 
  template&lt;class Sndr, class... Values&gt;
    concept <em>sender-of</em> =                 <em>// exposition only</em>
      <em>sender-in-of-impl</em>&lt;Sndr, set_value_t(Values...)&gt;;
</div>
}
</pre></blockquote>

</blockquote>

<p><span class="ednote">Change [exec.awaitables] p 1-4 as
follows:</span></p>
<blockquote>

<ol type="1">
<li><p>The sender concepts recognize awaitables as senders. For [exec],
an <em>awaitable</em> is an expression that would be well-formed as the
operand of a <code>co_await</code> expression within a given
context.</p></li>
<li><p>For a subexpression <code>c</code>, let
<code>GET-AWAITER(c, p)</code> be expression-equivalent to the series of
transformations and conversions applied to <code>c</code> as the operand
of an <em>await-expression</em> in a coroutine, resulting in lvalue
<code>e</code> as described by [expr.await], where <code>p</code> is an
lvalue referring to the coroutine’s promise, which has type
<code>Promise</code>.</p>
<p>[<em>Note 1</em>: This includes the invocation of the promise type’s
<code>await_transform</code> member if any, the invocation of the
<code>operator co_await</code> picked by overload resolution if any, and
any necessary implicit conversions and materializations. -- <em>end
note</em>]</p>
<p><ins>Let <code>GET-AWAITER(c)</code> be expression-equivalent to
<code>GET-AWAITER(c, q)</code> where <code>q</code> is an lvalue of an
unspecified empty class type <em><code>none-such</code></em> that lacks
an <code>await_transform</code> member, and where
<code>coroutine_handle&lt;<em>none-such</em>&gt;</code> behaves as
<code>coroutine_handle&lt;void&gt;</code>.</ins></p></li>
<li><p>Let <em><code>is-awaitable</code></em> be the following
exposition-only concept:</p>
 <pre class="sourceCode">
     template&lt;class T&gt;
     concept <em>await-suspend-result</em> = <em>see below</em>;

     template&lt;class A, class<ins>...</ins> Promise&gt;
     concept <em>is-awaiter</em> = <em>// exposition only</em>
         requires (A&amp; a, coroutine_handle&lt;Promise<ins>...</ins>&gt; h) {
             a.await_ready() ? 1 : 0;
             { a.await_suspend(h) } -&gt; <em>await-suspend-result</em>;
             a.await_resume();
         };

     template&lt;class C, class<ins>...</ins> Promise&gt;
     concept <em>is-awaitable</em> =
         requires (C (*fc)() noexcept, Promise&amp;<ins>...</ins> p) {
             { <em>GET-AWAITER</em>(fc(), p<ins>...</ins>) } -&gt; <em>is-awaiter</em>&lt;Promise<ins>...</ins>&gt;;
         };
 </pre>

<p><code><em>await-suspend-result</em>&lt;T&gt;</code> is
<code>true</code> if and only if one of the following is
<code>true</code>:</p>
<ul>
<li><code>T</code> is <code>void</code>, or</li>
<li><code>T</code> is <code>bool</code>, or</li>
<li><code>T</code> is a specialization of
<code>coroutine_handle</code>.</li>
</ul></li>
<li><p>For a subexpression <code>c</code> such that
<code>decltype((c))</code> is type <code>C</code>, and an lvalue
<code>p</code> of type <code>Promise</code>,
<code><em>await-result-type</em>&lt;C, Promise&gt;</code> denotes the
type <code>decltype(<em>GET-AWAITER</em>(c, p).await_resume())</code>
<ins> and <code><em>await-result-type</em>&lt;C&gt;</code> denotes the
type
<code>decltype(<em>GET-AWAITER</em>(c).await_resume())</code></ins>.</p></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.getcomplsigs] as
follows:</span></p>
<blockquote>

<ol type="1">
<li><p><code>get_completion_signatures</code> is a customization point
object. Let <code>sndr</code> be an expression such that
<code>decltype((sndr))</code> is <code>Sndr</code>, and let
<code>env</code> be <del>an expression such that
<code>decltype((env))</code> is <code>Env</code></del> <ins>a pack of
zero or one expression</ins>. <del>Let</del><ins>If
<code>sizeof...(env) == 0</code> is <code>true</code>, let
<code>new_sndr</code> be <code>sndr</code>; otherwise, let</ins>
<code>new_sndr</code> be the expression
<code>transform_sender(decltype(<em>get-domain-late</em>(sndr,
env<ins>...</ins>)){}, sndr, env<ins>...</ins>)</code><ins>.</ins><del>,
and let</del><ins> Let</ins> <code>NewSndr</code> be
<code>decltype((new_sndr))</code>. Then
<code>get_completion_signatures(sndr, env<ins>...</ins>)</code> is
expression-equivalent to <code>(void(sndr), void(env)<ins>...</ins>,
CS())</code> except that <code>void(sndr)</code> and
<code>void(env)<ins>...</ins></code> are indeterminately sequenced,
where <code>CS</code> is:</p>
<ol type="1">
<li><p><code>decltype(new_sndr.get_completion_signatures(env<ins><code>...</code></ins>))</code>
if that type is well-formed,</p></li>
<li><p><ins>Otherwise, if <code>sizeof...(env) == 1</code> is
<code>true</code>, then
<code>decltype(new_sndr.get_completion_signatures())</code> if that
expression is well-formed,</ins></p></li>
<li><p>Otherwise,
<code>remove_cvref_t&lt;NewSndr&gt;::completion_signatures</code> if
that type is well-formed,</p></li>
<li><p>Otherwise, if <code><em>is-awaitable</em>&lt;NewSndr,
<em>env-promise</em>&lt;<del>Env</del><ins>decltype((env))</ins>&gt;<ins>...</ins>&gt;</code>
is <code>true</code>, then:</p></li>
</ol>
   <blockquote>
     <pre class="sourceCode">
   completion_signatures&lt;
      <em>SET-VALUE-SIG</em>(<em>await-result-type</em>&lt;NewSndr, <em>env-promise</em>&lt;<del>Env</del><ins>decltype((env))</ins>&gt;<ins>...</ins>&gt;), // see [exec.snd.concepts]
      set_error_t(exception_ptr),
      set_stopped_t()&gt;
     </pre>
   </blockquote>

<ol start="5" type="1">
<li>Otherwise, <code>CS</code> is ill-formed.</li>
</ol></li>
</ol>
<div class="ins">

<ol start="2" type="1">
<li><p>If <code>get_completion_signatures(sndr)</code> is well-formed
and its type denotes a specialization of the
<code>completion_signatures</code> class template, then
<code>Sndr</code> is a non-dependent sender type ([async.ops]).</p></li>
<li><p>Given a type <code>Env</code>, if
<code>completion_signatures_of_t&lt;Sndr&gt;</code> and
<code>completion_signatures_of_t&lt;Sndr, Env&gt;</code> are both
well-formed, they shall denote the same type.</p></li>
</ol>
</div>

<ol start="4" type="1">
<li>Let <code>rcvr</code> be an rvalue whose type <code>Rcvr</code>
<span style="color:blue"><em>...as before</em></span></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.adapt.general] as
follows:</span></p>
<blockquote>

<ul class="dash">

<li><span class="marginalizedparent">(3.4)</span>When a parent sender is
connected to a receiver <code>rcvr</code>, any receiver used to connect a child
sender has an associated environment equal to
<code><em>FWD-ENV</em>(get_env(rcvr))</code>.</li>

<li><span class="marginalizedparent"><ins>(3.5)</ins></span><ins>An adaptor
whose child senders are all non-dependent ([async.ops]) is itself non-dependent.
</ins></li>

<li><p><span class="marginalizedparent">(3.<ins>6</ins>)</span>These
requirements apply to any function that is selected by the implementation of the
sender adaptor.</p></span>

<li><span class="marginalizedparent"><ins>(3.7)</ins></span><ins><em>Recommended
practice</em>: Implementors are encouraged to use the completion signatures of
the adaptors to communicate type errors to users and to propagate any such type
errors from child senders.</ins></li>

</ul>

</blockquote>

<p><span class="ednote">Change [exec.then] as follows:</span></p>
<blockquote>

<ol start="2" type="1">
<li>The names <code>then</code>, <code>upon_error</code>, and
<code>upon_stopped</code> denote pipeable sender adaptor objects.
<ins>For <code>then</code>, <code>upon_error</code>, and
<code>upon_stopped</code>, let <em><code>set-cpo</code></em> be
<code>set_value</code>, <code>set_error</code>, and
<code>set_stopped</code> respectively.</ins> Let the expression
<em><code>then-cpo</code></em> be one of <code>then</code>,
<code>upon_error</code>, or <code>upon_stopped</code>. For
subexpressions <code>sndr</code> and <code>f</code>, if
<code>decltype((sndr))</code> does not satisfy <code>sender</code>, or
<code>decltype((f))</code> does not satisfy
<em><code>movable-value</code></em>, <code><em>then-cpo</em>(sndr,
f)</code> is ill-formed.</li>
</ol>
<div class="ins">

<ol start="3" type="1">
<li>Otherwise, let <em><code>invoke-result</code></em> be an alias
template such that <code><em>invoke-result</em>&lt;Ts...&gt;</code>
denotes the type <code>invoke_result_t&lt;F, Ts...&gt;</code> where
<code>F</code> is the decayed type of <code>f</code>. The expression
<code><em>then-cpo</em>(sndr, f)</code> mandates that either
<code>sender_in&lt;Sndr&gt;</code> is <code>false</code> or the type
<code><em>gather-signatures</em>&lt;<em>decayed-typeof</em>&lt;<em>set-cpo</em>&gt;,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is well-formed.</li>
</ol>
</div>

<ol start="4" type="1">
<li><p><del>Otherwise, the</del><ins>The</ins> expression
<code><em>then-cpo</em>(sndr, f)</code> is expression-equivalent to:</p>
<blockquote><pre class="sourceCode">
transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(<em>then-cpo</em>, f, sndr))
</pre></blockquote>
except that <code>sndr</code> is evaluated only once.
</li>
<li><p><del>For <code>then</code>, <code>upon_error</code>, and
<code>upon_stopped</code>, let <em><code>set-cpo</code></em> be
<code>set_value</code>, <code>set_error</code>, and
<code>set_stopped</code> respectively.</del> The exposition-only class
template <em><code>impls-for</code></em> ([exec.snd.general]) is
specialized for <em><code>then-cpo</code></em> as follows:
<span style="color:blue"><em>...as before</em></span></p></li>
</ol>
<div>
</blockquote>

<p><span class="ednote">Change [exec.let] by inserting a new paragraph
between (3) and (4) as follows:</span></p>
<blockquote>
<div class="ins">

<ol start="4" type="1">
<li>Let <em><code>invoke-result</code></em> be an alias template such
that <code><em>invoke-result</em>&lt;Ts...&gt;</code> denotes the type
<code>invoke_result_t&lt;F, Ts...&gt;</code> where <code>F</code> is the
decayed type of <code>f</code>. The expression
<code><em>let-cpo</em>(sndr, f)</code> mandates that either
<code>sender_in&lt;Sndr&gt;</code> is <code>false</code> or the type
<code><em>gather-signatures</em>&lt;<em>decayed-typeof</em>&lt;<em>set-cpo</em>&gt;,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is well-formed and that the types in the
resulting type list all satisfy <code>sender</code>.</li>
</ol>
</div>

<ol start="5" type="1">
<li><del>Otherwise, the</del><ins>The</ins> expression
<code><em>let-cpo</em>(sndr, f)</code> is expression-equivalent to:
<blockquote><pre class="sourceCode">
transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(<em>let-cpo</em>, f, sndr))
</pre></blockquote>
except that <code>sndr</code> is evaluated only once.
</li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.bulk] by inserting a new paragraph
between (1) and (2) as follows:</span></p>
<blockquote>
<div class="ins">

<ol start="2" type="1">
<li>Let <em><code>invoke-result</code></em> be an alias template such
that <code><em>invoke-result</em>&lt;Ts...&gt;</code> denotes the type
<code>invoke_result_t&lt;F, Shape, Ts...&gt;</code> where <code>F</code>
is the decayed type of <code>f</code>. The expression
<code>bulk(sndr, f)</code> mandates that either
<code>sender_in&lt;Sndr&gt;</code> is <code>false</code> or the type
<code><em>gather-signatures</em>&lt;set_value_t,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is well-formed.</li>
</ol>
</div>

<ol start="3" type="1">
<li><del>Otherwise, the</del><ins>The</ins> expression
<code>bulk(sndr, shape, f)</code> is expression-equivalent to:
<blockquote><pre class="sourceCode">
transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(bulk, <em>product-type</em>{shape f}, sndr))
</pre></blockquote>
except that <code>sndr</code> is evaluated only once.
</li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.split] as follows:</span></p>
<blockquote>

<ol start="3" type="1">
<li><p>The name <code>split</code> denotes a pipeable sender adaptor
object. For a subexpression <code>sndr</code>, let <code>Sndr</code> be
<code>decltype((sndr))</code>. <del>If</del><ins> The expression
<code>split(sndr)</code> mandates that</ins> <code>sender_in&lt;Sndr,
<em>split-env</em>&gt;</code> is <ins><code>true</code></ins>
<del><code>false</code>, <code>split(sndr)</code> is
ill-formed</del>.</p></li>
<li><p><del>Otherwise, the</del><ins>The</ins> expression
<code>split(sndr)</code> is expression-equivalent to:</p>
<blockquote><pre class="sourceCode">
transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(split, {}, sndr))
</pre></blockquote>
except that <code>sndr</code> is evaluated only once.

<p>[<em>Note 1</em>: The default implementation of
<code>transform_sender</code> will have the effect of connecting the
sender to a receiver. It will return a sender with a different tag type.
-- <em>end note</em>]</p></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.stopped.opt] as follows:</span></p>
<blockquote>

<ol start="2" type="1">
<li><p>The name <code>stopped_as_optional</code> denotes a pipeable
sender adaptor object. For a subexpression <code>sndr</code>, let
<code>Sndr</code> be <code>decltype((sndr))</code>. <ins>The expression
<code>stopped_as_optional(sndr)</code> mandates that
<code>!sender_in&lt;Sndr&gt; ||
<em>single-sender</em>&lt;Sndr&gt;</code> is <code>true</code>.</ins>
The expression <code>stopped_as_optional(sndr)</code> is
expression-equivalent to:</p>
<blockquote><pre class="sourceCode">
transform_sender(<em>get-domain-early</em>(sndr), <em>make-sender</em>(stopped_as_optional, {}, sndr))
</pre></blockquote>

<p>except that <code>sndr</code> is only evaluated once.</p></li>
<li><p>Let <code>sndr</code> and <code>env</code> be subexpressions such
that <code>Sndr</code> is <code>decltype((sndr))</code> and
<code>Env</code> is <code>decltype((env))</code>. If
<code><em>sender-for</em>&lt;Sndr, stopped_as_optional_t&gt;</code> is
<code>false</code><del>, or if the type
<code><em>single-sender-value-type</em>&lt;Sndr, Env&gt;</code> is
ill-formed or <code>void</code>,</del> then the expression
<code>stopped_as_optional.transform_sender(sndr, env)</code> is
ill-formed; <ins>otherwise, that expression mandates that the type
<code><em>single-sender-value-type</em>&lt;Sndr, Env&gt;</code> is
well-formed and not <code>void</code>, and</ins> <del>otherwise,
it</del> is equivalent to: <span style="color:blue">... as
before</span></p></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.sync.wait] as follows:</span></p>
<blockquote>

<ol start="4" type="1">
<li><p>The name <code>this_thread::sync_wait</code> denotes a
customization point object. For a subexpression <code>sndr</code>, let
<code>Sndr</code> be <code>decltype((sndr))</code>. <del>If
<code>sender_in&lt;Sndr, <i>sync-wait-env</i>&gt;</code> is
<code>false</code>, the expression
<code>this_thread::sync_wait(sndr)</code> is ill-formed. Otherwise,
it</del> <ins>The expression
<code>this_thread::sync_wait(sndr)</code></ins> is expression-equivalent
to the following, except that <code>sndr</code> is evaluated only
once:</p>
<blockquote><pre class="sourceCode">
apply_sender(<em>get-domain-early</em>(sndr), sync_wait, sndr)
</pre></blockquote>

<p><em>Mandates</em>:</p>
<ul class="dash">

<li><span class="marginalizedparent">(4.1)</span>
<ins><code>sender_in&lt;Sndr, <i>sync-wait-env</i>&gt;</code> is <code>true</code>.</ins></li>

<li><span class="marginalizedparent">(4.<ins>2</ins>)</span>
The type <code><em>sync-wait-result-type</em>&lt;Sndr&gt;</code> is well-formed.</li>

<li><span class="marginalizedparent">(4.<ins>3</ins>)</span>
<code>same_as&lt;decltype(<em>e</em>),
<em>sync-wait-result-type</em>&lt;Sndr&gt;&gt;</code> is <code>true</code>, where
<em><code>e</code></em> is the <code>apply_sender</code> expression above.</li>

</ul>

<p><span style="color:blue"><em>...as before</em></span></p></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.sync.wait.var] as
follows:</span></p>
<blockquote>

<ol type="1">
<li><p>The name <code>this_thread::sync_wait_with_variant</code> denotes
a customization point object. For a subexpression <code>sndr</code>, let
<code>Sndr</code> be <code>decltype(into_variant(sndr))</code>. <del>If
<code>sender_in&lt;Sndr, <i>sync-wait-env</i>&gt;</code> is
<code>false</code>, the expression
<code>this_thread::sync_wait(sndr)</code> is ill-formed. Otherwise,
it</del> <ins>The expression
<code>this_thread::sync_wait_with_variant(sndr)</code></ins> is
expression-equivalent to the following, except that <code>sndr</code> is
evaluated only once:</p>
<blockquote><pre class="sourceCode">
apply_sender(<em>get-domain-early</em>(sndr), sync_wait_with_variant, sndr)
</pre></blockquote>

<p><em>Mandates</em>:</p>
<ul class="dash">

<li><span class="marginalizedparent">(1.1)</span>
<ins><code>sender_in&lt;Sndr, <i>sync-wait-env</i>&gt;</code> is <code>true</code>.</ins></li>

<li><span class="marginalizedparent">(1.<ins>2</ins>)</span>
The type <code><em>sync-wait-with-variant-result-type</em>&lt;Sndr&gt;</code>
is well-formed.</li>

<li><span class="marginalizedparent">(1.<ins>3</ins>)</span>
<code>same_as&lt;decltype(<em>e</em>),
<em>sync-wait-with-variant-result-type</em>&lt;Sndr&gt;&gt;</code> is <code>true</code>,
where <em><code>e</code></em> is the <code>apply_sender</code> expression above.</li>

</ul>
</li>
<li><p><del>If <code><em>callable</em>&lt;sync_wait_t, Sndr&gt;</code>
is <code>false</code>, the expression
<code>sync_wait_with_variant.apply_sender(sndr)</code> is ill-formed.
Otherwise, it</del><ins>The expression
<code>sync_wait_with_variant.apply_sender(sndr)</code></ins> is
equivalent to <span style="color:blue"><em>...as before</em></span></p></li>
</ol>
</blockquote>

<p><span class="ednote">Change [exec.run.loop.types] para 5 as
follows:</span></p>
<blockquote>

<ol start="5" type="1">
<li><em><code>run-loop-sender</code></em> is an exposition-only type
that satisfies <code>sender</code>. <del> For any type
<code>Env</code>,</del> <code>completion_signatures_of_t&lt;
<em>run-loop-sender</em><del>, Env</del>&gt;</code> is
<code>completion_signatures&lt;set_value_t(), set_error_t(exception_ptr), set_stopped_t()&gt;</code>.</li>
</ol>
</blockquote>

<h2 id="acknowlegments">Acknowlegments</h2>
<p>We owe our thanks to Ville Voutilainen who first noticed that most
sender expressions could be type-checked eagerly but are not by
P2300R8.</p>
</body>
</html>
