<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
  <meta charset="utf-8" />
  <meta name="generator" content="pandoc" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
  <title>P1494R5: Partial program correctness</title>
  <style>
    html {
      color: #1a1a1a;
      background-color: #fdfdfd;
    }
    body {
      margin: 0 auto;
      max-width: 36em;
      padding-left: 50px;
      padding-right: 50px;
      padding-top: 50px;
      padding-bottom: 50px;
      hyphens: auto;
      overflow-wrap: break-word;
      text-rendering: optimizeLegibility;
      font-kerning: normal;
    }
    @media (max-width: 600px) {
      body {
        font-size: 0.9em;
        padding: 12px;
      }
      h1 {
        font-size: 1.8em;
      }
    }
    @media print {
      html {
        background-color: white;
      }
      body {
        background-color: transparent;
        color: black;
        font-size: 12pt;
      }
      p, h2, h3 {
        orphans: 3;
        widows: 3;
      }
      h2, h3, h4 {
        page-break-after: avoid;
      }
    }
    p {
      margin: 1em 0;
    }
    a {
      color: #0645ad;
    }
    a:visited {
      color: #0645ad;
    }
    img {
      max-width: 100%;
    }
    svg {
      height: auto;
      max-width: 100%;
    }
    h1, h2, h3, h4, h5, h6 {
      margin-top: 1.4em;
    }
    h5, h6 {
      font-size: 1em;
      font-style: italic;
    }
    h6 {
      font-weight: normal;
    }
    ol, ul {
      padding-left: 1.7em;
      margin-top: 1em;
    }
    li > ol, li > ul {
      margin-top: 0;
    }
    blockquote {
      margin: 1em 0 1em 1.7em;
      padding-left: 1em;
      border-left: 2px solid #e6e6e6;
      color: #606060;
    }
    code {
      font-family: Menlo, Monaco, Consolas, 'Lucida Console', monospace;
      font-size: 85%;
      margin: 0;
      hyphens: manual;
    }
    pre {
      margin: 1em 0;
      overflow: auto;
    }
    pre code {
      padding: 0;
      overflow: visible;
      overflow-wrap: normal;
    }
    .sourceCode {
     background-color: transparent;
     overflow: visible;
    }
    hr {
      background-color: #1a1a1a;
      border: none;
      height: 1px;
      margin: 1em 0;
    }
    table {
      margin: 1em 0;
      border-collapse: collapse;
      width: 100%;
      overflow-x: auto;
      display: block;
      font-variant-numeric: lining-nums tabular-nums;
    }
    table caption {
      margin-bottom: 0.75em;
    }
    tbody {
      margin-top: 0.5em;
      border-top: 1px solid #1a1a1a;
      border-bottom: 1px solid #1a1a1a;
    }
    th {
      border-top: 1px solid #1a1a1a;
      padding: 0.25em 0.5em 0.25em 0.5em;
    }
    td {
      padding: 0.125em 0.5em 0.25em 0.5em;
    }
    header {
      margin-bottom: 4em;
      text-align: center;
    }
    #TOC li {
      list-style: none;
    }
    #TOC ul {
      padding-left: 1.3em;
    }
    #TOC > ul {
      padding-left: 0;
    }
    #TOC a:not(:hover) {
      text-decoration: none;
    }
    code{white-space: pre-wrap;}
    span.smallcaps{font-variant: small-caps;}
    div.columns{display: flex; gap: min(4vw, 1.5em);}
    div.column{flex: auto; overflow-x: auto;}
    div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
    /* The extra [class] is a hack that increases specificity enough to
       override a similar rule in reveal.js */
    ul.task-list[class]{list-style: none;}
    ul.task-list li input[type="checkbox"] {
      font-size: inherit;
      width: 0.8em;
      margin: 0 0.8em 0.2em -1.6em;
      vertical-align: middle;
    }
    .display.math{display: block; text-align: center; margin: 0.5rem auto;}
    /* CSS for syntax highlighting */
    html { -webkit-text-size-adjust: 100%; }
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { display: inline-block; line-height: 1.25; }
    pre > code.sourceCode > span:empty { height: 1.2em; }
    .sourceCode { overflow: visible; }
    code.sourceCode > span { color: inherit; text-decoration: inherit; }
    div.sourceCode { margin: 1em 0; }
    pre.sourceCode { margin: 0; }
    @media screen {
    div.sourceCode { overflow: auto; }
    }
    @media print {
    pre > code.sourceCode { white-space: pre-wrap; }
    pre > code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
    }
    pre.numberSource code
      { counter-reset: source-line 0; }
    pre.numberSource code > span
      { position: relative; left: -4em; counter-increment: source-line; }
    pre.numberSource code > span > a:first-child::before
      { content: counter(source-line);
        position: relative; left: -1em; text-align: right; vertical-align: baseline;
        border: none; display: inline-block;
        -webkit-touch-callout: none; -webkit-user-select: none;
        -khtml-user-select: none; -moz-user-select: none;
        -ms-user-select: none; user-select: none;
        padding: 0 4px; width: 4em;
        color: #aaaaaa;
      }
    pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa;  padding-left: 4px; }
    div.sourceCode
      {   }
    @media screen {
    pre > code.sourceCode > span > a:first-child::before { text-decoration: underline; }
    }
    code span.al { color: #ff0000; font-weight: bold; } /* Alert */
    code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
    code span.at { color: #7d9029; } /* Attribute */
    code span.bn { color: #40a070; } /* BaseN */
    code span.bu { color: #008000; } /* BuiltIn */
    code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
    code span.ch { color: #4070a0; } /* Char */
    code span.cn { color: #880000; } /* Constant */
    code span.co { color: #60a0b0; font-style: italic; } /* Comment */
    code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
    code span.do { color: #ba2121; font-style: italic; } /* Documentation */
    code span.dt { color: #902000; } /* DataType */
    code span.dv { color: #40a070; } /* DecVal */
    code span.er { color: #ff0000; font-weight: bold; } /* Error */
    code span.ex { } /* Extension */
    code span.fl { color: #40a070; } /* Float */
    code span.fu { color: #06287e; } /* Function */
    code span.im { color: #008000; font-weight: bold; } /* Import */
    code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
    code span.kw { color: #007020; font-weight: bold; } /* Keyword */
    code span.op { color: #666666; } /* Operator */
    code span.ot { color: #007020; } /* Other */
    code span.pp { color: #bc7a00; } /* Preprocessor */
    code span.sc { color: #4070a0; } /* SpecialChar */
    code span.ss { color: #bb6688; } /* SpecialString */
    code span.st { color: #4070a0; } /* String */
    code span.va { color: #19177c; } /* Variable */
    code span.vs { color: #4070a0; } /* VerbatimString */
    code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
  </style>
  <style type="text/css">
  div.del, section.del { background: #fdd; }
  div.ins, section.ins { background: #cfb; }
  .mv { background: #ddf; }
  s, .del { background: #faa; margin-right: 0.15ch; }
  u, .ins { background: #afa; }
  /* from https://stackoverflow.com/a/32456613 */
  div > blockquote, body > blockquote {
      display: list-item;
      list-style-type: "- ";
  }
  /* With a 3 em gutter and two columns, ANSI letter is 127 characters wide. */
  pre {
  		margin-left: 1.2em;
  }
  .tony {
    border-collapse: collapse;
  }
  .tony > tbody > tr {
    vertical-align: top;
  }
  tr.hr, tr.hr {
    border-bottom: thin solid #60a0b0;  /* relies on collapse */
  }
  </style>
</head>
<body>
<header id="title-block-header">
<h1 class="title">P1494R5: Partial program
correctness<!-- -*- c++-md -*- --></h1>
</header>
<p><em>Audience</em>: CWG; LWG<br />
S. Davis Herring &lt;<a href="mailto:herring@lanl.gov"
class="email">herring@lanl.gov</a>&gt;<br />
Los Alamos National Laboratory<br />
February 14, 2025</p>
<h1 id="history">History</h1>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1494r4.html">r4</a>:</p>
<ul>
<li>Rebased onto N5001</li>
<li>Moved to <code>&lt;utility&gt;</code></li>
<li>Extended wording to cover more I/O cases</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1494r3.html">r3</a>:</p>
<ul>
<li>Rebased onto N4988</li>
<li>Made I/O automatically a checkpoint</li>
<li>Added missing erroneous behavior context</li>
<li>Added C cross reference for host environment</li>
<li>Removed outdated note</li>
<li>Clarified, simplified, and corrected wording</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1494r2.html">r2</a>:</p>
<ul>
<li>Rebased onto N4981</li>
<li>Required freestanding support, per LEWG review</li>
<li>Clarified implementability</li>
<li>Mentioned implicit checkpoint alternative</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1494r1.html">r1</a>:</p>
<ul>
<li>Discussed the preexisting problem of input dependence</li>
<li>Discussed interactions with other kinds of observable behavior</li>
<li>Discussed lack of debugger support</li>
<li>Fixed (preexisting) “first” in wording based on SG1 review</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1494r0.html">r0</a>:</p>
<ul>
<li>Made introduction explicitly independent of contracts</li>
<li>Fixed conditionals in example</li>
</ul>
<h1 id="problem">Problem</h1>
<p>Undefined behavior enables and extends many important optimizations
(<em>e.g.</em>, simplifying signed integer arithmetic and dead-code
elimination). The “time travel” aspect of such optimizations (explicitly
authorized by [intro.abstract]/5) is surprising to many programmers in
that it can sometimes eliminate tests meant to detect the invalid
operation in question. In particular, consider</p>
<div class="sourceCode" id="cb1"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="pp">#include</span><span class="im">&lt;cstdio&gt;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="pp">#include</span><span class="im">&lt;cstdlib&gt;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="at">static</span> <span class="dt">void</span> bad<span class="op">(</span><span class="at">const</span> <span class="dt">char</span> <span class="op">*</span>msg<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>fputs<span class="op">(</span>msg<span class="op">,</span> stderr<span class="op">);</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="pp">#ifdef DIE</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>abort<span class="op">();</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="pp">#endif</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> inc<span class="op">(</span><span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span><span class="op">(!</span>p<span class="op">)</span> bad<span class="op">(</span><span class="st">&quot;Null!</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">++*</span>p<span class="op">;</span></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Without <code>-DDIE</code>, a conforming implementation can elide the
test in <code>inc</code> entirely: <code>std::fputs</code> always
returns, so any call <code>inc(nullptr)</code> is guaranteed to have
undefined behavior and need not call <code>bad</code>. (Note that
current implementations do not do so in this case.)</p>
<p>This issue came up again near the beginning of 2019 in the discussion
of contracts:</p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> f<span class="op">(</span><span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> <span class="op">[[</span><span class="at">expects</span><span class="op">:</span><span class="at"> p</span><span class="op">]]</span> <span class="op">[[</span><span class="at">expects</span><span class="op">:</span><span class="at"> </span><span class="op">*</span><span class="at">p</span><span class="op">&lt;</span><span class="dv">5</span><span class="op">]];</span></span></code></pre></div>
<p>Discomfort with the idea that (with continuation mode “on”) the first
<em>contract-attribute-specifier</em> might be elided because of the
second was one of the motivations for the many late proposals to change
(and eventually remove) contracts. <a
href="http://wiki.edg.com/pub/Wg21sandiego2018/EvolutionWorkingGroup/p1343-contracts-update.html">Many</a>
<a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1429r0.pdf#subsection.A.1">wondered</a>
about the possibility of making a contract violation handler “opaque to
optimization”, so that the first precondition must be checked on the
supposition that the handler might not return (but rather throw or
terminate).</p>
<p>The capability of establishing such a “checkpoint”, where subsequent
program behavior, <em>even if undefined</em>, does not affect the
preceding behavior, would be useful in general for purposes of stability
and debugging.</p>
<p>There is already an analogous issue concerning program input: clearly
a program can have undefined behavior for some inputs and not others.
[intro.abstract]/3 and /5 acknowledge this by referring to “a given
input” and “that input”, but such a monolithic approach neglects the
paradoxical possibility of input correlated with, say, unspecified
quantities:</p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> x<span class="op">[</span><span class="dv">1</span><span class="op">];</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>uintptr_t a<span class="op">=</span><span class="kw">reinterpret_cast</span><span class="op">&lt;</span><span class="bu">std::</span>uintptr_t<span class="op">&gt;(</span>x<span class="op">)%</span><span class="dv">3</span><span class="op">+</span><span class="dv">1</span><span class="op">,</span>b<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cerr <span class="op">&lt;&lt;</span> <span class="st">&quot;The car is behind door number &quot;</span> <span class="op">&lt;&lt;</span> a</span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>              <span class="op">&lt;&lt;</span> <span class="st">&quot;.  Door to open: &quot;</span><span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cin <span class="op">&gt;&gt;</span> b<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> x<span class="op">[</span>a<span class="op">-</span>b<span class="op">];</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This program might suggest 2 (if the cast yields, say, 0x1000), but
one could absurdly argue that responding with 2 leads to undefined
behavior because almost all of the unspecified possibilities for the
“address” of <code>x</code> (<em>e.g.</em>, 0x2000) lead to undefined
behavior for that input. An OOTA interpretation is also available:
“<code>a</code> is really 3, so the input 2 leads to undefined behavior,
whose effect is to print 2 instead of <code>a</code>”. We should reject
this on the grounds of causality, since we require that undefined
behavior respect input which can quite reasonably depend on
<em>prior</em> output.</p>
<p>The standard does not have the necessary notion of <em>prior</em>,
and this paper does not address this situation, but its checkpoints may
be used to strengthen the guarantee of /7.3, that prompts are delivered
before waiting for input, to include all observable behavior. This usage
does exclude the OOTA interpretation: the output delivered to the host
environment must accurately represent <code>a</code> if (as is
automatically true) it is separated by a checkpoint from any potential
undefined behavior.</p>
<h1 id="previous-work">Previous work</h1>
<p>I <a
href="http://lists.isocpp.org/ext/2018/11/6427.php">suggested</a> a
trick involving a <code>volatile</code> variable, based on the idea that
a volatile read is <em>observable behavior</em> ([intro.abstract]/7)
that must be preserved by optimization.</p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">inline</span> <span class="dt">void</span> log<span class="op">(</span><span class="at">const</span> <span class="dt">char</span> <span class="op">*</span>msg<span class="op">)</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="op">{</span><span class="bu">std::</span>fputs<span class="op">(</span>msg<span class="op">,</span> stderr<span class="op">);}</span>    <span class="co">// always returns</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="dt">bool</span> on_fire<span class="op">()</span> <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  <span class="at">static</span> <span class="at">volatile</span> <span class="dt">bool</span> fire<span class="op">;</span>  <span class="co">// always false</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> fire<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> f<span class="op">(</span><span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>p <span class="op">==</span> <span class="kw">nullptr</span><span class="op">)</span> log<span class="op">(</span><span class="st">&quot;bad thing 1&quot;</span><span class="op">);</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(</span>on_fire<span class="op">())</span> <span class="bu">std::</span>abort<span class="op">();</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(*</span>p <span class="op">&gt;=</span> <span class="dv">5</span><span class="op">)</span> log<span class="op">(</span><span class="st">&quot;bad thing 2&quot;</span><span class="op">);</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The idea is that the compiler cannot assume that
<code>on_fire()</code> returns <code>false</code>, and so the check for
<code>p</code> being null cannot be eliminated. However, the compiler
can observe that, if <code>p</code> is null, the behavior will be
undefined <em>unless</em> <code>on_fire()</code> returns
<code>true</code>, and so it can elide that check (though not the
volatile read) and call <code>abort()</code>. This therefore seems to
convey a certain capability of <em>observing</em> the upcoming undefined
behavior without actually experiencing it.</p>
<p>Unfortunately, conforming implementations are not constrained to
follow this analysis. It is logically necessary that the implementation
perform the observable volatile read unless it can somehow obtain its
result otherwise. However, after reading the value <code>false</code>
(as of course it will be in practice) the implementation may take any
action whatsoever, even “undoing” the call to <code>log</code>. For
example, it would be permissible to perform the implicit flush for
<code>stderr</code> only just before the call to <code>std::abort</code>
(which never happens). One might hope for the implementation to allow
for the possibility that <code>log</code> affects some hardware state
that affects the volatile read, but it might not as such a scheme would
require support from the operating system.</p>
<h1 id="general-solution">General solution</h1>
<p>We can instead introduce a special library function</p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">namespace</span> std <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="co">// in &lt;utility&gt;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> observable<span class="op">()</span> <span class="kw">noexcept</span><span class="op">;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>that divides the program’s execution into <em>epochs</em>, each of
which has its own observable behavior. If any epoch completes without
undefined behavior occurring, the implementation is required to exhibit
the epoch’s observable behavior. Ending an epoch is nonetheless distinct
from ending the program: for example, there is no automatic flushing of
<code>&lt;cstdio&gt;</code> streams.</p>
<p>Undefined behavior in one epoch may obscure the observable behavior
of a previous epoch (for example, by re-opening an output file), but
external mechanisms such as pipes to a logging process can be used to
guarantee receipt of an epoch’s output. With multiple threads, it is not
the epochs themselves that are meaningful but their boundaries (or
<em>checkpoints</em>); normal thread synchronization is required for the
observable behavior of one thread to be included in an checkpoint
defined by another.</p>
<p>As a practical matter, a compiler can implement
<code>std::observable</code> efficiently as an intrinsic that counts as
a possible termination, which the optimizer thus cannot remove. After
optimization (including any link-time optimization), the code generator
can then produce zero machine instructions for it. In Tokyo, some
concerns were raised regarding implementability of this proposal,
perhaps because of what then appeared to be dependence on the library
function; the wording has been adjusted to avoid that implication.
Discussions there and since with Michael Spencer suggest that the
implementation strategy outlined in this paragraph is practicable.</p>
<p>Note that <code>std::observable</code> does not itself constitute
observable behavior, and it does not forgive infinite non-trivial empty
loops ([intro.progress]/1). There is no explicit connection to volatile
access, but the ordinary happens-before rules apply (as much as possible
given the vacuous [intro.abstract]/7.1). Finally, there is no guarantee
that, for instance, local variables have been spilled to registers at
each checkpoint: <code>std::observable</code> prevents certain program
reorderings, but it is not a general aid for comprehensibility when
using a debugger. (In general, it is difficult if not impossible to
specify semantics that allow optimization and yet behave correctly when
presented with input from a user equipped with a debugger.)</p>
<h2 id="limited-assumptions">Limited assumptions</h2>
A call to <code>std::observable</code> prevents the propagation of
assumptions based on the potential for undefined behavior after it into
code before it. The following functions (ignoring for illustration the
implicit checkpoints associated with the I/O functions) offer the same
opportunities for dead-code elimination:
<table>
<tr style="vertical-align: top">
<td>
<div class="sourceCode" id="cb6"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> a<span class="op">(</span><span class="dt">int</span> <span class="op">&amp;</span>r<span class="op">,</span> <span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>p<span class="op">)</span> <span class="bu">std::</span>fprintf<span class="op">(</span>stderr<span class="op">,</span> <span class="st">&quot;count: </span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> <span class="op">++</span>r<span class="op">);</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>p<span class="op">)</span> <span class="bu">std::</span>abort<span class="op">();</span>  <span class="co">// henceforth, p is known to be non-null</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>p<span class="op">)</span> <span class="bu">std::</span>fprintf<span class="op">(</span>stderr<span class="op">,</span> <span class="st">&quot;p is null</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</td>
<td style="width: 5em">
</td>
<td>
<div class="sourceCode" id="cb7"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> b<span class="op">(</span><span class="dt">int</span> <span class="op">&amp;</span>r<span class="op">,</span> <span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>p<span class="op">)</span> <span class="bu">std::</span>fprintf<span class="op">(</span>stderr<span class="op">,</span> <span class="st">&quot;count: </span><span class="sc">%d\n</span><span class="st">&quot;</span><span class="op">,</span> <span class="op">++</span>r<span class="op">);</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>observable<span class="op">();</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span> <span class="op">(!</span>p<span class="op">)</span> <span class="bu">std::</span>fprintf<span class="op">(</span>stderr<span class="op">,</span> <span class="st">&quot;p is null</span><span class="sc">\n</span><span class="st">&quot;</span><span class="op">);</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">*</span>p <span class="op">+=</span> r<span class="op">;</span>               <span class="co">// p may be assumed non-null</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
</td>
</tr>
</table>
<p>In both cases, the “p is null” output can be elided: in
<code>a</code>, because execution would not continue past the
<code>std::abort</code>; in <code>b</code>, because of the following
dereference of <code>p</code>. In both cases, the <code>count</code>
output must appear if <code>p</code> is null: in <code>a</code>, because
the program thereafter has the defined behavior of aborting; in
<code>b</code>, because the epoch ends before undefined behavior
occurs.</p>
<p>The function <code>b</code>, however, offers the additional
optimization of not checking for null pointers at run time. It is very
useful to support such optimizations without compromising
diagnostics.</p>
<h2 id="usage">Usage</h2>
<p>The obvious place to use <code>std::observable</code> would be after
any sort of I/O that always returns, especially in any code run when an
error is detected (and so imminent undefined behavior is likely). In a
contracts context, the violation handler is one such routine; since
<code>std::observable()</code> has no side effects, it would also be
permissible to include it in specific contract conditions to guarantee
that previous contracts are checked (even if the violation handler
always returns):</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> f<span class="op">(</span><span class="dt">int</span> <span class="op">*</span>p<span class="op">)</span> pre<span class="op">(</span>p<span class="op">)</span> pre<span class="op">((</span><span class="bu">std::</span>observable<span class="op">(),</span> <span class="op">*</span>p<span class="op">&lt;</span><span class="dv">5</span><span class="op">));</span></span></code></pre></div>
<p>The <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2900r13.pdf">current
Contracts paper</a> inserts appropriate observable checkpoints in the
sequence of assertion evaluations automatically rather than requiring
the above syntax.</p>
<h2 id="implicit-approach">Implicit approach</h2>
<p>In C23, all I/O (including volatile accesses) is implicitly
considered an observable checkpoint (albeit without that phrasing,
instead <a
href="https://open-std.org/JTC1/SC22/WG14/www/docs/n3128.pdf">changing
the definition of undefined behavior</a>). (Formally this refers to
<code>fflush</code> and not <code>printf</code>, but the latter calls
the former on a schedule that the optimizer cannot in general predict.)
The wording below includes small changes to make all library I/O
consistent in this regard.</p>
<p>Because even future versions of C are likely not to need manual
checkpoints, there is no need to provide <code>::observable</code> in a
C header. The most appropriate C++ header appears to be
<code>&lt;utility&gt;</code>, where <code>std::observable</code> can be
a counterpart to <code>std::unreachable</code>.</p>
<p>An objection might be raised to this approach that the compiler
doesn’t know the semantics of every function that performs I/O (and we
can’t expect, say, POSIX to annotate its definition of
<code>write</code> with C++-specific text). However, such a situation is
harmless: any function whose definition is truly unknown to the
implementation might terminate the program, so no optimization can
propagate back past a call to such a function anyway. If the compiler
does know about a platform-specific I/O function (and, say, that it
always returns), it is no more complicated for it to know that it is an
optimization barrier as well.</p>
<p>The other plausible objection is that optimization might be too
strongly affected by such ordering, although the I/O itself would tend
to drown such savings. In August 2024, LEWG found these concerns not to
be problematic and asked for library I/O to be an automatic checkpoint
and asked for EWG to similarly consider the case of volatile accesses
(where optimization might be more of a concern). If volatile accesses
were also made observable, it would make sense to adopt the requisite
changes to the definition of undefined behavior (and automatic
checkpoints) without the user-accessible <code>std::observable</code>.
In Wrocław, EWG considered the <code>volatile</code> question, but did
not reach consensus on any change.</p>
<h1 id="wording">Wording</h1>
<p>Relative to N5001.</p>
<h2 id="intro.abstract">#[intro.abstract]</h2>
<p>Remove the note in paragraph 4:</p>
<blockquote>
<p>Certain other operations are described in this document as undefined
behavior (for example, the effect of attempting to modify a const
object).</p>
<p><s>[<em>Note</em>: This document imposes no requirements on the
behavior of programs that contain undefined behavior. — <em>end
note</em>]</s></p>
</blockquote>
<p>Insert before paragraph 5:</p>
<div class="ins">
<blockquote>
<p>Certain events in the execution of a program are termed
<em>observable checkpoints</em>.</p>
<p>[<em>Note</em>: A call to
<code>std::observable</code> ([utility.undefined]) is an observable
checkpoint. — <em>end note</em>]</p>
</blockquote>
</div>
<p>Change paragraph 5:</p>
<blockquote>
<p><u>The <em>defined prefix</em> of an execution comprises the
operations <em>O</em> for which for every undefined operation <em>U</em>
there is an observable checkpoint <em>C</em> such that <em>O</em>
happens before <em>C</em> and <em>C</em> happens before
<em>U</em>.</u></p>
<p><u>[<em>Note:</em> The undefined behavior that arises from a data
race ([intro.races]) occurs on all participating threads. — <em>end
note</em>]</u></p>
<p>A conforming implementation executing a well-formed program shall
produce the <s>same </s>observable behavior <s>as</s><u>of the defined
prefix of </u>one of the possible executions of the corresponding
instance of the abstract machine with the same program and the same
input. <s>However, i</s><u>I</u>f <s>any such</s><u>the selected</u>
execution contains an undefined operation, <s>this document places no
requirement on </s>the implementation executing that program with that
input <s>(not even with regard to operations preceding the first
undefined operation)</s><u>may produce arbitrary additional observable
behavior afterwards</u>. If the execution contains an operation
specified as having erroneous behavior, the implementation is permitted
to issue a diagnostic and is permitted to terminate the execution at an
unspecified time after that operation.</p>
</blockquote>
<p>Change paragraph 7:</p>
<blockquote>
<p>The <s>least requirements on a conforming implementation
are</s><u>following specify the <em>observable behavior</em> of the
program</u>:</p>
<ol type="1">
<li><p>Accesses through volatile glvalues are evaluated strictly
according to the rules of the abstract machine.</p></li>
<li><p><s>At program termination, all d</s><u>D</u>ata <u>is delivered
to the host environment to be</u> written into files<s> shall be
identical to one of the possible results that execution of the program
according to the abstract semantics would have produced</s><u> (C17
7.21.3)</u>.</p>
<p><u>[<em>Note:</em> Delivering such data is followed by an observable
checkpoint ([cstdio.syn]). Not all host environments provide access to
file contents before program termination. — <em>end
note</em>]</u></p></li>
<li><p>The input and output dynamics of interactive devices shall take
place in such a fashion that prompting output is actually delivered
before a program waits for input. What constitutes an interactive device
is implementation-defined.</p></li>
</ol>
<p><s>These collectively are referred to as the <em>observable
behavior</em> of the program.</s></p>
<p>[<em>Note</em>: More stringent correspondences between abstract and
actual semantics can be defined by each implementation. — <em>end
note</em>]</p>
</blockquote>
<h2 id="utilities">#[utilities]</h2>
<h3 id="utility.syn">#[utility.syn]</h3>
<p>Change part of the synopsis:</p>
<blockquote>
<p><code>//</code> [utility.<s>unreachable</s><u>undefined</u>],
<s>unreachable</s><u>undefined behavior</u><br />
<code>[[noreturn]] void unreachable();</code><br />
<u><code>void observable() noexcept;</code></u></p>
</blockquote>
<h3
id="utility.unreachableundefined-function-unreachableundefined-behavior">#[utility.<s>unreachable</s><u>undefined</u>]
<s>Function <code>unreachable</code></s><u>Undefined behavior</u></h3>
<p>Rename subclause and change stable label (directing the old to the
new).</p>
<p>Add at the end of the subclause:</p>
<div class="ins">
<blockquote>
<p><code>void observable() noexcept;</code></p>
</blockquote>
<blockquote>
<p><em>Effects:</em> Establishes an observable
checkpoint ([intro.abstract]).</p>
</blockquote>
</div>
<h2 id="input.output">#[input.output]</h2>
<h3 id="iostream.objects.overview">#[iostream.objects.overview]</h3>
<p>Change paragraph 2:</p>
<blockquote>
<p>The header <code>&lt;iostream&gt;</code> declares objects that
associate objects with the standard C streams provided for by the
functions declared in <code>&lt;cstdio&gt;</code>, and includes all the
headers necessary to use these objects. <u>The dynamic types of the
stream buffers initially associated with these objects are unspecified,
but they have the behavior specified for
<code>std::basic_filebuf&lt;char&gt;</code> or
<code>std::basic_filebuf&lt;wchar_t&gt;</code>.</u></p>
</blockquote>
<h3 id="iostream.format">#[iostream.format]</h3>
<h4 id="ostream.formatted.print">#[ostream.formatted.print]</h4>
<p>Change bullet (4.3):</p>
<blockquote>
<p>If the function is <code>vprint_unicode</code> and <code>os</code> is
a stream that refers to a terminal that is capable of displaying Unicode
only via a native Unicode API, which is determined in an
implementation-defined manner, flushes <code>os</code> and then writes
<code>out</code> to the terminal using the native Unicode API; if
<code>out</code> contains invalid code units, the behavior is undefined.
<u>Then establishes an observable checkpoint ([intro.abstract]).</u></p>
</blockquote>
<h4 id="print.fun">#[print.fun]</h4>
<p>Change bullet (10.1):</p>
<blockquote>
<p>If <code>stream</code> refers to a terminal that is capable of
displaying Unicode only via a native Unicode API, flushes
<code>stream</code> and then writes <code>out</code> to the terminal
using the native Unicode API; if <code>out</code> contains invalid code
units, the behavior is undefined. <u>Then establishes an observable
checkpoint ([intro.abstract]).</u></p>
</blockquote>
<h3 id="filebuf.virtuals">#[filebuf.virtuals]</h3>
<p>Change paragraph 10:</p>
<blockquote>
<ol start="3" type="1">
<li>[…]</li>
<li>Otherwise output from <code>xbuf</code> to <code>xbuf_end</code>,
and fail if output fails. At this point if <code>b != p</code> and
<code>b == end</code> (<code>xbuf</code> isn’t large enough) then
increase <code>XSIZE</code> and repeat from the beginning.</li>
</ol>
<p><u>Then establishes an observable
checkpoint ([intro.abstract]).</u></p>
</blockquote>
<h3 id="cstdio.syn">#[cstdio.syn]</h3>
<p>Insert before paragraph 2:</p>
<div class="ins">
<blockquote>
<p>The return from each function call that delivers data to the host
environment to be written to a file (C17 7.21.3) is an observable
checkpoint ([intro.abstract]).</p>
</blockquote>
</div>
</body>
</html>
