| <!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> |