Refactor associated item resolution more

When resolving an associated item in value namespace, use the `Ty` lowering code
for the segments before the last instead of replicating it.
This commit is contained in:
Florian Diebold 2019-09-15 12:50:57 +02:00
parent 828d60574f
commit 406280e52f
2 changed files with 121 additions and 125 deletions

View file

@ -48,7 +48,7 @@ use crate::{
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
ty::infer::diagnostics::InferenceDiagnostic, ty::infer::diagnostics::InferenceDiagnostic,
type_ref::{Mutability, TypeRef}, type_ref::{Mutability, TypeRef},
Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, ImplItem, Name, Path, Adt, AssocItem, ConstData, DefWithBody, Either, FnData, Function, HasBody, Name, Path,
StructField, StructField,
}; };
@ -508,7 +508,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn resolve_assoc_item( fn resolve_assoc_item(
&mut self, &mut self,
mut def_or_ty: Either<TypeNs, Ty>, // mut def_or_ty: Either<TypeNs, Ty>,
def: TypeNs,
path: &Path, path: &Path,
remaining_index: usize, remaining_index: usize,
id: ExprOrPatId, id: ExprOrPatId,
@ -516,62 +517,40 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
assert!(remaining_index < path.segments.len()); assert!(remaining_index < path.segments.len());
let krate = self.resolver.krate()?; let krate = self.resolver.krate()?;
let mut ty = Ty::Unknown; // there may be more intermediate segments between the resolved one and
// the end. Only the last segment needs to be resolved to a value; from
// the segments before that, we need to get either a type or a trait ref.
// resolve intermediate segments let resolved_segment = &path.segments[remaining_index - 1];
for (i, segment) in path.segments[remaining_index..].iter().enumerate() { let remaining_segments = &path.segments[remaining_index..];
let is_last_segment = i == path.segments[remaining_index..].len() - 1; let is_before_last = remaining_segments.len() == 1;
ty = match def_or_ty {
Either::A(def) => { let (def, substs) = match (def, is_before_last) {
let typable: TypableDef = match def { (TypeNs::Trait(_trait), true) => {
TypeNs::Adt(it) => it.into(), // Associated item of trait, e.g. `Default::default`
TypeNs::TypeAlias(it) => it.into(), // FIXME
TypeNs::BuiltinType(it) => it.into(),
// FIXME associated item of traits, generics, and Self
TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => {
return None; return None;
} }
// FIXME: report error here (def, _) => {
TypeNs::EnumVariant(_) => return None, // Either we already have a type (e.g. `Vec::new`), or we have a
}; // trait but it's not the last segment, so the next segment
// should resolve to an associated type of that trait (e.g. `<T
let ty = self.db.type_for_def(typable, Namespace::Types); // as Iterator>::Item::default`)
let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1];
// For example, this substs will take `Gen::*<u32>*::make` let ty = Ty::from_partly_resolved_hir_path(
assert!(remaining_index > 0);
let substs = Ty::substs_from_path_segment(
self.db, self.db,
&self.resolver, &self.resolver,
&path.segments[remaining_index + i - 1], def,
typable, resolved_segment,
remaining_segments_for_ty,
); );
ty.subst(&substs) if let Ty::Unknown = ty {
} return None;
Either::B(ty) => ty,
};
if is_last_segment {
break;
} }
// Attempt to find an impl_item for the type which has a name matching let segment =
// the current segment remaining_segments.last().expect("there should be at least one segment here");
log::debug!("looking for path segment: {:?}", segment); // Find impl
let ty = mem::replace(&mut ty, Ty::Unknown);
def_or_ty = ty.iterate_impl_items(self.db, krate, |item| {
match item {
crate::ImplItem::Method(_) | crate::ImplItem::Const(_) => None,
// FIXME: Resolve associated types
crate::ImplItem::TypeAlias(_) => {
// Some(Either::A(TypeNs::TypeAlias(..)))
None
}
}
})?;
}
let segment = path.segments.last().unwrap();
let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item { let def = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
crate::ImplItem::Method(func) => { crate::ImplItem::Method(func) => {
if segment.name == func.name(self.db) { if segment.name == func.name(self.db) {
@ -582,7 +561,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
} }
crate::ImplItem::Const(konst) => { crate::ImplItem::Const(konst) => {
if konst.name(self.db).map_or(false, |n| n == segment.name) { if segment.name == konst.name(self.db) {
Some(ValueNs::Const(konst)) Some(ValueNs::Const(konst))
} else { } else {
None None
@ -590,6 +569,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
} }
crate::ImplItem::TypeAlias(_) => None, crate::ImplItem::TypeAlias(_) => None,
})?; })?;
let self_types = self.find_self_types(&def, ty);
(def, self_types)
}
};
self.write_assoc_resolution( self.write_assoc_resolution(
id, id,
match def { match def {
@ -598,8 +582,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
_ => unreachable!(), _ => unreachable!(),
}, },
); );
let self_types = self.find_self_types(&def, ty); Some((def, substs))
Some((def, self_types))
} }
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {

View file

@ -86,23 +86,19 @@ impl Ty {
} }
} }
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty { pub(crate) fn from_partly_resolved_hir_path(
// Resolve the path (in type namespace) db: &impl HirDatabase,
let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { resolver: &Resolver,
Some(it) => it, resolution: TypeNs,
None => return Ty::Unknown, resolved_segment: &PathSegment,
}; remaining_segments: &[PathSegment],
) -> Ty {
let ty = match resolution { let ty = match resolution {
TypeNs::Trait(trait_) => { TypeNs::Trait(trait_) => {
let segment = match remaining_index { let trait_ref =
None => path.segments.last().expect("resolved path has at least one element"), TraitRef::from_resolved_path(db, resolver, trait_, resolved_segment, None);
Some(i) => &path.segments[i - 1], return if remaining_segments.len() == 1 {
}; let segment = &remaining_segments[0];
let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None);
return if let Some(remaining_index) = remaining_index {
if remaining_index == path.segments.len() - 1 {
let segment = &path.segments[remaining_index];
match trait_ref match trait_ref
.trait_ .trait_
.associated_type_by_name_including_super_traits(db, &segment.name) .associated_type_by_name_including_super_traits(db, &segment.name)
@ -119,48 +115,65 @@ impl Ty {
Ty::Unknown Ty::Unknown
} }
} }
} else { } else if remaining_segments.len() > 1 {
// FIXME report error (ambiguous associated type) // FIXME report error (ambiguous associated type)
Ty::Unknown Ty::Unknown
}
} else { } else {
Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)])) Ty::Dyn(Arc::new([GenericPredicate::Implemented(trait_ref)]))
}; };
} }
TypeNs::GenericParam(idx) => { TypeNs::GenericParam(idx) => {
// FIXME: maybe return name in resolution? // FIXME: maybe return name in resolution?
let name = match remaining_index { let name = resolved_segment.name.clone();
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 } Ty::Param { idx, name }
} }
TypeNs::SelfType(impl_block) => impl_block.target_ty(db), TypeNs::SelfType(impl_block) => impl_block.target_ty(db),
TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), TypeNs::Adt(it) => Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into()),
TypeNs::BuiltinType(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), TypeNs::BuiltinType(it) => {
TypeNs::TypeAlias(it) => Ty::from_hir_path_inner(db, resolver, path, it.into()), Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into())
}
TypeNs::TypeAlias(it) => {
Ty::from_hir_path_inner(db, resolver, resolved_segment, it.into())
}
// FIXME: report error // FIXME: report error
TypeNs::EnumVariant(_) => return Ty::Unknown, TypeNs::EnumVariant(_) => return Ty::Unknown,
}; };
if let Some(remaining_index) = remaining_index { if remaining_segments.len() == 1 {
// resolve unselected assoc types // resolve unselected assoc types
if remaining_index == path.segments.len() - 1 { let segment = &remaining_segments[0];
let segment = &path.segments[remaining_index];
Ty::select_associated_type(db, resolver, ty, segment) Ty::select_associated_type(db, resolver, ty, segment)
} else { } else if remaining_segments.len() > 1 {
// FIXME report error (ambiguous associated type) // FIXME report error (ambiguous associated type)
Ty::Unknown Ty::Unknown
}
} else { } else {
ty ty
} }
} }
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Ty {
// Resolve the path (in type namespace)
let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) {
Some(it) => it,
None => return Ty::Unknown,
};
let (resolved_segment, remaining_segments) = match remaining_index {
None => (
path.segments.last().expect("resolved path has at least one element"),
&[] as &[PathSegment],
),
Some(i) => (&path.segments[i - 1], &path.segments[i..]),
};
Ty::from_partly_resolved_hir_path(
db,
resolver,
resolution,
resolved_segment,
remaining_segments,
)
}
fn select_associated_type( fn select_associated_type(
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
@ -190,11 +203,11 @@ impl Ty {
fn from_hir_path_inner( fn from_hir_path_inner(
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
path: &Path, segment: &PathSegment,
typable: TypableDef, typable: TypableDef,
) -> Ty { ) -> Ty {
let ty = db.type_for_def(typable, Namespace::Types); let ty = db.type_for_def(typable, Namespace::Types);
let substs = Ty::substs_from_path(db, resolver, path, typable); let substs = Ty::substs_from_path_segment(db, resolver, segment, typable);
ty.subst(&substs) ty.subst(&substs)
} }