<!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>P2434R3: Nondeterministic pointer provenance</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 */
    pre > code.sourceCode { white-space: pre; position: relative; }
    pre > code.sourceCode > span { 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">P2434R3: Nondeterministic pointer
provenance<!-- -*- c++-md -*- --></h1>
</header>
<p><em>Audience</em>: EWG, CWG, SG22<br />
S. Davis Herring &lt;<a href="mailto:herring@lanl.gov"
class="email">herring@lanl.gov</a>&gt;<br />
Los Alamos National Laboratory<br />
January 10, 2025</p>
<h1 id="history">History</h1>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2434r2.html">r2</a>:</p>
<ul>
<li>Rebased onto N5001</li>
<li>Handled <code>%p</code> in wording</li>
<li>Strengthened aliasing example</li>
<li>Corrected (minor) alignment discussion</li>
<li>Clarified editing instructions</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2434r1.html">r1</a>:</p>
<ul>
<li>Rebased onto N4988</li>
<li>Explained interaction with P1494/WG14:N3128</li>
<li>Gave [basic.types.trivial] a title</li>
<li>Removed term “bit value”, relying more on “value
representation”</li>
<li>Clarified unspecified pointer comparisons</li>
<li>Extended implementation effects discussion</li>
</ul>
<p>Since <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2434r0.html">r0</a>:</p>
<ul>
<li>Rebased onto N4981</li>
<li>Clarified discussion of P2318R1’s analysis and proposal</li>
<li>Discussed implications for pointer zap</li>
</ul>
<h1 id="introduction">Introduction</h1>
<p><a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2318r1.pdf">P2318R1</a>
describes a variety of plausible models of pointer provenance that
differ principally in how they handle conversions between pointers and
integers (including the integer values of the storage bytes for a
pointer). (See its §A.4 for discussion of the variants in terms of
examples.) It proposes the variant called PNVI-ae-udi, which does seem
to provide a good combination of optimization possibilities and support
for existing code. However, in several cases it is overly charitable to
the programmer: for example, using an explicit copy loop rather than
calling <code>std::memcpy</code> “exposes” storage, which can interfere
with optimization, even if the byte values obviously do not escape. (The
paper proposes to eventually add annotations to avoid such unwanted side
effects.) It also sometimes depends in an apparently arbitrary fashion
on the order of operations with no data dependencies (as in the
<code>pointer_from_integer_1ig.c</code> example with an exposure of
<code>j</code> added).</p>
<p>The main alternative that was considered and rejected is the PVI
model, which avoids the notion of storage exposure but imposes further
restrictions on integer conversions. These restrictions provide further
opportunities for optimization but also complicate the execution model
in subtle ways that make it difficult for the programmer to determine
whether a manipulation preserves the validity of a pointer (yet to be
reconstructed). They also interact badly with serialization of pointers
where operations on the converted pointer value are entirely invisible;
additional annotations might be required to support this use case.</p>
<p>This paper compares the existing rules for pointers to these
provenance models and proposes minor changes to better align them. These
changes have the additional benefit of addressing some of the concerns
surrounding “pointer zap”. In particular, the use cases for the
“provenance fence” operation proposed in <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2414r3.pdf">P2414R3</a>
become implementable with only mild assumptions about
implementation-defined behavior for invalid pointer values.</p>
<h1 id="analysis">Analysis</h1>
<p>Pointer values are described in abstract terms ([basic.compound]/3);
while they “represent the address” of an object, that address may be
given a numerical interpretation only via copying the bits into an
integer or via the implementation-defined mapping to
integers ([expr.reinterpret.cast]/4), and there is no specification of
how any address is chosen. As such, integers obtained by
<code>memcpy</code>ing or casting pointers may be taken to be completely
unspecified aside from the (separate) round-trip requirements.
([basic.align]/1 talks about addresses as ordinal or cardinal numbers of
bytes, but the only observable effect is a requirement when manually
constructing objects in buffers.)</p>
<p>This nondeterminism already implies undefined behavior in many of the
circumstances that the provenance models are meant to reject. Consider
the simple example</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="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> jenny<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>  <span class="co">// std::cout &lt;&lt; std::hex &lt;&lt; (uintptr_t)&amp;jenny &lt;&lt; &#39;\n&#39;;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">*(</span><span class="dt">int</span><span class="op">*)</span><span class="dv">8675309</span><span class="op">=</span><span class="dv">1</span><span class="op">;</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> jenny<span class="op">;</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>(Assume that the implementation does not specially define a pointer
value corresponding to this particular integer.) Even if the address of
<code>jenny</code> is the suspicious value given, this has undefined
behavior under PNVI-ae-udi (because the address of <code>jenny</code> is
never exposed) and PVI (because the integer literal is not derived from
the address of <code>jenny</code>). However, it is equally undefined in
N5001 because there are possible executions of the
program ([intro.abstract]/5) where the address is some other value and
the cast produces an invalid pointer value. P2318R1 considers this
interpretation but does not deem it conclusive, explaining that the same
analysis of many tests can be obtained from address nondeterminism
instead of the explicit provenance semantics but lamenting that that
approach “requir[es] examination of multiple executions”.</p>
<p>Note that in N5001 printing the address is no help: even if the
program displays “845fed”, that itself can be a manifestation of the
undefined behavior. However, an interactive program that reads input
after writing output (which has long been neglected by
[intro.abstract]/5) constitutes a significant complication here:</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">int</span> coin<span class="op">(</span><span class="dt">int</span> x<span class="op">,</span><span class="dt">int</span><span class="op">)</span> <span class="op">{</span><span class="cf">return</span> x<span class="op">;}</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> coin<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> i<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> coin<span class="op">(</span>i<span class="op">,++</span>i<span class="op">);</span>  <span class="co">// either 0 or 1 based on unspecified evaluation order</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="dt">int</span> mystery<span class="op">=</span>coin<span class="op">();</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;number other than &quot;</span> <span class="op">&lt;&lt;</span> mystery <span class="op">&lt;&lt;</span> <span class="st">&quot;: &quot;</span><span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> d<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cin <span class="op">&gt;&gt;</span> d<span class="op">;</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">127</span><span class="op">/(</span>d<span class="op">-</span>mystery<span class="op">);</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>It would be absurd to suggest that this program has undefined
behavior if a user who was prompted not to enter 0 entered 1 just
because a different execution exists where entering 1 would produce
division by zero. (Consider that similar prompts might be used for
indices into arrays of size learned only at runtime.) Clearly printing
<code>&amp;jenny</code> might influence any further input, perhaps as
simple as a flag that controls using the guessed address (or an integer
that switches among multiple guesses). As such, any guess that might be
informed by interactive input ought to be allowed. This paper does not
propose to specify appropriate semantics for interactive programs in
general, but the notion of an observable checkpoint from <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p1494r3.html">P1494</a>
or the similar restriction of undefined behavior in C23 via <a
href="https://open-std.org/JTC1/SC22/WG14/www/docs/n3128.pdf">WG14:N3128</a>
do something stronger by making the output already constitute a
commitment by the implementation to a particular subset of potential
executions. The implementation would then then be obliged to either have
the output of <code>&amp;jenny</code> not match the guess or honor the
guess; while in simple cases the output could be adjusted, the guess
would need to be honored in programs wherein the connection between the
output and the guess is sufficiently complicated. The latter approach
has almost exactly the same semantics as PNVI-ae-udi, except that an
exposure that can be proven not to influence observable behavior can be
disregarded.</p>
<p>Further subtle distinctions concern integer manipulation;
consider</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> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> x<span class="op">,</span>y<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">uintptr_t</span> p<span class="op">=(</span><span class="dt">uintptr_t</span><span class="op">)&amp;</span>x<span class="op">,</span>q<span class="op">=(</span><span class="dt">uintptr_t</span><span class="op">)&amp;</span>y<span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>  p<span class="op">^=</span>q<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  q<span class="op">^=</span>p<span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>  p<span class="op">^=</span>q<span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  <span class="op">*(</span><span class="dt">int</span><span class="op">*)</span>q<span class="op">=*(</span><span class="dt">int</span><span class="op">*)</span>p<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></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>PVI disallows this manipulation, saying that the values resulting
from operations on <code>p</code> and <code>q</code> do not inherit
their provenances; PNVI-ae-udi allows it because it simply observes that
both addresses have escaped from the abstract world of pointer values.
In the latter case, the compiler would have to support “guessing”
addresses even if it could prove that the value of such a pointer
doesn’t actually depend on the original pointer. N5001 already handles
this case more elegantly: the program has well defined behavior because
for any choice of addresses for <code>x</code> and <code>y</code>,
<code>q</code> (<code>p</code>) ends up being the address of
<code>x</code> (<code>y</code>), so the casts back to pointers produce
the swapped pointer values. This interpretation extends to arbitrary
integer manipulations and I/O: the operations allowed are precisely
those that <em>reliably</em> reproduce some address initially obtained,
whether via arithmetic, control flow, or data dependencies. In practice
it is impossible to detect every pointer value construction that fails
to be reliable (that “guesses”) or fails to influence observable
behavior (for interactive input), but there is no obligation to do so
since the behavior is undefined in such cases.</p>
<p>The pointer zap rule ([basic.compound]/4) has two purposes: its
footnote explains that even examining a pointer into deallocated storage
might trap (in the course of loading something like a segment register),
but it also serves as a sort of notional “generation counter” for reused
parts of the free store. Consider</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="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> <span class="op">*</span>p<span class="op">=</span><span class="kw">new</span> <span class="dt">int</span><span class="op">;</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>  <span class="dt">uintptr_t</span> i<span class="op">=(</span><span class="dt">uintptr_t</span><span class="op">)</span>p<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>  <span class="kw">delete</span> p<span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  p<span class="op">=</span><span class="kw">new</span> <span class="dt">int</span><span class="op">(</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span><span class="op">((</span><span class="dt">uintptr_t</span><span class="op">)</span>p<span class="op">==</span>i<span class="op">)</span> <span class="op">*(</span><span class="dt">int</span><span class="op">*)</span>i<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="op">*</span>p<span class="op">;</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>PVI rejects this LBYL approach: <code>i</code> cannot acquire the
provenance of the new <code>p</code> merely because it has been compared
to another integer with that provenance. (Consider that the comparison
might occur in an opaque function.) PNVI-ae-udi allows it because the
address of that new object is exposed in the course of making the check;
N5001 allows it simply because <code>i</code> is cast back to a pointer
precisely when it has the same value as the cast of the new
<code>p</code>. It is only natural that two integers with the same value
should have the same behavior.</p>
<p>N5001 does not, however, implement the user-disambiguation
(<em>udi</em>) provision: in saying that a pointer subjected to a
round-trip conversion “will have its original value”,
[expr.reinterpret.cast]/5 erroneously forbids real implementations that
produce the same integer value for pointers to an object and one past
the object that immediately precedes it in memory. Similarly,
[basic.types.general]/2–3 refer to a singular value for what might be a
pointer reconstructed from bytes (and /4 claims that the bits are <a
href="https://lists.isocpp.org/ext/2020/06/14435.php">sufficient to
determine the value</a>). [bit.cast]/2 acknowledges the possibility of
more than one value with the same value representation, but leaves it
unspecified which is produced.</p>
<p>The case of concern for lock-free algorithms is analogous to the
one-past pointer situation in that multiple abstract pointer values
exist whose memory representations (as must be the currency of atomic
operations) are identical. Of course, at most one such value (of a given
type) is valid at any moment during execution, so there is no ambiguity
as to which value the program would prefer to result from a conversion.
In its place is the complication that the normal phrasings of these
algorithms cannot use any such result because it might be invalidated by
unsequenced deallocations. In this case a temporal version of
user-disambiguation may be applied: any correct algorithm discovers that
the address in question is that of some live object before it uses any
associated pointer value, thereby nominating that object’s identity as
the value of the pointer.</p>
<h1 id="proposal">Proposal</h1>
<p>To implement <em>udi</em>, apply the same <em>angelic
nondeterminism</em> by which <a
href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0593r6.html">implicit
object creation</a> selects the objects to create: if any pointer value
exists that corresponds to the integer and gives the program defined
behavior, one such value is the result. (As is necessary for any
implementation to exist, no program can observe the acausal information
about which pointer value is selected.) This change affects
[basic.types.general]/2–4, [expr.reinterpret.cast]/5, and [bit.cast]/2
(which currently instead plays into the general <em>demonic
nondeterminism</em> of [intro.abstract]/5). This paper does not attempt
to address the situation of modifying a pointer by <a
href="https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2022/p1839r4.pdf">storing
to <em>part</em> of its object representation</a>, but it changes the
simple <code>memcpy</code> case to avoid eventually giving different
behavior to direct and circuitous means of accomplishing a bit-wise
copy.</p>
<p>To avoid confusing inconsistencies with comparing their integer
representations (on implementations where each address has just one
such), restrict [expr.eq] to provide consistent results for any pair of
pointer values. This change cannot be detected during constant
evaluation ([expr.const]/5.24); following P2318R1, we could consider
actually comparing the addresses other than during constant evaluation
(because programs cannot benefit from the inherently unreliable
<code>!=</code> result for distinct pointer values that share an
address).</p>
<h2 id="consequences-for-pointer-zap">Consequences for pointer zap</h2>
<p>Note that the pointer value that gives the program defined behavior
might be to an object whose lifetime has not started or even whose
region of storage has not yet been created. Clearly such a “prospective”
pointer value cannot immediately be used: any attempt to do so
necessitates that the result of the integer conversion instead be a
pointer to some object in existing storage which cannot then be used
after a <code>delete</code> and <code>new</code>. Of course, the reuse
of the address might never take place; a correct algorithm will never
use the pointer in that circumstance, so it is harmless for it to refer
to the previous object. Otherwise, there is no inconsistency in allowing
it to be stored until such time as its object comes into existence.
(Note that references cannot be used in place of pointers here because a
pointer value must be valid in the context of applying <code>*</code> to
it ([basic.compound]/4).)</p>
<p>We can then implement LIFO Push with a trivial modification of the
initial implementation from P2414R3:</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">template</span> <span class="op">&lt;</span><span class="kw">typename</span> Node<span class="op">&gt;</span> <span class="kw">class</span> LIFOList <span class="op">{</span> <span class="co">// Node must support set_next()</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>atomic<span class="op">&lt;</span>Node<span class="op">*&gt;</span> <span class="va">top_</span><span class="op">{</span><span class="kw">nullptr</span><span class="op">};</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span><span class="op">:</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> push<span class="op">(</span>Node<span class="op">*</span> newnode<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">while</span> <span class="op">(</span><span class="kw">true</span><span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>      Node<span class="op">*</span> oldtop <span class="op">=</span> <span class="kw">reinterpret_cast</span><span class="op">&lt;</span>Node<span class="op">*&gt;(</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>        <span class="kw">reinterpret_cast</span><span class="op">&lt;</span><span class="bu">std::</span>uintptr_t<span class="op">&gt;(</span><span class="va">top_</span><span class="op">.</span>load<span class="op">()));</span> <span class="co">// step 1</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>      newnode<span class="op">-&gt;</span>set_next<span class="op">(</span>oldtop<span class="op">);</span> <span class="co">// step 2</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>      <span class="cf">if</span> <span class="op">(</span><span class="va">top_</span><span class="op">.</span>compare_exchange_weak<span class="op">(</span>oldtop<span class="op">,</span> newnode<span class="op">))</span> <span class="cf">return</span><span class="op">;</span> <span class="co">// step 3</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>  Node<span class="op">*</span> pop_all<span class="op">()</span> <span class="op">{</span> <span class="cf">return</span> <span class="va">top_</span><span class="op">.</span>exchange<span class="op">(</span><span class="kw">nullptr</span><span class="op">);</span> <span class="op">}</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>The only change here is the round-trip cast in step 1. If
<code>top_</code> is deallocated and replaced (at the same address)
after the <code>load()</code>, <code>oldtop</code> is a pointer to the
new object, so <code>newnode</code> contains a pointer valid when it is
installed by the compare-exchange. No “provenance fence” is then needed
in <code>pop_all</code> because the pointers it reads are all valid at
the time. Note that the algorithm still relies on the
implementation-defined behavior of applying various operations (other
than indirection or deallocation) to the pointer value
<code>top_.load()</code> that might not be valid in the context of such
operations ([basic.compound]/4), since the reallocation might not yet
have happened (if it happens at all).</p>
<p>This paper does not propose requiring that all implementations
support these operations (integer conversion, initialization/assignment,
and compare-exchange) on non-valid pointer values: it is already
optional for the implementation to provide <code>std::uintptr_t</code>,
so it is not fundamentally more restrictive to implement LIFO Push only
on implementations that support such operations. Nonetheless, EWG might
choose to strengthen these requirements while considering these
applications to improve their portability.</p>
<p>Nor does this paper propose special behavior for
<code>std::atomic&lt;T*&gt;</code> (as has been considered as another
partial solution for such algorithms): the mismatched value obtained by
the compare-exchange must itself be subjected to a round-trip in order
to potentially refer to a future object. It would however be reasonable
to specify that such a round trip is implicit in the operation since
<code>std::atomic</code> already traffics in value representations and
suppresses certain kinds of undefined behavior.</p>
<h2 id="consequences-for-implementations">Consequences for
implementations</h2>
<p>These changes are intended more to formalize than to change existing
implementations, in that there does not seem to be any other consistent
model that can be used in the presence of multiple pointer values with
the same address. However, implementations do not always use a
consistent model, as illustrated by the following program:</p>
<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="pp">#include</span><span class="im">&lt;cstdint&gt;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> y<span class="op">,</span>x<span class="op">;</span> <span class="co">// use x,y at -O0</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>  y<span class="op">=</span><span class="dv">1</span><span class="op">;</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">int</span> <span class="op">*</span><span class="at">const</span> p<span class="op">=&amp;</span>x<span class="op">+</span><span class="dv">1</span><span class="op">,*</span><span class="at">const</span> q<span class="op">=&amp;</span>y<span class="op">;</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="bu">std::</span>uintptr_t u<span class="op">=(</span><span class="bu">std::</span>uintptr_t<span class="op">)</span>p<span class="op">,</span>v<span class="op">=(</span><span class="bu">std::</span>uintptr_t<span class="op">)</span>q<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>  <span class="cf">if</span><span class="op">(</span>u<span class="op">==</span>v<span class="op">)</span> <span class="op">{</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="at">const</span> <span class="kw">auto</span> w<span class="op">=</span>u<span class="op">+</span>v<span class="op">-</span>u<span class="op">/</span><span class="dv">2</span><span class="op">-</span>u<span class="op">/</span><span class="dv">2</span><span class="op">-(</span>u<span class="op">&amp;</span><span class="dv">1</span><span class="op">);</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span><span class="op">(</span>w<span class="op">==</span>u<span class="op">)</span> <span class="op">{</span> <span class="co">// redundant check</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>      <span class="op">*(</span><span class="dt">int</span><span class="op">*)</span>w<span class="op">=</span><span class="dv">2</span><span class="op">;</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>      <span class="cf">return</span> y<span class="op">;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="op">-</span><span class="dv">1</span><span class="op">;</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Here GCC and Clang each forward the store of 1 to the
<code>return y</code> when optimizing, despite the fact that
<code>w</code> is actually equal to <code>v</code> regardless of
<code>u</code>’s value (rather than, say, equal to <code>u</code>
regardless of <code>v</code>) and despite the two surrounding equality
checks. Apparently the “original value” for <code>w</code> is deduced
from the “redundant check” comparison, since changing that to
<code>w==v</code> causes both implementations to return 2 instead.
(Indeed, the generated assembly describes the store through
<code>w</code> as being to <code>$x+4</code> as given and to
<code>$y</code> with the comparison to <code>v</code> substituted.)</p>
<p>The commonly-assumed implementation-defined support for non-valid
pointer values also produces what may be a surprising optimization
limitation in an example originally from Hans Boehm:</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> f<span class="op">(</span>T<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>  T<span class="op">*</span> t <span class="op">=</span> <span class="kw">new</span> T<span class="op">();</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  opaque_fn<span class="op">(</span>t<span class="op">);</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="co">// *t and *p can alias here</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>It is possible that <code>p</code> is a prospective pointer value
referring to <code>*t</code> that was produced by a round-trip cast (if
the implementation allowed such to occur before the allocation of
<code>t</code>) and that <code>opaque_fn</code> aborts unless
<code>t</code> has the same address as <code>p</code> (stored
elsewhere). This situation seems surprising based on local reasoning
akin to that involved in <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3292r0.html#early-escape">certain
multi-threaded cases</a>, but it is isomorphic to LIFO Push above:
<code>p</code> is <code>oldtop</code> (as passed to
<code>set_next</code>), <code>t</code> is <code>newnode</code>, and
<code>opaque_fn</code> does the CAS (albeit with the threatened abort
rather than a simple <code>if</code>). Since we want
(<code>set_next</code>’s copies of) <code>oldtop</code> to be usable
(without requiring something like <code>usable_ptr</code> there), we
need <code>p</code> to be as usable as <code>t</code> is here.</p>
<p>One further point of concern raised about <em>udi</em> is that it
might allow unrestricted navigation through a structure with a pointer,
which (despite the existence of <code>offsetof</code>) current
implementations forbid. However, such manipulations remain undefined
behavior except where they do not grant any additional power: given</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="kw">struct</span> point <span class="op">{</span><span class="dt">float</span> x<span class="op">,</span>y<span class="op">,</span>z<span class="op">;};</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="dt">void</span> g<span class="op">(</span><span class="dt">float</span> <span class="op">*</span>f<span class="op">);</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a><span class="dt">float</span> zero<span class="op">(</span>point p<span class="op">)</span> <span class="op">{</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>  p<span class="op">.</span>z<span class="op">=</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>  g<span class="op">(&amp;</span>p<span class="op">.</span>y<span class="op">);</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> p<span class="op">.</span>z<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><code>zero</code> can still perform SRA and return <code>0f</code>
because <code>g</code> cannot access <code>p.z</code>: it can’t obtain
<code>&amp;p</code> despite <code>point</code> being standard-layout
because <code>y</code> isn’t the first member, and while on practical
implementations we know that
<code>(uintptr_t)(&amp;p.y+1)==(uintptr_t)&amp;p.z</code>,
<code>g</code> cannot dereference <code>(float*)(uintptr_t)(f+1)</code>
without having proven that the equality holds in its specific case
(since the implementation might have a complicated pointer–integer
relationship that just <em>usually</em> has that property). Proving the
equality in a specific case of course requires having access to
<code>&amp;p.z</code> or some quantity computed therefrom, so the usual
alias analysis holds.</p>
<h1 id="wording">Wording</h1>
<p>Relative to N5001.</p>
<h2 id="basic.types">#[basic.types]</h2>
<h3 id="basic.types.general">#[basic.types.general]</h3>
<p>Move paragraphs 2 and 3 to [basic.types.trivial] (<em>q.v.</em>).</p>
<p>Change paragraph 4:</p>
<blockquote>
<p>The <em>object representation</em> of a complete object type
<code>T</code> is the sequence of <em>N</em> <code>unsigned</code>
<code>char</code> objects taken up by a non-bit-field complete object of
type <code>T</code>, where <em>N</em> equals <code>sizeof(T)</code>. The
<em>value representation</em> of a type <code>T</code> is the set of
bits in the object representation of <code>T</code> that participate in
representing a value of type <code>T</code>. The object and value
representation of a non-bit-field complete object of type <code>T</code>
are the bytes and bits, respectively, of the object corresponding to the
object and value representation of its type. The object representation
of a bit-field object is the sequence of <em>N</em> bits taken up by the
object, where <em>N</em> is the width of the bit-field ([class.bit]).
The value representation of a bit-field object is the set of bits in the
object representation that participate in representing its value. Bits
in the object representation of a type or object that are not part of
the value representation are <em>padding bits</em>. <s>For trivially
copyable types, the value representation is a set of bits in the object
representation that determines a <em>value</em>, which is one discrete
element of an implementation-defined set of
values.</s>[<em>Footnote</em>: […] — <em>end footnote</em>]</p>
</blockquote>
<section id="trivially-copyable-types-basic.types.trivial" class="ins">
<h3>Trivially copyable types #[basic.types.trivial]</h3>
</section>
<p>Add this subclause before [basic.fundamental]:</p>
<div class="ins">
<blockquote>
<p>Each trivially copyable type <code>T</code> has an
implementation-defined set of discrete <em>values</em>. Each possible
value representation of an object of type <code>T</code> corresponds to
a distinct implementation-defined subset of this set. These subsets for
a type are disjoint, and their union is the set of values; for scalar
types other than object pointer types, each contains no more than one
value. Certain operations cause an object to <em>acquire</em> a value
representation, in which case the object’s value is replaced with an
unspecified member of the corresponding subset that would result in the
program having defined behavior, if any.</p>
</blockquote>
<blockquote>
<p>[<em>Note</em>: A single subset for a pointer type can contain
pointers to multiple objects in each of several regions of storage whose
durations are disjoint. — <em>end note</em>]</p>
</blockquote>
</div>
<p>Move paragraphs 2 and 3 here from [basic.types.general] and change
them:</p>
<blockquote>
<p><s>For any</s><u>If an</u> object <s>(other than</s><u>of such a type
<code>T</code> is not</u> a potentially-overlapping subobject<s>) of
trivially copyable type <code>T</code></s>, whether or not the object
holds a valid value of type <code>T</code>, the underlying
bytes ([intro.memory]) making up the object can be copied into an array
of <code>char</code>, <code>unsigned</code> <code>char</code>, or
<code>std::byte</code> ([cstddef.syn]).[<em>Footnote</em>: […] — <em>end
footnote</em>] If the content of that array is copied back into the
object, the object <s>shall subsequently hold</s><u>acquires</u> its
original value<u> representation</u>.</p>
<p>[<em>Example</em>:</p>
<p>[…]</p>
<p>— <em>end example</em>]</p>
</blockquote>
<blockquote>
<p>For two distinct <u>such </u>objects <code>obj1</code> and
<code>obj2</code><s> of trivially copyable type <code>T</code>, where
neither <code>obj1</code> nor <code>obj2</code> is a
potentially-overlapping subobject</s>, if the underlying
bytes ([intro.memory]) making up <code>obj1</code> are copied into
<code>obj2</code>,[<em>Footnote</em>: […] — <em>end footnote</em>]
<code>obj2</code> <s>shall subsequently hold</s><u>acquires</u> the
<s>same</s> value <s>as</s><u>representation of</u>
<code>obj1</code>.</p>
<p>[<em>Example</em>:</p>
<p>[…]</p>
<p>— <em>end example</em>]</p>
</blockquote>
<h3 id="basic.compound">#[basic.compound]</h3>
<p>Change paragraph 4:</p>
<blockquote>
<p>A pointer value <em>P</em> is <em>valid in the context of</em> an
evaluation <em>E</em> if <em>P</em> is a pointer to function or a null
pointer value, or if it is a pointer to or past the end of an object
<em>O</em> and <em>E</em> <u>happens after the beginning and </u>happens
before the end of the duration of the region of storage for <em>O</em>.
[…]</p>
</blockquote>
<h2 id="expr">#[expr]</h2>
<h3 id="expr.reinterpret.cast">#[expr.reinterpret.cast]</h3>
<p>Change paragraphs 4 and 5:</p>
<blockquote>
<p>A pointer can be explicitly converted to any integral type large
enough to <s>hold</s><u>distinguish</u> all value<s>s</s><u>
representations</u> of its type. The mapping function is
implementation-defined.</p>
<p>[<em>Note</em>: It is intended to be unsurprising to those who know
the addressing structure of the underlying machine. — <em>end
note</em>]</p>
<p>[…]</p>
</blockquote>
<blockquote>
<p>A value of integral type or enumeration type can be explicitly
converted to a pointer. <s>A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value</s><u>If the value is
equal to that produced by converting one or more pointer
values</u> ([basic.compound])<s>; mappings between pointers and integers
are</s><u> to an integral type, the result is an unspecified choice
among all such values that would result in the program having defined
behavior. If no such value exists, the behavior is undefined.</u></p>
<p><u>[<em>Note</em>: It is possible for the result to not be valid in
the context of the conversion ([basic.compound]) because it points to an
object in a region of storage whose duration has ended or has not yet
begun. — <em>end note</em>]</u></p>
<p><s>o</s><u>O</u>therwise<u>, the result is</u>
implementation-defined.</p>
<p><u>[<em>Note</em>: It can be an invalid pointer value. — <em>end
note</em>]</u></p>
</blockquote>
<h3 id="expr.eq">#[expr.eq]</h3>
<p>Insert before paragraph 3:</p>
<div class="ins">
<blockquote>
<p>Any two pointer values or two pointer-to-member values either compare
equal or compare unequal.</p>
<p>[<em>Note</em>: Repeated comparisons are consistent so long as
neither value is an invalid pointer value. — <em>end note</em>]</p>
</blockquote>
</div>
<blockquote>
<p>If at least one of the converted operands is a pointer, pointer
conversions ([conv.ptr]), function pointer conversions ([conv.fctptr]),
and qualification conversions ([conv.qual]) are performed on both
operands to bring them to their composite pointer type ([expr.type]).
Comparing pointers is defined as follows:</p>
<ol type="1">
<li>If one pointer represents the address of a complete object, and
another pointer represents the address one past the last element of a
different complete object, [<em>Footnote</em>: As specified in
[basic.compound], an object that is not an array element is considered
to belong to a single-element array for this purpose. — <em>end
footnote</em>] the result of the comparison is unspecified.</li>
<li>Otherwise, if the pointers are both null, both point to the same
function, or both represent the same address ([basic.compound]), they
compare equal.</li>
<li>Otherwise, the pointers compare unequal.</li>
</ol>
</blockquote>
<p>Change paragraph 6:</p>
<blockquote>
<p>If two operands compare equal, the result is <code>true</code> for
the <code>==</code> operator and <code>false</code> for the
<code>!=</code> operator. If two operands compare unequal, the result is
<code>false</code> for the <code>==</code> operator and
<code>true</code> for the <code>!=</code> operator. <s>Otherwise, the
result of each of the operators is unspecified.</s></p>
</blockquote>
<p>[<em>Drafting note</em>: Which sentence applies might still be
unspecified per /3.1, /4.3, or /4.4. — <em>end drafting note</em>]</p>
<h2 id="bit.cast">#[bit.cast]</h2>
<p>Change paragraph 2:</p>
<blockquote>
<p><em>Returns</em>: An object of type <code>To</code>. Implicitly
creates objects nested within the result ([intro.object]). Each bit of
the value representation of the result is equal to the corresponding bit
in the object representation of <code>from</code>. Padding bits of the
result are unspecified. <s>For</s><u>Every trivially copyable object
among</u> the result and each object created within it<s>, if there is
no value of the object’s type corresponding to</s><u> acquires</u> the
value representation produced<u>; if any such object does not receive a
value</u>, the behavior is undefined. <s>If there are multiple such
values, which value is produced is unspecified.</s> A bit in the value
representation of the result is indeterminate if it does not correspond
to a bit in the value representation of <code>from</code> or corresponds
to a bit for which the smallest enclosing object is not within its
lifetime or has an indeterminate value ([basic.indet]). […]</p>
</blockquote>
<h2 id="cstdio.syn">#[cstdio.syn]</h2>
<p>Insert before paragraph 2:</p>
<div class="ins">
<blockquote>
<p>When the input item for the <code>%p</code> conversion of the
<code>fscanf</code> function (or equivalent) has been produced from more
than one pointer value, the pointer that results is an unspecified
choice among all those values that would result in the program having
defined behavior. If no such value exists, the behavior is
undefined.</p>
</blockquote>
</div>
<h1 id="acknowledgments">Acknowledgments</h1>
<p>Thanks to Richard Smith and Peter Sewell for reviewing an early,
overcomplicated draft of this paper. Thanks to Hans Boehm and Paul
McKenney for an enlightening discussion of <a
href="https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2414r3.pdf">P2414R3</a>.</p>
</body>
</html>
