From 51e2d76b9839410020c75ac02ad602675b0a5aa9 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 12 Sep 2019 23:35:53 +0300 Subject: Specify desirable namespace when calling resolve That way, we are able to get rid of a number of unreachable statements --- crates/ra_hir/src/ty/infer.rs | 256 ++++++++++++++++++++---------------------- crates/ra_hir/src/ty/lower.rs | 108 +++++++++--------- 2 files changed, 176 insertions(+), 188 deletions(-) (limited to 'crates/ra_hir/src/ty') diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs index 540a99b15..3ee083a04 100644 --- a/crates/ra_hir/src/ty/infer.rs +++ b/crates/ra_hir/src/ty/infer.rs @@ -45,11 +45,10 @@ use crate::{ name, nameres::Namespace, path::{GenericArg, GenericArgs, PathKind, PathSegment}, - resolve::{Resolution, Resolver}, + resolve::{Resolver, TypeNs, ValueNs, ValueOrPartial}, ty::infer::diagnostics::InferenceDiagnostic, type_ref::{Mutability, TypeRef}, - Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, ModuleDef, Name, Path, - StructField, + Adt, ConstData, DefWithBody, FnData, Function, HasBody, ImplItem, Name, Path, StructField, }; mod unify; @@ -472,141 +471,138 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { } fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option { - let resolved = resolver.resolve_path_segments(self.db, &path); + let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; - let (def, remaining_index) = resolved.into_inner(); + let (value, self_subst) = match value_or_partial { + ValueOrPartial::ValueNs(it) => (it, None), + ValueOrPartial::Partial(def, remaining_index) => { + self.resolve_assoc_item(def, path, remaining_index, id)? + } + }; - log::debug!( - "path {:?} resolved to {:?} with remaining index {:?}", - path, - def, - remaining_index - ); + let typable: TypableDef = match value { + ValueNs::LocalBinding(pat) => { + let ty = self.result.type_of_pat.get(pat)?.clone(); + let ty = self.resolve_ty_as_possible(&mut vec![], ty); + return Some(ty); + } + ValueNs::Function(it) => it.into(), + ValueNs::Const(it) => it.into(), + ValueNs::Static(it) => it.into(), + ValueNs::Struct(it) => it.into(), + ValueNs::EnumVariant(it) => it.into(), + }; - // if the remaining_index is None, we expect the path - // to be fully resolved, in this case we continue with - // the default by attempting to `take_values´ from the resolution. - // Otherwise the path was partially resolved, which means - // we might have resolved into a type for which - // we may find some associated item starting at the - // path.segment pointed to by `remaining_index´ - let mut resolved = - if remaining_index.is_none() { def.take_values()? } else { def.take_types()? }; + let mut ty = self.db.type_for_def(typable, Namespace::Values); + if let Some(self_subst) = self_subst { + ty = ty.subst(&self_subst); + } - let remaining_index = remaining_index.unwrap_or_else(|| path.segments.len()); - let mut actual_def_ty: Option = None; + let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); + let ty = ty.subst(&substs); + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + Some(ty) + } + + fn resolve_assoc_item( + &mut self, + mut def: TypeNs, + path: &Path, + remaining_index: usize, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option)> { + assert!(remaining_index < path.segments.len()); + let krate = self.resolver.krate()?; + + let mut ty = Ty::Unknown; - let krate = resolver.krate()?; // resolve intermediate segments for (i, segment) in path.segments[remaining_index..].iter().enumerate() { - let ty = match resolved { - Resolution::Def(def) => { - // FIXME resolve associated items from traits as well - let typable: Option = def.into(); - let typable = typable?; - - let ty = self.db.type_for_def(typable, Namespace::Types); - - // For example, this substs will take `Gen::**::make` - assert!(remaining_index > 0); - let substs = Ty::substs_from_path_segment( - self.db, - &self.resolver, - &path.segments[remaining_index + i - 1], - typable, - ); - - ty.subst(&substs) - } - Resolution::LocalBinding(_) => { - // can't have a local binding in an associated item path - return None; - } - Resolution::GenericParam(..) => { - // FIXME associated item of generic param - return None; - } - Resolution::SelfType(_) => { - // FIXME associated item of self type - return None; - } + let is_last_segment = i == path.segments[remaining_index..].len() - 1; + ty = { + let typable: TypableDef = match def { + TypeNs::Adt(it) => it.into(), + TypeNs::TypeAlias(it) => it.into(), + TypeNs::BuiltinType(it) => it.into(), + // FIXME associated item of traits, generics, and Self + TypeNs::Trait(_) | TypeNs::GenericParam(_) | TypeNs::SelfType(_) => { + return None; + } + // FIXME: report error here + TypeNs::EnumVariant(_) => return None, + }; + + let ty = self.db.type_for_def(typable, Namespace::Types); + + // For example, this substs will take `Gen::**::make` + assert!(remaining_index > 0); + let substs = Ty::substs_from_path_segment( + self.db, + &self.resolver, + &path.segments[remaining_index + i - 1], + typable, + ); + ty.subst(&substs) }; + if is_last_segment { + break; + } // Attempt to find an impl_item for the type which has a name matching // the current segment log::debug!("looking for path segment: {:?}", segment); - actual_def_ty = Some(ty.clone()); - - let item: crate::ModuleDef = ty.iterate_impl_items(self.db, krate, |item| { - let matching_def: Option = match item { - crate::ImplItem::Method(func) => { - if segment.name == func.name(self.db) { - Some(func.into()) - } else { - None - } - } - - crate::ImplItem::Const(konst) => { - let data = konst.data(self.db); - if segment.name == *data.name() { - Some(konst.into()) - } else { - None - } - } + let ty = mem::replace(&mut ty, Ty::Unknown); + def = ty.iterate_impl_items(self.db, krate, |item| { + match item { + crate::ImplItem::Method(_) => None, + crate::ImplItem::Const(_) => None, // FIXME: Resolve associated types - crate::ImplItem::TypeAlias(_) => None, - }; - match matching_def { - Some(_) => { - self.write_assoc_resolution(id, item); - matching_def + crate::ImplItem::TypeAlias(_) => { + // Some(TypeNs::TypeAlias(..)) + None:: } - None => None, } })?; - - resolved = Resolution::Def(item); } - match resolved { - Resolution::Def(def) => { - let typable: Option = def.into(); - let typable = typable?; - let mut ty = self.db.type_for_def(typable, Namespace::Values); - if let Some(sts) = self.find_self_types(&def, actual_def_ty) { - ty = ty.subst(&sts); + let segment = path.segments.last().unwrap(); + let def = ty.clone().iterate_impl_items(self.db, krate, |item| { + let matching_def: Option = match item { + crate::ImplItem::Method(func) => { + if segment.name == func.name(self.db) { + Some(ValueNs::Function(func)) + } else { + None + } } - let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); - let ty = ty.subst(&substs); - let ty = self.insert_type_vars(ty); - let ty = self.normalize_associated_types_in(ty); - Some(ty) - } - Resolution::LocalBinding(pat) => { - let ty = self.result.type_of_pat.get(pat)?.clone(); - let ty = self.resolve_ty_as_possible(&mut vec![], ty); - Some(ty) - } - Resolution::GenericParam(..) => { - // generic params can't refer to values... yet - None - } - Resolution::SelfType(_) => { - log::error!("path expr {:?} resolved to Self type in values ns", path); - None + crate::ImplItem::Const(konst) => { + let data = konst.data(self.db); + if segment.name == *data.name() { + Some(ValueNs::Const(konst)) + } else { + None + } + } + crate::ImplItem::TypeAlias(_) => None, + }; + match matching_def { + Some(_) => { + self.write_assoc_resolution(id, item); + matching_def + } + None => None, } - } + })?; + let self_types = self.find_self_types(&def, ty); + Some((def, self_types)) } - fn find_self_types(&self, def: &ModuleDef, actual_def_ty: Option) -> Option { - let actual_def_ty = actual_def_ty?; - - if let crate::ModuleDef::Function(func) = def { + fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option { + if let ValueNs::Function(func) = def { // We only do the infer if parent has generic params let gen = func.generic_params(self.db); if gen.count_parent_params() == 0 { @@ -641,30 +637,24 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { None => return (Ty::Unknown, None), }; let resolver = &self.resolver; - let typable: Option = + let def: TypableDef = // FIXME: this should resolve assoc items as well, see this example: // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521 - match resolver.resolve_path_without_assoc_items(self.db, &path).take_types() { - Some(Resolution::Def(def)) => def.into(), - Some(Resolution::LocalBinding(..)) => { - // this cannot happen - log::error!("path resolved to local binding in type ns"); - return (Ty::Unknown, None); - } - Some(Resolution::GenericParam(..)) => { - // generic params can't be used in struct literals - return (Ty::Unknown, None); - } - Some(Resolution::SelfType(..)) => { - // FIXME this is allowed in an impl for a struct, handle this - return (Ty::Unknown, None); + match resolver.resolve_path_in_type_ns_fully(self.db, &path) { + Some(TypeNs::Adt(Adt::Struct(it))) => it.into(), + Some(TypeNs::Adt(Adt::Union(it))) => it.into(), + Some(TypeNs::EnumVariant(it)) => it.into(), + Some(TypeNs::TypeAlias(it)) => it.into(), + + Some(TypeNs::SelfType(_)) | + Some(TypeNs::GenericParam(_)) | + Some(TypeNs::BuiltinType(_)) | + Some(TypeNs::Trait(_)) | + Some(TypeNs::Adt(Adt::Enum(_))) | + None => { + return (Ty::Unknown, None) } - None => return (Ty::Unknown, None), }; - let def = match typable { - None => return (Ty::Unknown, None), - Some(it) => it, - }; // FIXME remove the duplication between here and `Ty::from_path`? let substs = Ty::substs_from_path(self.db, resolver, path, def); match def { diff --git a/crates/ra_hir/src/ty/lower.rs b/crates/ra_hir/src/ty/lower.rs index 946e9e9fb..3fdb2ca92 100644 --- a/crates/ra_hir/src/ty/lower.rs +++ b/crates/ra_hir/src/ty/lower.rs @@ -19,7 +19,7 @@ use crate::{ generics::{GenericDef, WherePredicate}, nameres::Namespace, path::{GenericArg, PathSegment}, - resolve::{Resolution, Resolver}, + resolve::{Resolver, TypeNs}, ty::Adt, type_ref::{TypeBound, TypeRef}, BuiltinType, Const, Enum, EnumVariant, Function, ModuleDef, Path, Static, Struct, StructField, @@ -88,16 +88,47 @@ impl 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) = resolver.resolve_path_segments(db, path).into_inner(); - let resolution = resolution.take_types(); - - let def = match resolution { - Some(Resolution::Def(def)) => def, - Some(Resolution::LocalBinding(..)) => { - // this should never happen - panic!("path resolved to local binding in type ns"); + let (resolution, remaining_index) = match resolver.resolve_path_in_type_ns(db, path) { + Some(it) => it, + None => return Ty::Unknown, + }; + + let typable: TypableDef = match resolution { + TypeNs::Trait(trait_) => { + let segment = match remaining_index { + None => path.segments.last().expect("resolved path has at least one element"), + Some(i) => &path.segments[i - 1], + }; + 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 + .trait_ + .associated_type_by_name_including_super_traits(db, &segment.name) + { + Some(associated_ty) => { + // FIXME handle type parameters on the segment + Ty::Projection(ProjectionTy { + associated_ty, + parameters: trait_ref.substs, + }) + } + None => { + // associated type not found + Ty::Unknown + } + } + } else { + // FIXME more than one segment remaining, is this possible? + Ty::Unknown + } + } else { + // FIXME dyn Trait without the dyn + Ty::Unknown + }; } - Some(Resolution::GenericParam(idx)) => { + TypeNs::GenericParam(idx) => { if remaining_index.is_some() { // e.g. T::Item return Ty::Unknown; @@ -111,57 +142,24 @@ impl Ty { .clone(), }; } - Some(Resolution::SelfType(impl_block)) => { + TypeNs::SelfType(impl_block) => { if remaining_index.is_some() { // e.g. Self::Item return Ty::Unknown; } return impl_block.target_ty(db); } - None => { - // path did not resolve - return Ty::Unknown; - } + + TypeNs::Adt(it) => it.into(), + TypeNs::BuiltinType(it) => it.into(), + TypeNs::TypeAlias(it) => it.into(), + // FIXME: report error + TypeNs::EnumVariant(_) => return Ty::Unknown, }; - if let ModuleDef::Trait(trait_) = def { - let segment = match remaining_index { - None => path.segments.last().expect("resolved path has at least one element"), - Some(i) => &path.segments[i - 1], - }; - let trait_ref = TraitRef::from_resolved_path(db, resolver, trait_, segment, None); - if let Some(remaining_index) = remaining_index { - if remaining_index == path.segments.len() - 1 { - let segment = &path.segments[remaining_index]; - let associated_ty = match trait_ref - .trait_ - .associated_type_by_name_including_super_traits(db, &segment.name) - { - Some(t) => t, - None => { - // associated type not found - return Ty::Unknown; - } - }; - // FIXME handle type parameters on the segment - Ty::Projection(ProjectionTy { associated_ty, parameters: trait_ref.substs }) - } else { - // FIXME more than one segment remaining, is this possible? - Ty::Unknown - } - } else { - // FIXME dyn Trait without the dyn - Ty::Unknown - } - } else { - let typable: TypableDef = match def.into() { - None => return Ty::Unknown, - Some(it) => it, - }; - let ty = db.type_for_def(typable, Namespace::Types); - let substs = Ty::substs_from_path(db, resolver, path, typable); - ty.subst(&substs) - } + let ty = db.type_for_def(typable, Namespace::Types); + let substs = Ty::substs_from_path(db, resolver, path, typable); + ty.subst(&substs) } pub(super) fn substs_from_path_segment( @@ -278,8 +276,8 @@ impl TraitRef { path: &Path, explicit_self_ty: Option, ) -> Option { - let resolved = match resolver.resolve_path_without_assoc_items(db, &path).take_types()? { - Resolution::Def(ModuleDef::Trait(tr)) => tr, + let resolved = match resolver.resolve_path_in_type_ns_fully(db, &path)? { + TypeNs::Trait(tr) => tr, _ => return None, }; let segment = path.segments.last().expect("path should have at least one segment"); -- cgit v1.2.3