<!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>D1780R0: Modular Relaxed Dependencies: A new approach to the Out-Of-Thin-Air Problem</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
 *
 * 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)
 *   - .assertion  for assertions                    (div, p, span)
 *   - .advisement for loud normative statements     (div, p, strong)
 *   - .annoying-warning for spec obsoletion notices (div, aside, details)
 *
 * 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
 *
 * 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)
 *
 ******************************************************************************/

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

	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;

		/* Colors */
		color: black;
		background: white 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-width: .65rem .7rem .6rem;
		border-radius: .4rem;
		background: #1a5e9a;
		color: white;
		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;
		border-color: #c00;
	}

	/* 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: 2;
			bottom: 0; left: 0;
			margin: 0;
			min-width: 1.33em;
			border-top-right-radius: 2rem;
			box-shadow: 0 0 2px;
			font-size: 1.5em;
			color: black;
		}
		#toc-nav > a {
			display: block;
			white-space: nowrap;

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

			background: white;
			box-shadow: 0 0 2px;
			border: none;
			border-top-right-radius: 1.33em;
			background: white;
		}
		#toc-nav > #toc-jump {
			padding-bottom: 2em;
			margin-bottom: -1.9em;
		}

		#toc-nav > a:hover,
		#toc-nav > a:focus {
			background: #f8f8f8;
		}
		#toc-nav > a:not(:hover):not(:focus) {
			color: #707070;
		}

		/* 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-toggle-inline {
			vertical-align: 0.05em;
			font-size: 80%;
			color: gray;
			color: hsla(203,20%,40%,.7);
			border-style: none;
			background: transparent;
			position: relative;
		}
		#toc-toggle-inline:hover:not(:active),
		#toc-toggle-inline:focus:not(:active) {
			text-shadow: 1px 1px silver;
			top: -1px;
			left: -1px;
		}

		#toc-nav :active {
			color: #C00;
		}
	}

/** 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);
			background: inherit;
			background-color: #f7f8f9;
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) 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);
		}
		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);
			background: inherit;
			background-color: #f7f8f9;
			z-index: 1;
			box-shadow: -.1em 0 .25em rgba(0,0,0,.1) 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);
		}

		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;
		background: transparent;
	}

	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,
	#subtitle {
		/* #subtitle 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) > hr {
		font-size: 1.5em;
		text-align: center;
		margin: 1em auto;
		height: auto;
		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;
	}

	/* Put nice boxes around each algorithm. */
	[data-algorithm]:not(.heading) {
	  padding: .5em;
	  border: thin solid #ddd; 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: medium;
	}
	dfn var {
		font-style: normal;
	}

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

	del { color: red;  text-decoration: line-through; }
	ins { color: #080; text-decoration: underline;    }

/** 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;
		page-break-inside: avoid;
		hyphens: none;
		text-transform: none;
	}
	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;
		text-decoration: none;
		border-bottom: 1px solid #707070;
		/* Need a bit of extending for it to look okay */
		padding: 0 1px 0;
		margin: 0 -1px 0;
	}
	a:visited {
		border-bottom-color: #BBB;
	}

	/* Use distinguishing colors when user is interacting with the link */
	a[href]:focus,
	a[href]:hover {
		background: #f8f8f8;
		background: rgba(75%, 75%, 75%, .25);
		border-bottom-width: 3px;
		margin-bottom: -2px;
	}
	a[href]:active {
		color: #C00;
		border-color: #C00;
	}

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

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

	img {
		border-style: none;
	}

	/* 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;
	}
	.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 {
		padding: .5em;
		border: .5em;
		border-left-style: solid;
		page-break-inside: avoid;
	}
	span.issue, span.note {
		padding: .1em .5em .15em;
		border-right-style: solid;
	}

	.issue,
	.note,
	.example,
	.advisement,
	.assertion,
	blockquote {
		margin: 1em auto;
	}
	.note  > p:first-child,
	.issue > p:first-child,
	blockquote > :first-child {
		margin-top: 0;
	}
	blockquote > :last-child {
		margin-bottom: 0;
	}

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

	blockquote {
		border-color: silver;
	}

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

	.issue {
		border-color: #E05252;
		background: #FBE9E9;
		counter-increment: issue;
		overflow: auto;
	}
	.issue::before, .issue > .marker {
		text-transform: uppercase;
		color: #AE1E1E;
		padding-right: 1em;
		text-transform: uppercase;
	}
	/* 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;
		background: #FCFAEE;
		counter-increment: example;
		overflow: auto;
		clear: both;
	}
	.example::before, .example > .marker {
		text-transform: uppercase;
		color: #827017;
		min-width: 7.5em;
		display: block;
	}
	/* 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;
		background: #E9FBE9;
		overflow: auto;
	}

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

	details.note > summary {
		display: block;
		color: hsl(120, 70%, 30%);
	}
	details.note[open] > summary {
		border-bottom: 1px silver solid;
	}

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

	.assertion {
		border-color: #AAA;
		background: #EEE;
	}

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

	.advisement {
		border-color: orange;
		border-style: none solid;
		background: #FFEECC;
	}
	strong.advisement {
		display: block;
		text-align: center;
	}
	.advisement > .marker {
		color: #B35F00;
	}

/** 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: #fdd;
		color: red;
		font-weight: bold;
		padding: .75em 1em;
		border: thick red;
		border-style: solid;
		border-radius: 1em;
	}
	.annoying-warning :last-child {
		margin-bottom: 0;
	}

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

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

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

	.def {
		padding: .5em 1em;
		background: #DEF;
		margin: 1.2em 0;
		border-left: 0.5em solid #8CCBF2;
	}

/******************************************************************************/
/*                                    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;
	}

	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-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;
		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;
		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-top: 0.1rem;
		/* Larger, more consistently-sized click target */
		display: block;
		/* Reverse color scheme */
		color: black;
		border-color: #3980B5;
		border-bottom-width: 3px !important;
		margin-bottom: 0px !important;
	}
	.toc a:visited {
		border-color: #054572;
	}
	.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;
		line-height: 1.1rem; /* consistent spacing */
	}

	/* 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 .secno { font-size: 85%; }
	.toc > li li li li li { font-size:   85%;    }
	.toc > li li li li li .secno { font-size: 100%; }

	/* @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 {
			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; }
		}
	/* } */

	@supports (display:grid) {
		/* 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;
		}
		#toc .content:hover {
			background: rgba(75%, 75%, 75%, .25);
			border-bottom: 3px solid #054572;
			margin-bottom: -3px;
		}
		#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 span {
			white-space: nowrap;
			color: transparent; }
		ul.index li a:hover + span,
		ul.index li a:focus + span {
			color: #707070;
		}
	}

/** 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]) {
		background: #f7f8f9;
	}

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

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

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

/******************************************************************************/
/*                                    Legacy                                  */
/******************************************************************************/

	/* This rule is inherited from past style sheets. No idea what it's for. */
	.hide { display: none }



/******************************************************************************/
/*                             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 > table {
		/* limit preferred width of table */
		max-width: 50em;
		margin-left: auto;
		margin-right: auto;
	}

	@media (min-width: 55em) {
		.overlarge {
			margin-left: calc(13px + 26.5rem - 50vw);
			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-left: calc(40em - 50vw) !important;
			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-left: 0 !important;
			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 type="text/css">
    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;
    }
  </style>
  <meta content="Bikeshed version 872cce6d3026423d677f3bd5837f1a1a46299a04" name="generator">
  <link href="http://wg21.link/D1780R0" rel="canonical">
  <link href="https://isocpp.org/favicon.ico" rel="icon">
  <meta content="9ba8030c5081fd3a6ad21ec4510f510053f9c2a4" name="document-revision">
<style type="text/css">
  .math {
    background-color: hsl(266, 67%, 98%);
    padding: 0.3em;
    border-radius: 0.3em;
  }

  .dp {
    font-family: monospace;
    font-style: normal;
    color: #920000;
  }
  .dp::after { content: "DP";  }
  
  .dep {
    font-family: sans-serif;
    font-style: normal;
    color: #920000;
  }
  .dep::after { content: "dep"; }

  .sb {
    font-family: sans-serif;
    font-style: normal;
    color: black;
  }
  .sb::after { content: "sb";  }
  
  .sdep {
    font-family: sans-serif;
    font-style: normal;
    color: #ff8306;
  }
  .sdep::after { content: "sdep";  }

  .rf {
    font-family: sans-serif;
    font-style: normal;
    color: darkgreen;
  }
  .rf::after { content: "rf"; }

  .title {
    font-weight: bold;
    padding: 0em 0.5em 0em 0.2em 0em;
  }
  
  .title + p {
	margin-top: 0em;
  }

  .litmus-title  {
    font-weight: bold;
    background-color: hsl(24, 20%, 95%);
    padding: 0.5em 0em 0.2em 1em;
  }

  .litmus-title + .highlight {
    margin-top: 0;
    padding-top: 0;
  }

</style>
<style>/* 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>/* 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>/* style-syntax-highlighting */

.highlight:not(.idl) { background: hsl(24, 20%, 95%); }
code.highlight { padding: .1em; border-radius: .3em; }
pre.highlight, pre > code.highlight { display: block; padding: 1em; margin: .5em 0; overflow: auto; border-radius: 0; }
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 */
</style>
<style>/* style-selflinks */

.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%;
}
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: gray;
    color: white;
    font-style: normal;
    transition: opacity .2s, background-color .2s, color .2s;
}
dfn:hover > a.self-link {
    opacity: 1;
}
dfn > a.self-link:hover {
    color: black;
}

a.self-link::before            { content: "¶"; }
.heading > a.self-link::before { content: "§"; }
dfn > a.self-link::before      { content: "#"; }</style>
<style>/* style-autolinks */

.css.css, .property.property, .descriptor.descriptor {
    color: #005a9c;
    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;
}</style>
 <body class="h-entry">
  <div class="head">
   <p data-fill-with="logo"></p>
   <h1 class="p-name no-ref" id="title">D1780R0<br>Modular Relaxed Dependencies: A new approach to the Out-Of-Thin-Air Problem</h1>
   <h2 class="no-num no-toc no-ref heading settled" id="subtitle"><span class="content">Draft Proposal, <time class="dt-updated" datetime="2019-06-14">2019-06-14</time></span></h2>
   <div data-fill-with="spec-metadata">
    <dl>
     <dt>This version:
     <dd><a class="u-url" href="http://wg21.link/D1780R0">http://wg21.link/D1780R0</a>
     <dt>Authors:
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:M.J.Batty@kent.ac.uk">Mark Batty</a> (<span class="p-org org">University of Kent</span>)
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:sjc205@kent.ac.uk">Simon Cooksey</a> (<span class="p-org org">University of Kent</span>)
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:S.A.Owens@kent.ac.uk">Scott Owens</a> (<span class="p-org org">University of Kent</span>)
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:anouk.paradis@polytechnique.edu">Anouk Paradis</a> (<span class="p-org org">Ecole Polytechnique</span>)
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:M.Paviotti@kent.ac.uk">Marco Paviotti</a> (<span class="p-org org">University of Kent</span>)
     <dd>
      <dd class="editor p-author h-card vcard"><a class="p-name fn u-email email" href="mailto:daw29@kent.ac.uk">Daniel Wright</a> (<span class="p-org org">University of Kent</span>)
     <dt>Audience:
     <dd>SG1
     <dt>Project:
     <dd>ISO/IEC JTC1/SC22/WG21 14882: Programming Language — C++
     <dt>Source:
     <dd><a href="https:/github.com/MBattyResearch/compositional-eventstructures/c++-pxxx/">https:/github.com/MBattyResearch/compositional-eventstructures/c++-pxxx/</a>
    </dl>
   </div>
   <div data-fill-with="warning"></div>
   <hr title="Separator for header">
  </div>
  <div class="p-summary" data-fill-with="abstract">
   <h2 class="no-num no-toc no-ref heading settled" id="abstract"><span class="content">Abstract</span></h2>
   <p>This note describes Modular Relaxed Dependency (MRD), an alteration of the existing concurrency specification that

          allows compiler optimisations while forbidding thin-air values. It has two key advantages over previous suggestions:
          first, it is a relatively contained change that leaves much of the existing concurrency definition unaltered; second,
          and in contrast to other solutions, the cost of evaluating the semantics is relatively low. We present the context, the
          ideas behind the change, and a tool that executes the amended model.</p>
  </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="#recap"><span class="secno">1</span> <span class="content">Recap</span></a>
    <li><a href="#solution"><span class="secno">2</span> <span class="content">Our Solution</span></a>
    <li><a href="#mrd"><span class="secno">3</span> <span class="content">Modular Relaxed Dependency</span></a>
    <li><a href="#mrder"><span class="secno">4</span> <span class="content">MRDer</span></a>
    <li><a href="#conclusion"><span class="secno">5</span> <span class="content">Conclusion</span></a>
   </ol>
  </nav>
  <main>
   <h2 class="heading settled" data-level="1" id="recap"><span class="secno">1. </span><span class="content">Recap</span><a class="self-link" href="#recap"></a></h2>
   <p>From its introduction in 2011, the C++ concurrency model has faced a great deal of scrutiny, leading to various
refinements of the standard, and a list of fixes to known issues that are yet to be incorporated. <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0668r5.html">P0668R5: Revising the
C++ Memory Model</a> describes changes needed to
incorporate the fixes of <a href="https://plv.mpi-sws.org/scfix/paper.pdf">RC11</a>, the latest revision of the existing C++
concurrency design, solving most known problems.</p>
   <p>This process of scrutiny and refinement has produced a mature model, but it has also uncovered major problems. The
out-of-thin-air problem, discussed most recently in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1217r1.html">P1217R1: Out-of-thin-air, revisited,
again</a> does not yet have a convenient solution:
existing solutions -- like the <a href="https://people.mpi-sws.org/~viktor/papers/popl2017-promising.pdf">Promising Semantics</a> -- discard the mature concurrency model of the standard and start afresh with a different paradigm. Implementing such a
change would require a wide-ranging rewrite of the standard.</p>
   <p>The out-of-thin-air problem arises from an under-specification of which program dependencies ought to order memory
accesses. The trouble is neatly explained by example.</p>
   <div class="litmus-title">LB</div>
<pre class="language-c++ highlight"><c- c1>// Thread 1</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->

<c- c1>// Thread 2</c->
<c- n>r2</c-> <c- o>=</c-> <c- n>y</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>x</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>In the example above, called Load Buffering (LB), C++ allows the execution where <code class="highlight"><c- n>r1</c-></code> and <code class="highlight"><c- n>r2</c-></code> have value 1 at the end
of execution. This outcome must be allowed to permit efficient compilation of relaxed accesses to plain loads and stores
on the Power and ARM architectures, where the corresponding behaviour is allowed.</p>
   <div class="litmus-title">LB+datas (P1217R1 OOTA Example 1, JCTC4)</div>
<pre class="language-c++ highlight"><c- c1>// Thread 1</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->

<c- c1>// Thread 2</c->
<c- n>r2</c-> <c- o>=</c-> <c- n>y</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>x</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>In the example above, taken from <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1217r1.html">P1217R1</a>, there
are now data dependencies from load to store and no explicit write of any value. Surprisingly, the outcome 42 is allowed
by the C++ standard -- we say that the value 42 is conjured from <em>thin-air</em>.</p>
   <div class="litmus-title">LB+ctrls (P1217R1 OOTA Example 2, JCTC13)</div>
<pre class="language-c++ highlight"><c- c1>// Thread 1</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-><c- p>(</c-><c- n>r1</c-> <c- o>==</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- c1>// Thread 2</c->
<c- n>r2</c-> <c- o>=</c-> <c- n>y</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-><c- p>(</c-><c- n>r2</c-> <c- o>==</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>x</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>A similar behaviour can be exhibited using control dependencies, as above. The thin-air outcomes of LB+datas and
LB+ctrls are the result of a design choice that aims to permit compiler optimisation: some optimisations remove
syntactic dependencies, so if C++ bestows ordering to no dependencies, the compiler is free to remove what it likes.</p>
   <div class="litmus-title">LB+false+ctrl (JCTC6)</div>
<pre class="language-c++ highlight"><c- c1>// Thread 1</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-><c- p>(</c-><c- n>r1</c-> <c- o>==</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c-> <c- k>else</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->

<c- c1>// Thread 2</c->
<c- n>r2</c-> <c- o>=</c-> <c- n>y</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-><c- p>(</c-><c- n>r2</c-> <c- o>==</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>x</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>In Thread 1 above, a compiler may spot that, whatever value is read, the write will be made regardless, optimising to
the program below, and allowing both threads to read 42:</p>
<pre class="language-c++ highlight"><c- c1>// Thread 1</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>As it stands, the standard allows both threads to read 42 and hence it permits the optimisation, but it also allows the
thin-air outcomes in LB+datas and
LB+ctrls. <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1217r1.html">P1217R1</a> explains that these outcomes
cannot occur in practice and must be forbidden to enable reasoning and formal verification.</p>
   <h2 class="heading settled" data-level="2" id="solution"><span class="secno">2. </span><span class="content">Our Solution</span><a class="self-link" href="#solution"></a></h2>
   <p><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0422r0.html">P0422R0: Out-of-Thin-Air Execution is Vacuous</a> highlights a series of litmus tests, borrowed from Java, that constrain the ideal behaviour of an improved semantics
(e.g. JCTC4, JCTC13 and JCTC6 above). The paper makes the observation that the thin-air execution of each test features
a cycle in (<span class="rf"></span> ∪ <span class="sdep"></span>) where <span class="sdep"></span>, <em>semantic
dependency</em>, is an as yet undefined relation that captures only the real dependencies that the compiler will leave in
place. If only we forbid cycles in (<span class="rf"></span> ∪ <span class="sdep"></span>), then the model would
have the behaviour we desire.</p>
   <p><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1217r1.html">P1217R1</a> highlights a simple way to guarantee a
lack of thin-air behaviour: forbid the reordering of relaxed loads with following stores. This is equivalent to
forbidding cycles in (<span class="rf"></span> ∪ <span class="sb"></span>), and is sufficient to avoid cycles in
(<span class="rf"></span> ∪ <span class="sdep"></span>). This approach is controversial because it may have a
substantial overhead, especially on GPUs.</p>
   <p>Our solution is to provide a definition of <span class="sdep"></span>, and to forbid cycles in (<span class="rf"></span> ∪ <span class="sdep"></span>). We have a (sketch) proof that our solution does not incur any overhead at all; that it
supports the optimum compilation strategies for Power, ARM, X86 and RISC-V; and that a DRF-SC result holds.</p>
   <p>The standard describes the three stages of working out the concurrent
behaviour of a program:</p>
   <ol>
    <li data-md>
     <p>Generate a list of pre-executions from the source of a program, each corresponding to an arbitrary choice of read
values.</p>
    <li data-md>
     <p>Pair each pre-execution with reads-from and modification order relations that capture the dynamic memory behaviour
of the program, and filter, keeping those that are <em>consistent</em> with the rules of the memory model.</p>
    <li data-md>
     <p>Finally, check for races: race free programs have the consistent executions calculated in step 2, racy programs have
undefined behaviour.</p>
   </ol>
   <p>We augment this scheme by calculating <span class="sdep"></span> in step 1.</p>
   <h2 class="heading settled" data-level="3" id="mrd"><span class="secno">3. </span><span class="content">Modular Relaxed Dependency</span><a class="self-link" href="#mrd"></a></h2>
   <p>This section explains the idea behind MRD by example, referring back to the programs already discussed.</p>
   <div class="title">LB+data Thread 1</div>
   <p>First we discuss LB+datas. C++ allows the program to read 42, despite there being no write of 42. It is the clearest
example of thin-air behaviour and it should be forbidden.</p>
<pre class="language-c++ highlight"><c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>Above is the first thread of LB+datas. We will contrast the pre-execution that is constructed in the existing C++
definition with the structure built by MRD.</p>
   <table>
    <tbody>
     <tr>
      <td><img alt="Execution of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb1.png" width="120">
      <td><img alt="Execution of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb2.png" width="140">
      <th><img alt="MRD Event Structure of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/es-lb+data1.png" width="200"> 
     <tr>
      <th colspan="2">C++ Pre-executions
      <th>MRD Event Structure
   </table>
   <p>On the left above is a list of pre-executions of this first thread -- note that we restrict the value set here to 0 and
1 to keep the diagrams tractable, so this list covers all of them. The C++ model intentionally ignores dependencies, so
the events of each pre-execution are ordered only by sequenced before.</p>
   <p>On the right is the structure generated by MRD (an <em>event structure</em> in the jargon). It is similar to the list of
executions on the left, but a red zigzag links events 1 and 3. The structure that MRD generates represents all possible
executions in the same graph, and the zigzag edge, called <em>conflict</em>, indicates a choice that must be made: a single
execution can contain a read of value 0 at <code class="highlight"><c- n>x</c-></code> or a read of value 1, not both. This structure includes <span class="sdep"></span> edges that allow us to forbid the unwanted outcome (we will explain their construction
shortly). MRD produces pre-executions compatible with the existing C++ standard. Each pre-execution of an MRD structure
is a path from top to bottom of the graph, making a choice at each conflict edge. In this program the pre-executions are
{1, 2} and {3, 4}.</p>
   <p>In LB+datas, we are interested in the outcome where both threads read a non-zero value. MRD generates one pre-execution
for thread 1 where this is the case, and another similar one for thread 2. Both feature a semantic dependency from the
read to the write, and a consequent cycle in (<span class="rf"></span> ∪ <span class="sdep"></span>). The outcome is forbidden as a result.</p>
   <div class="title">LB+ctrl Thread 1</div>
<pre class="language-c++ highlight"><c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-> <c- p>(</c-><c- n>r1</c-> <c- o>==</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>42</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>MRD treats LB+ctrl similarly, with a small difference in the generated structure. Recall Thread 1 above.</p>
   <table>
    <tbody>
     <tr>
      <td><img alt="Execution of LB+ctrl thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb+ctrl1.png" width="80">
      <td><img alt="Execution of LB+ctrl thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb+ctrl2.png" width="80">
      <td><img alt="MRD Event Structure of LB+ctrl thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/es-lb+ctrl1.png" width="200">
     <tr>
      <th colspan="2">C++ Pre-executions
      <th>MRD Event Structure
   </table>
   <p>In this program, the write is only made if value 42 is read, and that is reflected in the pre-executions of C++ and also
the structure generated by MRD. In LB+ctrls, both threads are of this form, and the thin-air outcome is once again
forbidden by the presence of the semantic dependency.</p>
   <div class="title">LB+false Thread 1</div>
<pre class="language-c++ highlight"><c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-> <c- p>(</c-><c- n>r1</c-> <c- o>==</c-> <c- mi>1</c-><c- p>)</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c-> <c- k>else</c-> <c- p>{</c->
  <c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- p>}</c->
</pre>
   <p>The first thread of LB+false+ctrl, given above, has a false dependency from the load of <code class="highlight"><c- n>x</c-></code> to the store of <code class="highlight"><c- n>y</c-></code>. In hardware
concurrency models, the control structure of the corresponding code would induce control dependencies, provide ordering,
and forbid the outcome where both threads read 42. In C++ however, we want to allow the optimiser to hoist the store to
y above the if-statement, so we cannot enforce this dependency.</p>
   <table>
    <tbody>
     <tr>
      <td><img alt="Execution of LB+false thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb+false1.png" width="120">
      <td><img alt="Execution of LB+false thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb2.png" width="140">
      <td><img alt="MRD Event Structure of LB+false thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/es-lb+false.png" width="200"> 
     <tr>
      <th colspan="2">C++ Pre-executions
      <th>MRD Event Structure
   </table>
   <p>C++ calculates the list of pre-executions above on the left. To allow the optimisation, C++ again omits any dependency
ordering, but in this case that turns out to be appropriate.</p>
   <p>MRD again combines all of the executions into a single structure, indicating with the red zigzag of conflict where
choices must be made to identify a single execution. There is enough information in this structure to spot that
regardless of the choices made, a write of value 1 to y is always performed. This is precisely what the machinery of MRD
does as it builds the structure, and as a consequence it omits the dependency. We say that the write of y has been <em>lifted</em> above its dependency on the load of <code class="highlight"><c- n>x</c-></code> because this mechanism is reminiscent of hoisting. Without the
dependency, the execution of LB+false+ctrl where both threads read 1 is allowed, and the hoisting optimisation is
sound, as intended.</p>
   <div class="title">LB Thread 1</div>
   <p>We now discuss the machinery that performs lifting in MRD, using LB as an example. Recall that the relaxed outcome where
both threads read 1 must be allowed, otherwise relaxed atomics cannot be implemented efficiently as plain loads and
stores on the Power and ARM architectures.</p>
<pre class="language-c++ highlight"><c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- mi>1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>We describe how MRD calculates the executions of the first thread of LB, given above.</p>
   <table>
    <tbody>
     <tr>
      <td><img alt="Execution of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb+false1.png" width="120">
      <td><img alt="Execution of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/cppmem-lb2.png" width="140">
      <td><img alt="MRD Event Structure of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/es-lb+false.png" width="200"> 
     <tr>
      <th colspan="2">C++ Pre-executions
      <th>MRD Event Structure
   </table>
   <p>MRD builds its structures following the program syntax. In contrast to other models, it works from back to front, so for
thread 1 of LB, MRD will build the event corresponding to the store of y and then build the events corresponding to the
load of <code class="highlight"><c- n>x</c-></code>. The calculation of semantic dependencies is made by using an intermediate representation of dependency called <em>justification</em>. Justification records more information than semantic dependency: it tracks all of the choices that lead
to the execution of a particular write.</p>
   <p>To build the structure for the first thread of LB, MRD first constructs a write event corresponding to the store to
y. At this point, before MRD has interpreted the load, we have a structure with a single write. No choices need to be
made to reach the write, so MRD builds a justification reflecting this, linking the empty set to the write. In this
case, we say that the write of <code class="highlight"><c- n>y</c-></code> is <em>independently justified</em>.</p>
   <p>Next MRD interprets the load of <code class="highlight"><c- n>x</c-></code>. There are two possible outcomes: either 0 is read from <code class="highlight"><c- n>x</c-></code>, followed by the write of <code class="highlight"><c- n>y</c-></code>,
or 1 is read from <code class="highlight"><c- n>x</c-></code>, followed by the write of y. MRD creates two read events and links them with a conflict edge to make
it clear that only one can occur. The event representing the write of y is duplicated, with one instance following each
read.</p>
   <p>On interpreting the load of <code class="highlight"><c- n>y</c-></code>, the justification relation is updated. Now there is a choice to be made before reaching
the write events, we can read either 0 or 1 from <code class="highlight"><c- n>x</c-></code>. In this case, that choice is of no consequence -- the same value
is stored to <code class="highlight"><c- n>y</c-></code> regardless. MRD therefore marks each write as independent in the justification relation.</p>
   <p>Once MRD has interpreted the whole program, the justification relation is used to construct the <span class="sdep"></span> relation. In this case <span class="sdep"></span> is empty.</p>
   <div class="title">LB+data Thread 1</div>
<pre class="language-c++ highlight"><c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
</pre>
   <p>MRD builds its structure for the first thread of LB+datas similarly, but here the stores are dependent on the state of
the registers. MRD has a symbolic representation of the register store that it resolves as it interprets the program. On
interpreting the load of x, the event corresponding to the store of <code class="highlight"><c- n>y</c-></code> is duplicated, and its value is resolved. This
leads to the creation of two events, one writing 0 to <code class="highlight"><c- n>y</c-></code>, and the other writing 1 to <code class="highlight"><c- n>y</c-></code>.</p>
   <p>The conflicting reads are constructed as before, but the construction of justification is different. Now the choice of
read <em>does</em> have a bearing on what write comes next. Reading 0 from <code class="highlight"><c- n>x</c-></code> will result in a write of 0 to <code class="highlight"><c- n>y</c-></code>, so we say that
the read of 0 from <code class="highlight"><c- n>x</c-></code> <em>justifies</em> the write of 0 to <code class="highlight"><c- n>y</c-></code>. Similarly for loading 1 from <code class="highlight"><c- n>x</c-></code>, we say that the read of 1 from <code class="highlight"><c- n>x</c-></code> <em>justifies</em> the write of 1 to <code class="highlight"><c- n>y</c-></code>. MRD translates these justifications into dependencies from the reads to the writes.</p>
   <p><img alt="MRD Event Structure of LB thread 1" src="https://www.cs.kent.ac.uk/people/rpg/sjc205/c++-p1780/images/es-lb+data1.png" width="200"></p>
   <p>Through these steps, MRD produces the structure above, and forbids the outcome where both threads read 1.</p>
   <h2 class="heading settled" data-level="4" id="mrder"><span class="secno">4. </span><span class="content">MRDer</span><a class="self-link" href="#mrder"></a></h2>
   <p>We have built a tool that takes C-like programs as input and evaluates them under the C++ memory model augmented with
MRD (MRDC). The language supports non-atomics, atomics, RMWs, fences, forks and joins. This combination uses the RC11
model, incorporating all known fixes to the original C++ specification. Most litmus tests are evaluated in under a
minute on a modestly spec’d computer (16GB of Memory, Intel i5-5250U @ 1.60GHz (2.5GHz turbo)).</p>
   <p>The performance of the tool is largely proportional to the size of the program being evaluated. For example, a variant
of ISA2 with fences is evaluated in 1.5s, whereas without the fences it is evaluated in 0.8s. We have run the tool on
100 hand-chosen tests, and we believe the performance to be compatible with automated testing.</p>
   <p>We will ultimately release the code but for now, in development, please eagerly email us with questions about the
tool. We can execute tests for you, or we can work together to get MRDer working in conjunction with your own tools.</p>
   <p>The RFUB test, below, was recently discussed
in <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1217r1.html">P1217R1</a>.</p>
<pre class="language-c++ highlight"><c- c1>// Thread 1:</c->
<c- n>r1</c-> <c- o>=</c-> <c- n>x</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>y</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r1</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->

<c- c1>// Thread 2:</c->

<c- b>bool</c-> <c- nf>assigned_42</c-><c- p>(</c->false<c- p>);</c->
<c- n>r2</c-> <c- o>=</c-> <c- n>y</c-><c- p>.</c-><c- n>load</c-><c- p>(</c-><c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- k>if</c-><c- p>(</c-><c- n>r2</c-> <c- o>!=</c-> <c- mi>42</c-><c- p>)</c-> <c- p>{</c->
  <c- n>assigned_42</c-> <c- o>=</c-> true<c- p>;</c->
  <c- n>r2</c-> <c- o>=</c-> <c- mi>42</c-><c- p>;</c->
<c- p>}</c->
<c- n>x</c-><c- p>.</c-><c- n>store</c-><c- p>(</c-><c- n>r2</c-><c- p>,</c-> <c- n>memory_order</c-><c- o>::</c-><c- n>relaxed</c-><c- p>);</c->
<c- n>assert_not</c-><c- p>(</c-><c- n>assigned_42</c-><c- p>);</c->
</pre>
   <p>We used MRDer to evaluate this test, and the outcome in question is allowed by MRDC as desired: <code class="highlight"><c- n>r1</c-> <c- o>==</c-> <c- mi>42</c-> <c- o>&amp;&amp;</c-> <c- n>r2</c-> <c- o>==</c-> <c- mi>42</c-></code> with no assertion failure. The reason is straightforward: regardless of the choice made for the read of <code class="highlight"><c- n>y</c-></code> in thread 1, a
write to <code class="highlight"><c- n>x</c-></code> of value 42 is always made. The machinery of MRD recognises this, lifting as it should.</p>
   <h2 class="heading settled" data-level="5" id="conclusion"><span class="secno">5. </span><span class="content">Conclusion</span><a class="self-link" href="#conclusion"></a></h2>
   <p>MRD is one possible solution to the thin-air problem. Its machinery is <em>orthogonal</em> to the conditions of the existing
memory model, and as a consequence, it could be implemented within the standard in a localised and self-contained
way. Moreover, all of the refinements made to the current standard would be carried over. MRDer makes it clear that MRD
can be evaluated efficiently, and enables us to quickly answer questions about how it behaves.</p>
   <p>Refinement of MRD will benefit from:</p>
   <ul>
    <li data-md>
     <p>interesting tests or optimisations that should be supported in the memory model, and</p>
    <li data-md>
     <p>discussion of the details with members of SG1.</p>
   </ul>
   <p>We believe MRDC hits quite a few desirable properties for a revision of the C++ concurrency model:</p>
   <ul>
    <li data-md>
     <p>continued support of <code class="highlight"><c- n>ld</c-><c- p>;</c-><c- n>st</c-></code> re-ordering,</p>
    <li data-md>
     <p>reasoning about programs with RFUB behaviour,</p>
    <li data-md>
     <p>implementability at no additional cost on major architectures (ARM, POWER, x86, RISC-V),</p>
    <li data-md>
     <p>augmentation of RC11, incorporating all known fixes to C++ concurrency,</p>
    <li data-md>
     <p>extension, rather than re-creation, of the existing standard,</p>
    <li data-md>
     <p>potential route to redefining <code class="highlight"><c- n>memory_order</c-><c- o>::</c-><c- n>consume</c-></code> using semantic dependency, and</p>
    <li data-md>
     <p>application to other memory models with an <em>axiomatic</em> representation, e.g. Java, Linux and OpenCL.</p>
   </ul>
  </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>