<!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" />
  <title>Allow programmers to control coroutine elision</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.hanging-indent{margin-left: 1.5em; text-indent: -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; }
      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;
font-size: 135%;
}
@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">Allow programmers to control coroutine elision</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2477R3</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2022-6-28</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>
      Evolution Working Group<br>
    </td>
  </tr>
  <tr>
    <td style="vertical-align:top">Reply-to:</td>
    <td>
      Chuanqi Xu<br>&lt;<a href="mailto:chuanqi.xcq@alibaba-inc.com" class="email">chuanqi.xcq@alibaba-inc.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="#abstract"><span class="toc-section-number">1</span> Abstract<span></span></a></li>
<li><a href="#change-log"><span class="toc-section-number">2</span> Change Log<span></span></a>
<ul>
<li><a href="#r3"><span class="toc-section-number">2.1</span> R3<span></span></a></li>
<li><a href="#r2"><span class="toc-section-number">2.2</span> R2<span></span></a></li>
<li><a href="#r1"><span class="toc-section-number">2.3</span> R1<span></span></a></li>
</ul></li>
<li><a href="#background-and-motivation"><span class="toc-section-number">3</span> Background and Motivation<span></span></a></li>
<li><a href="#proposed-solutions"><span class="toc-section-number">4</span> Proposed Solutions<span></span></a>
<ul>
<li><a href="#enum-class-coro_elision"><span class="toc-section-number">4.1</span> enum class coro_elision<span></span></a></li>
<li><a href="#terminology"><span class="toc-section-number">4.2</span> Terminology<span></span></a>
<ul>
<li><a href="#coroutine-call"><span class="toc-section-number">4.2.1</span> Coroutine Call<span></span></a></li>
</ul></li>
<li><a href="#informal-description-for-coroutine-elision"><span class="toc-section-number">4.3</span> Informal Description for coroutine elision<span></span></a></li>
<li><a href="#informal-description-for-controlling-coroutine-elision"><span class="toc-section-number">4.4</span> Informal Description for controlling coroutine elision<span></span></a></li>
</ul></li>
<li><a href="#examples"><span class="toc-section-number">5</span> Examples<span></span></a>
<ul>
<li><a href="#controlling-coroutine-elision-by-the-default-argument"><span class="toc-section-number">5.1</span> Controlling coroutine elision by the default argument<span></span></a></li>
</ul></li>
<li><a href="#a-case-study-for-asynchronous-coroutines"><span class="toc-section-number">6</span> A case study for asynchronous coroutines<span></span></a>
<ul>
<li><a href="#compiler-side"><span class="toc-section-number">6.1</span> Compiler Side<span></span></a></li>
<li><a href="#programmers-side"><span class="toc-section-number">6.2</span> Programmers Side<span></span></a></li>
</ul></li>
<li><a href="#limitations"><span class="toc-section-number">7</span> Limitations<span></span></a></li>
<li><a href="#concerns"><span class="toc-section-number">8</span> Concerns<span></span></a></li>
<li><a href="#wording"><span class="toc-section-number">9</span> Wording<span></span></a></li>
<li><a href="#implementation"><span class="toc-section-number">10</span> Implementation<span></span></a></li>
<li><a href="#summary"><span class="toc-section-number">11</span> Summary<span></span></a></li>
<li><a href="#qa"><span class="toc-section-number">12</span> Q&amp;A<span></span></a>
<ul>
<li><a href="#whats-the-relationship-between-coroutine-elision-and-copy-elision"><span class="toc-section-number">12.1</span> What’s the relationship between Coroutine elision and Copy Elision?<span></span></a></li>
<li><a href="#is-it-good-to-connect-coroutine-elision-and-copy-elision"><span class="toc-section-number">12.2</span> Is it good to connect coroutine elision and copy elision?<span></span></a></li>
<li><a href="#would-the-compiler-now-perform-coroutine-elision-stably-for-synchronous-cases"><span class="toc-section-number">12.3</span> Would the compiler now perform coroutine elision stably for synchronous cases?<span></span></a></li>
<li><a href="#what-will-happen-if-we-perform-coroutine-elision-inside-another-coroutine"><span class="toc-section-number">12.4</span> What will happen if we perform coroutine elision inside another coroutine?<span></span></a></li>
<li><a href="#is-it-possible-that-the-total-used-memory-becomes-lower-after-we-elide-more-coroutines"><span class="toc-section-number">12.5</span> Is it possible that the total used memory becomes lower after we elide more coroutines<span></span></a></li>
<li><a href="#is-it-possible-that-the-total-used-memory-becomes-higher-after-we-elide-more-coroutines"><span class="toc-section-number">12.6</span> Is it possible that the total used memory becomes higher after we elide more coroutines<span></span></a></li>
<li><a href="#would-the-allocation-counts-go-higher-if-we-elide-more-coroutines"><span class="toc-section-number">12.7</span> Would the allocation counts go higher if we elide more coroutines<span></span></a></li>
</ul></li>
<li><a href="#acknowledgments"><span class="toc-section-number">13</span> Acknowledgments<span></span></a></li>
</ul>
</div>
<h1 data-number="1" id="abstract"><span class="header-section-number">1</span> Abstract<a href="#abstract" class="self-link"></a></h1>
<p>It is a well-known problem that coroutine needs dynamic allocation to work. Although there is a compiler optimization called coroutine elision which aims to eliminate dynamic allocations for coroutines, it doesn’t work effectively and programmers couldn’t control it in a well-defined way. So here we propose a method to allow programmers to control coroutine elision explicitly.</p>
<h1 data-number="2" id="change-log"><span class="header-section-number">2</span> Change Log<a href="#change-log" class="self-link"></a></h1>
<h2 data-number="2.1" id="r3"><span class="header-section-number">2.1</span> R3<a href="#r3" class="self-link"></a></h2>
<ul>
<li>Rewrite the proposal to make it easier to read.</li>
<li>Propose to evaluate <code class="sourceCode default">promise_type::must_elide(P0, ..., Pn)</code> first. The design mimics the current design of coroutines to try construct <code class="sourceCode default">promise_type</code> from <code class="sourceCode default">promise_type(P0, ..., Pn)</code> and call <code class="sourceCode default">promise_type::new(std::size_t, P0, ..., Pn)</code> to allocate coroutine state at first.</li>
<li>Add a case study for the asynchronous case to explain why the compiler couldn’t handle it and why the programmers could understand it better than the compiler.</li>
<li>Don’t propose to detect coroutine elision due to the limited use case.</li>
<li>Discuss the relationship between coroutine elision and the total memory usage.</li>
<li>More examples.</li>
</ul>
<h2 data-number="2.2" id="r2"><span class="header-section-number">2.2</span> R2<a href="#r2" class="self-link"></a></h2>
<ul>
<li>Change the return type of <code class="sourceCode default">must_elide</code> from boolean to a tristate enum class.</li>
<li>Add more backgrounds in the <code class="sourceCode default">Background and Motivation</code> section.</li>
<li>Add a discussion talking about making <code class="sourceCode default">must_elide</code> a non-constexpr function.</li>
</ul>
<h2 data-number="2.3" id="r1"><span class="header-section-number">2.3</span> R1<a href="#r1" class="self-link"></a></h2>
<ul>
<li>Adding more examples. Many thanks for Lewis Baker offering these examples!</li>
<li>Rename <code class="sourceCode default">should_elide</code> to <code class="sourceCode default">must_elide</code>.</li>
<li>Discussing the semantics of the storage lifetime of elided coroutine frame.</li>
<li>Adding more words about necessity, risks, and design ideas.</li>
<li>Correcting that <code class="sourceCode default">coroutine_handle&lt;&gt;::elided()</code> and <code class="sourceCode default">coroutine_handle&lt;PromiseType&gt;::elided()</code> should be a runtime constant instead of a compile constant.</li>
<li>Discussing how should we support <code class="sourceCode default">coroutine_handle&lt;&gt;::elided</code>.</li>
<li>Raising the problem that indirect calls couldn’t be elided.</li>
<li>Discussing the relation of object and coroutine frame.</li>
<li>Add a conclusion section.</li>
</ul>
<h1 data-number="3" id="background-and-motivation"><span class="header-section-number">3</span> Background and Motivation<a href="#background-and-motivation" class="self-link"></a></h1>
<p>A coroutine needs space to store information (coroutine status) when it suspends. Generally, a coroutine would use <code class="sourceCode default">promise_type::operator new</code> (if existing) or <code class="sourceCode default">::operator new</code> to allocate coroutine status. However, it is expensive to allocate memory dynamically. Even if we could use user-provided allocators, it is not easy (or very hard) to implement a safe and effective allocator. And dynamic allocation wouldn’t be cheaper than static allocation after all.</p>
<p>To mitigate the expenses, Richard Smith and Gor Nishanov designed <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0981r0.html">HALO</a> (which is called <code class="sourceCode default">coroutine elision</code> nowadays) to allocate coroutine on the stack when the compiler finds it is safe to do so. There is a corresponding optimization called <code class="sourceCode default">CoroElide</code> in Clang/LLVM. But it is not mentioned in the C++ specification. The document only says:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb1-1"><a href="#cb1-1"></a>[dcl.fct.def.coroutine]p9</span>
<span id="cb1-2"><a href="#cb1-2"></a>An implementation **may** need to allocate additional storage for a coroutine.</span></code></pre></div>
<p>So it leaves the space for the compiler to do optimization. But the programmers couldn’t find a well-defined method to control coroutine elision. Also the <code class="sourceCode default">coroutine elision</code> is not guaranteed to happen just like other compiler optimizations. An elidable source code may become unelidable in a newer version of the same compiler. A recent example could be found at: https://github.com/llvm/llvm-project/issues/56262.</p>
<p>And <code class="sourceCode default">coroutine elision</code> doesn’t like other compiler optimization which is transparent to programmers. Programmers have a strong feeling about dynamic allocation. An example could be found at: https://stackoverflow.com/questions/71581531/halo-support-on-recent-compilers-for-c-coroutines/72770957.</p>
<p>And the coroutine elision optimization in the compiler side is not satisfying, too. Although it works fine for simple cases, it couldn’t optimize complex(asynchronous) cases. And it is hopeless to solve it in the compiler side in the recent future. However, the programmers are capable of analyzing the safety here. So the programmers pay for what they could have avoided. I think it is a violation of the zero abstract rule.</p>
<p>Except for the general need to avoid dynamic allocation to speed up. I heard from Niall Douglas that it is needed to disable <code class="sourceCode default">coroutine elision</code> in an embedded system. In the resource-limited system, we could use <code class="sourceCode default">promise_type::operator new</code> to allocate space from a big static-allocated bytes array. In this case, it would be annoying if the programmer found that his coroutine get elided surprisingly. And the programmer has nothing to do to disable it.</p>
<p>For the above reasons, we think it is necessary to provide a mechanism to control the coroutine elision in the specification.</p>
<h1 data-number="4" id="proposed-solutions"><span class="header-section-number">4</span> Proposed Solutions<a href="#proposed-solutions" class="self-link"></a></h1>
<h2 data-number="4.1" id="enum-class-coro_elision"><span class="header-section-number">4.1</span> enum class coro_elision<a href="#enum-class-coro_elision" class="self-link"></a></h2>
<p>To describe the requirement for eliding a coroutine. We need to introduce an enum class <code class="sourceCode default">coro_elision</code> to the standard library. The definition of <code class="sourceCode default">coro_elision</code> should be:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1"></a><span class="kw">enum</span> <span class="kw">class</span> coro_elision {</span>
<span id="cb1-2"><a href="#cb1-2"></a>    may,</span>
<span id="cb1-3"><a href="#cb1-3"></a>    always,</span>
<span id="cb1-4"><a href="#cb1-4"></a>    never</span>
<span id="cb1-5"><a href="#cb1-5"></a>};</span></code></pre></div>
<h2 data-number="4.2" id="terminology"><span class="header-section-number">4.2</span> Terminology<a href="#terminology" class="self-link"></a></h2>
<h3 data-number="4.2.1" id="coroutine-call"><span class="header-section-number">4.2.1</span> Coroutine Call<a href="#coroutine-call" class="self-link"></a></h3>
<p>Let’s say a coroutine call is a direct call to a coroutine function with a reachable definition.</p>
<h2 data-number="4.3" id="informal-description-for-coroutine-elision"><span class="header-section-number">4.3</span> Informal Description for coroutine elision<a href="#informal-description-for-coroutine-elision" class="self-link"></a></h2>
<p>The coroutine elision is only meaningful for the coroutine calls. When the coroutine elision is performed on a coroutine call, the necessary coroutine state for that coroutine call would be a local variable in the enclosing block.</p>
<p>(This implies that we could skip to allocate and deallocate the additional storage for the coroutine.)</p>
<h2 data-number="4.4" id="informal-description-for-controlling-coroutine-elision"><span class="header-section-number">4.4</span> Informal Description for controlling coroutine elision<a href="#informal-description-for-controlling-coroutine-elision" class="self-link"></a></h2>
<p>For every coroutine call <code class="sourceCode default">C(P0, ..., Pn)</code> with promise type <code class="sourceCode default">promise_type</code>, the compiler would try to evaluate <code class="sourceCode default">promise_type::must_elide(P0, ..., Pn)</code> in the constexpr time. If the evaluated result is <code class="sourceCode default">std::coro_elision::always</code>, the coroutine elision for this call is guaranteed to perform. And if the evaluated result is <code class="sourceCode default">std::coro_elision::never</code>, the coroutine elision for this call is guaranteed to not perform.</p>
<p>Otherwise (the result is <code class="sourceCode default">std::coro_elision::may</code> or <code class="sourceCode default">promise_type::must_elide(P0, ..., Pn)</code> is not a valid constexpr expression), the compiler would try to evaluate <code class="sourceCode default">promise_type::must_elide()</code> in the constexpr time. If the evaluated result is <code class="sourceCode default">std::coro_elision::always</code> or <code class="sourceCode default">std::coro_elision::never</code>, the coroutine elision for this call is guaranteed to perform or not perform according to the corresponding result. Otherwise, the behavior is implementation-defined.</p>
<h1 data-number="5" id="examples"><span class="header-section-number">5</span> Examples<a href="#examples" class="self-link"></a></h1>
<p>A set of examples could be found at: https://github.com/ChuanqiXu9/llvm-project/tree/P2477/P2477_Examples</p>
<p>Here we share a usage to control the coroutine elision at the callsite by the default argument.</p>
<h2 data-number="5.1" id="controlling-coroutine-elision-by-the-default-argument"><span class="header-section-number">5.1</span> Controlling coroutine elision by the default argument<a href="#controlling-coroutine-elision-by-the-default-argument" class="self-link"></a></h2>
<p>The following example implements a strategy to allow us to control the coroutine elision at the callsite by the default argument.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">struct</span> TaskPromiseAlternative : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb2-2"><a href="#cb2-2"></a>    <span class="co">// Return the last argument if its type is std::coro_elision.</span></span>
<span id="cb2-3"><a href="#cb2-3"></a>    <span class="co">// Return std::coro_elision::may otherwise.</span></span>
<span id="cb2-4"><a href="#cb2-4"></a>    <span class="kw">template</span> &lt;<span class="kw">typename</span>... Args&gt;</span>
<span id="cb2-5"><a href="#cb2-5"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="bu">std::</span>coro_elision must_elide(Args&amp;&amp;... args) {</span>
<span id="cb2-6"><a href="#cb2-6"></a>        <span class="kw">using</span> ArgsTuple = <span class="bu">std::</span>tuple&lt;Args...&gt;;</span>
<span id="cb2-7"><a href="#cb2-7"></a>        <span class="kw">constexpr</span> <span class="bu">std::</span>size_t ArgsSize = <span class="bu">std::</span>tuple_size_v&lt;ArgsTuple&gt;;</span>
<span id="cb2-8"><a href="#cb2-8"></a>        <span class="cf">if</span> <span class="kw">constexpr</span> (ArgsSize &gt; <span class="dv">0</span>) {</span>
<span id="cb2-9"><a href="#cb2-9"></a>            <span class="kw">using</span> LastArgType = <span class="bu">std::</span>tuple_element_t&lt;ArgsSize - <span class="dv">1</span>, ArgsTuple&gt;;</span>
<span id="cb2-10"><a href="#cb2-10"></a>            <span class="cf">if</span> <span class="kw">constexpr</span> (<span class="bu">std::</span>is_convertible_v&lt;LastArgType, <span class="bu">std::</span>coro_elision&gt;)</span>
<span id="cb2-11"><a href="#cb2-11"></a>                <span class="cf">return</span> <span class="bu">std::</span>get&lt;ArgsSize - <span class="dv">1</span>&gt;(<span class="bu">std::</span>forward_as_tuple(args...));</span>
<span id="cb2-12"><a href="#cb2-12"></a>        }</span>
<span id="cb2-13"><a href="#cb2-13"></a></span>
<span id="cb2-14"><a href="#cb2-14"></a>        <span class="cf">return</span> <span class="bu">std::</span>coro_elision<span class="bu">::</span>may;</span>
<span id="cb2-15"><a href="#cb2-15"></a>    }</span>
<span id="cb2-16"><a href="#cb2-16"></a>};</span>
<span id="cb2-17"><a href="#cb2-17"></a></span>
<span id="cb2-18"><a href="#cb2-18"></a><span class="kw">using</span> ControllableTask = Task&lt;TaskPromiseAlternative&gt;;</span></code></pre></div>
<p>The usage looks like:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1"></a>ControllableTask an_elide_controllable_coroutine(<span class="bu">std::</span>coro_elision elision_state = <span class="bu">std::</span>coro_elision<span class="bu">::</span>may) {</span>
<span id="cb3-2"><a href="#cb3-2"></a>    <span class="kw">co_return</span> <span class="dv">43</span>;</span>
<span id="cb3-3"><a href="#cb3-3"></a>}</span>
<span id="cb3-4"><a href="#cb3-4"></a></span>
<span id="cb3-5"><a href="#cb3-5"></a>NormalTask example(<span class="dt">int</span> count) {</span>
<span id="cb3-6"><a href="#cb3-6"></a>  <span class="dt">int</span> sum = <span class="dv">0</span>;</span>
<span id="cb3-7"><a href="#cb3-7"></a>  <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i &lt; count; ++i) {</span>
<span id="cb3-8"><a href="#cb3-8"></a>    <span class="kw">auto</span> t = an_elide_controllable_coroutine(<span class="bu">std::</span>coro_elision<span class="bu">::</span>always);</span>
<span id="cb3-9"><a href="#cb3-9"></a>    sum += <span class="kw">co_await</span> <span class="bu">std::</span>move(t);</span>
<span id="cb3-10"><a href="#cb3-10"></a>  }</span>
<span id="cb3-11"><a href="#cb3-11"></a></span>
<span id="cb3-12"><a href="#cb3-12"></a>  <span class="kw">co_return</span> sum;</span>
<span id="cb3-13"><a href="#cb3-13"></a>}</span>
<span id="cb3-14"><a href="#cb3-14"></a></span>
<span id="cb3-15"><a href="#cb3-15"></a>NormalTask example2(<span class="dt">int</span> count) {</span>
<span id="cb3-16"><a href="#cb3-16"></a>  <span class="bu">std::</span>vector&lt;TaskBase *&gt; tasks;</span>
<span id="cb3-17"><a href="#cb3-17"></a>  <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i &lt; count; ++i)</span>
<span id="cb3-18"><a href="#cb3-18"></a>    <span class="co">// Or</span></span>
<span id="cb3-19"><a href="#cb3-19"></a>    <span class="co">//    tasks.push_back(an_elide_controllable_coroutine());</span></span>
<span id="cb3-20"><a href="#cb3-20"></a>    tasks.push_back(an_elide_controllable_coroutine(<span class="bu">std::</span>coro_elision<span class="bu">::</span>never));</span>
<span id="cb3-21"><a href="#cb3-21"></a></span>
<span id="cb3-22"><a href="#cb3-22"></a>  <span class="kw">co_await</span> whenAll(tasks);</span>
<span id="cb3-23"><a href="#cb3-23"></a>  <span class="co">// ...</span></span>
<span id="cb3-24"><a href="#cb3-24"></a>}</span></code></pre></div>
<p>For example1, the lifetime of the coroutines of <code class="sourceCode default">an_elide_controllable_coroutine</code> is limited to this loop, so it is safe to elide them. But it is not true for example2, since we’re going to resume the coroutines after the loop ended.</p>
<h1 data-number="6" id="a-case-study-for-asynchronous-coroutines"><span class="header-section-number">6</span> A case study for asynchronous coroutines<a href="#a-case-study-for-asynchronous-coroutines" class="self-link"></a></h1>
<p>In this section, we would discuss 2 things:</p>
<ol type="1">
<li>Why the compiler can’t handle complex (asynchronous in this context) situations</li>
<li>Why the programmers are capable of analyzing it.</li>
</ol>
<p>The source code for the case study could be found at: https://github.com/ChuanqiXu9/llvm-project/tree/P2477/P2477_Examples/AsyncCase</p>
<p>The example implements a simple executor based on a simple thread pool and a simple task. When we <code class="sourceCode default">co_await</code> the simple task, the job to resume the awaited task is submitted to the simple executor.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">template</span> &lt;<span class="kw">typename</span> U&gt;</span>
<span id="cb4-2"><a href="#cb4-2"></a><span class="dt">void</span> await_suspend(<span class="bu">std::</span>coroutine_handle&lt;U&gt; continuation) <span class="kw">noexcept</span> {</span>
<span id="cb4-3"><a href="#cb4-3"></a>    handle.promise().continuation = continuation;</span>
<span id="cb4-4"><a href="#cb4-4"></a>    <span class="bu">std::</span>function&lt;<span class="dt">void</span>()&gt; func = [h = handle]() <span class="at">mutable</span> {</span>
<span id="cb4-5"><a href="#cb4-5"></a>        h.resume();</span>
<span id="cb4-6"><a href="#cb4-6"></a>    };</span>
<span id="cb4-7"><a href="#cb4-7"></a>    <span class="ot">assert</span>(handle.promise()._executor);</span>
<span id="cb4-8"><a href="#cb4-8"></a>    handle.promise()._executor-&gt;schedule(func);</span>
<span id="cb4-9"><a href="#cb4-9"></a>}</span></code></pre></div>
<p>Then the executor would push the job to a random thread job queue and then the awaited task is waiting to be resumed. Once the awaited task is completed, the awaited task would resume the suspended task in the final suspend point:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">struct</span> FinalAwaiter {</span>
<span id="cb5-2"><a href="#cb5-2"></a>    <span class="dt">bool</span> await_ready() <span class="at">const</span> <span class="kw">noexcept</span> { <span class="cf">return</span> <span class="kw">false</span>; }</span>
<span id="cb5-3"><a href="#cb5-3"></a>    <span class="kw">template</span> &lt;<span class="kw">typename</span> PromiseType&gt;</span>
<span id="cb5-4"><a href="#cb5-4"></a>    <span class="kw">auto</span> await_suspend(<span class="bu">std::</span>coroutine_handle&lt;PromiseType&gt; h) <span class="kw">noexcept</span> {</span>
<span id="cb5-5"><a href="#cb5-5"></a>        <span class="cf">return</span> h.promise().continuation;</span>
<span id="cb5-6"><a href="#cb5-6"></a>    }</span>
<span id="cb5-7"><a href="#cb5-7"></a>    <span class="dt">void</span> await_resume() <span class="kw">noexcept</span> {}</span>
<span id="cb5-8"><a href="#cb5-8"></a>};</span>
<span id="cb5-9"><a href="#cb5-9"></a>FinalAwaiter final_suspend() <span class="kw">noexcept</span> { <span class="cf">return</span> {}; }</span></code></pre></div>
<p>When the suspended task is resumed by the awaited task, the suspended task is going to destroy the awaited task:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1"></a><span class="kw">auto</span> await_resume() <span class="kw">noexcept</span> {</span>
<span id="cb6-2"><a href="#cb6-2"></a>    <span class="kw">auto</span> value =  handle.promise()._value;</span>
<span id="cb6-3"><a href="#cb6-3"></a>    handle.destroy();</span>
<span id="cb6-4"><a href="#cb6-4"></a>    <span class="cf">return</span> value;</span>
<span id="cb6-5"><a href="#cb6-5"></a>}</span></code></pre></div>
<h2 data-number="6.1" id="compiler-side"><span class="header-section-number">6.1</span> Compiler Side<a href="#compiler-side" class="self-link"></a></h2>
<p>(The readers could skip this part if not interested)</p>
<p>When the compiler wants to perform the coroutine elision for a coroutine call, the most important thing it ensures if it is safe to do so.</p>
<p>The condition to ensure the safety is:</p>
<blockquote>
<p>Every path from the creation point of the coroutine to the exit point of the enclosing scope would meet the destroy point of the coroutine.</p>
</blockquote>
<p>So when we <code class="sourceCode default">co_await</code> a asynchronous task,</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">auto</span> task = task_creation();</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">co_await</span> task;</span></code></pre></div>
<p>the generated code would look like:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a>task = task_creation();</span>
<span id="cb8-2"><a href="#cb8-2"></a>task.promise().continuation = current_task;</span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="bu">std::</span>function&lt;<span class="dt">void</span>()&gt; func = [h = task]() <span class="at">mutable</span> {</span>
<span id="cb8-4"><a href="#cb8-4"></a>    h.resume();</span>
<span id="cb8-5"><a href="#cb8-5"></a>};</span>
<span id="cb8-6"><a href="#cb8-6"></a>task.promise()._executor-&gt;schedule(func);</span>
<span id="cb8-7"><a href="#cb8-7"></a>&lt;suspend-points&gt;</span>
<span id="cb8-8"><a href="#cb8-8"></a>task.destroy();</span></code></pre></div>
<p>Although it <strong>looks like</strong> the creation point is covered by the destroy point, it is not true. The compiler has no idea that <code class="sourceCode default">current_task</code> would be resumed after all unless the implementation of <code class="sourceCode default">schedule</code> is too easy, which wouldn’t be true in real cases. So the <code class="sourceCode default">&lt;suspend-points&gt;</code> here is an exit point indeed.</p>
<p>Also, the compiler couldn’t infer the information that the <code class="sourceCode default">current_task</code> wouldn’t be resumed before the <code class="sourceCode default">task</code> completes. From the perspective of the compiler, it could only find the coroutine frame of the <code class="sourceCode default">current_task</code> is escaped and the compiler would only assume the <code class="sourceCode default">current_task</code> might be resumed or destroyed at any point.</p>
<p>So from the perspective of the compiler, it couldn’t be sure if the <code class="sourceCode default">task</code> would be destroyed finally nor if the <code class="sourceCode default">current_task</code> would be destroyed before the <code class="sourceCode default">task</code>. So the compiler couldn’t do anything.</p>
<p>Note that things may be more complicated for the compiler. e.g., the coroutine type doesn’t contain std::coroutine_handle only or there are more operations.</p>
<h2 data-number="6.2" id="programmers-side"><span class="header-section-number">6.2</span> Programmers Side<a href="#programmers-side" class="self-link"></a></h2>
<p>From the programmers’ side, we could know the fact easily:</p>
<blockquote>
<p>The <code class="sourceCode default">current_task</code> wouldn’t be resumed before <code class="sourceCode default">task</code> get completed.</p>
</blockquote>
<p>Given this fact, we know it is safe to make the coroutine state of <code class="sourceCode default">task</code> to be a local variable of the enclosing block. Since the local variable wouldn’t be destructed before the <code class="sourceCode default">task</code> gets completed.</p>
<p>We could know the fact if:</p>
<ol type="1">
<li>We’re the original author of the framework.</li>
<li>We heard the information from the author.</li>
<li>This is formally documented.</li>
<li>…</li>
</ol>
<p>After all, it is easy for the programmers to the lifetime model for a specific coroutine type and a specific scheduling framework from my experiences.</p>
<p>The point of the section is that the programmers could understand the higher-level semantics more than the compiler so that we could make better decisions.</p>
<h1 data-number="7" id="limitations"><span class="header-section-number">7</span> Limitations<a href="#limitations" class="self-link"></a></h1>
<p>The limitation of the proposal is that it couldn’t handle the indirect call or the function definition is unreachable. This is a necessary condition. Otherwise, the compiler couldn’t know if the function is a coroutine or not. The limitation should be unsolvable from my perspective.</p>
<p>My idea is to make it an undefined behavior if the evaluated result of <code class="sourceCode default">must_elide</code> is <code class="sourceCode default">coro_elision::always</code> but the definition is not reachable and the call is indirect.</p>
<p>The reason for the decision is that the compiler is not capable to forbid these cases since the compiler wouldn’t know if the called function is a coroutine or not. I know it is possible to forbid something and say “no diagnostic is required”. But I am not sure if it is too strict.</p>
<p>(The indirect call contains virtual functions and function pointers. The unreachable function definition refers to the coroutine functions which is defined in other TU. But I feel the unreachable function definition is much more precise.)</p>
<h1 data-number="8" id="concerns"><span class="header-section-number">8</span> Concerns<a href="#concerns" class="self-link"></a></h1>
<p>The main concern I received is the potential misuse of the feature.</p>
<p>An example may be:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1"></a>Task&lt;<span class="dt">int</span>&gt; example2(<span class="dt">int</span> task_count) {</span>
<span id="cb9-2"><a href="#cb9-2"></a>  <span class="bu">std::</span>vector&lt;Task&lt;<span class="dt">int</span>&gt;&gt; tasks;</span>
<span id="cb9-3"><a href="#cb9-3"></a>  <span class="cf">for</span> (<span class="dt">int</span> i = <span class="dv">0</span>; i &lt; task_count; i++)</span>
<span id="cb9-4"><a href="#cb9-4"></a>    <span class="co">// The coroutine state of coro_task() becomes a local</span></span>
<span id="cb9-5"><a href="#cb9-5"></a>    <span class="co">// variable of the loop.</span></span>
<span id="cb9-6"><a href="#cb9-6"></a>    tasks.emplace_back(coro_task(<span class="bu">std::</span>coro_elision<span class="bu">::</span>always));</span>
<span id="cb9-7"><a href="#cb9-7"></a></span>
<span id="cb9-8"><a href="#cb9-8"></a>  <span class="co">// The semantics of whenAll is that it would</span></span>
<span id="cb9-9"><a href="#cb9-9"></a>  <span class="co">// complete after all the tasks are completed.</span></span>
<span id="cb9-10"><a href="#cb9-10"></a>  <span class="co">//</span></span>
<span id="cb9-11"><a href="#cb9-11"></a>  <span class="co">// The tasks would contain to dangling pointers only.</span></span>
<span id="cb9-12"><a href="#cb9-12"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> whenAll(<span class="bu">std::</span>move(tasks));</span>
<span id="cb9-13"><a href="#cb9-13"></a>}</span></code></pre></div>
<p>For the above example, <code class="sourceCode default">tasks</code> would contain a series of <code class="sourceCode default">Task&lt;int&gt;</code>, which contains a pointer to the corresponding coroutine frame. But if we force the coroutine elision, <code class="sourceCode default">tasks</code> would contain a series of dangling pointers. The behavior is undefined.</p>
<p>And my response is: this is not a silver bullet and it has cost. It requires the users to understand the lifetime model of that coroutine they are using. And if the users don’t think they understand it, it is OK to not use it.</p>
<p>I think the key point here is whether or not we’re going to bring coroutine elision for users. If the answer is yes, no matter what the solution is, we would meet the same problem after all. This is the price we need to pay.</p>
<p>But I don’t feel the price is too high. On the one hand, the lifetime model is not so hard to understand. On the other hand, it is OK to not use it.</p>
<p>A similar example I am wondering about is the <a href="https://eel.is/c++draft/atomics.order">memory order and consistency</a>. I know many C++ programmers don’t understand it clearly and it is much more complex than the lifetime model for specific coroutines.</p>
<h1 data-number="9" id="wording"><span class="header-section-number">9</span> Wording<a href="#wording" class="self-link"></a></h1>
<p>No formal wording yet. Let’s go for it if we could get some consensus.</p>
<h1 data-number="10" id="implementation"><span class="header-section-number">10</span> Implementation<a href="#implementation" class="self-link"></a></h1>
<p>An implementation for clang could be found at: https://github.com/ChuanqiXu9/llvm-project/tree/P2477</p>
<p>If anyone wants a try, remember to add the <code class="sourceCode default">O1/2/3</code> option since this is not implemented in O0.</p>
<p>For other compilers, I think it might not be too hard to implement this. Since the hardest part to implement coroutine elision is the analysis part. But the proposal offloads the analysis burden to the users. So I think it is implementable.</p>
<h1 data-number="11" id="summary"><span class="header-section-number">11</span> Summary<a href="#summary" class="self-link"></a></h1>
<p>The paper proposes to introduce coroutine elision to the standard and provide a mechanism for the users to control coroutine elision. In this way, the users are capable to avoid some unnecessary dynamic allocations. The method wouldn’t bring any breaking change by design and it is relatively easy to implement. The main limitation is that it couldn’t handle indirect calls and unreachable function definitions. The main concern is the potential UB if the users don’t use it properly. Both the limitation and the concern are unavoidable as long as we want to introduce coroutine elision to the standard.</p>
<h1 data-number="12" id="qa"><span class="header-section-number">12</span> Q&amp;A<a href="#qa" class="self-link"></a></h1>
<h2 data-number="12.1" id="whats-the-relationship-between-coroutine-elision-and-copy-elision"><span class="header-section-number">12.1</span> What’s the relationship between Coroutine elision and Copy Elision?<a href="#whats-the-relationship-between-coroutine-elision-and-copy-elision" class="self-link"></a></h2>
<p>There is no relationship between copy elision and coroutine elision. But it might be confusing indeed. Here is an example:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1"></a><span class="kw">template</span>&lt;<span class="kw">typename</span>... Args&gt;</span>
<span id="cb10-2"><a href="#cb10-2"></a>Task&lt;<span class="dt">void</span>&gt; foo_impl(Args... args) { <span class="kw">co_await</span> whatever(); ... }</span>
<span id="cb10-3"><a href="#cb10-3"></a></span>
<span id="cb10-4"><a href="#cb10-4"></a><span class="kw">template</span>&lt;<span class="kw">typename</span>... Args&gt;</span>
<span id="cb10-5"><a href="#cb10-5"></a>Task&lt;<span class="dt">void</span>&gt; foo(<span class="bu">std::</span>tuple&lt;Args&gt; t) {</span>
<span id="cb10-6"><a href="#cb10-6"></a>  <span class="kw">co_await</span> <span class="bu">std::</span>apply([](Args&amp;... args) { <span class="cf">return</span> foo_impl(args...); }, t);</span>
<span id="cb10-7"><a href="#cb10-7"></a>}</span></code></pre></div>
<p>Here the return object of <code class="sourceCode default">foo_impl</code> is guaranteed to be copy elided. But it doesn’t affect coroutine elision. When coroutine elision performs for the call to <code class="sourceCode default">foo_impl()</code>, the coroutine state becomes a local variable of the enclosing block, which would be destructed later. So we shouldn’t force coroutine elision on <code class="sourceCode default">foo_impl()</code>.</p>
<h2 data-number="12.2" id="is-it-good-to-connect-coroutine-elision-and-copy-elision"><span class="header-section-number">12.2</span> Is it good to connect coroutine elision and copy elision?<a href="#is-it-good-to-connect-coroutine-elision-and-copy-elision" class="self-link"></a></h2>
<p>Probably not. The reason why people would be confused by the above example at the first sight is that the implementation of <code class="sourceCode default">Task</code> would combine it with the coroutine frame tightly. But this is semantic of <code class="sourceCode default">Task</code>, a specific coroutine type. It is not semantic of coroutine. We have no guarantee between the return object of the coroutine and the coroutine state at the language level.</p>
<h2 data-number="12.3" id="would-the-compiler-now-perform-coroutine-elision-stably-for-synchronous-cases"><span class="header-section-number">12.3</span> Would the compiler now perform coroutine elision stably for synchronous cases?<a href="#would-the-compiler-now-perform-coroutine-elision-stably-for-synchronous-cases" class="self-link"></a></h2>
<p>No. It still depends on the complexity of the code pattern.</p>
<h2 data-number="12.4" id="what-will-happen-if-we-perform-coroutine-elision-inside-another-coroutine"><span class="header-section-number">12.4</span> What will happen if we perform coroutine elision inside another coroutine?<a href="#what-will-happen-if-we-perform-coroutine-elision-inside-another-coroutine" class="self-link"></a></h2>
<div class="sourceCode" id="cb11"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1"></a>Task foo(<span class="bu">std::</span>coro_elision kind) {</span>
<span id="cb11-2"><a href="#cb11-2"></a>    <span class="kw">co_return</span> <span class="dv">43</span>;</span>
<span id="cb11-3"><a href="#cb11-3"></a>}</span>
<span id="cb11-4"><a href="#cb11-4"></a>Task bar() {</span>
<span id="cb11-5"><a href="#cb11-5"></a>    <span class="kw">co_return</span> <span class="kw">co_await</span> foo(<span class="bu">std::</span>coro_elision<span class="bu">::</span>always);</span>
<span id="cb11-6"><a href="#cb11-6"></a>}</span></code></pre></div>
<p>For the above example, the coroutine state of the call to <code class="sourceCode default">foo()</code> would become a local variable of <code class="sourceCode default">bar()</code>. Since <code class="sourceCode default">bar()</code> is a coroutine too, it has its coroutine state. Depends on the result of compiler optimization, the local variable may live in the coroutine state of <code class="sourceCode default">bar()</code>. Assume the variable lives in the coroutine state of <code class="sourceCode default">bar()</code>. The coroutine state of <code class="sourceCode default">bar()</code> might look like:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">struct</span> coro_state_of_bar {</span>
<span id="cb12-2"><a href="#cb12-2"></a>  <span class="dt">void</span> (*resume_func)(coro_state_of_bar*);</span>
<span id="cb12-3"><a href="#cb12-3"></a>  <span class="dt">void</span> (*destroy_func)(coro_state_of_bar*);</span>
<span id="cb12-4"><a href="#cb12-4"></a>  <span class="dt">promise_type</span>;</span>
<span id="cb12-5"><a href="#cb12-5"></a>  <span class="kw">struct</span> coro_state_of_foo {</span>
<span id="cb12-6"><a href="#cb12-6"></a>      <span class="dt">void</span> (*resume_func)(coro_state_of_foo*);</span>
<span id="cb12-7"><a href="#cb12-7"></a>      <span class="dt">void</span> (*destroy_func)(coro_state_of_foo*);</span>
<span id="cb12-8"><a href="#cb12-8"></a>      <span class="dt">promise_type</span>;</span>
<span id="cb12-9"><a href="#cb12-9"></a>      <span class="co">// Other information needed.</span></span>
<span id="cb12-10"><a href="#cb12-10"></a>  };</span>
<span id="cb12-11"><a href="#cb12-11"></a>  coro_state_of_foo state_of_foo;</span>
<span id="cb12-12"><a href="#cb12-12"></a>  <span class="co">// Other information needed.</span></span>
<span id="cb12-13"><a href="#cb12-13"></a>};</span></code></pre></div>
<p>Given a call to bar() is not elided, the required size for the allocation would be <code class="sourceCode default">SizeOfOriginalBarFrame + SizeOfFooFrame</code>. So in this case, the times of allocating get decreased but the required size of one allocation goes higher. And the peak used memory keep the same with the unelided case.</p>
<h2 data-number="12.5" id="is-it-possible-that-the-total-used-memory-becomes-lower-after-we-elide-more-coroutines"><span class="header-section-number">12.5</span> Is it possible that the total used memory becomes lower after we elide more coroutines<a href="#is-it-possible-that-the-total-used-memory-becomes-lower-after-we-elide-more-coroutines" class="self-link"></a></h2>
<p>This is a trivial case. Presented for symmetry.</p>
<h2 data-number="12.6" id="is-it-possible-that-the-total-used-memory-becomes-higher-after-we-elide-more-coroutines"><span class="header-section-number">12.6</span> Is it possible that the total used memory becomes higher after we elide more coroutines<a href="#is-it-possible-that-the-total-used-memory-becomes-higher-after-we-elide-more-coroutines" class="self-link"></a></h2>
<p>This is possible too. See the following example:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1"></a>Task foo(<span class="bu">std::</span>coro_elision kind) {</span>
<span id="cb13-2"><a href="#cb13-2"></a>    <span class="kw">co_return</span> <span class="dv">43</span>;</span>
<span id="cb13-3"><a href="#cb13-3"></a>}</span>
<span id="cb13-4"><a href="#cb13-4"></a>Task bar() {</span>
<span id="cb13-5"><a href="#cb13-5"></a>    <span class="kw">auto</span> r1 = <span class="kw">co_await</span> foo(<span class="bu">std::</span>coro_elision<span class="bu">::</span>always);</span>
<span id="cb13-6"><a href="#cb13-6"></a>    <span class="kw">auto</span> r2 = <span class="kw">co_await</span> foo(<span class="bu">std::</span>coro_elision<span class="bu">::</span>always);</span>
<span id="cb13-7"><a href="#cb13-7"></a>    <span class="kw">co_return</span> (r1 + r2);</span>
<span id="cb13-8"><a href="#cb13-8"></a>}</span></code></pre></div>
<p>The peak used memory size would be <code class="sourceCode default">SizeOfOriginalBarFrame + 2 * SizeOfOriginalFooFrame</code>. But for the never elide case:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb14-1"><a href="#cb14-1"></a>Task foo(<span class="bu">std::</span>coro_elision kind) {</span>
<span id="cb14-2"><a href="#cb14-2"></a>    <span class="kw">co_return</span> <span class="dv">43</span>;</span>
<span id="cb14-3"><a href="#cb14-3"></a>}</span>
<span id="cb14-4"><a href="#cb14-4"></a>Task bar() {</span>
<span id="cb14-5"><a href="#cb14-5"></a>    <span class="kw">auto</span> r1 = <span class="kw">co_await</span> foo(<span class="bu">std::</span>coro_elision<span class="bu">::</span>never);</span>
<span id="cb14-6"><a href="#cb14-6"></a>    <span class="kw">auto</span> r2 = <span class="kw">co_await</span> foo(<span class="bu">std::</span>coro_elision<span class="bu">::</span>never);</span>
<span id="cb14-7"><a href="#cb14-7"></a>    <span class="kw">co_return</span> (r1 + r2);</span>
<span id="cb14-8"><a href="#cb14-8"></a>}</span></code></pre></div>
<p>The peak used memory size would be <code class="sourceCode default">SizeOfOriginalBarFrame + SizeOfOriginalFooFrame</code> since the second call to <code class="sourceCode default">foo()</code> wouldn’t happen if the first frame is not destroyed.</p>
<p>Note1: The readers who are familiar with allocators may want to argue that the allocator wouldn’t clean the memory immediately when <code class="sourceCode default">free()</code> is called. So the above example is not accurate. Yeah, it is true. So we might not be able to observe this when the scale is too small.</p>
<p>Note2: Maybe readers want to ask if the compiler can optimize the above example to make the frame of foo share one position. For the above case, I think it is possible ideally. But there will always be cases the compiler couldn’t cover.</p>
<h2 data-number="12.7" id="would-the-allocation-counts-go-higher-if-we-elide-more-coroutines"><span class="header-section-number">12.7</span> Would the allocation counts go higher if we elide more coroutines<a href="#would-the-allocation-counts-go-higher-if-we-elide-more-coroutines" class="self-link"></a></h2>
<p>It shouldn’t be true.</p>
<h1 data-number="13" id="acknowledgments"><span class="header-section-number">13</span> Acknowledgments<a href="#acknowledgments" class="self-link"></a></h1>
<p>Many thanks to Lewis Baker, Mathias Stearn, and Gašper Ažman for reviewing the series of papers.</p>
</div>
</div>
</body>
</html>
