blob: 51061aa0de7ebc9afedd682263539036ec1aef76 [file] [log] [blame]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Coherence 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/coherence.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="coherence"><a class="header" href="#coherence">Coherence</a></h1>
<blockquote>
<p>NOTE: this is based on <a href="https://github.com/rust-lang/rust/pull/121848">notes by @lcnr</a></p>
</blockquote>
<p>Coherence checking is what detects both of trait impls and inherent impls overlapping with others.
(reminder: <a href="https://doc.rust-lang.org/reference/items/implementations.html#inherent-implementations">inherent impls</a> are impls of concrete types like <code>impl MyStruct {}</code>)</p>
<p>Overlapping trait impls always produce an error,
while overlapping inherent impls result in an error only if they have methods with the same name.</p>
<p>Checking for overlaps is split in two parts. First there's the <a href="#overlap-checks">overlap check(s)</a>,
which finds overlaps between traits and inherent implementations that the compiler currently knows about.</p>
<p>However, Coherence also results in an error if any other impls <strong>could</strong> exist,
even if they are currently unknown.
This affects impls which may get added to upstream crates in a backwards compatible way,
and impls from downstream crates.
This is called the Orphan check.</p>
<h2 id="overlap-checks"><a class="header" href="#overlap-checks">Overlap checks</a></h2>
<p>Overlap checks are performed for both inherent impls, and for trait impls.
This uses the same overlap checking code, really done as two separate analyses.
Overlap checks always consider pairs of implementations, comparing them to each other.</p>
<p>Overlap checking for inherent impl blocks is done through <code>fn check_item</code> (in coherence/inherent_impls_overlap.rs),
where you can very clearly see that (at least for small <code>n</code>), the check really performs <code>n^2</code>
comparisons between impls.</p>
<p>In the case of traits, this check is currently done as part of building the <a href="traits/specialization.html">specialization graph</a>,
to handle specializing impls overlapping with their parent, but this may change in the future.</p>
<p>In both cases, all pairs of impls are checked for overlap.</p>
<p>Overlapping is sometimes partially allowed:</p>
<ol>
<li>for marker traits</li>
<li>under <a href="traits/specialization.html">specialization</a></li>
</ol>
<p>but normally isn't.</p>
<p>The overlap check has various modes (see <a href="https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/traits/specialization_graph/enum.OverlapMode.html"><code>OverlapMode</code></a>).
Importantly, there's the explicit negative impl check, and the implicit negative impl check.
Both try to prove that an overlap is definitely impossible.</p>
<h3 id="the-explicit-negative-impl-check"><a class="header" href="#the-explicit-negative-impl-check">The explicit negative impl check</a></h3>
<p>This check is done in <a href="https://doc.rust-lang.org/beta/nightly-rustc/rustc_trait_selection/traits/coherence/fn.impl_intersection_has_negative_obligation.html"><code>impl_intersection_has_negative_obligation</code></a>.</p>
<p>This check tries to find a negative trait implementation.
For example:</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>struct MyCustomErrorType;
// both in your own crate
impl From&lt;&amp;str&gt; for MyCustomErrorType {}
impl&lt;E&gt; From&lt;E&gt; for MyCustomErrorType where E: Error {}
<span class="boring">}</span></code></pre></pre>
<p>In this example, we'd get:
<code>MyCustomErrorType: From&lt;&amp;str&gt;</code> and <code>MyCustomErrorType: From&lt;?E&gt;</code>, giving <code>?E = &amp;str</code>.</p>
<p>And thus, these two implementations would overlap.
However, libstd provides <code>&amp;str: !Error</code>, and therefore guarantees that there
will never be a positive implementation of <code>&amp;str: Error</code>, and thus there is no overlap.</p>
<p>Note that for this kind of negative impl check, we must have explicit negative implementations provided.
This is not currently stable.</p>
<h3 id="the-implicit-negative-impl-check"><a class="header" href="#the-implicit-negative-impl-check">The implicit negative impl check</a></h3>
<p>This check is done in <a href="https://doc.rust-lang.org/beta/nightly-rustc/rustc_trait_selection/traits/coherence/fn.impl_intersection_has_impossible_obligation.html"><code>impl_intersection_has_impossible_obligation</code></a>,
and does not rely on negative trait implementations and is stable.</p>
<p>Let's say there's a</p>
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
</span><span class="boring">fn main() {
</span>impl From&lt;MyLocalType&gt; for Box&lt;dyn Error&gt; {} // in your own crate
impl&lt;E&gt; From&lt;E&gt; for Box&lt;dyn Error&gt; where E: Error {} // in std
<span class="boring">}</span></code></pre></pre>
<p>This would give: <code>Box&lt;dyn Error&gt;: From&lt;MyLocalType&gt;</code>, and <code>Box&lt;dyn Error&gt;: From&lt;?E&gt;</code>,<br />
giving <code>?E = MyLocalType</code>.</p>
<p>In your crate there's no <code>MyLocalType: Error</code>, downstream crates cannot implement <code>Error</code> (a remote trait) for <code>MyLocalType</code> (a remote type).
Therefore, these two impls do not overlap.
Importantly, this works even if there isn't a <code>impl !Error for MyLocalType</code>.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="variance.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="opaque-types-type-alias-impl-trait.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="variance.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="opaque-types-type-alias-impl-trait.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>