<!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" />
  <title>Improving diagnostics for sender expressions</title>
  <style>
    html {
      color: #1a1a1a;
      background-color: #fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      overflow-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 12px;
      }
      h1 {
        font-size: 1.8em;
      }
    }
    @media print {
      html {
        background-color: white;
      }
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color: #1a1a1a;
    }
    a:visited {
      color: #1a1a1a;
    }
    img {
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid #e6e6e6;
      color: #606060;
    }
    code {
      font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
      font-size: 85%;
      margin: 0;
      hyphens: manual;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
      overflow-wrap: normal;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      background-color: #1a1a1a;
      border: none;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid #1a1a1a;
      border-bottom: 1px solid #1a1a1a;
    }
    th {
      border-top: 1px solid #1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC ul {
      padding-left: 1.3em;
    }
    #TOC > ul {
      padding-left: 0;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    div.columns{display: flex; gap: min(4vw, 1.5em);}
    div.column{flex: auto; overflow-x: auto;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    /* The extra [class] is a hack that increases specificity enough to
       override a similar rule in reveal.js */
    ul.task-list[class]{list-style: none;}
    ul.task-list li input[type="checkbox"] {
      font-size: inherit;
      width: 0.8em;
      margin: 0 0.8em 0.2em -1.6em;
      vertical-align: middle;
    }
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
    /* CSS for syntax highlighting */
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
    pre > code.sourceCode > span:empty { height: 1.2em; }
    .sourceCode { overflow: visible; }
    code.sourceCode > span { color: inherit; text-decoration: inherit; }
    div.sourceCode { margin: 1em 0; }
    pre.sourceCode { margin: 0; }
    @media screen {
    div.sourceCode { overflow: auto; }
    }
    @media print {
    pre > code.sourceCode { white-space: pre-wrap; }
    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
    }
    pre.numberSource code
      { counter-reset: source-line 0; }
    pre.numberSource code > span
      { position: relative; left: -4em; counter-increment: source-line; }
    pre.numberSource code > span > a:first-child::before
      { content: counter(source-line);
        position: relative; left: -1em; text-align: right; vertical-align: baseline;
        border: none; display: inline-block;
        -webkit-touch-callout: none; -webkit-user-select: none;
        -khtml-user-select: none; -moz-user-select: none;
        -ms-user-select: none; user-select: none;
        padding: 0 4px; width: 4em;
        color: #aaaaaa;
      }
    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
    div.sourceCode
      {   }
    @media screen {
    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
    }
    code span.al { color: #ff0000; font-weight: bold; } /* Alert */
    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
    code span.at { color: #7d9029; } /* Attribute */
    code span.bn { color: #40a070; } /* BaseN */
    code span.bu { color: #008000; } /* BuiltIn */
    code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
    code span.ch { color: #4070a0; } /* Char */
    code span.cn { color: #880000; } /* Constant */
    code span.co { color: #60a0b0; font-style: italic; } /* Comment */
    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
    code span.do { color: #ba2121; font-style: italic; } /* Documentation */
    code span.dt { color: #902000; } /* DataType */
    code span.dv { color: #40a070; } /* DecVal */
    code span.er { color: #ff0000; font-weight: bold; } /* Error */
    code span.ex { } /* Extension */
    code span.fl { color: #40a070; } /* Float */
    code span.fu { color: #06287e; } /* Function */
    code span.im { color: #008000; font-weight: bold; } /* Import */
    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
    code span.kw { color: #007020; font-weight: bold; } /* Keyword */
    code span.op { color: #666666; } /* Operator */
    code span.ot { color: #007020; } /* Other */
    code span.pp { color: #bc7a00; } /* Preprocessor */
    code span.sc { color: #4070a0; } /* SpecialChar */
    code span.ss { color: #bb6688; } /* SpecialString */
    code span.st { color: #4070a0; } /* String */
    code span.va { color: #19177c; } /* Variable */
    code span.vs { color: #4070a0; } /* VerbatimString */
    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
  </style>
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">Improving diagnostics for sender expressions</h1>
</header>
<style>
body { min-width: 50% !important; }
p {text-align:justify}
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;
}
dfn {
    font-style: italic;
    font-weight: bold;
}
code:not(sourceCode) {
    white-space: normal;
    font-size: 80% !important;
}
ins > code:not(sourceCode) {
    white-space: normal;
    font-size: 80% !important;
}
div.sourceCode {
    margin-left: 20pt !important;
}
</style>
<div>
<dl>
<dt>
<strong>Authors:</strong>
</dt>
<dd>
<a href="mailto:eric.niebler@gmail.com">Eric Niebler</a>
</dd>
<dt>
<strong>Date:</strong>
</dt>
<dd>
February 29, 2024
</dd>
<dt>
<strong>Source:</strong>
</dt>
<dd>
<a href="https://github.com/ericniebler/wg21/blob/main/P3164/P3164R0.md">GitHub</a>
</dd>
<dt>
<strong>Issue tracking:</strong>
</dt>
<dd>
<a href="https://github.com/ericniebler/wg21/issues">GitHub</a>
</dd>
<dt>
<strong>Project:</strong>
</dt>
<dd>
ISO/IEC JTC1/SC22/WG21 14882: Programming Language — C++
</dd>
<dt>
<strong>Audience:</strong>
</dt>
<dd>
LEWG
</dd>
</dl>
</div>
<h2 id="synopsis">Synopsis</h2>
<p>This paper aims to improve the user experience of the sender
framework by moving the diagnosis of invalid sender expression earlier,
when the expression is constructed, rather than later when it is
connected to a receiver. A trivial change to the sender adaptor
algorithms makes it possible for the majority of sender expressions to
be type-checked early.</p>
<h2 id="executive-summary">Executive Summary</h2>
<p>Below are the specific changes this paper proposes in order to make
early type-checking of sender expressions possible:</p>
<ol type="1">
<li><p>Define a “non-dependent sender” to be one whose completions are
knowable without an environment.</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>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’s non-dependent signatures, if such exist.</p></li>
<li><p>Require the sender adaptor algorithms to preserve the
“non-dependent sender” property wherever possible.</p></li>
<li><p>Add “Mandates:” paragraphs to the sender adaptor algorithms to
require them to hard-error when passed non-dependent senders that fail
type-checking.</p></li>
</ol>
<h2 id="problem-description">Problem Description</h2>
<p>Type-checking a sender expression involves computing its completion
signatures. In the general case, a sender’s completion signatures may
depend on the receiver’s execution environment. For example, the
sender:</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>read<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’s environment and then pass it
back to the receiver, as follows:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode cpp"><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(get_stop_token)</code> doesn’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 don’t depend on the
receiver’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 “non-dependent” 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 cpp"><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’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’t, for example, do any
type-checking if the input sender were <code>read(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 P2300 does 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 cpp"><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<span class="bu">::</span>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<span class="bu">::</span>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 cpp"><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_cvref_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>
<h2 id="suggested-solution">Suggested Solution</h2>
<p>The authors suggests that this notion of non-dependent senders be
given fuller treatment in P2300. 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 “non-dependent” 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>
<h2 id="design-considerations">Design Considerations</h2>
<h3
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?</h3>
<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="cb6"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-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="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>execution<span class="bu">::</span>completion_signatures<span class="op">&lt;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>        <span class="bu">std::</span>execution<span class="bu">::</span>set_value_t<span class="op">(</span>T<span class="op">)</span></span>
<span id="cb6-4"><a href="#cb6-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="cb7"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">using</span> completion_signatures <span class="op">=</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>execution<span class="bu">::</span>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<span class="bu">::</span>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>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’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 “non-dependent” property of the
senders it adapts. For instance, one could define
<code>then_sender</code> like:</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-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="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> then_sender <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    Sndr <span class="va">sndr_</span><span class="op">;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    Fun <span class="va">fun_</span><span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-6"><a href="#cb8-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="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> get_completion_signatures<span class="op">(</span><span class="at">const</span> Env<span class="op">&amp;...</span> env<span class="op">)</span> <span class="at">const</span></span>
<span id="cb8-8"><a href="#cb8-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="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">//....</span></span>
<span id="cb8-11"><a href="#cb8-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 “non-dependent-ness” of the
adapted sender.</p>
<h2 id="proposed-wording">Proposed Wording</h2>
<p><span class="ednote">The wording in this section assumes the adoption
of <a href="https://wg21.link/P2855R1">P2855R1</a>.</span></p>
<p>Change [async.ops]/13 as follows:</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>. <span
class="note">The type of the receiver does not affect an asychronous
operation’s completion signatures, only the type of the receiver’s
environment.</span> <ins>A sender type whose completion signatures are
knowable independent of an execution environment is known as a
<dfn>non-dependent sender</dfn>.</li>
</ol>
</blockquote>
<p>Change [exec.syn] as follows:</p>
<blockquote>
<pre style="white-space: pre-wrap; font-size: 85%;">
...

template&lt;class Sndr, class<ins>...</ins> Env <del>= empty_env</del>>
  concept sender_in = <em>see below</em>;
...

template&lt;class Sndr, class<ins>...</ins> Env <del>= empty_env</del>>
  requires sender_in&lt;Sndr, Env<ins>...</ins>>
using completion_signatures_of_t = call-result-t&lt;get_completion_signatures_t, Sndr, Env<ins>...</ins>>;
...
</pre>
</blockquote>
<p>Change [exec.snd.concepts] as follows:</p>
<blockquote>
<pre style="white-space: pre-wrap; font-size: 85%;">
template&lt;class Sndr, class<ins>...</ins> Env <del>= empty_env</del>>
  concept sender_in =
    sender&lt;Sndr> &&
    <ins>(sizeof...(Env) <= 1)</ins>
    <ins>(</ins>queryable&lt;Env><ins> &&...)</ins> &&
    requires (Sndr&& sndr, Env&&<ins>...</ins> env) {
      { get_completion_signatures(
           std::forward&lt;Sndr>(sndr), std::forward&lt;Env>(env)<ins>...</ins>) }
        -> <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>empty_env</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, empty_env&gt;</code>.</span></p>
<p>Change [exec.awaitables] as follows:</p>
<blockquote>
<ol type="1">
<li><p>The sender concepts recognize awaitables as senders. For this
clause ([exec]), an <strong><em>awaitable</em></strong> 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]/3.2-4, where <code>p</code>
is an lvalue referring to the coroutine’s promise type,
<code>Promise</code>. 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. <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;none-such&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 style="white-space: pre-wrap; font-size: 85%;">
     template&lt;class T>
     concept <em>await-suspend-result</em> = <em>see below</em>;

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

     template&lt;class C, class<ins>...</ins> Promise>
     concept <em>is-awaitable</em> =
         requires (C (*fc)() noexcept, Promise&<ins>...</ins> p) {
             { GET-AWAITER(fc(), p<ins>...</ins>) } -> is-awaiter&lt;Promise<ins>...</ins>>;
         };
 </pre>
<p><code>await-suspend-result&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>await-result-type&lt;C, Promise&gt;</code> denotes the type
<code>decltype(GET-AWAITER(c, p).await_resume())</code> <ins>, and
<code>await-result-type&lt;C&gt;</code> denotes the type
<code>decltype(GET-AWAITER(c).await_resume())</code></ins>.</p></li>
</ol>
</blockquote>
<p>Change [exec.getcomplsigs] as follows:</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> <del>, and let
<code>env</code> be an expression such that <code>decltype((env))</code>
is <code>Env</code></del>. <ins>Then
<code>get_completion_aignatures(sndr)</code> is expression-equivalent
to:</ins></p>
<div class="ins">
<ol type="1">
<li><p><code>remove_cvref_t&lt;Sndr&gt;::completion_signatures{}</code>
if that expression is well-formed,</p></li>
<li><p>Otherwise,
<code>decltype(sndr.get_completion_signatures()){}</code> if that
expression is well-formed,</p></li>
<li><p>Otherwise, if <code>is-awaitable&lt;Sndr&gt;</code> is
<code>true</code>, then:</p>
<pre style="white-space: pre-wrap;">
     completion_signatures<
         SET-VALUE-SIG(await-result-type&lt;Sndr>), // see [exec.snd.concepts]
         set_error_t(exception_ptr),
         set_stopped_t()>{}
 </pre></li>
<li><p>Otherwise, <code>get_completion_signatures(sndr)</code> is
ill-formed.</p></li>
</ol>
</div></li>
<li><p><span><ins>Let <code>env</code> be an expression such that
<code>decltype((env))</code> is <code>Env</code>.</ins></span> Then
<code>get_completion_signatures(sndr, env)</code> is
expression-equivalent to:</p>
<ol type="1">
<li><ins>
<code>remove_cvref_t&lt;Sndr&gt;::completion_signatures{}</code> if that
expression is well-formed,
</ins></li>
</ol>
<p><!-- --></p>
<ol start="2" type="1">
<li><ins>
Otherwise, <code>decltype(sndr.get_completion_signatures(env)){}</code>
if that expression is well-formed,
</ins></li>
</ol>
<p><!-- --></p>
<ol start="2" type="1">
<li><del>
<p>Otherwise,
<code>remove_cvref_t&lt;Sndr&gt;::completion_signatures{}</code> if that
expression is well-formed,</p>
</del></li>
<li><p>Otherwise, if
<code>is-awaitable&lt;Sndr, env-promise&lt;Env&gt;&gt;</code> is
<code>true</code>, then:</p>
<pre style="white-space: pre-wrap;">
     completion_signatures<
         SET-VALUE-SIG(await-result-type&lt;Sndr, env-promise&lt;Env>>), // see [exec.snd.concepts]
         set_error_t(exception_ptr),
         set_stopped_t()>{}
 </pre></li>
<li><p>Otherwise, <code>get_completion_signatures(sndr, env)</code> is
ill-formed.</p></li>
</ol></li>
</ol>
<div class="ins">
<ol start="3" 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 pack of subexpressions <code>e</code>, the expression
<code>get_completion_signatures(e...)</code> is ill-formed if
<code>sizeof...(e)</code> is less than <code>1</code> or greater than
<code>2</code>.</p></li>
<li><p>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 set of completion signatures,
disregarding the order of signatures and rvalue reference qualification
of arguments.</p></li>
</ol>
</div>
<ol start="6" type="1">
<li>Let <code>rcvr</code> be an rvalue receiver of type
<code>Rcvr</code>….</li>
</ol>
</blockquote>
<p>To [exec.adapt.general], add a paragraph (8) as follows:</p>
<blockquote>
<div class="ins">
<ol start="8" type="1">
<li>Unless otherwise specified, an adaptor whose child senders are all
non-dependent ([async.ops]) is itself non-dependent. This requirement
applies to any function that is selected by the implementation of the
sender adaptor.</li>
</ol>
</div>
</blockquote>
<p>Change [exec.then] as follows:</p>
<blockquote>
<ol start="2" type="1">
<li>The names <code>then</code>, <code>upon_error</code>, and
<code>upon_stopped</code> denote customization point 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>, let
<code>Sndr</code> be <code>decltype((sndr))</code> and let
<code>F</code> be the decayed type of <code>f</code>. If
<code>Sndr</code> does not satisfy sender, or <code>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>. If
<code>sender_in&lt;Sndr&gt;</code> is <code>true</code> and
<code><em>gather-signatures</em>&lt;tag_t&lt;<em>set-cpo</em>&gt;,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is ill-formed, the program is
ill-formed.</li>
</ol>
</div>
<ol start="4" type="1">
<li><p>Otherwise, the expression <code><em>then-cpo</em>(sndr, f)</code>
is expression-equivalent to:…..</span></p></li>
<li><del>
<p>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.</p>
</del>
<p>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:….</p>
<div></li>
</ol>
</blockquote>
<p>Change [exec.let] by inserting a new paragraph between (4) and (5) as
follows:</p>
<blockquote>
<div class="ins">
<ol start="5" 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>. If
<code>sender_in&lt;Sndr&gt;</code> is <code>true</code> and
<code><em>gather-signatures</em>&lt;tag_t&lt;<em>set-cpo</em>&gt;,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is ill-formed, the program is
ill-formed.</li>
</ol>
</div>
</blockquote>
<p>Change [exec.bulk] by inserting a new paragraph between (3) and (4)
as follows:</p>
<blockquote>
<div class="ins">
<ol start="5" 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>. If
<code>sender_in&lt;Sndr&gt;</code> is <code>true</code> and
<code><em>gather-signatures</em>&lt;tag_t&lt;<em>set-cpo</em>&gt;,
completion_signatures_of_t&lt;Sndr&gt;, <em>invoke-result</em>,
<em>type-list</em>&gt;</code> is ill-formed, the program is
ill-formed.</li>
</ol>
</div>
</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>
