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.rs112
1 files changed, 91 insertions, 21 deletions
diff --git a/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs
index 2267fedaa8..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,26 +21,43 @@ 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 Some(last) = path.segments().last() else { return None };
- let ty = self.make_ty(type_ref);
- let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
+ let last = path.segments().last()?;
+
+ // Don't use `self.make_ty()` here as we need `orig_ns`.
let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
- let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
+ let (ty, orig_ns) = ctx.lower_ty_ext(type_ref);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
+
+ let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
+ let (ty, _) = ctx.lower_ty_relative_path(ty, orig_ns, remaining_segments_for_ty);
+ let ty = self.table.insert_type_vars(ty);
+ let ty = self.table.normalize_associated_types_in(ty);
self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
} else {
// FIXME: report error, unresolved first path segment
let value_or_partial =
- self.resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
+ self.resolver.resolve_path_in_value_ns(self.db.upcast(), path)?;
match value_or_partial {
ResolveValueResult::ValueNs(it) => (it, None),
@@ -49,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;
@@ -75,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),
@@ -104,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(
@@ -169,7 +232,7 @@ impl<'a> InferenceContext<'a> {
) -> Option<(ValueNs, Substitution)> {
let trait_ = trait_ref.hir_trait_id();
let item =
- self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|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 {
@@ -288,7 +351,7 @@ impl<'a> InferenceContext<'a> {
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Substitution)> {
- let ty = self.resolve_ty_shallow(ty);
+ let ty = self.resolve_ty_shallow(&ty);
let (enum_id, subst) = match ty.as_adt() {
Some((AdtId::EnumId(e), subst)) => (e, subst),
_ => return None,
@@ -300,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),
+}