blob: 0eb8fc89432ee0eb623269dfbc64f340ba13d868 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Closure constraints - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../../favicon.svg">
<link rel="shortcut icon" href="../../favicon.png">
<link rel="stylesheet" href="../../css/variables.css">
<link rel="stylesheet" href="../../css/general.css">
<link rel="stylesheet" href="../../css/chrome.css">
<link rel="stylesheet" href="../../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="../../highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="../../tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="../../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../../";
const default_light_theme = "light";
const default_dark_theme = "navy";
</script>
<!-- Start loading toc.js asap -->
<script src="../../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development Guide</h1>
<div class="right-buttons">
<a href="../../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/master/src/borrow_check/region_inference/closure_constraints.md" title="Suggest an edit" aria-label="Suggest an edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="propagating-closure-constraints"><a class="header" href="#propagating-closure-constraints">Propagating closure constraints</a></h1>
<p>When we are checking the type tests and universal regions, we may come
across a constraint that we can't prove yet if we are in a closure
body! However, the necessary constraints may actually hold (we just
don't know it yet). Thus, if we are inside a closure, we just collect
all the constraints we can't prove yet and return them. Later, when we
are borrow check the MIR node that created the closure, we can also
check that these constraints hold. At that time, if we can't prove
they hold, we report an error.</p>
<h2 id="how-this-is-implemented"><a class="header" href="#how-this-is-implemented">How this is implemented</a></h2>
<p>While borrow-checking a closure inside of <code>RegionInferenceContext::solve</code> we separately try to propagate type-outlives and region-outlives constraints to the parent if we're unable to prove them locally.</p>
<h3 id="region-outlive-constraints"><a class="header" href="#region-outlive-constraints">Region-outlive constraints</a></h3>
<p>If <code>RegionInferenceContext::check_universal_regions</code> fails to prove some outlives constraint <code>'longer_fr: 'shorter_fr</code>, we try to propagate it in <code>fn try_propagate_universal_region_error</code>. Both these universal regions are either local to the closure or an external region.</p>
<p>In case <code>'longer_fr</code> is a local universal region, we search for the largest external region <code>'fr_minus</code> which is outlived by <code>'longer_fr</code>, i.e. <code>'longer_fr: 'fr_minus</code>. In case there are multiple such regions, we pick the <code>mutual_immediate_postdominator</code>: the fixpoint of repeatedly computing the GLB of all GLBs, see <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/transitive_relation/struct.TransitiveRelation.html#method.postdom_upper_bound">TransitiveRelation::postdom_upper_bound</a> for more details.</p>
<p>If <code>'fr_minus</code> exists we require it to outlive all non-local upper bounds of <code>'shorter_fr</code>. There will always be at least one non-local upper bound <code>'static</code>.</p>
<h3 id="type-outlive-constraints"><a class="header" href="#type-outlive-constraints">Type-outlive constraints</a></h3>
<p>Type-outlives constraints are proven in <code>check_type_tests</code>. This happens after computing the outlives graph, which is now immutable.</p>
<p>For all type tests we fail to prove via <code>fn eval_verify_bound</code> inside of the closure we call <code>try_promote_type_test</code>. A <code>TypeTest</code> represents a type-outlives bound <code>generic_kind: lower_bound</code> together with a <code>verify_bound</code>. If the <code>VerifyBound</code> holds for the <code>lower_bound</code>, the constraint is satisfied. <code>try_promote_type_test</code> does not care about the <code> verify_bound</code>.</p>
<p>It starts by calling <code>fn try_promote_type_test_subject</code>. This function takes the <code>GenericKind</code> and tries to transform it to a <code>ClosureOutlivesSubject</code> which is no longer references anything local to the closure. This is done by replacing all free regions in that type with either <code>'static</code> or region parameters which are equal to that free region. This operation fails if the <code>generic_kind</code> contains a region which cannot be replaced.</p>
<p>We then promote the <code>lower_bound</code> into the context of the caller. If the lower bound is equal to a placeholder, we replace it with <code>'static</code></p>
<p>We then look at all universal regions <code>uv</code> which are required to be outlived by <code>lower_bound</code>, i.e. for which borrow checking added region constraints. For each of these we then emit a <code>ClosureOutlivesRequirement</code> for all non-local universal regions which are known to outlive <code>uv</code>.</p>
<p>As we've already built the region graph of the closure at this point and separately check that it is consistent, we are also able to assume the outlive constraints <code>uv: lower_bound</code> here.</p>
<p>So if we have a type-outlives bounds we can't prove, e.g. <code>T: 'local_infer</code>, we use the region graph to go to universal variables <code>'a</code> with <code>'a: local_infer</code>. In case <code>'a</code> are local, we then use the assumed outlived constraints to go to non-local ones.</p>
<p>We then store the list of promoted type tests in the <code>BorrowCheckResults</code>.
We then apply them in while borrow-checking its parent in <code>TypeChecker::prove_closure_bounds</code>.</p>
<p>TODO: explain how exactly that works :3</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../../borrow_check/region_inference/placeholders_and_universes.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../borrow_check/region_inference/error_reporting.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../../borrow_check/region_inference/placeholders_and_universes.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../../borrow_check/region_inference/error_reporting.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../../elasticlunr.min.js"></script>
<script src="../../mark.min.js"></script>
<script src="../../searcher.js"></script>
<script src="../../clipboard.min.js"></script>
<script src="../../highlight.js"></script>
<script src="../../book.js"></script>
<!-- Custom JS scripts -->
<script src="../../mermaid.min.js"></script>
<script src="../../mermaid-init.js"></script>
</div>
</body>
</html>