Resolve assoc types on type parameters

E.g. `fn foo<T: Iterator>() -> T::Item`. It seems that rustc does this only for
type parameters and only based on their bounds, so we also only consider traits
from bounds.
This commit is contained in:
Florian Diebold 2019-09-14 13:25:05 +02:00
parent 16ee779483
commit 913ab1ec0a
2 changed files with 61 additions and 24 deletions

View File

@ -93,7 +93,7 @@ impl Ty {
None => return Ty::Unknown,
};
let typable: TypableDef = match resolution {
let ty = match resolution {
TypeNs::Trait(trait_) => {
let segment = match remaining_index {
None => path.segments.last().expect("resolved path has at least one element"),
@ -115,12 +115,12 @@ impl Ty {
})
}
None => {
// associated type not found
// associated type not found (FIXME: report error)
Ty::Unknown
}
}
} else {
// FIXME more than one segment remaining, is this possible?
// FIXME report error (ambiguous associated type)
Ty::Unknown
}
} else {
@ -128,34 +128,71 @@ impl Ty {
};
}
TypeNs::GenericParam(idx) => {
if remaining_index.is_some() {
// e.g. T::Item
return Ty::Unknown;
}
return Ty::Param {
idx,
// FIXME: maybe return name in resolution?
name: path
// FIXME: maybe return name in resolution?
let name = match remaining_index {
None => path
.as_ident()
.expect("generic param should be single-segment path")
.clone(),
Some(idx) => path.segments[idx - 1].name.clone(),
};
Ty::Param { idx, name }
}
TypeNs::SelfType(impl_block) => {
if remaining_index.is_some() {
// e.g. Self::Item
return Ty::Unknown;
}
return impl_block.target_ty(db);
}
TypeNs::SelfType(impl_block) => impl_block.target_ty(db),
TypeNs::Adt(it) => it.into(),
TypeNs::BuiltinType(it) => it.into(),
TypeNs::TypeAlias(it) => it.into(),
TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()),
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()),
TypeNs::TypeAlias(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()),
// FIXME: report error
TypeNs::EnumVariant(_) => return Ty::Unknown,
};
if let Some(remaining_index) = remaining_index {
// resolve unselected assoc types
if remaining_index == path.segments.len() - 1 {
let segment = &path.segments[remaining_index];
Ty::select_associated_type(db, resolver, ty, segment)
} else {
// FIXME report error (ambiguous associated type)
Ty::Unknown
}
} else {
ty
}
}
fn select_associated_type(
db: &impl HirDatabase,
resolver: &Resolver,
self_ty: Ty,
segment: &PathSegment,
) -> Ty {
let env = trait_env(db, resolver);
let traits_from_env = env.trait_predicates_for_self_ty(&self_ty).map(|tr| tr.trait_);
let traits = traits_from_env.flat_map(|t| t.all_super_traits(db));
let mut result = Ty::Unknown;
for t in traits {
if let Some(associated_ty) = t.associated_type_by_name(db, &segment.name) {
let generics = t.generic_params(db);
let mut substs = Vec::new();
substs.push(self_ty.clone());
substs.extend(
iter::repeat(Ty::Unknown).take(generics.count_params_including_parent() - 1),
);
// FIXME handle type parameters on the segment
result = Ty::Projection(ProjectionTy { associated_ty, parameters: substs.into() });
break;
}
}
result
}
fn from_hir_path_inner(
db: &impl HirDatabase,
resolver: &Resolver,
path: &Path,
typable: TypableDef,
) -> Ty {
let ty = db.type_for_def(typable, Namespace::Types);
let substs = Ty::substs_from_path(db, resolver, path, typable);
ty.subst(&substs)

View File

@ -2732,9 +2732,9 @@ fn test() {
[147; 148) 't': T
[178; 180) '{}': ()
[191; 236) '{ ...(S); }': ()
[201; 202) 'x': {unknown}
[205; 209) 'foo1': fn foo1<S>(T) -> {unknown}
[205; 212) 'foo1(S)': {unknown}
[201; 202) 'x': u32
[205; 209) 'foo1': fn foo1<S>(T) -> <T as Iterable>::Item
[205; 212) 'foo1(S)': u32
[210; 211) 'S': S
[222; 223) 'y': u32
[226; 230) 'foo2': fn foo2<S>(T) -> <T as Iterable>::Item