<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="mpark/wg21" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <meta name="dcterms.date" content="2023-12-12" />
  <title>Sender Algorithm Customization</title>
  <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
      div.csl-block{margin-left: 1.5em;}
      ul.task-list{list-style: none;}
      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
        {  background-color: #f6f8fa; }
      @media screen {
      pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span { } /* Normal */
      code span.al { color: #ff0000; } /* Alert */
      code span.an { } /* Annotation */
      code span.at { } /* Attribute */
      code span.bn { color: #9f6807; } /* BaseN */
      code span.bu { color: #9f6807; } /* BuiltIn */
      code span.cf { color: #00607c; } /* ControlFlow */
      code span.ch { color: #9f6807; } /* Char */
      code span.cn { } /* Constant */
      code span.co { color: #008000; font-style: italic; } /* Comment */
      code span.cv { color: #008000; font-style: italic; } /* CommentVar */
      code span.do { color: #008000; } /* Documentation */
      code span.dt { color: #00607c; } /* DataType */
      code span.dv { color: #9f6807; } /* DecVal */
      code span.er { color: #ff0000; font-weight: bold; } /* Error */
      code span.ex { } /* Extension */
      code span.fl { color: #9f6807; } /* Float */
      code span.fu { } /* Function */
      code span.im { } /* Import */
      code span.in { color: #008000; } /* Information */
      code span.kw { color: #00607c; } /* Keyword */
      code span.op { color: #af1915; } /* Operator */
      code span.ot { } /* Other */
      code span.pp { color: #6f4e37; } /* Preprocessor */
      code span.re { } /* RegionMarker */
      code span.sc { color: #9f6807; } /* SpecialChar */
      code span.ss { color: #9f6807; } /* SpecialString */
      code span.st { color: #9f6807; } /* String */
      code span.va { } /* Variable */
      code span.vs { color: #9f6807; } /* VerbatimString */
      code span.wa { color: #008000; font-weight: bold; } /* Warning */
      code.diff {color: #898887}
      code.diff span.va {color: #006e28}
      code.diff span.st {color: #bf0303}
  </style>
  <style type="text/css">
body {
margin: 5em;
font-family: serif;

hyphens: auto;
line-height: 1.35;
text-align: justify;
}
@media screen and (max-width: 30em) {
body {
margin: 1.5em;
}
}
div.wrapper {
max-width: 60em;
margin: auto;
}
ul {
list-style-type: none;
padding-left: 2em;
margin-top: -0.2em;
margin-bottom: -0.2em;
}
a {
text-decoration: none;
color: #4183C4;
}
a.hidden_link {
text-decoration: none;
color: inherit;
}
li {
margin-top: 0.6em;
margin-bottom: 0.6em;
}
h1, h2, h3, h4 {
position: relative;
line-height: 1;
}
a.self-link {
position: absolute;
top: 0;
left: calc(-1 * (3.5rem - 26px));
width: calc(3.5rem - 26px);
height: 2em;
text-align: center;
border: none;
transition: opacity .2s;
opacity: .5;
font-family: sans-serif;
font-weight: normal;
font-size: 83%;
}
a.self-link:hover { opacity: 1; }
a.self-link::before { content: "§"; }
ul > li:before {
content: "\2014";
position: absolute;
margin-left: -1.5em;
}
:target { background-color: #C9FBC9; }
:target .codeblock { background-color: #C9FBC9; }
:target ul { background-color: #C9FBC9; }
.abbr_ref { float: right; }
.folded_abbr_ref { float: right; }
:target .folded_abbr_ref { display: none; }
:target .unfolded_abbr_ref { float: right; display: inherit; }
.unfolded_abbr_ref { display: none; }
.secnum { display: inline-block; min-width: 35pt; }
.header-section-number { display: inline-block; min-width: 35pt; }
.annexnum { display: block; }
div.sourceLinkParent {
float: right;
}
a.sourceLink {
position: absolute;
opacity: 0;
margin-left: 10pt;
}
a.sourceLink:hover {
opacity: 1;
}
a.itemDeclLink {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
opacity: 0;
}
a.itemDeclLink:hover { opacity: 1; }
span.marginalizedparent {
position: relative;
left: -5em;
}
li span.marginalizedparent { left: -7em; }
li ul > li span.marginalizedparent { left: -9em; }
li ul > li ul > li span.marginalizedparent { left: -11em; }
li ul > li ul > li ul > li span.marginalizedparent { left: -13em; }
div.footnoteNumberParent {
position: relative;
left: -4.7em;
}
a.marginalized {
position: absolute;
font-size: 75%;
text-align: right;
width: 5em;
}
a.enumerated_item_num {
position: relative;
left: -3.5em;
display: inline-block;
margin-right: -3em;
text-align: right;
width: 3em;
}
div.para { margin-bottom: 0.6em; margin-top: 0.6em; text-align: justify; }
div.section { text-align: justify; }
div.sentence { display: inline; }
span.indexparent {
display: inline;
position: relative;
float: right;
right: -1em;
}
a.index {
position: absolute;
display: none;
}
a.index:before { content: "⟵"; }

a.index:target {
display: inline;
}
.indexitems {
margin-left: 2em;
text-indent: -2em;
}
div.itemdescr {
margin-left: 3em;
}
.bnf {
font-family: serif;
margin-left: 40pt;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.ncbnf {
font-family: serif;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
}
.ncsimplebnf {
font-family: serif;
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-left: 40pt;
background: inherit; 
}
span.textnormal {
font-style: normal;
font-family: serif;
white-space: normal;
display: inline-block;
}
span.rlap {
display: inline-block;
width: 0px;
}
span.descr { font-style: normal; font-family: serif; }
span.grammarterm { font-style: italic; }
span.term { font-style: italic; }
span.terminal { font-family: monospace; font-style: normal; }
span.nonterminal { font-style: italic; }
span.tcode { font-family: monospace; font-style: normal; }
span.textbf { font-weight: bold; }
span.textsc { font-variant: small-caps; }
a.nontermdef { font-style: italic; font-family: serif; }
span.emph { font-style: italic; }
span.techterm { font-style: italic; }
span.mathit { font-style: italic; }
span.mathsf { font-family: sans-serif; }
span.mathrm { font-family: serif; font-style: normal; }
span.textrm { font-family: serif; }
span.textsl { font-style: italic; }
span.mathtt { font-family: monospace; font-style: normal; }
span.mbox { font-family: serif; font-style: normal; }
span.ungap { display: inline-block; width: 2pt; }
span.textit { font-style: italic; }
span.texttt { font-family: monospace; }
span.tcode_in_codeblock { font-family: monospace; font-style: normal; }
span.phantom { color: white; }

span.math { font-style: normal; }
span.mathblock {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 1.2em;
margin-bottom: 1.2em;
text-align: center;
}
span.mathalpha {
font-style: italic;
}
span.synopsis {
font-weight: bold;
margin-top: 0.5em;
display: block;
}
span.definition {
font-weight: bold;
display: block;
}
.codeblock {
margin-left: 1.2em;
line-height: 127%;
}
.outputblock {
margin-left: 1.2em;
line-height: 127%;
}
div.itemdecl {
margin-top: 2ex;
}
code.itemdeclcode {
white-space: pre;
display: block;
}
span.textsuperscript {
vertical-align: super;
font-size: smaller;
line-height: 0;
}
.footnotenum { vertical-align: super; font-size: smaller; line-height: 0; }
.footnote {
font-size: small;
margin-left: 2em;
margin-right: 2em;
margin-top: 0.6em;
margin-bottom: 0.6em;
}
div.minipage {
display: inline-block;
margin-right: 3em;
}
div.numberedTable {
text-align: center;
margin: 2em;
}
div.figure {
text-align: center;
margin: 2em;
}
table {
border: 1px solid black;
border-collapse: collapse;
margin-left: auto;
margin-right: auto;
margin-top: 0.8em;
text-align: left;
hyphens: none; 
}
td, th {
padding-left: 1em;
padding-right: 1em;
vertical-align: top;
}
td.empty {
padding: 0px;
padding-left: 1px;
}
td.left {
text-align: left;
}
td.right {
text-align: right;
}
td.center {
text-align: center;
}
td.justify {
text-align: justify;
}
td.border {
border-left: 1px solid black;
}
tr.rowsep, td.cline {
border-top: 1px solid black;
}
tr.even, tr.odd {
border-bottom: 1px solid black;
}
tr.capsep {
border-top: 3px solid black;
border-top-style: double;
}
tr.header {
border-bottom: 3px solid black;
border-bottom-style: double;
}
th {
border-bottom: 1px solid black;
}
span.centry {
font-weight: bold;
}
div.table {
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
width: 90%;
}
span.indented {
display: block;
margin-left: 2em;
margin-bottom: 1em;
margin-top: 1em;
}
ol.enumeratea { list-style-type: none; background: inherit; }
ol.enumerate { list-style-type: none; background: inherit; }

code.sourceCode > span { display: inline; }
</style>
  <link href="data:image/x-icon;base64,AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAVoJEAN6CRADegkQAWIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wCCRAAAgkQAAIJEAACCRAAsgkQAvoJEAP+CRAD/gkQA/4JEAP+CRADAgkQALoJEAACCRAAAgkQAAP///wD///8AgkQAAIJEABSCRACSgkQA/IJEAP99PQD/dzMA/3czAP99PQD/gkQA/4JEAPyCRACUgkQAFIJEAAD///8A////AHw+AFiBQwDqgkQA/4BBAP9/PxP/uZd6/9rJtf/bybX/upd7/39AFP+AQQD/gkQA/4FDAOqAQgBc////AP///wDKklv4jlEa/3o7AP+PWC//8+3o///////////////////////z7un/kFox/35AAP+GRwD/mVYA+v///wD///8A0Zpk+NmibP+0d0T/8evj///////+/fv/1sKz/9bCs//9/fr//////+/m2/+NRwL/nloA/5xYAPj///8A////ANKaZPjRmGH/5cKh////////////k149/3UwAP91MQD/lmQ//86rhv+USg3/m1YA/5hSAP+bVgD4////AP///wDSmmT4zpJY/+/bx///////8+TV/8mLT/+TVx//gkIA/5lVAP+VTAD/x6B//7aEVv/JpH7/s39J+P///wD///8A0ppk+M6SWP/u2sf///////Pj1f/Nj1T/2KFs/8mOUv+eWhD/lEsA/8aee/+0glT/x6F7/7J8Rvj///8A////ANKaZPjRmGH/48Cf///////+/v7/2qt//82PVP/OkFX/37KJ/86siv+USg7/mVQA/5hRAP+bVgD4////AP///wDSmmT40ppk/9CVXP/69O////////7+/v/x4M//8d/P//7+/f//////9u7n/6tnJf+XUgD/nFgA+P///wD///8A0ppk+NKaZP/RmWL/1qNy//r07///////////////////////+vXw/9akdP/Wnmn/y5FY/6JfFvj///8A////ANKaZFTSmmTo0ppk/9GYYv/Ql1//5cWm//Hg0P/x4ND/5cWm/9GXYP/RmGH/0ppk/9KaZOjVnmpY////AP///wDSmmQA0ppkEtKaZI7SmmT60ppk/9CWX//OkVb/zpFW/9CWX//SmmT/0ppk/NKaZJDSmmQS0ppkAP///wD///8A0ppkANKaZADSmmQA0ppkKtKaZLrSmmT/0ppk/9KaZP/SmmT/0ppkvNKaZCrSmmQA0ppkANKaZAD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkUtKaZNzSmmTc0ppkVNKaZADSmmQA0ppkANKaZADSmmQA////AP5/AAD4HwAA4AcAAMADAACAAQAAgAEAAIABAACAAQAAgAEAAIABAACAAQAAgAEAAMADAADgBwAA+B8AAP5/AAAoAAAAIAAAAEAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAyCRACMgkQA6oJEAOqCRACQgkQAEIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRABigkQA5oJEAP+CRAD/gkQA/4JEAP+CRADqgkQAZoJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAA4gkQAwoJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQAxIJEADyCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAAgkQAAP///wD///8A////AP///wCCRAAAgkQAAIJEAACCRAAAgkQAAIJEAACCRAAWgkQAmIJEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAJyCRAAYgkQAAIJEAACCRAAAgkQAAIJEAACCRAAA////AP///wD///8A////AIJEAACCRAAAgkQAAIJEAACCRAAAgkQAdIJEAPCCRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAP+CRAD/gkQA/4JEAPSCRAB4gkQAAIJEAACCRAAAgkQAAIJEAAD///8A////AP///wD///8AgkQAAIJEAACCRAAAgkQASoJEANKCRAD/gkQA/4JEAP+CRAD/g0YA/39AAP9zLgD/bSQA/2shAP9rIQD/bSQA/3MuAP9/PwD/g0YA/4JEAP+CRAD/gkQA/4JEAP+CRADUgkQAToJEAACCRAAAgkQAAP///wD///8A////AP///wB+PwAAgkUAIoJEAKiCRAD/gkQA/4JEAP+CRAD/hEcA/4BBAP9sIwD/dTAA/5RfKv+viF7/vp56/76ee/+wiF7/lWAr/3YxAP9sIwD/f0AA/4RHAP+CRAD/gkQA/4JEAP+CRAD/gkQArIJEACaBQwAA////AP///wD///8A////AIBCAEBzNAD6f0EA/4NFAP+CRAD/gkQA/4VIAP92MwD/bSUA/6N1Tv/ezsL/////////////////////////////////38/D/6V3Uv9uJgD/dTEA/4VJAP+CRAD/gkQA/4JEAP+BQwD/fUAA/4FDAEj///8A////AP///wD///8AzJRd5qBlKf91NgD/dDUA/4JEAP+FSQD/cy4A/3YyAP/PuKP//////////////////////////////////////////////////////9K7qP94NQD/ciwA/4VJAP+CRAD/fkEA/35BAP+LSwD/mlYA6v///wD///8A////AP///wDdpnL/4qx3/8KJUv+PUhf/cTMA/3AsAP90LgD/4dK+/////////////////////////////////////////////////////////////////+TYxf91MAD/dTIA/31CAP+GRwD/llQA/6FcAP+gWwD8////AP///wD///8A////ANGZY/LSm2X/4ap3/92mcP+wdT3/byQA/8mwj////////////////////////////////////////////////////////////////////////////+LYxv9zLgP/jUoA/59bAP+hXAD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/RmWL/1p9q/9ubXv/XqXj////////////////////////////7+fD/vZyG/6BxS/+gcUr/vJuE//r37f//////////////////////3MOr/5dQBf+dVQD/nVkA/5xYAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmWP/yohJ//jo2P//////////////////////4NTG/4JDFf9lGAD/bSQA/20kAP9kGAD/fz8S/+Xb0f//////5NG9/6txN/+LOgD/m1QA/51aAP+cWAD/m1cA/5xYAP+cWADy////AP///wD///8A////ANKaZPLSmmT/0ppk/8+TWf/Unmv//v37//////////////////////+TWRr/VwsA/35AAP+ERgD/g0UA/4JGAP9lHgD/kFga/8KXX/+TRwD/jT4A/49CAP+VTQD/n10A/5xYAP+OQQD/lk4A/55cAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/y4tO/92yiP//////////////////////8NnE/8eCQP+rcTT/ez0A/3IyAP98PgD/gEMA/5FSAP+USwD/jj8A/5lUAP+JNwD/yqV2/694Mf+HNQD/jkAA/82rf/+laBj/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/LiUr/4byY///////////////////////gupX/0I5P/+Wuev/Lklz/l1sj/308AP+QSwD/ol0A/59aAP+aVQD/k0oA/8yoh///////+fXv/6pwO//Lp3v///////Pr4f+oay7y////AP///wD///8A////ANKaZPLSmmT/0ppk/8uJSv/hvJj//////////////////////+G7l//Jhkb/0ppk/96nc//fqXX/x4xO/6dkFP+QSQD/llEA/5xXAP+USgD/yaOA///////38uv/qG05/8ijdv//////8efb/6ZpLPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/zIxO/9yxh///////////////////////7dbA/8iEQf/Sm2X/0Zlj/9ScZv/eqHf/2KJv/7yAQf+XTgD/iToA/5lSAP+JNgD/yKFv/611LP+HNQD/jT8A/8qmeP+kZRT/jT4A8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/Pk1n/1J5q//78+//////////////////+/fv/1aFv/8iEQv/Tm2b/0ppl/9GZY//Wn2z/1pZc/9eldf/Bl2b/kUcA/4w9AP+OQAD/lUwA/59eAP+cWQD/jT8A/5ZOAP+eXADy////AP///wD///8A////ANKaZPLSmmT/0ppk/9KZY//KiEn/8d/P///////////////////////47+f/05tm/8iCP//KiEj/yohJ/8eCP//RmGH//vfy///////n1sP/rXQ7/4k4AP+TTAD/nVoA/5xYAP+cVwD/nFgA/5xYAPL///8A////AP///wD///8A0ppk8tKaZP/SmmT/0ptl/8uLTf/aq37////////////////////////////+/fz/6c2y/961jv/etY7/6Myx//78+v//////////////////////3MWv/5xXD/+ORAD/mFQA/51ZAP+cWAD/nFgA8v///wD///8A////AP///wDSmmTy0ppk/9KaZP/SmmT/0ppk/8mFRP/s1b//////////////////////////////////////////////////////////////////////////////+PD/0JFU/7NzMv+WUQD/kUsA/5tXAP+dWQDy////AP///wD///8A////ANKaZP/SmmT/0ppk/9KaZP/Sm2X/z5NZ/8yMT//z5NX/////////////////////////////////////////////////////////////////9Ofa/8yNUP/UmGH/36p5/8yTWv+qaSD/kksA/5ROAPz///8A////AP///wD///8A0ppk5NKaZP/SmmT/0ppk/9KaZP/TnGf/zY9T/82OUv/t1sD//////////////////////////////////////////////////////+7Yw//OkFX/zI5R/9OcZ//SmmP/26V0/9ymdf/BhUf/ol8R6P///wD///8A////AP///wDSmmQ80ppk9tKaZP/SmmT/0ppk/9KaZP/TnGj/zpFW/8qJSv/dson/8uHS//////////////////////////////////Lj0//etIv/y4lL/86QVf/TnGj/0ppk/9KaZP/RmWP/05xn/9ymdfjUnWdC////AP///wD///8A////ANKaZADSmmQc0ppkotKaZP/SmmT/0ppk/9KaZP/Tm2b/0Zli/8qJSf/NjlH/16Z3/+G8mP/myKr/5siq/+G8mP/Xp3f/zY5S/8qISf/RmGH/05tm/9KaZP/SmmT/0ppk/9KaZP/SmmSm0pljINWdaQD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkQtKaZMrSmmT/0ppk/9KaZP/SmmT/0ptl/9GYYf/Nj1P/y4lL/8qISP/KiEj/y4lK/82PU//RmGH/0ptl/9KaZP/SmmT/0ppk/9KaZP/SmmTO0ppkRtKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZGzSmmTu0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmTw0ppkcNKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZBLSmmSQ0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppklNKaZBTSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP///wD///8A0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQy0ppkutKaZP/SmmT/0ppk/9KaZP/SmmT/0ppk/9KaZP/SmmT/0ppkvtKaZDbSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkAP///wD///8A////AP///wDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkXNKaZODSmmT/0ppk/9KaZP/SmmT/0ppk5NKaZGDSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA////AP///wD///8A////ANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkBtKaZIbSmmTo0ppk6tKaZIrSmmQK0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZADSmmQA0ppkANKaZAD///8A////AP/8P///+B///+AH//+AAf//AAD//AAAP/AAAA/gAAAHwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA8AAAAPAAAADwAAAA+AAAAfwAAAP/AAAP/8AAP//gAH//+AH///4H////D//" rel="icon" />
  
  <!--[if lt IE 9]>
    <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
  <![endif]-->
</head>
<body>
<div class="wrapper">
<header id="title-block-header">
<h1 class="title" style="text-align:center">Sender Algorithm
Customization</h1>
<h3 class="subtitle" style="text-align:center">Draft Proposal</h3>
<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2999R3</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2023-12-12</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Project:</td>
    <td>Programming Language C++</td>
  </tr>
  <tr>
    <td style="vertical-align:top">Audience:</td>
    <td>
      LEWG Library Evolution<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Eric Niebler<br>&lt;<a href="mailto:eric.niebler@gmail.com" class="email">eric.niebler@gmail.com</a>&gt;<br>
    </td>
  </tr>
</table>
</header>
<div style="clear:both">
<div id="TOC" role="doc-toc">
<h1 id="toctitle">Contents</h1>
<ul>
<li><a href="#introduction" id="toc-introduction"><span class="toc-section-number">1</span> Introduction<span></span></a>
<ul>
<li><a href="#the-issue" id="toc-the-issue"><span class="toc-section-number">1.1</span> The Issue<span></span></a></li>
<li><a href="#revision-history" id="toc-revision-history"><span class="toc-section-number">1.2</span> Revision history<span></span></a>
<ul>
<li><a href="#r2" id="toc-r2"><span class="toc-section-number">1.2.1</span> R2<span></span></a></li>
<li><a href="#r1" id="toc-r1"><span class="toc-section-number">1.2.2</span> R1<span></span></a></li>
<li><a href="#r0" id="toc-r0"><span class="toc-section-number">1.2.3</span> R0<span></span></a></li>
</ul></li>
</ul></li>
<li><a href="#proposed-design" id="toc-proposed-design"><span class="toc-section-number">2</span> Proposed Design<span></span></a>
<ul>
<li><a href="#features-and-rationale" id="toc-features-and-rationale"><span class="toc-section-number">2.1</span> Features and
rationale<span></span></a>
<ul>
<li><a href="#dispatching-via-execution-domain-tags" id="toc-dispatching-via-execution-domain-tags"><span class="toc-section-number">2.1.1</span> Dispatching via execution domain
tags<span></span></a></li>
<li><a href="#late-senderreceiver-connection-time-customization" id="toc-late-senderreceiver-connection-time-customization"><span class="toc-section-number">2.1.2</span> Late (sender/receiver
connection-time) customization<span></span></a></li>
<li><a href="#early-sender-construction-time-customization" id="toc-early-sender-construction-time-customization"><span class="toc-section-number">2.1.3</span> Early (sender construction-time)
customization<span></span></a></li>
<li><a href="#decomposable-senders" id="toc-decomposable-senders"><span class="toc-section-number">2.1.4</span> Decomposable
senders<span></span></a></li>
</ul></li>
<li><a href="#why-have-both-early-and-late-customization" id="toc-why-have-both-early-and-late-customization"><span class="toc-section-number">2.2</span> Why have both early and late
customization?<span></span></a></li>
<li><a href="#an-example-of-early-and-late-customizations" id="toc-an-example-of-early-and-late-customizations"><span class="toc-section-number">2.3</span> An example of early and late
customizations<span></span></a></li>
<li><a href="#summary-of-proposed-changes" id="toc-summary-of-proposed-changes"><span class="toc-section-number">2.4</span> Summary of proposed
changes<span></span></a></li>
</ul></li>
<li><a href="#implementation-experience" id="toc-implementation-experience"><span class="toc-section-number">3</span> Implementation
Experience<span></span></a></li>
<li><a href="#proposed-wording" id="toc-proposed-wording"><span class="toc-section-number">4</span> Proposed
Wording<span></span></a></li>
<li><a href="#bibliography" id="toc-bibliography"><span class="toc-section-number">5</span> References<span></span></a></li>
</ul>
</div>
<h1 data-number="1" id="introduction"><span class="header-section-number">1</span> Introduction<a href="#introduction" class="self-link"></a></h1>
<p>This paper proposes some design changes to P2300 to address some
shortcomings in how algorithm customizations are found.</p>
<h2 data-number="1.1" id="the-issue"><span class="header-section-number">1.1</span> The Issue<a href="#the-issue" class="self-link"></a></h2>
<dl>
<dt>The essence of the issue is this:</dt>
<dd>
<p><em>Many senders do not know on what execution context they will
complete, so using solely that information to find customizations (as
P2300R7 does) is unsatisfactory.</em></p>
</dd>
</dl>
<p>In <span class="citation" data-cites="P2300R7">[<a href="#ref-P2300R7" role="doc-biblioref">P2300R7</a>]</span>, the sender
algorithms (<code class="sourceCode default">then</code>,
<code class="sourceCode default">let_value</code>, etc) are
customization point objects that internally dispatch via
<code class="sourceCode default">tag_invoke</code> to the correct
algorithm implementation. Each algorithm has a default implementation
that is used if no custom implementation is found.</p>
<p>Custom implementations of sender algorithms are found by asking the
predecessor sender for its completion scheduler and using the scheduler
as a tag for the purpose of tag dispatching. A <em>completion
scheduler</em> is a scheduler that refers to the execution context on
which that sender will complete.</p>
<p>A typical sender algorithm like
<code class="sourceCode default">then</code> might be implemented as
follows:</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><span class="co">/// </span><span class="an">@brief</span><span class="co"> A helper concept for testing whether an algorithm customization</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="co">///   exists</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> AlgoTag, <span class="kw">class</span> SetTag, <span class="kw">class</span> Sender, <span class="kw">class</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="kw">concept</span> <em>has-customization</em> <span class="op">=</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">requires</span> <span class="op">(</span>Sender snd, Args<span class="op">...</span> args<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    tag_invoke<span class="op">(</span>AlgoTag<span class="op">()</span>,</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>               get_completion_scheduler<span class="op">&lt;</span>SetTag<span class="op">&gt;(</span>get_env<span class="op">(</span>snd<span class="op">))</span>,</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>               std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>               std<span class="op">::</span>forward<span class="op">&lt;</span>Args<span class="op">&gt;(</span>args<span class="op">)...)</span>;</span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span>;</span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a><span class="co">/// </span><span class="an">@brief</span><span class="co"> The tag type and the customization point object type for the</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a><span class="co">///   `then` sender algorithm</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> then_t <span class="op">{</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>sender Sender, <span class="kw">class</span> Fun<span class="op">&gt;</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* requirements here */</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sender<span class="op">&amp;&amp;</span> snd, Fun fun<span class="op">)</span> <span class="kw">const</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">{</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a>    <span class="co">// If the predecessor sender has a completion scheduler, and if we can use</span></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>    <span class="co">// the completion scheduler to find a custom implementation for the `then`</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>    <span class="co">// algorithm, dispatch to that. Otherwise, dispatch to the default `then`</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>    <span class="co">// implementation.</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><em>has-customization</em><span class="op">&lt;</span>then_t, set_value_t, Sender, Fun<span class="op">&gt;)</span></span>
<span id="cb1-24"><a href="#cb1-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb1-25"><a href="#cb1-25" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span><span class="op">&amp;&amp;</span> env <span class="op">=</span> get_env<span class="op">(</span>snd<span class="op">)</span>;</span>
<span id="cb1-26"><a href="#cb1-26" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> tag_invoke<span class="op">(*</span><span class="kw">this</span>,</span>
<span id="cb1-27"><a href="#cb1-27" aria-hidden="true" tabindex="-1"></a>                        get_completion_scheduler<span class="op">&lt;</span>set_value_t<span class="op">&gt;(</span>env<span class="op">)</span>,</span>
<span id="cb1-28"><a href="#cb1-28" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb1-29"><a href="#cb1-29" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">))</span>;</span>
<span id="cb1-30"><a href="#cb1-30" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-31"><a href="#cb1-31" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span></span>
<span id="cb1-32"><a href="#cb1-32" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb1-33"><a href="#cb1-33" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> <em>then-sender</em><span class="op">&lt;</span>Sender, Fun<span class="op">&gt;(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">))</span>;</span>
<span id="cb1-34"><a href="#cb1-34" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb1-35"><a href="#cb1-35" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb1-36"><a href="#cb1-36" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span>
<span id="cb1-37"><a href="#cb1-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-38"><a href="#cb1-38" aria-hidden="true" tabindex="-1"></a><span class="kw">inline</span> <span class="kw">constexpr</span> then_t then <span class="op">{}</span>;</span></code></pre></div>
<p>This scheme has a number of shortcomings:</p>
<ol type="1">
<li><p>A simple sender like
<code class="sourceCode default">just(42)</code> does not know its
completion scheduler. It completes on the execution context on which it
is started. That is not known at the time the sender is constructed,
which is when we are looking for customizations.</p></li>
<li><p>For a sender like
<code class="sourceCode default">on( sch, then(just(), fun) )</code>,
the nested <code class="sourceCode default">then</code> sender is
constructed before we have specified the scheduler, but we need the
scheduler to dispatch to the correct customization of
<code class="sourceCode default">then</code>. How?</p></li>
<li><p>A composite sender like
<code class="sourceCode default">when_all(snd1, snd2)</code> cannot know
its completion scheduler in the general case. Even if
<code class="sourceCode default">snd1</code> and
<code class="sourceCode default">snd2</code> both know their completion
schedulers – say, <code class="sourceCode default">sched1</code> and
<code class="sourceCode default">sched2</code> respectively – the
<code class="sourceCode default">when_all</code> sender can complete on
<em>either</em> <code class="sourceCode default">sched1</code>
<em>or</em> <code class="sourceCode default">sched2</code> depending on
which of <code class="sourceCode default">snd1</code> and
<code class="sourceCode default">snd2</code> completes last. That is a
dynamic property of the program’s execution, not suitable for finding an
algorithm customization.</p></li>
</ol>
<p>In cases (1) and (2), the issue is that the information necessary to
find the correct algorithm implementation is not available at the time
we look for customizations. In case (3), the issue is that the algorithm
semantics make it impossible to know statically to what algorithm
customization scheme to dispatch.</p>
<p>The issue described in (2) above is particularly pernicious. Consider
these two programs (where <code class="sourceCode default">ex::</code>
is a namespace alias for
<code class="sourceCode default">std::execution</code>); the differences
are highlighted:</p>
<table>
<caption><blockquote>
<p>Table 1: Algorithms customizations are found or not depending on
subtle differences</p>
</blockquote></caption>
<thead>
<tr class="header">
<th><div style="text-align:center">
<strong>Good</strong>
</div></th>
<th><div style="text-align:center">
<strong>Bad</strong>
</div></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><div>

<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="co">// Describe some bulk work on a given scheduler</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> work<span class="op">(</span>ex<span class="op">::</span>scheduler <span class="kw">auto</span> sch, <span class="kw">auto</span> data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <mark>ex::transfer_just(sch, data)</mark></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> ex<span class="op">::</span>bulk<span class="op">(</span>data<span class="op">.</span>size<span class="op">()</span>,</span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>              <span class="op">[](</span><span class="dt">int</span> i, <span class="kw">auto</span><span class="op">&amp;</span> data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>                <span class="op">++</span>data<span class="op">[</span>i<span class="op">]</span>;</span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>              <span class="op">})</span>;</span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>my<span class="op">::</span>thread_pool_scheduler thread_pool <span class="op">=</span> <span class="co">/*...*/</span>;</span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sender <span class="kw">auto</span> task <span class="op">=</span> work<span class="op">(</span>thread_pool, data<span class="op">)</span>;</span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Execute the work</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>this_thread<span class="op">::</span>sync_wait<span class="op">(</span><mark>std::move(task)</mark><span class="op">)</span>;</span></code></pre></div>

</div></td>
<td><div>

<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><span class="co">// Describe some bulk work</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> work<span class="op">(</span><span class="kw">auto</span> data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <mark>ex::just(data)</mark></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span> ex<span class="op">::</span>bulk<span class="op">(</span>data<span class="op">.</span>size<span class="op">()</span>,</span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>              <span class="op">[](</span><span class="dt">int</span> i, <span class="kw">auto</span><span class="op">&amp;</span> data<span class="op">)</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>                <span class="op">++</span>data<span class="op">[</span>i<span class="op">]</span>;</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>              <span class="op">})</span>;</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>my<span class="op">::</span>thread_pool_scheduler thread_pool <span class="op">=</span> <span class="co">/*...*/</span>;</span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sender <span class="kw">auto</span> task <span class="op">=</span> work<span class="op">(</span>data<span class="op">)</span>;</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="co">// Execute the bulk work on a thread pool</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>std<span class="op">::</span>this_thread<span class="op">::</span>sync_wait<span class="op">(</span><mark>ex::on(thread_pool, std::move(task))</mark><span class="op">)</span>;</span></code></pre></div>

</div></td>
</tr>
</tbody>
</table>
<p>These two programs <em>should</em> be equivalent, but they are not.
The author of the
<code class="sourceCode default">thread_pool_scheduler</code> gave it a
custom <code class="sourceCode default">bulk</code> implementation by
defining:</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">namespace</span> my <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">// customization of the bulk algorithm for the thread_pool_scheduler:</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>ex<span class="op">::</span>sender Sender, std<span class="op">::</span>integral Shape, <span class="kw">class</span> Function<span class="op">&gt;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> tag_invoke<span class="op">(</span>ex<span class="op">::</span>bulk_t,</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>                  thread_pool_scheduler sch,</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>                  Sender<span class="op">&amp;&amp;</span> snd,</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>                  Shape shape,</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>                  Function fun<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">/*</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a><span class="co">     * Do bulk work in parallel</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="co">     * ...</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a><span class="co">     */</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This overload is found only when the
<code class="sourceCode default">bulk</code> sender’s predecessor
completes on a
<code class="sourceCode default">thread_pool_scheduler</code>, which is
the case for the code on the left.</p>
<p>In the code to the right, however, the predecessor of the
<code class="sourceCode default">bulk</code> operation is
<code class="sourceCode default">just(data)</code>, a sender that does
not know where it will complete. As a result, the above customization of
the <code class="sourceCode default">bulk</code> algorithm will not be
found, and the bulk operation will execute serially on a single thread
in the thread pool. That’s almost certainly <em>not</em> what the
programmer intended.</p>
<p>This is clearly broken and badly in need of fixing.</p>
<blockquote>
<p><em>Note:</em> On the need for async algorithms customization</p>
<p>It is worth asking why async algorithms need customization at all.
After all, the classic STL algorithms need no customization; they
dispatch using a fixed concept hierarchy to a closed set of possible
implementations.</p>
<p>The reason is because of the open and continually evolving nature of
execution contexts. There is little hope of capturing every salient
attribute of every interesting execution model – CPUs, GPUs, FPGAs,
etc., past, present, and future – in a fixed ontology around which we
can build named concepts and immutable basis operations. Instead we do
the best we can and then hedge against the future by making the
algorithms customizable. For example, say we add an algorithm
<code class="sourceCode default">std::par_algo</code>, but we allow that
there may be an accelerator “out there” that may do
<code class="sourceCode default">par_algo</code> more efficiently than
the standard one, so we make
<code class="sourceCode default">par_algo</code> customizable.</p>
</blockquote>
<h2 data-number="1.2" id="revision-history"><span class="header-section-number">1.2</span> Revision history<a href="#revision-history" class="self-link"></a></h2>
<h3 data-number="1.2.1" id="r2"><span class="header-section-number">1.2.1</span> R2<a href="#r2" class="self-link"></a></h3>
<ul>
<li>Add “<a href="#an-example-of-early-and-late-customizations">An
example of early and late customizations</a>”</li>
<li>Add a discussion of the <a href="#why-have-both-early-and-late-customization">reasons to keep eager
customization</a></li>
<li>Fix the computation of the domain at
<code class="sourceCode default">connect</code>-time</li>
<li>Add a requirement on senders that their completion schedulers all
share a domain</li>
<li><code class="sourceCode default">stdexec::transform_sender</code>
recurses when necessary</li>
<li>remove no-longer-necessary exposition-only
<em><code class="sourceCode default">make-transformer-fn</code></em> and
all uses</li>
</ul>
<h3 data-number="1.2.2" id="r1"><span class="header-section-number">1.2.2</span> R1<a href="#r1" class="self-link"></a></h3>
<ul>
<li>Proposed wording added</li>
<li>Add comparison tables</li>
</ul>
<h3 data-number="1.2.3" id="r0"><span class="header-section-number">1.2.3</span> R0<a href="#r0" class="self-link"></a></h3>
<ul>
<li>Initial revision</li>
</ul>
<h1 data-number="2" id="proposed-design"><span class="header-section-number">2</span> Proposed Design<a href="#proposed-design" class="self-link"></a></h1>
<h2 data-number="2.1" id="features-and-rationale"><span class="header-section-number">2.1</span> Features and rationale<a href="#features-and-rationale" class="self-link"></a></h2>
<p>This section describes at a high level the salient features of the
proposed design for sender algorithm customization, and their rationale.
But in a nutshell, the basic idea is as follows:</p>
<blockquote>
<p>For every invocation of a sender algorithm, the implementation looks
for a customization <em>twice</em>: once immediately while the algorithm
is constructing a sender to return, and once later <em>when the
resulting sender is <code class="sourceCode default">connect</code>-ed
with a receiver</em>.</p>
</blockquote>
<p>It is the second look-up that is new. By looking for a customization
at <code class="sourceCode default">connect</code> time, the dispatching
logic is informed both by information from the predecessor sender(s) as
well as from the receiver. It is the receiver that has information about
the environment of the currently executing asynchronous operation,
information that is key to picking the right customization in the cases
we looked at above.</p>
<h3 data-number="2.1.1" id="dispatching-via-execution-domain-tags"><span class="header-section-number">2.1.1</span> Dispatching via execution
domain tags<a href="#dispatching-via-execution-domain-tags" class="self-link"></a></h3>
<p>As described above, the
<code class="sourceCode default">when_all</code> sender doesn’t know its
completion scheduler, so we cannot use the completion scheduler to find
the <code class="sourceCode default">when_all</code> customization. Even
if all its child senders advertise completion schedulers with the same
type – say, <code class="sourceCode default">static_thread_pool</code> –
<code class="sourceCode default">when_all</code> itself can’t advertise
a completion scheduler because it doesn’t know that they are all the
<em>same</em>
<code class="sourceCode default">static_thread_pool</code>.</p>
<p>In the case just described, consider that we can know the completion
scheduler’s <em>type</em> but not its value. So at the very least, we
need to add a query about the type of the scheduler apart from its
value, for times when we know one but not the other.</p>
<p>Once we have done that, further generalizing the query from a
scheduler type to an abstract tag type is a short hop. We call this
abstract tag type an <em>execution domain</em>. Several different
scheduler types may all want to use the same set of algorithm
implementations; those schedulers can all use the same execution domain
type for the purpose of dispatching.</p>
<p>If <code class="sourceCode default">when_all</code>’s child senders
all share an execution domain, we know the execution domain of the
<code class="sourceCode default">when_all</code> sender itself even if
we don’t know which scheduler it will complete on. But that no longer
prevents us from dispatching to the correct implementation.</p>
<p>This paper proposes the addition of a forwarding
<code class="sourceCode default">get_domain</code> query in the
<code class="sourceCode default">std::execution</code> namespace, and
that the domain is used together with the algorithm tag to dispatch to
the correct algorithm implementation.</p>
<p>Additionally, we proposed that the
<code class="sourceCode default">when_all</code> algorithm only accepts
a set of senders when they all share a common domain. Likewise for
<code class="sourceCode default">let_value</code> and
<code class="sourceCode default">let_error</code>, we require that there
is only one possible domain on which their senders may complete.</p>
<h3 data-number="2.1.2" id="late-senderreceiver-connection-time-customization"><span class="header-section-number">2.1.2</span> Late (sender/receiver
connection-time) customization<a href="#late-senderreceiver-connection-time-customization" class="self-link"></a></h3>
<p>As described above, the sender algorithm customization points don’t
have all the information they need to dispatch to the correct algorithm
implementation in all cases. The solution is to look again for a
customization when all the information is available. That happens when
the sender is <code class="sourceCode default">connect</code>-ed to a
receiver.</p>
<p>This paper proposes the addition of a
<code class="sourceCode default">transform_sender</code> function that
is called by the <code class="sourceCode default">connect</code>
customization point to transform a sender prior to connecting it with
the receiver. The correct sender transformation is found using a
property read from the receiver’s environment.</p>
<p>The following comparison table shows how we propose to change the
<code class="sourceCode default">connect</code> customization point
(changes highlighted):</p>
<table style="caption-side: top;">
<caption>
Table 2: The addition of
<code class="sourceCode default">transform_sender</code> to
<code class="sourceCode default">connect</code>
</caption>
<thead>
<tr class="header">
<th style="text-align: center;">
<strong>Before</strong>
</th>
<th style="text-align: center;">
<strong>After</strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<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">struct</span> connect_t <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>receiver Receiver, sender_in<span class="op">&lt;</span>env_of_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span> Sender<span class="op">&gt;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sender<span class="op">&amp;&amp;</span> snd, Receiver<span class="op">&amp;&amp;</span> rcv<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">// First, look for a customization of tag_invoke:</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>tag_invocable<span class="op">&lt;</span>connect_t, Sender, Receiver<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> tag_invoke<span class="op">(*</span><span class="kw">this</span>,</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Receiver<span class="op">&gt;(</span>rcv<span class="op">))</span>;</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Next, see if the sender is co_await-able:</span></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><em>is-await-connectable</em><span class="op">&lt;</span>Sender, Receiver<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>      <span class="co">/* ... */</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
<td>
<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">struct</span> connect_t <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>receiver Receiver, sender_in<span class="op">&lt;</span>env_of_t<span class="op">&lt;</span>Receiver<span class="op">&gt;&gt;</span> Sender<span class="op">&gt;</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sender<span class="op">&amp;&amp;</span> snd, Receiver<span class="op">&amp;&amp;</span> rcv<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>    <mark><code class="sourceCode default">// Apply any sender tranformations using the receiver&#39;s domain:</code></mark></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <mark><code class="sourceCode default">auto&amp;&amp; snd2 = transform_sender(<em>get-domain-late</em>(snd, get_env(rcv)),</code></mark></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>                                    <mark><code class="sourceCode default">std::forward&lt;Sender&gt;(snd),</code></mark></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>                                    <mark><code class="sourceCode default">get_env(rcv));</code></mark></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <mark><code class="sourceCode default">using Sender2 = decltype(snd2);</code></mark></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">// First, look for a customization of tag_invoke:</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>tag_invocable<span class="op">&lt;</span>connect_t, <mark>Sender2</mark>, Receiver<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> tag_invoke<span class="op">(*</span><span class="kw">this</span>,</span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span><mark>Sender2</mark><span class="op">&gt;(</span><mark>snd2</mark><span class="op">)</span>,</span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Receiver<span class="op">&gt;(</span>rcv<span class="op">))</span>;</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Next, see if the sender is co_await-able:</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><em>is-await-connectable</em><span class="op">&lt;</span><mark>Sender2</mark>, Receiver<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>      <span class="co">/* ... */</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
</tr>
</tbody>
</table>
<h4 data-number="2.1.2.1" id="parity-with-coroutines"><span class="header-section-number">2.1.2.1</span> Parity with coroutines<a href="#parity-with-coroutines" class="self-link"></a></h4>
<p>The use of <code class="sourceCode default">transform_sender</code>
in <code class="sourceCode default">connect</code> it is analagous to
the use of <a href="https://eel.is/c++draft/expr.await#3.2"><code class="sourceCode default">await_transform</code></a>
in <code class="sourceCode default">co_await</code>. Glossing over some
details, in a coroutine the expression
<code class="sourceCode default">co_await <em>expr</em></code> is
“lowered” to <code class="sourceCode default">operator co_await(<em>p</em>.await_transform(<em>expr</em>)).await_suspend(<em>handle-to-p</em>)</code>,
where <em><code class="sourceCode default">p</code></em> is a reference
to coroutine’s promise. This gives the coroutine task type some say in
how <code class="sourceCode default">co_await</code> expressions are
evaluated.</p>
<p>The addition of
<code class="sourceCode default">transform_sender</code> to P2300
satisfies the same need to customize the launch behavior of child async
tasks. An expression like <code class="sourceCode default">connect(<em>sndr</em>, <em>detached-receiver</em>)</code>
is “lowered” to <code class="sourceCode default">connect(transform_sender(<em>domain</em>, <em>sndr</em>, get_env(<em>detached-receiver</em>)), <em>detached-receiver</em>)</code>,
where <em><code class="sourceCode default">domain</code></em> is a
property of the receiver’s environment. This gives the receiver some say
in how <code class="sourceCode default">connect</code> expressions are
evaluated.</p>
<h4 data-number="2.1.2.2" id="recursive-transformations"><span class="header-section-number">2.1.2.2</span> Recursive transformations<a href="#recursive-transformations" class="self-link"></a></h4>
<p>The author anticipates the need to sometimes apply a transformation
recursively to all of a sender’s child senders. Such a generic recursive
transformation might look something like 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="co">// my_domain applies a transformation recursively</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> my_domain<span class="op">::</span>transform_sender<span class="op">(</span>Sender<span class="op">&amp;&amp;</span> snd, <span class="kw">const</span> Env<span class="op">&amp;</span> env<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="op">[</span>tag, data, <span class="op">...</span>child<span class="op">]</span> <span class="op">=</span> snd;</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Create a temporary sender with transformed children</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> tmp <span class="op">=</span> <em>make-sender</em><span class="op">(</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    tag,</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>    data,</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>transform_sender<span class="op">(*</span><span class="kw">this</span>, child, env<span class="op">)...)</span>;</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Use the transformed children to compute a domain</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>  <span class="co">// (they all must share a domain or it&#39;s an error)</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span><span class="op">&amp;&amp;</span> <span class="op">[</span>x, y, <span class="op">...</span>child2<span class="op">]</span> <span class="op">=</span> tmp;</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> domain2 <span class="op">=</span> <em>common-domain-of</em><span class="op">(</span>child2<span class="op">...)</span>;</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Use the predecessor domain to transform the temporary sender:</span></span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> ex<span class="op">::</span>transform_sender<span class="op">(</span>domain2, move<span class="op">(</span>tmp<span class="op">)</span>, env<span class="op">)</span>;</span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This works well until we apply this function to a sender that
modifies the environment it passes to its child operations. Take the
case of <code class="sourceCode default">on(sch, snd)</code>: when it is
connected to a receiver, it connects its child sender
<code class="sourceCode default">snd</code> with a receiver whose
environment has been modified to show that the current scheduler is
<code class="sourceCode default">sch</code> (because
<code class="sourceCode default">on</code> will start
<code class="sourceCode default">snd</code> there).</p>
<p>But the implementation of
<code class="sourceCode default">my_domain::tranform_sender</code> above
does not update the environment when it is recursively transforming an
<code class="sourceCode default">on</code> sender’s child. That means
the child will be transformed with incorrect information about where it
will be executing, which can change the meaning of the transformation.
That’s not good. Something is missing.</p>
<p>We need a way to ask a sender to apply its transformation to an
environment. That is, in addition to
<code class="sourceCode default">transform_sender</code> we need
<code class="sourceCode default">transform_env</code> that can be used
to fix the code above as follows (differences highlighted):</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="co">// my_domain applies a transformation recursively</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> my_domain<span class="op">::</span>transform_sender<span class="op">(</span>Sender<span class="op">&amp;&amp;</span> snd, <span class="kw">const</span> Env<span class="op">&amp;</span> env<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="op">[</span>tag, data, <span class="op">...</span>child<span class="op">]</span> <span class="op">=</span> snd;</span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Apply any necessary transformations to the environment</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  <mark><code class="sourceCode default">auto&amp;&amp; env2 = ex::transform_env(*this, snd, env);</code></mark></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Create a temporary sender with transformed children,</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>  <span class="co">// using the transformed environment from the line above</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> tmp <span class="op">=</span> <em>make-sender</em><span class="op">(</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>    tag,</span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>    data,</span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>transform_sender<span class="op">(*</span><span class="kw">this</span>, child, <mark><code class="sourceCode default">env2</code></mark><span class="op">)...)</span>;</span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Use the transformed children to compute a domain</span></span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>  <span class="co">// (they all must share a domain or it&#39;s an error)</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span><span class="op">&amp;&amp;</span> <span class="op">[</span>x, y, <span class="op">...</span>child2<span class="op">]</span> <span class="op">=</span> tmp;</span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> domain2 <span class="op">=</span> <em>common-domain-of</em><span class="op">(</span>child2<span class="op">...)</span>;</span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>  <span class="co">// Use the predecessor domain to transform the temporary sender:</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> ex<span class="op">::</span>transform_sender<span class="op">(</span>domain2, move<span class="op">(</span>tmp<span class="op">)</span>, env<span class="op">)</span>;</span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Now expressions can generically be transformed recursively.</p>
<h3 data-number="2.1.3" id="early-sender-construction-time-customization"><span class="header-section-number">2.1.3</span> Early (sender
construction-time) customization<a href="#early-sender-construction-time-customization" class="self-link"></a></h3>
<p>We can use <code class="sourceCode default">transform_sender</code>
for early customization as well as late. The benefit of doing this is
that only one set of customizations needs be written for each domain,
rather than two (early and late).</p>
<p>This paper proposes that each algorithm constructs a default sender
that implements the default behavior for that algorithm. It then passes
that sender to <code class="sourceCode default">transform_sender</code>
along with the sender’s domain. The result of
<code class="sourceCode default">transform_sender</code> is what the
algorithm returns.</p>
<p>The following comparison table shows how we propose to change the
<code class="sourceCode default">connect</code> customization point:</p>
<table style="caption-side: top;">
<caption>
Table 3: The proposed changes to the
<code class="sourceCode default">then</code> customization point
</caption>
<thead>
<tr class="header">
<th style="text-align: center;">
<strong>Before</strong>
</th>
<th style="text-align: center;">
<strong>After</strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> then_t <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>sender Sender, <span class="kw">class</span> Fun<span class="op">&gt;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sender<span class="op">&amp;&amp;</span> snd, Fun fun<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// First, use the completion scheduler to look for a tag_invoke </span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span><em>has-customization</em><span class="op">&lt;</span>then_t, set_value_t, Sender, Fun<span class="op">&gt;)</span> <span class="op">{</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span><span class="op">&amp;&amp;</span> env <span class="op">=</span> get_env<span class="op">(</span>snd<span class="op">)</span>;</span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> tag_invoke<span class="op">(*</span><span class="kw">this</span>,</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>                        get_completion_scheduler<span class="op">&lt;</span>set_value_t<span class="op">&gt;(</span>env<span class="op">)</span>,</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">))</span>;</span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Otherwise, use the default implementation:</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> then<span class="op">-</span>sender<span class="op">&lt;</span>Sender, Fun<span class="op">&gt;(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a>                                      std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">))</span>;</span>
<span id="cb9-17"><a href="#cb9-17" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb9-18"><a href="#cb9-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb9-19"><a href="#cb9-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
<td>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> then_t <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>sender Sender, <span class="kw">class</span> Fun<span class="op">&gt;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Sender<span class="op">&amp;&amp;</span> snd, Fun fun<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Get the domain from the predecessor sender:</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> domain <span class="op">=</span> <em>get-domain-early</em><span class="op">(</span>snd<span class="op">)</span>;</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Create a `then` sender and ask the predecessor to</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">// transform it if desired</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> transform_sender<span class="op">(</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>      domain,</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>      <em>then-sender</em><span class="op">&lt;</span>Sender, Fun<span class="op">&gt;(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>,</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>                               std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">)))</span>;</span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
</tr>
</tbody>
</table>
<p>Some algorithms are required to do some work eagerly in their default
implementation (<em>e.g.</em>,
<code class="sourceCode default">split</code>,
<code class="sourceCode default">ensure_started</code>). These
algorithms must first create a dummy sender to pass to
<code class="sourceCode default">transform_sender</code>. The “default”
domain, which is used when no other domain has been specified, can
transform these dummy senders and do their eager work in the process.
The same mechanism is also useful to implement customizable sender
algorithms whose default implementation merely lowers to a more
primitive expression (<em>e.g.</em>
<code class="sourceCode default">transfer(snd,sch)</code> becomes
<code class="sourceCode default">schedule_from(sch,snd)</code>, and
<code class="sourceCode default">transfer_just(sch, ts...)</code>
becomes
<code class="sourceCode default">just(ts...) | transfer(sch)</code>).</p>
<p>For example, here is how the
<code class="sourceCode default">transfer_just</code> customization
point might look after the change:</p>
<table style="caption-side: top;">
<caption>
Table 4: The proposed changes to the
<code class="sourceCode default">then</code> customization point
</caption>
<thead>
<tr class="header">
<th style="text-align: center;">
<strong>Before</strong>
</th>
<th style="text-align: center;">
<strong>After</strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> transfer_just_t <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>scheduler Scheduler, <span class="kw">class</span><span class="op">...</span> Ts<span class="op">&gt;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Scheduler sch, Ts<span class="op">&amp;&amp;...</span> ts<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// First, use the completion scheduler to look for a tag_invoke </span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>      <em>has-customization</em><span class="op">&lt;</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>        transfer_just_t, set_value_t, Scheduler, Ts<span class="op">...&gt;)</span> <span class="op">{</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>      <span class="kw">auto</span><span class="op">&amp;&amp;</span> env <span class="op">=</span> get_env<span class="op">(</span>snd<span class="op">)</span>;</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> tag_invoke<span class="op">(*</span><span class="kw">this</span>,</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>                        get_completion_scheduler<span class="op">&lt;</span>set_value_t<span class="op">&gt;(</span>env<span class="op">)</span>,</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>move<span class="op">(</span>sch<span class="op">)</span>,</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>                        std<span class="op">::</span>forward<span class="op">&lt;</span>Ts<span class="op">&gt;(</span>ts<span class="op">)...)</span>;</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Otherwise, use the default implementation:</span></span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb11-17"><a href="#cb11-17" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> just<span class="op">(</span>std<span class="op">::</span>forward<span class="op">&lt;</span>Ts<span class="op">&gt;(</span>ts<span class="op">)...)</span></span>
<span id="cb11-18"><a href="#cb11-18" aria-hidden="true" tabindex="-1"></a>           <span class="op">|</span> transfer<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>sch<span class="op">))</span>;</span>
<span id="cb11-19"><a href="#cb11-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb11-20"><a href="#cb11-20" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb11-21"><a href="#cb11-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
<td>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> transfer_just_t <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>scheduler Scheduler, <span class="kw">class</span><span class="op">...</span> Ts<span class="op">&gt;</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> <span class="co">/* ... */</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> <span class="kw">operator</span><span class="op">()(</span>Scheduler sch, Ts<span class="op">&amp;&amp;...</span> ts<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Get the predecessor&#39;s domain</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> domain <span class="op">=</span> <em>get-domain-early</em><span class="op">(</span>snd<span class="op">)</span>;</span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Construct a transfer_just sender and transform it</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> transform_sender<span class="op">(</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>      domain,</span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>      <em>make-sender</em><span class="op">(*</span><span class="kw">this</span>, tuple<span class="op">{</span>std<span class="op">::</span>move<span class="op">(</span>sch<span class="op">)</span>,</span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>                               std<span class="op">::</span>forward<span class="op">&lt;</span>Ts<span class="op">&gt;(</span>ts<span class="op">)...}))</span>;</span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a>  <span class="co">// default_domain::transform_sender dispatches here:</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span><span class="kw">class</span> Sender, <span class="kw">class</span> Env<span class="op">&gt;</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> transform_sender<span class="op">(</span>Sender<span class="op">&amp;&amp;</span> snd, <span class="kw">const</span> Env<span class="op">&amp;</span> env<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="op">[</span>tag, data<span class="op">]</span> <span class="op">=</span> std<span class="op">::</span>forward<span class="op">&lt;</span>Sender<span class="op">&gt;(</span>snd<span class="op">)</span>;</span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="op">[</span>sch, <span class="op">...</span>ts<span class="op">]</span> <span class="op">=</span> std<span class="op">::</span>move<span class="op">(</span>data<span class="op">)</span>;</span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> just<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>ts<span class="op">)...)</span></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true" tabindex="-1"></a>         <span class="op">|</span> transfer<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>sch<span class="op">))</span>;</span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
</td>
</tr>
</tbody>
</table>
<p>Some algorithms are entirely eager with no lazy component, like
<code class="sourceCode default">sync_wait</code> and
<code class="sourceCode default">start_detached</code>. For these,
“transforming” a sender isn’t what you want; you want to dispatch to an
eager algorithm that will actually <em>consume</em> the sender. We can
use domains to dispatch to the correct implementation for those as well.
This paper proposes the addition of an
<code class="sourceCode default">apply_sender</code> function.</p>
<p>The following table describes the differences between
<code class="sourceCode default">transform_sender</code> and
<code class="sourceCode default">apply_sender</code>:</p>
<table style="caption-side: top;">
<caption>
Table 5: The differences between
<code class="sourceCode default">transform_sender</code> and
<code class="sourceCode default">apply_sender</code>
</caption>
<thead>
<tr class="header">
<th style="text-align: center;">
<strong><code class="sourceCode default">transform_sender</code></strong>
</th>
<th style="text-align: center;">
<strong><code class="sourceCode default">apply_sender</code></strong>
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<ul>
<li>
always called with either a sender or a sender+env
</li>
<li>
always returns a sender
</li>
<li>
has a sensible default implementation: identity
</li>
<li>
useful for lazy or partly lazy algorithms (e.g.,
<code class="sourceCode default">then</code>,
<code class="sourceCode default">ensure_started</code>)
</li>
</ul>
</td>
<td>
<ul>
<li>
called with a sender and an arbitrary set of additional arguments
</li>
<li>
can return anything
</li>
<li>
has no sensible default implementation
</li>
<li>
useful for fully eager algorithms (e.g.,
<code class="sourceCode default">start_detached</code>,
<code class="sourceCode default">sync_wait</code>)
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>To permit third parties to author customizable sender algorithms with
partly or fully eager behavior, the mechanism by which the default
domain finds the default
<code class="sourceCode default">transform_sender</code> and
<code class="sourceCode default">apply_sender</code> implementations
shall be specified: they both dispatch to similarly named functions on
the tag type of the input sender; i.e., <code class="sourceCode default">default_domain().transform_sender(snd, env)</code>
is equal to <code class="sourceCode default">tag_of_t&lt;decltype(snd)&gt;().transform_sender(snd, env)</code>.</p>
<h3 data-number="2.1.4" id="decomposable-senders"><span class="header-section-number">2.1.4</span> Decomposable senders<a href="#decomposable-senders" class="self-link"></a></h3>
<p>For the <code class="sourceCode default">transform_sender</code>
customization point to be useful, we need a way to access the
constituent pieces of a sender and re-assemble it from (possibly
transformed) pieces. Senders, like coroutines, generally begin in a
“suspended” state; they merely curry their algorithm’s arguments into a
subsequent call to <code class="sourceCode default">connect</code>.
These “suspended” senders are colloquially known as <em>lazy</em>
senders.</p>
<p>Each lazy sender has an associated algorithm tag, a (possibly empty)
set of auxiliary data and a (possibly empty) set of child senders;
<em>e.g.</em>, the sender returned from
<code class="sourceCode default">then(snd, fun)</code> has
<code class="sourceCode default">then_t</code> as its tag, the set
<code class="sourceCode default">[fun]</code> as its auxiliary data, and
<code class="sourceCode default">[snd]</code> as its set of child
senders, while <code class="sourceCode default">just(42, 3.14)</code>
has <code class="sourceCode default">just_t</code> as its tag,
<code class="sourceCode default">[42, 3.14]</code> as its data set and
<code class="sourceCode default">[]</code> as its child set.</p>
<p>This paper proposes to use structured bindings as the API for
decomposing a lazy sender into its tag, data, and child senders:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span><span class="op">&amp;&amp;</span> <span class="op">[</span>tag, data, <span class="op">...</span>children<span class="op">]</span> <span class="op">=</span> snd;</span></code></pre></div>
<p><span class="citation" data-cites="P1061R5">[<a href="#ref-P1061R5" role="doc-biblioref">P1061R5</a>]</span>, currently in Core wording
review for C++26, permits the declaration of variadic structured
bindings like above, making this syntax very appealing.</p>
<p>Not all senders are required to be decomposable, although all the
“standard” lazy senders shall be. There needs to be a syntactic way to
distinguish between decomposable and ordinary,non-decomposable senders
(decomposable senders subsuming the
<code class="sourceCode default">sender</code> concept).</p>
<p>There is currently no trait for determining whether a type can be the
initializer of a structured binding. However, EWG has already approved
<span class="citation" data-cites="P2141R1">[<a href="#ref-P2141R1" role="doc-biblioref">P2141R1</a>]</span> for C++26, and with it such a
trait could be built, giving us a simple way to distinguish between
decomposable and non-decomposable senders.</p>
<p>If P2141 is not adopted for C++26, we will need some other syntactic
way to opt-in. One possibility is to require that the sender type’s
nested <code class="sourceCode default">is_sender</code> type shall have
some known, standard tag type as a base class to signify that that
sender type can be decomposed.</p>
<h4 data-number="2.1.4.1" id="recomposing-senders"><span class="header-section-number">2.1.4.1</span> Recomposing senders<a href="#recomposing-senders" class="self-link"></a></h4>
<p>After decomposing a sender, it is often desirable to re-compose it
from its modified constituents. No separate API for reconstituting
senders is necessary though. It is enough to construct a decomposable
sender of some arbitrary type and then pass it to
<code class="sourceCode default">transform_sender</code> with an
execution domain to place it in its final form.</p>
<p>Consider the case where
<code class="sourceCode default">my_domain::transform_sender()</code> is
passed
<code class="sourceCode default">your_sender&lt;Children...&gt;</code>.
It unpacks it into its tag/data/children constituents and munges the
children somehow. It then wants to reconstruct a
<code class="sourceCode default">your_sender</code> from the munged
children. Instead, it constructs <code class="sourceCode default"><em>arbitrary-sender</em>{tag, data, munged_children}</code>
and passes it to
<code class="sourceCode default">execution::transform_sender()</code>
along with <code class="sourceCode default">your_domain</code>, the
domain associated with
<code class="sourceCode default">your_sender</code>. That presumably
will transform the
<em><code class="sourceCode default">arbitrary-sender</code></em> back
into a <code class="sourceCode default">your_sender</code>.</p>
<h2 data-number="2.2" id="why-have-both-early-and-late-customization"><span class="header-section-number">2.2</span> Why have both early and late
customization?<a href="#why-have-both-early-and-late-customization" class="self-link"></a></h2>
<p>The full information necessary to dispatch to the correct algorithm
implementation isn’t available until the execution environment is known,
when we are <code class="sourceCode default">connect</code>-ing a sender
to a receiver. Adding a
<code class="sourceCode default">connect</code>-time search for a
customization is sensible. One might wonder whether it is necessary to
keep the early construction-time customization search.</p>
<p>With senders that can be de- and re-composed, it is true that any
transformation that can be applied at sender construction time can be
expressed instead as a tree transformation at
<code class="sourceCode default">connect</code> time, so strictly
speaking the early customization phase is unnecessary. There are
technical and proceedural reasons to keep the early customization phase,
however. Here we list a few.</p>
<ol type="1">
<li><p><strong>Some transformations are more easily and efficiently
applied at sender construction time.</strong> Let’s consider a simple
pipeline like the following:</p>
<blockquote>
<div class="sourceCode" id="cb14"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> snd <span class="op">=</span> on<span class="op">(</span> io_sched, fetch_data<span class="op">()</span> <span class="op">)</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> transfer<span class="op">(</span> cpu_sched <span class="op">)</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>          <span class="op">|</span> then<span class="op">(</span> compute <span class="op">)</span>;</span></code></pre></div>
</blockquote>
<p>When constructing the outermost
<code class="sourceCode default">then</code> sender, the information
about where it will execute is readily available: the left operand of
the <code class="sourceCode default">| then(..)</code> expression has
perfect information that the
<code class="sourceCode default">then</code> algorithm can use
immediately to pick the correct algorithm implementation.</p>
<p>Had we deferred the customization until
<code class="sourceCode default">connect</code> time, we are presented
with a problem: when we are
<code class="sourceCode default">connect</code>-ing the
<code class="sourceCode default">then</code> sender, the information
about where it will execute is buried within the expression tree.
<code class="sourceCode default">connect</code> would have to introspect
the tree to compute the information, which is far more
complicated.</p></li>
<li><p><strong>Domain-specific eager transformations can create fluid
interfaces.</strong> Users might choose to apply an eager transformation
to decorate the “standard” senders in ways that make them more useful
for their purposes. They might add members, adapt them to more easily
interoperate with another framework, add a
<code class="sourceCode default">co_await</code> operator, or add
implicit conversions. They might even choose to pass each sender through
<code class="sourceCode default">ensure_started</code> to get work
executing before the task graph is fully built.</p></li>
<li><p><strong>Removing eager execution presents procedural
difficulties.</strong> Support for eager execution via customization was
needed for consensus in SG1. Any attempt to remove eager execution would
bounce P2300 back to SG1 where the removal is likely to be met
unfavorably. And in order for that to happen, someone would have to
write and champion a paper proposing the removal, and it is unclear who
would do that work.</p></li>
</ol>
<h2 data-number="2.3" id="an-example-of-early-and-late-customizations"><span class="header-section-number">2.3</span> An example of early and late
customizations<a href="#an-example-of-early-and-late-customizations" class="self-link"></a></h2>
<p>In this section, we work through an example of a sender expression
with multiple transitions between two domains. We show the interactions
between a predecessor sender’s completion scheduler, and a receiver’s
scheduler. In the end, we’ll see that customized senders will run on the
scheduler corresponding to the domain that created them.</p>
<p>First, we define two domains:
<code class="sourceCode default">domainA</code> and
<code class="sourceCode default">domainB</code>, and we give each a
customization of the <code class="sourceCode default">then()</code>
algorithm that replaces the default
<code class="sourceCode default">then</code> sender with a custom one
that prints tracing messages to
<code class="sourceCode default">cout</code>.</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> domainA <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>  <span class="kw">template</span> <span class="op">&lt;</span>sender Sender, <span class="kw">class</span><span class="op">...</span> Env<span class="op">&gt;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">requires</span> same_as<span class="op">&lt;</span>ex<span class="op">::</span>tag_of_t<span class="op">&lt;</span>Sender<span class="op">&gt;</span>, ex<span class="op">::</span>then_t<span class="op">&gt;</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> transform_sender<span class="op">(</span>Sender<span class="op">&amp;&amp;</span> snd, <span class="kw">const</span> Env<span class="op">&amp;...</span> env<span class="op">)</span> <span class="kw">const</span> <span class="op">{</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>    std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;hello from domain A transform_sender, &quot;</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>              <span class="op">&lt;&lt;</span> <span class="op">(</span><span class="kw">sizeof</span><span class="op">...(</span>env<span class="op">)</span> <span class="op">?</span> <span class="st">&quot;late&quot;</span> <span class="op">:</span> <span class="st">&quot;early&quot;</span><span class="op">)</span> <span class="op">&lt;&lt;</span> <span class="ch">&#39;</span><span class="sc">\n</span><span class="ch">&#39;</span>;</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">auto</span> <span class="op">[</span>tag, fun, child<span class="op">]</span> <span class="op">=</span> <span class="op">(</span>Sender<span class="op">&amp;&amp;)</span> snd;</span>
<span id="cb15-8"><a href="#cb15-8" aria-hidden="true" tabindex="-1"></a>    <span class="cf">return</span> trace_then_sender<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>child<span class="op">)</span>, std<span class="op">::</span>move<span class="op">(</span>fun<span class="op">)</span>, <span class="st">&quot;A&quot;</span><span class="op">)</span>;</span>
<span id="cb15-9"><a href="#cb15-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb15-10"><a href="#cb15-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>;</span></code></pre></div>
<p><code class="sourceCode default">domainB</code> is defined similarly.
Note that this <code class="sourceCode default">transform_sender</code>
member is constrained to only accept
<code class="sourceCode default">then</code> senders, and that it
replaces the default <code class="sourceCode default">then</code> sender
with a custom one. We use the presence or absence of the second
parameter, <code class="sourceCode default">env</code>, to tell whether
this transformation is being done early or late and print that to
<code class="sourceCode default">cout</code>.</p>
<p>Let’s assume the existence of an execution context that lets us
schedule work onto a worker thread and that is parameterizable with an
execution domain. We create two such contexts and give each one a name
that gets stored in thread-local storage.</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">thread_local</span> std<span class="op">::</span>string thread_name<span class="op">{}</span>;</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>  thread_context<span class="op">&lt;</span>domainA<span class="op">&gt;</span> ctxA<span class="op">(</span><span class="st">&quot;A&quot;</span><span class="op">)</span>;</span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a>  thread_context<span class="op">&lt;</span>domainB<span class="op">&gt;</span> ctxB<span class="op">(</span><span class="st">&quot;B&quot;</span><span class="op">)</span>;</span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> schedA <span class="op">=</span> ctxA<span class="op">.</span>get_scheduler<span class="op">()</span>;</span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> schedB <span class="op">=</span> ctxB<span class="op">.</span>get_scheduler<span class="op">()</span>;</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> hello <span class="op">=</span> <span class="op">[]</span> <span class="op">{</span> std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;  running on thread &quot;</span> <span class="op">&lt;&lt;</span> thread_name <span class="op">&lt;&lt;</span> <span class="ch">&#39;</span><span class="sc">\n</span><span class="ch">&#39;</span>; <span class="op">}</span>;</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a><span class="co">//...</span></span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Next, let’s create a sender with a transition to one of the thread
execution contexts. Each line is annotated with information about the
currenly active domain.</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> work <span class="op">=</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>    ex<span class="op">::</span>just<span class="op">()</span>           <span class="co">// no domain here</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> ex<span class="op">::</span>then<span class="op">(</span>hello<span class="op">)</span>      <span class="co">// no domain here</span></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> ex<span class="op">::</span>transfer<span class="op">(</span>schedA<span class="op">)</span> <span class="co">// transition into domain A</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> ex<span class="op">::</span>then<span class="op">(</span>hello<span class="op">)</span>;     <span class="co">// &quot;hello from domain A transform_sender, early&quot;</span></span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>                         <span class="co">//    (using predecessor&#39;s domain via completion scheduler)</span></span></code></pre></div>
<p>This statement causes:</p>
<blockquote>
<div class="sourceCode" id="cb1"><pre class="sourceCode txt"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>hello from domain A transform_sender, early</span></code></pre></div>
</blockquote>
<p>to be printed to <code class="sourceCode default">cout</code>. This
is because the <code class="sourceCode default">then</code> that follows
<code class="sourceCode default">transfer(schedA)</code> gets
transformed by the
<code class="sourceCode default">transform_sender</code> of
<code class="sourceCode default">domainA</code>, the domain associated
with the completion scheduler of
<code class="sourceCode default">transfer(schedA)</code>; i.e.,
<code class="sourceCode default">schedA</code>.</p>
<p>Next, we launch this work on thread context “B” and wait for it to
complete:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>ex<span class="op">::</span>sync_wait<span class="op">(</span> ex<span class="op">::</span>on<span class="op">(</span> schedB, work <span class="op">)</span> <span class="op">)</span>;</span></code></pre></div>
<p>The composite sender passed to
<code class="sourceCode default">sync_wait</code> has an
<code class="sourceCode default">on</code> sender that is outermost.
Stored immidiately within that is the
<code class="sourceCode default">trace_then_sender</code>, amd within
that the <code class="sourceCode default">transfer</code>,
<code class="sourceCode default">then</code>, and
<code class="sourceCode default">just</code> senders, with
<code class="sourceCode default">just</code> being innermost.</p>
<p><code class="sourceCode default">sync_wait</code> calls
<code class="sourceCode default">connect</code> on the
<code class="sourceCode default">on</code> sender, passing it a
receiver. It is the job of <code class="sourceCode default">on</code> to
launch its child on <code class="sourceCode default">schedB</code>. It
tells its child where it is executing by putting
<code class="sourceCode default">schedB</code> in the environment of the
receiver that <code class="sourceCode default">on</code> connects it
with. So, when the
<code class="sourceCode default">trace_then_sender</code> gets
connected, it sees <code class="sourceCode default">schedB</code> as the
current scheduler in the receiver’s environment.</p>
<p>The <code class="sourceCode default">connect</code> customization
point is responsible for applying any late customization by using the
current execution domain to find the correct implementation. The
<code class="sourceCode default">trace_then_sender</code> presents
<code class="sourceCode default">connect</code> with a connundrum: the
domain in the receiver is
<code class="sourceCode default">domainB</code>, but the domain of the
predecessor sender
(<code class="sourceCode default">ex::transfer(schedA)</code>) is
<code class="sourceCode default">domainA</code>. So which should
<code class="sourceCode default">connect</code> use?</p>
<p>The answer is <code class="sourceCode default">domainA</code>, the
domain from the predecessor sender. Looking at the expression, we can
see that the outermost <code class="sourceCode default">then</code> will
be executed on <code class="sourceCode default">schedA</code>, so using
<code class="sourceCode default">domainA</code> to find the right
implementation is correct. The rule is: domain information from the
predecessor(s) trumps domain information from the receiver.</p>
<p>So we use <code class="sourceCode default">domainA</code> to
transform the <code class="sourceCode default">trace_then_sender</code>.
But <code class="sourceCode default">domainA</code> doesn’t have a
<code class="sourceCode default">transform_sender</code> that accepts
<code class="sourceCode default">trace_then_sender</code>; it is passed
through unchanged and then
<code class="sourceCode default">connect</code>-ed.</p>
<p>That in turn causes
<code class="sourceCode default">transfer(schedA)</code> to be connected
with a <code class="sourceCode default">trace_then_receiver</code>.
There is no need to update the receiver’s scheduler;
<code class="sourceCode default">transfer</code> runs its
<em>contunuation</em> on <code class="sourceCode default">schedA</code>.
Its child is <em>started</em> on
<code class="sourceCode default">schedB</code> so we leave the
environment alone.</p>
<p>The <code class="sourceCode default">transfer</code> sender then
connects its child, which is a
<code class="sourceCode default">then</code> sender. The current domain
is still <code class="sourceCode default">domainB</code> because the
scheduler in the receiver’s environment is still
<code class="sourceCode default">schedB</code>. The
<code class="sourceCode default">connect</code> customization point uses
<code class="sourceCode default">domainB</code> to transform the
<code class="sourceCode default">then</code> sender, and the following
is printed on the terminal:</p>
<blockquote>
<div class="sourceCode" id="cb19"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a>hello from domain B transform_sender, late</span></code></pre></div>
</blockquote>
<p>The <code class="sourceCode default">then</code> sender gets
transformed into a
<code class="sourceCode default">trace_then_sender</code>, which
remembers that it was created by
<code class="sourceCode default">domainB</code>. That sender is then
connected, which causes the <code class="sourceCode default">just</code>
sender to be connected, and finally the operation state is fully
constructed. The <code class="sourceCode default">on</code> operation
state is outermost and the <code class="sourceCode default">just</code>
operation state is innermost.</p>
<p><code class="sourceCode default">sync_wait</code> then calls
<code class="sourceCode default">start</code> on the
<code class="sourceCode default">on</code> operation state, which causes
execution to transition to thread “B”. There, all the nested operation
states are started in turn, with the
<code class="sourceCode default">just</code> operation state being
started last.</p>
<p>Being the last started, it is the first to complete. That causes its
parent operation to complete, its parent being the
<code class="sourceCode default">trace_then_sender</code> that was
created by <code class="sourceCode default">domainB</code>.
<code class="sourceCode default">set_value</code> is invoked on
<code class="sourceCode default">trace_then_receiver</code>. The
<code class="sourceCode default">trace_then_receiver</code> has a
<code class="sourceCode default">set_value</code> customization that
looks like this:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> tag_invoke<span class="op">(</span>ex<span class="op">::</span>set_value_t, trace_then_receiver<span class="op">&amp;&amp;</span> self<span class="op">)</span> <span class="kw">noexcept</span> <span class="op">{</span></span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a>  std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;then sender from domain &quot;</span> <span class="op">&lt;&lt;</span> self<span class="op">.</span>name <span class="op">&lt;&lt;</span> <span class="ch">&#39;</span><span class="sc">\n</span><span class="ch">&#39;</span>;</span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>  self<span class="op">.</span>fun<span class="op">()</span>;</span>
<span id="cb20-4"><a href="#cb20-4" aria-hidden="true" tabindex="-1"></a>  ex<span class="op">::</span>set_value<span class="op">(</span>std<span class="op">::</span>move<span class="op">(</span>self<span class="op">.</span>rcv<span class="op">))</span>;</span>
<span id="cb20-5"><a href="#cb20-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The following is printed to
<code class="sourceCode default">cout</code>:</p>
<blockquote>
<div class="sourceCode" id="cb21"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a>then sender from domain B</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a>  running on thread B</span></code></pre></div>
</blockquote>
<p><em>So we see that we have run “B”’s
<code class="sourceCode default">trace_then_sender</code> on thread
“B”</em>, which is exactly the point.</p>
<p>Next to complete is the
<code class="sourceCode default">transfer</code> to
<code class="sourceCode default">schedA</code>, which causes execution
to hop over to thread “A”. The
<code class="sourceCode default">trace_then_sender</code> created by
<code class="sourceCode default">domainA</code> completes next, causing
the following to be printed:</p>
<blockquote>
<div class="sourceCode" id="cb22"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a>then sender from domain A</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a>  running on thread A</span></code></pre></div>
</blockquote>
<p><em>So we have run “A”’s
<code class="sourceCode default">trace_then_sender</code> on thread
“A”.</em> As it should be.</p>
<p>Finally, the <code class="sourceCode default">on</code> sender
completes, which signals completion to the main thread and
<code class="sourceCode default">sync_wait</code> returns.</p>
<p>In summary, the rules governing the active domain both at sender
construction time and at sender connection time work together to ensure
that when an algorithm is executing on a particular scheduler, it is the
correct algorithm implementation that is getting executed. The
dispatching is controled by the active domain, which is used to
transform senders into ones that implement the algorithm correctly for
the current execution context.</p>
<p>The code for this example can be found in <a href="https://gist.github.com/ericniebler/3c810343d153af4c0edaeb4f4751bc39">this
gist</a>, which was compiled against <a href="https://github.com/NVIDIA/stdexec/commit/2e3ba0d84ba8889514c75294c2689ee6092c7446">this
revision</a> of stdexec, the
<code class="sourceCode default">std::execution</code> reference
implementation.</p>
<h2 data-number="2.4" id="summary-of-proposed-changes"><span class="header-section-number">2.4</span> Summary of proposed changes<a href="#summary-of-proposed-changes" class="self-link"></a></h2>
<p>In condensed form, here are the changes this paper is proposing:</p>
<ol>
<li><p>Add a <code class="sourceCode default">default_domain</code> type
for use when no other domain is determinable.</p></li>
<li><p>Add a new <code class="sourceCode default">get_domain(<em>env</em>) -&gt; <em>domain-tag</em></code>
forwarding query.</p></li>
<li><p>A sender can publish up to 3 different completion schedulers.
They can all be different, but they must all agree about the current
execution domain. This paper proposes adding that as a requirement to
the <code class="sourceCode default">sender</code> concept.</p></li>
<li><p>Add a new <code class="sourceCode default">transform_sender(<em>domain</em>, <em>sender</em> [, <em>env</em>]) -&gt; <em>sender</em></code>
API. This function is not itself customizable, but it will be used for
both early customization (at sender construction-time) and late
customization (at sender/receiver connection-time).</p>
<p><em>Early customization:</em></p>
<ul>
<li>called from within each sender algorithm’s customization point
object</li>
<li>replaces the current mechanism of tag-dispatching to a sender
factory function using the completion scheduler as a tag</li>
<li>called without an environment argument</li>
<li><code class="sourceCode default"><em>domain</em></code> is derived
from the sender by trying the following in order:
<ol type="1">
<li><code class="sourceCode default">get_domain(get_env(<em>sender</em>))</code></li>
<li><code class="sourceCode default"><em>completion-domain</em>(<em>sender</em>)</code>,
where <code class="sourceCode default"><em>completion-domain</em></code>
is the domain shared by all of the sender’s completion signatures.</li>
<li><code class="sourceCode default">default_domain()</code></li>
</ol></li>
</ul>
<p><em>Late customization:</em></p>
<ul>
<li>called from the <code class="sourceCode default">connect</code>
customization point object before tag-dispatching with
<code class="sourceCode default">connect_t</code> to
<code class="sourceCode default">tag_invoke</code></li>
<li>called with the receiver’s environment</li>
<li><code class="sourceCode default"><em>domain</em></code> is derived
from the sender and the receiver by trying the following in order:
<ol type="1">
<li><code class="sourceCode default">get_domain(get_env(<em>sender</em>))</code></li>
<li><code class="sourceCode default"><em>completion-domain</em>(<em>sender</em>)</code>,</li>
<li><code class="sourceCode default">get_domain(get_env(<em>receiver</em>))</code></li>
<li><code class="sourceCode default">get_domain(get_scheduler(get_env(<em>receiver</em>)))</code></li>
<li><code class="sourceCode default">default_domain()</code></li>
</ol></li>
</ul>
<p><code class="sourceCode default">transform_sender(<em>domain</em>, <em>sender</em> [, <em>env</em>])</code>
returns the first of these that is well-formed:</p>
<ul>
<li><code class="sourceCode default"><em>domain</em>.transform_sender(<em>sender</em> [, <em>env</em>])</code></li>
<li><code class="sourceCode default">default_domain().transform_sender(<em>sender</em> [, <em>env</em>])</code></li>
<li><code class="sourceCode default"><em>sender</em></code></li>
</ul>
<p>If the sender returned from
<code class="sourceCode default">transform_sender</code> has a different
type than the one passed to it,
<code class="sourceCode default">transform_sender</code> invokes itself
recursively.</p></li>
<li><p>Add a <code class="sourceCode default">transform_env(<em>domain</em>, <em>sender</em>, <em>env</em>) -&gt; <em>env’</em></code>
API in support of generic recursive sender transformations. The
<code class="sourceCode default"><em>domain</em></code> argument is
determined from <em><code class="sourceCode default">sender</code></em>
and <em><code class="sourceCode default">env</code></em> as for
<code class="sourceCode default">transform_sender</code>.</p>
<p><code class="sourceCode default">transform_env(<em>domain</em>, <em>sender</em>, <em>env</em>)</code>
returns the first of these that is well-formed:</p>
<ul>
<li><code class="sourceCode default"><em>domain</em>.transform_env(<em>sender</em>, <em>env</em>)</code></li>
<li><code class="sourceCode default">default_domain().transform_env(<em>sender</em>, <em>env</em>)</code></li>
</ul></li>
<li><p>The standard, “lazy” sender types (i.e., those returned from
sender factory and adaptor functions) return sender types that are
decomposable using structured bindings into its [<em>tag</em>,
<em>data</em>, …<em>children</em>] components.</p></li>
<li><p>A call to the <code class="sourceCode default">when_all</code>
algorithm should be ill-formed unless all of the sender arguments have
the same domain type (as determined for senders above). The resulting
<code class="sourceCode default">when_all</code> sender should publish
that domain via the sender’s environment.</p></li>
<li><p>The receiver that the
<code class="sourceCode default">on(sch, snd)</code> algorithm uses to
connect <code class="sourceCode default">snd</code> should have
<code class="sourceCode default">sch</code> as the current scheduler in
its environment.</p></li>
<li><p>The sender factories
<code class="sourceCode default">just</code>,
<code class="sourceCode default">just_error</code>, and
<code class="sourceCode default">just_stopped</code> need their tag
types to be specified. Name them
<code class="sourceCode default">just_t</code>,
<code class="sourceCode default">just_error_t</code>, and
<code class="sourceCode default">just_stopped_t</code>.</p></li>
<li><p>In the algorithm
<code class="sourceCode default">let_value(snd, fun)</code>, if the
predecessor sender <code class="sourceCode default">snd</code> has a
completion domain, then the receiver connected to the secondary sender
(the one returned from <code class="sourceCode default">fun</code> when
called with <code class="sourceCode default">snd</code>’s results) shall
expose that domain as the current one in the receiver’s environment.</p>
<p>In other words, if the predecessor sender
<code class="sourceCode default">snd</code> completes with values
<code class="sourceCode default">vs...</code>, then the result of
<code class="sourceCode default">fun(vs...)</code> will be connected to
a receiver <code class="sourceCode default">rcv</code> such that
<code class="sourceCode default">get_domain(get_env(rcv))</code> is
equal to
<code class="sourceCode default"><em>completion-domain</em>(snd)</code>.</p>
<p>The same is true also of the completion scheduler, if the predecessor
has one.</p>
<p>So for <code class="sourceCode default">let_value</code>, likewise
also for <code class="sourceCode default">let_error</code> and
<code class="sourceCode default">let_stopped</code>, using
<code class="sourceCode default">set_error_t</code> and
<code class="sourceCode default">set_stopped</code> respectively when
querying for the predecessor sender’s completion scheduler and
domain.</p></li>
<li><p>The
<code class="sourceCode default">schedule_from(sch, snd)</code>
algorithm should return a sender
<code class="sourceCode default">snd2</code> such that
<code class="sourceCode default">get_domain(get_env(snd2))</code> is
equal to
<code class="sourceCode default">get_domain(sch)</code>.</p></li>
<li><p>The following customizable algorithms, whose default
implementations must do work before returning the result sender, will
have their work performed in overloads of
<code class="sourceCode default">default_domain::transform_sender</code>:</p>
<ul>
<li><code class="sourceCode default">split</code></li>
<li><code class="sourceCode default">ensure_started</code></li>
</ul></li>
<li><p>The following customizable algorithms, whose default
implementations are trivially expressed in terms of other more primitive
operations, will be lowered into their primitive forms by overloads of
<code class="sourceCode default">default_domain::transform_sender</code>:</p>
<ul>
<li><code class="sourceCode default">transfer</code></li>
<li><code class="sourceCode default">transfer_just</code></li>
<li><code class="sourceCode default">transfer_when_all</code></li>
<li><code class="sourceCode default">transfer_when_all_with_variant</code></li>
<li><code class="sourceCode default">when_all_with_variant</code></li>
</ul></li>
<li><p>In the algorithm
<code class="sourceCode default">let_value(snd, fun)</code>, all of the
sender types that the input function
<code class="sourceCode default">fun</code> might return – the set of
potential result senders – must all have the same domain; otherwise, the
call to <code class="sourceCode default">let_value</code> is
ill-formed.</p>
<p>Ideally, the <code class="sourceCode default">let_value</code> sender
would report the result senders’ domain as its domain, however we don’t
know the set of completions until the
<code class="sourceCode default">let_value</code> sender is connected to
a receiver; hence, we also don’t know the set of potential result
senders or their domains. Instead, we require that all the result
senders share an execution domain with the predecessor sender. If they
differ, <code class="sourceCode default">connect</code> is
ill-formed.</p>
<p>For example, consider the following sender:</p>
<blockquote>
<div class="sourceCode" id="cb23"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="kw">auto</span> snd <span class="op">=</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>    read<span class="op">(</span>get_scheduler<span class="op">)</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> transfer<span class="op">(</span>schA<span class="op">)</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> let_value<span class="op">([](</span><span class="kw">auto</span> schB<span class="op">){</span> <span class="cf">return</span> schedule<span class="op">(</span>schB<span class="op">)</span>; <span class="op">})</span></span></code></pre></div>
</blockquote>
<p>This reads the current scheduler from the receiver, transfers
execution to <code class="sourceCode default">schA</code>, and then
(indirectly, through <code class="sourceCode default">let_value</code>)
transitions onto the scheduler read from the receiver
(<code class="sourceCode default">schB</code>). This sender can be
<code class="sourceCode default">connect</code>-ed only to receivers
<code class="sourceCode default">Rcv</code> for which the scheduler
<code class="sourceCode default">get_scheduler(get_env(Rcv))</code> has
the same execution domain as that of
<code class="sourceCode default">schA</code>.</p>
<p>Likewise for <code class="sourceCode default">let_error</code> and
<code class="sourceCode default">let_stopped</code>.</p>
<p>This solution is not ideal. I am currently working on a more flexible
solution, but I’m not yet sufficiently confident in it to propose it
here.</p></li>
<li><p>Add a new <code class="sourceCode default">apply_sender(<em>domain</em>, <em>tag</em>, <em>sender</em>, <em>args</em>...) -&gt; <em>result</em></code>
API. Like <code class="sourceCode default">transform_sender</code>, this
function is not itself customizable, but it will be used to customize
sender consuming algorithms such as
<code class="sourceCode default">start_detached</code> and
<code class="sourceCode default">sync_wait</code>.</p>
<ul>
<li>called from within each consuming sender algorithm’s customization
point object</li>
<li>replaces the current mechanism of tag-dispatching to a custom
implementation using the completion scheduler as a tag</li>
<li>called the sender’s execution domain, the algorithm’s tag, the
sender, and any additional algorithm arguments</li>
<li><code class="sourceCode default"><em>domain</em></code> is
determined as for
<code class="sourceCode default">transform_sender</code></li>
</ul>
<p><code class="sourceCode default">apply_sender(<em>domain</em>, <em>tag</em>, <em>sender</em>, <em>args</em>...)</code>
returns the first of these that is well-formed:</p>
<ul>
<li><code class="sourceCode default"><em>domain</em>.apply_sender(<em>tag</em>, <em>sender</em>, <em>args</em>...)</code></li>
<li><code class="sourceCode default">default_domain().apply_sender(<em>tag</em>, <em>sender</em>, <em>args</em>...)</code></li>
</ul></li>
<li><p>The following customizable sender-consuming algorithms will have
their default implementations in overloads of
<code class="sourceCode default">default_domain::apply_sender</code>:</p>
<ul>
<li><code class="sourceCode default">start_detached</code></li>
<li><code class="sourceCode default">sync_wait</code></li>
</ul></li>
</ol>
<h1 data-number="3" id="implementation-experience"><span class="header-section-number">3</span> Implementation Experience<a href="#implementation-experience" class="self-link"></a></h1>
<p><em>Has it been implented?</em> YES. The design changes herein
proposed are implemented in the main branch of <span class="citation" data-cites="stdexecgithub">[<a href="#ref-stdexecgithub" role="doc-biblioref">stdexecgithub</a>]</span>, the reference
implementation. The bulk of the changes including
<code class="sourceCode default">get_domain</code>,
<code class="sourceCode default">transform_sender</code>, and the
changes to <code class="sourceCode default">connect</code> have been
shipping since <a href="https://github.com/NVIDIA/stdexec/commit/0693876c6144479ab5d9bec671751bd32d14e23a">this
commit</a> on August 3, 2023 which changed the
<code class="sourceCode default">static_thread_pool</code> scheduler to
use <code class="sourceCode default">transform_sender</code> to
parallelize the <code class="sourceCode default">bulk</code>
algorithm.</p>
<h1 data-number="4" id="proposed-wording"><span class="header-section-number">4</span> Proposed Wording<a href="#proposed-wording" class="self-link"></a></h1>
<p>The following proposed changes are relative to <span class="citation" data-cites="P2300R7">[<a href="#ref-P2300R7" role="doc-biblioref">P2300R7</a>]</span>.</p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.4 [exec.syn]</strong> as follows: ]</span></p>
<div>
<div class="sourceCode" id="cb24"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a>  <em>// [exec.queries], queries</em></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a>  enum class forward_progress_guarantee;</span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>  namespace <em>queries</em> { <em>// exposition only</em></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins>struct get_domain_t;</ins></span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>    struct get_scheduler_t;</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>    struct get_delegatee_scheduler_t;</span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>    struct get_forward_progress_guarantee_t;</span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>    template&lt;class CPO&gt;</span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a>      struct get_completion_scheduler_t;</span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>using <span class="citation" data-cites="_queries_"><em>queries</em></span>::get_domain_t;</ins></span></span>
<span id="cb24-13"><a href="#cb24-13" aria-hidden="true" tabindex="-1"></a>  using <em>queries</em>::get_scheduler_t;</span>
<span id="cb24-14"><a href="#cb24-14" aria-hidden="true" tabindex="-1"></a>  using <em>queries</em>::get_delegatee_scheduler_t;</span>
<span id="cb24-15"><a href="#cb24-15" aria-hidden="true" tabindex="-1"></a>  using <em>queries</em>::get_forward_progress_guarantee_t;</span>
<span id="cb24-16"><a href="#cb24-16" aria-hidden="true" tabindex="-1"></a>  using <em>queries</em>::get_completion_scheduler_t;</span>
<span id="cb24-17"><a href="#cb24-17" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>inline constexpr get_domain_t get_domain{};</ins></span></span>
<span id="cb24-18"><a href="#cb24-18" aria-hidden="true" tabindex="-1"></a>  inline constexpr get_scheduler_t get_scheduler{};</span>
<span id="cb24-19"><a href="#cb24-19" aria-hidden="true" tabindex="-1"></a>  inline constexpr get_delegatee_scheduler_t get_delegatee_scheduler{};</span>
<span id="cb24-20"><a href="#cb24-20" aria-hidden="true" tabindex="-1"></a>  inline constexpr get_forward_progress_guarantee_t get_forward_progress_guarantee{};</span>
<span id="cb24-21"><a href="#cb24-21" aria-hidden="true" tabindex="-1"></a>  template&lt;class CPO&gt;</span>
<span id="cb24-22"><a href="#cb24-22" aria-hidden="true" tabindex="-1"></a>    inline constexpr get_completion_scheduler_t&lt;CPO&gt; get_completion_scheduler{};</span>
<span id="cb24-23"><a href="#cb24-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb24-24"><a href="#cb24-24" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span class="citation" data-cites="_/"><em>/</span>/ [exec.domain.default], domains</em></ins></span></span>
<span id="cb24-25"><a href="#cb24-25" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>struct default_domain;</ins></span></span></code></pre></div>
</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: … and …
]</span></p>
<div>
<div class="sourceCode" id="cb25"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a>  template&lt;class Snd, class Env = empty_env&gt;</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>      requires sender_in&lt;Snd, Env&gt;</span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>    inline constexpr bool sends_stopped = <em>see below</em>;</span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">template &lt;sender Sender&gt;</code></span></ins></span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">using tag_of_t = <em>see below</em>;</code></span></ins></span></span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>// [exec.snd.transform], sender transformations</em></code></span></ins></span></span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">template &lt;class Domain, sender Sender&gt;</code></span></ins></span></span>
<span id="cb25-10"><a href="#cb25-10" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">constexpr sender decltype(auto) transform_sender(Domain dom, Sender&amp;&amp; snd);</code></span></ins></span></span>
<span id="cb25-11"><a href="#cb25-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-12"><a href="#cb25-12" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">template &lt;class Domain, sender Sender, queryable Env&gt;</code></span></ins></span></span>
<span id="cb25-13"><a href="#cb25-13" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">constexpr sender decltype(auto) transform_sender(Domain dom, Sender&amp;&amp; snd, const Env&amp; env);</code></span></ins></span></span>
<span id="cb25-14"><a href="#cb25-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-15"><a href="#cb25-15" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">template &lt;class Domain, sender Sender, queryable Env&gt;</code></span></ins></span></span>
<span id="cb25-16"><a href="#cb25-16" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">constexpr decltype(auto) transform_env(Domain dom, Sender&amp;&amp; snd, Env&amp;&amp; env) noexcept;</code></span></ins></span></span>
<span id="cb25-17"><a href="#cb25-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-18"><a href="#cb25-18" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>// [exec.snd.apply], sender algorithm application</em></code></span></ins></span></span>
<span id="cb25-19"><a href="#cb25-19" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">template &lt;class Domain, class Tag, sender Sender, class... Args&gt;</code></span></ins></span></span>
<span id="cb25-20"><a href="#cb25-20" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">constexpr decltype(auto) apply_sender(Domain dom, Tag, Sender&amp;&amp; snd, Args&amp;&amp;... args) noexcept(<em>see below</em>);</code></span></ins></span></span>
<span id="cb25-21"><a href="#cb25-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb25-22"><a href="#cb25-22" aria-hidden="true" tabindex="-1"></a>  <em>// [exec.connect], the connect sender algorithm</em></span>
<span id="cb25-23"><a href="#cb25-23" aria-hidden="true" tabindex="-1"></a>  namespace senders-connect { // exposition only</span>
<span id="cb25-24"><a href="#cb25-24" aria-hidden="true" tabindex="-1"></a>    struct connect_t;</span>
<span id="cb25-25"><a href="#cb25-25" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb25-26"><a href="#cb25-26" aria-hidden="true" tabindex="-1"></a>  using senders-connect::connect_t;</span>
<span id="cb25-27"><a href="#cb25-27" aria-hidden="true" tabindex="-1"></a>  inline constexpr connect_t connect{};</span></code></pre></div>
</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: … and …
]</span></p>
<div>
<div class="sourceCode" id="cb26"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>  <em>// [exec.factories], sender factories</em></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>  namespace <em>senders-factories</em> { <em>// exposition only</em></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">struct just_t;</code></span></ins></span></span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">struct just_error_t;</code></span></ins></span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">struct just_stopped_t;</code></span></ins></span></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>    struct schedule_t;</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>    struct transfer_just_t;</span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>using <span class="citation" data-cites="_sender-factories_"><em>sender-factories</em></span>::just_t;</ins></span></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>using <span class="citation" data-cites="_sender-factories_"><em>sender-factories</em></span>::just_error_t;</ins></span></span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a>  <span class="add" style="color: #006e28"><ins>using <span class="citation" data-cites="_sender-factories_"><em>sender-factories</em></span>::just_stopped_t;</ins></span></span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>  inline constexpr <span class="rm" style="color: #bf0303"><del><span class="citation" data-cites="_unspecified_"><em>unspecified</em></span></del></span><span class="add" style="color: #006e28"><ins>just_t</ins></span> just{};</span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>  inline constexpr <span class="rm" style="color: #bf0303"><del><span class="citation" data-cites="_unspecified_"><em>unspecified</em></span></del></span><span class="add" style="color: #006e28"><ins>just_error_t</ins></span> just_error{};</span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a>  inline constexpr <span class="rm" style="color: #bf0303"><del><span class="citation" data-cites="_unspecified_"><em>unspecified</em></span></del></span><span class="add" style="color: #006e28"><ins>just_stopped_t</ins></span> just_stopped{};</span></code></pre></div>
</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: After
<strong>§11.5.4 [exec.get.stop.token]</strong>, add the following new
subsection: ]</span></p>
<div class="add" style="color: #006e28">
<p><strong>§11.5.?
<code class="sourceCode default">execution::get_domain</code>
[exec.get.domain]</strong></p>
<ol type="1">
<li><p><code class="sourceCode default">get_domain</code> asks an object
for an associated execution domain tag.</p></li>
<li><p>The name <code class="sourceCode default">get_domain</code>
denotes a query object. For some subexpression
<code class="sourceCode default">o</code>,
<code class="sourceCode default">get_domain(o)</code> is
expression-equivalent to <code class="sourceCode default"><em>mandate-nothrow-call</em>(tag_invoke, get_domain, as_const(o))</code>,
if this expression is well-formed.</p></li>
<li><p><code class="sourceCode default">std::forwarding_query(execution::get_domain)</code>
is <code class="sourceCode default">true</code>.</p></li>
<li><p><code class="sourceCode default">get_domain()</code> (with no
arguments) is expression-equivalent to
<code class="sourceCode default">execution::read(get_domain)</code>
([exec.read]).</p></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: To
section <strong>§11.6 [exec.sched]</strong>, insert a new paragraph
between 6 and 7 as follows: ]</span></p>
<div class="add" style="color: #006e28">

<ol start="7" type="1">
<li>For a given scheduler expression
<code class="sourceCode default">sch</code>, if the expression
<code class="sourceCode default">get_domain(sch)</code> is well-formed,
then the expression <code class="sourceCode default">get_domain(get_env(schedule(sch)))</code> is
also well-formed and has the same type.</li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: To
section <strong>§11.9.1 [exec.snd.concepts]</strong>, after paragraph 4,
add two new paragraphs as follows: ]</span></p>
<div class="add" style="color: #006e28">

<ol start="5" type="1">
<li><p>Let <code class="sourceCode default">snd</code> be an expression
such that <code class="sourceCode default">decltype((snd))</code> is
<code class="sourceCode default">Snd</code>. The type
<code class="sourceCode default">tag_of_t&lt;Snd&gt;</code> is as
follows:</p>
<ul>
<li><p>If the declaration <code class="sourceCode default">auto&amp;&amp; [tag, data, ...children] = snd;</code>
would be well-formed,
<code class="sourceCode default">tag_of_t&lt;Snd&gt;</code> is an alias
for
<code class="sourceCode default">decltype(auto(tag))</code>.</p></li>
<li><p>Otherwise,
<code class="sourceCode default">tag_of_t&lt;Snd&gt;</code> is
ill-formed.</p></li>
</ul>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: There is
no way in standard C++ to determine whether the above declaration is
well-formed without causing a hard error, so this presumes compiler
magic. However, the author anticipates the adoption of <span class="citation" data-cites="P2141R1">[<a href="#ref-P2141R1" role="doc-biblioref">P2141R1</a>]</span>, which makes it possible to
implement this purely in the library. P2141 has already been approved by
EWG for C++26. ]</span></p></li>
<li><p>Let <code class="sourceCode default"><em>sender-for</em></code>
be an exposition-only concept defined as follows:</p>
<blockquote>
<div class="sourceCode" id="cb27"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sender, class Tag&gt;</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>concept <em>sender-for</em> =</span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>  sender&lt;Sender&gt; &amp;&amp;</span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>  same_as&lt;tag_of_t&lt;Sender&gt;, Tag&gt;;</span></code></pre></div>
</blockquote></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: After
<strong>§11.9.2 [exec.awaitables]</strong>, add the following new
subsections: ]</span></p>
<div class="add" style="color: #006e28">
<p><strong>§11.9.?
<code class="sourceCode default">execution::default_domain</code>
[exec.domain.default]</strong></p>
<div class="sourceCode" id="cb28"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb28-1"><a href="#cb28-1" aria-hidden="true" tabindex="-1"></a>struct default_domain {</span>
<span id="cb28-2"><a href="#cb28-2" aria-hidden="true" tabindex="-1"></a>  template &lt;sender Sender&gt;</span>
<span id="cb28-3"><a href="#cb28-3" aria-hidden="true" tabindex="-1"></a>    static constexpr sender decltype(auto) transform_sender(Sender&amp;&amp; snd) noexcept(<em>see below</em>);</span>
<span id="cb28-4"><a href="#cb28-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-5"><a href="#cb28-5" aria-hidden="true" tabindex="-1"></a>  template &lt;sender Sender, queryable Env&gt;</span>
<span id="cb28-6"><a href="#cb28-6" aria-hidden="true" tabindex="-1"></a>    static constexpr sender decltype(auto) transform_sender(Sender&amp;&amp; snd, const Env&amp; env) noexcept(<em>see below</em>);</span>
<span id="cb28-7"><a href="#cb28-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-8"><a href="#cb28-8" aria-hidden="true" tabindex="-1"></a>  template &lt;sender Sender, queryable Env&gt;</span>
<span id="cb28-9"><a href="#cb28-9" aria-hidden="true" tabindex="-1"></a>    static constexpr decltype(auto) transform_env(Sender&amp;&amp; snd, Env&amp;&amp; env) noexcept;</span>
<span id="cb28-10"><a href="#cb28-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb28-11"><a href="#cb28-11" aria-hidden="true" tabindex="-1"></a>  template &lt;class Tag, sender Sender, class... Args&gt;</span>
<span id="cb28-12"><a href="#cb28-12" aria-hidden="true" tabindex="-1"></a>    static constexpr decltype(auto) apply_sender(Tag, Sender&amp;&amp; snd, Args&amp;&amp;... args) noexcept(<em>see below</em>);</span>
<span id="cb28-13"><a href="#cb28-13" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
<p><strong>§11.9.?.1 Static members
[exec.domain.default.statics]</strong></p>
<div class="sourceCode" id="cb29"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb29-1"><a href="#cb29-1" aria-hidden="true" tabindex="-1"></a>template &lt;sender Sender&gt;</span>
<span id="cb29-2"><a href="#cb29-2" aria-hidden="true" tabindex="-1"></a>  constexpr sender decltype(auto) default_domain::transform_sender(Sender&amp;&amp; snd) noexcept(<em>see below</em>);</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">tag_of_t&lt;Sender&gt;().transform_sender(std::forward&lt;Sender&gt;(snd))</code>
if that expression is well-formed; otherwise,
<code class="sourceCode default">std::forward&lt;Sender&gt;(snd)</code>.</p>
<p><em>Remarks:</em> The exception specification is equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb30"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb30-1"><a href="#cb30-1" aria-hidden="true" tabindex="-1"></a>noexcept(tag_of_t&lt;Sender&gt;().transform_sender(std::forward&lt;Sender&gt;(snd)))</span></code></pre></div>
</blockquote>
<p>if that expression is well-formed; otherwise,
<code class="sourceCode default">true</code>;</p>
<div class="sourceCode" id="cb31"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb31-1"><a href="#cb31-1" aria-hidden="true" tabindex="-1"></a>template &lt;sender Sender, queryable Env&gt;</span>
<span id="cb31-2"><a href="#cb31-2" aria-hidden="true" tabindex="-1"></a>  constexpr sender decltype(auto) default_domain::transform_sender(Sender&amp;&amp; snd, const Env&amp; env) noexcept(<em>see below</em>);</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">tag_of_t&lt;Sender&gt;().transform_sender(std::forward&lt;Sender&gt;(snd), env)</code>
if that expression is well-formed; otherwise,
<code class="sourceCode default">std::forward&lt;Sender&gt;(snd)</code>.</p>
<p><em>Remarks:</em> The exception specification is equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb32"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb32-1"><a href="#cb32-1" aria-hidden="true" tabindex="-1"></a>noexcept(tag_of_t&lt;Sender&gt;().transform_sender(std::forward&lt;Sender&gt;(snd), env))</span></code></pre></div>
</blockquote>
<p>if that expression is well-formed; otherwise,
<code class="sourceCode default">true</code>;</p>
<div class="sourceCode" id="cb33"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb33-1"><a href="#cb33-1" aria-hidden="true" tabindex="-1"></a>template &lt;sender Sender, queryable Env&gt;</span>
<span id="cb33-2"><a href="#cb33-2" aria-hidden="true" tabindex="-1"></a>  constexpr decltype(auto) default_domain::transform_env(Sender&amp;&amp; snd, Env&amp;&amp; env) noexcept;</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">tag_of_t&lt;Sender&gt;().transform_env(std::forward&lt;Sender&gt;(snd), std::forward&lt;Env&gt;(env))</code>
if that expression is well-formed; otherwise, <code class="sourceCode default">static_cast&lt;Env&gt;(std::forward&lt;Env&gt;(env))</code>.</p>
<p><em>Mandates:</em> The selected expression in <em>Returns:</em> is
not potentially throwing.</p>
<div class="sourceCode" id="cb34"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb34-1"><a href="#cb34-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Tag, sender Sender, class... Args&gt;</span>
<span id="cb34-2"><a href="#cb34-2" aria-hidden="true" tabindex="-1"></a>  static constexpr decltype(auto) default_domain::apply_sender(Tag, Sender&amp;&amp; snd, Args&amp;&amp;... args) noexcept(<em>see below</em>);</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">Tag().apply_sender(std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...)</code>
if that expression is well-formed; otherwise, this function shall not
participate in overload resolution.</p>
<p><em>Remarks:</em> The exception specification is equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb35"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb35-1"><a href="#cb35-1" aria-hidden="true" tabindex="-1"></a>noexcept(Tag().apply_sender(std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...))</span></code></pre></div>
</blockquote>
<p><strong>§11.9.?
<code class="sourceCode default">execution::transform_sender</code>
[exec.snd.transform]</strong></p>
<div class="sourceCode" id="cb36"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb36-1"><a href="#cb36-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Domain, sender Sender&gt;</span>
<span id="cb36-2"><a href="#cb36-2" aria-hidden="true" tabindex="-1"></a>  constexpr sender decltype(auto) transform_sender(Domain dom, Sender&amp;&amp; snd);</span>
<span id="cb36-3"><a href="#cb36-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb36-4"><a href="#cb36-4" aria-hidden="true" tabindex="-1"></a>template &lt;class Domain, sender Sender, class Env&gt;</span>
<span id="cb36-5"><a href="#cb36-5" aria-hidden="true" tabindex="-1"></a>  constexpr sender decltype(auto) transform_sender(Domain dom, Sender&amp;&amp; snd, const Env&amp; env);</span></code></pre></div>
<p><em>Returns:</em> Let
<i><code class="sourceCode default">ENV</code></i> be a parameter pack
consisting of the single expression
<code class="sourceCode default">env</code> for the second overload and
an empty pack for the first. Let
<code class="sourceCode default">snd2</code> be the expression
<code>dom.transform_sender(std::forward&lt;Sender&gt;(snd),
<i>ENV</i>…)</code> if that expression is well-formed; otherwise,
<code>default_domain().transform_sender(std::forward&lt;Sender&gt;(snd),
<i>ENV</i>...)</code>. If <code class="sourceCode default">snd2</code>
and <code class="sourceCode default">snd</code> have the same type
ignoring <em>cv</em> qualifiers, returns
<code class="sourceCode default">snd2</code>; otherwise,
<code>transform_sender(dom, snd2, <i>ENV</i>...)</code>.</p>
<div class="sourceCode" id="cb37"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb37-1"><a href="#cb37-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Domain, sender Sender, queryable Env&gt;</span>
<span id="cb37-2"><a href="#cb37-2" aria-hidden="true" tabindex="-1"></a>  constexpr decltype(auto) transform_env(Domain dom, Sender&amp;&amp; snd, Env&amp;&amp; env) noexcept;</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">dom.transform_sender(std::forward&lt;Sender&gt;(snd), std::forward&lt;Env&gt;(env))</code>
if that expression is well-formed; otherwise, <code class="sourceCode default">default_domain().transform_sender(std::forward&lt;Sender&gt;(snd), std::forward&lt;Env&gt;(env))</code>.</p>
<p><strong>§11.9.?
<code class="sourceCode default">execution::apply_sender</code>
[exec.snd.apply]</strong></p>
<div class="sourceCode" id="cb38"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb38-1"><a href="#cb38-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Domain, class Tag, sender Sender, class... Args&gt;</span>
<span id="cb38-2"><a href="#cb38-2" aria-hidden="true" tabindex="-1"></a>  constexpr decltype(auto) apply_sender(Domain dom, Tag, Sender&amp;&amp; snd, Args&amp;&amp;... args) noexcept(<em>see below</em>);</span></code></pre></div>
<p><em>Returns:</em> <code class="sourceCode default">dom.apply_sender(Tag(), std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...)</code>
if that expression is well-formed; otherwise, <code class="sourceCode default">default_domain().apply_sender(Tag(), std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...)</code>
if that expression is well-formed; otherwise, this function shall not
participate in overload resolution.</p>
<p><em>Remarks:</em> The exception specification is equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb39"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb39-1"><a href="#cb39-1" aria-hidden="true" tabindex="-1"></a>noexcept(dom.apply_sender(Tag(), std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...))</span></code></pre></div>
</blockquote>
<p>if that expression is well-formed; otherwise,</p>
<blockquote>
<div class="sourceCode" id="cb40"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb40-1"><a href="#cb40-1" aria-hidden="true" tabindex="-1"></a>noexcept(default_domain().apply_sender(Tag(), std::forward&lt;Sender&gt;(snd), std::forward&lt;Args&gt;(args)...))</span></code></pre></div>
</blockquote>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Add a
paragraph to <strong>§11.9 [exec.snd]</strong> ]</span></p>
<div class="add" style="color: #006e28">

<ol type="1">
<li><p>This section makes use of the following exposition-only
entities.</p>
<ol type="1">
<li><div class="sourceCode" id="cb41"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb41-1"><a href="#cb41-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Default = default_domain, class Sender&gt;</span>
<span id="cb41-2"><a href="#cb41-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>completion-domain</em>(const Sender&amp; snd) noexcept;</span></code></pre></div>
<p><em>Effects:</em> Let
<code class="sourceCode default"><em>COMPL-DOMAIN</em>(T)</code> be the
type of the expression <code class="sourceCode default">get_domain(get_completion_scheduler&lt;T&gt;(get_env(snd)))</code>.
If <code class="sourceCode default"><em>COMPL-DOMAIN</em>(set_value_t, snd)</code>,
<code class="sourceCode default"><em>COMPL-DOMAIN</em>(set_error_t, snd)</code>,
and <code class="sourceCode default"><em>COMPL-DOMAIN</em>(set_stopped_t, snd)</code>
all share a common type [meta.trans.other] (ignoring those types that
are ill-formed), then <code class="sourceCode default"><em>completion-domain</em>&lt;Default&gt;(snd)</code>
is a default-constructed prvalue of that type. Otherwise, if all of
those types are ill-formed, <code class="sourceCode default"><em>completion-domain</em>&lt;Default&gt;(snd)</code>
is a default-constructed prvalue of type
<code class="sourceCode default">Default</code>. Otherwise, <code class="sourceCode default"><em>completion-domain</em>&lt;Default&gt;(snd)</code>
is ill-formed.</p></li>
<li><div class="sourceCode" id="cb42"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb42-1"><a href="#cb42-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Tag, class Env, class Default&gt;</span>
<span id="cb42-2"><a href="#cb42-2" aria-hidden="true" tabindex="-1"></a>constexpr decltype(auto) <em>query-with-default</em>(Tag, const Env&amp; env, Default&amp;&amp; value) noexcept(<em>see below</em>);</span></code></pre></div>
<p><em>Effects:</em> Equivalent to:</p>
<p>— <code class="sourceCode default">return Tag()(env);</code> if that
expression is well-formed,</p>
<p>— <code class="sourceCode default">return static_cast&lt;Default&gt;(std::forward&lt;Default&gt;(value));</code>
otherwise.</p>
<p><em>Remarks:</em> The expression in the
<code class="sourceCode default">noexcept</code> clause is:</p>
<blockquote>
<div class="sourceCode" id="cb43"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb43-1"><a href="#cb43-1" aria-hidden="true" tabindex="-1"></a>is_invocable_v&lt;Tag, const Env&amp;&gt; ? is_nothrow_invocable_v&lt;Tag, const Env&amp;&gt;</span>
<span id="cb43-2"><a href="#cb43-2" aria-hidden="true" tabindex="-1"></a>                                : is_nothrow_constructible_v&lt;Default, Default&gt;</span></code></pre></div>
</blockquote></li>
<li><div class="sourceCode" id="cb44"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb44-1"><a href="#cb44-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sender&gt;</span>
<span id="cb44-2"><a href="#cb44-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>get-domain-early</em>(const Sender&amp; snd) noexcept;</span></code></pre></div>
<p><em>Effects:</em> Equivalent to the first of the following that is
well-formed:</p>
<p>—
<code class="sourceCode default">return get_domain(get_env(snd));</code></p>
<p>— <code class="sourceCode default">return <em>completion-domain</em>(snd);</code></p>
<p>—
<code class="sourceCode default">return default_domain();</code></p></li>
<li><div class="sourceCode" id="cb45"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb45-1"><a href="#cb45-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Sender, class Env&gt;</span>
<span id="cb45-2"><a href="#cb45-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>get-domain-late</em>(const Sender&amp; snd, const Env&amp; env) noexcept;</span></code></pre></div>
<p><em>Effects:</em> Equivalent to:</p>
<p>— If <code class="sourceCode default"><em>sender-for</em>&lt;Sender, transfer_t&gt;</code>
is <code class="sourceCode default">true</code>, then <code class="sourceCode default">return <em>query-or-default</em>(get_domain, sch, default_domain())</code>
where <code class="sourceCode default">sch</code> is the scheduler that
was used to construct <code class="sourceCode default">snd</code>,</p>
<p>— Otherwise,
<code class="sourceCode default">return get_domain(get_env(snd));</code>
if that expression is well-formed,</p>
<p>— Otherwise, <code class="sourceCode default">return <em>completion-domain</em>&lt;<em>X</em>&gt;(snd);</code>
if that expression is well-formed and its type is not
<em><code class="sourceCode default">X</code></em> where
<em><code class="sourceCode default">X</code></em> is an unspecified
type,</p>
<p>— Otherwise,
<code class="sourceCode default">return get_domain(env);</code> if that
expression is well-formed,</p>
<p>— Otherwise, <code class="sourceCode default">return get_domain(get_scheduler(env));</code>
if that expression is well-formed,</p>
<p>— Otherwise,
<code class="sourceCode default">return default_domain();</code>.</p>
<p><span class="note"><span>[ <em>Note:</em> </span>The
<code class="sourceCode default">transfer</code> algorithm is unique in
that it ignores the execution domain of its predecessor, using only its
destination scheduler to select a customization.<span> — <em>end
note</em> ]</span></span></p></li>
<li><div class="sourceCode" id="cb46"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb46-1"><a href="#cb46-1" aria-hidden="true" tabindex="-1"></a>template &lt;class... T&gt;</span>
<span id="cb46-2"><a href="#cb46-2" aria-hidden="true" tabindex="-1"></a>struct <em>product-type</em> {</span>
<span id="cb46-3"><a href="#cb46-3" aria-hidden="true" tabindex="-1"></a>  T<sub><em>0</em></sub> <em>t</em><sub><em>0</em></sub>;      // exposition only</span>
<span id="cb46-4"><a href="#cb46-4" aria-hidden="true" tabindex="-1"></a>  T<sub><em>1</em></sub> <em>t</em><sub><em>1</em></sub>;      // exposition only</span>
<span id="cb46-5"><a href="#cb46-5" aria-hidden="true" tabindex="-1"></a>    ...</span>
<span id="cb46-6"><a href="#cb46-6" aria-hidden="true" tabindex="-1"></a>  T<sub><em>n-1</em></sub> <em>t</em><sub><em>n-1</em></sub>;   // exposition only</span>
<span id="cb46-7"><a href="#cb46-7" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
<p>— <span class="note"><span>[ <em>Note:</em> </span>An expression of
type <code class="sourceCode default"><em>product-type</em></code> is
usable as the initializer of a structured binding declaration
[dcl.struct.bind].<span> — <em>end note</em> ]</span></span></p></li>
<li><div class="sourceCode" id="cb47"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb47-1"><a href="#cb47-1" aria-hidden="true" tabindex="-1"></a>template &lt;semiregular Tag, <em>movable-value</em> Data = <em>see below</em>, sender... Child&gt;</span>
<span id="cb47-2"><a href="#cb47-2" aria-hidden="true" tabindex="-1"></a>constexpr auto <em>make-sender</em>(Tag, Data&amp;&amp; data, Child&amp;&amp;... child);</span></code></pre></div>
<p><em>Remarks:</em> The default template argument for the
<code class="sourceCode default">Data</code> template parameter names
and unspecified empty trivial class type.</p>
<p><em>Returns:</em> A prvalue of type <code class="sourceCode default"><em>basic-sender</em>&lt;Tag, decay_t&lt;Data&gt;, decay_t&lt;Child&gt;...&gt;</code>
where the <em><code class="sourceCode default">tag</code></em> member
has been default-initialized and the
<em><code class="sourceCode default">data</code></em> and <code class="sourceCode default"><em>child</em><sub><em>n</em></sub>...</code>
members have been direct initialized from their respective forwarded
arguments, where
<em><code class="sourceCode default">basic-sender</code></em> is the
following exposition-only class template except as noted below:</p>
<blockquote>
<div class="sourceCode" id="cb48"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb48-1"><a href="#cb48-1" aria-hidden="true" tabindex="-1"></a>template &lt;class Tag, class Data, class... Child&gt; // arguments are not associated entities ([lib.tmpl-heads])</span>
<span id="cb48-2"><a href="#cb48-2" aria-hidden="true" tabindex="-1"></a>struct <em>basic-sender</em> : <em>unspecified</em> {</span>
<span id="cb48-3"><a href="#cb48-3" aria-hidden="true" tabindex="-1"></a>  using is_sender = <em>unspecified</em>;</span>
<span id="cb48-4"><a href="#cb48-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb48-5"><a href="#cb48-5" aria-hidden="true" tabindex="-1"></a>  [[no_unique_address]] Tag <em>tag</em>;  // exposition only</span>
<span id="cb48-6"><a href="#cb48-6" aria-hidden="true" tabindex="-1"></a>  Data <em>data</em>;          // exposition only</span>
<span id="cb48-7"><a href="#cb48-7" aria-hidden="true" tabindex="-1"></a>  Child<sub><em>0</em></sub> <em>child</em><sub><em>0</em></sub>;      // exposition only</span>
<span id="cb48-8"><a href="#cb48-8" aria-hidden="true" tabindex="-1"></a>  Child<sub><em>1</em></sub> <em>child</em><sub><em>1</em></sub>;      // exposition only</span>
<span id="cb48-9"><a href="#cb48-9" aria-hidden="true" tabindex="-1"></a>    ...</span>
<span id="cb48-10"><a href="#cb48-10" aria-hidden="true" tabindex="-1"></a>  Child<sub><em>n-1</em></sub> <em>child</em><sub><em>n-1</em></sub>;   // exposition only</span>
<span id="cb48-11"><a href="#cb48-11" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
</blockquote>
<p>— It is unspecified whether instances of
<em><code class="sourceCode default">basic-sender</code></em> can be
aggregate initialized.</p>
<p>— The unspecified base type has no non-static data members. It may
define member functions or hidden friend functions
([hidden.friends]).</p>
<p>— <span class="note"><span>[ <em>Note:</em> </span>An expression of
type <code class="sourceCode default"><em>basic-sender</em></code> is
usable as the initializer of a structured binding declaration
[dcl.struct.bind].<span> — <em>end note</em> ]</span></span></p></li>
</ol></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: To
<strong>§11.9.5.2 [exec.just]</strong>, add a paragraph 6 as follows:
]</span></p>
<div class="add" style="color: #006e28">

<ol start="6" type="1">
<li>When used as the initializer of a structured binding declaration,
expressions of type <code class="sourceCode default"><em>just-sender</em>&lt;Tag, Ts...&gt;</code>
behave as do expressions of type <code class="sourceCode default"><em>basic-sender</em>&lt;Tag, <em>product-type</em>&lt;Ts...&gt;&gt;</code>.</li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.5.3 [exec.transfer.just]</strong> as follows (some
identifiers in this section have had their names changed for the sake of
clarity; the name changes have not been marked up): ]</span></p>
<ol start="2" type="1">
<li><p>The name <code class="sourceCode default">transfer_just</code>
denotes a customization point object. For some subexpression
<code class="sourceCode default">sch</code> and pack of subexpressions
<code class="sourceCode default">vs</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and let
<code class="sourceCode default">Vs</code> be the template parameter
pack <code class="sourceCode default">decltype((vs))...</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or any type
<code class="sourceCode default">V</code> in
<code class="sourceCode default">Vs</code> does not satisfy
<em><code class="sourceCode default">movable-value</code></em>,
<code class="sourceCode default">transfer_just(sch, vs...)</code> is
ill-formed. Otherwise,
<code class="sourceCode default">transfer_just(sch, vs...)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb49"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb49-1"><a href="#cb49-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb49-2"><a href="#cb49-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb49-3"><a href="#cb49-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(transfer_just, <em>product-type</em>{sch, vs...}));</span></code></pre></div>

</div>
</blockquote>
<ol type="1">
<li><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">tag_invoke(transfer_just, sch, vs...)</code></span>,
if that expression is valid.</del></span></li>
</ol></li>
<li><p>Let <code class="sourceCode default">as</code> be a pack of
rvalue subexpressions of types
<code class="sourceCode default">decay_t&lt;Vs&gt;...</code> referring
to objects direct-initilized from
<code class="sourceCode default">vs</code>. If <span class="rm" style="color: #bf0303"><del>the function selected by
<span><code class="sourceCode default">tag_invoke</code></span></del></span><span class="add" style="color: #006e28"><ins>a sender
<span><code class="sourceCode default">out_snd</code></span> returned
from
<span><code class="sourceCode default">transfer_just(sch, vs...)</code></span>
is connected with a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, Env)</code></span></ins></span>
does not return a sender whose asynchronous operations execute value
completion operations on an execution agent belonging to the execution
resource associated with <code class="sourceCode default">sch</code>,
with value result datums <code class="sourceCode default">as</code>, the
behavior of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">transfer_just(sch, vs...)</code></span></del></span>
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p>
<ul>
<li><em>Mandates:</em> <code class="sourceCode default">sender_of&lt;<span class="rm" style="color: #bf0303"><del>R</del></span><span class="add" style="color: #006e28"><ins>Snd</ins></span>, set_value_t(decay_t&lt;Vs&gt;...), Env&gt;</code>,
where
<code class="sourceCode default"><span class="rm" style="color: #bf0303"><del>R</del></span><span class="add" style="color: #006e28"><ins>Snd</ins></span></code> is the
type of <span class="rm" style="color: #bf0303"><del>the
<span><code class="sourceCode default">tag_invoke</code></span>
expression above</del></span> <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">transfer_just(sch, vs...)</code></span></ins></span>,
and <code class="sourceCode default">Env</code> is the type of an
environment.</li>
</ul></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol start="2" type="1">
<li>Otherwise,
<code class="sourceCode default">transfer(just(vs...), sch)</code>.</li>
</ol>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<ol start="4" type="1">
<li><p>For some subexpression
<code class="sourceCode default">sch</code> and pack of subexpressions
<code class="sourceCode default">vs</code>, let
<code class="sourceCode default">out_snd</code> be a subexpression
referring to an object returned from
<code class="sourceCode default">transform_just(sch, vs...)</code> or a
copy of such. Then <code class="sourceCode default">get_completion_scheduler&lt;set_value_t&gt;(get_env(out_snd)) == sch</code>
is <code class="sourceCode default">true</code>, <code class="sourceCode default">get_completion_scheduler&lt;set_stopped_t&gt;(get_env(out_snd)) == sch</code>
is <code class="sourceCode default">true</code>, and
<code class="sourceCode default">get_domain(get_env(out_snd))</code> is
expression-equivalent to
<code class="sourceCode default">get_domain(sch)</code>.</p></li>
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, transfer_just_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">transfer_just_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb50"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb50-1"><a href="#cb50-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data] = snd;</span>
<span id="cb50-2"><a href="#cb50-2" aria-hidden="true" tabindex="-1"></a>auto&amp; [sch, ...vs] = data;</span>
<span id="cb50-3"><a href="#cb50-3" aria-hidden="true" tabindex="-1"></a>return transfer(just(std::move(vs)...), std::move(sch));</span></code></pre></div>
</blockquote>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">transform_just(sch, vs...)</code>
sender to become
<code class="sourceCode default">transform(just(vs...), sch)</code> when
it is connected with a receiver whose execution domain does not
customize <code class="sourceCode default">transform_just</code>.<span>
— <em>end note</em> ]</span></span></p></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Update
<strong>§11.9.6.3 [exec.on]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">on</code> adapts an input sender
into a sender that will start on an execution agent belonging to a
particular scheduler’s associated execution resource.</p></li>
<li><p>Let
<code class="sourceCode default"><em>replace-scheduler</em>(env, sch)</code>
be an expression denoting an object
<code class="sourceCode default">env2</code> such that
<code class="sourceCode default">get_scheduler(env<span class="add" style="color: #006e28"><ins>2</ins></span>)</code>
returns a copy of <code class="sourceCode default">sch</code>, <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">get_domain(env2)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">get_domain(sch)</code></span>,</ins></span>
and
<code class="sourceCode default">tag_invoke(tag, env2, args...)</code>
is expression-equivalent to
<code class="sourceCode default">tag(env, args...)</code> for all
arguments <code class="sourceCode default">args...</code> and for all
<code class="sourceCode default">tag</code> whose type satisfies
<code class="sourceCode default"><em>forwarding-query</em></code> and is
not <code class="sourceCode default">get_scheduler_t</code> <span class="add" style="color: #006e28"><ins>or
<span><code class="sourceCode default">get_domain_t</code></span></ins></span>.</p></li>
<li><p>The name <code class="sourceCode default">on</code> denotes a
customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">on<span class="add" style="color: #006e28"><ins>(sch, snd)</ins></span></code>
is ill-formed. Otherwise, the expression
<code class="sourceCode default">on(sch, snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb51"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb51-1"><a href="#cb51-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb51-2"><a href="#cb51-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb51-3"><a href="#cb51-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(on, sch, snd));</span></code></pre></div>

</div>
</blockquote>
<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">tag_invoke(on, sch, snd)</code></span>,
if that expression is valid. If the function selected
above</del></span><span class="add" style="color: #006e28"><ins>If a
sender <span><code class="sourceCode default">out_snd</code></span>
returned from
<span><code class="sourceCode default">on(sch, snd)</code></span> is
connected with a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender that starts
<code class="sourceCode default">snd</code> on an execution agent of the
associated execution resource of
<code class="sourceCode default">sch</code> when started, the behavior
of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">on(sch, snd)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p>
<ul>
<li><span class="rm" style="color: #bf0303"><del><em>Mandates:</em> The
type of the
<span><code class="sourceCode default">tag_invoke</code></span>
expression above satisfies
<span><code class="sourceCode default">sender</code></span>.</del></span></li>
</ul></li>
</ol></li>
<li><p><span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">snd</code></span> and
<span><code class="sourceCode default">env</code></span> be
subexpressions such that
<span><code class="sourceCode default">Snd</code></span> is
<span><code class="sourceCode default">decltype((snd))</code></span>. If
<span><code class="sourceCode default"><em>sender-for</em>&lt;Snd, on_t&gt;</code></span>
is <span><code class="sourceCode default">false</code></span>, then the
expression <span><code class="sourceCode default">on_t().transform_sender(snd, env)</code></span>
is ill-formed; otherwise, it returns</ins></span> <span class="rm" style="color: #bf0303"><del>Otherwise, constructs</del></span> a sender
<code class="sourceCode default">snd1</code> <span class="rm" style="color: #bf0303"><del>. When</del></span><span class="add" style="color: #006e28"><ins>such that when</ins></span>
<code class="sourceCode default">snd1</code> is connected with some
receiver <code class="sourceCode default">out_rcv</code>, it:</p>
<ol type="1">
<li><p>Constructs a receiver <code class="sourceCode default">rcv</code>
such that:</p>
<ol type="1">
<li><p>When <code class="sourceCode default">set_value(rcv)</code> is
called, it calls
<code class="sourceCode default">connect(snd, rcv2)</code>, where
<code class="sourceCode default">rcv2</code> is as specified below,
which results in <code class="sourceCode default">op_state3</code>. It
calls <code class="sourceCode default">start(op_state3)</code>. If any
of these throws an exception, it calls
<code class="sourceCode default">set_error</code> on
<code class="sourceCode default">out_rcv</code>, passing
<code class="sourceCode default">current_exception()</code> as the
second argument.</p></li>
<li><p><code class="sourceCode default">set_error(rcv, err)</code> is
expression-equivalent to
<code class="sourceCode default">set_error(out_rcv, err)</code>.</p></li>
<li><p><code class="sourceCode default">set_stopped(rcv)</code> is
expression-equivalent to
<code class="sourceCode default">set_stopped(out_rcv)</code>.</p></li>
<li><p><code class="sourceCode default">get_env(rcv)</code> is
expression-equivalent to
<code class="sourceCode default">get_env(out_rcv)</code>.</p></li>
</ol></li>
<li><p>Calls <code class="sourceCode default">schedule(sch)</code>,
which results in <code class="sourceCode default">snd2</code>. It then
calls <code class="sourceCode default">connect(snd2, rcv)</code>,
resulting in <code class="sourceCode default">op_state2</code>.</p></li>
<li><p><code class="sourceCode default">op_state2</code> is wrapped by a
new operation state, <code class="sourceCode default">op_state1</code>,
that is returned to the caller.</p></li>
<li><p><code class="sourceCode default">rcv2</code> is a receiver that
wraps a reference to <code class="sourceCode default">out_rcv</code> and
forwards all completion operations to it. In addition,
<code class="sourceCode default">get_env(rcv2)</code> returns
<code class="sourceCode default"><em>replace-scheduler</em>(env, sch)</code>.</p></li>
<li><p>When <code class="sourceCode default">start</code> is called on
<code class="sourceCode default">op_state1</code>, it calls
<code class="sourceCode default">start</code> on
<code class="sourceCode default">op_state2</code>.</p></li>
<li><p>The lifetime of
<code class="sourceCode default">op_state2</code>, once constructed,
lasts until either <code class="sourceCode default">op_state3</code> is
constructed or <code class="sourceCode default">op_state1</code> is
destroyed, whichever comes first. The lifetime of
<code class="sourceCode default">op_state3</code>, once constructed,
lasts until <code class="sourceCode default">op_state1</code> is
destroyed.</p></li>
</ol></li>
<li><p><span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">snd</code></span> and
<span><code class="sourceCode default">env</code></span> be
subexpressions such that
<span><code class="sourceCode default">Snd</code></span> is
<span><code class="sourceCode default">decltype((snd))</code></span>. If
<span><code class="sourceCode default"><em>sender-for</em>&lt;Snd, on_t&gt;</code></span>
is <span><code class="sourceCode default">false</code></span>, then the
expression
<span><code class="sourceCode default">on_t().transform_env(snd, env)</code></span>
is ill-formed; otherwise, let
<span><code class="sourceCode default">sch</code></span> be the
scheduler used to construct
<span><code class="sourceCode default">snd</code></span>.
<span><code class="sourceCode default">on_t().transform_env(snd, env)</code></span>
is equal to
<span><code class="sourceCode default"><em>replace-scheduler</em>(env, sch)</code></span>.</ins></span></p></li>
<li><p>Given subexpressions <code class="sourceCode default">snd1</code>
and <code class="sourceCode default">env</code>, where
<code class="sourceCode default">snd1</code> is a sender returned from
<code class="sourceCode default">on</code> or a copy of such, let
<code class="sourceCode default">Snd1</code> be
<code class="sourceCode default">decltype((snd1))</code>. Let
<code class="sourceCode default">Env2</code> be <code class="sourceCode default">decltype((<em>replace-scheduler</em>(env, sch)))</code>.
Then the type of <code class="sourceCode default">tag_invoke(get_completion_signatures, snd1, env)</code>
shall be:</p>
<blockquote>
<div class="sourceCode" id="cb52"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb52-1"><a href="#cb52-1" aria-hidden="true" tabindex="-1"></a>make_completion_signatures&lt;</span>
<span id="cb52-2"><a href="#cb52-2" aria-hidden="true" tabindex="-1"></a>  copy_cvref_t&lt;Snd1, Snd&gt;,</span>
<span id="cb52-3"><a href="#cb52-3" aria-hidden="true" tabindex="-1"></a>  Env2,</span>
<span id="cb52-4"><a href="#cb52-4" aria-hidden="true" tabindex="-1"></a>  make_completion_signatures&lt;</span>
<span id="cb52-5"><a href="#cb52-5" aria-hidden="true" tabindex="-1"></a>    schedule_result_t&lt;Sch&gt;,</span>
<span id="cb52-6"><a href="#cb52-6" aria-hidden="true" tabindex="-1"></a>    Env,</span>
<span id="cb52-7"><a href="#cb52-7" aria-hidden="true" tabindex="-1"></a>    completion_signatures&lt;set_error_t(exception_ptr)&gt;,</span>
<span id="cb52-8"><a href="#cb52-8" aria-hidden="true" tabindex="-1"></a>    <em>no-value-completions</em>&gt;&gt;;</span></code></pre></div>
</blockquote>
<p>where <code class="sourceCode default"><em>no-value-completions</em>&lt;As...&gt;</code>
names the type
<code class="sourceCode default">completion_signatures&lt;&gt;</code>
for any set of types
<code class="sourceCode default">As...</code>.</p></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Update
<strong>§11.9.6.4 [exec.transfer]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">transfer</code> adapts a sender
into a sender with a different associated
<code class="sourceCode default">set_value</code> completion scheduler.
<span class="note"><span>[ <em>Note:</em> </span>It results in a
transition between different execution resources when executed.<span> —
<em>end note</em> ]</span></span></p></li>
<li><p>The name <code class="sourceCode default">transfer</code> denotes
a customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">transfer<span class="add" style="color: #006e28"><ins>(snd, sch)</ins></span></code>
is ill-formed. Otherwise, the expression
<code class="sourceCode default">transfer(snd, sch)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb53"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb53-1"><a href="#cb53-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb53-2"><a href="#cb53-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb53-3"><a href="#cb53-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(transfer, sch, snd));</span></code></pre></div>

</div>
</blockquote>
<div class="rm" style="color: #bf0303">
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(transfer, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd, sch)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">tag_invoke(transfer, snd, sch)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">schedule_from(sch, snd)</code>.</p>
</div></li>
</ol>
<blockquote>
<p><span class="rm" style="color: #bf0303"><del>If the function selected
above</del></span><span class="add" style="color: #006e28"><ins>If a
sender <span><code class="sourceCode default">out_snd</code></span>
returned from
<span><code class="sourceCode default">transfer(snd, sch)</code></span>
is connected with a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender <span class="rm" style="color: #bf0303"><del>which</del></span><span class="add" style="color: #006e28"><ins>that</ins></span> is a result of a call to
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">transform_sender(_get-domain-late_(out_snd, env),</code></span></ins></span><code class="sourceCode default">schedule_from(sch, snd2)</code><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">, env)</code></span></ins></span>,
where <code class="sourceCode default">snd2</code> is a sender <span class="rm" style="color: #bf0303"><del>which</del></span><span class="add" style="color: #006e28"><ins>that</ins></span> sends values
<span class="rm" style="color: #bf0303"><del>equivalent</del></span><span class="add" style="color: #006e28"><ins>equal</ins></span> to those sent by
<code class="sourceCode default">snd</code>, the behavior of calling
<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">transfer(snd, sch)</code></span></del></span>
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p>
</blockquote></li>
</ol>
<!-- this comment is needed here to un-confundle the markdown processor -->
<ol start="3" type="1">
<li>For a sender <code class="sourceCode default">out_snd</code>
returned from
<code class="sourceCode default">transfer(snd, sch)</code>,
<code class="sourceCode default">get_env(out_snd)</code> shall return a
queryable object <code class="sourceCode default">q</code> such that
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">get_domain(q)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">get_domain(sch)</code></span>
and</ins></span> <code class="sourceCode default">get_completion_scheduler&lt;<em>CPO</em>&gt;(q)</code>
returns a copy of <code class="sourceCode default">sch</code>, where
<em><code class="sourceCode default">CPO</code></em> is either
<code class="sourceCode default">set_value_t</code> or
<code class="sourceCode default">set_stopped_t</code>. The <code class="sourceCode default">get_completion_scheduler&lt;set_error_t&gt;</code>
query is not implemented, as the scheduler cannot be guaranteed in case
an error is thrown while trying to schedule work on the given scheduler
object. For all other query objects
<em><code class="sourceCode default">Q</code></em> whose type satisfies
<em><code class="sourceCode default">forwarding-query</code></em>, the
expression
<code class="sourceCode default"><em>Q</em>(q,     args...)</code> shall
be equivalent to
<code class="sourceCode default"><em>Q</em>(get_env(snd), args...)</code>.</li>
</ol>
<div class="add" style="color: #006e28">

<ol start="4" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, transfer_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">transfer_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb54"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb54-1"><a href="#cb54-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, child] = snd;</span>
<span id="cb54-2"><a href="#cb54-2" aria-hidden="true" tabindex="-1"></a>return schedule_from(std::move(data), std::move(child));</span></code></pre></div>
</blockquote>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">transfer(snd, sch)</code> sender to
become <code class="sourceCode default">schedule_from(sch, snd)</code>
when it is connected with a receiver whose execution domain does not
customize <code class="sourceCode default">transfer</code>.<span> —
<em>end note</em> ]</span></span></p></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Update
<strong>§11.9.6.5 [exec.schedule.from]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">schedule_from</code> schedules
work dependent on the completion of a sender onto a scheduler’s
associated execution resource. <span class="note"><span>[ <em>Note:</em>
</span><code class="sourceCode default">schedule_from</code> is not
meant to be used in user code; it is used in the implementation of
<code class="sourceCode default">transfer</code>.<span> — <em>end
note</em> ]</span></span></p></li>
<li><p>The name <code class="sourceCode default">schedule_from</code>
denotes a customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>, <code class="sourceCode default">schedule_from<span class="add" style="color: #006e28"><ins>(sch, snd)</ins></span></code>
is ill-formed. Otherwise, the expression
<code class="sourceCode default">schedule_from(sch, snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb55"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb55-1"><a href="#cb55-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb55-2"><a href="#cb55-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb55-3"><a href="#cb55-3" aria-hidden="true" tabindex="-1"></a>  <em>make-schedule-from-sender</em>(sch, snd));</span></code></pre></div>

</div>
</blockquote>
<div class="add" style="color: #006e28">

where <code class="sourceCode default"><em>make-schedule-from-sender</em>(sch, snd)</code>
is expression-equivalent to <code class="sourceCode default"><em>make-sender</em>(schedule_from, sch, snd)</code>
and returns a sender object <code class="sourceCode default">snd2</code>
that behaves as follows:

</div>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(schedule_from, sch, snd)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not return a
sender that completes on an execution agent belonging to the associated
execution resource of <code class="sourceCode default">sch</code> and
completing with the same async result ([async.ops]) as
<code class="sourceCode default">snd</code>, the behavior of calling
<code class="sourceCode default">schedule_from(sch, snd)</code> is
undefined.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
</ol>
<p><!-- --></p>
<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,
constructs a sender
<span><code class="sourceCode default">snd2</code></span>.</del></span>
When <code class="sourceCode default">snd2</code> is connected with some
receiver <code class="sourceCode default">out_rcv</code>, it:</p>
<ol type="1">
<li><p>Constructs a receiver <code class="sourceCode default">rcv</code>
such that when a receiver completion operation
<code class="sourceCode default"><em>Tag</em>(rcv, args...)</code> is
called, it decay-copies <code class="sourceCode default">args...</code>
into <code class="sourceCode default">op_state</code> (see below) as
<code class="sourceCode default"><span class="rm" style="color: #bf0303"><del>args&#39;</del></span><span class="add" style="color: #006e28"><ins>args2</ins></span>...</code> and
constructs a receiver <code class="sourceCode default">rcv2</code> such
that:</p>
<ol type="1">
<li><p>When <code class="sourceCode default">set_value(rcv2)</code> is
called, it calls <code class="sourceCode default"><em>Tag</em>(out_rcv, std::move(<span class="rm" style="color: #bf0303"><del>args&#39;</del></span><span class="add" style="color: #006e28"><ins>args2</ins></span>)...)</code>.</p></li>
<li><p><code class="sourceCode default">set_error(rcv2, err)</code> is
expression-equivalent to
<code class="sourceCode default">set_error(out_rcv, err)</code>.</p></li>
<li><p><code class="sourceCode default">set_stopped(rcv2)</code> is
expression-equivalent to
<code class="sourceCode default">set_stopped(out_rcv)</code>.</p></li>
<li><p><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">get_env(rcv2)</code></span>
is equal to
<span><code class="sourceCode default">get_env(rcv)</code></span>.</ins></span></p></li>
</ol>
<p>It then calls <code class="sourceCode default">schedule(sch)</code>,
resulting in a sender <code class="sourceCode default">snd3</code>. It
then calls <code class="sourceCode default">connect(snd3, rcv2)</code>,
resulting in an operation state
<code class="sourceCode default">op_state3</code>. It then calls
<code class="sourceCode default">start(op_state3)</code>. If any of
these throws an exception, it catches it and calls <code class="sourceCode default">set_error(out_rcv, current_exception())</code>.
If any of these expressions would be ill-formed, then
<code class="sourceCode default"><em>Tag</em>(rcv, args...)</code> is
ill-formed.</p></li>
<li><p>Calls <code class="sourceCode default">connect(snd, rcv)</code>
resulting in an operation state
<code class="sourceCode default">op_state2</code>. If this expression
would be ill-formed,
<code class="sourceCode default">connect(snd2, out_rcv)</code> is
ill-formed.</p></li>
<li><p>Returns an operation state
<code class="sourceCode default">op_state</code> that contains
<code class="sourceCode default">op_state2</code>. When
<code class="sourceCode default">start(op_state)</code> is called, calls
<code class="sourceCode default">start(op_state2)</code>. The lifetime
of <code class="sourceCode default">op_state3</code> ends when
<code class="sourceCode default">op_state</code> is destroyed.</p></li>
</ol></li>
<li><p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: This
para is taken from the removed para (1) above. ]</span> <span class="rm" style="color: #bf0303"><del>If the function selected by
<span><code class="sourceCode default">tag_invoke</code></span></del></span><span class="add" style="color: #006e28"><ins>If a sender
<span><code class="sourceCode default">out_snd</code></span> returned
from
<span><code class="sourceCode default">schedule_from(sch, snd)</code></span>
is connected with a receiver
<span><code class="sourceCode default">rcv</code></span> with
environmment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender that completes on an execution agent belonging
to the associated execution resource of
<code class="sourceCode default">sch</code> and <span class="rm" style="color: #bf0303"><del>completing</del></span> with the same async
result ([async.ops]) as <code class="sourceCode default">snd</code>, the
behavior of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">schedule_from(sch, snd)</code></span></del></span>
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p></li>
<li><p>Given subexpressions <code class="sourceCode default">snd2</code>
and <code class="sourceCode default">env</code>, where
<code class="sourceCode default">snd2</code> is a sender returned from
<code class="sourceCode default">schedule_from</code> or a copy of such,
let <code class="sourceCode default">Snd2</code> be
<code class="sourceCode default">decltype((snd2))</code> and let
<code class="sourceCode default">Env</code> be
<code class="sourceCode default">decltype((env))</code>. Then the type
of <code class="sourceCode default">tag_invoke(get_completion_signatures, snd2, env)</code>
shall be:</p>
<blockquote>
<div class="sourceCode" id="cb56"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb56-1"><a href="#cb56-1" aria-hidden="true" tabindex="-1"></a>make_completion_signatures&lt;</span>
<span id="cb56-2"><a href="#cb56-2" aria-hidden="true" tabindex="-1"></a>  copy_cvref_t&lt;Snd2, Snd&gt;,</span>
<span id="cb56-3"><a href="#cb56-3" aria-hidden="true" tabindex="-1"></a>  Env,</span>
<span id="cb56-4"><a href="#cb56-4" aria-hidden="true" tabindex="-1"></a>  make_completion_signatures&lt;</span>
<span id="cb56-5"><a href="#cb56-5" aria-hidden="true" tabindex="-1"></a>    schedule_result_t&lt;Sch&gt;,</span>
<span id="cb56-6"><a href="#cb56-6" aria-hidden="true" tabindex="-1"></a>    Env,</span>
<span id="cb56-7"><a href="#cb56-7" aria-hidden="true" tabindex="-1"></a>    <em>potentially-throwing-completions</em>,</span>
<span id="cb56-8"><a href="#cb56-8" aria-hidden="true" tabindex="-1"></a>    <em>no-completions</em>&gt;,</span>
<span id="cb56-9"><a href="#cb56-9" aria-hidden="true" tabindex="-1"></a>  <em>value-completions</em>,</span>
<span id="cb56-10"><a href="#cb56-10" aria-hidden="true" tabindex="-1"></a>  <em>error-completions</em>&gt;;</span></code></pre></div>
</blockquote>
<p>where
<em><code class="sourceCode default">potentially-throwing-completions</code></em>,
<em><code class="sourceCode default">no-completions</code></em>,
<em><code class="sourceCode default">value-completions</code></em>, and
<em><code class="sourceCode default">error-completions</code></em> are
defined as follows:</p>
<blockquote>
<div class="sourceCode" id="cb57"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb57-1"><a href="#cb57-1" aria-hidden="true" tabindex="-1"></a>template &lt;class... Ts&gt;</span>
<span id="cb57-2"><a href="#cb57-2" aria-hidden="true" tabindex="-1"></a>using <em>all-nothrow-decay-copyable</em> =</span>
<span id="cb57-3"><a href="#cb57-3" aria-hidden="true" tabindex="-1"></a>  boolean_constant&lt;(is_nothrow_constructible_v&lt;decay_t&lt;Ts&gt;, Ts&gt; &amp;&amp; ...)&gt;</span>
<span id="cb57-4"><a href="#cb57-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb57-5"><a href="#cb57-5" aria-hidden="true" tabindex="-1"></a>template &lt;class... Ts&gt;</span>
<span id="cb57-6"><a href="#cb57-6" aria-hidden="true" tabindex="-1"></a>using <em>conjunction</em> = boolean_constant&lt;(Ts::value &amp;&amp;...)&gt;;</span>
<span id="cb57-7"><a href="#cb57-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb57-8"><a href="#cb57-8" aria-hidden="true" tabindex="-1"></a>using <em>potentially-throwing-completions</em> =</span>
<span id="cb57-9"><a href="#cb57-9" aria-hidden="true" tabindex="-1"></a>  conditional_t&lt;</span>
<span id="cb57-10"><a href="#cb57-10" aria-hidden="true" tabindex="-1"></a>    error_types_of_t&lt;copy_cvref_t&lt;Snd2, Snd&gt;, Env, <em>all-nothrow-decay-copyable</em>&gt;::value &amp;&amp;</span>
<span id="cb57-11"><a href="#cb57-11" aria-hidden="true" tabindex="-1"></a>      value_types_of_t&lt;copy_cvref_t&lt;Snd2, Snd&gt;, Env, <em>all-nothrow-decay-copyable</em>, <em>conjunction</em>&gt;::value,</span>
<span id="cb57-12"><a href="#cb57-12" aria-hidden="true" tabindex="-1"></a>    completion_signatures&lt;&gt;,</span>
<span id="cb57-13"><a href="#cb57-13" aria-hidden="true" tabindex="-1"></a>    completion_signatures&lt;set_error_t(exception_ptr)&gt;;</span>
<span id="cb57-14"><a href="#cb57-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb57-15"><a href="#cb57-15" aria-hidden="true" tabindex="-1"></a>template &lt;class...&gt;</span>
<span id="cb57-16"><a href="#cb57-16" aria-hidden="true" tabindex="-1"></a>using <em>no-completions</em> = completion_signatures&lt;&gt;;</span>
<span id="cb57-17"><a href="#cb57-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb57-18"><a href="#cb57-18" aria-hidden="true" tabindex="-1"></a>template &lt;class... Ts&gt;</span>
<span id="cb57-19"><a href="#cb57-19" aria-hidden="true" tabindex="-1"></a>using <em>value-completions</em> = completion_signatures&lt;set_value_t(decay_t&lt;Ts&gt;&amp;&amp;...)&gt;;</span>
<span id="cb57-20"><a href="#cb57-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb57-21"><a href="#cb57-21" aria-hidden="true" tabindex="-1"></a>template &lt;class T&gt;</span>
<span id="cb57-22"><a href="#cb57-22" aria-hidden="true" tabindex="-1"></a>using <em>error-completions</em> = completion_signatures&lt;set_error_t(decay_t&lt;T&gt;&amp;&amp;)&gt;;</span></code></pre></div>
</blockquote></li>
</ol></li>
</ol>
<!-- -->
<ol start="3" type="1">
<li>For a sender <code class="sourceCode default">out_snd</code>
returned from
<code class="sourceCode default">schedule_from(sch, snd)</code>,
<code class="sourceCode default">get_env(out_snd)</code> shall return a
queryable object <code class="sourceCode default">q</code> such that
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">get_domain(q)</code></span>
is expression-equivalent to
<span><code class="sourceCode default">get_domain(sch)</code></span>
and</ins></span> <code class="sourceCode default">get_completion_scheduler&lt;<em>CPO</em>&gt;(q)</code>
returns a copy of <code class="sourceCode default">sch</code>, where
<em><code class="sourceCode default">CPO</code></em> is either
<code class="sourceCode default">set_value_t</code> or
<code class="sourceCode default">set_stopped_t</code>. The <code class="sourceCode default">get_completion_scheduler&lt;set_error_t&gt;</code>
query is not implemented, as the scheduler cannot be guaranteed in case
an error is thrown while trying to schedule work on the given scheduler
object. For all other query objects
<em><code class="sourceCode default">Q</code></em> whose type satisfies
<em><code class="sourceCode default">forwarding-query</code></em>, the
expression
<code class="sourceCode default"><em>Q</em>(q, args...)</code> shall be
equivalent to
<code class="sourceCode default"><em>Q</em>(get_env(snd), args...)</code>.</li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Update
<strong>§11.9.6.6 [exec.then]</strong> (with analogous changes to
<strong>§11.9.6.7 [exec.upon.error]</strong> and <strong>§11.9.6.8
[exec.upon.stopped]</strong>) as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">then</code> attaches an
invocable as a continuation for an input sender’s value completion
operation.</p></li>
<li><p>The name <code class="sourceCode default">then</code> denotes a
customization point object. For some subexpressions
<code class="sourceCode default">snd</code> and
<code class="sourceCode default">f</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>, let
<code class="sourceCode default">F</code> be the decayed type of
<code class="sourceCode default">f</code>, and let
<code class="sourceCode default"><span class="rm" style="color: #bf0303"><del>f’</del></span><span class="add" style="color: #006e28"><ins>f2</ins></span></code> be an
xvalue referring to an object decay-copied from
<code class="sourceCode default">f</code>. If
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>, or
<code class="sourceCode default">F</code> does not model
<em><code class="sourceCode default">movable-value</code></em>,
<code class="sourceCode default">then<span class="add" style="color: #006e28"><ins>(snd, f)</ins></span></code>
is ill-formed. Otherwise, the expression
<code class="sourceCode default">then(snd, f)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb58"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb58-1"><a href="#cb58-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb58-2"><a href="#cb58-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb58-3"><a href="#cb58-3" aria-hidden="true" tabindex="-1"></a>  <em>make-then-sender</em>(f, snd));</span></code></pre></div>

</div>
</blockquote>
<div class="add" style="color: #006e28">

where
<code class="sourceCode default"><em>make-then-sender</em>(f, snd)</code>
is expression-equivalent to
<code class="sourceCode default"><em>make-sender</em>(then, f, snd)</code>
and returns a sender object <code class="sourceCode default">snd2</code>
that behaves as follows:

</div>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(then, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd, f)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul>

</div></li>
<li><div class="rm" style="color: #bf0303">
<p>Otherwise,
<code class="sourceCode default">tag_invoke(then, snd, f)</code>, if
that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul>

</div></li>
</ol>
<p><!-- --></p>
<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,
constructs a sender
<span><code class="sourceCode default">snd2</code></span>.</del></span>
When <code class="sourceCode default">snd2</code> is connected with some
receiver <code class="sourceCode default">out_rcv</code>, it:</p>
<ol type="1">
<li><p>Constructs a receiver <code class="sourceCode default">rcv</code>
such that:</p>
<ol type="1">
<li><p>When
<code class="sourceCode default">set_value(rcv, args...)</code> is
called, let <code class="sourceCode default">v</code> be the expression
<code class="sourceCode default">invoke(<span class="rm" style="color: #bf0303"><del>f’</del></span><span class="add" style="color: #006e28"><ins>f2</ins></span>, args...)</code>.
If <code class="sourceCode default">decltype(v)</code> is
<code class="sourceCode default">void</code>, evaluates the
expression<br />
<code class="sourceCode default">(v, set_value(out_rcv))</code>;
otherwise,
<code class="sourceCode default">set_value(out_rcv, v)</code>. If any of
these throw an exception, it catches it and calls <code class="sourceCode default">set_error(out_rcv, current_exception())</code>.
If any of these expressions would be ill-formed, the expression
<code class="sourceCode default">set_value(rcv, args...)</code> is
ill-formed.</p></li>
<li><p><code class="sourceCode default">set_error(rcv, err)</code> is
expression-equivalent to
<code class="sourceCode default">set_error(out_rcv, err)</code>.</p></li>
<li><p><code class="sourceCode default">set_stopped(rcv)</code> is
expression-equivalent to
<code class="sourceCode default">set_stopped(out_rcv)</code>.</p></li>
</ol></li>
<li><p>Returns an expression-equivalent to
<code class="sourceCode default">connect(snd, rcv)</code>.</p></li>
<li><p><span class="rm" style="color: #bf0303"><del>Let <span><code class="sourceCode default"><em>compl-sig-t</em>&lt;Tag, Args...&gt;</code></span>
name the type <span><code class="sourceCode default">Tag()</code></span>
if <span><code class="sourceCode default">Args...</code></span> is a
template paramter pack containing the single type
<span><code class="sourceCode default">void</code></span>; otherwise,
<span><code class="sourceCode default">Tag(Args...)</code></span>.</del></span>
Given subexpressions <code class="sourceCode default">out_snd</code> and
<code class="sourceCode default">env</code> where
<code class="sourceCode default">out_snd</code> is a sender returned
from <code class="sourceCode default">then</code> or a copy of such, let
<code class="sourceCode default">OutSnd</code> be
<code class="sourceCode default">decltype((out_snd))</code> and let
<code class="sourceCode default">Env</code> be
<code class="sourceCode default">decltype((env))</code>. The type of
<code class="sourceCode default">tag_invoke(get_completion_signatures, out_snd, env)</code>
shall be <span class="rm" style="color: #bf0303"><del>equivalent
to</del></span>:</p>
<blockquote>
<div class="sourceCode" id="cb59"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb59-1"><a href="#cb59-1" aria-hidden="true" tabindex="-1"></a>make_completion_signatures&lt;&gt;</span>
<span id="cb59-2"><a href="#cb59-2" aria-hidden="true" tabindex="-1"></a>  copy_cvref_t&lt;OutSnd, Snd&gt;, Env, <em>set-error-signature</em>,</span>
<span id="cb59-3"><a href="#cb59-3" aria-hidden="true" tabindex="-1"></a>    <em>set-value-completions</em>&gt; <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">;</code></span></del></span></span></code></pre></div>
</blockquote>
<p>where
<em><code class="sourceCode default">set-value-completions</code></em>
is <span class="rm" style="color: #bf0303"><del>an alias
for</del></span><span class="add" style="color: #006e28"><ins>the alias
template</ins></span>:</p>
<blockquote>
<div class="sourceCode" id="cb60"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb60-1"><a href="#cb60-1" aria-hidden="true" tabindex="-1"></a>template&lt;class... As&gt;</span>
<span id="cb60-2"><a href="#cb60-2" aria-hidden="true" tabindex="-1"></a>  <em>set-value-completions</em> =</span>
<span id="cb60-3"><a href="#cb60-3" aria-hidden="true" tabindex="-1"></a>    completion_signatures&lt;<span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>compl-sig-t</em>&lt;set_value_t,</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>SET-VALUE-SIG</em>(invoke_result_t&lt;F, As...&gt;)</code></span></ins></span><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">&gt;</code></span></del></span>&gt;</span></code></pre></div>
</blockquote>
<p>and
<em><code class="sourceCode default">set-error-signature</code></em> is
an alias for <code class="sourceCode default">completion_signatures&lt;set_error_t(exception_ptr)&gt;</code>
if any of the types in the
<em><code class="sourceCode default">type-list</code></em> named by
<code class="sourceCode default">value_types_of_t&lt;copy_cvref_t&lt;OutSnd, Snd&gt;, Env, <em>potentially-throwing</em>, <em>type-list</em>&gt;</code>
are <code class="sourceCode default">true_type</code>; otherwise,
<code class="sourceCode default">completion_signatures&lt;&gt;</code>,
where
<em><code class="sourceCode default">potentially-throwing</code></em> is
the <span class="rm" style="color: #bf0303"><del>template
alias</del></span><span class="add" style="color: #006e28"><ins>alias
template</ins></span>:</p>
<blockquote>
<div class="sourceCode" id="cb61"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb61-1"><a href="#cb61-1" aria-hidden="true" tabindex="-1"></a>template&lt;class... As&gt;</span>
<span id="cb61-2"><a href="#cb61-2" aria-hidden="true" tabindex="-1"></a>  using <em>potentially-throwing</em> =</span>
<span id="cb61-3"><a href="#cb61-3" aria-hidden="true" tabindex="-1"></a>    <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">bool_constant&lt;!is_nothrow_invocable_v&lt;F, As...&gt;&gt;</code></span></del></span></span>
<span id="cb61-4"><a href="#cb61-4" aria-hidden="true" tabindex="-1"></a>    <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">negation&lt;is_nothrow_invocable&lt;F, As...&gt;&gt;</code></span></ins></span>;</span></code></pre></div>
</blockquote></li>
</ol></li>
<li><p><span class="rm" style="color: #bf0303"><del>If the function
selected above</del></span> <span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">out_snd</code></span> be the
result of calling
<span><code class="sourceCode default">then(snd, f)</code></span> or a
copy of such. If
<span><code class="sourceCode default">out_snd</code></span> is
connected with a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender that<span class="add" style="color: #006e28"><ins>:</ins></span> <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: reformated as a list for
comprehensibility: ]</span></p>
<p>— invokes <code class="sourceCode default">f</code> with the value
result datums of <code class="sourceCode default">snd</code>,</p>
<p>— <span class="rm" style="color: #bf0303"><del>using</del></span><span class="add" style="color: #006e28"><ins>uses</ins></span>
<code class="sourceCode default">f</code>’s return value as <span class="rm" style="color: #bf0303"><del>the sender’s</del></span> <span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">out_snd</code></span>’s</ins></span>
value completion, and</p>
<p>— forwards the non-value completion operations <span class="add" style="color: #006e28"><ins>to
<span><code class="sourceCode default">rcv</code></span></ins></span>
unchanged,</p>
<p>then the behavior of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">then(snd, f)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p></li>
</ol></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.9 [exec.let]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">let_value</code> transforms a
sender’s value completion into a new child asynchronous operation.
<code class="sourceCode default">let_error</code> transforms a sender’s
error completion into a new child asynchronous operation.
<code class="sourceCode default">let_stopped</code> transforms a
sender’s stopped completion into a new child asynchronous
operation.</p></li>
<li><p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note:
Copied from below: ]</span> Let the expression
<em><code class="sourceCode default">let-cpo</code></em> be one of
<code class="sourceCode default">let_value</code>,
<code class="sourceCode default">let_error</code>, or
<code class="sourceCode default">let_stopped</code> <span class="add" style="color: #006e28"><ins>and let
<em><span><code class="sourceCode default">set-cpo</code></span></em> be
the completion function that corresponds to
<em><span><code class="sourceCode default">let-cpo</code></span></em>
(<span><code class="sourceCode default">set_value</code></span> for
<span><code class="sourceCode default">let_value</code></span>, etc.).
For subexpressions
<span><code class="sourceCode default">snd</code></span> and
<span><code class="sourceCode default">re</code></span>, let
<span><code class="sourceCode default"><em>inner-env</em>(snd, re)</code></span>
be an environment
<span><code class="sourceCode default">env</code></span> such
that:</ins></span></p></li>
</ol>
<div class="add" style="color: #006e28">

<blockquote>
<p>— <code class="sourceCode default">get_domain(env)</code> is
expression-equivalent
<code class="sourceCode default"><em>get-domain-late</em>(snd, re)</code></p>
<p>— <code class="sourceCode default">get_scheduler(env)</code> is
expression-equivalent to the first well-formed expression below:</p>
<blockquote>
<ul>
<li><p><code class="sourceCode default">get_completion_scheduler&lt;<em>set-cpo-t</em>&gt;(get_env(snd))</code>,
where <em><code class="sourceCode default">set-cpo-t</code></em> is the
type of
<em><code class="sourceCode default">set-cpo</code></em>.</p></li>
<li><p><code class="sourceCode default">get_scheduler(re)</code></p></li>
</ul>
<p>or if neither of them are,
<code class="sourceCode default">get_scheduler(env)</code> is
ill-formed.</p>
</blockquote>
<p>— For all other query objects
<em><code class="sourceCode default">Q</code></em> and arguments
<code class="sourceCode default">args...</code>,
<code class="sourceCode default"><em>Q</em>(env, args...)</code> is
expression-equivalent to
<code class="sourceCode default"><em>Q</em>(re, args...)</code>.</p>
</blockquote>

</div>
<ol start="3" type="1">
<li><p>The names <code class="sourceCode default">let_value</code>,
<code class="sourceCode default">let_error</code>, and
<code class="sourceCode default">let_stopped</code> denote customization
point objects. <span class="rm" style="color: #bf0303"><del>Let the
expression
<em><span><code class="sourceCode default">let-cpo</code></span></em> be
one of <span><code class="sourceCode default">let_value</code></span>,
<span><code class="sourceCode default">let_error</code></span>, or
<span><code class="sourceCode default">let_stopped</code></span>.</del></span>
For subexpressions <code class="sourceCode default">snd</code> and
<code class="sourceCode default">f</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>, let
<code class="sourceCode default">F</code> be the decayed type of
<code class="sourceCode default">f</code>, and let
<code class="sourceCode default">f2</code> be an xvalue that refers to
an object decay-copied from <code class="sourceCode default">f</code>.
If <code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>, the expression
<code class="sourceCode default"><em>let-cpo</em>(snd, f)</code> is
ill-formed. If <code class="sourceCode default">F</code> does not
satisfy <code class="sourceCode default">invocable</code>, the
expression <code class="sourceCode default">let_stopped(snd, f)</code>
is ill-formed. Otherwise, the expression
<code class="sourceCode default"><em>let-cpo</em>(snd, f)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb62"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb62-1"><a href="#cb62-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb62-2"><a href="#cb62-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb62-3"><a href="#cb62-3" aria-hidden="true" tabindex="-1"></a>  <em>make-let-sender</em>(f, snd));</span></code></pre></div>

</div>
</blockquote>
<div class="add" style="color: #006e28">

where
<code class="sourceCode default"><em>make-let-sender</em>(f, snd)</code>
is expression-equivalent to <code class="sourceCode default"><em>make-sender</em>(<em>let-cpo</em>, f, snd)</code>
and returns a sender object <code class="sourceCode default">snd2</code>
that behaves as follows:

</div>
<ol type="1">
<li><span class="add" style="color: #006e28"><ins>When
<span><code class="sourceCode default">snd2</code></span> is connected
to some receiver
<span><code class="sourceCode default">out_rcv</code></span>,
it:</ins></span></li>
</ol>
<p><!-- --></p>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(<em>let-cpo</em>, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd, f)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
<li><div class="rm" style="color: #bf0303">
<p>Otherwise,
<code class="sourceCode default">tag_invoke(<em>let-cpo</em>, snd, f)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
<li><div class="rm" style="color: #bf0303">
<p>Otherwise, given a receiver
<code class="sourceCode default">out_rcv</code> and an lvalue
<code class="sourceCode default">out_rcv&#39;</code> referring to an
object decay-copied from
<code class="sourceCode default">out_rcv</code>.</p>
<ol type="1">
<li>For <code class="sourceCode default">let_value</code>, let
<em><code class="sourceCode default">set-cpo</code></em> be
<code class="sourceCode default">set_value</code>. For
<code class="sourceCode default">let_error</code>, let
<em><code class="sourceCode default">set-cpo</code></em> be
<code class="sourceCode default">set_error</code>. For
<code class="sourceCode default">let_stopped</code>, let
<em><code class="sourceCode default">set-cpo</code></em> be
<code class="sourceCode default">set_stopped</code>. Let
<em><code class="sourceCode default">completion-function</code></em> be
one of <code class="sourceCode default">set_value</code>,
<code class="sourceCode default">set_error</code>, or
<code class="sourceCode default">set_stopped</code>.
</div></li>
</ol>
<p><!-- --></p>
<blockquote>
<ol type="1">
<li><p><span class="add" style="color: #006e28"><ins>Decay-copies
<span><code class="sourceCode default">out_rcv</code></span> into
<span><code class="sourceCode default">op_state2</code></span> (see
below). <span><code class="sourceCode default">out_rcv2</code></span> is
an xvalue referring to the copy of
<span><code class="sourceCode default">out_rcv</code></span>.</ins></span></p></li>
<li><p><span class="rm" style="color: #bf0303"><del>Let
<span><code class="sourceCode default">rcv</code></span> be an rvalue of
a receiver type
<span><code class="sourceCode default">Rcv</code></span></del></span><span class="add" style="color: #006e28"><ins>Constructs a receiver
<span><code class="sourceCode default">rcv</code></span> such
that</ins></span> such that:</p>
<ol type="1">
<li><p>When
<code class="sourceCode default"><em>set-cpo</em>(rcv, args...)</code>
is called, the receiver <code class="sourceCode default">rcv</code>
decay-copies <code class="sourceCode default">args...</code> into
<code class="sourceCode default">op_state2</code> as
<code class="sourceCode default">args2...</code>, then calls
<code class="sourceCode default">invoke(f2, args2...)</code> resulting
in a sender <code class="sourceCode default">snd3</code>. It then calls
<code class="sourceCode default">connect(snd3, <span class="rm" style="color: #bf0303"><del>std::move(out_rcv’)</del></span><span class="add" style="color: #006e28"><ins>out_rcv3</ins></span>)</code>,
resulting in an operation state
<code class="sourceCode default">op_state3</code><span class="add" style="color: #006e28"><ins>, where
<span><code class="sourceCode default">out_rcv3</code></span> is a
receiver described below</ins></span>.
<code class="sourceCode default">op_state3</code> is saved as a part of
<code class="sourceCode default">op_state2</code>. It then calls
<code class="sourceCode default">start(op_state3)</code>. If any of
these throws an exception, it catches it and calls <code class="sourceCode default">set_error(<span class="rm" style="color: #bf0303"><del>std::move(out_rcv’)</del></span><span class="add" style="color: #006e28"><ins>out_rcv2</ins></span>, current_exception())</code>.
If any of these expressions would be ill-formed,
<code class="sourceCode default"><em>set-cpo</em>(rcv, args...)</code>
is ill-formed.</p></li>
<li><p><code class="sourceCode default"><em><span class="rm" style="color: #bf0303"><del>completion-function</del></span><span class="add" style="color: #006e28"><ins>CF</ins></span></em>(rcv, args...)</code>
is expression-equivalent to <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>completion-function</em>(std::move(out_rcv&#39;), args...)</code></span>
when
<em><span><code class="sourceCode default">completion-function</code></span></em>
is different from
<em><span><code class="sourceCode default">set-cpo</code></span></em></del></span>
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default"><em>CF</em>(out_rcv2, args...)</code></span>,
where <em><span><code class="sourceCode default">CF</code></span></em>
is a completion function other than
<em><span><code class="sourceCode default">set-cpo</code></span></em></ins></span>.</p></li>
<li><div class="add" style="color: #006e28">

<code class="sourceCode default">get_env(rcv)</code> is
expression-equivalent to
<code class="sourceCode default">get_env(out_rcv)</code>.

</div></li>
<li><div class="add" style="color: #006e28">

<code class="sourceCode default">out_rcv3</code> is a receiver that
forwards its completion operations to
<code class="sourceCode default">out_rcv2</code> and for which
<code class="sourceCode default">get_env(out_rcv3)</code> returns <code class="sourceCode default"><em>inner-env</em>(get_env(snd), get_env(out_rcv2))</code>.

</div></li>
</ol></li>
<li><p><span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>let-cpo</em>(snd, f)</code></span>
returns a sender
<span><code class="sourceCode default">snd2</code></span> such
that:</del></span><span class="add" style="color: #006e28"><ins>Calls
<span><code class="sourceCode default">connect(snd, rcv)</code></span>
resulting in an operation state
<span><code class="sourceCode default">op_state2</code></span>.</ins></span>
<span class="ednote" style="color: #0000ff">[ Editor&#39;s note: The
formatting is changed here. ]</span> If the expression
<code class="sourceCode default">connect(snd, rcv)</code> is ill-formed,
<code class="sourceCode default">connect(snd2, out_rcv)</code> is
ill-formed.</p></li>
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise, let
<span><code class="sourceCode default">op_state2</code></span> be the
result of
<span><code class="sourceCode default">connect(snd, rcv)</code></span>.
<span><code class="sourceCode default">connect(snd2, out_rcv)</code></span>
returns</del></span><span class="add" style="color: #006e28"><ins>Returns</ins></span> an operation state
<code class="sourceCode default">op_state</code> that stores
<code class="sourceCode default">op_state2</code>.
<code class="sourceCode default">start(op_state)</code> is
expression-equivalent to
<code class="sourceCode default">start(op_state2)</code>.</p></li>
</ol>
</blockquote></li>
</ol></li>
</ol>
<!-- -->
<ol start="4" type="1">
<li><p>Given subexpressions
<code class="sourceCode default">out_snd</code> and
<code class="sourceCode default">env</code>, where
<code class="sourceCode default">out_snd</code> is a sender returned
from <code class="sourceCode default"><em>let-cpo</em>(snd, f)</code> or
a copy of such, let <code class="sourceCode default">OutSnd</code> be
<code class="sourceCode default">decltype((out_snd))</code>, let
<code class="sourceCode default">Env</code> be
<code class="sourceCode default">decltype((env))</code>, and let
<code class="sourceCode default">DS</code> be
<code class="sourceCode default">copy_cvref_t&lt;OutSnd, Snd&gt;</code>.
Then the type of <code class="sourceCode default">tag_invoke(get_completion_signatures, out_snd, env)</code>
is specified as follows:</p>
<ol type="1">
<li><p>If
<code class="sourceCode default">sender_in&lt;DS, Env&gt;</code> is
<code class="sourceCode default">false</code>, the expression <code class="sourceCode default">tag_invoke(get_completion_signatures, out_snd, env)</code>
is ill-formed.</p></li>
<li><p>Otherwise, let <code class="sourceCode default">Sigs...</code> be
the set of template arguments of the
<code class="sourceCode default">completion_signatures</code>
specialization named by <code class="sourceCode default">completion_signatures_of_t&lt;DS, Env&gt;</code>,
let <code class="sourceCode default">Sigs2...</code> be the set of
function types in <code class="sourceCode default">Sigs...</code> whose
return type is <em><code class="sourceCode default">set-cpo</code></em>,
and let <code class="sourceCode default">Rest...</code> be the set of
function types in <code class="sourceCode default">Sigs...</code> but
not <code class="sourceCode default">Sigs2...</code>.</p></li>
<li><p>For each
<code class="sourceCode default">Sig2<sub><em>i</em></sub></code> in
<code class="sourceCode default">Sigs2...</code>, let <code class="sourceCode default">Vs<sub><em>i</em></sub>...</code> be the set
of function arguments in
<code class="sourceCode default">Sig2<sub><em>i</em></sub></code> and
let <code class="sourceCode default">Snd3<sub><em>i</em></sub></code> be
<code class="sourceCode default">invoke_result_t&lt;F, decay_t&lt;Vs<sub><em>i</em></sub>&gt;&amp;...&gt;</code>.
If <code class="sourceCode default">Snd3<sub><em>i</em></sub></code> is
ill-formed, <span class="add" style="color: #006e28"><ins>or if
<span><code class="sourceCode default"><em>get-domain-early</em>(declval&lt;Snd3<sub><em>i</em></sub>&gt;())</code></span>
has a different type than
<span><code class="sourceCode default"><em>get-domain-early</em>(snd)</code></span>,</ins></span>
or if <code class="sourceCode default">sender_in&lt;Snd3<sub><em>i</em></sub>, <span class="rm" style="color: #bf0303"><del>Env</del></span><span class="add" style="color: #006e28"><ins>Env2</ins></span>&gt;</code> is
not satisfied <span class="add" style="color: #006e28"><ins>where
<span><code class="sourceCode default">Env2</code></span> is the type of
<span><code class="sourceCode default"><em>inner-env</em>(get_env(snd), env)</code></span></ins></span>,
then the expression <code class="sourceCode default">tag_invoke(get_completion_signatures, out_snd, env)</code>
is ill-formed.</p></li>
<li><p>Otherwise, let <code class="sourceCode default">Sigs3<sub><em>i</em></sub>...</code> be the
set of template arguments of the
<code class="sourceCode default">completion_signatures</code>
specialization named by <code class="sourceCode default">completion_signatures_of_t&lt;Snd3<sub><em>i</em></sub>, <span class="rm" style="color: #bf0303"><del>Env</del></span><span class="add" style="color: #006e28"><ins>Env2</ins></span>&gt;</code>.
Then the type of <code class="sourceCode default">tag_invoke(get_completion_signatures, out_snd, env)</code>
shall be equivalent to <code class="sourceCode default">completion_signatures&lt;Sigs3<sub><em>0</em></sub>..., Sigs3<sub><em>1</em></sub>...,</code>
<code class="sourceCode default">... Sigs3<sub><em>n-1</em></sub>..., Rest..., set_error_t(exception_ptr)&gt;</code>,
where <em><code class="sourceCode default">n</code></em> is
<code class="sourceCode default">sizeof...(Sigs2)</code>.</p></li>
</ol></li>
</ol>
<div class="add" style="color: #006e28">

<ol start="5" type="1">
<li>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code> and
<code class="sourceCode default">Env</code> is
<code class="sourceCode default">decltype((env))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, <em>let-cpo-t</em>&gt;</code>
is <code class="sourceCode default">false</code> where
<em><code class="sourceCode default">let-cpo-t</code></em> is the type
of <em><code class="sourceCode default">let-cpo</code></em>, then the
expression <code class="sourceCode default"><em>let-cpo-t</em>().transform_env(snd, env)</code>
is ill-formed. Otherwise, it is equal to
<code class="sourceCode default"><em>inner-env</em>(get_env(snd), env)</code>.</li>
</ol>

</div>
<!-- -->
<ol start="6" type="1">
<li><p>If <span class="add" style="color: #006e28"><ins>a sender
<span><code class="sourceCode default">out_snd</code></span> returned
from</ins></span>
<code class="sourceCode default"><em>let-cpo</em>(snd, f)</code> <span class="add" style="color: #006e28"><ins>is connected to a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender that <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: reformated as a list for
comprehensibility ]</span>:</p>
<p>— invokes <code class="sourceCode default">f</code> when
<em><code class="sourceCode default">set-cpo</code></em> is called <span class="add" style="color: #006e28"><ins>with
<span><code class="sourceCode default">snd</code></span>’s result
datums</ins></span>, <span class="rm" style="color: #bf0303"><del>and</del></span></p>
<p>— makes its completion dependent on the completion of a sender
returned by <code class="sourceCode default">f</code>, and</p>
<p>— propagates the other completion operations sent by
<code class="sourceCode default">snd</code>,</p>
<p>the behavior of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default"><em>let-cpo</em>(snd, f)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.10 [exec.bulk]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">bulk</code> runs a task
repeatedly for every index in an index space.</p></li>
<li><p>The name <code class="sourceCode default">bulk</code> denotes a
customization point object. For some subexpressions
<code class="sourceCode default">snd</code>,
<code class="sourceCode default">shape</code>, and
<code class="sourceCode default">f</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>,
<code class="sourceCode default">Shape</code> be
<code class="sourceCode default">decltype((shape))</code>, and
<code class="sourceCode default">F</code> be
<code class="sourceCode default">decltype((f))</code>. If
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code> or
<code class="sourceCode default">Shape</code> does not satisfy
<code class="sourceCode default">integral</code>,
<code class="sourceCode default">bulk</code> is ill-formed. Otherwise,
the expression
<code class="sourceCode default">bulk(snd, shape, f)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb63"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb63-1"><a href="#cb63-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb63-2"><a href="#cb63-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb63-3"><a href="#cb63-3" aria-hidden="true" tabindex="-1"></a>  <em>make-bulk-sender</em>(<em>product-type</em>{shape, f}, snd));</span></code></pre></div>

</div>
</blockquote>
<div class="add" style="color: #006e28">
<p>where
<code class="sourceCode default"><em>make-bulk-sender</em>(t, snd)</code>
is expression-equivalent to
<code class="sourceCode default"><em>make-sender</em>(bulk, t, snd)</code>
for a subexpression <code class="sourceCode default">t</code> and
returns a sender object <code class="sourceCode default">snd2</code>
that behaves as follows:</p>
</div>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(bulk, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd, shape, f)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
<li><div class="rm" style="color: #bf0303">
<p>Otherwise,
<code class="sourceCode default">tag_invoke(bulk, snd, shape, f)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
</ol>
<p><!-- --></p>
<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,
constructs a sender
<span><code class="sourceCode default">snd2</code></span>.</del></span>
When <code class="sourceCode default">snd2</code> is connected with some
receiver <code class="sourceCode default">out_rcv</code>, it:</p>
<ol type="1">
<li><p>Constructs a receiver
<code class="sourceCode default">rcv</code>:</p>
<ol type="1">
<li><p>When
<code class="sourceCode default">set_value(rcv, args...)</code> is
called, calls <code class="sourceCode default">f(i, args...)</code> for
each <code class="sourceCode default">i</code> of type
<code class="sourceCode default">Shape</code> from
<code class="sourceCode default">0</code> to
<code class="sourceCode default">shape</code>, then calls
<code class="sourceCode default">set_value(out_rcv, args...)</code>. If
any of these throws an exception, it catches it and calls <code class="sourceCode default">set_error(out_rcv, current_exception())</code>.
<span class="add" style="color: #006e28"><ins>If any of these
expressions are ill-formed,
<span><code class="sourceCode default">set_value(rcv, args...)</code></span>
is ill-formed.</ins></span></p></li>
<li><p>When <code class="sourceCode default">set_error(rcv, err)</code>
is called, calls
<code class="sourceCode default">set_error(out_rcv, err)</code>.</p></li>
<li><p>When <code class="sourceCode default">set_stopped(rcv)</code> is
called, calls
<code class="sourceCode default">set_stopped(out_rcv, env)</code>.</p></li>
</ol></li>
<li><p>Calls <code class="sourceCode default">connect(snd, rcv)</code>,
which results in an operation state
<code class="sourceCode default">op_state2</code>.</p></li>
<li><p>Returns an operation state
<code class="sourceCode default">op_state</code> that contains
<code class="sourceCode default">op_state2</code>. When
<code class="sourceCode default">start(op_state)</code> is called, calls
<code class="sourceCode default">start(op_state2)</code>.</p></li>
<li><p>Given subexpressions <code class="sourceCode default">snd2</code>
and <code class="sourceCode default">env</code> where
<code class="sourceCode default">snd2</code> is a sender returned from
<code class="sourceCode default">bulk</code> or a copy of such, let
<code class="sourceCode default">Snd2</code> be
<code class="sourceCode default">decltype((snd2))</code>, let
<code class="sourceCode default">Env</code> be
<code class="sourceCode default">decltype((env))</code>, let
<code class="sourceCode default">DS</code> be
<code class="sourceCode default">copy_cvref_t&lt;Snd2, Snd&gt;</code>,
let <code class="sourceCode default">Shape</code> be
<code class="sourceCode default">decltype((shape))</code> and let
<em><code class="sourceCode default">nothrow-callable</code></em> be the
alias template:</p>
<blockquote>
<div class="sourceCode" id="cb64"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb64-1"><a href="#cb64-1" aria-hidden="true" tabindex="-1"></a>template&lt;class... As&gt;</span>
<span id="cb64-2"><a href="#cb64-2" aria-hidden="true" tabindex="-1"></a>  using <em>nothrow-callable</em> =</span>
<span id="cb64-3"><a href="#cb64-3" aria-hidden="true" tabindex="-1"></a>    bool_constant&lt;is_nothrow_invocable_v&lt;decay_t&lt;F&gt;&amp;, Shape, As...&gt;&gt;;</span></code></pre></div>
</blockquote>
<ol type="1">
<li><p>If any of the types in the
<em><code class="sourceCode default">type-list</code></em> named by
<code class="sourceCode default">value_types_of_t&lt;DS, Env, <em>nothrow-callable</em>, <em>type-list</em>&gt;</code>
are <code class="sourceCode default">false_type</code>, then the type of
<code class="sourceCode default">tag_invoke(get_completion_signatures, snd2, env)</code>
shall be <span class="rm" style="color: #bf0303"><del>equivalent
to</del></span>:</p>
<div class="sourceCode" id="cb65"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb65-1"><a href="#cb65-1" aria-hidden="true" tabindex="-1"></a>make_completion_signatures&lt;</span>
<span id="cb65-2"><a href="#cb65-2" aria-hidden="true" tabindex="-1"></a>  DS, Env, completion_signatures&lt;set_error_t(exception_ptr)&gt;&gt;</span></code></pre></div></li>
<li><p>Otherwise, the type of <code class="sourceCode default">tag_invoke(get_completion_signatures, snd2, env)</code>
shall be <span class="rm" style="color: #bf0303"><del>equivalent
to</del></span> <code class="sourceCode default">completion_signatures_of_t&lt;DS, Env&gt;</code>.</p></li>
</ol></li>
</ol></li>
<li><p><span class="rm" style="color: #bf0303"><del>If the function
selected above</del></span> <span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">out_snd</code></span> be the
result of calling
<span><code class="sourceCode default">bulk(snd, shape, f)</code></span>
or a copy of such. If
<span><code class="sourceCode default">out_snd</code></span> is
connected to a receiver
<span><code class="sourceCode default">rcv</code></span> with
environment <span><code class="sourceCode default">env</code></span>
such that <span><code class="sourceCode default">transform_sender(<em>get-domain-late</em>(out_snd, env), out_snd, env)</code></span></ins></span>
does not return a sender that invokes
<code class="sourceCode default">f(i, args...)</code> for each
<code class="sourceCode default">i</code> of type
<code class="sourceCode default">Shape</code> from
<code class="sourceCode default">0</code> to
<code class="sourceCode default">shape</code> where
<code class="sourceCode default">args</code> is a pack of subexpressions
referring to the value completion result datums of the input sender, or
does not execute a value completion operation with said datums, the
behavior of calling <span class="rm" style="color: #bf0303"><del><span><code class="sourceCode default">bulk(snd, shape, f)</code></span></del></span><span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">connect(out_snd, rcv)</code></span></ins></span>
is undefined.</p></li>
</ol></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.11 [exec.split]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">split</code> adapts an arbitrary
sender into a sender that can be connected multiple times.</p></li>
<li><p>Let <em><code class="sourceCode default">split-env</code></em> be
the type of an environment such that, given an instance
<code class="sourceCode default">env</code>, the expression
<code class="sourceCode default">get_stop_token(env)</code> is
well-formed and has type
<code class="sourceCode default">stop_token</code>.</p></li>
<li><p>The name <code class="sourceCode default">split</code> denotes a
customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default">sender_in&lt;Snd, <em>split-env</em>&gt;</code>
or <code class="sourceCode default">constructible_from&lt;decay_t&lt;env_of_t&lt;Snd&gt;&gt;, env_of_t&lt;Snd&gt;&gt;</code>
is <code class="sourceCode default">false</code>,
<code class="sourceCode default">split</code> is ill-formed. Otherwise,
the expression <code class="sourceCode default">split(snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb66"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb66-1"><a href="#cb66-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb66-2"><a href="#cb66-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb66-3"><a href="#cb66-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(split, snd));</span></code></pre></div>

</div>
</blockquote>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(split, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd)</code>,
if that expression is valid.</p>
<ul>
<li><p><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</p>
</div></li>
</ul></li>
<li><div class="rm" style="color: #bf0303">
<p>Otherwise,
<code class="sourceCode default">tag_invoke(split, snd)</code>, if that
expression is valid.</p>
<ul>
<li><p><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</p>
</div></li>
</ul></li>
</ol>
<p><!-- --></p>
<ol type="1">
<li><p><span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">snd</code></span> be a
subexpression such that
<span><code class="sourceCode default">Snd</code></span> is
<span><code class="sourceCode default">decltype((snd))</code></span>,
and let <span><code class="sourceCode default">env...</code></span> be a
pack of subexpressions such that
<span><code class="sourceCode default">sizeof...(env) &lt;= 1</code></span>
is <span><code class="sourceCode default">true</code></span>. If
<span><code class="sourceCode default"><em>sender-for</em>&lt;Snd, split_t&gt;</code></span>
is <span><code class="sourceCode default">false</code></span>, then the
expression <span><code class="sourceCode default">split_t().transform_sender(snd, env...)</code></span>
is ill-formed; otherwise, it returns</ins></span> <span class="rm" style="color: #bf0303"><del>Otherwise, constructs</del></span> a sender
<code class="sourceCode default">snd2</code> <span class="rm" style="color: #bf0303"><del>, which</del></span><span class="add" style="color: #006e28"><ins>that</ins></span>:</p>
<ol type="1">
<li>Creates an object <code class="sourceCode default">sh_state</code>
that <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: … as
before ]</span></li>
</ol></li>
</ol></li>
</ol>
<p>[Change <strong>§11.9.6.12 [exec.when.all]</strong> as follows:]</p>
<ol type="1">
<li><p><code class="sourceCode default">when_all</code> and
<code class="sourceCode default">when_all_with_variant</code> both adapt
multiple input senders into a sender that completes when all input
senders have completed. <code class="sourceCode default">when_all</code>
only accepts senders with a single value completion signature and on
success concatenates all the input senders’ value result datums into its
own value completion operation.
<code class="sourceCode default">when_all_with_variant(snd...)</code> is
semantically equivilant to
<code class="sourceCode default">when_all(into_variant(snd)...)</code>,
where <code class="sourceCode default">snd</code> is a pack of
subexpressions of sender types.</p></li>
<li><p>The name<span class="add" style="color: #006e28"><ins>s</ins></span>
<code class="sourceCode default">when_all</code> <span class="add" style="color: #006e28"><ins>and
<span><code class="sourceCode default">when_all_with_variant</code></span></ins></span>
denote<span class="rm" style="color: #bf0303"><del>s</del></span> <span class="rm" style="color: #bf0303"><del>a</del></span> customization
point object<span class="add" style="color: #006e28"><ins>s</ins></span>. For some subexpressions
<code class="sourceCode default">snd<sub><em>i</em></sub>...</code>, let <code class="sourceCode default">Snd<sub><em>i</em></sub>...</code> be <code class="sourceCode default">decltype((snd<sub><em>i</em></sub>))...</code>.
The expression<span class="add" style="color: #006e28"><ins>s</ins></span> <code class="sourceCode default">when_all(snd<sub><em>i</em></sub>...)</code>
<span class="rm" style="color: #bf0303"><del>is</del></span> <span class="add" style="color: #006e28"><ins>and <span><code class="sourceCode default">when_all_with_variant(snd<sub><em>i</em></sub>...)</code></span>
are</ins></span> ill-formed if any of the following is true:</p>
<ul>
<li><p>If the number of subexpressions <code class="sourceCode default">snd<sub><em>i</em></sub>...</code> is 0,
or</p></li>
<li><p>If any type
<code class="sourceCode default">Snd<sub><em>i</em></sub></code> does
not satisfy <code class="sourceCode default">sender</code>.</p></li>
<li><p><span class="add" style="color: #006e28"><ins>If the expression
<span><code class="sourceCode default"><em>get-domain-early</em>(snd<sub><em>i</em></sub>)</code></span>
has a different type for any other value of
<em><span><code class="sourceCode default">i</code></span></em>.</ins></span></p></li>
</ul>
<p><span class="add" style="color: #006e28"><ins>Otherwise, those
expressions have the semantics specified below.</ins></span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: The
following paragraph becomes numbered and subsequent paragraphs are
renumbered. ]</span></p></li>
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,
the</del></span><span class="add" style="color: #006e28"><ins>The</ins></span> expression <code class="sourceCode default">when_all(snd<sub><em>i</em></sub>...)</code>
is expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb67"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb67-1"><a href="#cb67-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb67-2"><a href="#cb67-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd<sub><em>0</em></sub>),</span>
<span id="cb67-3"><a href="#cb67-3" aria-hidden="true" tabindex="-1"></a>  <em>make-when-all-sender</em>(snd<sub><em>0</em></sub>, ... snd<sub><em>n-1</em></sub>))</span></code></pre></div>

</div>
</blockquote>
<p><span class="add" style="color: #006e28"><ins>where <span><code class="sourceCode default"><em>make-when-all-sender</em>(snd<sub><em>i</em></sub>...)</code></span>
is expression-equivalent to <span><code class="sourceCode default"><em>make-sender</em>(when_all, {}, snd<sub><em>i</em></sub>...)</code></span>
and returns a sender object
<span><code class="sourceCode default">w</code></span> of type
<span><code class="sourceCode default">W</code></span> that behaves as
follows:</ins></span></p>
<ol type="1">
<li><div class="rm" style="color: #bf0303">
<p><code class="sourceCode default">tag_invoke(when_all, snd<sub><em>i</em></sub>...)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not return a
sender that sends a concatenation of values sent by <code class="sourceCode default">snd<sub><em>i</em></sub>...</code> when they
all complete with <code class="sourceCode default">set_value</code>, the
behavior of calling <code class="sourceCode default">when_all(snd<sub><em>i</em></sub>...)</code>
is undefined.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.
</div></li>
</ul></li>
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise,
constructs a sender
<span><code class="sourceCode default">w</code></span> of type
<span><code class="sourceCode default">W</code></span>.</del></span>
When <code class="sourceCode default">w</code> is connected with some
receiver <code class="sourceCode default">out_rcv</code> of type
<code class="sourceCode default">OutR</code>, it returns an operation
state <code class="sourceCode default">op_state</code> specified as
below:</p>
<ol type="1">
<li>For each sender
<code class="sourceCode default">snd<sub><em>i</em></sub></code>, …
<span class="ednote" style="color: #0000ff">[ Editor&#39;s note: … as before
]</span></li>
</ol>
<blockquote>
<p>…</p>
</blockquote></li>
</ol></li>
<li><p><span class="rm" style="color: #bf0303"><del>The name
<span><code class="sourceCode default">when_all_with_variant</code></span>
denotes a customization point object. For some subexpressions
<span><code class="sourceCode default">snd...</code></span>, let
<span><code class="sourceCode default">Snd</code></span> be
<span><code class="sourceCode default">decltype((snd))</code></span>. If
any type
<span><code class="sourceCode default">Snd<sub><em>i</em></sub></code></span>
in <span><code class="sourceCode default">Snd...</code></span> does not
satisfy <span><code class="sourceCode default">sender</code></span>,
<span><code class="sourceCode default">when_all_with_variant</code></span>
is ill-formed. Otherwise, the</del></span> <span class="add" style="color: #006e28"><ins>The</ins></span> expression <code class="sourceCode default">when_all_with_variant(snd<span class="add" style="color: #006e28"><ins><sub><em>i</em></sub></ins></span>...)</code>
is expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb68"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb68-1"><a href="#cb68-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb68-2"><a href="#cb68-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd<sub><em>0</em></sub>),</span>
<span id="cb68-3"><a href="#cb68-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(when_all_with_variant, {}, snd<sub><em>0</em></sub>, ... snd<sub><em>n-1</em></sub>))</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(when_all_with_variant, snd...)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not return a
sender that, when connected with a receiver of type
<code class="sourceCode default">Rcv</code>, sends the types <code class="sourceCode default"><em>into-variant-type</em>&lt;Snd, env_of_t&lt;Rcv&gt;&gt;...</code>
when they all complete with
<code class="sourceCode default">set_value</code>, the behavior of
calling <code class="sourceCode default">when_all(snd<sub><em>i</em></sub>...)</code>
is undefined.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">when_all(into_variant(snd)...)</code>.</p></li>
</ol>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<ol start="5" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, when_all_with_variant_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">when_all_with_variant_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb69"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb69-1"><a href="#cb69-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, ...child] = snd;</span>
<span id="cb69-2"><a href="#cb69-2" aria-hidden="true" tabindex="-1"></a>return when_all(into_variant(std::move(child))...);</span></code></pre></div>
</blockquote>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">when_all_with_variant(snd...)</code>
sender to become
<code class="sourceCode default">when_all(into_variant(snd)...)</code>
when it is connected with a receiver whose execution domain does not
customize
<code class="sourceCode default">when_all_with_variant</code>.<span> —
<em>end note</em> ]</span></span></p></li>
</ol>

</div>
<ol start="5" type="1">
<li><span class="rm" style="color: #bf0303"><del>For a sender
<span><code class="sourceCode default">snd2</code></span> returned from
<span><code class="sourceCode default">when_all</code></span> or
<span><code class="sourceCode default">when_all_with_variant</code></span>,
<span><code class="sourceCode default">get_env(snd2)</code></span> shall
return an instance of a class equivalent to
<span><code class="sourceCode default">empty_env</code></span>.</del></span>
<span class="add" style="color: #006e28"><ins>Given a pack of
subexpressions
<span><code class="sourceCode default">snd...</code></span>, let
<span><code class="sourceCode default">out_snd</code></span> be an
object returned from
<span><code class="sourceCode default">when_all(snd...)</code></span> or
<span><code class="sourceCode default">when_all_with_variant(snd...)</code></span>
or a copy of such, and let
<span><code class="sourceCode default">env</code></span> be the
environment object returned from
<span><code class="sourceCode default">get_env(out_snd)</code></span>.
Given a query object
<span><code class="sourceCode default">Q</code></span>,
<span><code class="sourceCode default">tag_invoke(Q, env)</code></span>
is expression-equivalent to <span><code class="sourceCode default"><em>get-domain-early</em>(snd<sub><em>0</em></sub>)</code></span>
when <span><code class="sourceCode default">Q</code></span> is
<span><code class="sourceCode default">get_domain</code></span>;
otherwise, it is ill-formed.</ins></span></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.13 [exec.transfer.when.all]</strong> as follows:
]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">transfer_when_all</code> and
<code class="sourceCode default">transfer_when_all_with_variant</code>
both adapt multiple input senders into a sender that completes when all
input senders have completed, ensuring the input senders complete on the
specified scheduler.
<code class="sourceCode default">transfer_when_all</code> only accepts
senders with a single value completion signature and on success
concatenates all the input senders’ value result datums into its own
value completion operation; <code class="sourceCode default">transfer_when_all(<em>scheduler</em>, <em>input-senders</em>...)</code>
is semantically equivalent to <code class="sourceCode default">transfer(when_all(<em>input-senders</em>...), <em>scheduler</em>)</code>.
<code class="sourceCode default">transfer_when_all_with_variant(<em>scheduler</em>, <em>input-senders</em>...)</code>
is semantically equivilant to <code class="sourceCode default">transfer_when_all(<em>scheduler</em>, into_variant(<em>intput-senders</em>)...)</code>.
<span class="note"><span>[ <em>Note:</em> </span>These customizable
composite algorithms can allow for more efficient customizations in some
cases.<span> — <em>end note</em> ]</span></span></p></li>
<li><p>The name
<code class="sourceCode default">transfer_when_all</code> denotes a
customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">snd...</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype(sch)</code> and
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code>, or any type
<code class="sourceCode default">Snd<sub><em>i</em></sub></code> in
<code class="sourceCode default">Snd...</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">transfer_when_all</code> is ill-formed.
Otherwise, the expression
<code class="sourceCode default">transfer_when_all(sch, snd...)</code>
is expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb70"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb70-1"><a href="#cb70-1" aria-hidden="true" tabindex="-1"></a>return transform_sender(</span>
<span id="cb70-2"><a href="#cb70-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb70-3"><a href="#cb70-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(transfer_when_all, sch, snd...));</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(transfer_when_all, sch, snd...)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not return a
sender that sends a concatenation of values sent by
<code class="sourceCode default">snd...</code> when they all complete
with <code class="sourceCode default">set_value</code>, or does not send
its completion operation, other than ones resulting from a scheduling
error, on an execution agent belonging to the associated execution
resource of <code class="sourceCode default">sch</code>, the behavior of
calling
<code class="sourceCode default">transfer_when_all(sch, snd...)</code>
is undefined.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">transfer(when_all(snd...), sch)</code>.</p></li>
</ol>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<ol start="3" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, transfer_when_all_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">transfer_when_all_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb71"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb71-1"><a href="#cb71-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, ...child] = snd;</span>
<span id="cb71-2"><a href="#cb71-2" aria-hidden="true" tabindex="-1"></a>return transfer(when_all(std::move(child)...), std::move(data));</span></code></pre></div>
</blockquote>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">transfer_when_all(sch, snd...)</code>
sender to become
<code class="sourceCode default">transfer(when_all(snd...), sch)</code>
when it is connected with a receiver whose execution domain does not
customize
<code class="sourceCode default">transfer_when_all</code>.<span> —
<em>end note</em> ]</span></span></p></li>
</ol>

</div>
<ol start="3" type="1">
<li><p>The name
<code class="sourceCode default">transfer_when_all_with_variant</code>
denotes a customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">snd...</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If any type
<code class="sourceCode default">Snd<sub><em>i</em></sub></code> in
<code class="sourceCode default">Snd...</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">transfer_when_all_with_variant</code>
is ill-formed. Otherwise, the expression <code class="sourceCode default">transfer_when_all_with_variant(sch, snd...)</code>
is expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb72"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb72-1"><a href="#cb72-1" aria-hidden="true" tabindex="-1"></a>return transform_sender(</span>
<span id="cb72-2"><a href="#cb72-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb72-3"><a href="#cb72-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(transfer_when_all_with_variant, sch, snd...));</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(transfer_when_all_with_variant, snd...)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not return a
sender that, when connected with a receiver of type
<code class="sourceCode default">Rcv</code>, sends the types <code class="sourceCode default"><em>into-variant-type</em>&lt;Snd, env_of_t&lt;Rcv&gt;&gt;...</code>
when they all complete with
<code class="sourceCode default">set_value</code>, the behavior of
calling <code class="sourceCode default">transfer_when_all_with_variant(sch, snd...)</code>
is undefined.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise, <code class="sourceCode default">transfer_when_all(sch, into_variant(snd)...)</code>.</p></li>
</ol>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<ol start="4" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, transfer_when_all_with_variant_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">transfer_when_all_with_variant_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb73"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb73-1"><a href="#cb73-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, ...child] = snd;</span>
<span id="cb73-2"><a href="#cb73-2" aria-hidden="true" tabindex="-1"></a>return transfer_when_all(std::move(data), into_variant(std::move(child))...);</span></code></pre></div>
</blockquote>
<p><span class="note"><span>[ <em>Note:</em> </span>This causes the
<code class="sourceCode default">transfer_when_all_with_variant(sch, snd...)</code>
sender to become <code class="sourceCode default">transfer_when_all(sch, into_variant(snd)...)</code>
when it is connected with a receiver whose execution domain does not
customize
<code class="sourceCode default">transfer_when_all_with_variant</code>.<span>
— <em>end note</em> ]</span></span></p></li>
</ol>

</div>
<ol start="5" type="1">
<li>For a sender <code class="sourceCode default">out_snd</code>
returned from
<code class="sourceCode default">transfer_when_all(sch, snd...)</code>
<span class="add" style="color: #006e28"><ins>or <span><code class="sourceCode default">transfer_when_all_with_variant(sch, snd...)</code></span></ins></span>,
<code class="sourceCode default">get_env(out_snd)</code> shall return a
queryable object <code class="sourceCode default">q</code> such that
<span class="add" style="color: #006e28"><ins><span><code class="sourceCode default">get_domain(q)</code></span>
shall be expression-equivalent to
<span><code class="sourceCode default">get_domain(sch)</code></span>,
and</ins></span> <code class="sourceCode default">get_completion_scheduler&lt;<em>CPO</em>&gt;(q)</code>
returns a copy of <code class="sourceCode default">sch</code>, where
<em><code class="sourceCode default">CPO</code></em> is either
<code class="sourceCode default">set_value_t</code> or
<code class="sourceCode default">set_stopped_t</code>. The <code class="sourceCode default">get_completion_scheduler&lt;set_error_t&gt;</code>
query is not implemented, as the scheduler cannot be guaranteed in case
an error is thrown while trying to schedule work on the given scheduler
object.</li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.14 [exec.into.variant]</strong> as follows: ]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">into_variant</code> adapts a
sender with multiple value completion signatures into a sender with just
one consisting of a <code class="sourceCode default">variant</code> of
<code class="sourceCode default">tuple</code>snd.</p></li>
<li><p>The template
<code class="sourceCode default"><em>into-variant-type</em></code>
computes the type sent by a sender returned from
<code class="sourceCode default">into_variant</code>.</p>
<blockquote>
<div class="sourceCode" id="cb74"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb74-1"><a href="#cb74-1" aria-hidden="true" tabindex="-1"></a>template&lt;class Snd, class Env&gt;</span>
<span id="cb74-2"><a href="#cb74-2" aria-hidden="true" tabindex="-1"></a>    requires sender_in&lt;Snd, Env&gt;</span>
<span id="cb74-3"><a href="#cb74-3" aria-hidden="true" tabindex="-1"></a>  using <em>into-variant-type</em> =</span>
<span id="cb74-4"><a href="#cb74-4" aria-hidden="true" tabindex="-1"></a>    value_types_of_t&lt;Snd, Env&gt;;</span></code></pre></div>
</blockquote></li>
<li><p><code class="sourceCode default">into_variant</code> is a
customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code>,
<code class="sourceCode default">into_variant(snd)</code> is ill-formed.
Otherwise, <code class="sourceCode default">into_variant(snd)</code>
<span class="add" style="color: #006e28"><ins>is expression-equivalent
to:</ins></span></p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb75"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb75-1"><a href="#cb75-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb75-2"><a href="#cb75-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb75-3"><a href="#cb75-3" aria-hidden="true" tabindex="-1"></a>  <em>make-into-variant-sender</em>(snd))</span></code></pre></div>

</div>
</blockquote>
<p><span class="add" style="color: #006e28"><ins>where <span><code class="sourceCode default"><em>make-into-variant-sender</em>(snd)</code></span>
is expression-equivalent to <span><code class="sourceCode default"><em>make-sender</em>(into_variant, {}, snd)</code></span>
and</ins></span> returns a sender <span class="add" style="color: #006e28"><ins>object</ins></span>
<code class="sourceCode default">snd2</code><span class="rm" style="color: #bf0303"><del>.</del></span> <span class="add" style="color: #006e28"><ins>that behaves as follows:</ins></span></p>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note:
Reformatting here ]</span></p>
<ol type="1">
<li><p>When <code class="sourceCode default">snd2</code> is connected
with some receiver <code class="sourceCode default">out_rcv</code>,
it:</p></li>
<li><p>Constructs a receiver
<code class="sourceCode default">rcv</code>:</p>
<ol type="1">
<li><p>If <code class="sourceCode default">set_value(rcv, ts...)</code>
is called, calls <code class="sourceCode default">set_value(out_rcv, <em>into-variant-type</em>&lt;Snd, env_of_t&lt;decltype((rcv))&gt;&gt;(<em>decayed-tuple</em>&lt;decltype(ts)...&gt;(ts...)))</code>.
If this expression throws an exception, calls <code class="sourceCode default">set_error(out_rcv, current_exception())</code>.</p></li>
<li><p><code class="sourceCode default">set_error(rcv, err)</code> is
expression-equivalent to
<code class="sourceCode default">set_error(out_rcv, err)</code>.</p></li>
<li><p><code class="sourceCode default">set_stopped(rcv)</code> is
expression-equivalent to
<code class="sourceCode default">set_stopped(out_rcv)</code>.</p></li>
</ol></li>
<li><p>Calls <code class="sourceCode default">connect(snd, rcv)</code>,
resulting in an operation state
<code class="sourceCode default">op_state2</code>.</p></li>
<li><p>Returns an operation state
<code class="sourceCode default">op_state</code> that contains
<code class="sourceCode default">op_state2</code>. When
<code class="sourceCode default">start(op_state)</code> is called, calls
<code class="sourceCode default">start(op_state2)</code>.</p></li>
<li><p>Given subexpressions <code class="sourceCode default">snd2</code>
and <code class="sourceCode default">env</code> […] <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: …as before ]</span></p></li>
</ol></li>
</ol>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.15 [exec.stopped.as.optional]</strong> as follows:
]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">stopped_as_optional</code> maps
an input sender’s stopped completion operation into the value completion
operation as an empty optional. The input sender’s value completion
operation is also converted into an optional. The result is a sender
that never completes with stopped, reporting cancellation by completing
with an empty optional.</p></li>
<li><p>The name
<code class="sourceCode default">stopped_as_optional</code> denotes a
customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. <span class="rm" style="color: #bf0303"><del>Let
<span><code class="sourceCode default">_get-env-sender_</code></span> be
an expression such that, when it is
<span><code class="sourceCode default">connect</code></span>ed with a
receiver <span><code class="sourceCode default">rcv</code></span>,
<span><code class="sourceCode default">start</code></span> on the
resulting operation state completes immediately by calling
<span><code class="sourceCode default">set_value(rcv, get_env(rcv))</code></span>.</del></span>
The expression
<code class="sourceCode default">stopped_as_optional(snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb76"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb76-1"><a href="#cb76-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb76-2"><a href="#cb76-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb76-3"><a href="#cb76-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(stopped_as_optional, {}, snd))</span></code></pre></div>

</div>
</blockquote>
<blockquote>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb77"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb77-1"><a href="#cb77-1" aria-hidden="true" tabindex="-1"></a>let_value(</span>
<span id="cb77-2"><a href="#cb77-2" aria-hidden="true" tabindex="-1"></a>  <em>get-env-sender</em>,</span>
<span id="cb77-3"><a href="#cb77-3" aria-hidden="true" tabindex="-1"></a>  []&lt;class Env&gt;(const Env&amp;) requires <em>single-sender</em>&lt;Snd, Env&gt; {</span>
<span id="cb77-4"><a href="#cb77-4" aria-hidden="true" tabindex="-1"></a>    return let_stopped(</span>
<span id="cb77-5"><a href="#cb77-5" aria-hidden="true" tabindex="-1"></a>      then(snd,</span>
<span id="cb77-6"><a href="#cb77-6" aria-hidden="true" tabindex="-1"></a>        []&lt;class T&gt;(T&amp;&amp; t) {</span>
<span id="cb77-7"><a href="#cb77-7" aria-hidden="true" tabindex="-1"></a>          return optional&lt;decay_t&lt;<em>single-sender-value-type</em>&lt;Snd, Env&gt;&gt;&gt;{</span>
<span id="cb77-8"><a href="#cb77-8" aria-hidden="true" tabindex="-1"></a>            std::forward&lt;T&gt;(t)</span>
<span id="cb77-9"><a href="#cb77-9" aria-hidden="true" tabindex="-1"></a>          };</span>
<span id="cb77-10"><a href="#cb77-10" aria-hidden="true" tabindex="-1"></a>        }</span>
<span id="cb77-11"><a href="#cb77-11" aria-hidden="true" tabindex="-1"></a>      ),</span>
<span id="cb77-12"><a href="#cb77-12" aria-hidden="true" tabindex="-1"></a>      [] () noexcept {</span>
<span id="cb77-13"><a href="#cb77-13" aria-hidden="true" tabindex="-1"></a>        return just(optional&lt;decay_t&lt;<em>single-sender-value-type</em>&lt;Snd, Env&gt;&gt;&gt;{});</span>
<span id="cb77-14"><a href="#cb77-14" aria-hidden="true" tabindex="-1"></a>      }</span>
<span id="cb77-15"><a href="#cb77-15" aria-hidden="true" tabindex="-1"></a>    );</span>
<span id="cb77-16"><a href="#cb77-16" aria-hidden="true" tabindex="-1"></a>  }</span>
<span id="cb77-17"><a href="#cb77-17" aria-hidden="true" tabindex="-1"></a>)</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="add" style="color: #006e28">

<ol start="3" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code> and
<code class="sourceCode default">Env</code> is
<code class="sourceCode default">decltype((env))</code>. If either <code class="sourceCode default"><em>sender-for</em>&lt;Snd, stopped_as_optional_t&gt;</code>
or <code class="sourceCode default"><em>single-sender</em>&lt;Snd, Env&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">stopped_as_optional_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb78"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb78-1"><a href="#cb78-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, child] = snd;</span>
<span id="cb78-2"><a href="#cb78-2" aria-hidden="true" tabindex="-1"></a>using V = <em>single-sender-value-type</em>&lt;Snd, Env&gt;;</span>
<span id="cb78-3"><a href="#cb78-3" aria-hidden="true" tabindex="-1"></a>return let_stopped(</span>
<span id="cb78-4"><a href="#cb78-4" aria-hidden="true" tabindex="-1"></a>    then(std::move(child),</span>
<span id="cb78-5"><a href="#cb78-5" aria-hidden="true" tabindex="-1"></a>         []&lt;class T&gt;(T&amp;&amp; t) { return optional&lt;V&gt;(std::forward&lt;T&gt;(t)); }),</span>
<span id="cb78-6"><a href="#cb78-6" aria-hidden="true" tabindex="-1"></a>    []() noexcept { return just(optional&lt;V&gt;()); });</span></code></pre></div>
</blockquote></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.16 [exec.stopped.as.error]</strong> as follows:
]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">stopped_as_error</code> maps an
input sender’s stopped completion operation into an error completion
operation as a custom error type. The result is a sender that never
completes with stopped, reporting cancellation by completing with an
error.</p></li>
<li><p>The name <code class="sourceCode default">stopped_as_error</code>
denotes a customization point object. For some subexpressions
<code class="sourceCode default">snd</code> and
<code class="sourceCode default">err</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code> and let
<code class="sourceCode default">Err</code> be
<code class="sourceCode default">decltype((err))</code>. If the type
<code class="sourceCode default">Snd</code> does not satisfy
<code class="sourceCode default">sender</code> or if the type
<code class="sourceCode default">Err</code> doesn’t satisfy
<em><code class="sourceCode default">movable-value</code></em>,
<code class="sourceCode default">stopped_as_error(snd, err)</code> is
ill-formed. Otherwise, the expression
<code class="sourceCode default">stopped_as_error(snd, err)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="rm" style="color: #bf0303">

<div class="sourceCode" id="cb79"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb79-1"><a href="#cb79-1" aria-hidden="true" tabindex="-1"></a>let_stopped(snd, [] { return just_error(err); })</span></code></pre></div>

</div>
</blockquote>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb80"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb80-1"><a href="#cb80-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb80-2"><a href="#cb80-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb80-3"><a href="#cb80-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(stopped_as_error, err, snd))</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="add" style="color: #006e28">

<ol start="3" type="1">
<li><p>Let <code class="sourceCode default">snd</code> and
<code class="sourceCode default">env</code> be subexpressions such that
<code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code> and
<code class="sourceCode default">Env</code> is
<code class="sourceCode default">decltype((env))</code>. If <code class="sourceCode default"><em>sender-for</em>&lt;Snd, stopped_as_error_t&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">stopped_as_error_t().transform_sender(snd, env)</code>
is ill-formed; otherwise, it is equal to:</p>
<blockquote>
<div class="sourceCode" id="cb81"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb81-1"><a href="#cb81-1" aria-hidden="true" tabindex="-1"></a>auto [tag, data, child] = snd;</span>
<span id="cb81-2"><a href="#cb81-2" aria-hidden="true" tabindex="-1"></a>return let_stopped(</span>
<span id="cb81-3"><a href="#cb81-3" aria-hidden="true" tabindex="-1"></a>    std::move(child),</span>
<span id="cb81-4"><a href="#cb81-4" aria-hidden="true" tabindex="-1"></a>    [err = std::move(data)]() mutable { return just_error(std::move(err)); });</span></code></pre></div>
</blockquote></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.6.17 [exec.ensure.started]</strong> as follows:
]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">ensure_started</code> eagerly
starts the execution of a sender, returning a sender that is usable as
intput to additional sender algorithms.</p></li>
<li><p>Let
<em><code class="sourceCode default">ensure-started-env</code></em> be
the type of an execution environment such that, given an instance
<code class="sourceCode default">env</code>, the expression
<code class="sourceCode default">get_stop_token(env)</code> is
well-formed and has type
<code class="sourceCode default">stop_token</code>.</p></li>
<li><p>The name <code class="sourceCode default">ensure_started</code>
denotes a customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default">sender_in&lt;Snd, <em>ensure-started-env</em>&gt;</code>
or <code class="sourceCode default">constructible_from&lt;decay_t&lt;env_of_t&lt;Snd&gt;&gt;, env_of_t&lt;Snd&gt;&gt;</code>
is <code class="sourceCode default">false</code>,
<code class="sourceCode default">ensure_started(snd)</code> is
ill-formed. Otherwise, the expression
<code class="sourceCode default">ensure_started(snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb82"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb82-1"><a href="#cb82-1" aria-hidden="true" tabindex="-1"></a>transform_sender(</span>
<span id="cb82-2"><a href="#cb82-2" aria-hidden="true" tabindex="-1"></a>  <em>get-domain-early</em>(snd),</span>
<span id="cb82-3"><a href="#cb82-3" aria-hidden="true" tabindex="-1"></a>  <em>make-sender</em>(ensure_started, {}, snd));</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(ensure_started, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">tag_invoke(ensure_started, snd)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above
satisfies <code class="sourceCode default">sender</code>.</li>
</ul></li>
</ol>
</blockquote>

</div>
<!-- -->
<blockquote>
<ol type="1">
<li><p><span class="add" style="color: #006e28"><ins>Let
<span><code class="sourceCode default">snd</code></span> be a
subexpression such that
<span><code class="sourceCode default">Snd</code></span> is
<span><code class="sourceCode default">decltype((snd))</code></span>,
and let <span><code class="sourceCode default">env...</code></span> be a
pack of subexpressions such that
<span><code class="sourceCode default">sizeof...(env) &lt;= 1</code></span>
is <span><code class="sourceCode default">true</code></span>. If
<span><code class="sourceCode default"><em>sender-for</em>&lt;Snd, ensure_started_t&gt;</code></span>
is <span><code class="sourceCode default">false</code></span>, then the
expression <span><code class="sourceCode default">ensure_started_t().transform_sender(snd, env...)</code></span>
is ill-formed; otherwise, it returns</ins></span> <span class="rm" style="color: #bf0303"><del>Otherwise, constructs</del></span> a sender
<code class="sourceCode default">snd2</code> <span class="rm" style="color: #bf0303"><del>, which</del></span><span class="add" style="color: #006e28"><ins>that</ins></span>:</p>
<ol type="1">
<li>Creates an object <code class="sourceCode default">sh_state</code>
that <span class="ednote" style="color: #0000ff">[ Editor&#39;s note: … as
before ]</span></li>
</ol></li>
</ol>
</blockquote>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.7.1 [exec.start.detached]</strong> as follows:
]</span></p>
<ol type="1">
<li><p><code class="sourceCode default">start_detached</code> eagerly
starts a sender without the caller needing to manage the lifetimes of
any objects.</p></li>
<li><p>The name <code class="sourceCode default">start_detached</code>
denotes a customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. <span class="rm" style="color: #bf0303"><del>If
<span><code class="sourceCode default">Snd</code></span> does not
satisfy
<span><code class="sourceCode default">sender</code></span></del></span><span class="add" style="color: #006e28"><ins>If
<span><code class="sourceCode default">sender_in&lt;Snd, empty_env&gt;</code></span>
is
<span><code class="sourceCode default">false</code></span></ins></span>,
<code class="sourceCode default">start_detached</code> is ill-formed.
Otherwise, the expression
<code class="sourceCode default">start_detached(snd)</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb83"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb83-1"><a href="#cb83-1" aria-hidden="true" tabindex="-1"></a>apply_sender(<em>get-domain-early</em>(snd), start_detached, snd)</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="add" style="color: #006e28">

<blockquote>
<ul>
<li><em>Mandates:</em> The type of the expression above is
<code class="sourceCode default">void</code>.</li>
</ul>
</blockquote>

</div>
<blockquote>
<p>If the <span class="rm" style="color: #bf0303"><del>function
selected</del></span><span class="add" style="color: #006e28"><ins>expression</ins></span> above does not
eagerly start the sender <code class="sourceCode default">snd</code>
after connecting it with a receiver that ignores value and stopped
completion operations and calls
<code class="sourceCode default">terminate()</code> on error
completions, the behavior of calling
<code class="sourceCode default">start_detached(snd)</code> is
undefined.</p>
</blockquote>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(start_detached, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default">void</code>.</li>
</ul></li>
<li><p>Otherwise,
<code class="sourceCode default">tag_invoke(start_detached, snd)</code>,
if that expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default">void</code>.</li>
</ul></li>
<li><p>Otherwise, let <code class="sourceCode default">Rcv</code> be the
type of a receiver, let <code class="sourceCode default">rcv</code> be
an rvalue of type <code class="sourceCode default">Rcv</code>, and let
<code class="sourceCode default">crcv</code> be a lvalue reference to
<code class="sourceCode default">const Rcv</code> such that:</p>
<p>— The expression
<code class="sourceCode default">set_value(rcv)</code> is not
potentially-throwing and has no effect,</p>
<p>— For any subexpression <code class="sourceCode default">err</code>,
the expression
<code class="sourceCode default">set_error(rcv, err)</code> is
expression-equivalent to
<code class="sourceCode default">terminate()</code>,</p>
<p>— The expression
<code class="sourceCode default">set_stopped(rcv)</code> is not
potentially-throwing and has no effect, and</p>
<p>— The expression
<code class="sourceCode default">get_env(crcv)</code> is
expression-equivalent to
<code class="sourceCode default">empty_env{}</code>.</p>
<p>Calls <code class="sourceCode default">connect(snd, rcv)</code>,
resulting in an operation state
<code class="sourceCode default">op_state</code>, then calls
<code class="sourceCode default">start(op_state)</code>.</p></li>
</ol>
</blockquote>

</div>
<div class="add" style="color: #006e28">

<ol start="3" type="1">
<li><p>Let <code class="sourceCode default">snd</code> be a
subexpression such that <code class="sourceCode default">Snd</code> is
<code class="sourceCode default">decltype((snd))</code>, and let
<em><code class="sourceCode default">detached-receiver</code></em> and
<em><code class="sourceCode default">detached-operation</code></em> be
the following exposition-only class types:</p>
<blockquote>
<div class="sourceCode" id="cb84"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb84-1"><a href="#cb84-1" aria-hidden="true" tabindex="-1"></a>struct <em>detached-receiver</em> {</span>
<span id="cb84-2"><a href="#cb84-2" aria-hidden="true" tabindex="-1"></a>  using is_receiver = <em>unspecified</em>;</span>
<span id="cb84-3"><a href="#cb84-3" aria-hidden="true" tabindex="-1"></a>  <em>detached-operation</em>* <em>op</em>; <em>// exposition only</em></span>
<span id="cb84-4"><a href="#cb84-4" aria-hidden="true" tabindex="-1"></a>   </span>
<span id="cb84-5"><a href="#cb84-5" aria-hidden="true" tabindex="-1"></a>  friend void tag_invoke(set_value_t, <em>detached-receiver</em>&amp;&amp; self) noexcept { delete self.<em>op</em>; }</span>
<span id="cb84-6"><a href="#cb84-6" aria-hidden="true" tabindex="-1"></a>  friend void tag_invoke(set_error_t, <em>detached-receiver</em>&amp;&amp;, auto&amp;&amp;) noexcept { terminate(); }</span>
<span id="cb84-7"><a href="#cb84-7" aria-hidden="true" tabindex="-1"></a>  friend void tag_invoke(set_stopped_t, <em>detached-receiver</em>&amp;&amp; self) noexcept { delete self.<em>op</em>; }</span>
<span id="cb84-8"><a href="#cb84-8" aria-hidden="true" tabindex="-1"></a>  friend empty_env tag_invoke(get_env_t, const <em>detached-receiver</em>&amp;) noexcept { return {}; }</span>
<span id="cb84-9"><a href="#cb84-9" aria-hidden="true" tabindex="-1"></a>};</span>
<span id="cb84-10"><a href="#cb84-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb84-11"><a href="#cb84-11" aria-hidden="true" tabindex="-1"></a>struct <em>detached-operation</em> {</span>
<span id="cb84-12"><a href="#cb84-12" aria-hidden="true" tabindex="-1"></a>  connect_result_t&lt;Snd, <em>detached-receiver</em>&gt; <em>op</em>; <em>// exposition only</em></span>
<span id="cb84-13"><a href="#cb84-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb84-14"><a href="#cb84-14" aria-hidden="true" tabindex="-1"></a>  explicit <em>detached-operation</em>(Snd&amp;&amp; snd)</span>
<span id="cb84-15"><a href="#cb84-15" aria-hidden="true" tabindex="-1"></a>    : <em>op</em>(connect(std::forward&lt;Snd&gt;(snd), <em>detached-receiver</em>{this}))</span>
<span id="cb84-16"><a href="#cb84-16" aria-hidden="true" tabindex="-1"></a>  {}</span>
<span id="cb84-17"><a href="#cb84-17" aria-hidden="true" tabindex="-1"></a>};</span></code></pre></div>
</blockquote>
<p>If <code class="sourceCode default">sender_to&lt;Snd, <em>detached-receiver</em>&gt;</code>
is <code class="sourceCode default">false</code>, then the expression
<code class="sourceCode default">start_detached_t().apply_sender(snd)</code>
is ill-formed; otherwise, it is expression-equivalent to:</p>
<blockquote>
<div class="sourceCode" id="cb85"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb85-1"><a href="#cb85-1" aria-hidden="true" tabindex="-1"></a>start((new <em>detached-operation</em>(snd))-&gt;<em>op</em>)</span></code></pre></div>
</blockquote></li>
</ol>

</div>
<p><span class="ednote" style="color: #0000ff">[ Editor&#39;s note: Change
<strong>§11.9.7.2 [exec.sync.wait]</strong> as follows: ]</span></p>
<ol type="1">
<li>[…]</li>
</ol>
<!-- -->
<ol start="4" type="1">
<li>The name
<code class="sourceCode default">this_thread::sync_wait</code> denotes a
customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be
<code class="sourceCode default">decltype((snd))</code>. If <code class="sourceCode default">sender_in&lt;Snd, <em>sync-wait-env</em>&gt;</code>
is <code class="sourceCode default">false</code>, or <span class="rm" style="color: #bf0303"><del>the number of the arguments <span><code class="sourceCode default">completion_signatures_of_t&lt;Snd, <em>sync-wait-env</em>&gt;::value_types</code></span>
passed into the
<span><code class="sourceCode default">Variant</code></span> template
parameter is not 1</del></span> <span class="add" style="color: #006e28"><ins>if the type <span><code class="sourceCode default">completion_signatures_of_t&lt;Snd, <em>sync-wait-env</em>, <em>type-list</em>, type_identity_t&gt;</code></span>
is ill-formed</ins></span>,
<code class="sourceCode default">this_thread::sync_wait(snd)</code> is
ill-formed. Otherwise,
<code class="sourceCode default">this_thread::sync_wait(snd)</code> is
expression-equivalent to:</li>
</ol>
<blockquote>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb86"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb86-1"><a href="#cb86-1" aria-hidden="true" tabindex="-1"></a>apply_sender(<em>get-domain-early</em>(snd), sync_wait, snd)</span></code></pre></div>

</div>
</blockquote>
</blockquote>
<div class="add" style="color: #006e28">

<blockquote>
<ul>
<li><em>Mandates:</em> The type of expression above is <code class="sourceCode default"><em>sync-wait-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul>
</blockquote>

</div>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(this_thread::sync_wait, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd)</code>,
if this expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default"><em>sync-wait-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul></li>
<li><p>Otherwise, <code class="sourceCode default">tag_invoke(this_thread::sync_wait, snd)</code>,
if this expression is valid and its type is.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default"><em>sync-wait-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul></li>
</ol>
</blockquote>

</div>
<ol start="5" type="1">
<li><p><span class="rm" style="color: #bf0303"><del>Otherwise:</del></span> <span class="add" style="color: #006e28"><ins>Let
<em><span><code class="sourceCode default">sync-wait-receiver</code></span></em>
be a class type that satisfies
<span><code class="sourceCode default">receiver</code></span>, let
<span><code class="sourceCode default">rcv</code></span> be an xvalue of
that type, and let
<span><code class="sourceCode default">crcv</code></span> be a const
lvalue referring to
<span><code class="sourceCode default">rcv</code></span> such that
<span><code class="sourceCode default">get_env(crcv)</code></span> has
type
<em><span><code class="sourceCode default">sync-wait-env</code></span></em>.
If <span><code class="sourceCode default">sender_in&lt;Snd, <em>sync-wait-env</em>&gt;</code></span>
is <span><code class="sourceCode default">false</code></span>, or if the
type <span><code class="sourceCode default">completion_signatures_of_t&lt;Snd, <em>sync-wait-env</em>, <em>type-list</em>, type_identity_t&gt;</code></span>
is ill-formed, the expression
<span><code class="sourceCode default">sync_wait_t().apply_sender(snd)</code></span>
is ill-formed; otherwise, it has the following effects:</ins></span></p>
<ol type="1">
<li><p><span class="rm" style="color: #bf0303"><del>Constructs a
receiver
<span><code class="sourceCode default">rcv</code></span>.</del></span></p></li>
<li><p>Calls <code class="sourceCode default">connect(snd, rcv)</code>,
resulting in an operation state
<code class="sourceCode default">op_state</code>, then calls
<code class="sourceCode default">start(op_state)</code>.</p></li>
<li><p>Blocks the current thread until a completion operation of
<code class="sourceCode default">rcv</code> is executed. When it is:</p>
<ol type="1">
<li><p>If <code class="sourceCode default">set_value(rcv, ts...)</code>
has been called, returns <code class="sourceCode default"><em>sync-wait-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;{<em>decayed-tuple</em>&lt;decltype(ts)...&gt;{ts...}}</code>.
If that expression exits exceptionally, the exception is propagated to
the caller of
<code class="sourceCode default">sync_wait</code>.</p></li>
<li><p>If <code class="sourceCode default">set_error(rcv, err)</code>
has been called, let <code class="sourceCode default">Err</code> be the
decayed type of <code class="sourceCode default">err</code>. If
<code class="sourceCode default">Err</code> is
<code class="sourceCode default">exception_ptr</code>, calls
<code class="sourceCode default">std::rethrow_exception(err)</code>.
Otherwise, if the <code class="sourceCode default">Err</code> is
<code class="sourceCode default">error_code</code>, throws
<code class="sourceCode default">system_error(err)</code>. Otherwise,
throws <code class="sourceCode default">err</code>.</p></li>
<li><p>If <code class="sourceCode default">set_stopped(rcv)</code> has
been called, returns <code class="sourceCode default"><em>sync-wait-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;{}</code>.</p></li>
</ol></li>
</ol></li>
<li><p>The name <code class="sourceCode default">this_thread::sync_wait_with_variant</code>
denotes a customization point object. For some subexpression
<code class="sourceCode default">snd</code>, let
<code class="sourceCode default">Snd</code> be the type of
<code class="sourceCode default">into_variant(snd)</code>. If <code class="sourceCode default">sender_in&lt;Snd, <em>sync-wait-env</em>&gt;</code>
is <code class="sourceCode default">false</code>, <code class="sourceCode default">this_thread::sync_wait_with_variant(snd)</code>
is ill-formed. Otherwise, <code class="sourceCode default">this_thread::sync_wait_with_variant(snd)</code>
is expression-equivalent to:</p></li>
</ol>
<blockquote>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb87"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb87-1"><a href="#cb87-1" aria-hidden="true" tabindex="-1"></a>apply_sender(<em>get-domain-early</em>(snd), sync_wait_with_variant, snd)</span></code></pre></div>

</div>
</blockquote>
</blockquote>
<div class="add" style="color: #006e28">

<blockquote>
<ul>
<li><em>Mandates:</em> The type of expression above is <code class="sourceCode default"><em>sync-wait-with-variant-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul>
</blockquote>

</div>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(this_thread::sync_wait_with_variant, get_completion_scheduler&lt;set_value_t&gt;(get_env(snd)), snd)</code>,
if this expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default"><em>sync-wait-with-variant-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul></li>
<li><p>Otherwise, <code class="sourceCode default">tag_invoke(this_thread::sync_wait_with_variant, snd)</code>,
if this expression is valid.</p>
<ul>
<li><em>Mandates:</em> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default"><em>sync-wait-with-variant-type</em>&lt;Snd, <em>sync-wait-env</em>&gt;</code>.</li>
</ul></li>
</ol>
</blockquote>

</div>
<ol start="6" type="1">
<li><span class="rm" style="color: #bf0303"><del>Otherwise,</del></span><span class="add" style="color: #006e28"><ins>The expression <span><code class="sourceCode default">sync_wait_with_variant_t().apply_sender(snd)</code></span>
is expression-equivalent to</ins></span> <code class="sourceCode default">this_thread::sync_wait(into_variant(snd))</code>.</li>
</ol>
<p>[Update <strong>§11.10 [exec.execute]</strong> as follows:]</p>
<ol type="1">
<li><p><code class="sourceCode default">execute</code> creates
fire-and-forget tasks on a specified scheduler.</p></li>
<li><p>The name <code class="sourceCode default">execute</code> denotes
a customization point object. For some subexpressions
<code class="sourceCode default">sch</code> and
<code class="sourceCode default">f</code>, let
<code class="sourceCode default">Sch</code> be
<code class="sourceCode default">decltype((sch))</code> and
<code class="sourceCode default">F</code> be
<code class="sourceCode default">decltype((f))</code>. If
<code class="sourceCode default">Sch</code> does not satisfy
<code class="sourceCode default">scheduler</code> or
<code class="sourceCode default">F</code> does not satisfy
<code class="sourceCode default">invocable</code>,
<code class="sourceCode default">execute</code> is ill-formed.
Otherwise, <code class="sourceCode default">execute</code> is
expression-equivalent to:</p>
<blockquote>
<div class="add" style="color: #006e28">

<div class="sourceCode" id="cb88"><pre class="sourceCode default default"><code class="sourceCode default"><span id="cb88-1"><a href="#cb88-1" aria-hidden="true" tabindex="-1"></a>apply_sender(</span>
<span id="cb88-2"><a href="#cb88-2" aria-hidden="true" tabindex="-1"></a>  <em>query-or-default</em>(get_domain, sch, default_domain()),</span>
<span id="cb88-3"><a href="#cb88-3" aria-hidden="true" tabindex="-1"></a>  execute, schedule(sch), f)</span></code></pre></div>

</div>
</blockquote></li>
</ol>
<div class="add" style="color: #006e28">

<blockquote>
<ul>
<li><em>Mandates:</em> The type of the expression above is
<code class="sourceCode default">void</code>.</li>
</ul>
</blockquote>

</div>
<div class="rm" style="color: #bf0303">

<blockquote>
<ol type="1">
<li><p><code class="sourceCode default">tag_invoke(execute, sch, f)</code>,
if that expression is valid. If the function selected by
<code class="sourceCode default">tag_invoke</code> does not invoke the
function <code class="sourceCode default">f</code> (or an object
decay-copied from <code class="sourceCode default">f</code>) on an
execution agent belonging to the associated execution resource of
<code class="sourceCode default">sch</code>, or if it does not call
<code class="sourceCode default">std::terminate</code> if an error
occurs after control is returned to the caller, the behavior of calling
<code class="sourceCode default">execute</code> is undefined.</p>
<ul>
<li><i>Mandates:</i> The type of the
<code class="sourceCode default">tag_invoke</code> expression above is
<code class="sourceCode default">void</code>.</li>
</ul></li>
</ol>
</blockquote>

</div>
<ol start="3" type="1">
<li><span class="rm" style="color: #bf0303"><del>Otherwise,</del></span>
<span class="add" style="color: #006e28"><ins>For some subexpressions
<span><code class="sourceCode default">snd</code></span> and
<span><code class="sourceCode default">f</code></span> where
<span><code class="sourceCode default">F</code></span> is
<span><code class="sourceCode default">decltype((f))</code></span>, if
<span><code class="sourceCode default">F</code></span> does not satisfy
<span><code class="sourceCode default">invocable</code></span>, the
expression
<span><code class="sourceCode default">execute_t().apply_sender(snd, f)</code></span>
is ill-formed; otherwise, it is expression-equivalent to</ins></span>
<code class="sourceCode default">start_detached(then(<span class="rm" style="color: #bf0303"><del>schedule(sch)</del></span><span class="add" style="color: #006e28"><ins>snd</ins></span>, f))</code>.</li>
</ol>
<h1 data-number="5" id="bibliography"><span class="header-section-number">5</span> References<a href="#bibliography" class="self-link"></a></h1>
<div id="refs" class="references csl-bib-body hanging-indent" role="doc-bibliography">
<div id="ref-P1061R5" class="csl-entry" role="doc-biblioentry">
[P1061R5] Barry Revzin, Jonathan Wakely. 2023-05-18. Structured Bindings
can introduce a Pack. <a href="https://wg21.link/p1061r5"><div class="csl-block">https://wg21.link/p1061r5</div></a>
</div>
<div id="ref-P2141R1" class="csl-entry" role="doc-biblioentry">
[P2141R1] Antony Polukhin. 2023-05-03. Aggregates are named tuples. <a href="https://wg21.link/p2141r1"><div class="csl-block">https://wg21.link/p2141r1</div></a>
</div>
<div id="ref-P2300R7" class="csl-entry" role="doc-biblioentry">
[P2300R7] Eric Niebler, Michał Dominiak, Georgy Evtushenko, Lewis Baker,
Lucian Radu Teodorescu, Lee Howes, Kirk Shoop, Michael Garland, Bryce
Adelstein Lelbach. 2023-04-21. `std::execution`. <a href="https://wg21.link/p2300r7"><div class="csl-block">https://wg21.link/p2300r7</div></a>
</div>
<div id="ref-stdexecgithub" class="csl-entry" role="doc-biblioentry">
[stdexecgithub] stdexec. <a href="https://github.com/NVIDIA/stdexec"><div class="csl-block">https://github.com/NVIDIA/stdexec</div></a>
</div>
</div>
</div>
</div>
</body>
</html>
