| //! Path expression resolution. |
| |
| use chalk_ir::cast::Cast; |
| use hir_def::{ |
| AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup, |
| expr_store::path::{Path, PathSegment}, |
| resolver::{ResolveValueResult, TypeNs, ValueNs}, |
| }; |
| use hir_expand::name::Name; |
| use stdx::never; |
| |
| use crate::{ |
| InferenceDiagnostic, Interner, LifetimeElisionKind, Substitution, TraitRef, TraitRefExt, Ty, |
| TyBuilder, TyExt, TyKind, ValueTyDefId, |
| builder::ParamKind, |
| consteval, error_lifetime, |
| generics::generics, |
| infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext, |
| method_resolution::{self, VisibleFromModule}, |
| next_solver::{ |
| DbInterner, |
| mapping::{ChalkToNextSolver, NextSolverToChalk}, |
| }, |
| to_chalk_trait_id, |
| }; |
| |
| use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource}; |
| |
| impl<'db> InferenceContext<'db> { |
| pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> { |
| let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? { |
| ValuePathResolution::GenericDef(value_def, generic_def, substs) => { |
| (value_def, generic_def, substs) |
| } |
| ValuePathResolution::NonGeneric(ty) => return Some(ty), |
| }; |
| let substs = |
| self.process_remote_user_written_ty::<_, crate::next_solver::GenericArgs<'db>>(substs); |
| |
| self.add_required_obligations_for_value_path(generic_def, &substs); |
| |
| let interner = DbInterner::new_with(self.db, None, None); |
| let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); |
| let ty = self.db.value_ty(value_def)?.instantiate(interner, args).to_chalk(interner); |
| let ty = self.process_remote_user_written_ty(ty); |
| Some(ty) |
| } |
| |
| fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> { |
| let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?; |
| |
| let value_def: ValueTyDefId = match value { |
| 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::LocalBinding(pat) => { |
| return match self.result.type_of_binding.get(pat) { |
| Some(ty) => Some(ValuePathResolution::NonGeneric(ty.clone())), |
| None => { |
| never!("uninferred pattern?"); |
| None |
| } |
| }; |
| } |
| ValueNs::ImplSelf(impl_id) => { |
| let generics = crate::generics::generics(self.db, impl_id.into()); |
| let interner = DbInterner::new_with(self.db, None, None); |
| let substs = generics.placeholder_subst(self.db); |
| let args: crate::next_solver::GenericArgs<'_> = substs.to_nextsolver(interner); |
| let ty = |
| self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); |
| return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { |
| Some(ValuePathResolution::GenericDef( |
| struct_id.into(), |
| struct_id.into(), |
| substs.clone(), |
| )) |
| } else { |
| // FIXME: report error, invalid Self reference |
| None |
| }; |
| } |
| ValueNs::GenericParam(it) => { |
| return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))); |
| } |
| }; |
| |
| let generic_def = value_def.to_generic_def_id(self.db); |
| if let GenericDefId::StaticId(_) = generic_def { |
| let interner = DbInterner::new_with(self.db, None, None); |
| // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type. |
| let ty = self.db.value_ty(value_def)?.skip_binder().to_chalk(interner); |
| return Some(ValuePathResolution::NonGeneric(ty)); |
| }; |
| |
| let substs = self.with_body_ty_lowering(|ctx| { |
| let mut path_ctx = ctx.at_path(path, id); |
| let last_segment = path.segments().len().checked_sub(1); |
| if let Some(last_segment) = last_segment { |
| path_ctx.set_current_segment(last_segment) |
| } |
| path_ctx.substs_from_path(value_def, true, false) |
| }); |
| let substs = substs.as_slice(Interner); |
| |
| if let ValueNs::EnumVariantId(_) = value { |
| let mut it = substs |
| .iter() |
| .chain(self_subst.as_ref().map_or(&[][..], |s| s.as_slice(Interner))) |
| .cloned(); |
| let builder = TyBuilder::subst_for_def(self.db, generic_def, None); |
| let substs = builder |
| .fill(|x| { |
| it.next().unwrap_or_else(|| match x { |
| ParamKind::Type => { |
| self.result.standard_types.unknown.clone().cast(Interner) |
| } |
| ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), |
| ParamKind::Lifetime => error_lifetime().cast(Interner), |
| }) |
| }) |
| .build(); |
| |
| return Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)); |
| } |
| |
| let parent_substs = self_subst.or_else(|| { |
| let generics = generics(self.db, generic_def); |
| let parent_params_len = generics.parent_generics()?.len(); |
| let parent_args = &substs[..parent_params_len]; |
| Some(Substitution::from_iter(Interner, parent_args)) |
| }); |
| let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner)); |
| let mut it = substs.iter().skip(parent_substs_len).cloned(); |
| let builder = TyBuilder::subst_for_def(self.db, generic_def, parent_substs); |
| let substs = builder |
| .fill(|x| { |
| it.next().unwrap_or_else(|| match x { |
| ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner), |
| ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()), |
| ParamKind::Lifetime => error_lifetime().cast(Interner), |
| }) |
| }) |
| .build(); |
| |
| Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) |
| } |
| |
| pub(super) fn resolve_value_path_inner( |
| &mut self, |
| path: &Path, |
| id: ExprOrPatId, |
| no_diagnostics: bool, |
| ) -> Option<(ValueNs, Option<chalk_ir::Substitution<Interner>>)> { |
| // Don't use `self.make_ty()` here as we need `orig_ns`. |
| let mut ctx = TyLoweringContext::new( |
| self.db, |
| &self.resolver, |
| self.body, |
| &self.diagnostics, |
| InferenceTyDiagnosticSource::Body, |
| self.generic_def, |
| LifetimeElisionKind::Infer, |
| ); |
| let mut path_ctx = if no_diagnostics { |
| ctx.at_path_forget_diagnostics(path) |
| } else { |
| ctx.at_path(path, id) |
| }; |
| let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { |
| let last = path.segments().last()?; |
| |
| let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref); |
| let ty = self.table.process_user_written_ty(ty); |
| |
| path_ctx.ignore_last_segment(); |
| let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true); |
| drop_ctx(ctx, no_diagnostics); |
| let ty = self.table.process_user_written_ty(ty); |
| self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))? |
| } else { |
| let hygiene = self.body.expr_or_pat_path_hygiene(id); |
| // FIXME: report error, unresolved first path segment |
| let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?; |
| |
| match value_or_partial { |
| ResolveValueResult::ValueNs(it, _) => { |
| drop_ctx(ctx, no_diagnostics); |
| (it, None) |
| } |
| ResolveValueResult::Partial(def, remaining_index, _) => { |
| // 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 remaining_segments = path.segments().skip(remaining_index); |
| let is_before_last = remaining_segments.len() == 1; |
| let last_segment = remaining_segments |
| .last() |
| .expect("there should be at least one segment here"); |
| |
| let (resolution, substs) = match (def, is_before_last) { |
| (TypeNs::TraitId(trait_), true) => { |
| let self_ty = self.table.new_type_var(); |
| let trait_ref = |
| path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true); |
| drop_ctx(ctx, no_diagnostics); |
| self.resolve_trait_assoc_item(trait_ref, last_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`) |
| path_ctx.ignore_last_segment(); |
| let (ty, _) = path_ctx.lower_partly_resolved_path(def, true); |
| drop_ctx(ctx, no_diagnostics); |
| if ty.is_unknown() { |
| return None; |
| } |
| |
| let ty = self.process_user_written_ty(ty); |
| |
| self.resolve_ty_assoc_item(ty, last_segment.name, id) |
| } |
| }?; |
| (resolution, Some(substs)) |
| } |
| } |
| }; |
| return Some((value, self_subst)); |
| |
| #[inline] |
| fn drop_ctx(mut ctx: TyLoweringContext<'_>, no_diagnostics: bool) { |
| if no_diagnostics { |
| ctx.forget_diagnostics(); |
| } |
| } |
| } |
| |
| fn add_required_obligations_for_value_path(&mut self, def: GenericDefId, subst: &Substitution) { |
| let predicates = self.db.generic_predicates(def); |
| for predicate in predicates.iter() { |
| let (predicate, binders) = |
| predicate.clone().substitute(Interner, &subst).into_value_and_skipped_binders(); |
| // Quantified where clauses are not yet handled. |
| stdx::always!(binders.is_empty(Interner)); |
| self.push_obligation(predicate.cast(Interner)); |
| } |
| |
| // We need to add `Self: Trait` obligation when `def` is a trait assoc item. |
| let container = match def { |
| GenericDefId::FunctionId(id) => id.lookup(self.db).container, |
| GenericDefId::ConstId(id) => id.lookup(self.db).container, |
| _ => return, |
| }; |
| |
| if let ItemContainerId::TraitId(trait_) = container { |
| let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self()); |
| let parent_subst = |
| Substitution::from_iter(Interner, subst.iter(Interner).take(parent_len)); |
| let trait_ref = |
| TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst }; |
| self.push_obligation(trait_ref.cast(Interner)); |
| } |
| } |
| |
| fn resolve_trait_assoc_item( |
| &mut self, |
| trait_ref: TraitRef, |
| segment: PathSegment<'_>, |
| id: ExprOrPatId, |
| ) -> Option<(ValueNs, Substitution)> { |
| let trait_ = trait_ref.hir_trait_id(); |
| let item = |
| trait_.trait_items(self.db).items.iter().map(|(_name, id)| *id).find_map(|item| { |
| match item { |
| AssocItemId::FunctionId(func) => { |
| if segment.name == &self.db.function_signature(func).name { |
| Some(AssocItemId::FunctionId(func)) |
| } else { |
| None |
| } |
| } |
| |
| AssocItemId::ConstId(konst) => { |
| if self.db.const_signature(konst).name.as_ref() == Some(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, trait_ref.substitution.clone()); |
| Some((def, trait_ref.substitution)) |
| } |
| |
| fn resolve_ty_assoc_item( |
| &mut self, |
| ty: Ty, |
| name: &Name, |
| id: ExprOrPatId, |
| ) -> Option<(ValueNs, 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().to_nextsolver(self.table.interner)); |
| |
| let mut not_visible = None; |
| let res = method_resolution::iterate_method_candidates( |
| &canonical_ty, |
| self.db, |
| self.table.trait_env.clone(), |
| self.get_traits_in_scope().as_ref().left_or_else(|&it| it), |
| VisibleFromModule::Filter(self.resolver.module()), |
| Some(name), |
| method_resolution::LookupMode::Path, |
| |_ty, item, visible| { |
| if visible { |
| Some((item, true)) |
| } else { |
| if not_visible.is_none() { |
| not_visible = Some((item, false)); |
| } |
| None |
| } |
| }, |
| ); |
| let res = res.or(not_visible); |
| if res.is_none() { |
| self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id }); |
| } |
| let (item, visible) = res?; |
| |
| let (def, container) = match item { |
| AssocItemId::FunctionId(f) => (ValueNs::FunctionId(f), f.lookup(self.db).container), |
| AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container), |
| AssocItemId::TypeAliasId(_) => unreachable!(), |
| }; |
| let substs = match container { |
| ItemContainerId::ImplId(impl_id) => { |
| let interner = DbInterner::new_with(self.db, None, None); |
| let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None) |
| .fill_with_inference_vars(&mut self.table) |
| .build(); |
| let args: crate::next_solver::GenericArgs<'_> = impl_substs.to_nextsolver(interner); |
| let impl_self_ty = |
| self.db.impl_self_ty(impl_id).instantiate(interner, args).to_chalk(interner); |
| self.unify(&impl_self_ty, &ty); |
| 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)); |
| trait_ref.substitution |
| } |
| ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { |
| never!("assoc item contained in module/extern block"); |
| return None; |
| } |
| }; |
| |
| self.write_assoc_resolution(id, item, substs.clone()); |
| if !visible { |
| self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item }); |
| } |
| Some((def, substs)) |
| } |
| |
| fn resolve_enum_variant_on_ty( |
| &mut self, |
| ty: &Ty, |
| name: &Name, |
| id: ExprOrPatId, |
| ) -> Option<(ValueNs, Substitution)> { |
| let ty = self.table.structurally_resolve_type(ty); |
| let (enum_id, subst) = match ty.as_adt() { |
| Some((AdtId::EnumId(e), subst)) => (e, subst), |
| _ => return None, |
| }; |
| let enum_data = enum_id.enum_variants(self.db); |
| let variant = enum_data.variant(name)?; |
| self.write_variant_resolution(id, variant.into()); |
| Some((ValueNs::EnumVariantId(variant), subst.clone())) |
| } |
| } |
| |
| #[derive(Debug)] |
| enum ValuePathResolution { |
| // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible |
| // conversion between them + `unwrap()`. |
| GenericDef(ValueTyDefId, GenericDefId, Substitution), |
| NonGeneric(Ty), |
| } |