Unnamed repository; edit this file 'description' to name the repository.
implement `feature(more_qualified_paths)`
Specifically, this allows the following patterns and expressions which
were not allowed before:
```rust
let <T as Trait>::Assoc { a } = <T as Trait>::Assoc { a: 0 };
let (<E as Trait>::Assoc::ES { a } | <E as Trait>::Assoc::ET(a))
= <E as Trait>::Assoc::ES { a: 0 };
let (<E>::ES { a } | <E>::ET(a)) = <E>::ES { a: 0 };
```
Co-authored-by: Waffle Lapkin <[email protected]>
Co-authored-by: Chayim Refael Friedman <[email protected]>
| -rw-r--r-- | crates/hir-ty/src/infer.rs | 100 | ||||
| -rw-r--r-- | crates/hir-ty/src/tests/traits.rs | 111 |
2 files changed, 199 insertions, 12 deletions
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index d14e9d6526..bd897113bf 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1706,6 +1706,61 @@ impl<'body, 'db> InferenceContext<'body, 'db> { self.generic_def, LifetimeElisionKind::Infer, ); + + if let Some(type_anchor) = path.type_anchor() { + let mut segments = path.segments(); + if segments.is_empty() { + return (self.err_ty(), None); + } + let (mut ty, type_ns) = ctx.lower_ty_ext(type_anchor); + ty = self.table.process_user_written_ty(ty); + + if let Some(TypeNs::SelfType(impl_)) = type_ns + && let Some(trait_ref) = self.db.impl_trait(impl_) + && let trait_ref = trait_ref.instantiate_identity() + && let Some(assoc_type) = trait_ref + .def_id + .0 + .trait_items(self.db) + .associated_type_by_name(segments.first().unwrap().name) + { + // `<Self>::AssocType` + let args = self.infcx().fill_rest_fresh_args(assoc_type.into(), trait_ref.args); + let alias = Ty::new_alias( + self.interner(), + AliasTyKind::Projection, + AliasTy::new_from_args(self.interner(), assoc_type.into(), args), + ); + ty = self.table.try_structurally_resolve_type(alias); + segments = segments.skip(1); + } + + let variant = match ty.as_adt() { + Some((AdtId::StructId(id), _)) => id.into(), + Some((AdtId::UnionId(id), _)) => id.into(), + Some((AdtId::EnumId(id), _)) => { + if let Some(segment) = segments.first() + && let enum_data = id.enum_variants(self.db) + && let Some(variant) = enum_data.variant(segment.name) + { + // FIXME: Report error if there are generics on the variant. + segments = segments.skip(1); + variant.into() + } else { + return (self.err_ty(), None); + } + } + None => return (self.err_ty(), None), + }; + + if !segments.is_empty() { + // FIXME: Report an error. + return (self.err_ty(), None); + } else { + return (ty, Some(variant)); + } + } + let mut path_ctx = ctx.at_path(path, node); let interner = DbInterner::conjure(); let (resolution, unresolved) = if value_ns { @@ -1838,6 +1893,46 @@ impl<'body, 'db> InferenceContext<'body, 'db> { }); (ty, variant) } + TypeNs::TraitId(_) => { + let Some(remaining_idx) = unresolved else { + return (self.err_ty(), None); + }; + + let remaining_segments = path.segments().skip(remaining_idx); + + if remaining_segments.len() >= 2 { + path_ctx.ignore_last_segment(); + } + + let (mut ty, _) = path_ctx.lower_partly_resolved_path(resolution, true); + ty = self.table.process_user_written_ty(ty); + + if let Some(segment) = remaining_segments.get(1) + && let Some((AdtId::EnumId(id), _)) = ty.as_adt() + { + let enum_data = id.enum_variants(self.db); + if let Some(variant) = enum_data.variant(segment.name) { + return if remaining_segments.len() == 2 { + (ty, Some(variant.into())) + } else { + // We still have unresolved paths, but enum variants never have + // associated types! + // FIXME: Report an error. + (self.err_ty(), None) + }; + } + } + + let variant = ty.as_adt().and_then(|(id, _)| match id { + AdtId::StructId(s) => Some(VariantId::StructId(s)), + AdtId::UnionId(u) => Some(VariantId::UnionId(u)), + AdtId::EnumId(_) => { + // FIXME Error E0071, expected struct, variant or union type, found enum `Foo` + None + } + }); + (ty, variant) + } TypeNs::TypeAliasId(it) => { let Some(mod_path) = path.mod_path() else { never!("resolver should always resolve lang item paths"); @@ -1859,10 +1954,7 @@ impl<'body, 'db> InferenceContext<'body, 'db> { // FIXME potentially resolve assoc type (self.err_ty(), None) } - TypeNs::AdtId(AdtId::EnumId(_)) - | TypeNs::BuiltinType(_) - | TypeNs::TraitId(_) - | TypeNs::ModuleId(_) => { + TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::ModuleId(_) => { // FIXME diagnostic (self.err_ty(), None) } diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 22359d8f1f..1d27d52a36 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -4449,14 +4449,14 @@ impl Trait for () { let a = Self::Assoc { x }; // ^ S let a = <Self>::Assoc { x }; // unstable - // ^ {unknown} + // ^ S // should be `Copy` but we don't track ownership anyway. let value = S { x }; if let Self::Assoc { x } = value {} // ^ u32 if let <Self>::Assoc { x } = value {} // unstable - // ^ {unknown} + // ^ u32 } } "#, @@ -4508,22 +4508,22 @@ impl Trait for () { let a = Self::Assoc::Struct { x }; // ^ E let a = <Self>::Assoc::Struct { x }; // unstable - // ^ {unknown} + // ^ E let a = <Self::Assoc>::Struct { x }; // unstable - // ^ {unknown} + // ^ E let a = <<Self>::Assoc>::Struct { x }; // unstable - // ^ {unknown} + // ^ E // should be `Copy` but we don't track ownership anyway. let value = E::Struct { x: 42 }; if let Self::Assoc::Struct { x } = value {} // ^ u32 if let <Self>::Assoc::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 if let <Self::Assoc>::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 if let <<Self>::Assoc>::Struct { x } = value {} // unstable - // ^ {unknown} + // ^ u32 } } "#, @@ -5148,3 +5148,98 @@ fn foo(v: Struct<f32>) { "#, ); } + +#[test] +fn more_qualified_paths() { + check_infer( + r#" +struct T; +struct S { + a: u32, +} + +trait Trait { + type Assoc; + + fn foo(); +} + +impl Trait for T { + type Assoc = S; + + fn foo() { + let <Self>::Assoc { a } = <Self>::Assoc { a: 0 }; + } +} + +enum E { + ES { a: u32 }, + ET(u32), +} + +impl Trait for E { + type Assoc = Self; + + fn foo() { + let <Self>::Assoc::ES { a } = <Self>::Assoc::ES { a: 0 }; + } +} + +fn foo() { + let <T as Trait>::Assoc { a } = <T as Trait>::Assoc { a: 0 }; + + let <E>::ES { a } = (<E>::ES { a: 0 }) else { loop {} }; + let <E>::ET(a) = <E>::ET(0) else { loop {} }; + let <E as Trait>::Assoc::ES { a } = (<E as Trait>::Assoc::ES { a: 0 }) else { loop {} }; + let <E as Trait>::Assoc::ET(a) = <E as Trait>::Assoc::ET(0) else { loop {} }; +} + "#, + expect![[r#" + 137..202 '{ ... }': () + 151..170 '<Self>... { a }': S + 167..168 'a': u32 + 173..195 '<Self>...a: 0 }': S + 192..193 '0': u32 + 306..379 '{ ... }': () + 320..343 '<Self>... { a }': E + 340..341 'a': u32 + 346..372 '<Self>...a: 0 }': E + 369..370 '0': u32 + 392..748 '{ ...} }; }': () + 402..427 '<T as ... { a }': S + 424..425 'a': u32 + 430..458 '<T as ...a: 0 }': S + 455..456 '0': u32 + 469..482 '<E>::ES { a }': E + 479..480 'a': u32 + 486..502 '<E>::E...a: 0 }': E + 499..500 '0': u32 + 509..520 '{ loop {} }': ! + 511..518 'loop {}': ! + 516..518 '{}': () + 530..540 '<E>::ET(a)': E + 538..539 'a': u32 + 543..550 '<E>::ET': fn ET(u32) -> E + 543..553 '<E>::ET(0)': E + 551..552 '0': u32 + 559..570 '{ loop {} }': ! + 561..568 'loop {}': ! + 566..568 '{}': () + 580..609 '<E as ... { a }': E + 606..607 'a': u32 + 613..645 '<E as ...a: 0 }': E + 642..643 '0': u32 + 652..663 '{ loop {} }': ! + 654..661 'loop {}': ! + 659..661 '{}': () + 673..699 '<E as ...:ET(a)': E + 697..698 'a': u32 + 702..725 '<E as ...oc::ET': fn ET(u32) -> E + 702..728 '<E as ...:ET(0)': E + 726..727 '0': u32 + 734..745 '{ loop {} }': ! + 736..743 'loop {}': ! + 741..743 '{}': () + "#]], + ); +} |