blob: 1c978f119daa53d910137a010df7b4eb5612e1f8 [file] [log] [blame] [edit]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Drop check - 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 -->
<link rel="stylesheet" href="../pagetoc.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "../";
const default_light_theme = "light";
const default_dark_theme = "navy";
window.path_to_searchindex_js = "../searchindex.js";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<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 = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</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 (`/`)" 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/main/src/borrow_check/drop_check.md" title="Suggest an edit" aria-label="Suggest an edit" rel="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">
<div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
</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="drop-check"><a class="header" href="#drop-check">Drop Check</a></h1>
<p>We generally require the type of locals to be well-formed whenever the
local is used. This includes proving the where-bounds of the local and
also requires all regions used by it to be live.</p>
<p>The only exception to this is when implicitly dropping values when they
go out of scope. This does not necessarily require the value to be live:</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
let x = vec![];
{
let y = String::from("I am temporary");
x.push(&amp;y);
}
// `x` goes out of scope here, after the reference to `y`
// is invalidated. This means that while dropping `x` its type
// is not well-formed as it contain regions which are not live.
}</code></pre></pre>
<p>This is only sound if dropping the value does not try to access any dead
region. We check this by requiring the type of the value to be
drop-live.
The requirements for which are computed in <code>fn dropck_outlives</code>.</p>
<p>The rest of this section uses the following type definition for a type
which requires its region parameter to be live:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>struct PrintOnDrop&lt;'a&gt;(&amp;'a str);
impl&lt;'a&gt; Drop for PrintOnDrop&lt;'_&gt; {
fn drop(&amp;mut self) {
println!("{}", self.0);
}
}
<span class="boring">}</span></code></pre></pre>
<h2 id="how-values-are-dropped"><a class="header" href="#how-values-are-dropped">How values are dropped</a></h2>
<p>At its core, a value of type <code>T</code> is dropped by executing its "drop
glue". Drop glue is compiler generated and first calls <code>&lt;T as Drop&gt;::drop</code> and then recursively calls the drop glue of any recursively
owned values.</p>
<ul>
<li>If <code>T</code> has an explicit <code>Drop</code> impl, call <code>&lt;T as Drop&gt;::drop</code>.</li>
<li>Regardless of whether <code>T</code> implements <code>Drop</code>, recurse into all values
<em>owned</em> by <code>T</code>:
<ul>
<li>references, raw pointers, function pointers, function items, trait
objects<sup class="footnote-reference" id="fr-traitobj-1"><a href="#footnote-traitobj">1</a></sup>, and scalars do not own anything.</li>
<li>tuples, slices, and arrays consider their elements to be owned.
For arrays of length zero we do not own any value of the element
type.</li>
<li>all fields (of all variants) of ADTs are considered owned. We
consider all variants for enums. The exception here is
<code>ManuallyDrop&lt;U&gt;</code> which is not considered to own <code>U</code>.
<code>PhantomData&lt;U&gt;</code> also does not own anything.
closures and generators own their captured upvars.</li>
</ul>
</li>
</ul>
<p>Whether a type has drop glue is returned by <a href="https://github.com/rust-lang/rust/blob/320b412f9c55bf480d26276ff0ab480e4ecb29c0/compiler/rustc_middle/src/ty/util.rs#L1086-L1108"><code>fn Ty::needs_drop</code></a>.</p>
<h3 id="partially-dropping-a-local"><a class="header" href="#partially-dropping-a-local">Partially dropping a local</a></h3>
<p>For types which do not implement <code>Drop</code> themselves, we can also
partially move parts of the value before dropping the rest. In this case
only the drop glue for the not-yet moved values is called, e.g.</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
let mut x = (PrintOnDrop("third"), PrintOnDrop("first"));
drop(x.1);
println!("second")
}</code></pre></pre>
<p>During MIR building we assume that a local may get dropped whenever it
goes out of scope <em>as long as its type needs drop</em>. Computing the exact
drop glue for a variable happens <strong>after</strong> borrowck in the
<code>ElaborateDrops</code> pass. This means that even if some part of the local
have been dropped previously, dropck still requires this value to be
live. This is the case even if we completely moved a local.</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
let mut x;
{
let temp = String::from("I am temporary");
x = PrintOnDrop(&amp;temp);
drop(x);
}
} //~ ERROR `temp` does not live long enough.</code></pre></pre>
<p>It should be possible to add some amount of drop elaboration before
borrowck, allowing this example to compile. There is an unstable feature
to move drop elaboration before const checking:
<a href="https://github.com/rust-lang/rust/issues/73255">#73255</a>. Such a feature
gate does not exist for doing some drop elaboration before borrowck,
although there's a <a href="https://github.com/rust-lang/compiler-team/issues/558">relevant
MCP</a>.</p>
<h3 id="dropck_outlives"><a class="header" href="#dropck_outlives"><code>dropck_outlives</code></a></h3>
<p>There are two distinct "liveness" computations that we perform:</p>
<ul>
<li>a value <code>v</code> is <em>use-live</em> at location <code>L</code> if it may be "used" later; a
<em>use</em> here is basically anything that is not a <em>drop</em></li>
<li>a value <code>v</code> is <em>drop-live</em> at location <code>L</code> if it maybe dropped later</li>
</ul>
<p>When things are <em>use-live</em>, their entire type must be valid at <code>L</code>. When
they are <em>drop-live</em>, all regions that are required by dropck must be
valid at <code>L</code>. The values dropped in the MIR are <em>places</em>.</p>
<p>The constraints computed by <code>dropck_outlives</code> for a type closely match
the generated drop glue for that type. Unlike drop glue,
<code>dropck_outlives</code> cares about the types of owned values, not the values
itself. For a value of type <code>T</code></p>
<ul>
<li>if <code>T</code> has an explicit <code>Drop</code>, require all generic arguments to be
live, unless they are marked with <code>#[may_dangle]</code> in which case they
are fully ignored</li>
<li>regardless of whether <code>T</code> has an explicit <code>Drop</code>, recurse into all
types <em>owned</em> by <code>T</code>
<ul>
<li>references, raw pointers, function pointers, function items, trait
objects<sup class="footnote-reference" id="fr-traitobj-2"><a href="#footnote-traitobj">1</a></sup>, and scalars do not own anything.</li>
<li>tuples, slices and arrays consider their element type to be owned.
<strong>For arrays we currently do not check whether their length is
zero</strong>.</li>
<li>all fields (of all variants) of ADTs are considered owned. The
exception here is <code>ManuallyDrop&lt;U&gt;</code> which is not considered to own
<code>U</code>. <strong>We consider <code>PhantomData&lt;U&gt;</code> to own <code>U</code></strong>.</li>
<li>closures and generators own their captured upvars.</li>
</ul>
</li>
</ul>
<p>The sections marked in bold are cases where <code>dropck_outlives</code> considers
types to be owned which are ignored by <code>Ty::needs_drop</code>. We only rely on
<code>dropck_outlives</code> if <code>Ty::needs_drop</code> for the containing local returned
<code>true</code>.This means liveness requirements can change depending on whether
a type is contained in a larger local. <strong>This is inconsistent, and
should be fixed: an example <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=8b5f5f005a03971b22edb1c20c5e6cbe">for
arrays</a>
and <a href="https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=44c6e2b1fae826329fd54c347603b6c8">for
<code>PhantomData</code></a>.</strong><sup class="footnote-reference" id="fr-core-1"><a href="#footnote-core">2</a></sup></p>
<p>One possible way these inconsistencies can be fixed is by MIR building
to be more pessimistic, probably by making <code>Ty::needs_drop</code> weaker, or
alternatively, changing <code>dropck_outlives</code> to be more precise, requiring
fewer regions to be live.</p>
<hr>
<ol class="footnote-definition"><li id="footnote-traitobj">
<p>you can consider trait objects to have a builtin <code>Drop</code>
implementation which directly uses the <code>drop_in_place</code> provided by the
vtable. This <code>Drop</code> implementation requires all its generic parameters
to be live. <a href="#fr-traitobj-1"></a> <a href="#fr-traitobj-2">↩2</a></p>
</li>
<li id="footnote-core">
<p>This is the core assumption of <a href="https://github.com/rust-lang/rust/issues/110288">#110288</a> and <a href="https://github.com/rust-lang/rfcs/pull/3417">RFC 3417</a>. <a href="#fr-core-1"></a></p>
</li>
</ol>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../borrow_check/type_check.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.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/type_check.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.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>
<script src="../pagetoc.js"></script>
</div>
</body>
</html>