584 lines
32 KiB
HTML
584 lines
32 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en" class="light sidebar-visible" dir="ltr">
|
|
<head>
|
|
<!-- Book generated using mdBook -->
|
|
<meta charset="UTF-8">
|
|
<title>Return Position Impl Trait In Trait - 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/return-position-impl-trait-in-trait.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="return-position-impl-trait-in-trait"><a class="header" href="#return-position-impl-trait-in-trait">Return Position Impl Trait In Trait</a></h1>
|
|
<p>Return-position impl trait in trait (RPITIT) is conceptually (and as of
|
|
<a href="https://github.com/rust-lang/rust/pull/112988">#112988</a>, literally) sugar that turns RPITs in trait methods into
|
|
generic associated types (GATs) without the user having to define that
|
|
GAT either on the trait side or impl side.</p>
|
|
<p>RPITIT was originally implemented in <a href="https://github.com/rust-lang/rust/pull/101224">#101224</a>, which added support for
|
|
async fn in trait (AFIT), since the implementation for RPITIT came for
|
|
free as a part of implementing AFIT which had been RFC'd previously. It
|
|
was then RFC'd independently in <a href="https://github.com/rust-lang/rfcs/pull/3425">RFC 3425</a>, which was recently approved
|
|
by T-lang.</p>
|
|
<h2 id="how-does-it-work"><a class="header" href="#how-does-it-work">How does it work?</a></h2>
|
|
<p>This doc is ordered mostly via the compilation pipeline:</p>
|
|
<ol>
|
|
<li>AST lowering (AST -> HIR)</li>
|
|
<li>HIR ty lowering (HIR -> rustc_middle::ty data types)</li>
|
|
<li>typeck</li>
|
|
</ol>
|
|
<h3 id="ast-lowering"><a class="header" href="#ast-lowering">AST lowering</a></h3>
|
|
<p>AST lowering for RPITITs is almost the same as lowering RPITs. We
|
|
still lower them as
|
|
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.OpaqueTy.html"><code>hir::ItemKind::OpaqueTy</code></a>.
|
|
The two differences are that:</p>
|
|
<p>We record <code>in_trait</code> for the opaque. This will signify that the opaque
|
|
is an RPITIT for HIR ty lowering, diagnostics that deal with HIR, etc.</p>
|
|
<p>We record <code>lifetime_mapping</code>s for the opaque type, described below.</p>
|
|
<h4 id="aside-opaque-lifetime-duplication"><a class="header" href="#aside-opaque-lifetime-duplication">Aside: Opaque lifetime duplication</a></h4>
|
|
<p><em>All opaques</em> (not just RPITITs) end up duplicating their captured
|
|
lifetimes into new lifetime parameters local to the opaque. The main
|
|
reason we do this is because RPITs need to be able to "reify"<sup class="footnote-reference" id="fr-1-1"><a href="#footnote-1">1</a></sup> any
|
|
captured late-bound arguments, or make them into early-bound ones. This
|
|
is so they can be used as generic args for the opaque, and later to
|
|
instantiate hidden types. Since we don't know which lifetimes are early-
|
|
or late-bound during AST lowering, we just do this for all lifetimes.</p>
|
|
<p>The main addition for RPITITs is that during lowering we track the
|
|
relationship between the captured lifetimes and the corresponding
|
|
duplicated lifetimes in an additional field,
|
|
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/struct.OpaqueTy.html#structfield.lifetime_mapping"><code>OpaqueTy::lifetime_mapping</code></a>.
|
|
We use this lifetime mapping later on in <code>predicates_of</code> to install
|
|
bounds that enforce equality between these duplicated lifetimes and
|
|
their source lifetimes in order to properly typecheck these GATs, which
|
|
will be discussed below.</p>
|
|
<h5 id="note"><a class="header" href="#note">Note</a></h5>
|
|
<p>It may be better if we were able to lower without duplicates and for
|
|
that I think we would need to stop distinguishing between early and late
|
|
bound lifetimes. So we would need a solution like <a href="https://github.com/rust-lang/rust/pull/103448">Account for
|
|
late-bound lifetimes in generics
|
|
#103448</a> and then also a
|
|
PR similar to <a href="https://github.com/rust-lang/rust/pull/103449">Inherit function lifetimes for impl-trait
|
|
#103449</a>.</p>
|
|
<h3 id="hir-ty-lowering"><a class="header" href="#hir-ty-lowering">HIR ty lowering</a></h3>
|
|
<p>The main change to HIR ty lowering is that we lower <code>hir::TyKind::OpaqueDef</code>
|
|
for an RPITIT to a projection instead of an opaque, using a newly
|
|
synthesized def-id for a new associated type in the trait. We'll
|
|
describe how exactly we get this def-id in the next section.</p>
|
|
<p>This means that any time we call <code>lower_ty</code> on the RPITIT, we end up
|
|
getting a projection back instead of an opaque. This projection can then
|
|
be normalized to the right value -- either the original opaque if we're
|
|
in the trait, or the inferred type of the RPITIT if we're in an impl.</p>
|
|
<h4 id="lowering-to-synthetic-associated-types"><a class="header" href="#lowering-to-synthetic-associated-types">Lowering to synthetic associated types</a></h4>
|
|
<p>Using query feeding, we synthesize new associated types on both the
|
|
trait side and impl side for RPITITs that show up in methods.</p>
|
|
<h5 id="lowering-rpitits-in-traits"><a class="header" href="#lowering-rpitits-in-traits">Lowering RPITITs in traits</a></h5>
|
|
<p>When <code>tcx.associated_item_def_ids(trait_def_id)</code> is called on a trait to
|
|
gather all of the trait's associated types, the query previously just
|
|
returned the def-ids of the HIR items that are children of the trait.
|
|
After <a href="https://github.com/rust-lang/rust/pull/112988">#112988</a>, additionally, for each method in the trait, we add the
|
|
def-ids returned by
|
|
<code>tcx.associated_types_for_impl_traits_in_associated_fn(trait_method_def_id)</code>,
|
|
which walks through each trait method, gathers any RPITITs that show up
|
|
in the signature, and then calls
|
|
<code>associated_type_for_impl_trait_in_trait</code> for each RPITIT, which
|
|
synthesizes a new associated type.</p>
|
|
<h5 id="lowering-rpitits-in-impls"><a class="header" href="#lowering-rpitits-in-impls">Lowering RPITITs in impls</a></h5>
|
|
<p>Similarly, along with the impl's HIR items, for each impl method, we
|
|
additionally add all of the
|
|
<code>associated_types_for_impl_traits_in_associated_fn</code> for the impl method.
|
|
This calls <code>associated_type_for_impl_trait_in_impl</code>, which will
|
|
synthesize an associated type definition for each RPITIT that comes from
|
|
the corresponding trait method.</p>
|
|
<h4 id="synthesizing-new-associated-types"><a class="header" href="#synthesizing-new-associated-types">Synthesizing new associated types</a></h4>
|
|
<p>We use query feeding
|
|
(<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/plumbing/struct.TyCtxtAt.html#method.create_def"><code>TyCtxtAt::create_def</code></a>)
|
|
to synthesize a new def-id for the synthetic GATs for each RPITIT.</p>
|
|
<p>Locally, most of rustc's queries match on the HIR of an item to compute
|
|
their values. Since the RPITIT doesn't really have HIR associated with
|
|
it, or at least not HIR that corresponds to an associated type, we must
|
|
compute many queries eagerly and
|
|
<a href="https://github.com/rust-lang/rust/pull/104940">feed</a> them, like
|
|
<code>opt_def_kind</code>, <code>associated_item</code>, <code>visibility</code>, and<code>defaultness</code>.</p>
|
|
<p>The values for most of these queries is obvious, since the RPITIT
|
|
conceptually inherits most of its information from the parent function
|
|
(e.g. <code>visibility</code>), or because it's trivially knowable because it's an
|
|
associated type (<code>opt_def_kind</code>).</p>
|
|
<p>Some other queries are more involved, or cannot be fed, and we
|
|
document the interesting ones of those below:</p>
|
|
<h5 id="generics_of-for-the-trait"><a class="header" href="#generics_of-for-the-trait"><code>generics_of</code> for the trait</a></h5>
|
|
<p>The GAT for an RPITIT conceptually inherits the same generics as the
|
|
RPIT it comes from. However, instead of having the method as the
|
|
generics' parent, the trait is the parent.</p>
|
|
<p>Currently we get away with taking the RPIT's generics and method
|
|
generics and flattening them both into a new generics list, preserving
|
|
the def-id of each of the parameters. (This may cause issues with
|
|
def-ids having the wrong parents, but in the worst case this will cause
|
|
diagnostics issues. If this ends up being an issue, we can synthesize
|
|
new def-ids for generic params whose parent is the GAT.)</p>
|
|
<details>
|
|
<summary> <b>An illustrated example</b> </summary>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
fn method<'early: 'early, 'late, T>() -> impl Sized + Captures<'early, 'late>;
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Would desugar to...</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
// vvvvvvvvv method's generics
|
|
// vvvvvvvvvvvvvvvvvvvvvvvv opaque's generics
|
|
type Gat<'early, T, 'early_duplicated, 'late>: Sized + Captures<'early_duplicated, 'late>;
|
|
|
|
fn method<'early: 'early, 'late, T>() -> Self::Gat<'early, T, 'early, 'late>;
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
</details>
|
|
<h5 id="generics_of-for-the-impl"><a class="header" href="#generics_of-for-the-impl"><code>generics_of</code> for the impl</a></h5>
|
|
<p>The generics for an impl's GAT are a bit more interesting. They are
|
|
composed of RPITIT's own generics (from the trait definition), appended
|
|
onto the impl's methods generics. This has the same issue as above,
|
|
where the generics for the GAT have parameters whose def-ids have the
|
|
wrong parent, but this should only cause issues in diagnostics.</p>
|
|
<p>We could fix this similarly if we were to synthesize new generics
|
|
def-ids, but this can be done later in a forwards-compatible way,
|
|
perhaps by a interested new contributor.</p>
|
|
<h5 id="opt_rpitit_info"><a class="header" href="#opt_rpitit_info"><code>opt_rpitit_info</code></a></h5>
|
|
<p>Some queries rely on computing information that would result in cycles
|
|
if we were to feed them eagerly, like <code>explicit_predicates_of</code>.
|
|
Therefore we defer to the <code>predicates_of</code> provider to return the right
|
|
value for our RPITIT's GAT. We do this by detecting early on in the
|
|
query if the associated type is synthetic by using
|
|
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.opt_rpitit_info"><code>opt_rpitit_info</code></a>,
|
|
which returns <code>Some</code> if the associated type is synthetic.</p>
|
|
<p>Then, during a query like <code>explicit_predicates_of</code>, we can detect if an
|
|
associated type is synthetic like:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ... {
|
|
if let Some(rpitit_info) = tcx.opt_rpitit_info(def_id) {
|
|
// Do something special for RPITITs...
|
|
return ...;
|
|
}
|
|
|
|
// The regular computation which relies on access to the HIR of `def_id`.
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<h5 id="explicit_predicates_of"><a class="header" href="#explicit_predicates_of"><code>explicit_predicates_of</code></a></h5>
|
|
<p>RPITITs begin by copying the predicates of the method that defined it,
|
|
both on the trait and impl side.</p>
|
|
<p>Additionally, we install "bidirectional outlives" predicates.
|
|
Specifically, we add region-outlives predicates in both directions for
|
|
each captured early-bound lifetime that constrains it to be equal to the
|
|
duplicated early-bound lifetime that results from lowering. This is best
|
|
illustrated in an example:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>trait Foo<'a> {
|
|
fn bar() -> impl Sized + 'a;
|
|
}
|
|
|
|
// Desugars into...
|
|
|
|
trait Foo<'a> {
|
|
type Gat<'a_duplicated>: Sized + 'a
|
|
where
|
|
'a: 'a_duplicated,
|
|
'a_duplicated: 'a;
|
|
//~^ Specifically, we should be able to assume that the
|
|
// duplicated `'a_duplicated` lifetime always stays in
|
|
// sync with the `'a` lifetime.
|
|
|
|
fn bar() -> Self::Gat<'a>;
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<h5 id="assumed_wf_types"><a class="header" href="#assumed_wf_types"><code>assumed_wf_types</code></a></h5>
|
|
<p>The GATs in both the trait and impl inherit the <code>assumed_wf_types</code> of
|
|
the trait method that defines the RPITIT. This is to make sure that the
|
|
following code is well formed when lowered.</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
fn iter<'a, T>(x: &'a [T]) -> impl Iterator<Item = &'a T>;
|
|
}
|
|
|
|
// which is lowered to...
|
|
|
|
trait FooDesugared {
|
|
type Iter<'a, T>: Iterator<Item = &'a T>;
|
|
//~^ assumed wf: `&'a [T]`
|
|
// Without assumed wf types, the GAT would not be well-formed on its own.
|
|
|
|
fn iter<'a, T>(x: &'a [T]) -> Self::Iter<'a, T>;
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Because <code>assumed_wf_types</code> is only defined for local def ids, in order
|
|
to properly implement <code>assumed_wf_types</code> for impls of foreign traits
|
|
with RPITs, we need to encode the assumed wf types of RPITITs in an
|
|
extern query
|
|
<a href="https://github.com/rust-lang/rust/blob/a17c7968b727d8413801961fc4e89869b6ab00d3/compiler/rustc_ty_utils/src/implied_bounds.rs#L14"><code>assumed_wf_types_for_rpitit</code></a>.</p>
|
|
<h3 id="typechecking"><a class="header" href="#typechecking">Typechecking</a></h3>
|
|
<h4 id="the-rpitit-inference-algorithm"><a class="header" href="#the-rpitit-inference-algorithm">The RPITIT inference algorithm</a></h4>
|
|
<p>The RPITIT inference algorithm is implemented in
|
|
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir_analysis/check/compare_impl_item/fn.collect_return_position_impl_trait_in_trait_tys.html"><code>collect_return_position_impl_trait_in_trait_tys</code></a>.</p>
|
|
<p><strong>High-level:</strong> Given a impl method and a trait method, we take the
|
|
trait method and instantiate each RPITIT in the signature with an infer
|
|
var. We then equate this trait method signature with the impl method
|
|
signature, and process all obligations that fall out in order to infer
|
|
the type of all of the RPITITs in the method.</p>
|
|
<p>The method is also responsible for making sure that the hidden types for
|
|
each RPITIT actually satisfy the bounds of the <code>impl Trait</code>, i.e. that
|
|
if we infer <code>impl Trait = Foo</code>, that <code>Foo: Trait</code> holds.</p>
|
|
<details>
|
|
<summary><b>An example...</b></summary>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span>#![feature(return_position_impl_trait_in_trait)]
|
|
|
|
<span class="boring">fn main() {
|
|
</span>use std::ops::Deref;
|
|
|
|
trait Foo {
|
|
fn bar() -> impl Deref<Target = impl Sized>;
|
|
// ^- RPITIT ?0 ^- RPITIT ?1
|
|
}
|
|
|
|
impl Foo for () {
|
|
fn bar() -> Box<String> { Box::new(String::new()) }
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>We end up with the trait signature that looks like <code>fn() -> ?0</code>, and
|
|
nested obligations <code>?0: Deref<Target = ?1></code>, <code>?1: Sized</code>. The impl
|
|
signature is <code>fn() -> Box<String></code>.</p>
|
|
<p>Equating these signatures gives us <code>?0 = Box<String></code>, which then after
|
|
processing the obligation <code>Box<String>: Deref<Target = ?1></code> gives us <code>?1 = String</code>, and the other obligation <code>String: Sized</code> evaluates to true.</p>
|
|
<p>By the end of the algorithm, we end up with a mapping between associated
|
|
type def-ids to concrete types inferred from the signature. We can then
|
|
use this mapping to implement <code>type_of</code> for the synthetic associated
|
|
types in the impl, since this mapping describes the type that should
|
|
come after the <code>=</code> in <code>type Assoc = ...</code> for each RPITIT.</p>
|
|
</details>
|
|
<h5 id="implied-bounds-in-rpitit-hidden-type-inference"><a class="header" href="#implied-bounds-in-rpitit-hidden-type-inference">Implied bounds in RPITIT hidden type inference</a></h5>
|
|
<p>Since <code>collect_return_position_impl_trait_in_trait_tys</code> does fulfillment and
|
|
region resolution, we must provide it <code>assumed_wf_types</code> so that we can prove
|
|
region obligations with the same expected implied bounds as
|
|
<code>compare_method_predicate_entailment</code> does.</p>
|
|
<p>Since the return type of a method is understood to be one of the assumed WF
|
|
types, and we eagerly fold the return type with inference variables to do
|
|
opaque type inference, after opaque type inference, the return type will
|
|
resolve to contain the hidden types of the RPITITs. this would mean that the
|
|
hidden types of the RPITITs would be assumed to be well-formed without having
|
|
independently proven that they are. This resulted in a
|
|
<a href="https://github.com/rust-lang/rust/pull/116072">subtle unsoundness bug</a>. In
|
|
order to prevent this cyclic reasoning, we instead replace the hidden types of
|
|
the RPITITs in the return type of the method with <em>placeholders</em>, which lead
|
|
to no implied well-formedness bounds.</p>
|
|
<h4 id="default-trait-body"><a class="header" href="#default-trait-body">Default trait body</a></h4>
|
|
<p>Type-checking a default trait body, like:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span><span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
fn bar() -> impl Sized {
|
|
1i32
|
|
}
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>requires one interesting hack. We need to install a projection predicate
|
|
into the param-env of <code>Foo::bar</code> allowing us to assume that the RPITIT's
|
|
GAT normalizes to the RPITIT's opaque type. This relies on the
|
|
observation that a trait method and RPITIT's GAT will always be "in
|
|
sync". That is, one will only ever be overridden if the other one is as
|
|
well.</p>
|
|
<p>Compare this to a similar desugaring of the code above, which would fail
|
|
because we cannot rely on this same assumption:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span>#![feature(impl_trait_in_assoc_type)]
|
|
#![feature(associated_type_defaults)]
|
|
|
|
<span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
type RPITIT = impl Sized;
|
|
|
|
fn bar() -> Self::RPITIT {
|
|
01i32
|
|
}
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>Failing because a down-stream impl could theoretically provide an
|
|
implementation for <code>RPITIT</code> without providing an implementation of
|
|
<code>bar</code>:</p>
|
|
<pre><code class="language-text">error[E0308]: mismatched types
|
|
--> src/lib.rs:8:9
|
|
|
|
|
5 | type RPITIT = impl Sized;
|
|
| ------------------------- associated type defaults can't be assumed inside the trait defining them
|
|
6 |
|
|
7 | fn bar() -> Self::RPITIT {
|
|
| ------------ expected `<Self as Foo>::RPITIT` because of return type
|
|
8 | 01i32
|
|
| ^^^^^ expected associated type, found `i32`
|
|
|
|
|
= note: expected associated type `<Self as Foo>::RPITIT`
|
|
found type `i32`
|
|
</code></pre>
|
|
<h4 id="well-formedness-checking"><a class="header" href="#well-formedness-checking">Well-formedness checking</a></h4>
|
|
<p>We check well-formedness of RPITITs just like regular associated types.</p>
|
|
<p>Since we added lifetime bounds in <code>predicates_of</code> that link the
|
|
duplicated early-bound lifetimes to their original lifetimes, and we
|
|
implemented <code>assumed_wf_types</code> which inherits the WF types of the method
|
|
from which the RPITIT originates (<a href="https://github.com/rust-lang/rust/pull/113704">#113704</a>), we have no issues
|
|
WF-checking the GAT as if it were a regular GAT.</p>
|
|
<h3 id="whats-broken-whats-weird-etc"><a class="header" href="#whats-broken-whats-weird-etc">What's broken, what's weird, etc.</a></h3>
|
|
<h5 id="specialization-is-super-busted"><a class="header" href="#specialization-is-super-busted">Specialization is super busted</a></h5>
|
|
<p>The "default trait methods" described above does not interact well with
|
|
specialization, because we only install those projection bounds in trait
|
|
default methods, and not in impl methods. Given that specialization is
|
|
already pretty busted, I won't go into detail, but it's currently a bug
|
|
tracked in:
|
|
* <code>tests/ui/impl-trait/in-trait/specialization-broken.rs</code></p>
|
|
<h5 id="projections-dont-have-variances"><a class="header" href="#projections-dont-have-variances">Projections don't have variances</a></h5>
|
|
<p>This code fails because projections don't have variances:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span>#![feature(return_position_impl_trait_in_trait)]
|
|
|
|
<span class="boring">fn main() {
|
|
</span>trait Foo {
|
|
// Note that the RPITIT below does *not* capture `'lt`.
|
|
fn bar<'lt: 'lt>() -> impl Eq;
|
|
}
|
|
|
|
fn test<'a, 'b, T: Foo>() -> bool {
|
|
<T as Foo>::bar::<'a>() == <T as Foo>::bar::<'b>()
|
|
//~^ ERROR
|
|
// (requires that `'a == 'b`)
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>This is because we can't relate <code><T as Foo>::Rpitit<'a></code> and <code><T as Foo>::Rpitit<'b></code>, even if they don't capture their lifetime. If we were
|
|
using regular opaque types, this would work, because they would be
|
|
bivariant in that lifetime parameter:</p>
|
|
<pre><pre class="playground"><code class="language-rust"><span class="boring">#![allow(unused)]
|
|
</span>#![feature(return_position_impl_trait_in_trait)]
|
|
|
|
<span class="boring">fn main() {
|
|
</span>fn bar<'lt: 'lt>() -> impl Eq {
|
|
()
|
|
}
|
|
|
|
fn test<'a, 'b>() -> bool {
|
|
bar::<'a>() == bar::<'b>()
|
|
}
|
|
<span class="boring">}</span></code></pre></pre>
|
|
<p>This is probably okay though, since RPITITs will likely have their
|
|
captures behavior changed to capture all in-scope lifetimes anyways.
|
|
This could also be relaxed later in a forwards-compatible way if we were
|
|
to consider variances of RPITITs when relating projections.</p>
|
|
<hr>
|
|
<ol class="footnote-definition"><li id="footnote-1">
|
|
<p>This is compiler-errors terminology, I'm not claiming it's accurate :^) <a href="#fr-1-1">↩</a></p>
|
|
</li>
|
|
</ol>
|
|
</main>
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
<!-- Mobile navigation buttons -->
|
|
<a rel="prev" href="opaque-types-impl-trait-inference.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/opaque-types-region-inference-restrictions.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="opaque-types-impl-trait-inference.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/opaque-types-region-inference-restrictions.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>
|