blob: 2dac8ec8db37e047cc14fa9b32356266b5c4485d [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Const condition checking - 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/effects.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="effects-and-const-condition-checking"><a class="header" href="#effects-and-const-condition-checking">Effects and const condition checking</a></h1>
<h2 id="the-hosteffect-predicate"><a class="header" href="#the-hosteffect-predicate">The <code>HostEffect</code> predicate</a></h2>
<p><a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/predicate/struct.HostEffectPredicate.html"><code>HostEffectPredicate</code></a>s are a kind of predicate from <code>~const Tr</code> or <code>const Tr</code>
bounds. It has a trait reference, and a <code>constness</code> which could be <code>Maybe</code> or
<code>Const</code> depending on the bound. Because <code>~const Tr</code>, or rather <code>Maybe</code> bounds
apply differently based on whichever contexts they are in, they have different
behavior than normal bounds. Where normal trait bounds on a function such as
<code>T: Tr</code> are collected within the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.predicates_of"><code>predicates_of</code></a> query to be proven when a
function is called and to be assumed within the function, bounds such as
<code>T: ~const Tr</code> will behave as a normal trait bound and add <code>T: Tr</code> to the result
from <code>predicates_of</code>, but also adds a <code>HostEffectPredicate</code> to the
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.const_conditions"><code>const_conditions</code></a> query.</p>
<p>On the other hand, <code>T: const Tr</code> bounds do not change meaning across contexts,
therefore they will result in <code>HostEffect(T: Tr, const)</code> being added to
<code>predicates_of</code>, and not <code>const_conditions</code>.</p>
<h2 id="the-const_conditions-query"><a class="header" href="#the-const_conditions-query">The <code>const_conditions</code> query</a></h2>
<p><code>predicates_of</code> represents a set of predicates that need to be proven to use an
item. For example, to use <code>foo</code> in the example below:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>fn foo&lt;T&gt;() where T: Default {}
<span class="boring">}</span></code></pre></pre>
<p>We must be able to prove that <code>T</code> implements <code>Default</code>. In a similar vein,
<code>const_conditions</code> represents a set of predicates that need to be proven to use
an item <em>in const contexts</em>. If we adjust the example above to use <code>const</code> trait
bounds:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>const fn foo&lt;T&gt;() where T: ~const Default {}
<span class="boring">}</span></code></pre></pre>
<p>Then <code>foo</code> would get a <code>HostEffect(T: Default, maybe)</code> in the <code>const_conditions</code>
query, suggesting that in order to call <code>foo</code> from const contexts, one must
prove that <code>T</code> has a const implementation of <code>Default</code>.</p>
<h2 id="enforcement-of-const_conditions"><a class="header" href="#enforcement-of-const_conditions">Enforcement of <code>const_conditions</code></a></h2>
<p><code>const_conditions</code> are currently checked in various places.</p>
<p>Every call in HIR from a const context (which includes <code>const fn</code> and <code>const</code>
items) will check that <code>const_conditions</code> of the function we are calling hold.
This is done in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_typeck/fn_ctxt/struct.FnCtxt.html#method.enforce_context_effects"><code>FnCtxt::enforce_context_effects</code></a>. Note that we don't check
if the function is only referred to but not called, as the following code needs
to compile:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>const fn hi&lt;T: ~const Default&gt;() -&gt; T {
T::default()
}
const X: fn() -&gt; u32 = hi::&lt;u32&gt;;
<span class="boring">}</span></code></pre></pre>
<p>For a trait <code>impl</code> to be well-formed, we must be able to prove the
<code>const_conditions</code> of the trait from the <code>impl</code>'s environment. This is checked
in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/wfcheck/fn.check_impl.html"><code>wfcheck::check_impl</code></a>.</p>
<p>Here's an example:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[const_trait]
trait Bar {}
#[const_trait]
trait Foo: ~const Bar {}
// `const_conditions` contains `HostEffect(Self: Bar, maybe)`
impl const Bar for () {}
impl const Foo for () {}
// ^ here we check `const_conditions` for the impl to be well-formed
<span class="boring">}</span></code></pre></pre>
<p>Methods of trait impls must not have stricter bounds than the method of the
trait that they are implementing. To check that the methods are compatible, a
hybrid environment is constructed with the predicates of the <code>impl</code> plus the
predicates of the trait method, and we attempt to prove the predicates of the
impl method. We do the same for <code>const_conditions</code>:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>#[const_trait]
trait Foo {
fn hi&lt;T: ~const Default&gt;();
}
impl&lt;T: ~const Clone&gt; Foo for Vec&lt;T&gt; {
fn hi&lt;T: ~const PartialEq&gt;();
// ^ we can't prove `T: ~const PartialEq` given `T: ~const Clone` and
// `T: ~const Default`, therefore we know that the method on the impl
// is stricter than the method on the trait.
}
<span class="boring">}</span></code></pre></pre>
<p>These checks are done in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_method_predicate_entailment.html"><code>compare_method_predicate_entailment</code></a>. A similar
function that does the same check for associated types is called
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.compare_type_predicate_entailment.html"><code>compare_type_predicate_entailment</code></a>. Both of these need to consider
<code>const_conditions</code> when in const contexts.</p>
<p>In MIR, as part of const checking, <code>const_conditions</code> of items that are called
are revalidated again in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_const_eval/check_consts/check/struct.Checker.html#method.revalidate_conditional_constness"><code>Checker::revalidate_conditional_constness</code></a>.</p>
<h2 id="explicit_implied_const_bounds-on-associated-types-and-traits"><a class="header" href="#explicit_implied_const_bounds-on-associated-types-and-traits"><code>explicit_implied_const_bounds</code> on associated types and traits</a></h2>
<p>Bounds on associated types, opaque types, and supertraits such as</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>trait Foo: ~const PartialEq {
type X: ~const PartialEq;
}
fn foo() -&gt; impl ~const PartialEq {
// ^ unimplemented syntax
}
<span class="boring">}</span></code></pre></pre>
<p>Have their bounds represented differently. Unlike <code>const_conditions</code> which need
to be proved for callers, and can be assumed inside the definition (e.g. trait
bounds on functions), these bounds need to be proved at definition (at the impl,
or when returning the opaque) but can be assumed for callers. The non-const
equivalent of these bounds are called <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.explicit_item_bounds"><code>explicit_item_bounds</code></a>.</p>
<p>These bounds are checked in <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.check_type_bounds.html"><code>compare_impl_item::check_type_bounds</code></a> for HIR
typeck, <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/effects/fn.evaluate_host_effect_from_item_bounds.html"><code>evaluate_host_effect_from_item_bounds</code></a> in the old solver and
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_next_trait_solver/solve/assembly/trait.GoalKind.html#tymethod.consider_additional_alias_assumptions"><code>consider_additional_alias_assumptions</code></a> in the new solver.</p>
<h2 id="proving-hosteffectpredicates"><a class="header" href="#proving-hosteffectpredicates">Proving <code>HostEffectPredicate</code>s</a></h2>
<p><code>HostEffectPredicate</code>s are implemented both in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html">old solver</a> and the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html">new
trait solver</a>. In general, we can prove a <code>HostEffect</code> predicate when either of
these conditions are met:</p>
<ul>
<li>The predicate can be assumed from caller bounds;</li>
<li>The type has a <code>const</code> <code>impl</code> for the trait, <em>and</em> that const conditions on
the impl holds, <em>and</em> that the <code>explicit_implied_const_bounds</code> on the trait
holds; or</li>
<li>The type has a built-in implementation for the trait in const contexts. For
example, <code>Fn</code> may be implemented by function items if their const conditions
are satisfied, or <code>Destruct</code> is implemented in const contexts if the type can
be dropped at compile time.</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="borrow_check/opaque-types-region-inference-restrictions.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="pat-exhaustive-checking.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/opaque-types-region-inference-restrictions.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="pat-exhaustive-checking.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>