owned this note
owned this note
Published
Linked with GitHub
---
title: Minimal TAIT stabilization proposal - supplemental materials
tags: impl-trait-everywhere, design-document
date: 2023-11-05
url: https://hackmd.io/cuq02C6JSl-nIS_0YeK-Ew
---
# Supplemental: Signature restriction forward compatibility matrix - source
Source:
```
flowchart TB
subgraph DefinesOnly["Defines Only"]
O8["Option 8\n\nDefines in defining scope"]
O9["Option 9\n\nDefines in defining scope\n+ defines outside of defining scope"]
end
subgraph BothRequired["Both Required"]
O6["Option 6\n\nSignature restriction\n+ mandatory defines in defining scope"]
O7["Option 7\n\nSignature restriction\n+ mandatory defines in defining scope\n+ defines outside of defining scope"]
end
subgraph SignatureRestriction["Signature Restriction"]
subgraph DefinesOutside["Defines Outside of Defining Scope"]
O1["Option 1\n\nSignature restriction\n+ defines outside of defining scope"]
O2["Option 2\n\nSignature restriction\n+ defines outside of defining scope\n+ optional defines in defining scope"]
O3["Option 3\n\nSignature restriction\n+ defines outside of defining scope\n+ optional defines in defining scope\n+ lint for absent defines"]
style O3 stroke:#fff000
end
O0["Option 0\n\nSignature restriction"]
O4["Option 4\n\nSignature restriction\n+ optional defines in defining scope"]
O5["Option 5\n\nSignature restriction\n+ optional defines in defining scope\n+ lint for absent defines"]
style O5 stroke:#fff000
end
O0 --> O1
O0 --> O2
O0 --> O3
O0 --> O4
O0 --> O5
O1 --> O2
O1 --> O3
O2 <--> O3
O4 --> O2
O4 --> O3
O4 <--> O5
O5 --> O2
O5 --> O3
O6 --> O0
O6 --> O1
O6 --> O2
O6 --> O3
O6 --> O4
O6 --> O5
O6 --> O7
O6 --> O8
O6 --> O9
O7 --> O1
O7 --> O2
O7 --> O3
O7 --> O9
O8 --> O9
```
# Supplemental: Mini-TAIT forward compatibility matrix - source
Source:
```
flowchart LR
S0["Mini-TAIT\n\nrestriction: once modulo regions\nrestriction: mention must define\nrestriction: define only within defining scope"]
S1["Mini-TAIT\n\nrestriction: once modulo regions\nallow: non-defining uses in defining scope\nrestriction: defining only within defining scope"]
S2["Mini-TAIT\n\nrestriction: once modulo regions\nrestriction: mention must define\nallow: defining outside of defining scope with defines syntax"]
S3["Maxi-TAIT\n\nrestriction: once modulo regions\nallow: non-defining uses in defining scope\nallow: defining outside of defining scope with defines syntax"]
style S0 text-align:left
style S1 text-align:left
style S2 text-align:left
style S3 text-align:left
S0 --> S1
S0 --> S2
S1 --> S3
S2 --> S3
```
# Supplemental: Discussion on IDE concerns
[This section contains curated discussion on the IDE concerns that led to the signature restriction and to the restriction on items nested inside function bodies.]
**matklad** [said on 2019-10-19](https://github.com/rust-lang/rust/issues/65516#issuecomment-544139022):
> > unfortunate for incremental
>
> It’s bad for laziness, not for incremental.
>
> The property we want here for IDE is:
>
> Function body can be type-checked without looking at other functions’ bodies
>
> That is, it’s not the nesting itself that creates troubles, but the fact that nested items are sometimes observable from the outside. Here’s the list of things that cause trouble that I know of:
>
> * local inherent impls
> * local impls of global traits for global types
> * local macro_export ed macros
>
> The firsts two can be fixed by treating item bodies as downstream crates from coherence pov.
>
> Additionally, another trouble some feature for ide is
>
> * local mod declarations (mod foo;, with semicolon)
>
> IDE gets requests like “what’s the definition of thing at offset 92 in file foo.rs”. The first thing IDE needs to do is to figure out, from the set of known crate roots, how foo.rs fits into the module system. At the moment, to do this precisely one has to look into the body of every function, precisely because a mod declaration might be hidden there. Note that we currently require an explicit path attribute for local module, but that doesn’t really help, as you need still need to parse everything to find out there isn’t a module with an attr.
**matklad** [said on 2019-10-19](https://github.com/rust-lang/rust/issues/65516#issuecomment-544139452):
> Note also that we would still need to parse _some_ function bodies due to autotrait leakage via impl trait, but only if you directly use that function. This is different from having to parse all function bodies because _somewhere_ and odd impl might be hidden, which affects type inference.
**matklad** [said on 2020-04-16](https://github.com/rust-lang/rust/issues/65516#issuecomment-614877487):
> Given that there are already four comments saying "we should allow impls for types that are themselves nested", I feel it would be useful to repeat:
>
> 1. Obviously yes
> 2. The precise rule we want is "function body acts as a downstream crate from coherence point of view for trait impls; for inherent impls, only private visibility (scoped to block/function) is allowed".
>
> EDIT: I _think_ I first heard the coherence formulation from @vlad20012.
**pnkfelix** [said on 2021-03-09](https://github.com/rust-lang/rust/issues/65516#issuecomment-794211337):
> We discussed this issue at a [recent lang design team meeting](https://youtu.be/Yu23X4bsK1A?t=2683) ([agenda item](https://hackmd.io/py1Ef8uERfe6Uj6Eluglsg?view#%E2%80%9CEdition-vNext-Consider-deprecating-weird-nesting-of-items%E2%80%9D-rust65516)).
>
> Here is my attempt to summarize the discussion as posted on this issue, as well as convey concerns raised during the lang team meeting itself.
>
> * The original proposal in the issue description makes it sound like the goal would be to remove the nesting of these items entirely.
> * There are cases where supporting nesting is valuable. As an example, it is used right now as the basis for generating doc tests in `rustdoc`, where you need in some cases to have item declarations sitting alongside Rust expressions, and that is something that you can only do within an `fn body`.
> * Subsequent comments here have pointed out that completely removing support for nesting is not necessarily the right goal. For example, matklad [points out](https://github.com/rust-lang/rust/issues/65516#issuecomment-614877487) that just restricting the _visibility_ of such nested items could have a lot of benefit for lazily-evaluating fragments of Rust programs (which is useful for IDEs).
>
> The basic consensus of the lang design team was that we are not prepared to address this for the 2021 edition.
>
> We might be willing to consider a soft-deprecation of the feature in the future, that would not need to be tied to an edition, but that can be a separate proposal (that would presumably be written in a manner that takes the dialogue here into account).
>
> (We didn't actually directly address the more narrow proposal put forth by matklad in the lang team meeting. But I think the same principles hold there: We are not prepared to address that for the 2021 edition. We would be willing to consider strategies for revising/restricting the semantics that are not tied to an edition.)
>
> So, the lang design team recommends that this issue be closed.
>
> @rfcbot fcp close
**Waffle Lapkin** [said on 2023-01-14](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/TAITs.20and.20lazy.20parsing/near/321333336):
> One option to speed up compilers and IDEs is to not parse function bodies, as most of the analysis can be done solely with the signature.
>
> Currently this can't applied to Rust because function bodies can have trait implementations and you really want to know all trait impls. However there is a plan to disallow such impls in the next edition (https://github.com/rust-lang/rust/issues/65516), so it would be nice to not close the doors to lazy parsing completely.
>
> Currently, with TAIT you get another reason to analyse all the bodies — they can contain defining uses:
> ```rust
> #![feature(type_alias_impl_trait)]
> type Alias = impl Sized;
> fn def() {
> let _: Alias = ();
> }
> ```
> It would be nice to restrict this somehow, so IDEs can be faster.
>
> I've discussed it for a bit with a intelij-rust plugin developer and we've come up with this restriction:
> > TAITs that are not mentioned in the signature and that are defined outside of a body can't be defined in this body
>
> Which means the example above is not allowed, but these are allowed:
> ```rust
> #![feature(type_alias_impl_trait)]
> fn def() {
> type Alias = impl Sized;
> let _: Alias = ();
> }
> ```
> ```
> #![feature(type_alias_impl_trait)]
> type Alias = impl Sized;
> fn def() -> Alias {
> ()
> }
> ```
> This won't hurt most of the TAIT use-cases I have in mind (naming futures and closures, mostly), but maybe I'm missing something?
>
> It's not really clear how much of a problem status quo TAITs are for IDEs, but if it won't limit them too much we can stabilize them with the restriction and lift it later, if needed.
>
> cc @**Lukas Wirth** you may have opinions wrt to rust analyzer
>
> (note that as with trait implementations you can't simply search the body string for tait names, since the identifier can come from a macro)
**jackh726** [said on 2023-01-17](https://github.com/rust-lang/rust/issues/63063#issuecomment-1385946789):
> @rfcbot concern function-defining-uses
>
> One concern brought up on [zulip](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/TAITs.20and.20lazy.20parsing/near/321333336) is that any function in the defining scope can have a defining use for an opaque type. This means that to find the hidden type of a TAIT, we must type check every function body.
>
> As proposed on zulip, an alternative approach would be that one functions with the TAIT in the function signature can have defining uses. I think we should consider this. Probably the easiest way would be to split the feature gate and do a crater run to check for breakages. Importantly, it's a relatively simple change to make _now_, but once stabilized will be impossible.
**afetisov** [said on 2023-01-20](https://github.com/rust-lang/rfcs/pull/3373#issuecomment-1398343422):
> > The IDEs are not going to want to handle this case, and allowing it will usually lead a poor IDE experience and user complaints.
>
> As simple test shows that Intellij-Rust already handles this pattern. If RA folks don't want to handle it, that's their own decision. Personally I don't believe that it's as big deal as people say in comments: skipping entire _modules_ is a major win for IDE performance, but with the way impls are designed it's impossible. So you still need to parse all modules and expand all macros if you want to find all possible impls. Once you do it, parsing the bodies of functions and adding external impls to the index is a really minor difference.
>
> There could possibly be IDE performance wins if _all_ items inside of function bodies were forbidden, or perhaps at least if types and impls could not be declared inside of function bodies. But simply forbidding impls of outer types for outer traits has pretty minor wins: you still need to parse all bodies, all impls inside them, _and_ you need to check whether that impl should be marked as error, _and_ there would be some complicated rules, similar to orphan rules, about which exact combinations of types and traits is forbidden. What are the conditions where
>
> ```
> impl<T: Trait> Frob<Foo, Item=Bar> for Baz<T, Quux, Elt=Pooh, const N: Moo>
> ```
>
> is forbidden? It looks like this rule adds more complexity than takes away.
**matklad** [said on 2023-01-27](https://github.com/rust-lang/rfcs/pull/3373#issuecomment-1406888432):
> I'd phrase this in a goal-oriented, rather solution oriented, way. The goal here is, roughly, to make it sound to not look at the function body during analysis.
>
> More specifically, when type-checking the body of function `foo`, there shouldn't be a need to look at any other body.
>
> The motivation for this goal is that 90% of input bytes for a compiler/IDE live in function bodies, and, with this restriction in place, we can more easily make the analysis lazy (yay IDE support!) and parallel (yay compile times!).
>
> When phrased this way, we can enumerate intentional and unintentional cases of information leakage. In particular, the RFC should articulate a single case where such leakage is very much intentional: auto-traits leak through impl trait:
>
> ```rust
> fn gimme_send() -> impl Default { 92 }
> ```
>
> Allowing this case _doesn't_ kill the idea, because:
>
> * the fraction of RPIT publicly reachable functions is low
> * it says on the function signature that you should look into its body (corollary: making this a warn-by-default lint would be a form of bertrand paradox: if a function with problematic nesting has `#[allow(problematic_nexting)]` attribute, the nesting isn't problematic because you know it is there!)
> * as a case study, Kotlin's new compiler explicitly distinguishes between functions with specified and inferred types (https://www.youtube.com/watch?v=db19VFLZqJM)
>
> What this RFC is about, "traits in items", is definitely unintenional. I love vlad20012's brilliant idea of using coherence to describe exactly the problematic cases. It's important to note that this is not so much about _items_, as about expresions, regardless of their position. In particular, you can sneak in an impl anywhere a type is allowed:
>
> ```rust
> struct Foo;
>
> trait T<C: PartialEq< [(); { impl Foo { fn quux(&self) {} } 0}] >> {}
>
> fn main() { Foo.quux() }
> ```
>
> Another clearly wrong case is (mentioned by @petrochenkov) `#[macro_export] macro_rules`.
>
> A case which is _probably_ wrong is
>
> ```rust
> fn foo() {
> #[path = "/my/file.rs"]
> mod foo;
> }
> ```
>
> If we do coherence thing, this'll actually be OK for parallel compilation. However, for IDEs there will be a bad case of providing completion inside `/my/file.rs`, as IDE won't know where in the module hierarchy that file is mounted without looking inside `foo`.
>
> A case which is questionable are inner attributes:
>
> ```rust
> fn foo() {
> //! Sneaky docs!
> }
> ```
>
> Attributes generally affect the externally-observable meaning of the fucntion (eg, must use, cfg), so it'd be nice to have them in the signature. It's not a deal breaker, as you can parse the prefix of the body, but, if we want to improve the situation here, we might as well fix that as well, so that a simpler strategy of just matching `{}` would work.
>
> A case I am unsure about: do `async` fn leak stuff? It seems they do: the following program may or may not type check depending on the body of `f` (not sure if there's non-transmute way to lift size of runtime value into a const context):
>
> ```rust
> async fn f() {}
>
> fn main() {
> unsafe { std::mem::transmute::<_, [u8; 1]>(f()); }
> }
> ```
>
> I _think_ this might be ok, cause async fn is just impl trait, including the fact that it says "look at the body!" on the tin. OTOH, I'd expect to see much more `async fn`s than RPITs, and needing to look into async fn bodies for type-checking would suck. But we already need to know static call graph for async fucntions to detect (indirect) recusion, so maybe its a lost cause already? Overall, feels like an unresolved question.
>
> I guess my overall thrust here is that, if we do this, its important to do 100% of it. We don't want to forbid this because it's an unreadable mess: no need to forbid something which isn't used anywhere outside of dtolnay's famous quiz. We want to forbid this, because we want to make the implementation faster. In some future edition, we want to just through every function onto a threadpool and typecheck the world on all cores, without any synchronization. If there's at least one case where we need to invalidate typechcking results of `foo` when we learned something new when checking `bar`, this optimization simply doesn't work.
**vlad20012** [said on 2023-02-04](https://github.com/rust-lang/rfcs/pull/3373#issuecomment-1416705036):
> > The implications for TAIT should be discussed
>
> It should be addressed by [#107073](https://github.com/rust-lang/rust/pull/107073). I've asked a question about nested functions [there](https://github.com/rust-lang/rust/pull/107073#issuecomment-1416704599).
**vlad20012** [said on 2023-02-04](https://github.com/rust-lang/rust/pull/107073#issuecomment-1416704599):
> Does this PR forbid register hidden type for a TAIT via a nested function return type while the TAIT is not mentioned in the root function signature?
>
> ```rust
> #![feature(type_alias_impl_trait)]
> type Alias = impl Default;
>
> pub fn foo() {
> #[derive(Default)] struct Foo;
> fn _f() -> Alias { Foo }
> }
>
> pub fn bar() -> Alias {
> Alias::default()
> }
> ```
**oli** [said on 2023-02-04](https://github.com/rust-lang/rust/pull/107073#issuecomment-1416720979):
> > Does this PR forbid register hidden type for a TAIT via a nested function return type while the TAIT is not mentioned in the root function signature?
>
> no, that will compile
**vlad20012** [said on 2023-02-04](https://github.com/rust-lang/rust/pull/107073#issuecomment-1416732902):
> That makes this PR kinda moot in regards to lazy parsing, since you still have to look into all bodies to check for defining uses due to local items.
**cramertj** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513665666):
> @traviscross Those are all great questions! This conversation has been long, and we've lost track of all the various requirements that led to the current design. Neither [RFC 2071](https://rust-lang.github.io/rfcs/2071-impl-trait-existential-types.html) nor [RFC 2515](https://github.com/rust-lang/rfcs/blob/master/text/2515-type_alias_impl_trait.md) carefully considered the IDE-related tradeoffs involved in explicitly or implicitly specifying defining usage sites. There was discussion of whether definitions should be specifically annotated, but this was rejected as unnecessary. Stabilizing the `#[defines(...)]`-based implementation would require an RFC to reexamine these tradeoffs.
>
> It seems like the defining usage annotation conversation has all spun out of [this zulip thread about IDE checking](https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/TAITs.20and.20lazy.20parsing/near/321333336) and [this resulting comment](https://github.com/rust-lang/rust/issues/63063#issuecomment-1385946789). However, after some discussion, @rust-lang/lang is not sure we understand the IDE concern.
>
> The only diagnostics we can come up with which would require parsing all TAIT-module function bodies are (1) auto trait-related errors or (2) mismatched concrete types due to multiple conflicting definitions. However, these errors can be issued lazily (as was done in rustc itself) by recording that a function needed a particular TAIT to be `Send`/`Sync`/a concrete type etc. These particular errors can be delayed until all bodies in the module have been checked, and the defining usage sites have been found.
>
> Other diagnostics, including autocomplete, should not require checking multiple bodies. Functions with defining uses don't have to search for the definition in other bodies (since they already know the definition), and functions without defining uses cannot use methods or other behaviors of the concrete type:
>
> ```rust
> struct Concrete;
>
> impl Concrete {
> fn concrete_method(self) {}
> }
>
> type Tait = impl Sized;
>
> fn define_tait() -> Tait {
> Concrete
> }
>
> fn use_tait_concrete(tait: Tait) {
> // tait.concrete_method(); // Does not compile
> let concrete: Concrete = tait; // Also a defining use
> concrete.concrete_method(); // Allowed
> }
>
> fn use_tait_abstract(tait: Tait) {
> // tait.concrete_method; // Does not compile
> }
> ```
>
> Can anyone (preferably an IDE maintainer) comment on whether our understanding is correct? If so, we're inclined to proceed without the `#[defines(...)]` attribute, as delayed errors for auto traits and concrete type mismatches don't seem like sufficient motivation to introduce the extra `#[defines(...)]` machinery.
**matklad** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513755277):
> From IDE POV, two questions are important:
>
> * does it need to know concrete type of TAIT to do completion?
> * if it does need to know concrete type of TAIT for whatever reason, can it compute it faster than compiling the whole crate?
>
> For the first question, the following is one litmus test:
>
> ```rust
> type Tait = impl Sized;
> fn define_tait() -> Tait { ... }
>
> trait IsSend { fn is_send(&self) {} }
> impl<T: Send> IsSend for T {}
>
> fn main() {
> let t = define_trait();
> t.is_send() // <- can this compile?
> }
> ```
>
> Here, the `Send` bound is not mentioned in TAIT. If we guarantee that the code does not compile, than the IDE is happy, because it doesn't need to be concerned with concrete type. If the auto-trait can leak without being explicitly mentioned (like RPIT works today), than that's sad, as IDE would have to learn the concrete type. How sad is that depends on how hard it would be to learn the hidden type.
>
> The second litmus test for the question would be something like this:
>
> ```rust
> const fn define_tait() -> Tait { ... }
> const S: usize = std::mem::size_of_val(&define_tait());
> ```
>
> If we can leak the sizeof tait into a `const`, we can then plug that const into some const-generic, which then can affect trait solving and completion.
>
> If nothing similar to the above is possible (that is, `type Tait = impl Sized;` tells us _everything_ that can affect type inference), taits are very friendly for IDEs. More friendly than RPITs even.
>
> If something similar to the above is possible, than the second question becomes relevant.
>
> If it is possible to tell, just looking at the signature of a function, whether it can define TAIT, than TAIT = RPIT in terms of problem for IDE.
>
> However, if something like
>
> ```rust
> fn define_tait_sneakily() {
> let _: Trait = ();
> }
> ```
>
> than that's significantly _worse_ than RPIT. That means that in the _common_ case IDE should attempt to type-check the whole crate, which is specifically the thing IDE is built to avoid.
>
> Whether the syntactic marker is an explicit marker like `defines` or an implicit marker like "has `Trait` somewhere in the signature" doesn't really matter.
**cramertj** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513768518):
> @matklad It seems like your example is focused around detecting auto traits, which I called out in my comment. My expectation was that IDEs could naiively assume they are implemented, not erroring until the defining use has been checked, at which point a more specific error could be generated. Thanks for pointing out that this does mean we would display methods that would not necessarily be callable.
**matklad** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513769794):
> The problem is not erroring, the problem is
>
> ```
> define_trait().<what completions are even available here?>
> ```
**cramertj** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513770623):
> Right, I'm saying I was assuming we would show all possible completions (assume that auto traits are present and so the dependent traits are implemented).
**matklad** [said on 2023-04-18](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513777528):
> I don't think that'd work correctly, one motivating example would be
>
> ```rust
> struct X<T>(T);
>
> impl<T: Send> X<T> {
> fn foo(&self) -> i32 { todo!() }
> }
>
> struct Y;
> impl Y {
> fn foo(&self) -> f32 { todo!() }
> }
>
> impl<T> std::ops::Deref for X<T> {
> type Target = Y;
> fn deref(&self) -> &Y { todo!() }
> }
>
> fn main() {
> let x = X(define_tait());
> let t = x.foo();
> }
> ```
>
> Depending on whether tait is Sync, the type of `t` might be comptletely different. autotraits can really affect type inference in arbitrary ways, you can't just assume that they hold.
**TC** [said on 2023-04-19](https://github.com/rust-lang/rust/issues/107645#issuecomment-1514129139):
> @matklad: thanks for providing a motivating example for analysis. However, it seems that we display methods _today_ that are not necessarily callable. Adapting [your example](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513777528), on stable Rust we can write:
>
> ```rust
> struct X<T>(T);
> impl<T: Send> X<T> {
> fn foo(&self) -> i32 { todo!() }
> }
>
> // This return type is `!Send`.
> fn returns_rpit() -> impl Sized { std::rc::Rc::new(()) }
>
> fn main() {
> let x = X(returns_rpit()); // `x` does not have a `foo()` method.
> let t = x.foo(); // RA suggests the `foo` method and infers `i32` here.
> // Error: ^^^ method cannot be called on `X<impl Sized>` due to unsatisfied trait bounds
> }
> ```
>
> Even though the returned type is `!Send`, `rust-analyzer` (at tag `2023-04-17`) suggests the `foo()` method and infers its type. If we add the `struct Y` and the `Deref` impl for `X` from [your example](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513777528), the method's return type is still incorrectly inferred as `i32`.
>
> For this example, TAIT does not appear to add any complexity that is not already present with RPIT.
>
> Is this analysis missing any subtlety that you had in mind in terms of distinguishing TAIT from RPIT?
**matklad** [said on 2023-04-19](https://github.com/rust-lang/rust/issues/107645#issuecomment-1514272341):
> As of today rust-analyzer in general is pretty buggy. Even using a concrete !Send type, like `Rc`, leads to wrong type inference for `t`. We do want to be able to fix that some day!
>
> > For this example, TAIT does not appear to add any complexity that is not already present with RPIT.
>
> To re-phrase [#107645 (comment)](https://github.com/rust-lang/rust/issues/107645#issuecomment-1513755277), there are two aspects:
>
> 1. When do you need to know concrete type?
> 2. How hard is it to know concrete type / what constraints lazy analysis?
>
> For 1, RPIT and TAIT are equivalent (under the assumption that TAIT leaks autotraits in a similar manner)
>
> For 2, the answer differs depending on whether there is some implicit or explicit syntactic marker in the signature, which tells if a function can defini TAIT.
>
> * there is marker -- TAIT=RPIT in terms of extra work for IDE
> * there is no marker -- TAIT is significantly worse than RPIT (roughly, O(1) vs O(N))
**TC** [said on 2023-04-19](https://github.com/rust-lang/rust/issues/107645#issuecomment-1515429437):
> ...This is essentially an instance of [stable autoderef-based specialization](http://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html).
**TC** [said on 2023-06-23](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Nested.20inner.20function.20restriction.20in.20TAIT/near/369036029):
> We're updating the stabilization proposal for type alias `impl Trait`, and we want to be sure that we've correctly captured the needs of rust-analyzer on a particular point related to a restriction on nested inner function bodies. Consider these two examples:
>
> Example A:
>
> ```rust
> trait Trait {}
> struct S;
> fn foo() {
> impl Trait for S {}
> }
> ```
>
> Example B:
>
> ```rust
> trait Trait {}
> struct S;
> type Foo = impl Sized;
> fn foo() {
> fn bar() -> Foo { S }
> }
> ```
>
> In both cases, the function body of `foo` does something that affects the broader scope. We could say it does this "sneakily" because it does so without mentioning anything about what it plans to affect in its signature.
>
> Is it correct to say that:
>
> - RA would prefer that neither of these examples were legal (as captured in this [draft proposal](https://github.com/rust-lang/rfcs/pull/3373)).
> - Both of these examples present the same basic problem for RA.
> - As long as either of these examples (or any of the other similar cases) are legal, RA must parse all function bodies to correctly infer all types.
>
> @**matklad** wrote a good comment about this [here](https://github.com/rust-lang/rfcs/pull/3373#issuecomment-1406888432). Apropos of the points that we're double checking above, he said:
>
> > I guess my overall thrust here is that, if we do this, its important to do 100% of it. We don't want to forbid this because it's an unreadable mess: no need to forbid something which isn't used anywhere outside of dtolnay's famous quiz. We want to forbid this, because we want to make the implementation faster. In some future edition, we want to just [throw] every function onto a threadpool and typecheck the world on all cores, without any synchronization. If there's at least one case where we need to invalidate typechcking results of `foo` when we learned something new when checking `bar`, this optimization simply doesn't work.
**matklad** [said on 2023-06-24](https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer/topic/Nested.20inner.20function.20restriction.20in.20TAIT/near/369091435):
> Good catch! Yes, this is the same problem!