Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic type alias fails to resolve fields #15943

Closed
cart opened this issue Nov 21, 2023 · 9 comments · Fixed by #15970 or #16152
Closed

Generic type alias fails to resolve fields #15943

cart opened this issue Nov 21, 2023 · 9 comments · Fixed by #15970 or #16152
Labels
A-ty type system / type inference / traits / method resolution C-bug Category: bug

Comments

@cart
Copy link

cart commented Nov 21, 2023

rust-analyzer version: 0.3.1740-standalone
rustc version: 1.73.0

type Wrap<T> = T;

enum X {
    A { cool: u32, stuff: u32 },
    B,
}

fn main() {
    let wrapped = Wrap::<X>::A {
        // autocomplete for fields breaks here
        cool: 100,
        stuff: 100,
    };

    // autocomplete for fields breaks here
    // RA fails to resolve `cool`'s type (lists as unknown)
    if let Wrap::<X>::A { cool, .. } = &wrapped {}
}

And a screenshot of the failed type hint:
image

Context

I'm building a DSL for Bevy Scenes with the goal of supporting as many RA niceties as possible (ex: autocomplete, go to definition, etc). I created a discussion here a few months ago and got very helpful advice, which has generally worked!

However when building support for enums in my DSL, I encountered a limitation in the Rust type system that required the use of this type alias wrapper workaround (or an experimental feature ... which isn't an option for us).

The actual proc_macro code in my impl looks like this:

 if let Wrap::<<#path as Schematic>::Props>::#variant { #(#fields,)* .. } = &mut props {
 }

If I replace Wrap::<<#path as Schematic>::Props> with the "actual" type, RA field autocomplete does work for the enum in my DSL. Sadly, I cannot name the type in the macro (as it is defined outside of the macro), so I need to rely on the type system to resolve this.

Given that RA fails to resolve the fields in the simplified if let Wrap::<X>::A { cool, .. } = &wrapped {}, I'm assuming that if RA can handle autocomplete for this case, it can probably handle autocompletion in the Bevy DSL proc_macro as well.

@cart cart added the C-bug Category: bug label Nov 21, 2023
@cart
Copy link
Author

cart commented Nov 21, 2023

This is the "actual Bevy" case I would like to resolve. I'm guessing this is resolved by fixing the simpler case, but I'll include it here for completeness.

type Wrap<T> = T;

enum X {
    A { cool: u32, stuff: u32 },
    B,
}

enum XProps {
    A { cool: u32, stuff: u32 },
    B,
}

trait Schematic {
    type Props;
}

impl Schematic for X {
    type Props = XProps;
}

fn main() {
    let wrapped = Wrap::<<X as Schematic>::Props>::A {
        // autocomplete breaks here
        cool: 100,
        stuff: 100,
    };

    // RA resolves `cool` to unknown
    if let Wrap::<<X as Schematic>::Props>::A { cool, .. } = &wrapped {}
}

@Veykril Veykril added the A-ty type system / type inference / traits / method resolution label Nov 21, 2023
@Veykril
Copy link
Member

Veykril commented Nov 21, 2023

Relevant part for this should be here I think

TypeNs::TypeAliasId(it) => {
let container = it.lookup(self.db.upcast()).container;
let parent_subst = match container {
ItemContainerId::TraitId(id) => {
let subst = TyBuilder::subst_for_def(self.db, id, None)
.fill_with_inference_vars(&mut self.table)
.build();
Some(subst)
}
// Type aliases do not exist in impls.
_ => None,
};
let ty = TyBuilder::def_ty(self.db, it.into(), parent_subst)
.fill_with_inference_vars(&mut self.table)
.build();
self.resolve_variant_on_alias(ty, unresolved, mod_path)
}

Unsure why this fails though, since resolve_variant_on_alias does at least handle enums

Some(1) => {
let segment = path.segments().last().unwrap();
// this could be an enum variant or associated type
if let Some((AdtId::EnumId(enum_id), _)) = ty.as_adt() {
let enum_data = self.db.enum_data(enum_id);
if let Some(local_id) = enum_data.variant(segment) {
let variant = EnumVariantId { parent: enum_id, local_id };
return (ty, Some(variant.into()));
}
}
// FIXME potentially resolve assoc type
(self.err_ty(), None)
}

@cart
Copy link
Author

cart commented Nov 29, 2023

I can confirm the first case now works on nightly. The second case (which is unfortunately my Bevy experiment use case) doesn't appear to be working.

@Veykril Veykril reopened this Nov 29, 2023
@Veykril
Copy link
Member

Veykril commented Nov 29, 2023

Can you extract what the proc-macro generates (by looking at the expansion or similar) somehow? It's difficult to figure out a concrete failing case here otherwise. We most likely fail to resolve <#path as Schematic>::Props

@Veykril
Copy link
Member

Veykril commented Nov 29, 2023

type Wrap<T> = T;

struct S;

trait Schematic {
    type Props;
}

impl Schematic for S {
    type Props = X;
}

enum X {
    A { cool: u32, stuff: u32 },
    B,
}

fn main() {
    let wrapped = Wrap::<<S as Schematic>::Props>::A {
        cool: 100,
        stuff: 100,
    };

    if let Wrap::<<S as Schematic>::Props>::A { cool, ..} = &wrapped {}
                                              //^^^^ &u32
}

fails to resolve when it should

@Veykril
Copy link
Member

Veykril commented Nov 29, 2023

I'm not sure whats going on but our substitutions fall back to errors once we put them in the projection here (projection in question being <S as Schematic>::Props)

let substitution = Substitution::from_iter(
Interner,
substitution
.iter(Interner)
.take(len_self)
.chain(trait_ref.substitution.iter(Interner)),
);
TyKind::Alias(AliasTy::Projection(ProjectionTy {
associated_ty_id: to_assoc_type_id(associated_ty),
substitution,
}))
.intern(Interner)

Debug printing the substitution on line 513 yields

[crates\hir-ty\src\lower.rs:520] &substitution = [?0 := AdtId(StructId(StructId(0)))<[]>]

whereas debug printing the projection right after yields

[crates\hir-ty\src\lower.rs:521] TyKind::Alias(AliasTy::Projection(ProjectionTy {
                associated_ty_id: to_assoc_type_id(associated_ty),
                substitution,
            })).intern(Interner) = AliasTy(?)

do you have any idea what could be going on there from a quick glance @flodiebold? Do we need to commit something somehow somewhere?

@flodiebold
Copy link
Member

If you mean the ? in AliasTy(?), that's not an error type, it's simply the debug printing not being able to resolve the interned projection type because it doesn't have access to the database. The Chalk printing infrastructure needs the db in TLS to fully work, which is done by wrapping code in hir_ty::tls::set_current_program, but that's mostly not done.

@cart
Copy link
Author

cart commented Dec 2, 2023

Can you extract what the proc-macro generates (by looking at the expansion or similar) somehow? It's difficult to figure out a concrete failing case here otherwise. We most likely fail to resolve <#path as Schematic>::Props

Thats what this chunk of code is. It looks like you either found it or reverse engineered the same thing though!

@Austaras
Copy link
Contributor

Hmm. Let me try again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-ty type system / type inference / traits / method resolution C-bug Category: bug
Projects
None yet
4 participants