<!doctype html><html lang="en">
 <head>
  <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
  <title>P3292R0R0: Provenance and Concurrency</title>
<style data-fill-with="stylesheet">/******************************************************************************
 *                   Style sheet for the W3C specifications                   *
 *
 * Special classes handled by this style sheet include:
 *
 * Indices
 *   - .toc for the Table of Contents (<ol class="toc">)
 *     + <span class="secno"> for the section numbers
 *   - #toc for the Table of Contents (<nav id="toc">)
 *   - ul.index for Indices (<a href="#ref">term</a><span>, in § N.M</span>)
 *   - table.index for Index Tables (e.g. for properties or elements)
 *
 * Structural Markup
 *   - table.data for general data tables
 *     -> use 'scope' attribute, <colgroup>, <thead>, and <tbody> for best results !
 *     -> use <table class='complex data'> for extra-complex tables
 *     -> use <td class='long'> for paragraph-length cell content
 *     -> use <td class='pre'> when manual line breaks/indentation would help readability
 *   - dl.switch for switch statements
 *   - ol.algorithm for algorithms (helps to visualize nesting)
 *   - .figure and .caption (HTML4) and figure and figcaption (HTML5)
 *     -> .sidefigure for right-floated figures
 *   - ins/del
 *     -> ins/del.c### for candidate and proposed changes (amendments)
 *
 * Code
 *   - pre and code
 *
 * Special Sections
 *   - .note       for informative notes             (div, p, span, aside, details)
 *   - .example    for informative examples          (div, p, pre, span)
 *   - .issue      for issues                        (div, p, span)
 *   - .advisement for loud normative statements     (div, p, strong)
 *   - .annoying-warning for spec obsoletion notices (div, aside, details)
 *   - .correction for "candidate corrections"       (div, aside, details, section)
 *   - .addition   for "candidate additions"         (div, aside, details, section)
 *   - .correction.proposed for "proposed corrections" (div, aside, details, section)
 *   - .addition.proposed   for "proposed additions"   (div, aside, details, section)
 *
 * Definition Boxes
 *   - pre.def   for WebIDL definitions
 *   - table.def for tables that define other entities (e.g. CSS properties)
 *   - dl.def    for definition lists that define other entitles (e.g. HTML elements)
 *
 * Numbering
 *   - .secno for section numbers in .toc and headings (<span class='secno'>3.2</span>)
 *   - .marker for source-inserted example/figure/issue numbers (<span class='marker'>Issue 4</span>)
 *   - ::before styled for CSS-generated issue/example/figure numbers:
 *     -> Documents wishing to use this only need to add
 *        figcaption::before,
 *        .caption::before { content: "Figure "  counter(figure) " ";  }
 *        .example::before { content: "Example " counter(example) " "; }
 *        .issue::before   { content: "Issue "   counter(issue) " ";   }
 *
 * Header Stuff (ignore, just don't conflict with these classes)
 *   - .head for the header
 *   - .copyright for the copyright
 *
 * Outdated warning for old specs
 *
 * Miscellaneous
 *   - .overlarge for things that should be as wide as possible, even if
 *     that overflows the body text area. This can be used on an item or
 *     on its container, depending on the effect desired.
 *     Note that this styling basically doesn't help at all when printing,
 *     since A4 paper isn't much wider than the max-width here.
 *     It's better to design things to fit into a narrower measure if possible.
 *
 *   - js-added ToC jump links (see fixup.js)
 *
 ******************************************************************************/

/* color variables included separately for reliability */

/******************************************************************************/
/*                                    Body                                    */
/******************************************************************************/

	html {
	}

	body {
		counter-reset: example figure issue;

		/* Layout */
		max-width: 50em;			  /* limit line length to 50em for readability   */
		margin: 0 auto;				/* center text within page                    */
		padding: 1.6em 1.5em 2em 50px; /* assume 16px font size for downlevel clients */
		padding: 1.6em 1.5em 2em calc(26px + 1.5em); /* leave space for status flag    */

		/* Typography */
		line-height: 1.5;
		font-family: sans-serif;
		widows: 2;
		orphans: 2;
		word-wrap: break-word;
		overflow-wrap: break-word;
		hyphens: auto;

		color: black;
		color: var(--text);
		background: white top left fixed no-repeat;
		background: var(--bg) top left fixed no-repeat;
		background-size: 25px auto;
	}


/******************************************************************************/
/*                         Front Matter & Navigation                          */
/******************************************************************************/

/** Header ********************************************************************/

	div.head { margin-bottom: 1em; }
	div.head hr { border-style: solid; }

	div.head h1 {
		font-weight: bold;
		margin: 0 0 .1em;
		font-size: 220%;
	}

	div.head h2 { margin-bottom: 1.5em;}

/** W3C Logo ******************************************************************/

	.head .logo {
		float: right;
		margin: 0.4rem 0 0.2rem .4rem;
	}

	.head img[src*="logos/W3C"] {
		display: block;
		border: solid #1a5e9a;
		border: solid var(--logo-bg);
		border-width: .65rem .7rem .6rem;
		border-radius: .4rem;
		background: #1a5e9a;
		background: var(--logo-bg);
		color: white;
		color: var(--logo-text);
		font-weight: bold;
	}

	.head a:hover > img[src*="logos/W3C"],
	.head a:focus > img[src*="logos/W3C"] {
		opacity: .8;
	}

	.head a:active > img[src*="logos/W3C"] {
		background: #c00;
		background: var(--logo-active-bg);
		border-color: #c00;
		border-color: var(--logo-active-bg);
	}

	/* see also additional rules in Link Styling section */

/** Copyright *****************************************************************/

	p.copyright,
	p.copyright small { font-size: small; }

/** Back to Top / ToC Toggle **************************************************/

	@media print {
		#toc-nav {
			display: none;
		}
	}
	@media not print {
		#toc-nav {
			position: fixed;
			z-index: 3;
			bottom: 0; left: 0;
			margin: 0;
			min-width: 1.33em;
			border-top-right-radius: 2rem;
			box-shadow: 0 0 2px;
			font-size: 1.5em;
		}
		#toc-nav > a {
			display: block;
			white-space: nowrap;

			height: 1.33em;
			padding: .1em 0.3em;
			margin: 0;

			box-shadow: 0 0 2px;
			border: none;
			border-top-right-radius: 1.33em;

			color: #707070;
			color: var(--tocnav-normal-text);
			background: white;
			background: var(--tocnav-normal-bg);
		}
		#toc-nav > a:hover,
		#toc-nav > a:focus {
			color: black;
			color: var(--tocnav-hover-text);
			background: #f8f8f8;
			background: var(--tocnav-hover-bg);
		}
		#toc-nav > a:active {
			color: #c00;
			color: var(--tocnav-active-text);
			background: white;
			background: var(--tocnav-active-bg);
		}

		#toc-nav > #toc-jump {
			padding-bottom: 2em;
			margin-bottom: -1.9em;
		}

		/* statusbar gets in the way on keyboard focus; remove once browsers fix */
		#toc-nav > a[href="#toc"]:not(:hover):focus:last-child {
			padding-bottom: 1.5rem;
		}

		#toc-nav:not(:hover) > a:not(:focus) > span + span {
			/* Ideally this uses :focus-within on #toc-nav */
			display: none;
		}
		#toc-nav > a > span + span {
			padding-right: 0.2em;
		}
	}

/** ToC Sidebar ***************************************************************/

	/* Floating sidebar */
	@media screen {
		body.toc-sidebar #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			max-width: 80%;
			max-width: calc(100% - 2em - 26px);
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body.toc-sidebar #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}
		body.toc-sidebar #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	/* Hide main scroller when only the ToC is visible anyway */
	@media screen and (max-width: 28em) {
		body.toc-sidebar {
			overflow: hidden;
		}
	}

	/* Sidebar with its own space */
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) #toc {
			position: fixed;
			top: 0; bottom: 0;
			left: 0;
			width: 23.5em;
			overflow: auto;
			padding: 0 1em;
			padding-left: 42px;
			padding-left: calc(1em + 26px);
			color: black;
			color: var(--tocsidebar-text);
			background: inherit;
			background-color: #f7f8f9;
			background-color: var(--tocsidebar-bg);
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) inset;
			box-shadow: -.1em 0 .25em var(--tocsidebar-shadow) inset;
		}
		body:not(.toc-inline) #toc h2 {
			margin-top: .8rem;
			font-variant: small-caps;
			font-variant: all-small-caps;
			text-transform: lowercase;
			font-weight: bold;
			color: gray;
			color: hsla(203,20%,40%,.7);
			color: var(--tocsidebar-heading-text);
		}

		body:not(.toc-inline) {
			padding-left: 29em;
		}
		/* See also Overflow section at the bottom */

		body:not(.toc-inline) #toc-jump:not(:focus) {
			width: 0;
			height: 0;
			padding: 0;
			position: absolute;
			overflow: hidden;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) {
			margin: 0 4em;
		}
	}

/******************************************************************************/
/*                                Sectioning                                  */
/******************************************************************************/

/** Headings ******************************************************************/

	h1, h2, h3, h4, h5, h6, dt {
		page-break-after: avoid;
		page-break-inside: avoid;
		font: 100% sans-serif;   /* Reset all font styling to clear out UA styles */
		font-family: inherit;	/* Inherit the font family. */
		line-height: 1.2;		/* Keep wrapped headings compact */
		hyphens: manual;		/* Hyphenated headings look weird */
	}

	h2, h3, h4, h5, h6 {
		margin-top: 3rem;
	}

	h1, h2, h3 {
		color: #005A9C;
		color: var(--heading-text);
	}

	h1 { font-size: 170%; }
	h2 { font-size: 140%; }
	h3 { font-size: 120%; }
	h4 { font-weight: bold; }
	h5 { font-style: italic; }
	h6 { font-variant: small-caps; }
	dt { font-weight: bold; }

/** Subheadings ***************************************************************/

	h1 + h2,
	#profile-and-date {
		/* #profile-and-date is a subtitle in an H2 under the H1 */
		margin-top: 0;
	}
	h2 + h3,
	h3 + h4,
	h4 + h5,
	h5 + h6 {
		margin-top: 1.2em; /* = 1 x line-height */
	}

/** Section divider ***********************************************************/

	:not(.head) > :not(.head) + hr {
		font-size: 1.5em;
		text-align: center;
		margin: 1em auto;
		height: auto;
		color: black;
		color: var(--hr-text);
		border: transparent solid 0;
		background: transparent;
	}
	:not(.head) > hr::before {
		content: "\2727\2003\2003\2727\2003\2003\2727";
	}

/******************************************************************************/
/*                            Paragraphs and Lists                            */
/******************************************************************************/

	p {
		margin: 1em 0;
	}

	dd > p:first-child,
	li > p:first-child {
		margin-top: 0;
	}

	ul, ol {
		margin-left: 0;
		padding-left: 2em;
	}

	li {
		margin: 0.25em 0 0.5em;
		padding: 0;
	}

	dl dd {
		margin: 0 0 .5em 2em;
	}

	.head dd + dd { /* compact for header */
		margin-top: -.5em;
	}

	/* Style for algorithms */
	ol.algorithm ol:not(.algorithm),
	.algorithm > ol ol:not(.algorithm) {
	border-left: 0.5em solid #DEF;
	border-left: 0.5em solid var(--algo-border);
	}

	/* Put nice boxes around each algorithm. */
	[data-algorithm]:not(.heading) {
	 padding: .5em;
	 border: thin solid #ddd;
	 border: thin solid var(--algo-border);
	 border-radius: .5em;
	 margin: .5em calc(-0.5em - 1px);
	}
	[data-algorithm]:not(.heading) > :first-child {
	 margin-top: 0;
	}
	[data-algorithm]:not(.heading) > :last-child {
	 margin-bottom: 0;
	}

	/* Style for switch/case <dl>s */
	dl.switch > dd > ol.only,
	dl.switch > dd > .only > ol {
	margin-left: 0;
	}
	dl.switch > dd > ol.algorithm,
	dl.switch > dd > .algorithm > ol {
	margin-left: -2em;
	}
	dl.switch {
	padding-left: 2em;
	}
	dl.switch > dt {
	text-indent: -1.5em;
	margin-top: 1em;
	}
	dl.switch > dt + dt {
	margin-top: 0;
	}
	dl.switch > dt::before {
	content: '\21AA';
	padding: 0 0.5em 0 0;
	display: inline-block;
	width: 1em;
	text-align: right;
	line-height: 0.5em;
	}

/** Terminology Markup ********************************************************/


/******************************************************************************/
/*                                 Inline Markup                              */
/******************************************************************************/

/** Terminology Markup ********************************************************/
	dfn   { /* Defining instance */
		font-weight: bolder;
	}
	a > i { /* Instance of term */
		font-style: normal;
	}
	dt dfn code, code.idl {
		font-size: inherit;
	}
	dfn var {
		font-style: normal;
	}

/** Change Marking ************************************************************/

	del {
		color: #aa0000;
		color: var(--del-text);
		background: transparent;
		background: var(--del-bg);
		text-decoration: line-through;
	}
	ins {
		color: #006100;
		color: var(--ins-text);
		background: transparent;
		background: var(--ins-bg);
		text-decoration: underline;
	}

	/* for amendments (candidate/proposed changes) */

	.amendment ins, .correction ins, .addition ins,
	ins[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment del, .correction del, .addition del,
	del[class^=c] {
		text-decoration-style: dotted;
	}
	.amendment.proposed ins, .correction.proposed ins, .addition.proposed ins,
	ins[class^=c].proposed {
		text-decoration-style: double;
	}
	.amendment.proposed del, .correction.proposed del, .addition.proposed del,
	del[class^=c].proposed {
		text-decoration-style: double;
	}

/** Miscellaneous improvements to inline formatting ***************************/

	sup {
		vertical-align: super;
		font-size: 80%
	}

/******************************************************************************/
/*                                    Code                                    */
/******************************************************************************/

/** General monospace/pre rules ***********************************************/

	pre, code, samp {
		font-family: Menlo, Consolas, "DejaVu Sans Mono", Monaco, monospace;
		font-size: .9em;
		hyphens: none;
		text-transform: none;
		text-align: left;
		text-align: start;
		font-variant: normal;
		orphans: 3;
		widows: 3;
		page-break-before: avoid;
	}
	pre code,
	code code {
		font-size: 100%;
	}

	pre {
		margin-top: 1em;
		margin-bottom: 1em;
		overflow: auto;
	}

/** Inline Code fragments *****************************************************/

	/* Do something nice. */

/******************************************************************************/
/*                                    Links                                   */
/******************************************************************************/

/** General Hyperlinks ********************************************************/

	/* We hyperlink a lot, so make it less intrusive */
	a[href] {
		color: #034575;
		color: var(--a-normal-text);
		text-decoration: underline #707070;
		text-decoration: underline var(--a-normal-underline);
		text-decoration-skip-ink: none;
	}
	a:visited {
		color: #034575;
		color: var(--a-visited-text);
		text-decoration-color: #bbb;
		text-decoration-color: var(--a-visited-underline);
	}

	/* Indicate interaction with the link */
	a[href]:focus,
	a[href]:hover {
		text-decoration-thickness: 2px;
	}
	a[href]:active {
		color: #c00;
		color: var(--a-active-text);
		text-decoration-color: #c00;
		text-decoration-color: var(--a-active-underline);
	}

	/* Backout above styling for W3C logo */
	.head .logo,
	.head .logo a {
		border: none;
		text-decoration: none;
		background: transparent;
	}

/******************************************************************************/
/*                                    Images                                  */
/******************************************************************************/

	img {
		border-style: none;
	}

	img, svg {
		/* Intentionally not color-scheme aware. */
		background: white;
	}

	/* For autogen numbers, add
	  .caption::before, figcaption::before { content: "Figure " counter(figure) ". "; }
	*/

	figure, .figure, .sidefigure {
		page-break-inside: avoid;
		text-align: center;
		margin: 2.5em 0;
	}
	.figure img,	.sidefigure img,	figure img,
	.figure object, .sidefigure object, figure object {
		max-width: 100%;
		margin: auto;
		height: auto;
	}
	.figure pre, .sidefigure pre, figure pre {
		text-align: left;
		display: table;
		margin: 1em auto;
	}
	.figure table, figure table {
		margin: auto;
	}
	@media screen and (min-width: 20em) {
		.sidefigure {
			float: right;
			width: 50%;
			margin: 0 0 0.5em 0.5em;
		}
	}
	.caption, figcaption, caption {
		font-style: italic;
		font-size: 90%;
	}
	.caption::before, figcaption::before, figcaption > .marker {
		font-weight: bold;
	}
	.caption, figcaption {
		counter-increment: figure;
	}

	/* DL list is indented 2em, but figure inside it is not */
	dd > .figure, dd > figure { margin-left: -2em; }

/******************************************************************************/
/*                             Colored Boxes                                  */
/******************************************************************************/

	.issue, .note, .example, .assertion, .advisement, blockquote,
	.amendment, .correction, .addition {
		margin: 1em auto;
		padding: .5em;
		border: .5em;
		border-left-style: solid;
		page-break-inside: avoid;
	}
	span.issue, span.note {
		padding: .1em .5em .15em;
		border-right-style: solid;
	}

	blockquote > :first-child,
	.note  > p:first-child,
	.issue > p:first-child,
	.amendment > p:first-child,
	.correction > p:first-child,
	.addition > p:first-child {
		margin-top: 0;
	}
	blockquote > :last-child,
	.note  > p:last-child,
	.issue > p:last-child,
	.amendment > p:last-child,
	.correction > p:last-child,
	.addition > p:last-child {
		margin-bottom: 0;
	}


	.issue::before, .issue > .marker,
	.example::before, .example > .marker,
	.note::before, .note > .marker,
	details.note > summary > .marker,
	.amendment::before, .amendment > .marker,
	details.amendment > summary > .marker,
	.addition::before, .addition > .marker,
	addition.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	correction.amendment > summary > .marker
	{
		text-transform: uppercase;
		padding-right: 1em;
	}

	.example::before, .example > .marker {
		display: block;
		padding-right: 0em;
	}

/** Blockquotes ***************************************************************/

	blockquote {
		border-color: silver;
		border-color: var(--blockquote-border);
		background: transparent;
		background: var(--blockquote-bg);
		color: currentcolor;
		color: var(--blockquote-text);
	}

/** Open issue ****************************************************************/

	.issue {
		border-color: #e05252;
		border-color: var(--issue-border);
		background: #fbe9e9;
		background: var(--issue-bg);
		color: black;
		color: var(--issue-text);
		counter-increment: issue;
		overflow: auto;
	}
	.issue::before, .issue > .marker {
		color: #831616;
		color: var(--issueheading-text);
	}
	/* Add .issue::before { content: "Issue " counter(issue) " "; } for autogen numbers,
	  or use class="marker" to mark up the issue number in source. */

/** Example *******************************************************************/

	.example {
		border-color: #e0cb52;
		border-color: var(--example-border);
		background: #fcfaee;
		background: var(--example-bg);
		color: black;
		color: var(--example-text);
		counter-increment: example;
		overflow: auto;
		clear: both;
	}
	.example::before, .example > .marker {
		color: #574b0f;
		color: var(--exampleheading-text);
	}
	/* Add .example::before { content: "Example " counter(example) " "; } for autogen numbers,
	  or use class="marker" to mark up the example number in source. */

/** Non-normative Note ********************************************************/

	.note {
		border-color: #52e052;
		border-color: var(--note-border);
		background: #e9fbe9;
		background: var(--note-bg);
		color: black;
		color: var(--note-text);
		overflow: auto;
	}

	.note::before, .note > .marker,
	details.note > summary {
		color: hsl(120, 70%, 30%);
		color: var(--noteheading-text);
	}
	/* Add .note::before { content: "Note "; } for autogen label,
	  or use class="marker" to mark up the label in source. */

	details.note[open] > summary {
		border-bottom: 1px silver solid;
		border-bottom: 1px var(--notesummary-underline) solid;
	}

/** Assertion Box *************************************************************/
	/*  for assertions in algorithms */

	.assertion {
		border-color: #AAA;
		border-color: var(--assertion-border);
		background: #EEE;
		background: var(--assertion-bg);
		color: black;
		color: var(--assertion-text);
	}

/** Advisement Box ************************************************************/
	/*  for attention-grabbing normative statements */

	.advisement {
		border-color: orange;
		border-color: var(--advisement-border);
		border-style: none solid;
		background: #fec;
		background: var(--advisement-bg);
		color: black;
		color: var(--advisement-text);
	}
	strong.advisement {
		display: block;
		text-align: center;
	}
	.advisement::before, .advisement > .marker {
		color: #b35f00;
		color: var(--advisementheading-text);
	}

/** Amendment Box *************************************************************/

	.amendment, .correction, .addition {
		border-color: #330099;
		border-color: var(--amendment-border);
		background: #F5F0FF;
		background: var(--amendment-bg);
		color: black;
		color: var(--amendment-text);
	}
	.amendment.proposed, .correction.proposed, .addition.proposed {
		border-style: solid;
		border-block-width: 0.25em;
	}
	.amendment::before, .amendment > .marker,
	details.amendment > summary::before, details.amendment > summary > .marker,
	.correction::before, .correction > .marker,
	details.correction > summary::before, details.correction > summary > .marker,
	.addition::before, .addition > .marker,
	details.addition > summary::before, details.addition > summary > .marker {
		color: #220066;
		color: var(--amendmentheading-text);
	}
	.amendment.proposed::before, .amendment.proposed > .marker,
	details.amendment.proposed > summary::before, details.amendment.proposed > summary > .marker,
	.correction.proposed::before, .correction.proposed > .marker,
	details.correction.proposed > summary::before, details.correction.proposed > summary > .marker,
	.addition.proposed::before, .addition.proposed > .marker,
	details.addition.proposed > summary::before, details.addition.proposed > summary > .marker {
		font-weight: bold;
	}

/** Spec Obsoletion Notice ****************************************************/
	/* obnoxious obsoletion notice for older/abandoned specs. */

	details {
		display: block;
	}
	summary {
		font-weight: bolder;
	}

	.annoying-warning:not(details),
	details.annoying-warning:not([open]) > summary,
	details.annoying-warning[open] {
		background: hsla(40,100%,50%,0.95);
		background: var(--warning-bg);
		color: black;
		color: var(--warning-text);
		padding: .75em 1em;
		border: red;
		border: var(--warning-border);
		border-style: solid none;
		box-shadow: 0 2px 8px black;
		text-align: center;
	}
	.annoying-warning :last-child {
		margin-bottom: 0;
	}

@media not print {
	details.annoying-warning[open] {
		position: fixed;
		left: 0;
		right: 0;
		bottom: 2em;
		z-index: 1000;
	}
}

	details.annoying-warning:not([open]) > summary {
		text-align: center;
	}

/** Entity Definition Boxes ***************************************************/

	.def {
		padding: .5em 1em;
		background: #def;
		background: var(--def-bg);
		margin: 1.2em 0;
		border-left: 0.5em solid #8ccbf2;
		border-left: 0.5em solid var(--def-border);
		color: black;
		color: var(--def-text);
	}

/******************************************************************************/
/*                                    Tables                                  */
/******************************************************************************/

	th, td {
		text-align: left;
		text-align: start;
	}

/** Property/Descriptor Definition Tables *************************************/

	table.def {
		/* inherits .def box styling, see above */
		width: 100%;
		border-spacing: 0;
	}

	table.def td,
	table.def th {
		padding: 0.5em;
		vertical-align: baseline;
		border-bottom: 1px solid #bbd7e9;
		border-bottom: 1px solid var(--defrow-border);
	}

	table.def > tbody > tr:last-child th,
	table.def > tbody > tr:last-child td {
		border-bottom: 0;
	}

	table.def th {
		font-style: italic;
		font-weight: normal;
		padding-left: 1em;
		width: 3em;
	}

	/* For when values are extra-complex and need formatting for readability */
	table td.pre {
		white-space: pre-wrap;
	}

	/* A footnote at the bottom of a def table */
	table.def td.footnote {
		padding-top: 0.6em;
	}
	table.def td.footnote::before {
		content: " ";
		display: block;
		height: 0.6em;
		width: 4em;
		border-top: thin solid;
	}

/** Data tables (and properly marked-up index tables) *************************/
	/*
		<table class="data"> highlights structural relationships in a table
		when correct markup is used (e.g. thead/tbody, th vs. td, scope attribute)

		Use class="complex data" for particularly complicated tables --
		(This will draw more lines: busier, but clearer.)

		Use class="long" on table cells with paragraph-like contents
		(This will adjust text alignment accordingly.)
		Alternately use class="longlastcol" on tables, to have the last column assume "long".
	*/

	table {
		word-wrap: normal;
		overflow-wrap: normal;
		hyphens: manual;
	}

	table.data,
	table.index {
		margin: 1em auto;
		border-collapse: collapse;
		border: hidden;
		width: 100%;
	}
	table.data caption,
	table.index caption {
		max-width: 50em;
		margin: 0 auto 1em;
	}

	table.data td,  table.data th,
	table.index td, table.index th {
		padding: 0.5em 1em;
		border-width: 1px;
		border-color: silver;
		border-color: var(--datacell-border);
		border-top-style: solid;
	}

	table.data thead td:empty {
		padding: 0;
		border: 0;
	}

	table.data  thead,
	table.index thead,
	table.data  tbody,
	table.index tbody {
		border-bottom: 2px solid;
	}

	table.data colgroup,
	table.index colgroup {
		border-left: 2px solid;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		border-right: 2px solid;
		border-top: 1px solid silver;
		border-top: 1px solid var(--datacell-border);
		padding-right: 1em;
	}

	table.data th[colspan],
	table.data td[colspan] {
		text-align: center;
	}

	table.complex.data th,
	table.complex.data td {
		border: 1px solid silver;
		border: 1px solid var(--datacell-border);
		text-align: center;
	}

	table.data.longlastcol td:last-child,
	table.data td.long {
		vertical-align: baseline;
		text-align: left;
	}

	table.data img {
		vertical-align: middle;
	}


/*
Alternate table alignment rules

	table.data,
	table.index {
		text-align: center;
	}

	table.data  thead th[scope="row"],
	table.index thead th[scope="row"] {
		text-align: right;
	}

	table.data  tbody th:first-child,
	table.index tbody th:first-child  {
		text-align: right;
	}

Possible extra rowspan handling

	table.data  tbody th[rowspan]:not([rowspan='1']),
	table.index tbody th[rowspan]:not([rowspan='1']),
	table.data  tbody td[rowspan]:not([rowspan='1']),
	table.index tbody td[rowspan]:not([rowspan='1']) {
		border-left: 1px solid silver;
	}

	table.data  tbody th[rowspan]:first-child,
	table.index tbody th[rowspan]:first-child,
	table.data  tbody td[rowspan]:first-child,
	table.index tbody td[rowspan]:first-child{
		border-left: 0;
		border-right: 1px solid silver;
	}
*/

/******************************************************************************/
/*                                  Indices                                   */
/******************************************************************************/


/** Table of Contents *********************************************************/

	.toc a {
		/* More spacing; use padding to make it part of the click target. */
		padding: 0.1rem 1px 0;
		/* Larger, more consistently-sized click target */
		display: block;
		/* Switch to using border-bottom for underlines */
		text-decoration: none;
		border-bottom: 1px solid;
		/* Reverse color scheme */
		color: black;
		color: var(--toclink-text);
		border-color: #3980b5;
		border-color: var(--toclink-underline);
	}
	.toc a:visited {
		color: black;
		color: var(--toclink-visited-text);
		border-color: #054572;
		border-color: var(--toclink-visited-underline);
	}
	.toc a:focus,
	.toc a:hover {
		background: rgba(75%, 75%, 75%, .25);
		background: var(--a-hover-bg);
		border-bottom-width: 3px;
		margin-bottom: -2px;
	}
	.toc a:not(:focus):not(:hover) {
		/* Allow colors to cascade through from link styling */
		border-bottom-color: transparent;
	}

	.toc, .toc ol, .toc ul, .toc li {
		list-style: none; /* Numbers must be inlined into source */
		/* because generated content isn't search/selectable and markers can't do multilevel yet */
		margin:  0;
		padding: 0;
	}
	.toc {
		line-height: 1.1em;
	}

	/* ToC not indented until third level, but font style & margins show hierarchy */
	.toc > li			{ font-weight: bold;   }
	.toc > li li		 { font-weight: normal; }
	.toc > li li li	  { font-size:   95%;	}
	.toc > li li li li	{ font-size:   90%;	}
	.toc > li li li li li { font-size:   85%;	}

	/* @supports not (display:grid) { */
		.toc > li			{ margin: 1.5rem 0;	}
		.toc > li li		 { margin: 0.3rem 0;	}
		.toc > li li li	  { margin-left: 2rem;   }

		/* Section numbers in a column of their own */
		.toc .secno {
			float: left;
			width: 4rem;
			white-space: nowrap;
		}
		.toc > li li li li .secno { font-size: 85%; }
		.toc > li li li li li .secno { font-size: 100%; }

		.toc li {
			clear: both;
		}

		:not(li) > .toc			 { margin-left:  5rem; }
		.toc .secno				 { margin-left: -5rem; }
		.toc > li li li .secno	  { margin-left: -7rem; }
		.toc > li li li li .secno	{ margin-left: -9rem; }
		.toc > li li li li li .secno { margin-left: -11rem; }

		/* Tighten up indentation in narrow ToCs */
		@media (max-width: 30em) {
			:not(li) > .toc			 { margin-left:  4rem; }
			.toc .secno				 { margin-left: -4rem; }
			.toc > li li li			 { margin-left:  1rem; }
			.toc > li li li .secno	  { margin-left: -5rem; }
			.toc > li li li li .secno	{ margin-left: -6rem; }
			.toc > li li li li li .secno { margin-left: -7rem; }
		}
		/* Loosen it on wide screens */
		@media screen and (min-width: 78em) {
			body:not(.toc-inline) :not(li) > .toc			 { margin-left:  4rem; }
			body:not(.toc-inline) .toc .secno				 { margin-left: -4rem; }
			body:not(.toc-inline) .toc > li li li			 { margin-left:  1rem; }
			body:not(.toc-inline) .toc > li li li .secno	  { margin-left: -5rem; }
			body:not(.toc-inline) .toc > li li li li .secno	{ margin-left: -6rem; }
			body:not(.toc-inline) .toc > li li li li li .secno { margin-left: -7rem; }
	}
	/* } */

	@supports (display:grid) and (display:contents) {
		/* Use #toc over .toc to override non-@supports rules. */
		#toc {
			display: grid;
			align-content: start;
			grid-template-columns: auto 1fr;
			grid-column-gap: 1rem;
			column-gap: 1rem;
			grid-row-gap: .6rem;
			row-gap: .6rem;
		}
		#toc h2 {
			grid-column: 1 / -1;
			margin-bottom: 0;
		}
		#toc ol,
		#toc li,
		#toc a {
			display: contents;
			/* Switch <a> to subgrid when supported */
		}
		#toc span {
			margin: 0;
		}
		#toc > .toc > li > a > span {
			/* The spans of the top-level list,
			  comprising the first items of each top-level section. */
			margin-top: 1.1rem;
		}
		#toc#toc .secno { /* Ugh, need more specificity to override base.css */
			grid-column: 1;
			width: auto;
			margin-left: 0;
		}
		#toc .content {
			grid-column: 2;
			width: auto;
			margin-right: 1rem;
			border-bottom: 3px solid transparent;
			margin-bottom: -3px;
		}
		#toc .content:hover,
		#toc .content:focus {
			background: rgba(75%, 75%, 75%, .25);
			background: var(--a-hover-bg);
			border-bottom-color: #054572;
			border-bottom-color: var(--toclink-underline);
		}
		#toc li li li .content {
			margin-left: 1rem;
		}
		#toc li li li li .content {
			margin-left: 2rem;
		}
	}


/** Index *********************************************************************/

	/* Index Lists: Layout */
	ul.index	  { margin-left: 0; columns: 15em; text-indent: 1em hanging; }
	ul.index li	{ margin-left: 0; list-style: none; break-inside: avoid; }
	ul.index li li { margin-left: 1em; }
	ul.index dl	{ margin-top: 0; }
	ul.index dt	{ margin: .2em 0 .2em 20px;}
	ul.index dd	{ margin: .2em 0 .2em 40px;}
	/* Index Lists: Typography */
	ul.index ul,
	ul.index dl { font-size: smaller; }
	@media not print {
		ul.index li a + span {
			white-space: nowrap;
			color: transparent; }
		ul.index li a:hover + span,
		ul.index li a:focus + span {
			color: #707070;
			color: var(--indexinfo-text);
		}
	}

/** Index Tables *****************************************************/
	/* See also the data table styling section, which this effectively subclasses */

	table.index {
		font-size: small;
		border-collapse: collapse;
		border-spacing: 0;
		text-align: left;
		margin: 1em 0;
	}

	table.index td,
	table.index th {
		padding: 0.4em;
	}

	table.index tr:hover td:not([rowspan]),
	table.index tr:hover th:not([rowspan]) {
		color: black;
		color: var(--indextable-hover-text);
		background: #f7f8f9;
		background: var(--indextable-hover-bg);
	}

	/* The link in the first column in the property table (formerly a TD) */
	table.index th:first-child a {
		font-weight: bold;
	}

/** Outdated warning **********************************************************/

.outdated-spec {
	color: black;
	color: var(--outdatedspec-text);
	background-color: rgba(0,0,0,0.5);
	background-color: var(--outdatedspec-bg);
}

.outdated-warning {
	position: fixed;
	bottom: 50%;
	left: 0;
	right: 0;
	margin: 0 auto;
	width: 50%;
	background: maroon;
	background: var(--outdated-bg);
	color: white;
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	color: var(--outdated-text);
	border-radius: 1em;
	box-shadow: 0 0 1em red;
	box-shadow: 0 0 1em var(--outdated-shadow);
	padding: 2em;
	text-align: center;
	z-index: 2;
}

.outdated-warning a {
	color: currentcolor;
	background: transparent;
}

.edited-rec-warning {
	background: darkorange;
	background: var(--editedrec-bg);
	box-shadow: 0 0 1em;
}

.outdated-warning button {
	position: absolute;
	top: 0;
	right:0;
	margin: 0;
	border: 0;
	padding: 0.25em 0.5em;
	background: transparent;
	color: white;
	color: var(--outdated-text);
	font:1em sans-serif;
	text-align:center;
}

.outdated-warning span {
	display: block;
}

.outdated-collapsed {
	bottom: 0;
	border-radius: 0;
	width: 100%;
	padding: 0;
}

/******************************************************************************/
/*                                    Print                                   */
/******************************************************************************/

	@media print {
		/* Pages have their own margins. */
		html {
			margin: 0;
		}
		/* Serif for print. */
		body {
			font-family: serif;
		}

		.outdated-warning {
			position: absolute;
			border-style: solid;
			border-color: red;
		}

		.outdated-warning input {
			display: none;
		}
	}
	@page {
		margin: 1.5cm 1.1cm;
	}



/******************************************************************************/
/*                             Overflow Control                               */
/******************************************************************************/

	.figure .caption, .sidefigure .caption, figcaption {
		/* in case figure is overlarge, limit caption to 50em */
		max-width: 50rem;
		margin-left: auto;
		margin-right: auto;
	}
	.overlarge {
		/* Magic to create good item positioning:
		  "content column" is 50ems wide at max; less on smaller screens.
		  Extra space (after ToC + content) is empty on the right.

		  1. When item < content column, centers item in column.
		  2. When content < item < available, left-aligns.
		  3. When item > available, fills available + scroll bar.
		*/
		display: grid;
		grid-template-columns: minmax(0, 50em);
	}
	.overlarge > table {
		/* limit preferred width of table */
		max-width: 50em;
		margin-left: auto;
		margin-right: auto;
	}

	@media (min-width: 55em) {
		.overlarge {
			margin-right: calc(13px + 26.5rem - 50vw);
			max-width: none;
		}
	}
	@media screen and (min-width: 78em) {
		body:not(.toc-inline) .overlarge {
			/* 30.5em body padding 50em content area */
			margin-right: calc(40em - 50vw) !important;
		}
	}
	@media screen and (min-width: 90em) {
		body:not(.toc-inline) .overlarge {
			/* 4em html margin 30.5em body padding 50em content area */
			margin-right: calc(84.5em - 100vw) !important;
		}
	}

	@media not print {
		.overlarge {
			overflow-x: auto;
			/* See Lea Verou's explanation background-attachment:
			* http://lea.verou.me/2012/04/background-attachment-local/
			*
			background: top left  / 4em 100% linear-gradient(to right,  #ffffff, rgba(255, 255, 255, 0)) local,
						top right / 4em 100% linear-gradient(to left, #ffffff, rgba(255, 255, 255, 0)) local,
						top left  / 1em 100% linear-gradient(to right,  #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						top right / 1em 100% linear-gradient(to left, #c3c3c5, rgba(195, 195, 197, 0)) scroll,
						white;
			background-repeat: no-repeat;
			*/
		}
	}
</style>
<style>
    table, th, td {
      border: 1px solid black;
      border-collapse: collapse;
      vertical-align: top;
    }
    th, td {
      border-left: none;
      border-right: none;
      padding: 0px 10px;
    }
    th {
      text-align: center;
    }

    del { background: #fcc; color: #000; text-decoration: line-through; }
    ins { background: #cfc; color: #000; }
    blockquote .highlight:not(.idl) { background: initial; margin: initial; padding: 0.5em }
    blockquote ul { background: inherit; }
    blockquote code.highlight:not(.idl) { padding: initial; }
    blockquote c-[a] { color: inherit; } /* Keyword.Declaration */
    blockquote c-[b] { color: inherit; } /* Keyword.Type */
    blockquote c-[c] { color: inherit; } /* Comment */
    blockquote c-[d] { color: inherit; } /* Comment.Multiline */
    blockquote c-[e] { color: inherit; } /* Name.Attribute */
    blockquote c-[f] { color: inherit; } /* Name.Tag */
    blockquote c-[g] { color: inherit; } /* Name.Variable */
    blockquote c-[k] { color: inherit; } /* Keyword */
    blockquote c-[l] { color: inherit; } /* Literal */
    blockquote c-[m] { color: inherit; } /* Literal.Number */
    blockquote c-[n] { color: inherit; } /* Name */
    blockquote c-[o] { color: inherit; } /* Operator */
    blockquote c-[p] { color: inherit; } /* Punctuation */
    blockquote c-[s] { color: inherit; } /* Literal.String */
    blockquote c-[t] { color: inherit; } /* Literal.String.Single */
    blockquote c-[u] { color: inherit; } /* Literal.String.Double */
    blockquote c-[cp] { color: inherit; } /* Comment.Preproc */
    blockquote c-[c1] { color: inherit; } /* Comment.Single */
    blockquote c-[cs] { color: inherit; } /* Comment.Special */
    blockquote c-[kc] { color: inherit; } /* Keyword.Constant */
    blockquote c-[kn] { color: inherit; } /* Keyword.Namespace */
    blockquote c-[kp] { color: inherit; } /* Keyword.Pseudo */
    blockquote c-[kr] { color: inherit; } /* Keyword.Reserved */
    blockquote c-[ld] { color: inherit; } /* Literal.Date */
    blockquote c-[nc] { color: inherit; } /* Name.Class */
    blockquote c-[no] { color: inherit; } /* Name.Constant */
    blockquote c-[nd] { color: inherit; } /* Name.Decorator */
    blockquote c-[ni] { color: inherit; } /* Name.Entity */
    blockquote c-[ne] { color: inherit; } /* Name.Exception */
    blockquote c-[nf] { color: inherit; } /* Name.Function */
    blockquote c-[nl] { color: inherit; } /* Name.Label */
    blockquote c-[nn] { color: inherit; } /* Name.Namespace */
    blockquote c-[py] { color: inherit; } /* Name.Property */
    blockquote c-[ow] { color: inherit; } /* Operator.Word */
    blockquote c-[mb] { color: inherit; } /* Literal.Number.Bin */
    blockquote c-[mf] { color: inherit; } /* Literal.Number.Float */
    blockquote c-[mh] { color: inherit; } /* Literal.Number.Hex */
    blockquote c-[mi] { color: inherit; } /* Literal.Number.Integer */
    blockquote c-[mo] { color: inherit; } /* Literal.Number.Oct */
    blockquote c-[sb] { color: inherit; } /* Literal.String.Backtick */
    blockquote c-[sc] { color: inherit; } /* Literal.String.Char */
    blockquote c-[sd] { color: inherit; } /* Literal.String.Doc */
    blockquote c-[se] { color: inherit; } /* Literal.String.Escape */
    blockquote c-[sh] { color: inherit; } /* Literal.String.Heredoc */
    blockquote c-[si] { color: inherit; } /* Literal.String.Interpol */
    blockquote c-[sx] { color: inherit; } /* Literal.String.Other */
    blockquote c-[sr] { color: inherit; } /* Literal.String.Regex */
    blockquote c-[ss] { color: inherit; } /* Literal.String.Symbol */
    blockquote c-[vc] { color: inherit; } /* Name.Variable.Class */
    blockquote c-[vg] { color: inherit; } /* Name.Variable.Global */
    blockquote c-[vi] { color: inherit; } /* Name.Variable.Instance */
    blockquote c-[il] { color: inherit; } /* Literal.Number.Integer.Long */
  </style>
  <meta content="Bikeshed version d765c696b, updated Fri Mar 8 15:58:52 2024 -0800" name="generator">
  <link href="http://wg21.link/P3292R0" rel="canonical">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="dark light" name="color-scheme">
<style>/* Boilerplate: style-autolinks */
.css.css, .property.property, .descriptor.descriptor {
    color: var(--a-normal-text);
    font-size: inherit;
    font-family: inherit;
}
.css::before, .property::before, .descriptor::before {
    content: "‘";
}
.css::after, .property::after, .descriptor::after {
    content: "’";
}
.property, .descriptor {
    /* Don't wrap property and descriptor names */
    white-space: nowrap;
}
.type { /* CSS value <type> */
    font-style: italic;
}
pre .property::before, pre .property::after {
    content: "";
}
[data-link-type="property"]::before,
[data-link-type="propdesc"]::before,
[data-link-type="descriptor"]::before,
[data-link-type="value"]::before,
[data-link-type="function"]::before,
[data-link-type="at-rule"]::before,
[data-link-type="selector"]::before,
[data-link-type="maybe"]::before {
    content: "‘";
}
[data-link-type="property"]::after,
[data-link-type="propdesc"]::after,
[data-link-type="descriptor"]::after,
[data-link-type="value"]::after,
[data-link-type="function"]::after,
[data-link-type="at-rule"]::after,
[data-link-type="selector"]::after,
[data-link-type="maybe"]::after {
    content: "’";
}

[data-link-type].production::before,
[data-link-type].production::after,
.prod [data-link-type]::before,
.prod [data-link-type]::after {
    content: "";
}

[data-link-type=element],
[data-link-type=element-attr] {
    font-family: Menlo, Consolas, "DejaVu Sans Mono", monospace;
    font-size: .9em;
}
[data-link-type=element]::before { content: "<" }
[data-link-type=element]::after  { content: ">" }

[data-link-type=biblio] {
    white-space: pre;
}

@media (prefers-color-scheme: dark) {
    :root {
        --selflink-text: black;
        --selflink-bg: silver;
        --selflink-hover-text: white;
    }
}
</style>
<style>/* Boilerplate: style-colors */
/* Any --*-text not paired with a --*-bg is assumed to have a transparent bg */
:root {
    color-scheme: light dark;

    --text: black;
    --bg: white;

    --unofficial-watermark: url(https://www.w3.org/StyleSheets/TR/2016/logos/UD-watermark);

    --logo-bg: #1a5e9a;
    --logo-active-bg: #c00;
    --logo-text: white;

    --tocnav-normal-text: #707070;
    --tocnav-normal-bg: var(--bg);
    --tocnav-hover-text: var(--tocnav-normal-text);
    --tocnav-hover-bg: #f8f8f8;
    --tocnav-active-text: #c00;
    --tocnav-active-bg: var(--tocnav-normal-bg);

    --tocsidebar-text: var(--text);
    --tocsidebar-bg: #f7f8f9;
    --tocsidebar-shadow: rgba(0,0,0,.1);
    --tocsidebar-heading-text: hsla(203,20%,40%,.7);

    --toclink-text: var(--text);
    --toclink-underline: #3980b5;
    --toclink-visited-text: var(--toclink-text);
    --toclink-visited-underline: #054572;

    --heading-text: #005a9c;

    --hr-text: var(--text);

    --algo-border: #def;

    --del-text: red;
    --del-bg: transparent;
    --ins-text: #080;
    --ins-bg: transparent;

    --a-normal-text: #034575;
    --a-normal-underline: #bbb;
    --a-visited-text: var(--a-normal-text);
    --a-visited-underline: #707070;
    --a-hover-bg: rgba(75%, 75%, 75%, .25);
    --a-active-text: #c00;
    --a-active-underline: #c00;

    --blockquote-border: silver;
    --blockquote-bg: transparent;
    --blockquote-text: currentcolor;

    --issue-border: #e05252;
    --issue-bg: #fbe9e9;
    --issue-text: var(--text);
    --issueheading-text: #831616;

    --example-border: #e0cb52;
    --example-bg: #fcfaee;
    --example-text: var(--text);
    --exampleheading-text: #574b0f;

    --note-border: #52e052;
    --note-bg: #e9fbe9;
    --note-text: var(--text);
    --noteheading-text: hsl(120, 70%, 30%);
    --notesummary-underline: silver;

    --assertion-border: #aaa;
    --assertion-bg: #eee;
    --assertion-text: black;

    --advisement-border: orange;
    --advisement-bg: #fec;
    --advisement-text: var(--text);
    --advisementheading-text: #b35f00;

    --warning-border: red;
    --warning-bg: hsla(40,100%,50%,0.95);
    --warning-text: var(--text);

    --amendment-border: #330099;
    --amendment-bg: #F5F0FF;
    --amendment-text: var(--text);
    --amendmentheading-text: #220066;

    --def-border: #8ccbf2;
    --def-bg: #def;
    --def-text: var(--text);
    --defrow-border: #bbd7e9;

    --datacell-border: silver;

    --indexinfo-text: #707070;

    --indextable-hover-text: black;
    --indextable-hover-bg: #f7f8f9;

    --outdatedspec-bg: rgba(0, 0, 0, .5);
    --outdatedspec-text: black;
    --outdated-bg: maroon;
    --outdated-text: white;
    --outdated-shadow: red;

    --editedrec-bg: darkorange;
}

@media (prefers-color-scheme: dark) {
    :root {
        --text: #ddd;
        --bg: black;

        --unofficial-watermark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400'%3E%3Cg fill='%23100808' transform='translate(200 200) rotate(-45) translate(-200 -200)' stroke='%23100808' stroke-width='3'%3E%3Ctext x='50%25' y='220' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EUNOFFICIAL%3C/text%3E%3Ctext x='50%25' y='305' style='font: bold 70px sans-serif; text-anchor: middle; letter-spacing: 6px;'%3EDRAFT%3C/text%3E%3C/g%3E%3C/svg%3E");

        --logo-bg: #1a5e9a;
        --logo-active-bg: #c00;
        --logo-text: white;

        --tocnav-normal-text: #999;
        --tocnav-normal-bg: var(--bg);
        --tocnav-hover-text: var(--tocnav-normal-text);
        --tocnav-hover-bg: #080808;
        --tocnav-active-text: #f44;
        --tocnav-active-bg: var(--tocnav-normal-bg);

        --tocsidebar-text: var(--text);
        --tocsidebar-bg: #080808;
        --tocsidebar-shadow: rgba(255,255,255,.1);
        --tocsidebar-heading-text: hsla(203,20%,40%,.7);

        --toclink-text: var(--text);
        --toclink-underline: #6af;
        --toclink-visited-text: var(--toclink-text);
        --toclink-visited-underline: #054572;

        --heading-text: #8af;

        --hr-text: var(--text);

        --algo-border: #456;

        --del-text: #f44;
        --del-bg: transparent;
        --ins-text: #4a4;
        --ins-bg: transparent;

        --a-normal-text: #6af;
        --a-normal-underline: #555;
        --a-visited-text: var(--a-normal-text);
        --a-visited-underline: var(--a-normal-underline);
        --a-hover-bg: rgba(25%, 25%, 25%, .2);
        --a-active-text: #f44;
        --a-active-underline: var(--a-active-text);

        --borderedblock-bg: rgba(255, 255, 255, .05);

        --blockquote-border: silver;
        --blockquote-bg: var(--borderedblock-bg);
        --blockquote-text: currentcolor;

        --issue-border: #e05252;
        --issue-bg: var(--borderedblock-bg);
        --issue-text: var(--text);
        --issueheading-text: hsl(0deg, 70%, 70%);

        --example-border: hsl(50deg, 90%, 60%);
        --example-bg: var(--borderedblock-bg);
        --example-text: var(--text);
        --exampleheading-text: hsl(50deg, 70%, 70%);

        --note-border: hsl(120deg, 100%, 35%);
        --note-bg: var(--borderedblock-bg);
        --note-text: var(--text);
        --noteheading-text: hsl(120, 70%, 70%);
        --notesummary-underline: silver;

        --assertion-border: #444;
        --assertion-bg: var(--borderedblock-bg);
        --assertion-text: var(--text);

        --advisement-border: orange;
        --advisement-bg: #222218;
        --advisement-text: var(--text);
        --advisementheading-text: #f84;

        --warning-border: red;
        --warning-bg: hsla(40,100%,20%,0.95);
        --warning-text: var(--text);

        --amendment-border: #330099;
        --amendment-bg: #080010;
        --amendment-text: var(--text);
        --amendmentheading-text: #cc00ff;

        --def-border: #8ccbf2;
        --def-bg: #080818;
        --def-text: var(--text);
        --defrow-border: #136;

        --datacell-border: silver;

        --indexinfo-text: #aaa;

        --indextable-hover-text: var(--text);
        --indextable-hover-bg: #181818;

        --outdatedspec-bg: rgba(255, 255, 255, .5);
        --outdatedspec-text: black;
        --outdated-bg: maroon;
        --outdated-text: white;
        --outdated-shadow: red;

        --editedrec-bg: darkorange;
    }
    /* In case a transparent-bg image doesn't expect to be on a dark bg,
       which is quite common in practice... */
    img { background: white; }
}
</style>
<style>/* Boilerplate: style-counters */
body {
    counter-reset: example figure issue;
}
.issue {
    counter-increment: issue;
}
.issue:not(.no-marker)::before {
    content: "Issue " counter(issue);
}

.example {
    counter-increment: example;
}
.example:not(.no-marker)::before {
    content: "Example " counter(example);
}
.invalid.example:not(.no-marker)::before,
.illegal.example:not(.no-marker)::before {
    content: "Invalid Example" counter(example);
}

figcaption {
    counter-increment: figure;
}
figcaption:not(.no-marker)::before {
    content: "Figure " counter(figure) " ";
}
</style>
<style>/* Boilerplate: style-issues */
a[href].issue-return {
    float: right;
    float: inline-end;
    color: var(--issueheading-text);
    font-weight: bold;
    text-decoration: none;
}
</style>
<style>/* Boilerplate: style-md-lists */
/* This is a weird hack for me not yet following the commonmark spec
   regarding paragraph and lists. */
[data-md] > :first-child {
    margin-top: 0;
}
[data-md] > :last-child {
    margin-bottom: 0;
}
</style>
<style>/* Boilerplate: style-selflinks */
:root {
    --selflink-text: white;
    --selflink-bg: gray;
    --selflink-hover-text: black;
}
.heading, .issue, .note, .example, li, dt {
    position: relative;
}
a.self-link {
    position: absolute;
    top: 0;
    left: calc(-1 * (3.5rem - 26px));
    width: calc(3.5rem - 26px);
    height: 2em;
    text-align: center;
    border: none;
    transition: opacity .2s;
    opacity: .5;
}
a.self-link:hover {
    opacity: 1;
}
.heading > a.self-link {
    font-size: 83%;
}
.example > a.self-link,
.note > a.self-link,
.issue > a.self-link {
    /* These blocks are overflow:auto, so positioning outside
       doesn't work. */
    left: auto;
    right: 0;
}
li > a.self-link {
    left: calc(-1 * (3.5rem - 26px) - 2em);
}
dfn > a.self-link {
    top: auto;
    left: auto;
    opacity: 0;
    width: 1.5em;
    height: 1.5em;
    background: var(--selflink-bg);
    color: var(--selflink-text);
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: var(--selflink-hover-text);
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }
</style>
<style>/* Boilerplate: style-syntax-highlighting */
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }

.highlight:not(.idl) { background: rgba(0, 0, 0, .03); }
c-[a] { color: #990055 } /* Keyword.Declaration */
c-[b] { color: #990055 } /* Keyword.Type */
c-[c] { color: #708090 } /* Comment */
c-[d] { color: #708090 } /* Comment.Multiline */
c-[e] { color: #0077aa } /* Name.Attribute */
c-[f] { color: #669900 } /* Name.Tag */
c-[g] { color: #222222 } /* Name.Variable */
c-[k] { color: #990055 } /* Keyword */
c-[l] { color: #000000 } /* Literal */
c-[m] { color: #000000 } /* Literal.Number */
c-[n] { color: #0077aa } /* Name */
c-[o] { color: #999999 } /* Operator */
c-[p] { color: #999999 } /* Punctuation */
c-[s] { color: #a67f59 } /* Literal.String */
c-[t] { color: #a67f59 } /* Literal.String.Single */
c-[u] { color: #a67f59 } /* Literal.String.Double */
c-[cp] { color: #708090 } /* Comment.Preproc */
c-[c1] { color: #708090 } /* Comment.Single */
c-[cs] { color: #708090 } /* Comment.Special */
c-[kc] { color: #990055 } /* Keyword.Constant */
c-[kn] { color: #990055 } /* Keyword.Namespace */
c-[kp] { color: #990055 } /* Keyword.Pseudo */
c-[kr] { color: #990055 } /* Keyword.Reserved */
c-[ld] { color: #000000 } /* Literal.Date */
c-[nc] { color: #0077aa } /* Name.Class */
c-[no] { color: #0077aa } /* Name.Constant */
c-[nd] { color: #0077aa } /* Name.Decorator */
c-[ni] { color: #0077aa } /* Name.Entity */
c-[ne] { color: #0077aa } /* Name.Exception */
c-[nf] { color: #0077aa } /* Name.Function */
c-[nl] { color: #0077aa } /* Name.Label */
c-[nn] { color: #0077aa } /* Name.Namespace */
c-[py] { color: #0077aa } /* Name.Property */
c-[ow] { color: #999999 } /* Operator.Word */
c-[mb] { color: #000000 } /* Literal.Number.Bin */
c-[mf] { color: #000000 } /* Literal.Number.Float */
c-[mh] { color: #000000 } /* Literal.Number.Hex */
c-[mi] { color: #000000 } /* Literal.Number.Integer */
c-[mo] { color: #000000 } /* Literal.Number.Oct */
c-[sb] { color: #a67f59 } /* Literal.String.Backtick */
c-[sc] { color: #a67f59 } /* Literal.String.Char */
c-[sd] { color: #a67f59 } /* Literal.String.Doc */
c-[se] { color: #a67f59 } /* Literal.String.Escape */
c-[sh] { color: #a67f59 } /* Literal.String.Heredoc */
c-[si] { color: #a67f59 } /* Literal.String.Interpol */
c-[sx] { color: #a67f59 } /* Literal.String.Other */
c-[sr] { color: #a67f59 } /* Literal.String.Regex */
c-[ss] { color: #a67f59 } /* Literal.String.Symbol */
c-[vc] { color: #0077aa } /* Name.Variable.Class */
c-[vg] { color: #0077aa } /* Name.Variable.Global */
c-[vi] { color: #0077aa } /* Name.Variable.Instance */
c-[il] { color: #000000 } /* Literal.Number.Integer.Long */

@media (prefers-color-scheme: dark) {
    .highlight:not(.idl) { background: rgba(255, 255, 255, .05); }

    c-[a] { color: #d33682 } /* Keyword.Declaration */
    c-[b] { color: #d33682 } /* Keyword.Type */
    c-[c] { color: #2aa198 } /* Comment */
    c-[d] { color: #2aa198 } /* Comment.Multiline */
    c-[e] { color: #268bd2 } /* Name.Attribute */
    c-[f] { color: #b58900 } /* Name.Tag */
    c-[g] { color: #cb4b16 } /* Name.Variable */
    c-[k] { color: #d33682 } /* Keyword */
    c-[l] { color: #657b83 } /* Literal */
    c-[m] { color: #657b83 } /* Literal.Number */
    c-[n] { color: #268bd2 } /* Name */
    c-[o] { color: #657b83 } /* Operator */
    c-[p] { color: #657b83 } /* Punctuation */
    c-[s] { color: #6c71c4 } /* Literal.String */
    c-[t] { color: #6c71c4 } /* Literal.String.Single */
    c-[u] { color: #6c71c4 } /* Literal.String.Double */
    c-[ch] { color: #2aa198 } /* Comment.Hashbang */
    c-[cp] { color: #2aa198 } /* Comment.Preproc */
    c-[cpf] { color: #2aa198 } /* Comment.PreprocFile */
    c-[c1] { color: #2aa198 } /* Comment.Single */
    c-[cs] { color: #2aa198 } /* Comment.Special */
    c-[kc] { color: #d33682 } /* Keyword.Constant */
    c-[kn] { color: #d33682 } /* Keyword.Namespace */
    c-[kp] { color: #d33682 } /* Keyword.Pseudo */
    c-[kr] { color: #d33682 } /* Keyword.Reserved */
    c-[ld] { color: #657b83 } /* Literal.Date */
    c-[nc] { color: #268bd2 } /* Name.Class */
    c-[no] { color: #268bd2 } /* Name.Constant */
    c-[nd] { color: #268bd2 } /* Name.Decorator */
    c-[ni] { color: #268bd2 } /* Name.Entity */
    c-[ne] { color: #268bd2 } /* Name.Exception */
    c-[nf] { color: #268bd2 } /* Name.Function */
    c-[nl] { color: #268bd2 } /* Name.Label */
    c-[nn] { color: #268bd2 } /* Name.Namespace */
    c-[py] { color: #268bd2 } /* Name.Property */
    c-[ow] { color: #657b83 } /* Operator.Word */
    c-[mb] { color: #657b83 } /* Literal.Number.Bin */
    c-[mf] { color: #657b83 } /* Literal.Number.Float */
    c-[mh] { color: #657b83 } /* Literal.Number.Hex */
    c-[mi] { color: #657b83 } /* Literal.Number.Integer */
    c-[mo] { color: #657b83 } /* Literal.Number.Oct */
    c-[sa] { color: #6c71c4 } /* Literal.String.Affix */
    c-[sb] { color: #6c71c4 } /* Literal.String.Backtick */
    c-[sc] { color: #6c71c4 } /* Literal.String.Char */
    c-[dl] { color: #6c71c4 } /* Literal.String.Delimiter */
    c-[sd] { color: #6c71c4 } /* Literal.String.Doc */
    c-[se] { color: #6c71c4 } /* Literal.String.Escape */
    c-[sh] { color: #6c71c4 } /* Literal.String.Heredoc */
    c-[si] { color: #6c71c4 } /* Literal.String.Interpol */
    c-[sx] { color: #6c71c4 } /* Literal.String.Other */
    c-[sr] { color: #6c71c4 } /* Literal.String.Regex */
    c-[ss] { color: #6c71c4 } /* Literal.String.Symbol */
    c-[fm] { color: #268bd2 } /* Name.Function.Magic */
    c-[vc] { color: #cb4b16 } /* Name.Variable.Class */
    c-[vg] { color: #cb4b16 } /* Name.Variable.Global */
    c-[vi] { color: #cb4b16 } /* Name.Variable.Instance */
    c-[vm] { color: #cb4b16 } /* Name.Variable.Magic */
    c-[il] { color: #657b83 } /* Literal.Number.Integer.Long */
}
</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">P3292R0R0<br>Provenance and Concurrency</h1>
   <h2 class="no-num no-toc no-ref heading settled" id="profile-and-date"><span class="content">Published Proposal, <time class="dt-updated" datetime="2024-05-13">2024-05-13</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt>This version:
     <dd><a class="u-url" href="http://wg21.link/P3292R0">http://wg21.link/P3292R0</a>
     <dt class="editor">Author:
     <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:davidtgoldblatt@gmail.com">David Goldblatt</a>
     <dt>Audience:
     <dd>SG1
     <dt>Project:
     <dd>ISO/IEC 14882 Programming Languages — C++, ISO/IEC JTC1/SC22/WG21
    </dl>
   </div>
   <div data-fill-with="warning"></div>
   <hr title="Separator for header">
  </div>
  <nav data-fill-with="table-of-contents" id="toc">
   <h2 class="no-num no-toc no-ref" id="contents">Table of Contents</h2>
   <ol class="toc" role="directory">
    <li><a href="#introduction"><span class="secno">1</span> <span class="content">Introduction</span></a>
    <li>
     <a href="#the-miscompilations"><span class="secno">2</span> <span class="content">The miscompilations</span></a>
     <ol class="toc">
      <li><a href="#early-escape"><span class="secno">2.1</span> <span class="content">Early Escape</span></a>
      <li><a href="#store-before-fence"><span class="secno">2.2</span> <span class="content">Store before fence</span></a>
     </ol>
    <li>
     <a href="#provenance-rules-as-the-direction-for-a-fix"><span class="secno">3</span> <span class="content">Provenance rules as the direction for a fix</span></a>
     <ol class="toc">
      <li><a href="#background-on-provenance"><span class="secno">3.1</span> <span class="content">Background on provenance</span></a>
      <li><a href="#direction-for-a-fix"><span class="secno">3.2</span> <span class="content">Direction for a fix</span></a>
     </ol>
    <li>
     <a href="#some-litmus-tests"><span class="secno">4</span> <span class="content">Some litmus tests</span></a>
     <ol class="toc">
      <li><a href="#early-escape①"><span class="secno">4.1</span> <span class="content">Early escape</span></a>
      <li><a href="#store-before-fence①"><span class="secno">4.2</span> <span class="content">Store before fence</span></a>
      <li><a href="#synchronized-load-unsynchronized-pointer"><span class="secno">4.3</span> <span class="content">Synchronized load, unsynchronized pointer</span></a>
      <li><a href="#synchronized-load-unsynchronized-pointer-3-threads"><span class="secno">4.4</span> <span class="content">Synchronized load, unsynchronized pointer, 3 threads</span></a>
     </ol>
    <li>
     <a href="#alternatives"><span class="secno">5</span> <span class="content">Alternatives</span></a>
     <ol class="toc">
      <li><a href="#do-nothing-change-the-compilers"><span class="secno">5.1</span> <span class="content">Do nothing, change the compilers</span></a>
      <li><a href="#require-memory_order_load_store-for-atomics"><span class="secno">5.2</span> <span class="content">Require <code class="highlight"><c- n>memory_order_load_store</c-></code> for atomics</span></a>
      <li><a href="#reverse-pointer-zap-provenance-stripping-at-store-time"><span class="secno">5.3</span> <span class="content">Reverse pointer-zap, provenance stripping at store time</span></a>
     </ol>
    <li><a href="#questions"><span class="secno">6</span> <span class="content">Questions</span></a>
    <li><a href="#references"><span class="secno">7</span> <span class="content">References</span></a>
   </ol>
  </nav>
  <main>
   <h2 class="heading settled" data-level="1" id="introduction"><span class="secno">1. </span><span class="content">Introduction</span><a class="self-link" href="#introduction"></a></h2>
   <p>When adapting lifetime rules to a concurrent C++, the committee added a sentence
to <code class="highlight"><c- p>[</c-><c- n>basic</c-><c- p>.</c-><c- n>life</c-><c- p>]</c-></code>: "In this subclause, <a class="property css" data-link-type="property">before</a> and <a class="property css" data-link-type="property">after</a> refer to the
'happens before' relation". In single-threaded C++, this is considerably
simpler: given any two statements A and B, either A happens before B or B
happens before A. In concurrent C++, though, the world has changed; now, A can
happen before B, B can happen before A, or <em>neither of these things can be
true</em>. It’s not clear what the rules are in that third case.</p>
   <p>As an example of where this gets tricky:
After a pointer is passed to a deallocation function, all other pointers to the
object become indeterminate values. But if "after" here means "any statement
that the deallocation happens before", then the rule is much too weak (pointers
in other threads remain valid). If it means "any statement that does not happen
before the deallocation", then it disallows implementing various important
concurrent algorithms.
We’re looking at the subtle issues this uncovers around the end of an object’s
lifetime in the <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf">pointer zap
paper</a>.</p>
   <p>But there is another set of problems that a mix of academics and compiler
engineers have been looking at, surrounding issues near the <em>beginning</em> of an
object’s lifetime (either before an object has been created, or before it has
escaped to another thread). These problems seem to have the shape "analysis of memory dependencies says
that two operations are independent and can be reordered, when in fact
counterintuitive aspects of concurrency mean that they cannot":</p>
   <ul>
    <li data-md>
     <p>Synchronization in some opaque extern function call can affect memory that it
  doesn’t access and whose address it may not even know.</p>
    <li data-md>
     <p>Load buffering can send values back in time, so they may have escaped to
  points sequenced before where they escape, or even the start of their lifetime; <code class="highlight"><c- b>void</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-></code> may
  alias other in-scope pointers without any UB.</p>
   </ul>
   <p>The issue is that changing the compilers to strictly match the memory model
disallows some useful and intuitive optimizations, and changing the memory model
to strictly match the compilers results in some unworkably unfriendly
semantics.</p>
   <p>We’ll look at two miscompilations, one I think the compilers are morally right
about and one I think they’re morally wrong about. I’ll suggest a couple of
directions for a fix; the least bad fix I can think of uses provenance rules to try to
retroactively bless some of compilers' current behavior.</p>
   <h2 class="heading settled" data-level="2" id="the-miscompilations"><span class="secno">2. </span><span class="content">The miscompilations</span><a class="self-link" href="#the-miscompilations"></a></h2>
   <h3 class="heading settled" data-level="2.1" id="early-escape"><span class="secno">2.1. </span><span class="content">Early Escape</span><a class="self-link" href="#early-escape"></a></h3>
   <p>Consider the following small snippet of code:</p>
<pre class="language-cpp highlight"><c- b>void</c-> <c- nf>some_extern_func</c-><c- p>(</c-><c- b>int</c-><c- p>);</c->

<c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>(</c-><c- b>int</c-><c- o>*</c-> <c- n>q</c-><c- p>)</c-> <c- p>{</c->
    <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
    <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
    <c- o>*</c-><c- n>q</c-> <c- o>=</c-> <c- mi>456</c-><c- p>;</c-> <c- c1>// B</c->
    <c- n>some_extern_func</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-><c- p>);</c-> <c- c1>// C. Can we rewrite this to `some_extern_func(123)` ?</c->
    <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->
</pre>
   <p>Clang and GCC both miscompile it at <code class="highlight"><c- o>-</c-><c- n>O1</c-></code> and higher. They will
constant-propagate the value stored into <code class="highlight"><c- o>*</c-><c- n>p</c-></code> at <code class="highlight"><c- n>A</c-></code> into the call at <code class="highlight"><c- n>C</c-></code>,
rewriting C into <code class="highlight"><c- n>some_extern_func</c-><c- p>(</c-><c- mi>123</c-><c- p>)</c-></code>. <a href="https://gcc.godbolt.org/z/Eoahbe7jc">(Godbolt link)</a></p>
   <p>This is not correct, because (surprisingly) it’s possible for <code class="highlight"><c- n>p</c-></code> and <code class="highlight"><c- n>q</c-></code> to be
equal without violating any lifetime rules, so that the store at <code class="highlight"><c- n>B</c-></code> overwrites
the one at <code class="highlight"><c- n>A</c-></code>.</p>
   <p>The reason that compilers make this mistake is that they employ reasoning like
the following: "we see <code class="highlight"><c- o>*</c-><c- n>p</c-></code>’s lifetime begin, and so any pointer value that was
obtained before that point must point to some other object (or else violate
provenance / ‘pointer zap’ rules).”.</p>
   <p>This belief is correct for single-threaded code, but turns out not to be correct
in the presence of concurrency. The reason is that we can use load-buffering to
send pointers to objects back in time to before their creation:</p>
<pre class="language-cpp highlight"><c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>(</c-><c- b>int</c-><c- o>*</c-> <c- n>q</c-><c- p>)</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
  <c- o>*</c-><c- n>q</c-> <c- o>=</c-> <c- mi>456</c-><c- p>;</c-> <c- c1>// B</c->
  <c- n>some_extern_func</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-><c- p>);</c-> <c- c1>// C. Can we rewrite this to `some_extern_func(123)` ?</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->

<c- b>int</c-> <c- n>dummy</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-> <c- o>*></c-> <c- n>ptr_to_int_1</c-><c- p>{</c-><c- o>&amp;</c-><c- n>dummy</c-><c- p>};</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-> <c- o>*></c-> <c- n>ptr_to_int_2</c-><c- p>{</c-><c- o>&amp;</c-><c- n>dummy</c-><c- p>};</c->

<c- b>void</c-> <c- nf>t1</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>q</c-> <c- o>=</c-> <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>f</c-><c- p>(</c-><c- n>q</c-><c- p>);</c->
  <c- c1>// Can get moved before the load of ptr_to_int_1</c->
  <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>t2</c-><c- p>()</c-> <c- p>{</c->
  <c- c1>// Launders t1's store to send it back to t1.</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
  <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>At runtime, T1’s CPU can reorder the store to <code class="highlight"><c- n>ptr_to_int_2</c-></code> in front of the
rest of <code class="highlight"><c- n>t1</c-></code>, then T2 can read that stored value and copy it to <code class="highlight"><c- n>ptr_to_int_1</c-></code>.
Then <code class="highlight"><c- n>t1</c-></code> can read it, passing it into <code class="highlight"><c- n>f</c-></code>, so that <code class="highlight"><c- n>p</c-></code> and <code class="highlight"><c- n>q</c-></code> have the same
value. Note that we don’t violate any lifetime rules here – the start of the
lifetime of the allocated <code class="highlight"><c- b>int</c-></code> happens before any access of it. This program
has defined semantics that the compiled code sometimes does not obey. Even
though the user is manipulating pointers to objects before those objects'
lifetime begins, they’re not <em>dereferencing</em> those pointers, which is the thing
that’s not allowed.</p>
   <p>Even though the compiler doesn’t follow the letter of the law here, I think it’s
morally correct. Insofar as we have rules of engagement between the concurrent
and single-threaded semantics, one of them is "SG1 shouldn’t get in the way of <a class="property css" data-link-type="property">normal</a> optimizations that aren’t too aggressive and aren’t in code that says <code class="highlight"><c- n>atomic</c-></code>". Assuming that new storage doesn’t alias anything in scope at the
start of its lifetime is pretty reasonable, and it only turns out to be false
because of strange concurrency patterns concocted to demonstrate compiler bugs.</p>
   <h3 class="heading settled" data-level="2.2" id="store-before-fence"><span class="secno">2.2. </span><span class="content">Store before fence</span><a class="self-link" href="#store-before-fence"></a></h3>
   <p>Consider the following snippet of code:</p>
<pre class="language-cpp highlight"><c- b>void</c-> <c- nf>some_extern_function</c-><c- p>();</c->

<c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
  <c- n>some_extern_function</c-><c- p>();</c-> <c- c1>// B</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->
</pre>
   <p>Can the optimizer swap the store and the function call, and rewrite this into
something that looks like the following?</p>
<pre class="language-cpp highlight"><c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- n>some_extern_function</c-><c- p>();</c-> <c- c1>// B</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->
</pre>
   <p>Clang will sometimes, incorrectly, answer "yes". Thankfully, it won’t actually
do this transformation for small snippets like this one. But, due to a bug in
the loop analysis passes, it sometimes will reorder the store and function call
when some extra code is added. But these details are sort of extraneous -- the
question of whether or not the compiler <em>will</em> reorder these is different than
whether or not it <em>should be allowed to</em>.</p>
   <p>The reason why this is incorrect is straightforward: if we fill in <code class="highlight"><c- n>some_extern_function</c-><c- p>()</c-></code> with a release fence and add some callers, we can break
simple message passing with fences:</p>
<pre class="language-cpp highlight"><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>*></c-> <c- n>atomic_int_ptr</c-><c- p>;</c->

<c- b>void</c-> <c- nf>some_extern_function</c-><c- p>()</c-> <c- p>{</c->
  <c- n>atomic_thread_fence</c-><c- p>(</c-><c- n>memory_order_release</c-><c- p>);</c->
<c- p>}</c->

<c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- n>some_extern_function</c-><c- p>();</c-> <c- c1>// B</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>message_pass_f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>f</c-><c- p>();</c->
  <c- n>atomic_int_ptr</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>receive_message</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>atomic_int_ptr</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
  <c- n>atomic_thread_fence</c-><c- p>(</c-><c- n>acquire</c-><c- p>);</c->
  <c- k>if</c-> <c- p>(</c-><c- n>p</c-> <c- o>!=</c-> <c- k>nullptr</c-><c- p>)</c-> <c- p>{</c->
    <c- n>assert</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-> <c- o>==</c-> <c- mi>123</c-><c- p>);</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
   <p>I think that, in this case, all reasonable people will agree that the memory
model is working as intended and compilers should not be allowed to do the
reordering. A compiler that does this transformation is buggy.</p>
   <p>But: such a compiler could be forgiven its mistake. A similar sort of reasoning
as above seems like it ought to apply: "<code class="highlight"><c- n>p</c-></code>’s memory is private to me at the
time of its allocation, so I can reorder it at will up until it escapes so long
as it matches single-threaded semantics". Just as above, this reasoning is
correct for single-threaded programs. Synchronization operations are weird:
they can have effects on memory whose address they don’t have access to.</p>
   <h2 class="heading settled" data-level="3" id="provenance-rules-as-the-direction-for-a-fix"><span class="secno">3. </span><span class="content">Provenance rules as the direction for a fix</span><a class="self-link" href="#provenance-rules-as-the-direction-for-a-fix"></a></h2>
   <p>When we’re trying to describe the memory model, we’re balancing the concerns of
two groups:</p>
   <ul>
    <li data-md>
     <p>For compiler-writers: we need a simple rule that’s quickly and
  locally (i.e. not requiring complex interprocedural or whole-program
  analysis) checkable rule for memory-dependence analyses to use. It should
  allow aggressive reordering in "reasonable" code, while staying correct with
  respect to some mathematical model.</p>
    <li data-md>
     <p>For memory-modelers: we need rules that are easy for users to reason about
  (insofar as the underlying hardware models permit) and allow reasonable
  concurrent code to stay well-defined in theory as well as in practice.</p>
   </ul>
   <p>I think we can integrate provenance rules into the memory model to make some of this work.</p>
   <p>The rule I want for compiler-writers is that we allow more or less all
reorderings they currently do (that are consistent with single-threaded
semantics), except across extern function calls that do release operations (this
is the rule that LLVM already mostly tries to follow; the fact that it doesn’t
in some cases is an error, not a design choice). This might seem like it
violates the "locally checkable" rule -- after all, we may not know at a call
site to some extern function if that function has a release operation in it. But
recent advances in LTO make this a more reasonable thing to assume. For
instance, modern clang can do bottom-up whole-program call graph analysis
without loading the whole program IR into memory.</p>
   <p>The rule I want for memory modelers is that we tighten up the rules on how
programs are allowed to pass around certain types of pointers in situations
where it would be illegal to dereference them. This adds some complexity to the
rules that users need to think about, but in an area (using pointers obtained
via dubious means) that they already know they should tread lightly.</p>
   <p>One thing to note is that the tradeoff I propose is pretty bad. In order to make
life easier for a small number of experts (compiler writers), we’re making life
harder for a large number of less sophisticated users (regular C++ programmers).
We should think hard about whether or not there are less extreme ways to achieve
our optimization goals. (I don’t have any better ideas).</p>
   <h3 class="heading settled" data-level="3.1" id="background-on-provenance"><span class="secno">3.1. </span><span class="content">Background on provenance</span><a class="self-link" href="#background-on-provenance"></a></h3>
   <p>In C and C++, pointers are not just bags of bits. If you manage to obtain some
value for a Foo* with the same bytes as another Foo*, but got it through illicit
means, you can’t necessarily use the first pointer to access the object
pointed to by the second. This lets the compiler make some useful assumptions,
like:</p>
   <ul>
    <li data-md>
     <p>Pointers to locals that don’t escape can be assumed not to alias other
  pointers.</p>
    <li data-md>
     <p>Accesses to an array contained in one object can be assumed not to read or
  write other objects (or other fields in the same object).</p>
   </ul>
   <p>These in turn let the compiler generate faster or smaller code, such as by:</p>
   <ul>
    <li data-md>
     <p>Leaving values in registers, so that they can skip a store-reload sequence</p>
    <li data-md>
     <p>Doing common subexpression elimination (if a value can change out from under
  you, you can’t assume it’s equal to some equivalently calculated value
  computed elsewhere).</p>
   </ul>
   <p>This should be intuitive; we need <em>some</em> way to disallow code that looks like <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2624.pdf">this example from Peter
Sewell</a>.</p>
<pre class="language-cpp highlight"><c- cp>#include</c-> &lt;stdio.h>
<c- cp>#include</c-> &lt;string.h><c- c1> </c->
<c- b>int</c-> <c- n>y</c-><c- o>=</c-><c- mi>2</c-><c- p>,</c-> <c- n>x</c-><c- o>=</c-><c- mi>1</c-><c- p>;</c->
<c- b>int</c-> <c- nf>main</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- o>&amp;</c-><c- n>x</c-> <c- o>+</c-> <c- mi>1</c-><c- p>;</c->
  <c- b>int</c-> <c- o>*</c-><c- n>q</c-> <c- o>=</c-> <c- o>&amp;</c-><c- n>y</c-><c- p>;</c->
  <c- n>printf</c-><c- p>(</c-><c- s>"Addresses: p=%p q=%p</c-><c- se>\n</c-><c- s>"</c-><c- p>,(</c-><c- b>void</c-><c- o>*</c-><c- p>)</c-><c- n>p</c-><c- p>,(</c-><c- b>void</c-><c- o>*</c-><c- p>)</c-><c- n>q</c-><c- p>);</c->
  <c- k>if</c-> <c- p>(</c-><c- n>memcmp</c-><c- p>(</c-><c- o>&amp;</c-><c- n>p</c-><c- p>,</c-> <c- o>&amp;</c-><c- n>q</c-><c- p>,</c-> <c- k>sizeof</c-><c- p>(</c-><c- n>p</c-><c- p>))</c-> <c- o>==</c-> <c- mi>0</c-><c- p>)</c-> <c- p>{</c->
    <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>11</c-><c- p>;</c->  <c- c1>// does this have undefined behaviour?</c->
    <c- n>printf</c-><c- p>(</c-><c- s>"x=%d y=%d *p=%d *q=%d</c-><c- se>\n</c-><c- s>"</c-><c- p>,</c-><c- n>x</c-><c- p>,</c-><c- n>y</c-><c- p>,</c-><c- o>*</c-><c- n>p</c-><c- p>,</c-><c- o>*</c-><c- n>q</c-><c- p>);</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
   <p>Provenance rules are way of saying things like "this is a disallowed buffer
overrun, even if you get lucky and have the one-past-the-end-of-the-array rule
let you form a valid pointer that just happens to be equal to some other valid
pointer to a different object.</p>
   <p>Unfortunately, provenance rules are very fuzzy in both C and C++. The best
candidate we have for something formalized is <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3226.pdf">the WG14 provenance
TS</a>.</p>
   <p>At a high level:</p>
   <ul>
    <li data-md>
     <p>When storage is created (i.e. a stack variable, via malloc or new, etc.), it
  gets assigned a globally unique (through the program run) opaque integer.</p>
    <li data-md>
     <p>A pointer is not just an address; it’s an address, together with one of those
  opaque integers (or a special "empty" provenance). That integer is not
  observable in the bytes of the pointer; it’s the pointer’s soul, not its body.</p>
    <li data-md>
     <p>Dereferencing pointers requires that the pointer’s provenance matches the
  provenance of the underlying storage.</p>
    <li data-md>
     <p>There is a set of rules of provenance propagation (e.g. memcpys copy
  provenance), reclaiming lost provenance (e.g. ptr -> uintptr_t -> ptr), etc.
  These are interesting and involve complex tradeoffs between compiler
  optimizations and ease of use, but I think we can ignore them for our purposes
  here.</p>
   </ul>
   <p>I believe the intent for C++ is similar, except that in C++ it’s objects that
get provenance, not storage locations (so that, for example, the result obtained
from placement-new’ing two objects at the same location results in pointers with
different provenance, even though they might have the same type and occupy the
same, never-deallocated, storage).</p>
   <h3 class="heading settled" data-level="3.2" id="direction-for-a-fix"><span class="secno">3.2. </span><span class="content">Direction for a fix</span><a class="self-link" href="#direction-for-a-fix"></a></h3>
   <p>Provenance rules already have the flavor of what we want: "You have to come by
your pointers honestly". We want a way to phrase the intuition that "sending a
pointer value back in time" is cheating in the same way that "getting a pointer
value by casting from <code class="highlight"><c- n>rand</c-><c- p>()</c-></code>" is.</p>
   <p>As a first cut, the intuition I want to capture is something like: "if some
thread T1 wouldn’t have the right to dereference a pointer, when it passes it
to another thread T2, T2 doesn’t have the right to dereference the pointer
either".</p>
   <p>One way we could do this is to add a new type of provenance. In the proposed C
model, we say a pointer has either empty provenance, or some specific
provenance (of some integer, say <code class="highlight"><c- n>i</c-></code>); we’ll call that "full provenance". We’ll
add something new: when
we pass a pointer to another thread, it gets "provisional provenance";
provisional provenance remembers the provenance it came from, but is
insufficient to grant access to the pointee until some synchronization is
established (and, this applies recursively if a pointer is passed between
multiple threads unsynchronized).</p>
   <p>In pseudo-standardese ("pseudo" because the standard is not this explicit about
provenance), we might say something like:</p>
   <p>Suppose there is a pointer value V with full provenance, pointing to some object
O.</p>
   <p>If there is an atomic pointer store S of V and atomic pointer load L, where L
takes its value from S, then:</p>
   <ul>
    <li data-md>
     <p>if S happens before L, L obtains V (i.e. its value has full provenance).</p>
    <li data-md>
     <p>If S does not happen before L, then L obtains a value V' which points to O, but
  with provisional provenance, contingent on L.</p>
    <li data-md>
     <p>On use of V', if there is some program point P such that:</p>
     <ul>
      <li data-md>
       <p>O’s lifetime start happens before P</p>
      <li data-md>
       <p>L, and every load which V''s provinence is contingent upon, happens
before P, and</p>
      <li data-md>
       <p>P happens before the use,</p>
     </ul>
   </ul>
   <p>then the use gets full provenance. Otherwise, it gets provisional provenance.
  If the use is a dereference and we have provisional provenance, then we get
  UB.</p>
   <p>This is intended just to give a vibe, not to be IS-ready wording. Really we’d
need to hammer down what exactly "provenance" here means, include some wording
for release sequences, formalize the "sets" of pointers a little more carefully,
figure out tricky interactions with things like uintptr or offsets into lazily
initialized arrays, etc.</p>
   <p>We should convince ourselves, at least informally, that this justifies our
compiler rule. The reasoning is that a time-travel loop like the one we’re
looking at would require a load to happen before the store it got its value
from; we ban this sort of thing elsewhere in the memory model, so can be fairly
assured that it’s unlikely.</p>
   <h2 class="heading settled" data-level="4" id="some-litmus-tests"><span class="secno">4. </span><span class="content">Some litmus tests</span><a class="self-link" href="#some-litmus-tests"></a></h2>
   <h3 class="heading settled" data-level="4.1" id="early-escape①"><span class="secno">4.1. </span><span class="content">Early escape</span><a class="self-link" href="#early-escape①"></a></h3>
   <p>Recall the earlier example (the line labels have changed):</p>
<pre class="language-cpp highlight"><c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>(</c-><c- b>int</c-><c- o>*</c-> <c- n>q</c-><c- p>)</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c->
  <c- o>*</c-><c- n>q</c-> <c- o>=</c-> <c- mi>456</c-><c- p>;</c-> <c- c1>// E</c->
  <c- n>some_extern_func</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-><c- p>);</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->

<c- b>int</c-> <c- n>dummy</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-> <c- o>*></c-> <c- n>ptr_to_int_1</c-><c- p>{</c-><c- o>&amp;</c-><c- n>dummy</c-><c- p>};</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-> <c- o>*></c-> <c- n>ptr_to_int_2</c-><c- p>{</c-><c- o>&amp;</c-><c- n>dummy</c-><c- p>};</c->

<c- b>void</c-> <c- nf>t1</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>q</c-> <c- o>=</c-> <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c-> <c- c1>// D</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>f</c-><c- p>(</c-><c- n>q</c-><c- p>);</c->
  <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c-> <c- c1>// A</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>t2</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c-> <c- c1>// B</c->
  <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c-> <c- c1>// C</c->
<c- p>}</c->
</pre>
   <p>Here, when t2 loads its pointer value, it gets a pointer with provisional
provenance (because A does not happen-before B). That pointer gets stored at C,
then loaded again at D, so <code class="highlight"><c- n>q</c-></code> in <code class="highlight"><c- n>t1</c-></code> has provisional provenance. At <code class="highlight"><c- n>E</c-></code> it is
dereferenced with provisional provenance, so we’re in the last set of bullet
points in our provisional provenance rule. Clearly, we don’t meet the conditions
(only the first few lines of <code class="highlight"><c- n>f</c-><c- p>()</c-></code> happen before the dereference, and the load
at B doesn’t happen before any of them), so the dereference acts as though we
have empty provenance and we get UB.</p>
   <h3 class="heading settled" data-level="4.2" id="store-before-fence①"><span class="secno">4.2. </span><span class="content">Store before fence</span><a class="self-link" href="#store-before-fence①"></a></h3>
   <p>Copying the example again:</p>
<pre class="language-cpp highlight"><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>*></c-> <c- n>atomic_int_ptr</c-><c- p>;</c->

<c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c-> <c- c1>// A</c->
  <c- n>some_extern_function</c-><c- p>();</c-> <c- c1>// Really, a release fence</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->

<c- b>int</c-><c- o>*</c-> <c- nf>f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-> <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- n>some_extern_function</c-><c- p>();</c-> <c- c1>// just a release fence</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>123</c-><c- p>;</c->
  <c- k>return</c-> <c- n>p</c-><c- p>;</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>message_pass_f</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>f</c-><c- p>();</c->
  <c- n>atomic_int_ptr</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->  
<c- p>}</c->

<c- b>void</c-> <c- nf>receive_message</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>atomic_int_ptr</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
  <c- n>atomic_thread_fence</c-><c- p>(</c-><c- n>acquire</c-><c- p>);</c->
  <c- k>if</c-> <c- p>(</c-><c- n>p</c-> <c- o>!=</c-> <c- k>nullptr</c-><c- p>)</c-> <c- p>{</c->
    <c- n>assert</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-> <c- o>==</c-> <c- mi>123</c-><c- p>);</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
   <p>Here, when <code class="highlight"><c- n>receive_message</c-></code> obtains the value <code class="highlight"><c- n>p</c-></code>, it has provisional
provenance. However, consider the point P immediately after the acquire fence.
If <code class="highlight"><c- n>p</c-></code> is non-null, then the <code class="highlight"><c- b>int</c-></code>’s lifetime start happens-before P (because
of the fences), the load happens before P, and P happens before the dereference.
So the dereference gets full provenance, and this example doesn’t have UB.</p>
   <h3 class="heading settled" data-level="4.3" id="synchronized-load-unsynchronized-pointer"><span class="secno">4.3. </span><span class="content">Synchronized load, unsynchronized pointer</span><a class="self-link" href="#synchronized-load-unsynchronized-pointer"></a></h3>
   <p>So far we’ve just looked at "easy" examples (where I think most people would
agree there’s just one obviously right answer). Here’s a case where things get
fuzzy.</p>
<pre class="language-cpp highlight"><c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-> <c- n>flag</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>*></c-> <c- n>ptr_to_int</c-><c- p>;</c->

<c- b>void</c-> <c- nf>t1</c-><c- p>()</c-> <c- p>{</c->
    <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
    <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>1</c-><c- p>;</c-> <c- c1>// A</c->
    <c- n>flag</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>release</c-><c- p>);</c-> <c- c1>// B</c->
    <c- n>ptr_to_int</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>t2</c-><c- p>()</c-> <c- p>{</c->
    <c- k>if</c-> <c- p>(</c-><c- n>flag</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>acquire</c-><c- p>))</c-> <c- p>{</c->
        <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>ptr_to_int</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
        <c- k>if</c-> <c- p>(</c-><c- n>p</c-> <c- o>!=</c-> <c- k>nullptr</c-><c- p>)</c-> <c- p>{</c->
            <c- c1>// PROGRAM POINT P</c->
            <c- n>assert</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-> <c- o>==</c-> <c- mi>1</c-><c- p>);</c-> <c- c1>// Must this succeed?</c->
        <c- p>}</c->
    <c- p>}</c->
<c- p>}</c->
</pre>
   <p>In the memory model of today, this must succeed. But the informal reasoning
above makes it a little suspect. The pattern we want to allow is:</p>
   <p>Publishing thread:</p>
   <ul>
    <li data-md>
     <p>P.1. initialize pointee</p>
    <li data-md>
     <p>P.2. Establish synchronization with consuming thread (maybe via a fence in some un-analyzable function)</p>
    <li data-md>
     <p>P.3. publish pointer</p>
   </ul>
   <p>Consuming thread:</p>
   <ul>
    <li data-md>
     <p>C.1 Get pointer</p>
    <li data-md>
     <p>C.2 Establish synchronization for pointee</p>
    <li data-md>
     <p>C.3 Access pointee</p>
   </ul>
   <p>In this example, though, the underlying pattern is:</p>
   <ul>
    <li data-md>
     <p>C1. Establish synchronization for pointee</p>
    <li data-md>
     <p>C2. Get pointer</p>
    <li data-md>
     <p>C3. Access pointee (no synchronization established between C2 and C3)</p>
   </ul>
   <p>Nonetheless, I think the actual rule leaves this still well-defined:</p>
   <ul>
    <li data-md>
     <p>The lifetime start of the int happens-before P</p>
    <li data-md>
     <p>The load of the pointer happens-before P</p>
    <li data-md>
     <p>P happens-before the dereference</p>
   </ul>
   <h3 class="heading settled" data-level="4.4" id="synchronized-load-unsynchronized-pointer-3-threads"><span class="secno">4.4. </span><span class="content">Synchronized load, unsynchronized pointer, 3 threads</span><a class="self-link" href="#synchronized-load-unsynchronized-pointer-3-threads"></a></h3>
   <p>What if we take the previous example and move the actual dereference to a third
thread?</p>
<pre class="language-cpp highlight"><c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>></c-> <c- n>flag</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>*></c-> <c- n>ptr_to_int_1</c-><c- p>;</c->
<c- n>std</c-><c- o>::</c-><c- n>atomic</c-><c- o>&lt;</c-><c- b>int</c-><c- o>*></c-> <c- n>ptr_to_int_2</c-><c- p>;</c->

<c- b>void</c-> <c- nf>t1</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- k>new</c-> <c- b>int</c-><c- p>;</c->
  <c- o>*</c-><c- n>p</c-> <c- o>=</c-> <c- mi>1</c-><c- p>;</c-> <c- c1>// A</c->
  <c- n>flag</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>release</c-><c- p>);</c-> <c- c1>// B</c->
  <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>t2</c-><c- p>()</c-> <c- p>{</c->
  <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>ptr_to_int_1</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
  <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>p</c-><c- p>,</c-> <c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- b>void</c-> <c- nf>t3</c-><c- p>()</c-> <c- p>{</c->
  <c- k>if</c-> <c- p>(</c-><c- n>flag</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>acquire</c-><c- p>))</c-> <c- p>{</c->
    <c- b>int</c-><c- o>*</c-> <c- n>p</c-> <c- o>=</c-> <c- n>ptr_to_int_2</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>relaxed</c-><c- p>);</c->
    <c- k>if</c-> <c- p>(</c-><c- n>p</c-> <c- o>!=</c-> <c- k>nullptr</c-><c- p>)</c-> <c- p>{</c->
      <c- n>assert</c-><c- p>(</c-><c- o>*</c-><c- n>p</c-> <c- o>==</c-> <c- mi>1</c-><c- p>);</c-> <c- c1>// Must this succeed?</c->
    <c- p>}</c->
  <c- p>}</c->
<c- p>}</c->
</pre>
   <p>This is an example I’m not too happy about. This program is well-defined
currently, but has UB under the suggested provenance rule -- the load in <code class="highlight"><c- n>t2</c-></code> is part of the set of contingent loads for the pointer with provisional
provenance in <code class="highlight"><c- n>t3</c-></code>. It feels very strange that relatively minor changes in where
pointer copying happens can make something UB, especially in a case where
there’s not an underlying hardware motivation (I don’t think this example is
actually problematic on real hardware).</p>
   <h2 class="heading settled" data-level="5" id="alternatives"><span class="secno">5. </span><span class="content">Alternatives</span><a class="self-link" href="#alternatives"></a></h2>
   <h3 class="heading settled" data-level="5.1" id="do-nothing-change-the-compilers"><span class="secno">5.1. </span><span class="content">Do nothing, change the compilers</span><a class="self-link" href="#do-nothing-change-the-compilers"></a></h3>
   <p>Another option would be to say "the standard is correct as is, and compilers
ought to change". I think the rule they would have to implement is "treat any
pointer that ever escapes as if it had escaped immediately upon creation". This
feels heavy-handed to me; compiler-writers have put a lot of work into lifetime
rules and escape analysis refinements. Contrary to popular (or at least, loudly
proclaimed) belief, they mostly don’t put in effort just to annoy Linus
Torvalds; on average they’re chasing the perf improvements, and put in effort
where they hope to find them.</p>
   <p>You can imagine some sort of annotation-based system for trying to solve this
sort of perf issue. But object initialization is often a "peanut butter cost"
that shows up spread around a binary. I’d be surprised if there were a hotspot
users could pinpoint.</p>
   <h3 class="heading settled" data-level="5.2" id="require-memory_order_load_store-for-atomics"><span class="secno">5.2. </span><span class="content">Require <code class="highlight"><c- n>memory_order_load_store</c-></code> for atomics</span><a class="self-link" href="#require-memory_order_load_store-for-atomics"></a></h3>
   <p>I think this solves the correctness issue while allowing aggressive compiler
reordering. It has some other nice properties: it solves the out-of-thin-air
problem, and localizes changes in generated code to places that use atomics.</p>
   <p>On the other hand, there is considerable opposition from both CPU vendors and
upstream compiler writers. A middle-ground might be requiring <code class="highlight"><c- n>memory_order_load_store</c-></code> for atomic pointers, but not other atomics.</p>
   <h3 class="heading settled" data-level="5.3" id="reverse-pointer-zap-provenance-stripping-at-store-time"><span class="secno">5.3. </span><span class="content">Reverse pointer-zap, provenance stripping at store time</span><a class="self-link" href="#reverse-pointer-zap-provenance-stripping-at-store-time"></a></h3>
   <p>There are some simpler provenance-y alternatives I thought about but couldn’t
get workable semantics for. I’m mentioning them here in the hopes that they
might inspire someone cleverer:</p>
   <ul>
    <li data-md>
     <p>Reverse pointer-zap: we could try to formalize some notion like "at the time
  an object’s lifetime begins, all other pointers with the same value lose their
  provenance". (It’s not clear what "at the time" here could mean;
  happens-before is too strong, but "anything that doesn’t happen-after" is too
  weak).</p>
    <li data-md>
     <p>Provenance stripping at store time: if some thread atomically stores a pointer
  to some
  object, if the start of the object’s lifetime happens-before the store, the
  stored pointer gets the provenance of that object; otherwise, it gets empty
  provenance.</p>
   </ul>
   <p>The problem is that both of these directions end up breaking reasonable-seeming
constructs in which pointers get passed around in a relaxed manner, only
establishing synchronization in batch after some period of time has elapsed. For
example, hazard pointer implementations will copy around pointers to objects
they would not be allowed to access via those pointers; only the pointer value
itself is used up until some synchronization point is reached. If we strip
provenance without any ability to regain it, we start placing restrictions on
the internal structure of those libraries.</p>
   <h2 class="heading settled" data-level="6" id="questions"><span class="secno">6. </span><span class="content">Questions</span><a class="self-link" href="#questions"></a></h2>
   <ul>
    <li data-md>
     <p>Should the Early Escape example be UB?</p>
    <li data-md>
     <p>Should the Synchronized-load, unsynchronized pointer example be UB? (If yes,
  we could come up with something more aggressive).</p>
    <li data-md>
     <p>Where should the provenance-stripping occur? Another option is to say that it
  happens when some thread <em>stores</em> a pointer whose referant it cannot access.</p>
    <li data-md>
     <p>Where does this fit in with pointer-zap discussions more generally?</p>
   </ul>
   <h2 class="heading settled" data-level="7" id="references"><span class="secno">7. </span><span class="content">References</span><a class="self-link" href="#references"></a></h2>
   <p><a href="https://www.youtube.com/watch?v=cILLfXBBITg">Stephen Dolan’s talk at FOWM</a> discusses essentially this issue, focusing on load buffering and load/store
ordering.</p>
   <p>The "synchronized load, unsynchronized pointer" example is distilled from <a href="https://github.com/llvm/llvm-project/issues/64188">an LLVM issue reported by Sung-Hwan
Lee</a>. (On that thread I
claimed that a provenance-based approach would render the example UB; I think I
misanalyzed it and my claim there is incorrect).</p>
   <p>The C committee has been doing a lot of work formalizing provenance rules;
some of the interesting and relevant links are <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3226.pdf">here</a>, <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2624.pdf">here</a>, and <a href="https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2311.pdf">here</a>.</p>
   <p>Hans Boehm gave <a href="https://docs.google.com/presentation/d/1TJBCPr6SP5wILt4or2j9QYUpl4rjhzQCAHRaumIB9mA">a
talk</a> in Tokyo with some good examples.</p>
  </main>
<script>
(function() {
  "use strict";
  var collapseSidebarText = '<span aria-hidden="true">←</span> '
                          + '<span>Collapse Sidebar</span>';
  var expandSidebarText   = '<span aria-hidden="true">→</span> '
                          + '<span>Pop Out Sidebar</span>';
  var tocJumpText         = '<span aria-hidden="true">↑</span> '
                          + '<span>Jump to Table of Contents</span>';

  var sidebarMedia = window.matchMedia('screen and (min-width: 78em)');
  var autoToggle   = function(e){ toggleSidebar(e.matches) };
  if(sidebarMedia.addListener) {
    sidebarMedia.addListener(autoToggle);
  }

  function toggleSidebar(on) {
    if (on == undefined) {
      on = !document.body.classList.contains('toc-sidebar');
    }

    /* Don't scroll to compensate for the ToC if we're above it already. */
    var headY = 0;
    var head = document.querySelector('.head');
    if (head) {
      // terrible approx of "top of ToC"
      headY += head.offsetTop + head.offsetHeight;
    }
    var skipScroll = window.scrollY < headY;

    var toggle = document.getElementById('toc-toggle');
    var tocNav = document.getElementById('toc');
    if (on) {
      var tocHeight = tocNav.offsetHeight;
      document.body.classList.add('toc-sidebar');
      document.body.classList.remove('toc-inline');
      toggle.innerHTML = collapseSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, 0 - tocHeight);
      }
      tocNav.focus();
      sidebarMedia.addListener(autoToggle); // auto-collapse when out of room
    }
    else {
      document.body.classList.add('toc-inline');
      document.body.classList.remove('toc-sidebar');
      toggle.innerHTML = expandSidebarText;
      if (!skipScroll) {
        window.scrollBy(0, tocNav.offsetHeight);
      }
      if (toggle.matches(':hover')) {
        /* Unfocus button when not using keyboard navigation,
           because I don't know where else to send the focus. */
        toggle.blur();
      }
    }
  }

  function createSidebarToggle() {
    /* Create the sidebar toggle in JS; it shouldn't exist when JS is off. */
    var toggle = document.createElement('a');
      /* This should probably be a button, but appearance isn't standards-track.*/
    toggle.id = 'toc-toggle';
    toggle.class = 'toc-toggle';
    toggle.href = '#toc';
    toggle.innerHTML = collapseSidebarText;

    sidebarMedia.addListener(autoToggle);
    var toggler = function(e) {
      e.preventDefault();
      sidebarMedia.removeListener(autoToggle); // persist explicit off states
      toggleSidebar();
      return false;
    }
    toggle.addEventListener('click', toggler, false);


    /* Get <nav id=toc-nav>, or make it if we don't have one. */
    var tocNav = document.getElementById('toc-nav');
    if (!tocNav) {
      tocNav = document.createElement('p');
      tocNav.id = 'toc-nav';
      /* Prepend for better keyboard navigation */
      document.body.insertBefore(tocNav, document.body.firstChild);
    }
    /* While we're at it, make sure we have a Jump to Toc link. */
    var tocJump = document.getElementById('toc-jump');
    if (!tocJump) {
      tocJump = document.createElement('a');
      tocJump.id = 'toc-jump';
      tocJump.href = '#toc';
      tocJump.innerHTML = tocJumpText;
      tocNav.appendChild(tocJump);
    }

    tocNav.appendChild(toggle);
  }

  var toc = document.getElementById('toc');
  if (toc) {
    createSidebarToggle();
    toggleSidebar(sidebarMedia.matches);

    /* If the sidebar has been manually opened and is currently overlaying the text
       (window too small for the MQ to add the margin to body),
       then auto-close the sidebar once you click on something in there. */
    toc.addEventListener('click', function(e) {
      if(e.target.tagName.toLowerCase() == "a" && document.body.classList.contains('toc-sidebar') && !sidebarMedia.matches) {
        toggleSidebar(false);
      }
    }, false);
  }
  else {
    console.warn("Can't find Table of Contents. Please use <nav id='toc'> around the ToC.");
  }

  /* Wrap tables in case they overflow */
  var tables = document.querySelectorAll(':not(.overlarge) > table.data, :not(.overlarge) > table.index');
  var numTables = tables.length;
  for (var i = 0; i < numTables; i++) {
    var table = tables[i];
    var wrapper = document.createElement('div');
    wrapper.className = 'overlarge';
    table.parentNode.insertBefore(wrapper, table);
    wrapper.appendChild(table);
  }

})();
</script>