<!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="2021-11-14" />
  <title>Allow programmer to control and detect 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;
}
@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 programmer to control and detect coroutine elision</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2477R2</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2021-11-14</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, Library 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="#r2"><span class="toc-section-number">2.1</span> R2<span></span></a></li>
<li><a href="#r1"><span class="toc-section-number">2.2</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="#terminology-and-some-background"><span class="toc-section-number">4</span> Terminology and some background<span></span></a>
<ul>
<li><a href="#halo-and-coroutine-elision"><span class="toc-section-number">4.1</span> HALO and Coroutine elision<span></span></a></li>
<li><a href="#constexpr-time-and-compile-time"><span class="toc-section-number">4.2</span> constexpr time and compile time<span></span></a></li>
<li><a href="#coroutine-status-and-corotuine-frame"><span class="toc-section-number">4.3</span> Coroutine status and Corotuine frame<span></span></a></li>
<li><a href="#coroutine-and-coroutine-instance"><span class="toc-section-number">4.4</span> Coroutine and coroutine instance<span></span></a></li>
<li><a href="#ramp-function"><span class="toc-section-number">4.5</span> Ramp function<span></span></a></li>
</ul></li>
<li><a href="#the-storage-lifetime-of-elided-coroutine-frame"><span class="toc-section-number">5</span> The storage lifetime of elided coroutine frame<span></span></a></li>
<li><a href="#design"><span class="toc-section-number">6</span> Design<span></span></a>
<ul>
<li><a href="#controlling-the-elision"><span class="toc-section-number">6.1</span> Controlling the elision<span></span></a>
<ul>
<li><a href="#enum-class-coro_elision"><span class="toc-section-number">6.1.1</span> enum class coro_elision<span></span></a></li>
<li><a href="#must_elide"><span class="toc-section-number">6.1.2</span> must_elide<span></span></a></li>
<li><a href="#necessity"><span class="toc-section-number">6.1.3</span> Necessity<span></span></a></li>
<li><a href="#risks"><span class="toc-section-number">6.1.4</span> Risks<span></span></a></li>
<li><a href="#design-ideas"><span class="toc-section-number">6.1.5</span> Design Ideas<span></span></a></li>
<li><a href="#why-in-promise_type"><span class="toc-section-number">6.1.6</span> Why in promise_type?<span></span></a></li>
<li><a href="#why-enum-class-coro_elision"><span class="toc-section-number">6.1.7</span> Why enum class coro_elision?<span></span></a></li>
</ul></li>
<li><a href="#detecting-the-elision"><span class="toc-section-number">6.2</span> Detecting the elision<span></span></a>
<ul>
<li><a href="#elided"><span class="toc-section-number">6.2.1</span> elided<span></span></a></li>
<li><a href="#support-both"><span class="toc-section-number">6.2.2</span> Support both<span></span></a></li>
<li><a href="#suppot-coroutine_handlepromisetypeelided-only"><span class="toc-section-number">6.2.3</span> Suppot coroutine_handle&lt;PromiseType&gt;::elided() only<span></span></a></li>
<li><a href="#giving-up-to-support-elided"><span class="toc-section-number">6.2.4</span> Giving up to support elided<span></span></a></li>
<li><a href="#summarize"><span class="toc-section-number">6.2.5</span> Summarize<span></span></a></li>
</ul></li>
</ul></li>
<li><a href="#examples"><span class="toc-section-number">7</span> Examples<span></span></a>
<ul>
<li><a href="#always-enabledisable-coroutine-elision"><span class="toc-section-number">7.1</span> Always Enable/Disable coroutine elision<span></span></a></li>
<li><a href="#no-guarantee-to-coroutine-elision"><span class="toc-section-number">7.2</span> No guarantee to coroutine elision<span></span></a></li>
<li><a href="#controlling-the-elision-at-callsite"><span class="toc-section-number">7.3</span> Controlling the elision at callsite<span></span></a></li>
</ul></li>
<li><a href="#limitations"><span class="toc-section-number">8</span> Limitations<span></span></a>
<ul>
<li><a href="#indirect-call"><span class="toc-section-number">8.1</span> Indirect Call<span></span></a></li>
<li><a href="#elision-cross-translation-unit"><span class="toc-section-number">8.2</span> Elision cross translation unit<span></span></a></li>
<li><a href="#use-out-of-the-scope-of-definition"><span class="toc-section-number">8.3</span> Use out of the scope of definition<span></span></a></li>
<li><a href="#summary"><span class="toc-section-number">8.4</span> Summary<span></span></a></li>
</ul></li>
<li><a href="#implementation"><span class="toc-section-number">9</span> Implementation<span></span></a>
<ul>
<li><a href="#current-implementation-issue"><span class="toc-section-number">9.1</span> Current Implementation issue<span></span></a></li>
<li><a href="#implementation-in-gcc"><span class="toc-section-number">9.2</span> Implementation in GCC<span></span></a></li>
</ul></li>
<li><a href="#qa-and-discussion"><span class="toc-section-number">10</span> Q&amp;A and Discussion<span></span></a>
<ul>
<li><a href="#how-does-the-compiler-guarantee-coroutine-elision-if-the-user-requires-it"><span class="toc-section-number">10.1</span> How does the compiler guarantee coroutine elision if the user requires it<span></span></a></li>
<li><a href="#is-elided-a-constexpr-function"><span class="toc-section-number">10.2</span> Is elided() a constexpr function?<span></span></a></li>
<li><a href="#does-the-semantics-of-guaranteed-copy-elision-matter"><span class="toc-section-number">10.3</span> Does the semantics of guaranteed copy-elision matter?<span></span></a></li>
<li><a href="#why-must_elide-must-be-a-static-constexpr-member-function"><span class="toc-section-number">10.4</span> Why <code class="sourceCode default">must_elide()</code> must be a static constexpr member function?<span></span></a></li>
</ul></li>
<li><a href="#conclusion"><span class="toc-section-number">11</span> Conclusion<span></span></a></li>
<li><a href="#acknowledgments"><span class="toc-section-number">12</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 an optimization in the compiler, programmers couldn’t control coroutine elision in a well-defined way still. Also there is not a workable method that a programmer could use to detect whether the coroutine elision happened or not. So I proposed two methods. One for controlling the coroutine elision. And one for detecting it. Both of them wouldn’t break any existing codes.</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="r2"><span class="header-section-number">2.1</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-conexpr function.</li>
</ul>
<h2 data-number="2.2" id="r1"><span class="header-section-number">2.2</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 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 cheap 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. And there is a corresponding optimization called <code class="sourceCode default">CoroElide</code> in Clang/LLVM. But there is no word in the C++ specification about <code class="sourceCode default">coroutine elision</code>. It 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 it doesn’t make any promise for programmers. Then programmers couldn’t find a well-defined method to control nor detect coroutine elision. And <code class="sourceCode default">coroutine elision</code> doesn’t like other compiler optimization which is tranparent to programmers. Programmers have a strong feeling about dynamic allocation. So I feel it is necessary to provide a mechanism to control and detect coroutine elision in the specification.</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 wold be annoying that the programmer found that his coroutine get elided surprisingly. And the programmer has nothing to do to disable it.</p>
<h1 data-number="4" id="terminology-and-some-background"><span class="header-section-number">4</span> Terminology and some background<a href="#terminology-and-some-background" class="self-link"></a></h1>
<p>This section aims at explaining the terminologies in this proposal to avoid misunderstanding. This aim is not to discuss the words used in the specification formally. That should be discussed in the wording stage. When terminology is introduced, some background more than definition would be provided to help people to get familiar with the context.</p>
<h2 data-number="4.1" id="halo-and-coroutine-elision"><span class="header-section-number">4.1</span> HALO and Coroutine elision<a href="#halo-and-coroutine-elision" class="self-link"></a></h2>
<p>The two terminologies refer to the same thing in this proposal. I found that people on the language side prefer to use the term <code class="sourceCode default">HALO</code>. And the people on the compiler side prefer to use the term <code class="sourceCode default">Corotuine elision</code>. I like the term <code class="sourceCode default">Coroutine elision</code> more personally. We could discuss what’s the preferred name if needed.</p>
<h2 data-number="4.2" id="constexpr-time-and-compile-time"><span class="header-section-number">4.2</span> constexpr time and compile time<a href="#constexpr-time-and-compile-time" class="self-link"></a></h2>
<p>Generally, these two terms refer to the same thing. But it’s not the case for coroutine. For example, the layout of coroutine status is built at compile time. But the programmer couldn’t see it. Informally, if a value is a constexpr-time constant, it could participate in the template/if-constexpr evaluation. And if a value is a compile-time constant, it needn’t be evaluated at runtime. So simply, a constexpr-time constant is a compile-time constant. But a compile-time constant may not be a constexpr-time constant.</p>
<h2 data-number="4.3" id="coroutine-status-and-corotuine-frame"><span class="header-section-number">4.3</span> Coroutine status and Corotuine frame<a href="#coroutine-status-and-corotuine-frame" class="self-link"></a></h2>
<p>Coroutine status is an abstract concept used in the language standard to refer to all the information the coroutine should keep. And the coroutine frame refers to the data structure generated by the compiler to keep the information needed. So the coroutine status is more abstract and the coroutine frame is more concret. So that we could think coroutine frame is a derived class of coroutine status : ).</p>
<h2 data-number="4.4" id="coroutine-and-coroutine-instance"><span class="header-section-number">4.4</span> Coroutine and coroutine instance<a href="#coroutine-and-coroutine-instance" class="self-link"></a></h2>
<p>A coroutine is a function which is suspendable. Althought we would usually say <code class="sourceCode default">a coroutine function</code> in practice, <code class="sourceCode default">a coroutine function</code> should be the same with <code class="sourceCode default">a coroutine</code>. There is no ambiguity since there is no coroutine class nor coroutine variables. When we say <code class="sourceCode default">a coroutine</code>, we should mean <code class="sourceCode default">a corotuine function</code> all the time. A coroutine instance is the alias to coroutine status. The relationship is pretty like <code class="sourceCode default">class</code> and <code class="sourceCode default">class Instance</code>. For example:</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">class</span> X{};</span>
<span id="cb1-2"><a href="#cb1-2"></a>X x;</span></code></pre></div>
<p>Here the <code class="sourceCode default">X</code> is a <code class="sourceCode default">class</code> and <code class="sourceCode default">x</code> is a <code class="sourceCode default">class instance</code>. Further more,</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a>Task CoroutineTask() {}</span>
<span id="cb2-2"><a href="#cb2-2"></a><span class="dt">void</span> func() {</span>
<span id="cb2-3"><a href="#cb2-3"></a>    <span class="kw">auto</span> T = CoroutineTask();</span>
<span id="cb2-4"><a href="#cb2-4"></a>}</span></code></pre></div>
<p>(The definition of <code class="sourceCode default">Task</code> is omitted here due its lengthy. People who are not familiar with this could visit: <a href="https://github.com/facebook/folly/blob/main/folly/experimental/coro/Task.h">Task</a>)</p>
<p>Here <code class="sourceCode default">CoroutineTask()</code> is a coroutine and it would generate a coroutine instance every time it is called. The key point here is that <code class="sourceCode default">T</code> is not coroutine instance! From my personal experience, people would treat <code class="sourceCode default">T</code> as the coroutine instance. It’s not complete right. The instance of <code class="sourceCode default">Task</code> has a pointer who points to a coroutine instance. So it is possible that the lifetime of the instance of <code class="sourceCode default">Task</code> would end before the coroutine instance or after it. Both of cases are possible.</p>
<h2 data-number="4.5" id="ramp-function"><span class="header-section-number">4.5</span> Ramp function<a href="#ramp-function" class="self-link"></a></h2>
<p>The compiler would split a coroutine function into several parts: ramp function, resume function and destroy function. Both the resume function and destroy function contain a compiler-generated state machine to make the coroutine work correctly. And the ramp function is the initial function that would initialize the coroutine frame. Note that in the case of the corresponding <code class="sourceCode default">promise_type::initial_suspend()</code> would return <code class="sourceCode default">std::suspend_always</code> all the time, the ramp function would be very small and it would only do allocations and copy parameters to the frame and other things necessary. So that the ramp function is very easy to inline in this case and inlining is the prerequest of coroutine elision. However,it’s possible that the return value of <code class="sourceCode default">promise_type::initial_suspend()</code> is not <code class="sourceCode default">std::suspend_always</code> all the time. In that case, the possibility of the ramp function get inlied would derease significantly. So that the possiblity of the corresponding coroutine instace gets elided would decrease significantly, too.</p>
<h1 data-number="5" id="the-storage-lifetime-of-elided-coroutine-frame"><span class="header-section-number">5</span> The storage lifetime of elided coroutine frame<a href="#the-storage-lifetime-of-elided-coroutine-frame" class="self-link"></a></h1>
<p>Simply, the semantics of the storage lifetime of a coroutine state is just like a corresponding local variable in the place of the callsite. In another word, the lifetime of the elided coroutine frame would end at the end of the enclosing block. For example:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1"></a>NormalTask example1(<span class="dt">int</span> count) {</span>
<span id="cb3-2"><a href="#cb3-2"></a>  <span class="dt">int</span> sum = <span class="dv">0</span>;</span>
<span id="cb3-3"><a href="#cb3-3"></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-4"><a href="#cb3-4"></a>    sum += <span class="kw">co_await</span> coroutine_func();</span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a>  <span class="kw">co_return</span> sum;</span>
<span id="cb3-7"><a href="#cb3-7"></a>}</span></code></pre></div>
<p>Assume <code class="sourceCode default">coroutine_func()</code> would be elided, then the code would look like:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a>AlwaysElideTask an_elided_coroutine<span class="op">()</span> <span class="op">{</span> <span class="op">...</span> <span class="op">}</span></span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a>NormalTask example<span class="op">(</span><span class="dt">int</span> count<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-4"><a href="#cb2-4"></a>  <span class="dt">int</span> sum <span class="op">=</span> <span class="dv">0</span>;</span>
<span id="cb2-5"><a href="#cb2-5"></a>  <span class="cf">for</span> <span class="op">(</span><span class="dt">int</span> i <span class="op">=</span> <span class="dv">0</span>; i <span class="op">&lt;</span> count; <span class="op">++</span>i<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-6"><a href="#cb2-6"></a>    coroutine_status_ty coroutine_status;  <span class="co">// for an_elided_coroutine</span></span>
<span id="cb2-7"><a href="#cb2-7"></a>    initializing coroutine_status<span class="op">...</span>      </span>
<span id="cb2-8"><a href="#cb2-8"></a>    any other works in an_elided_coroutine <span class="co">// This is possible if the intial_suspend </span></span>
<span id="cb2-9"><a href="#cb2-9"></a>                                           <span class="co">// of AlwaysElideTask is not suspend_alwyas.</span></span>
<span id="cb2-10"><a href="#cb2-10"></a>    <span class="co">// Generally, this would pass the address of coroutine_status to Task.</span></span>
<span id="cb2-11"><a href="#cb2-11"></a>    AlwaysElideTask Task <span class="op">=</span> coroutine_status<span class="op">.</span>promise<span class="op">.</span>get_return_object<span class="op">()</span>;</span>
<span id="cb2-12"><a href="#cb2-12"></a>    sum <span class="op">+=</span> <span class="kw">co_await</span> Task;</span>
<span id="cb2-13"><a href="#cb2-13"></a>  <span class="op">}</span> <span class="co">// So the coroutine_status is invalided here.</span></span>
<span id="cb2-14"><a href="#cb2-14"></a></span>
<span id="cb2-15"><a href="#cb2-15"></a>  <span class="kw">co_return</span> sum;</span>
<span id="cb2-16"><a href="#cb2-16"></a><span class="op">}</span></span></code></pre></div>
<p>From the example we could found that the lifetime of <code class="sourceCode default">coroutine_status</code> is limited in the enclosing blocks clearly. And it would be used in one iteration only, so above transformation is valid.</p>
<h1 data-number="6" id="design"><span class="header-section-number">6</span> Design<a href="#design" class="self-link"></a></h1>
<p>The design consists of 2 parts. One for controlling the elision. One for detecting the elision.</p>
<h2 data-number="6.1" id="controlling-the-elision"><span class="header-section-number">6.1</span> Controlling the elision<a href="#controlling-the-elision" class="self-link"></a></h2>
<h3 data-number="6.1.1" id="enum-class-coro_elision"><span class="header-section-number">6.1.1</span> enum class coro_elision<a href="#enum-class-coro_elision" class="self-link"></a></h3>
<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="cb3"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb3-1"><a href="#cb3-1"></a>enum class coro_elision {</span>
<span id="cb3-2"><a href="#cb3-2"></a>    may,</span>
<span id="cb3-3"><a href="#cb3-3"></a>    always,</span>
<span id="cb3-4"><a href="#cb3-4"></a>    never</span>
<span id="cb3-5"><a href="#cb3-5"></a>};</span></code></pre></div>
<h3 data-number="6.1.2" id="must_elide"><span class="header-section-number">6.1.2</span> must_elide<a href="#must_elide" class="self-link"></a></h3>
<p>The paper proposed a new component <code class="sourceCode default">must_elide</code> to <code class="sourceCode default">promise_type</code> for controlling elision. When a coroutine is begin compliled, the compiler would try to evaluate the result of <code class="sourceCode default">promise_type::must_elide()</code> (if exist). If its result is <code class="sourceCode default">coro_elision::always</code>, all the coroutine instance generated from the coroutine would be elided. if its result is <code class="sourceCode default">coro_elision::never</code>, all the coroutine instance generated from the coroutine would be proved to not elide. If its result is <code class="sourceCode default">coro_elision::may</code> or its result couldn’t be evaluated at constexpr time, the compiler is free to do elision or not. User shouldn’t assume or depend in the last condition.</p>
<p>In the case the compiler couldn’t find the name <code class="sourceCode default">must_elide</code> in promise_type or the name <code class="sourceCode default">must_elide</code> in promise_type doesn’t mean a static constexpr member function, nothing would change.</p>
<h3 data-number="6.1.3" id="necessity"><span class="header-section-number">6.1.3</span> Necessity<a href="#necessity" class="self-link"></a></h3>
<p>The necessity of <code class="sourceCode default">must_elide</code> comes from the fact we observed in practice: “There is no way that the programmer could control coroutine elision in a well-defined way.” Different from other compiler optimization, programmers could feel dynamic allocation strongly. So there is a lack of a mechanism that programmers could control coroutine elision.</p>
<p>There is a tip to make the coroutine easier to elide: Make the coroutine type contain a coroutine_handle only. But here are some drawbacks: - It is a hack. There is no reason to do so from the perspective of the standard. And I think programmers couldn’t understand it except the compiler writers. It is hard to understand. - There is no promise that the compiler made to elide coroutine if some observable conditions are met. No. It means that we tested a coroutine is OK to elide under some conditions in a version of the compiler. But nobody knows whether or not it would elide in other versions. - There are coroutine types that couldn’t contain a coroutine_handle only. But the programmers understand that it is OK. - Even the coroutine types contain a coroutine_handle only. It would be possible that its coroutine instance couldn’t get elided. - There are coroutine types whose initial_suepdn wouldn’t always return <code class="sourceCode default">std::suspend_always</code>. But the programmers understand that is it OK. - There is the need to disable coroutine elision in certain circumstances. Like we mentioned above in embeded systems.</p>
<p>The core idea behind this is that we need a mechanism in the language level to specify that we want this coroutine to elide and another coroutine to not elide <strong>explicitly</strong>. And a fact we observed is that there are many coroutine frames that couldn’t be elided but they are safe to elide in fact. I think it is a little bit violates the design principles of C++ for “Pay for what you used”. Now the situation becomes the programmer using coroutine need to pay what they could have avoided.</p>
<h3 data-number="6.1.4" id="risks"><span class="header-section-number">6.1.4</span> Risks<a href="#risks" class="self-link"></a></h3>
<p>The key risk here is that: - It is not always safe to elide a coroutine frame.</p>
<p>Here is an example:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1"></a>Task&lt;<span class="dt">int</span>&gt; example2(<span class="dt">int</span> task_count) {</span>
<span id="cb4-2"><a href="#cb4-2"></a>  <span class="bu">std::</span>vector&lt;ElidedTask&lt;<span class="dt">int</span>&gt;&gt; tasks;</span>
<span id="cb4-3"><a href="#cb4-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="cb4-4"><a href="#cb4-4"></a>    tasks.emplace_back(coro_task());</span>
<span id="cb4-5"><a href="#cb4-5"></a>    </span>
<span id="cb4-6"><a href="#cb4-6"></a>  <span class="co">// The semantics of whenAll is that it would</span></span>
<span id="cb4-7"><a href="#cb4-7"></a>  <span class="co">// complete after all the tasks completed.</span></span>
<span id="cb4-8"><a href="#cb4-8"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> whenAll(<span class="bu">std::</span>move(tasks));</span>
<span id="cb4-9"><a href="#cb4-9"></a>}</span></code></pre></div>
<p>If we make <code class="sourceCode default">coro_task()</code> to elide all the time, the code would look like this:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1"></a>Task&lt;<span class="dt">int</span>&gt; example2(<span class="dt">int</span> task_count) {</span>
<span id="cb5-2"><a href="#cb5-2"></a>  <span class="bu">std::</span>vector&lt;coroutine_status_ty *&gt; tasks;</span>
<span id="cb5-3"><a href="#cb5-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="cb5-4"><a href="#cb5-4"></a>    coroutine_status_ty coroutine_status;</span>
<span id="cb5-5"><a href="#cb5-5"></a>    tasks.emplace_back(&amp;coroutine_status);</span>
<span id="cb5-6"><a href="#cb5-6"></a>  }</span>
<span id="cb5-7"><a href="#cb5-7"></a>  </span>
<span id="cb5-8"><a href="#cb5-8"></a>  <span class="co">// Now all the pointer in tasks are dangling</span></span>
<span id="cb5-9"><a href="#cb5-9"></a>  <span class="co">// pointer!</span></span>
<span id="cb5-10"><a href="#cb5-10"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> whenAll(<span class="bu">std::</span>move(tasks));</span>
<span id="cb5-11"><a href="#cb5-11"></a>}</span></code></pre></div>
<p>It should be clear that <code class="sourceCode default">tasks</code> would contain only dangling pointers after the for-loop. And there would be other examples.</p>
<p>And my answer for these cases is that we shouldn’t make tasks always elide.</p>
<h3 data-number="6.1.5" id="design-ideas"><span class="header-section-number">6.1.5</span> Design Ideas<a href="#design-ideas" class="self-link"></a></h3>
<p>The core design ideas include: - Offer a mechanism that the programmer could control coroutine elision. So that they could avoid cost for dynamic allocation or unwanted elision. - It is not designed as a panacea. There are cases it couldn’t handle. And it is possible to be a risk if the user uses it arbitrarily. But after this proposal we could solve some problems we couldn’t solve before. - It is not a forced option. You could avoid using it. So that it wouldn’t break any existing code. It is designed for the programmer who could understand it and willing to pay for the risk. And if you think you don’t understand it or you don’t want to pay that risk, you could avoid to use it simply. - We should support to force a coroutine to elide, to not elide or offloading the judgment to the compiler. Or in another word, the mechanism should return tristate.</p>
<h3 data-number="6.1.6" id="why-in-promise_type"><span class="header-section-number">6.1.6</span> Why in promise_type?<a href="#why-in-promise_type" class="self-link"></a></h3>
<p>People may complain that it is too coarse grained to define <code class="sourceCode default">must_elide</code> at the promise_type level. It should be better to annotate at the callsite. I agree with it. But the key problem here is that there is <strong>no mechanism in C++ to annotate an expression</strong>! Although there is attribute in C++ standard. It contains statement level and declaration level only. And it would be a much harder problem to support expression attribute. So it would be a long time to support the expression attribute from my point of view (Or we couldn’t make it actually).</p>
<p>And here are two reasons to define <code class="sourceCode default">must_elide</code> in promise_type: - It is more suit for the current coroutine design. Now the behavior of a coroutine is defined in promise_type and corresponding awaiter. So it is natural to define <code class="sourceCode default">must_elide</code> in promise_type. - It is possible to control the coroutine to elide at the callsite level in the current design.</p>
<p>The second point matters more. For example:</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">struct</span> ShouldElideTagT;</span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw">struct</span> NoElideTagT;</span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="kw">struct</span> MayElideTagT;</span>
<span id="cb6-4"><a href="#cb6-4"></a></span>
<span id="cb6-5"><a href="#cb6-5"></a><span class="kw">template</span>&lt;<span class="kw">typename</span> T&gt;</span>
<span id="cb6-6"><a href="#cb6-6"></a><span class="kw">concept</span> ElideTag = (<span class="bu">std::</span>same_as&lt;T, ShouldElideTagT&gt; ||</span>
<span id="cb6-7"><a href="#cb6-7"></a>                    <span class="bu">std::</span>same_as&lt;T, NoElideTagT&gt; ||</span>
<span id="cb6-8"><a href="#cb6-8"></a>                    <span class="bu">std::</span>same_as&lt;T, MayElideTagT&gt;);</span>
<span id="cb6-9"><a href="#cb6-9"></a></span>
<span id="cb6-10"><a href="#cb6-10"></a><span class="kw">template</span> &lt;ElideTag Tag&gt;</span>
<span id="cb6-11"><a href="#cb6-11"></a><span class="kw">struct</span> TaskPromiseAlternative : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb6-12"><a href="#cb6-12"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide() {</span>
<span id="cb6-13"><a href="#cb6-13"></a>        <span class="cf">if</span> <span class="kw">constexpr</span> (<span class="bu">std::</span>is_same_v&lt;Tag, ShouldElideTagT&gt;)</span>
<span id="cb6-14"><a href="#cb6-14"></a>            <span class="cf">return</span> coro_elision::always;</span>
<span id="cb6-15"><a href="#cb6-15"></a>        <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> (<span class="bu">std::</span>is_same_v&lt;Tag, NoElideTagT&gt;)</span>
<span id="cb6-16"><a href="#cb6-16"></a>            <span class="cf">return</span> coro_elision::never;</span>
<span id="cb6-17"><a href="#cb6-17"></a>        <span class="cf">else</span></span>
<span id="cb6-18"><a href="#cb6-18"></a>            <span class="cf">return</span> coro_elision::may;</span>
<span id="cb6-19"><a href="#cb6-19"></a>    }</span>
<span id="cb6-20"><a href="#cb6-20"></a>};</span>
<span id="cb6-21"><a href="#cb6-21"></a></span>
<span id="cb6-22"><a href="#cb6-22"></a><span class="kw">template</span> &lt;ElideTag Tag = MayElideTagT&gt;</span>
<span id="cb6-23"><a href="#cb6-23"></a><span class="kw">using</span> AlternativeTask = Task&lt;TaskPromiseAlternative&lt;Tag&gt;&gt;;</span>
<span id="cb6-24"><a href="#cb6-24"></a><span class="kw">using</span> Task = AlternativeTask&lt;MayElideTagT&gt;;</span>
<span id="cb6-25"><a href="#cb6-25"></a></span>
<span id="cb6-26"><a href="#cb6-26"></a><span class="kw">template</span> &lt;ElideTag Tag = MayElideTagT&gt;</span>
<span id="cb6-27"><a href="#cb6-27"></a>AlternativeTask&lt;Tag&gt; alternative_task () { <span class="co">/* ... */</span> } <span class="co">// This is a coroutine</span></span>
<span id="cb6-28"><a href="#cb6-28"></a></span>
<span id="cb6-29"><a href="#cb6-29"></a>Task NaturalTask() { <span class="co">/* ... */</span> } <span class="co">// This is a coroutine.</span></span>
<span id="cb6-30"><a href="#cb6-30"></a></span>
<span id="cb6-31"><a href="#cb6-31"></a><span class="dt">int</span> foo() {</span>
<span id="cb6-32"><a href="#cb6-32"></a>  <span class="kw">auto</span> t1 = alternative_task&lt;ShouldElideTagT&gt;();</span>
<span id="cb6-33"><a href="#cb6-33"></a>  <span class="co">// Task::elided would call coroutine_handle::elided</span></span>
<span id="cb6-34"><a href="#cb6-34"></a>  <span class="ot">assert</span> (t1.elided());</span>
<span id="cb6-35"><a href="#cb6-35"></a>  <span class="kw">auto</span> t2 = alternative_task&lt;NoElideTagT&gt;();</span>
<span id="cb6-36"><a href="#cb6-36"></a>  <span class="ot">assert</span> (!t1.elided());</span>
<span id="cb6-37"><a href="#cb6-37"></a>  <span class="co">// The default case, which would be general for most cases</span></span>
<span id="cb6-38"><a href="#cb6-38"></a>  alternative_task();</span>
<span id="cb6-39"><a href="#cb6-39"></a>  <span class="co">// The author of the function don&#39;t want us to control its elision</span></span>
<span id="cb6-40"><a href="#cb6-40"></a>  <span class="co">// or the author think it doesn&#39;t matter to elide or not. After all,</span></span>
<span id="cb6-41"><a href="#cb6-41"></a>  <span class="co">// the compiler would make the decision.</span></span>
<span id="cb6-42"><a href="#cb6-42"></a>  NaturalTask();</span>
<span id="cb6-43"><a href="#cb6-43"></a>}</span></code></pre></div>
<p>In this way, we could control the elision at call site! All cost we need to pay is that we need to type a little more in the definition. And users who don’t want to pay attention could use the default behavior. So it wouldn’t be a big cost for the users.</p>
<h3 data-number="6.1.7" id="why-enum-class-coro_elision"><span class="header-section-number">6.1.7</span> Why enum class coro_elision?<a href="#why-enum-class-coro_elision" class="self-link"></a></h3>
<p>In the previous version of this proposal, the tristate of <code class="sourceCode default">promise_type::must_elide()</code> is <code class="sourceCode default">constexpr time true</code>, <code class="sourceCode default">constexpr time false</code> and <code class="sourceCode default">constexpr time unknown</code>. I feel it is natural and clean. But Gašper reminded me that it is problematic in the case of deriving class. Gašper raised an example like:</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">class</span> AlwaysElidePromiseBase {</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">public</span>:</span>
<span id="cb7-3"><a href="#cb7-3"></a>     <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide() { <span class="cf">return</span> <span class="kw">true</span>; }</span>
<span id="cb7-4"><a href="#cb7-4"></a>};</span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="dt">bool</span> undetermism;</span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="kw">class</span> MayElidePromiseDerived1 : <span class="kw">public</span> AlwaysElidePromiseBase {</span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="kw">public</span>:</span>
<span id="cb7-8"><a href="#cb7-8"></a>    <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide() { <span class="cf">return</span> undetermism; }</span>
<span id="cb7-9"><a href="#cb7-9"></a>};</span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="kw">class</span> Task {</span>
<span id="cb7-11"><a href="#cb7-11"></a><span class="kw">public</span>:</span>
<span id="cb7-12"><a href="#cb7-12"></a>    <span class="kw">using</span> <span class="dt">promise_type</span> = MayElidePromiseDerived1;</span>
<span id="cb7-13"><a href="#cb7-13"></a>};</span></code></pre></div>
<p>In this example, the base class is declared as always eliding and the derived class wants to rewrite the behavior by returning a <code class="sourceCode default">constexpr time unkown</code>. However, the code above is not right. Since it wouldn’t be possible to be evaluated at constexpr time, which violates the constexpr function rule <a href="https://eel.is/c++draft/dcl.constexpr#6">[dcl.constexpr]p6</a>. So in the original design, it is impossible to make the derived promise base to declare the coroutine should be <code class="sourceCode default">may-elide</code>. So I choose to make the <code class="sourceCode default">must_elide</code> function to return a explicit tristate.</p>
<h2 data-number="6.2" id="detecting-the-elision"><span class="header-section-number">6.2</span> Detecting the elision<a href="#detecting-the-elision" class="self-link"></a></h2>
<h3 data-number="6.2.1" id="elided"><span class="header-section-number">6.2.1</span> elided<a href="#elided" class="self-link"></a></h3>
<p>Add a non-static member bool function <code class="sourceCode default">elided</code> to <code class="sourceCode default">std::coroutine_handle&lt;&gt;</code> and <code class="sourceCode default">std::coroutine_handle&lt;PromiseType&gt;</code> .</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1"></a><span class="kw">namespace</span> std <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2"></a>  <span class="kw">template</span><span class="op">&lt;&gt;</span></span>
<span id="cb4-3"><a href="#cb4-3"></a>  <span class="kw">struct</span> coroutine_handle<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span></span>
<span id="cb4-4"><a href="#cb4-4"></a>  <span class="op">{</span> </span>
<span id="cb4-5"><a href="#cb4-5"></a>    <span class="co">// ...</span></span>
<span id="cb4-6"><a href="#cb4-6"></a>    <span class="co">// [coroutine.handle.observers], observers</span></span>
<span id="cb4-7"><a href="#cb4-7"></a>    <span class="kw">constexpr</span> <span class="kw">explicit</span> <span class="kw">operator</span> <span class="dt">bool</span><span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span>;</span>
<span id="cb4-8"><a href="#cb4-8"></a>    <span class="dt">bool</span> done<span class="op">()</span> <span class="kw">const</span>;</span>
<span id="cb4-9"><a href="#cb4-9"></a><span class="op">+</span>   <span class="dt">bool</span> elided<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span>;</span>
<span id="cb4-10"><a href="#cb4-10"></a>    <span class="co">// ...</span></span>
<span id="cb4-11"><a href="#cb4-11"></a>  <span class="op">}</span>;</span>
<span id="cb4-12"><a href="#cb4-12"></a></span>
<span id="cb4-13"><a href="#cb4-13"></a>  <span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> Promise<span class="op">&gt;</span></span>
<span id="cb4-14"><a href="#cb4-14"></a>  <span class="kw">struct</span> coroutine_handle</span>
<span id="cb4-15"><a href="#cb4-15"></a>  <span class="op">{</span></span>
<span id="cb4-16"><a href="#cb4-16"></a>    <span class="co">// ...</span></span>
<span id="cb4-17"><a href="#cb4-17"></a>    <span class="co">// [coroutine.handle.observers], observers</span></span>
<span id="cb4-18"><a href="#cb4-18"></a>    <span class="kw">constexpr</span> <span class="kw">explicit</span> <span class="kw">operator</span> <span class="dt">bool</span><span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span>;</span>
<span id="cb4-19"><a href="#cb4-19"></a>    <span class="dt">bool</span> done<span class="op">()</span> <span class="kw">const</span>;</span>
<span id="cb4-20"><a href="#cb4-20"></a><span class="op">+</span>   <span class="dt">bool</span> elided<span class="op">()</span> <span class="kw">const</span> <span class="kw">noexcept</span>;</span>
<span id="cb4-21"><a href="#cb4-21"></a>        <span class="co">// ...</span></span>
<span id="cb4-22"><a href="#cb4-22"></a>  <span class="op">}</span>;</span>
<span id="cb4-23"><a href="#cb4-23"></a><span class="op">}</span></span></code></pre></div>
<p>And the semantics should be clear. If the corresponding coroutine is elided, then <code class="sourceCode default">elided</code> would return true. And if the corresponding coroutine is not elided, then <code class="sourceCode default">elided</code> should return false.</p>
<p>Here are two reasons I want to introduce <code class="sourceCode default">elided</code>:</p>
<ul>
<li>For the language side, if we introduce the concept of coroutine elision in the language. It is odd that the user couldn’t observe it.</li>
<li>For the engineering side, as a new feature, the developer should write tests if they want to introduce this feature into their codebases. But they couldn’t make it until <code class="sourceCode default">elided</code> is provided.</li>
</ul>
<p>But introducing <code class="sourceCode default">elided</code> would introduce overhead. Since it would require the coroutine status to store extra information.</p>
<p>And here is 3 direction:</p>
<ul>
<li>Support both <code class="sourceCode default">coroutine_handle&lt;&gt;::elided()</code> and <code class="sourceCode default">coroutine_handle&lt;PromiseType&gt;::elided()</code>.</li>
<li>Support <code class="sourceCode default">coroutine_handle&lt;PromiseType&gt;::elided()</code> only.</li>
<li>Giving up to support <code class="sourceCode default">elided()</code>.</li>
</ul>
<h3 data-number="6.2.2" id="support-both"><span class="header-section-number">6.2.2</span> Support both<a href="#support-both" class="self-link"></a></h3>
<p>If we want to support both <code class="sourceCode default">coroutine_handle&lt;&gt;::elided()</code> and <code class="sourceCode default">coroutine_handle&lt;PromiseType&gt;::elided()</code>, we must refactor the ABI for coroutine into the following form:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a><span class="kw">struct</span> CoroutineFrame {</span>
<span id="cb8-2"><a href="#cb8-2"></a>  <span class="dt">void</span> (*resume_func)(CoroutineFrame*);</span>
<span id="cb8-3"><a href="#cb8-3"></a>  <span class="dt">void</span> (*destroy_func)(CoroutineFrame*);</span>
<span id="cb8-4"><a href="#cb8-4"></a>  <span class="dt">bool</span> Elided;</span>
<span id="cb8-5"><a href="#cb8-5"></a>  <span class="dt">promise_type</span>;</span>
<span id="cb8-6"><a href="#cb8-6"></a>  <span class="co">// any other information needed</span></span>
<span id="cb8-7"><a href="#cb8-7"></a>};</span></code></pre></div>
<p>[Note: The current coroutine ABI is:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1"></a><span class="kw">struct</span> CoroutineFrame {</span>
<span id="cb9-2"><a href="#cb9-2"></a>  <span class="dt">void</span> (*resume_func)(CoroutineFrame*);</span>
<span id="cb9-3"><a href="#cb9-3"></a>  <span class="dt">void</span> (*destroy_func)(CoroutineFrame*);</span>
<span id="cb9-4"><a href="#cb9-4"></a>  <span class="dt">promise_type</span>;</span>
<span id="cb9-5"><a href="#cb9-5"></a>  <span class="co">// any other information needed</span></span>
<span id="cb9-6"><a href="#cb9-6"></a>};</span></code></pre></div>
<p>]</p>
<p>There are several drawbacks:</p>
<ul>
<li>It breaks the ABI.</li>
<li>It would enlarge the coroutine frame. Due to the alignment requirement, it may introduce more space than 1 bit for every coroutine frame.</li>
<li>It would introduce an extra store instruction for every coroutine.</li>
</ul>
<p>For the ABI problem, I am not sure if it matters so much. There are some reasons:</p>
<ul>
<li>Coroutine is a new feature so the historical dependent problem may not be so crucial.</li>
<li>Now, the different compilers would generate different ABI. The type for the resume_func, destroy_func, and the index for the state machine are not the same in different compilers.</li>
<li>For the same compiler, the different versions of the compilers may generate different coroutine frames although their headers are the same. The remainder of the frame may be different.</li>
<li>The coroutine ABI is not standardized as far as I know.</li>
</ul>
<p>And for the second and the third drawbacks, they are the price we must pay to enable this feature.</p>
<p>And the implementation demo implements this strategy.</p>
<h3 data-number="6.2.3" id="suppot-coroutine_handlepromisetypeelided-only"><span class="header-section-number">6.2.3</span> Suppot coroutine_handle&lt;PromiseType&gt;::elided() only<a href="#suppot-coroutine_handlepromisetypeelided-only" class="self-link"></a></h3>
<p>If we decided to not support <code class="sourceCode default">coroutine_handle&lt;&gt;::elided()</code>, we could move the <code class="sourceCode default">Elided</code> bit in the back of <code class="sourceCode default">promise_type</code>:</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">struct</span> CoroutineFrame {</span>
<span id="cb10-2"><a href="#cb10-2"></a>  <span class="dt">void</span> (*resume_func)(CoroutineFrame*),</span>
<span id="cb10-3"><a href="#cb10-3"></a>  <span class="dt">void</span> (*destroy_func)(CoroutineFrame*),</span>
<span id="cb10-4"><a href="#cb10-4"></a>  <span class="dt">promise_type</span>,</span>
<span id="cb10-5"><a href="#cb10-5"></a>  <span class="dt">bool</span> Elided,</span>
<span id="cb10-6"><a href="#cb10-6"></a>  <span class="co">// any other information needed</span></span>
<span id="cb10-7"><a href="#cb10-7"></a>};</span></code></pre></div>
<p>So that we could avoid the ABI problems. But I feel odd for giving up to support <code class="sourceCode default">coroutine_handle&lt;&gt;::elided</code>. Since <code class="sourceCode default">coroutine_handle&lt;&gt;</code> is a generic abstract for coroutine and <code class="sourceCode default">elided</code> should be a generic feature for all the coroutine.</p>
<h3 data-number="6.2.4" id="giving-up-to-support-elided"><span class="header-section-number">6.2.4</span> Giving up to support elided<a href="#giving-up-to-support-elided" class="self-link"></a></h3>
<p>This is simple. We wouldn’t need to pay anything in implementation and language wording.</p>
<h3 data-number="6.2.5" id="summarize"><span class="header-section-number">6.2.5</span> Summarize<a href="#summarize" class="self-link"></a></h3>
<p>I prefer to support both and pay for the price for more space needed and the extra store instruction. But I am not so sure since the use cases for <code class="sourceCode default">elided</code> I got now are in the test only. And for the codes which are actually running in production, I couldn’t imagine the use cases for <code class="sourceCode default">elided</code>. So in another word, the people who use coroutine in production would pay for the price that they don’t use. This violates the principle of C++ that “Pay for what you use”. But as I said, it is really odd that people could control coroutine elision without observing it.</p>
<p>So I am not sure about this. Any opinion is welcome.</p>
<h1 data-number="7" id="examples"><span class="header-section-number">7</span> Examples<a href="#examples" class="self-link"></a></h1>
<p>A set of examples could be found at <a href="https://github.com/ChuanqiXu9/llvm-project/tree/CoroMustElide/MustElide">Examples</a>. Example-1~6 mimic the use of loop, select operator, goto statement, function default argument, the initializer of an if-block, and the initializer of a class data-member. All of them are simple and easy to understand. Here talks about the basic usage for <code class="sourceCode default">must_elide</code> only.</p>
<p>(Note: This contains the example in R1 only. The only difference may be that the return type of <code class="sourceCode default">promise_type::must_elide</code> would be constexpr bool still.)</p>
<h2 data-number="7.1" id="always-enabledisable-coroutine-elision"><span class="header-section-number">7.1</span> Always Enable/Disable coroutine elision<a href="#always-enabledisable-coroutine-elision" class="self-link"></a></h2>
<p>We could enable/disable coroutine elision for a kind of coroutine like:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1"></a><span class="kw">struct</span> TaskPromiseAlwaysElide : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb11-2"><a href="#cb11-2"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide() {</span>
<span id="cb11-3"><a href="#cb11-3"></a>        <span class="cf">return</span> <span class="kw">true</span>;</span>
<span id="cb11-4"><a href="#cb11-4"></a>    }</span>
<span id="cb11-5"><a href="#cb11-5"></a>};</span>
<span id="cb11-6"><a href="#cb11-6"></a></span>
<span id="cb11-7"><a href="#cb11-7"></a><span class="kw">struct</span> TaskPromiseNeverElide : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb11-8"><a href="#cb11-8"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide() {</span>
<span id="cb11-9"><a href="#cb11-9"></a>        <span class="cf">return</span> <span class="kw">false</span>;</span>
<span id="cb11-10"><a href="#cb11-10"></a>    }</span>
<span id="cb11-11"><a href="#cb11-11"></a>};</span></code></pre></div>
<p>Then for the coroutine like:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">class</span> PromiseType<span class="op">&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2"></a><span class="kw">class</span> Task <span class="op">:</span> <span class="kw">public</span> TaskBase<span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb5-4"><a href="#cb5-4"></a>    <span class="kw">using</span> promise_type <span class="op">=</span> PromiseType;</span>
<span id="cb5-5"><a href="#cb5-5"></a>    <span class="kw">using</span> HandleType <span class="op">=</span> std<span class="op">::</span>experimental<span class="op">::</span>coroutine_handle<span class="op">&lt;</span>promise_type<span class="op">&gt;</span>;</span>
<span id="cb5-6"><a href="#cb5-6"></a>    <span class="co">// ...</span></span>
<span id="cb5-7"><a href="#cb5-7"></a><span class="op">}</span>; <span class="co">// end of Task</span></span>
<span id="cb5-8"><a href="#cb5-8"></a><span class="kw">using</span> AlwaysElideTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseAlwaysElide<span class="op">&gt;</span>;</span>
<span id="cb5-9"><a href="#cb5-9"></a><span class="kw">using</span> NeverElideTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseNeverElide<span class="op">&gt;</span>;</span>
<span id="cb5-10"><a href="#cb5-10"></a></span>
<span id="cb5-11"><a href="#cb5-11"></a>AlwaysElideTask always_elide_task <span class="op">()</span> <span class="op">{</span></span>
<span id="cb5-12"><a href="#cb5-12"></a>    <span class="co">// ... contain coroutine keywords</span></span>
<span id="cb5-13"><a href="#cb5-13"></a><span class="op">}</span></span>
<span id="cb5-14"><a href="#cb5-14"></a>NeverElideTask never_elide_task <span class="op">()</span> <span class="op">{</span></span>
<span id="cb5-15"><a href="#cb5-15"></a>    <span class="co">// ... contain coroutine keywords</span></span>
<span id="cb5-16"><a href="#cb5-16"></a><span class="op">}</span></span></code></pre></div>
<p>Then every coroutine frame generated from <code class="sourceCode default">always_elide_task()</code> would be elided. And every coroutine frame generated from <code class="sourceCode default">never_elide_task</code> wouldn’t be elided.</p>
<h2 data-number="7.2" id="no-guarantee-to-coroutine-elision"><span class="header-section-number">7.2</span> No guarantee to coroutine elision<a href="#no-guarantee-to-coroutine-elision" class="self-link"></a></h2>
<p>If the compiler couldn’t infer the result of <code class="sourceCode default">promise_type::must_elide</code> at compile time, then the compiler is free to do elision or not. And the behavior would be the same with <code class="sourceCode default">must_elide</code> is not exist in <code class="sourceCode default">promise_type</code>.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1"></a><span class="dt">bool</span> undertermism;</span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw">struct</span> TaskPromiseMeaningless <span class="op">:</span> <span class="kw">public</span> TaskPromiseBase <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4"></a>        <span class="cf">return</span> undertermism;</span>
<span id="cb6-5"><a href="#cb6-5"></a>    <span class="op">}</span></span>
<span id="cb6-6"><a href="#cb6-6"></a><span class="op">}</span>;</span></code></pre></div>
<p>Then <code class="sourceCode default">TaskPromiseMeaningless</code> would be the same as <code class="sourceCode default">TaskPromiseBase</code> for every case.</p>
<h2 data-number="7.3" id="controlling-the-elision-at-callsite"><span class="header-section-number">7.3</span> Controlling the elision at callsite<a href="#controlling-the-elision-at-callsite" class="self-link"></a></h2>
<p>It is possible that we could control coroutine elision for specific call. Here is my demo,</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1"></a><span class="kw">using</span> NormalTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseBase<span class="op">&gt;</span>;</span>
<span id="cb7-2"><a href="#cb7-2"></a><span class="kw">using</span> AlwaysElideTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseAlwaysElide<span class="op">&gt;</span>;</span>
<span id="cb7-3"><a href="#cb7-3"></a><span class="kw">using</span> NeverElideTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseNeverElide<span class="op">&gt;</span>;</span>
<span id="cb7-4"><a href="#cb7-4"></a></span>
<span id="cb7-5"><a href="#cb7-5"></a><span class="kw">struct</span> ShouldElideTagT;</span>
<span id="cb7-6"><a href="#cb7-6"></a><span class="kw">struct</span> NoElideTagT;</span>
<span id="cb7-7"><a href="#cb7-7"></a><span class="kw">struct</span> MayElideTagT;</span>
<span id="cb7-8"><a href="#cb7-8"></a></span>
<span id="cb7-9"><a href="#cb7-9"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span> T<span class="op">&gt;</span></span>
<span id="cb7-10"><a href="#cb7-10"></a><span class="kw">concept</span> ElideTag <span class="op">=</span> <span class="op">(</span>std<span class="op">::</span>same_as<span class="op">&lt;</span>T, ShouldElideTagT<span class="op">&gt;</span> <span class="op">||</span></span>
<span id="cb7-11"><a href="#cb7-11"></a>                    std<span class="op">::</span>same_as<span class="op">&lt;</span>T, NoElideTagT<span class="op">&gt;</span> <span class="op">||</span></span>
<span id="cb7-12"><a href="#cb7-12"></a>                    std<span class="op">::</span>same_as<span class="op">&lt;</span>T, MayElideTagT<span class="op">&gt;)</span>;</span>
<span id="cb7-13"><a href="#cb7-13"></a></span>
<span id="cb7-14"><a href="#cb7-14"></a><span class="dt">bool</span> undetermism;</span>
<span id="cb7-15"><a href="#cb7-15"></a><span class="kw">template</span> <span class="op">&lt;</span>ElideTag Tag<span class="op">&gt;</span></span>
<span id="cb7-16"><a href="#cb7-16"></a><span class="kw">struct</span> TaskPromiseAlternative <span class="op">:</span> <span class="kw">public</span> TaskPromiseBase <span class="op">{</span></span>
<span id="cb7-17"><a href="#cb7-17"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> must_elide<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-18"><a href="#cb7-18"></a>        <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>std<span class="op">::</span>is_same_v<span class="op">&lt;</span>Tag, ShouldElideTagT<span class="op">&gt;)</span></span>
<span id="cb7-19"><a href="#cb7-19"></a>            <span class="cf">return</span> <span class="kw">true</span>;</span>
<span id="cb7-20"><a href="#cb7-20"></a>        <span class="cf">else</span> <span class="cf">if</span> <span class="kw">constexpr</span> <span class="op">(</span>std<span class="op">::</span>is_same_v<span class="op">&lt;</span>Tag, NoElideTagT<span class="op">&gt;)</span></span>
<span id="cb7-21"><a href="#cb7-21"></a>            <span class="cf">return</span> <span class="kw">false</span>;</span>
<span id="cb7-22"><a href="#cb7-22"></a>        <span class="cf">else</span></span>
<span id="cb7-23"><a href="#cb7-23"></a>            <span class="cf">return</span> undetermism;</span>
<span id="cb7-24"><a href="#cb7-24"></a>    <span class="op">}</span></span>
<span id="cb7-25"><a href="#cb7-25"></a><span class="op">}</span>;</span>
<span id="cb7-26"><a href="#cb7-26"></a></span>
<span id="cb7-27"><a href="#cb7-27"></a><span class="kw">template</span> <span class="op">&lt;</span>ElideTag Tag <span class="op">=</span> MayElideTagT<span class="op">&gt;</span></span>
<span id="cb7-28"><a href="#cb7-28"></a><span class="kw">using</span> AlternativeTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseAlternative<span class="op">&lt;</span>Tag<span class="op">&gt;&gt;</span>;</span>
<span id="cb7-29"><a href="#cb7-29"></a></span>
<span id="cb7-30"><a href="#cb7-30"></a><span class="kw">template</span> <span class="op">&lt;</span>ElideTag Tag <span class="op">=</span> MayElideTagT<span class="op">&gt;</span></span>
<span id="cb7-31"><a href="#cb7-31"></a>AlternativeTask<span class="op">&lt;</span>Tag<span class="op">&gt;</span> alternative_task <span class="op">()</span> <span class="op">{</span> <span class="co">/* ... */</span> <span class="op">}</span> <span class="co">// This is a coroutine</span></span>
<span id="cb7-32"><a href="#cb7-32"></a></span>
<span id="cb7-33"><a href="#cb7-33"></a><span class="dt">int</span> foo<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-34"><a href="#cb7-34"></a>  <span class="kw">auto</span> t1 <span class="op">=</span> alternative_task<span class="op">&lt;</span>ShouldElideTagT<span class="op">&gt;()</span>;</span>
<span id="cb7-35"><a href="#cb7-35"></a>  <span class="co">// Task::elided would call coroutine_handle::elided</span></span>
<span id="cb7-36"><a href="#cb7-36"></a>  <span class="ot">assert</span> <span class="op">(</span>t1<span class="op">.</span>elided<span class="op">())</span>;</span>
<span id="cb7-37"><a href="#cb7-37"></a>  <span class="kw">auto</span> t2 <span class="op">=</span> alternative_task<span class="op">&lt;</span>NoElideTagT<span class="op">&gt;()</span>;</span>
<span id="cb7-38"><a href="#cb7-38"></a>  <span class="ot">assert</span> <span class="op">(!</span>t1<span class="op">.</span>elided<span class="op">())</span>;</span>
<span id="cb7-39"><a href="#cb7-39"></a>  <span class="co">// The default case, which would be general for most cases</span></span>
<span id="cb7-40"><a href="#cb7-40"></a>  alternative_task<span class="op">()</span>; </span>
<span id="cb7-41"><a href="#cb7-41"></a><span class="op">}</span> </span></code></pre></div>
<p>From the example in <code class="sourceCode default">foo</code>, we could find that we get the ability to control elision at callsite finally! Although it looks tedious to add <code class="sourceCode default">template &lt;ElideTag Tag&gt;</code> alone the way. But I believe the cases that we need to control wouldn’t be too much. We only need to control some of the coroutines in a project.</p>
<h1 data-number="8" id="limitations"><span class="header-section-number">8</span> Limitations<a href="#limitations" class="self-link"></a></h1>
<p>This section describes the limitations of this proposal. It contains 2 situations that we couldn’t elide a coroutine even if the <code class="sourceCode default">promise_type::must_elide</code> evaluates to true at compile time and a situation that is beyond the current elision design.</p>
<h2 data-number="8.1" id="indirect-call"><span class="header-section-number">8.1</span> Indirect Call<a href="#indirect-call" class="self-link"></a></h2>
<p>An indirect call is the call which the compiler couldn’t find the corresponding callee at compile time. The most common indirect call is virtual functions. For example:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1"></a>Class A <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb8-3"><a href="#cb8-3"></a><span class="kw">virtual</span> CoroType CoroFunc<span class="op">()</span> <span class="op">{</span> <span class="co">/*...*/</span> <span class="op">}</span></span>
<span id="cb8-4"><a href="#cb8-4"></a><span class="op">}</span>;</span>
<span id="cb8-5"><a href="#cb8-5"></a><span class="kw">class</span> B <span class="op">:</span> <span class="kw">public</span> A <span class="op">{</span></span>
<span id="cb8-6"><a href="#cb8-6"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb8-7"><a href="#cb8-7"></a>CoroType CoroFunc<span class="op">()</span> <span class="op">{</span> <span class="co">/*...*/</span> <span class="op">}</span></span>
<span id="cb8-8"><a href="#cb8-8"></a><span class="op">}</span>;</span>
<span id="cb8-9"><a href="#cb8-9"></a>CoroType foo<span class="op">(</span>A<span class="op">*</span> a<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-10"><a href="#cb8-10"></a>    <span class="kw">co_await</span> a<span class="op">-&gt;</span>CoroFunc<span class="op">()</span>; <span class="co">// The compiler couldn&#39;t know which function CoroFunc refers to.</span></span>
<span id="cb8-11"><a href="#cb8-11"></a><span class="op">}</span>;</span></code></pre></div>
<p>Another example is function pointers:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode default"><code class="sourceCode default"><span id="cb9-1"><a href="#cb9-1"></a>// coro_func is a global variable</span>
<span id="cb9-2"><a href="#cb9-2"></a>std::function&lt;Task()&gt; coro_func;</span>
<span id="cb9-3"><a href="#cb9-3"></a>// ... many operations to fulfill coro_func</span>
<span id="cb9-4"><a href="#cb9-4"></a>// in another function</span>
<span id="cb9-5"><a href="#cb9-5"></a>co_await coro_func(); </span></code></pre></div>
<p>Although we could see from the signature the function that <code class="sourceCode default">coro_func</code> refers to should elide all the way. But the compiler couldn’t elide it actually since it can’t know the coroutine <code class="sourceCode default">coro_func</code> refers to usually. The compiler couldn’t know if <code class="sourceCode default">coro_func</code> refers to a coroutine actually. And the compiler couldn’t do too much even if we could use any hack possible.</p>
<p>The internal reason is that the compiler needs to know how many bits we need to allocate the coroutine status if we want to elide a coroutine. And the compiler couldn’t infer that if it couldn’t see the body of the coroutine.</p>
<h2 data-number="8.2" id="elision-cross-translation-unit"><span class="header-section-number">8.2</span> Elision cross translation unit<a href="#elision-cross-translation-unit" class="self-link"></a></h2>
<p>Another issue is that we couldn’t elide a coroutine into a function in another translation unit. For example:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb10-1"><a href="#cb10-1"></a><span class="co">// CoroType.h</span></span>
<span id="cb10-2"><a href="#cb10-2"></a>AlwaysElideTask may_be_a_coroutine<span class="op">()</span>;      <span class="co">// We couldn&#39;t see if this is a coroutine</span></span>
<span id="cb10-3"><a href="#cb10-3"></a>                                           <span class="co">// from the signature.</span></span>
<span id="cb10-4"><a href="#cb10-4"></a><span class="co">// A.cpp</span></span>
<span id="cb10-5"><a href="#cb10-5"></a>AlwaysElideTask may_be_a_coroutine<span class="op">()</span> <span class="op">{</span>     <span class="co">// It is a coroutine indeed.</span></span>
<span id="cb10-6"><a href="#cb10-6"></a>  <span class="kw">co_await</span> std<span class="op">::</span>suspend_always<span class="op">{}</span>;</span>
<span id="cb10-7"><a href="#cb10-7"></a>  <span class="co">// ... Other works needed.</span></span>
<span id="cb10-8"><a href="#cb10-8"></a><span class="op">}</span></span>
<span id="cb10-9"><a href="#cb10-9"></a><span class="co">// B.cpp</span></span>
<span id="cb10-10"><a href="#cb10-10"></a>AlwaysElideTask CoroA<span class="op">()</span> <span class="op">{</span></span>
<span id="cb10-11"><a href="#cb10-11"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> may_be_a_coroutine<span class="op">()</span>; <span class="co">// Is it a coroutine?</span></span>
<span id="cb10-12"><a href="#cb10-12"></a><span class="op">}</span></span></code></pre></div>
<p>The reason why the compiler couldn’t elide it is similar to the above question. If the compiler couldn’t know the callee, it couldn’t do optimization.</p>
<p>From the perspective of a compiler, this issue is potentially solvable by enabling LTO (Link Time Optimization). In LTO, the compiler could see every translation unit. But since we are talking about the language standard. I feel like that is not possible that we could add the concept LTO in the standard nor we could change the compilation model.</p>
<h2 data-number="8.3" id="use-out-of-the-scope-of-definition"><span class="header-section-number">8.3</span> Use out of the scope of definition<a href="#use-out-of-the-scope-of-definition" class="self-link"></a></h2>
<p>There is an example above to address the problem:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a>Task&lt;<span class="dt">int</span>&gt; example2(<span class="dt">int</span> task_count) {</span>
<span id="cb12-2"><a href="#cb12-2"></a>  <span class="bu">std::</span>vector&lt;ElidedTask&lt;<span class="dt">int</span>&gt;&gt; tasks;</span>
<span id="cb12-3"><a href="#cb12-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="cb12-4"><a href="#cb12-4"></a>    tasks.emplace_back(coro_task());</span>
<span id="cb12-5"><a href="#cb12-5"></a>    </span>
<span id="cb12-6"><a href="#cb12-6"></a>  <span class="co">// the semantics of whenAll is that it would</span></span>
<span id="cb12-7"><a href="#cb12-7"></a>  <span class="co">// complete after all the tasks completed.</span></span>
<span id="cb12-8"><a href="#cb12-8"></a>  <span class="kw">co_return</span> <span class="kw">co_await</span> whenAll(<span class="bu">std::</span>move(tasks));</span>
<span id="cb12-9"><a href="#cb12-9"></a>}</span></code></pre></div>
<p>We shouldn’t make <code class="sourceCode default">coro_task()</code> to elide since it would be problematic when we use <code class="sourceCode default">tasks</code>. The reason behind this is that the storage lifetime of elided coroutine would end at the enclosing braces, but it is possible to use after the lifetime ends!</p>
<p>This is unsolvable in the current design. Since the compiler needs to know the stack frame size for each function statically at compile time. But the compiler couldn’t know the value of <code class="sourceCode default">task_count</code> at compile time, so the compiler won’t know the space needed for the function.</p>
<h2 data-number="8.4" id="summary"><span class="header-section-number">8.4</span> Summary<a href="#summary" class="self-link"></a></h2>
<p>The two limitations here exist since the compiler needs to know the callee at compile time. And another limitation exists since the compiler need to know the frame size statically. I think all three are unable to solve.</p>
<p>For the last limitation, I think it is OK to leave it in the future to resolve (if possible). And for the other limitations, I think there are two methods: - Make the program ill-formed once the compiler found the situations. - Define that the <code class="sourceCode default">must_elide</code> doesn’t work for virtual functions and functions in another translation unit explicitly.</p>
<p>I think the latter is better. Although it is constrained, the semantics are well-defined.</p>
<h1 data-number="9" id="implementation"><span class="header-section-number">9</span> Implementation<a href="#implementation" class="self-link"></a></h1>
<p>An implementation could be found here: <a href="https://github.com/ChuanqiXu9/llvm-project/tree/CoroMustElide">CoroMustElide</a>.</p>
<h2 data-number="9.1" id="current-implementation-issue"><span class="header-section-number">9.1</span> Current Implementation issue<a href="#current-implementation-issue" class="self-link"></a></h2>
<p>An issue in the current implementation is that: it could work only if we turned optimization on. In other words, if we define this in the specification, it should work correctly under O0.</p>
<p>The reason is the internal pass ordering problem in the compiler. I definitely believe it could be solved if we get in consensus on this proposal.</p>
<h2 data-number="9.2" id="implementation-in-gcc"><span class="header-section-number">9.2</span> Implementation in GCC<a href="#implementation-in-gcc" class="self-link"></a></h2>
<p>Mathias Stearn points out that there is no coroutine elision optimization in GCC now. So it would be harder to implement this proposal in GCC since it would require GCC to implement coroutine elision first.</p>
<h1 data-number="10" id="qa-and-discussion"><span class="header-section-number">10</span> Q&amp;A and Discussion<a href="#qa-and-discussion" class="self-link"></a></h1>
<p>This section includes the questions had been asked and the corresponding answer/discussion.</p>
<h2 data-number="10.1" id="how-does-the-compiler-guarantee-coroutine-elision-if-the-user-requires-it"><span class="header-section-number">10.1</span> How does the compiler guarantee coroutine elision if the user requires it<a href="#how-does-the-compiler-guarantee-coroutine-elision-if-the-user-requires-it" class="self-link"></a></h2>
<p>Here talks about the case of clang only. Since the question was raised because clang handles coroutine in two parts, the frontend part and the middle end part. But I think this wouldn’t be a problem for GCC since GCC handles coroutine in the frontend.</p>
<p>Simply, the frontend would pass specified information to the middle end after parsing source codes. So when the frontend sees the value of <code class="sourceCode default">promise_type::must_elide</code> evaluates to true or false, the frontend would pass a special marker to the middle end to inform that the coroutine should be elided or not.</p>
<p>Due to that coroutine elision depends on inlining now. So in the case, the middle end sees an <code class="sourceCode default">always-elide</code> marker, it would mark the ramp function as <code class="sourceCode default">always_inline</code> to ensure it could be inlined as expected.</p>
<h2 data-number="10.2" id="is-elided-a-constexpr-function"><span class="header-section-number">10.2</span> Is elided() a constexpr function?<a href="#is-elided-a-constexpr-function" class="self-link"></a></h2>
<p>No, although it looks like a constexpr function. Even its result isn’t a compile time constant. This doesn’t make much sense. Since we could know that the elision is done at compile time. So it is natural that whether or not a coroutine is elided at runtime. But the point here is that we couldn’t associate a coroutine_hadle with a coroutine at compile time. For example:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1"></a>__attribute__<span class="op">((</span>noinline<span class="op">))</span></span>
<span id="cb11-2"><a href="#cb11-2"></a><span class="dt">bool</span> IsElided<span class="op">(</span>coroutine_handle<span class="op">&lt;&gt;</span> handle<span class="op">)</span> <span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3"></a>  <span class="cf">return</span> handle<span class="op">.</span>elided<span class="op">()</span>;</span>
<span id="cb11-4"><a href="#cb11-4"></a><span class="op">}</span></span></code></pre></div>
<p>The compiler couldn’t connect the handle with a coroutine clearly. So it must read information in the memory somewhere. So the result of <code class="sourceCode default">elided()</code> should be a runtime constant. (The runtime constant seems to stand for little in C++.)</p>
<h2 data-number="10.3" id="does-the-semantics-of-guaranteed-copy-elision-matter"><span class="header-section-number">10.3</span> Does the semantics of guaranteed copy-elision matter?<a href="#does-the-semantics-of-guaranteed-copy-elision-matter" class="self-link"></a></h2>
<p>Here is an example:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb12-2"><a href="#cb12-2"></a>ElidedTask<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> foo_impl<span class="op">(</span>Args<span class="op">...</span> args<span class="op">)</span> <span class="op">{</span> <span class="kw">co_await</span> whatever<span class="op">()</span>; <span class="op">...</span> <span class="op">}</span></span>
<span id="cb12-3"><a href="#cb12-3"></a></span>
<span id="cb12-4"><a href="#cb12-4"></a><span class="kw">template</span><span class="op">&lt;</span><span class="kw">typename</span><span class="op">...</span> Args<span class="op">&gt;</span></span>
<span id="cb12-5"><a href="#cb12-5"></a>ElidedTask<span class="op">&lt;</span><span class="dt">void</span><span class="op">&gt;</span> foo<span class="op">(</span>std<span class="op">::</span>tuple<span class="op">&lt;</span>Args<span class="op">&gt;</span> t<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-6"><a href="#cb12-6"></a>  <span class="kw">co_await</span> std<span class="op">::</span>apply<span class="op">([](</span>Args<span class="op">&amp;...</span> args<span class="op">)</span> <span class="op">{</span> <span class="cf">return</span> foo_impl<span class="op">(</span>args<span class="op">...)</span>; <span class="op">}</span>, t<span class="op">)</span>;</span>
<span id="cb12-7"><a href="#cb12-7"></a><span class="op">}</span></span></code></pre></div>
<p>Due to the guaranteed copy-elision, the return value of foo_impl should be constructed directly at the lambda in <code class="sourceCode default">std::apply</code>. Then the question is: how about the coroutine frame? Where does its lifetime end? Is the code above good? Does it violate the rule guaranteed copy-elision?</p>
<p>First, for the current language specification, coroutine elision should be irrelevant to copy-elision. Since coroutine elision talks about whether or not to allocate a coroutine status. And copy-elision talks about where we should construct an object.</p>
<p>The key point here is that an object would never be a coroutine status and coroutine status would never be an object. The object could have pointers that points to the coroutine status. And the coroutine status is a special instance created by the compiler in the middle end. So copy elision wouldn’t affect coroutine elision. Now let’s back to the above example and see what would happen.</p>
<p>When <code class="sourceCode default">foo_impl()</code> got to be elided into the lambda function of <code class="sourceCode default">foo()</code>, the coroutine status of <code class="sourceCode default">foo_impl()</code> would become a local variable in the lambda. So when the lambda exits, the coroutine status would be released. And when <code class="sourceCode default">foo()</code> try to resume the coroutine returned by <code class="sourceCode default">std::apply</code>, it would access the coroutine status, which is a dangling pointer now. So it would crash generally (Or an undefined bahavior).</p>
<p>People may argue that this is not same with the intuition. But I think the overall behavior is well-defined except what would happen finally is an undefined behavior. BTW, the behavior wouldn’t change if <code class="sourceCode default">foo_impl()</code> would be inlined into the lambda.</p>
<p>So the above example is not good. We couldn’t mark the return type for <code class="sourceCode default">foo_impl()</code> as <code class="sourceCode default">ElidedTask&lt;void&gt;</code>.</p>
<h2 data-number="10.4" id="why-must_elide-must-be-a-static-constexpr-member-function"><span class="header-section-number">10.4</span> Why <code class="sourceCode default">must_elide()</code> must be a static constexpr member function?<a href="#why-must_elide-must-be-a-static-constexpr-member-function" class="self-link"></a></h2>
<p>First, <code class="sourceCode default">must_elide()</code> must be a static member function since it determinates how coroutine status would be allocated. Note that the lifetime of the promise is as long as the coroutine status. So we coulnd’t write <code class="sourceCode default">coro_promise.must_elide()</code> since the promise should not be constructed yet!</p>
<p>Second, <code class="sourceCode default">must_elide</code> could be a non-constexpr function in fact. How could it be? For example, if <code class="sourceCode default">must_elide</code> could be a non-constexpr function, the compiler should generate the following code for a coroutine:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode c++"><code class="sourceCode cpp"><span id="cb13-1"><a href="#cb13-1"></a>coroutine_status_ty coroutine_status;</span>
<span id="cb13-2"><a href="#cb13-2"></a>coroutine_handle&lt;<span class="dt">promise_type</span>&gt; <span class="va">handle_</span>;</span>
<span id="cb13-3"><a href="#cb13-3"></a><span class="cf">if</span> (<span class="dt">promise_type</span>::must_elide())</span>
<span id="cb13-4"><a href="#cb13-4"></a>    <span class="va">handle_</span> = &amp;coroutine_status;</span>
<span id="cb13-5"><a href="#cb13-5"></a><span class="cf">else</span></span>
<span id="cb13-6"><a href="#cb13-6"></a>    <span class="va">handle_</span> = <span class="dt">promise_type</span>::<span class="kw">operator</span> <span class="kw">new</span>(size_of(coroutine_status_ty));</span>
<span id="cb13-7"><a href="#cb13-7"></a><span class="co">// use of handle_</span></span></code></pre></div>
<p>Note that in this case we could fill the arguments of the coroutine into <code class="sourceCode default">promise_type::must_elide()</code> if we want.</p>
<p>So why do I didn’t propose this? Here are my reasons: - Cost. - Unimaged usage.</p>
<p>First, for the cost problem, if <code class="sourceCode default">must_elide</code> exists and the compiler couldn’t evaluate it at compile time, there would be an extra stack varaible and condition statement for every coroutine instance. Honestly, the cost is not every expensive to me. Especially, the implementation of <code class="sourceCode default">must_elide()</code> shouldn’t be too complex so the compiler may be possible to eliminate the redundant.</p>
<p>The key reason I didn’t propose this is that I couldn’t find the usage which couldn’t be handled by the constexpr one. This form looks a little bit cooler. But I don’t figure out what users could implement in this form that couldn’t be implemented in constexpr one. So I give up to this one.</p>
<h1 data-number="11" id="conclusion"><span class="header-section-number">11</span> Conclusion<a href="#conclusion" class="self-link"></a></h1>
<p>This proposal proposes two components to coroutine. One called <code class="sourceCode default">must_elide</code> allows the user to control the coroutine elision which is impossible before. One called <code class="sourceCode default">elided</code> allows the user to observe the coroutine elision. For <code class="sourceCode default">must_elide</code>, it gives the rights to user but also the risks. But it is an alternative, user who don’t want to pay the risk could avoid to use it and nothing would change. But there are still cases (indirect call, cross TU and out-of-block use) that couldn’t be handled by <code class="sourceCode default">must_elide</code> , we could only define the condition clearly in the standard. But after all, the situation would be much better. Now people could even control the elision at the callsite. For <code class="sourceCode default">elided</code>, it would require another bit in coroutine frame and an additional store instruction at runtime. And the use cases we found now are in tests only. We need more discussion to make a decision.</p>
<h1 data-number="12" id="acknowledgments"><span class="header-section-number">12</span> Acknowledgments<a href="#acknowledgments" class="self-link"></a></h1>
<p>Many thanks for Lewis Baker, Mathias Stearn and Gašper Ažman reviewing this paper.</p>
</div>
</div>
</body>
</html>
