blob: d99ab2de9aefdcec7ce5f10628b6d99e972f83ad [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>The solver - 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/solve/the-solver.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="the-solver"><a class="header" href="#the-solver">The solver</a></h1>
<p>Also consider reading the documentation for <a href="https://rust-lang.github.io/chalk/book/recursive.html">the recursive solver in chalk</a>
as it is very similar to this implementation and also talks about limitations of this
approach.</p>
<h2 id="a-rough-walkthrough"><a class="header" href="#a-rough-walkthrough">A rough walkthrough</a></h2>
<p>The entry-point of the solver is <code>InferCtxtEvalExt::evaluate_root_goal</code>. This
function sets up the root <code>EvalCtxt</code> and then calls <code>EvalCtxt::evaluate_goal</code>,
to actually enter the trait solver.</p>
<p><code>EvalCtxt::evaluate_goal</code> handles <a href="./canonicalization.html">canonicalization</a>, caching,
overflow, and solver cycles. Once that is done, it creates a nested <code>EvalCtxt</code> with a
separate local <code>InferCtxt</code> and calls <code>EvalCtxt::compute_goal</code>, which is responsible for the
'actual solver behavior'. We match on the <code>PredicateKind</code>, delegating to a separate function
for each one.</p>
<p>For trait goals, such a <code>Vec&lt;T&gt;: Clone</code>, <code>EvalCtxt::compute_trait_goal</code> has
to collect all the possible ways this goal can be proven via
<code>EvalCtxt::assemble_and_evaluate_candidates</code>. Each candidate is handled in
a separate "probe", to not leak inference constraints to the other candidates.
We then try to merge the assembled candidates via <code>EvalCtxt::merge_candidates</code>.</p>
<h2 id="important-concepts-and-design-patterns"><a class="header" href="#important-concepts-and-design-patterns">Important concepts and design patterns</a></h2>
<h3 id="evalctxtadd_goal"><a class="header" href="#evalctxtadd_goal"><code>EvalCtxt::add_goal</code></a></h3>
<p>To prove nested goals, we don't directly call <code>EvalCtxt::compute_goal</code>, but instead
add the goal to the <code>EvalCtxt</code> with <code>EvalCtxt::all_goal</code>. We then prove all nested
goals together in either <code>EvalCtxt::try_evaluate_added_goals</code> or
<code>EvalCtxt::evaluate_added_goals_and_make_canonical_response</code>. This allows us to handle
inference constraints from later goals.</p>
<p>E.g. if we have both <code>?x: Debug</code> and <code>(): ConstrainToU8&lt;?x&gt;</code> as nested goals,
then proving <code>?x: Debug</code> is initially ambiguous, but after proving <code>(): ConstrainToU8&lt;?x&gt;</code>
we constrain <code>?x</code> to <code>u8</code> and proving <code>u8: Debug</code> succeeds.</p>
<h3 id="matching-on-tykind"><a class="header" href="#matching-on-tykind">Matching on <code>TyKind</code></a></h3>
<p>We lazily normalize types in the solver, so we always have to assume that any types
and constants are potentially unnormalized. This means that matching on <code>TyKind</code> can easily
be incorrect.</p>
<p>We handle normalization in two different ways. When proving <code>Trait</code> goals when normalizing
associated types, we separately assemble candidates depending on whether they structurally
match the self type. Candidates which match on the self type are handled in
<code>EvalCtxt::assemble_candidates_via_self_ty</code> which recurses via
<code>EvalCtxt::assemble_candidates_after_normalizing_self_ty</code>, which normalizes the self type
by one level. In all other cases we have to match on a <code>TyKind</code> we first use
<code>EvalCtxt::try_normalize_ty</code> to normalize the type as much as possible.</p>
<h3 id="higher-ranked-goals"><a class="header" href="#higher-ranked-goals">Higher ranked goals</a></h3>
<p>In case the goal is higher-ranked, e.g. <code>for&lt;'a&gt; F: FnOnce(&amp;'a ())</code>, <code>EvalCtxt::compute_goal</code>
eagerly instantiates <code>'a</code> with a placeholder and then recursively proves
<code>F: FnOnce(&amp;'!a ())</code> as a nested goal.</p>
<h3 id="dealing-with-choice"><a class="header" href="#dealing-with-choice">Dealing with choice</a></h3>
<p>Some goals can be proven in multiple ways. In these cases we try each option in
a separate "probe" and then attempt to merge the resulting responses by using
<code>EvalCtxt::try_merge_responses</code>. If merging the responses fails, we use
<code>EvalCtxt::flounder</code> instead, returning ambiguity. For some goals, we try to
incompletely prefer some choices over others in case <code>EvalCtxt::try_merge_responses</code>
fails.</p>
<h2 id="learning-more"><a class="header" href="#learning-more">Learning more</a></h2>
<p>The solver should be fairly self-contained. I hope that the above information provides a
good foundation when looking at the code itself. Please reach out on Zulip if you get stuck
while doing so or there are some quirks and design decisions which were unclear and deserve
better comments or should be mentioned here.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../solve/invariants.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="../solve/candidate-preference.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="../solve/invariants.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="../solve/candidate-preference.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>