Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #19956 from WaffleLapkin/more_qualified_paths
Implement `feature(more_qualified_paths)`
Chayim Refael Friedman 8 weeks ago
parent 207f035 · parent 48b6cff · commit 021531f
-rw-r--r--crates/hir-ty/src/infer.rs100
-rw-r--r--crates/hir-ty/src/tests/traits.rs111
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 '{}': ()
+ "#]],
+ );
+}