<!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-10-14" />
  <title>Allow programmer to control and detect coroutine elision by static constexpr bool should\_elide() and coroutine\_handle::elided()</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 by static constexpr bool should_elide() and coroutine_handle::elided()</h1>

<table style="border:none;float:right">
  <tr>
    <td>Document #:</td>
    <td>P2477R0</td>
  </tr>
  <tr>
    <td>Date:</td>
    <td>2021-10-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="#background-and-motivation"><span class="toc-section-number">2</span> Background and Motivation<span></span></a></li>
<li><a href="#terminology"><span class="toc-section-number">3</span> Terminology<span></span></a>
<ul>
<li><a href="#halo-and-coroutine-elision"><span class="toc-section-number">3.1</span> HALO and Coroutine elision<span></span></a></li>
<li><a href="#constexpr-time-and-compile-time"><span class="toc-section-number">3.2</span> constexpr time and compile time<span></span></a></li>
<li><a href="#coroutine-status-and-corotuine-frame"><span class="toc-section-number">3.3</span> Coroutine status and Corotuine frame<span></span></a></li>
<li><a href="#ramp-function"><span class="toc-section-number">3.4</span> Ramp function<span></span></a></li>
</ul></li>
<li><a href="#design-and-example"><span class="toc-section-number">4</span> Design and Example<span></span></a>
<ul>
<li><a href="#should_elide"><span class="toc-section-number">4.1</span> should_elide<span></span></a></li>
<li><a href="#elided"><span class="toc-section-number">4.2</span> elided<span></span></a></li>
<li><a href="#examples"><span class="toc-section-number">4.3</span> Examples<span></span></a>
<ul>
<li><a href="#always-enabledisable-coroutine-elision"><span class="toc-section-number">4.3.1</span> Always Enable/Disable coroutine elision<span></span></a></li>
<li><a href="#no-guarantee-to-coroutine-elision"><span class="toc-section-number">4.3.2</span> No guarantee to coroutine elision<span></span></a></li>
<li><a href="#controlling-the-elision-at-callsite"><span class="toc-section-number">4.3.3</span> Controlling the elision at callsite<span></span></a></li>
</ul></li>
</ul></li>
<li><a href="#issue"><span class="toc-section-number">5</span> Issue<span></span></a></li>
<li><a href="#implementation"><span class="toc-section-number">6</span> Implementation<span></span></a>
<ul>
<li><a href="#current-implementation-issue"><span class="toc-section-number">6.1</span> Current Implementation issue<span></span></a></li>
<li><a href="#implementation-in-gcc"><span class="toc-section-number">6.2</span> Implementation in GCC<span></span></a></li>
</ul></li>
<li><a href="#qa"><span class="toc-section-number">7</span> Q&amp;A<span></span></a>
<ul>
<li><a href="#how-the-compiler-guarantee-coroutine-elision-if-should_elide-evaluates-to-true-at-constexpr-time"><span class="toc-section-number">7.1</span> How the compiler guarantee coroutine elision if should_elide evaluates to true at constexpr time<span></span></a></li>
<li><a href="#is-elided-a-constexpr-function"><span class="toc-section-number">7.2</span> Is elided() a constexpr function?<span></span></a></li>
</ul></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 compiler, programmers couldn’t control coroutine elision in a well-defined way. And there is not a good method that a programmer to use to detect whether the 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 nor introduce any performance overhead.</p>
<h1 data-number="2" id="background-and-motivation"><span class="header-section-number">2</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 stack when the compiler find 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. So programmers couldn’t find a well-defined method to control coroutine elision nor detecting whether the coroutine elision happened. And <code class="sourceCode default">coroutine elision</code> doesn’t like other compiler optimization since programmers could feel strongly about the dynamic allocation. So I feel it is necessary to support it.</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 embedded system. In the resouces-limited system, we could use <code class="sourceCode default">promise_type::operator new</code> to allocate space from a big static-allocated bytes array. And the feature to disable <code class="sourceCode default">coroutine elision</code> is not present in the standard too.</p>
<h1 data-number="3" id="terminology"><span class="header-section-number">3</span> Terminology<a href="#terminology" class="self-link"></a></h1>
<p>This section aims at explaining terminology in this prposal to avoid misunderstanding. This aim is not to discuss the words used in the specification formally. That should be discussed in the wording stage.</p>
<h2 data-number="3.1" id="halo-and-coroutine-elision"><span class="header-section-number">3.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 perfer 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 prefer name later.</p>
<h2 data-number="3.2" id="constexpr-time-and-compile-time"><span class="header-section-number">3.2</span> constexpr time and compile time<a href="#constexpr-time-and-compile-time" class="self-link"></a></h2>
<p>Generally this two term 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 to 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="3.3" id="coroutine-status-and-corotuine-frame"><span class="header-section-number">3.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 all the information the coroutine should keep. And the coroutine frame refers to the data structure the compiler generated to keep the information needed. We could think coroutine frame is a derived class of coroutine status : ).</p>
<h2 data-number="3.4" id="ramp-function"><span class="header-section-number">3.4</span> Ramp function<a href="#ramp-function" class="self-link"></a></h2>
<p>The compiler would split a coroutine function into several part: 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 which would initialize the coroutine frame.</p>
<h1 data-number="4" id="design-and-example"><span class="header-section-number">4</span> Design and Example<a href="#design-and-example" class="self-link"></a></h1>
<p>The design consists of 2 parts. One for controling the elision. One for detecting the elision. Both of them should be easy to understand.</p>
<h2 data-number="4.1" id="should_elide"><span class="header-section-number">4.1</span> should_elide<a href="#should_elide" class="self-link"></a></h2>
<p>If the compiler could find the name <code class="sourceCode default">should_elide</code> in the scope of promise_type, then if the results of <code class="sourceCode default">promise_type::should_elide()</code> evaluates to true at compile time, all the coroutine instance generated from the coroutine function are guaranteed to be elided. Or if the results of <code class="sourceCode default">promise_type::should_elide()</code> evaluates to false at compile time, all the coroutine instance generated from the coroutine function are guaranteed to not be elided. Otherwise, if the compiler couldn’t find <code class="sourceCode default">should_elide</code> in the scope of promise_type or the results of <code class="sourceCode default">promise_type::should_elide</code> couldn’t be evaluated at compile time, the compiler is free to elide every single coroutine instance generated from the coroutine function.</p>
<h2 data-number="4.2" id="elided"><span class="header-section-number">4.2</span> elided<a href="#elided" class="self-link"></a></h2>
<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="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1"></a><span class="kw">namespace</span> std <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2"></a>  <span class="kw">template</span><span class="op">&lt;&gt;</span></span>
<span id="cb2-3"><a href="#cb2-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="cb2-4"><a href="#cb2-4"></a>  <span class="op">{</span> </span>
<span id="cb2-5"><a href="#cb2-5"></a>    <span class="co">// ...</span></span>
<span id="cb2-6"><a href="#cb2-6"></a>    <span class="co">// [coroutine.handle.observers], observers</span></span>
<span id="cb2-7"><a href="#cb2-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="cb2-8"><a href="#cb2-8"></a>    <span class="dt">bool</span> done<span class="op">()</span> <span class="kw">const</span>;</span>
<span id="cb2-9"><a href="#cb2-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="cb2-10"><a href="#cb2-10"></a>    <span class="co">// ...</span></span>
<span id="cb2-11"><a href="#cb2-11"></a>  <span class="op">}</span>;</span>
<span id="cb2-12"><a href="#cb2-12"></a></span>
<span id="cb2-13"><a href="#cb2-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="cb2-14"><a href="#cb2-14"></a>  <span class="kw">struct</span> coroutine_handle</span>
<span id="cb2-15"><a href="#cb2-15"></a>  <span class="op">{</span></span>
<span id="cb2-16"><a href="#cb2-16"></a>    <span class="co">// ...</span></span>
<span id="cb2-17"><a href="#cb2-17"></a>    <span class="co">// [coroutine.handle.observers], observers</span></span>
<span id="cb2-18"><a href="#cb2-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="cb2-19"><a href="#cb2-19"></a>    <span class="dt">bool</span> done<span class="op">()</span> <span class="kw">const</span>;</span>
<span id="cb2-20"><a href="#cb2-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="cb2-21"><a href="#cb2-21"></a>        <span class="co">// ...</span></span>
<span id="cb2-22"><a href="#cb2-22"></a>  <span class="op">}</span>;</span>
<span id="cb2-23"><a href="#cb2-23"></a><span class="op">}</span></span></code></pre></div>
<p>And the semantic is 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> would return false.</p>
<h2 data-number="4.3" id="examples"><span class="header-section-number">4.3</span> Examples<a href="#examples" class="self-link"></a></h2>
<p>A complete example could be find at: https://github.com/ChuanqiXu9/llvm-project/blob/CoroShouldElide/ShouldElide/ShouldElide.cpp</p>
<h3 data-number="4.3.1" id="always-enabledisable-coroutine-elision"><span class="header-section-number">4.3.1</span> Always Enable/Disable coroutine elision<a href="#always-enabledisable-coroutine-elision" class="self-link"></a></h3>
<p>We could enable/disable coroutine elision for a kind of coroutine like:</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">struct</span> TaskPromiseAlwaysElide : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb1-2"><a href="#cb1-2"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> should_elide() {</span>
<span id="cb1-3"><a href="#cb1-3"></a>        <span class="cf">return</span> <span class="kw">true</span>;</span>
<span id="cb1-4"><a href="#cb1-4"></a>    }</span>
<span id="cb1-5"><a href="#cb1-5"></a>};</span>
<span id="cb1-6"><a href="#cb1-6"></a></span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="kw">struct</span> TaskPromiseNeverElide : <span class="kw">public</span> TaskPromiseBase {</span>
<span id="cb1-8"><a href="#cb1-8"></a>    <span class="at">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> should_elide() {</span>
<span id="cb1-9"><a href="#cb1-9"></a>        <span class="cf">return</span> <span class="kw">false</span>;</span>
<span id="cb1-10"><a href="#cb1-10"></a>    }</span>
<span id="cb1-11"><a href="#cb1-11"></a>};</span></code></pre></div>
<p>Then for the coroutine like:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-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="cb3-2"><a href="#cb3-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="cb3-3"><a href="#cb3-3"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb3-4"><a href="#cb3-4"></a>    <span class="kw">using</span> promise_type <span class="op">=</span> PromiseType;</span>
<span id="cb3-5"><a href="#cb3-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="cb3-6"><a href="#cb3-6"></a>    <span class="co">// ...</span></span>
<span id="cb3-7"><a href="#cb3-7"></a><span class="op">}</span>; <span class="co">// end of Task</span></span>
<span id="cb3-8"><a href="#cb3-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="cb3-9"><a href="#cb3-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="cb3-10"><a href="#cb3-10"></a></span>
<span id="cb3-11"><a href="#cb3-11"></a>AlwaysElideTask always_elide_task <span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-12"><a href="#cb3-12"></a>    <span class="co">// ... contain coroutine keywords</span></span>
<span id="cb3-13"><a href="#cb3-13"></a><span class="op">}</span></span>
<span id="cb3-14"><a href="#cb3-14"></a>NeverElideTask never_elide_task <span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-15"><a href="#cb3-15"></a>    <span class="co">// ... contain coroutine keywords</span></span>
<span id="cb3-16"><a href="#cb3-16"></a><span class="op">}</span></span></code></pre></div>
<p>Then every coroutine instance generated from <code class="sourceCode default">always_elide_task()</code> would be elided. And every coroutine instance generated from <code class="sourceCode default">never_elide_task</code> wouldn’t be elided.</p>
<h3 data-number="4.3.2" id="no-guarantee-to-coroutine-elision"><span class="header-section-number">4.3.2</span> No guarantee to coroutine elision<a href="#no-guarantee-to-coroutine-elision" class="self-link"></a></h3>
<p>If the compiler couldn’t infer the result of <code class="sourceCode default">promise_type::should_elide</code> at compile time, then compiler is free to do elision or not. And the behavior would be the same with <code class="sourceCode default">should_elide</code> is not existed in <code class="sourceCode default">promise_type</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="dt">bool</span> undertermism;</span>
<span id="cb4-2"><a href="#cb4-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="cb4-3"><a href="#cb4-3"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> should_elide<span class="op">()</span> <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4"></a>        <span class="cf">return</span> undertermism;</span>
<span id="cb4-5"><a href="#cb4-5"></a>    <span class="op">}</span></span>
<span id="cb4-6"><a href="#cb4-6"></a><span class="op">}</span>;</span></code></pre></div>
<p>Then <code class="sourceCode default">TaskPromiseMeaningless</code> would be the same with <code class="sourceCode default">TaskPromiseBase</code> for every case.</p>
<h3 data-number="4.3.3" id="controlling-the-elision-at-callsite"><span class="header-section-number">4.3.3</span> Controlling the elision at callsite<a href="#controlling-the-elision-at-callsite" class="self-link"></a></h3>
<p>It is possible that we could control coroutine elision for specific call. Here is my demo,</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">using</span> NormalTask <span class="op">=</span> Task<span class="op">&lt;</span>TaskPromiseBase<span class="op">&gt;</span>;</span>
<span id="cb5-2"><a href="#cb5-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="cb5-3"><a href="#cb5-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="cb5-4"><a href="#cb5-4"></a></span>
<span id="cb5-5"><a href="#cb5-5"></a><span class="kw">struct</span> ShouldElideTagT;</span>
<span id="cb5-6"><a href="#cb5-6"></a><span class="kw">struct</span> NoElideTagT;</span>
<span id="cb5-7"><a href="#cb5-7"></a><span class="kw">struct</span> MayElideTagT;</span>
<span id="cb5-8"><a href="#cb5-8"></a></span>
<span id="cb5-9"><a href="#cb5-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="cb5-10"><a href="#cb5-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="cb5-11"><a href="#cb5-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="cb5-12"><a href="#cb5-12"></a>                    std<span class="op">::</span>same_as<span class="op">&lt;</span>T, MayElideTagT<span class="op">&gt;)</span>;</span>
<span id="cb5-13"><a href="#cb5-13"></a></span>
<span id="cb5-14"><a href="#cb5-14"></a><span class="dt">bool</span> undetermism;</span>
<span id="cb5-15"><a href="#cb5-15"></a><span class="kw">template</span> <span class="op">&lt;</span>ElideTag Tag<span class="op">&gt;</span></span>
<span id="cb5-16"><a href="#cb5-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="cb5-17"><a href="#cb5-17"></a>    <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> should_elide<span class="op">()</span> <span class="op">{</span></span>
<span id="cb5-18"><a href="#cb5-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="cb5-19"><a href="#cb5-19"></a>            <span class="cf">return</span> <span class="kw">true</span>;</span>
<span id="cb5-20"><a href="#cb5-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="cb5-21"><a href="#cb5-21"></a>            <span class="cf">return</span> <span class="kw">false</span>;</span>
<span id="cb5-22"><a href="#cb5-22"></a>        <span class="cf">else</span></span>
<span id="cb5-23"><a href="#cb5-23"></a>            <span class="cf">return</span> undetermism;</span>
<span id="cb5-24"><a href="#cb5-24"></a>    <span class="op">}</span></span>
<span id="cb5-25"><a href="#cb5-25"></a><span class="op">}</span>;</span>
<span id="cb5-26"><a href="#cb5-26"></a></span>
<span id="cb5-27"><a href="#cb5-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="cb5-28"><a href="#cb5-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="cb5-29"><a href="#cb5-29"></a></span>
<span id="cb5-30"><a href="#cb5-30"></a><span class="kw">template</span> <span class="op">&lt;</span>ElideTag Tag<span class="op">&gt;</span></span>
<span id="cb5-31"><a href="#cb5-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="cb5-32"><a href="#cb5-32"></a></span>
<span id="cb5-33"><a href="#cb5-33"></a><span class="dt">int</span> foo<span class="op">()</span> <span class="op">{</span></span>
<span id="cb5-34"><a href="#cb5-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="cb5-35"><a href="#cb5-35"></a>  <span class="co">// Task::elided would call coroutine_handle::elided</span></span>
<span id="cb5-36"><a href="#cb5-36"></a>  <span class="ot">assert</span> <span class="op">(</span>t1<span class="op">.</span>elided<span class="op">())</span>;</span>
<span id="cb5-37"><a href="#cb5-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="cb5-38"><a href="#cb5-38"></a>  <span class="ot">assert</span> <span class="op">(!</span>t1<span class="op">.</span>elided<span class="op">())</span>;</span>
<span id="cb5-39"><a href="#cb5-39"></a>  <span class="co">// The default case, which would be general for most cases</span></span>
<span id="cb5-40"><a href="#cb5-40"></a>  alternative_task<span class="op">()</span>; </span>
<span id="cb5-41"><a href="#cb5-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 coroutines in a project.</p>
<h1 data-number="5" id="issue"><span class="header-section-number">5</span> Issue<a href="#issue" class="self-link"></a></h1>
<p>A big issue I see now is that we couldn’t elide a coroutine into a function in another translation unit. Let’s see the example:</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="co">// CoroType.h</span></span>
<span id="cb6-2"><a href="#cb6-2"></a><span class="kw">class</span> PromiseType <span class="op">{</span></span>
<span id="cb6-3"><a href="#cb6-3"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb6-4"><a href="#cb6-4"></a>  <span class="kw">static</span> <span class="kw">constexpr</span> <span class="dt">bool</span> should_elide<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-5"><a href="#cb6-5"></a>     <span class="cf">return</span> <span class="kw">true</span>;</span>
<span id="cb6-6"><a href="#cb6-6"></a>  <span class="op">}</span></span>
<span id="cb6-7"><a href="#cb6-7"></a>  <span class="co">// ...</span></span>
<span id="cb6-8"><a href="#cb6-8"></a><span class="op">}</span>;</span>
<span id="cb6-9"><a href="#cb6-9"></a><span class="kw">class</span> AlwaysElideTask <span class="op">{</span></span>
<span id="cb6-10"><a href="#cb6-10"></a>  <span class="kw">using</span> promise_type <span class="op">=</span> PromiseType;</span>
<span id="cb6-11"><a href="#cb6-11"></a>  <span class="co">// ...</span></span>
<span id="cb6-12"><a href="#cb6-12"></a><span class="op">}</span>;</span>
<span id="cb6-13"><a href="#cb6-13"></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="cb6-14"><a href="#cb6-14"></a>                                           <span class="co">// from the signature.</span></span>
<span id="cb6-15"><a href="#cb6-15"></a><span class="co">// A.cpp</span></span>
<span id="cb6-16"><a href="#cb6-16"></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="cb6-17"><a href="#cb6-17"></a>  <span class="kw">co_await</span> std<span class="op">::</span>suspend_always<span class="op">{}</span>;</span>
<span id="cb6-18"><a href="#cb6-18"></a>  <span class="co">// ... Other works needed.</span></span>
<span id="cb6-19"><a href="#cb6-19"></a><span class="op">}</span></span>
<span id="cb6-20"><a href="#cb6-20"></a><span class="co">// B.cpp</span></span>
<span id="cb6-21"><a href="#cb6-21"></a>AlwaysElideTask CoroA<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-22"><a href="#cb6-22"></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="cb6-23"><a href="#cb6-23"></a><span class="op">}</span></span></code></pre></div>
<p>The example above shows the issue directly. We couldn’t elide a coroutine defined in another translation unit since we couldn’t know if it is coroutine even!</p>
<p>Could we solve this problem by adding specicial function attribute to mark a function is a coroutine? No, we can’t. Since at least we need to know how many bits we need to allocate the coroutine status if we want to do elide. And compiler couldn’t infer that if it coulnd’t see the body of the coroutine. The key point here is that the compiler would compile translation unit one by one and the compiler couldn’t see the intents from other translation unit.</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 is no possible that we could add the concept LTO in the standard nor we could change the compilation model.</p>
<p>So this problem is unsolvable if we didn’t change the complation model. So the only solution I could image is that we constrain the semantics of <code class="sourceCode default">should_elide</code> by specifiying that we couldn’t elide a coroutine in another translation unit explicitly in the standard.</p>
<h1 data-number="6" id="implementation"><span class="header-section-number">6</span> Implementation<a href="#implementation" class="self-link"></a></h1>
<p>A implementation could be found here: https://github.com/ChuanqiXu9/llvm-project/tree/CoroShouldElide</p>
<h2 data-number="6.1" id="current-implementation-issue"><span class="header-section-number">6.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 corrrectly 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 consensus on this proposal.</p>
<h2 data-number="6.2" id="implementation-in-gcc"><span class="header-section-number">6.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="7" id="qa"><span class="header-section-number">7</span> Q&amp;A<a href="#qa" class="self-link"></a></h1>
<p>This section includes question had been asked and the corresponding answer.</p>
<h2 data-number="7.1" id="how-the-compiler-guarantee-coroutine-elision-if-should_elide-evaluates-to-true-at-constexpr-time"><span class="header-section-number">7.1</span> How the compiler guarantee coroutine elision if should_elide evaluates to true at constexpr time<a href="#how-the-compiler-guarantee-coroutine-elision-if-should_elide-evaluates-to-true-at-constexpr-time" class="self-link"></a></h2>
<p>Here talks about the case of clang only. Since the question 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 to 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 see the value of <code class="sourceCode default">promise_type::should_elide</code> evaluates to true or false, the frontend would pass specific 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 middle end see a <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="7.2" id="is-elided-a-constexpr-function"><span class="header-section-number">7.2</span> Is elided() a constexpr function?<a href="#is-elided-a-constexpr-function" class="self-link"></a></h2>
<p>No. Although elided() is evaluated at compile time, it is not evaluated at constexpr time. For example, the following function would print “elided is not constexpr function” all the way:</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">class</span> Coro <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2"></a>  Coro<span class="op">(</span>coroutine_handle<span class="op">&lt;&gt;</span> handle<span class="op">)</span> <span class="op">:</span> handle<span class="op">(</span>handle<span class="op">)</span> <span class="op">{}</span></span>
<span id="cb7-3"><a href="#cb7-3"></a>  <span class="dt">bool</span> elided<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-4"><a href="#cb7-4"></a>    <span class="cf">if</span> <span class="kw">constexpr</span><span class="op">(</span>handle<span class="op">.</span>elided<span class="op">())</span></span>
<span id="cb7-5"><a href="#cb7-5"></a>      std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;elided is constexpr function.</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb7-6"><a href="#cb7-6"></a>    <span class="cf">else</span></span>
<span id="cb7-7"><a href="#cb7-7"></a>      std<span class="op">::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;elided is not constexpr function.</span><span class="sc">\n</span><span class="st">&quot;</span>;</span>
<span id="cb7-8"><a href="#cb7-8"></a>  <span class="op">}</span></span>
<span id="cb7-9"><a href="#cb7-9"></a><span class="kw">private</span><span class="op">:</span></span>
<span id="cb7-10"><a href="#cb7-10"></a>  coroutine_handle<span class="op">&lt;&gt;</span> handle;</span>
<span id="cb7-11"><a href="#cb7-11"></a><span class="op">}</span>;</span></code></pre></div>
</div>
</div>
</body>
</html>
