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 | 91 |
1 files changed, 77 insertions, 14 deletions
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 368c3f6524..95a20f983f 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/crates/hir-ty/src/infer/path.rs @@ -4,7 +4,7 @@ use chalk_ir::cast::Cast; use hir_def::{ path::{Path, PathSegment}, resolver::{ResolveValueResult, TypeNs, ValueNs}, - AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup, + AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup, }; use hir_expand::name::Name; use stdx::never; @@ -13,6 +13,7 @@ use crate::{ builder::ParamKind, consteval, method_resolution::{self, VisibleFromModule}, + to_chalk_trait_id, utils::generics, InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind, ValueTyDefId, @@ -20,15 +21,25 @@ use crate::{ use super::{ExprOrPatId, InferenceContext, TraitRef}; -impl<'a> InferenceContext<'a> { +impl InferenceContext<'_> { pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> { - let ty = self.resolve_value_path(path, id)?; - let ty = self.insert_type_vars(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.insert_type_vars(substs); + let substs = self.normalize_associated_types_in(substs); + + self.add_required_obligations_for_value_path(generic_def, &substs); + + let ty = self.db.value_ty(value_def).substitute(Interner, &substs); let ty = self.normalize_associated_types_in(ty); Some(ty) } - fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> { + fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<ValuePathResolution> { let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { let last = path.segments().last()?; @@ -56,9 +67,9 @@ impl<'a> InferenceContext<'a> { } }; - let typable: ValueTyDefId = match value { + let value_def = match value { ValueNs::LocalBinding(pat) => match self.result.type_of_binding.get(pat) { - Some(ty) => return Some(ty.clone()), + Some(ty) => return Some(ValuePathResolution::NonGeneric(ty.clone())), None => { never!("uninferred pattern?"); return None; @@ -82,28 +93,45 @@ impl<'a> InferenceContext<'a> { 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); + return Some(ValuePathResolution::GenericDef( + struct_id.into(), + struct_id.into(), + substs.clone(), + )); } else { // FIXME: report error, invalid Self reference return None; } } - ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)), + ValueNs::GenericParam(it) => { + return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty(it))) + } }; let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); - let substs = ctx.substs_from_path(path, typable, true); + let substs = ctx.substs_from_path(path, value_def, true); let substs = substs.as_slice(Interner); let parent_substs = self_subst.or_else(|| { - let generics = generics(self.db.upcast(), typable.to_generic_def_id()?); + let generics = generics(self.db.upcast(), value_def.to_generic_def_id()?); let parent_params_len = generics.parent_generics()?.len(); let parent_args = &substs[substs.len() - 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().take(substs.len() - parent_substs_len).cloned(); - let ty = TyBuilder::value_ty(self.db, typable, parent_substs) + + let Some(generic_def) = value_def.to_generic_def_id() else { + // `value_def` is the kind of item that can never be generic (i.e. statics, at least + // currently). We can just skip the binders to get its type. + let (ty, binders) = self.db.value_ty(value_def).into_value_and_skipped_binders(); + stdx::always!( + parent_substs.is_none() && binders.is_empty(Interner), + "non-empty binders for non-generic def", + ); + return Some(ValuePathResolution::NonGeneric(ty)); + }; + 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), @@ -111,7 +139,35 @@ impl<'a> InferenceContext<'a> { }) }) .build(); - Some(ty) + + Some(ValuePathResolution::GenericDef(value_def, generic_def, substs)) + } + + 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.upcast()).container, + GenericDefId::ConstId(id) => id.lookup(self.db.upcast()).container, + _ => return, + }; + + if let ItemContainerId::TraitId(trait_) = container { + let param_len = generics(self.db.upcast(), def).len_self(); + let parent_subst = + Substitution::from_iter(Interner, subst.iter(Interner).skip(param_len)); + let trait_ref = + TraitRef { trait_id: to_chalk_trait_id(trait_), substitution: parent_subst }; + self.push_obligation(trait_ref.cast(Interner)); + } } fn resolve_assoc_item( @@ -307,3 +363,10 @@ impl<'a> InferenceContext<'a> { Some((ValueNs::EnumVariantId(variant), subst.clone())) } } + +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), +} |