Infer variants through type aliased enums

This commit is contained in:
Lukas Wirth 2021-04-07 11:31:50 +02:00
parent e6a1c9ca60
commit 41563fd612
2 changed files with 70 additions and 33 deletions

View File

@ -484,36 +484,13 @@ fn resolve_variant(&mut self, path: Option<&Path>) -> (Ty, Option<VariantId>) {
let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
let substs = generics.type_params_subst(self.db); let substs = generics.type_params_subst(self.db);
let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs); let ty = self.db.impl_self_ty(impl_id).substitute(&Interner, &substs);
match unresolved { self.resolve_variant_on_alias(ty, unresolved, path)
None => {
let variant = ty_variant(&ty);
(ty, variant)
}
Some(1) => {
let segment = path.mod_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)
}
Some(_) => {
// FIXME diagnostic
(self.err_ty(), None)
}
}
} }
TypeNs::TypeAliasId(it) => { TypeNs::TypeAliasId(it) => {
let ty = TyBuilder::def_ty(self.db, it.into()) let ty = TyBuilder::def_ty(self.db, it.into())
.fill(std::iter::repeat_with(|| self.table.new_type_var())) .fill(std::iter::repeat_with(|| self.table.new_type_var()))
.build(); .build();
let variant = ty_variant(&ty); self.resolve_variant_on_alias(ty, unresolved, path)
forbid_unresolved_segments((ty, variant), unresolved)
} }
TypeNs::AdtSelfType(_) => { TypeNs::AdtSelfType(_) => {
// FIXME this could happen in array size expressions, once we're checking them // FIXME this could happen in array size expressions, once we're checking them
@ -540,16 +517,43 @@ fn forbid_unresolved_segments(
(TyKind::Error.intern(&Interner), None) (TyKind::Error.intern(&Interner), None)
} }
} }
}
fn ty_variant(ty: &Ty) -> Option<VariantId> { fn resolve_variant_on_alias(
ty.as_adt().and_then(|(adt_id, _)| match adt_id { &mut self,
AdtId::StructId(s) => Some(VariantId::StructId(s)), ty: Ty,
AdtId::UnionId(u) => Some(VariantId::UnionId(u)), unresolved: Option<usize>,
AdtId::EnumId(_) => { path: &Path,
// FIXME Error E0071, expected struct, variant or union type, found enum `Foo` ) -> (Ty, Option<VariantId>) {
None match unresolved {
None => {
let variant = ty.as_adt().and_then(|(adt_id, _)| match adt_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)
}
Some(1) => {
let segment = path.mod_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)
}
Some(_) => {
// FIXME diagnostic
(self.err_ty(), None)
}
} }
} }

View File

@ -2564,3 +2564,36 @@ fn f() {
"#, "#,
) )
} }
#[test]
fn infer_type_alias_variant() {
check_infer(
r#"
type Qux = Foo;
enum Foo {
Bar(i32),
Baz { baz: f32 }
}
fn f() {
match Foo::Bar(3) {
Qux::Bar(bar) => (),
Qux::Baz { baz } => (),
}
}
"#,
expect![[r#"
72..166 '{ ... } }': ()
78..164 'match ... }': ()
84..92 'Foo::Bar': Bar(i32) -> Foo
84..95 'Foo::Bar(3)': Foo
93..94 '3': i32
106..119 'Qux::Bar(bar)': Foo
115..118 'bar': i32
123..125 '()': ()
135..151 'Qux::B... baz }': Foo
146..149 'baz': f32
155..157 '()': ()
"#]],
)
}