Impl Trait Everywhere Initiative
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Write
        • Owners
        • Signed-in users
        • Everyone
        Owners Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Help
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Write
Owners
  • Owners
  • Signed-in users
  • Everyone
Owners Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    --- 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!

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully