blob: d9b11b0098cb3c43219699d4aaa367ed75fa26ad [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Type inference - 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/type-inference.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="type-inference"><a class="header" href="#type-inference">Type inference</a></h1>
<ul>
<li><a href="#a-note-on-terminology">A note on terminology</a></li>
<li><a href="#creating-an-inference-context">Creating an inference context</a></li>
<li><a href="#inference-variables">Inference variables</a></li>
<li><a href="#enforcing-equality--subtyping">Enforcing equality / subtyping</a></li>
<li><a href="#trying-equality">"Trying" equality</a></li>
<li><a href="#snapshots">Snapshots</a></li>
<li><a href="#subtyping-obligations">Subtyping obligations</a></li>
<li><a href="#region-constraints">Region constraints</a></li>
<li><a href="#solving-region-constraints">Solving region constraints</a></li>
<li><a href="#lexical-region-resolution">Lexical region resolution</a></li>
</ul>
<p>Type inference is the process of automatic detection of the type of an
expression.</p>
<p>It is what allows Rust to work with fewer or no type annotations,
making things easier for users:</p>
<pre><pre class="playground"><code class="language-rust">fn main() {
let mut things = vec![];
things.push("thing");
}</code></pre></pre>
<p>Here, the type of <code>things</code> is <em>inferred</em> to be <code>Vec&lt;&amp;str&gt;</code> because of the value
we push into <code>things</code>.</p>
<p>The type inference is based on the standard Hindley-Milner (HM) type inference
algorithm, but extended in various ways to accommodate subtyping, region
inference, and higher-ranked types.</p>
<h2 id="a-note-on-terminology"><a class="header" href="#a-note-on-terminology">A note on terminology</a></h2>
<p>We use the notation <code>?T</code> to refer to inference variables, also called
existential variables.</p>
<p>We use the terms "region" and "lifetime" interchangeably. Both refer to
the <code>'a</code> in <code>&amp;'a T</code>.</p>
<p>The term "bound region" refers to a region that is bound in a function
signature, such as the <code>'a</code> in <code>for&lt;'a&gt; fn(&amp;'a u32)</code>. A region is
"free" if it is not bound.</p>
<h2 id="creating-an-inference-context"><a class="header" href="#creating-an-inference-context">Creating an inference context</a></h2>
<p>You create an inference context by doing something like
the following:</p>
<pre><code class="language-rust ignore">let infcx = tcx.infer_ctxt().build();
// Use the inference context `infcx` here.</code></pre>
<p><code>infcx</code> has the type <code>InferCtxt&lt;'tcx&gt;</code>, the same <code>'tcx</code> lifetime as on
the <code>tcx</code> it was built from.</p>
<p>The <code>tcx.infer_ctxt</code> method actually returns a builder, which means
there are some kinds of configuration you can do before the <code>infcx</code> is
created. See <code>InferCtxtBuilder</code> for more information.</p>
<p><a id="vars"></a></p>
<h2 id="inference-variables"><a class="header" href="#inference-variables">Inference variables</a></h2>
<p>The main purpose of the inference context is to house a bunch of
<strong>inference variables</strong> – these represent types or regions whose precise
value is not yet known, but will be uncovered as we perform type-checking.</p>
<p>If you're familiar with the basic ideas of unification from H-M type
systems, or logic languages like Prolog, this is the same concept. If
you're not, you might want to read a tutorial on how H-M type
inference works, or perhaps this blog post on
<a href="http://smallcultfollowing.com/babysteps/blog/2017/03/25/unification-in-chalk-part-1/">unification in the Chalk project</a>.</p>
<p>All told, the inference context stores five kinds of inference variables
(as of <!-- date-check --> March 2023):</p>
<ul>
<li>Type variables, which come in three varieties:
<ul>
<li>General type variables (the most common). These can be unified with any
type.</li>
<li>Integral type variables, which can only be unified with an integral type,
and arise from an integer literal expression like <code>22</code>.</li>
<li>Float type variables, which can only be unified with a float type, and
arise from a float literal expression like <code>22.0</code>.</li>
</ul>
</li>
<li>Region variables, which represent lifetimes, and arise all over the place.</li>
<li>Const variables, which represent constants.</li>
</ul>
<p>All the type variables work in much the same way: you can create a new
type variable, and what you get is <code>Ty&lt;'tcx&gt;</code> representing an
unresolved type <code>?T</code>. Then later you can apply the various operations
that the inferencer supports, such as equality or subtyping, and it
will possibly <strong>instantiate</strong> (or <strong>bind</strong>) that <code>?T</code> to a specific
value as a result.</p>
<p>The region variables work somewhat differently, and are described
below in a separate section.</p>
<h2 id="enforcing-equality--subtyping"><a class="header" href="#enforcing-equality--subtyping">Enforcing equality / subtyping</a></h2>
<p>The most basic operations you can perform in the type inferencer is
<strong>equality</strong>, which forces two types <code>T</code> and <code>U</code> to be the same. The
recommended way to add an equality constraint is to use the <code>at</code>
method, roughly like so:</p>
<pre><code class="language-rust ignore">infcx.at(...).eq(t, u);</code></pre>
<p>The first <code>at()</code> call provides a bit of context, i.e. why you are
doing this unification, and in what environment, and the <code>eq</code> method
performs the actual equality constraint.</p>
<p>When you equate things, you force them to be precisely equal. Equating
returns an <code>InferResult</code> – if it returns <code>Err(err)</code>, then equating
failed, and the enclosing <code>TypeError</code> will tell you what went wrong.</p>
<p>The success case is perhaps more interesting. The "primary" return
type of <code>eq</code> is <code>()</code> – that is, when it succeeds, it doesn't return a
value of any particular interest. Rather, it is executed for its
side-effects of constraining type variables and so forth. However, the
actual return type is not <code>()</code>, but rather <code>InferOk&lt;()&gt;</code>. The
<code>InferOk</code> type is used to carry extra trait obligations – your job is
to ensure that these are fulfilled (typically by enrolling them in a
fulfillment context). See the <a href="traits/resolution.html">trait chapter</a> for more background on that.</p>
<p>You can similarly enforce subtyping through <code>infcx.at(..).sub(..)</code>. The same
basic concepts as above apply.</p>
<h2 id="trying-equality"><a class="header" href="#trying-equality">"Trying" equality</a></h2>
<p>Sometimes you would like to know if it is <em>possible</em> to equate two
types without error. You can test that with <code>infcx.can_eq</code> (or
<code>infcx.can_sub</code> for subtyping). If this returns <code>Ok</code>, then equality
is possible – but in all cases, any side-effects are reversed.</p>
<p>Be aware, though, that the success or failure of these methods is always
<strong>modulo regions</strong>. That is, two types <code>&amp;'a u32</code> and <code>&amp;'b u32</code> will
return <code>Ok</code> for <code>can_eq</code>, even if <code>'a != 'b</code>. This falls out from the
"two-phase" nature of how we solve region constraints.</p>
<h2 id="snapshots"><a class="header" href="#snapshots">Snapshots</a></h2>
<p>As described in the previous section on <code>can_eq</code>, often it is useful
to be able to do a series of operations and then roll back their
side-effects. This is done for various reasons: one of them is to be
able to backtrack, trying out multiple possibilities before settling
on which path to take. Another is in order to ensure that a series of
smaller changes take place atomically or not at all.</p>
<p>To allow for this, the inference context supports a <code>snapshot</code> method.
When you call it, it will start recording changes that occur from the
operations you perform. When you are done, you can either invoke
<code>rollback_to</code>, which will undo those changes, or else <code>confirm</code>, which
will make them permanent. Snapshots can be nested as long as you follow
a stack-like discipline.</p>
<p>Rather than use snapshots directly, it is often helpful to use the
methods like <code>commit_if_ok</code> or <code>probe</code> that encapsulate higher-level
patterns.</p>
<h2 id="subtyping-obligations"><a class="header" href="#subtyping-obligations">Subtyping obligations</a></h2>
<p>One thing worth discussing is subtyping obligations. When you force
two types to be a subtype, like <code>?T &lt;: i32</code>, we can often convert those
into equality constraints. This follows from Rust's rather limited notion
of subtyping: so, in the above case, <code>?T &lt;: i32</code> is equivalent to <code>?T = i32</code>.</p>
<p>However, in some cases we have to be more careful. For example, when
regions are involved. So if you have <code>?T &lt;: &amp;'a i32</code>, what we would do
is to first "generalize" <code>&amp;'a i32</code> into a type with a region variable:
<code>&amp;'?b i32</code>, and then unify <code>?T</code> with that (<code>?T = &amp;'?b i32</code>). We then
relate this new variable with the original bound:</p>
<pre><code class="language-text">&amp;'?b i32 &lt;: &amp;'a i32
</code></pre>
<p>This will result in a region constraint (see below) of <code>'?b: 'a</code>.</p>
<p>One final interesting case is relating two unbound type variables,
like <code>?T &lt;: ?U</code>. In that case, we can't make progress, so we enqueue
an obligation <code>Subtype(?T, ?U)</code> and return it via the <code>InferOk</code>
mechanism. You'll have to try again when more details about <code>?T</code> or
<code>?U</code> are known.</p>
<h2 id="region-constraints"><a class="header" href="#region-constraints">Region constraints</a></h2>
<p>Regions are inferenced somewhat differently from types. Rather than
eagerly unifying things, we simply collect constraints as we go, but
make (almost) no attempt to solve regions. These constraints have the
form of an "outlives" constraint:</p>
<pre><code class="language-text">'a: 'b
</code></pre>
<p>Actually the code tends to view them as a subregion relation, but it's the same
idea:</p>
<pre><code class="language-text">'b &lt;= 'a
</code></pre>
<p>(There are various other kinds of constraints, such as "verifys"; see
the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/region_constraints/index.html"><code>region_constraints</code></a> module for details.)</p>
<p>There is one case where we do some amount of eager unification. If you have an
equality constraint between two regions</p>
<pre><code class="language-text">'a = 'b
</code></pre>
<p>we will record that fact in a unification table. You can then use
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/region_constraints/struct.RegionConstraintCollector.html#method.opportunistic_resolve_var"><code>opportunistic_resolve_var</code></a> to convert <code>'b</code> to <code>'a</code> (or vice
versa). This is sometimes needed to ensure termination of fixed-point
algorithms.</p>
<h2 id="solving-region-constraints"><a class="header" href="#solving-region-constraints">Solving region constraints</a></h2>
<p>Region constraints are only solved at the very end of
typechecking, once all other constraints are known and
all other obligations have been proven. There are two
ways to solve region constraints right now: lexical and
non-lexical. Eventually there will only be one.</p>
<p>An exception here is the leak-check which is used during trait solving
and relies on region constraints containing higher-ranked regions. Region
constraints in the root universe (i.e. not arising from a <code>for&lt;'a&gt;</code>) must
not influence the trait system, as these regions are all erased during
codegen.</p>
<p>To solve <strong>lexical</strong> region constraints, you invoke
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.resolve_regions_and_report_errors"><code>resolve_regions_and_report_errors</code></a>. This "closes" the region
constraint process and invokes the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/lexical_region_resolve/index.html"><code>lexical_region_resolve</code></a> code. Once
this is done, any further attempt to equate or create a subtyping
relationship will yield an ICE.</p>
<p>The NLL solver (actually, the MIR type-checker) does things slightly
differently. It uses canonical queries for trait solving which use
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.take_and_reset_region_constraints"><code>take_and_reset_region_constraints</code></a> at the end. This extracts all of the
outlives constraints added during the canonical query. This is required
as the NLL solver must not only know <em>what</em> regions outlive each other,
but also <em>where</em>. Finally, the NLL solver invokes <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxt.html#method.take_region_var_origins"><code>take_region_var_origins</code></a>,
providing all region variables to the solver.</p>
<h2 id="lexical-region-resolution"><a class="header" href="#lexical-region-resolution">Lexical region resolution</a></h2>
<p>Lexical region resolution is done by initially assigning each region
variable to an empty value. We then process each outlives constraint
repeatedly, growing region variables until a fixed-point is reached.
Region variables can be grown using a least-upper-bound relation on
the region lattice in a fairly straightforward fashion.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="typing_parameter_envs.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="traits/resolution.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="typing_parameter_envs.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="traits/resolution.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>