Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/infer/path.rs')
| -rw-r--r-- | crates/hir-ty/src/infer/path.rs | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs new file mode 100644 index 0000000000..f580e09e91 --- /dev/null +++ b/crates/hir-ty/src/infer/path.rs @@ -0,0 +1,295 @@ +//! Path expression resolution. + +use chalk_ir::cast::Cast; +use hir_def::{ + path::{Path, PathSegment}, + resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, + AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup, +}; +use hir_expand::name::Name; + +use crate::{ + builder::ParamKind, + consteval, + method_resolution::{self, VisibleFromModule}, + GenericArgData, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, + ValueTyDefId, +}; + +use super::{ExprOrPatId, InferenceContext, TraitRef}; + +impl<'a> InferenceContext<'a> { + pub(super) fn infer_path( + &mut self, + resolver: &Resolver, + path: &Path, + id: ExprOrPatId, + ) -> Option<Ty> { + let ty = self.resolve_value_path(resolver, path, id)?; + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + Some(ty) + } + + fn resolve_value_path( + &mut self, + resolver: &Resolver, + path: &Path, + id: ExprOrPatId, + ) -> Option<Ty> { + let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { + if path.segments().is_empty() { + // This can't actually happen syntax-wise + return None; + } + let ty = self.make_ty(type_ref); + let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); + let ctx = crate::lower::TyLoweringContext::new(self.db, resolver); + let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty); + self.resolve_ty_assoc_item( + ty, + path.segments().last().expect("path had at least one segment").name, + id, + )? + } else { + let value_or_partial = + resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?; + + match value_or_partial { + ResolveValueResult::ValueNs(it) => (it, None), + ResolveValueResult::Partial(def, remaining_index) => { + self.resolve_assoc_item(def, path, remaining_index, id)? + } + } + }; + + let typable: ValueTyDefId = match value { + ValueNs::LocalBinding(pat) => { + let ty = self.result.type_of_pat.get(pat)?.clone(); + return Some(ty); + } + ValueNs::FunctionId(it) => it.into(), + ValueNs::ConstId(it) => it.into(), + ValueNs::StaticId(it) => it.into(), + ValueNs::StructId(it) => { + self.write_variant_resolution(id, it.into()); + + it.into() + } + ValueNs::EnumVariantId(it) => { + self.write_variant_resolution(id, it.into()); + + it.into() + } + ValueNs::ImplSelf(impl_id) => { + let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); + let substs = generics.placeholder_subst(self.db); + let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs); + if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { + let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs); + return Some(ty); + } else { + // FIXME: diagnostic, invalid Self reference + return None; + } + } + ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)), + }; + + let parent_substs = self_subst.unwrap_or_else(|| Substitution::empty(Interner)); + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); + let substs = ctx.substs_from_path(path, typable, true); + let mut it = substs.as_slice(Interner)[parent_substs.len(Interner)..].iter().cloned(); + let ty = TyBuilder::value_ty(self.db, typable) + .use_parent_substs(&parent_substs) + .fill(|x| { + it.next().unwrap_or_else(|| match x { + ParamKind::Type => { + GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner) + } + ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), + }) + }) + .build(); + Some(ty) + } + + fn resolve_assoc_item( + &mut self, + def: TypeNs, + path: &Path, + remaining_index: usize, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option<Substitution>)> { + assert!(remaining_index < path.segments().len()); + // 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. + + let resolved_segment = path.segments().get(remaining_index - 1).unwrap(); + let remaining_segments = path.segments().skip(remaining_index); + let is_before_last = remaining_segments.len() == 1; + + match (def, is_before_last) { + (TypeNs::TraitId(trait_), true) => { + let segment = + remaining_segments.last().expect("there should be at least one segment here"); + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); + let trait_ref = + ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + self.resolve_trait_assoc_item(trait_ref, segment, id) + } + (def, _) => { + // 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 + // as Iterator>::Item::default`) + let remaining_segments_for_ty = + remaining_segments.take(remaining_segments.len() - 1); + let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); + let (ty, _) = ctx.lower_partly_resolved_path( + def, + resolved_segment, + remaining_segments_for_ty, + true, + ); + if let TyKind::Error = ty.kind(Interner) { + return None; + } + + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); + + let segment = + remaining_segments.last().expect("there should be at least one segment here"); + + self.resolve_ty_assoc_item(ty, segment.name, id) + } + } + } + + fn resolve_trait_assoc_item( + &mut self, + trait_ref: TraitRef, + segment: PathSegment<'_>, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option<Substitution>)> { + let trait_ = trait_ref.hir_trait_id(); + let item = + self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| { + match item { + AssocItemId::FunctionId(func) => { + if segment.name == &self.db.function_data(func).name { + Some(AssocItemId::FunctionId(func)) + } else { + None + } + } + + AssocItemId::ConstId(konst) => { + if self + .db + .const_data(konst) + .name + .as_ref() + .map_or(false, |n| n == segment.name) + { + Some(AssocItemId::ConstId(konst)) + } else { + None + } + } + AssocItemId::TypeAliasId(_) => None, + } + })?; + let def = match item { + AssocItemId::FunctionId(f) => ValueNs::FunctionId(f), + AssocItemId::ConstId(c) => ValueNs::ConstId(c), + AssocItemId::TypeAliasId(_) => unreachable!(), + }; + + self.write_assoc_resolution(id, item); + Some((def, Some(trait_ref.substitution))) + } + + fn resolve_ty_assoc_item( + &mut self, + ty: Ty, + name: &Name, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option<Substitution>)> { + if let TyKind::Error = ty.kind(Interner) { + return None; + } + + if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { + return Some(result); + } + + let canonical_ty = self.canonicalize(ty.clone()); + let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); + + method_resolution::iterate_method_candidates( + &canonical_ty.value, + self.db, + self.table.trait_env.clone(), + &traits_in_scope, + VisibleFromModule::Filter(self.resolver.module()), + Some(name), + method_resolution::LookupMode::Path, + move |_ty, item| { + let (def, container) = match item { + AssocItemId::FunctionId(f) => { + (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container) + } + AssocItemId::ConstId(c) => { + (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container) + } + AssocItemId::TypeAliasId(_) => unreachable!(), + }; + let substs = match container { + ItemContainerId::ImplId(impl_id) => { + let impl_substs = TyBuilder::subst_for_def(self.db, impl_id) + .fill_with_inference_vars(&mut self.table) + .build(); + let impl_self_ty = + self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs); + self.unify(&impl_self_ty, &ty); + Some(impl_substs) + } + ItemContainerId::TraitId(trait_) => { + // we're picking this method + let trait_ref = TyBuilder::trait_ref(self.db, trait_) + .push(ty.clone()) + .fill_with_inference_vars(&mut self.table) + .build(); + self.push_obligation(trait_ref.clone().cast(Interner)); + Some(trait_ref.substitution) + } + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, + }; + + self.write_assoc_resolution(id, item); + Some((def, substs)) + }, + ) + } + + fn resolve_enum_variant_on_ty( + &mut self, + ty: &Ty, + name: &Name, + id: ExprOrPatId, + ) -> Option<(ValueNs, Option<Substitution>)> { + let ty = self.resolve_ty_shallow(ty); + let (enum_id, subst) = match ty.as_adt() { + Some((AdtId::EnumId(e), subst)) => (e, subst), + _ => return None, + }; + let enum_data = self.db.enum_data(enum_id); + let local_id = enum_data.variant(name)?; + let variant = EnumVariantId { parent: enum_id, local_id }; + self.write_variant_resolution(id, variant.into()); + Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) + } +} |