Register obligations during path inference

This commit is contained in:
Ryo Yoshida 2023-04-24 00:35:04 +09:00
parent c6aea8c2f9
commit 12ba5cab11
No known key found for this signature in database
GPG Key ID: E25698A930586171
5 changed files with 132 additions and 37 deletions

View File

@ -18,7 +18,6 @@
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
ValueTyDefId,
};
#[derive(Debug, Clone, PartialEq, Eq)]
@ -362,21 +361,4 @@ pub fn def_ty(
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<Ty>> {
TyBuilder::subst_for_def(db, def, None).with_data(db.impl_self_ty(def))
}
pub fn value_ty(
db: &dyn HirDatabase,
def: ValueTyDefId,
parent_subst: Option<Substitution>,
) -> TyBuilder<Binders<Ty>> {
let poly_value_ty = db.value_ty(def);
let id = match def.to_generic_def_id() {
Some(id) => id,
None => {
// static items
assert!(parent_subst.is_none());
return TyBuilder::new_empty(poly_value_ty);
}
};
TyBuilder::subst_for_def(db, id, parent_subst).with_data(poly_value_ty)
}
}

View File

@ -16,7 +16,10 @@
use std::sync::Arc;
use std::{convert::identity, ops::Index};
use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags};
use chalk_ir::{
cast::Cast, fold::TypeFoldable, interner::HasInterner, DebruijnIndex, Mutability, Safety,
Scalar, TypeFlags,
};
use either::Either;
use hir_def::{
body::Body,
@ -798,7 +801,10 @@ fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
self.table.insert_type_vars_shallow(ty)
}
fn insert_type_vars(&mut self, ty: Ty) -> Ty {
fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
self.table.insert_type_vars(ty)
}
@ -875,7 +881,10 @@ fn struct_tail_with_normalize(
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
self.table.normalize_associated_types_in(ty)
}

View File

@ -4,7 +4,7 @@
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 @@
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 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 @@ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
}
};
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 @@ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
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 @@ fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
})
})
.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 @@ fn resolve_enum_variant_on_ty(
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),
}

View File

@ -231,7 +231,10 @@ pub(crate) fn canonicalize<T: TypeFoldable<Interner> + HasInterner<Interner = In
/// type annotation (e.g. from a let type annotation, field type or function
/// call). `make_ty` handles this already, but e.g. for field types we need
/// to do it as well.
pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty {
pub(crate) fn normalize_associated_types_in<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
fold_tys(
ty,
|ty, _| match ty.kind(Interner) {
@ -720,7 +723,10 @@ fn callable_sig_from_fn_trait(
}
}
pub(super) fn insert_type_vars(&mut self, ty: Ty) -> Ty {
pub(super) fn insert_type_vars<T>(&mut self, ty: T) -> T
where
T: HasInterner<Interner = Interner> + TypeFoldable<Interner>,
{
fold_tys_and_consts(
ty,
|x, _| match x {

View File

@ -4375,3 +4375,38 @@ fn f() {
"#,
);
}
#[test]
fn trait_obligations_should_be_registered_during_path_inference() {
check_types(
r#"
//- minicore: fn, from
struct S<T>(T);
fn map<T, U, F: FnOnce(T) -> S<U>>(_: T, _: F) -> U { loop {} }
fn test(v: S<i32>) {
let res = map(v, Into::into);
//^^^ i32
}
"#,
);
}
#[test]
fn fn_obligation_should_be_registered_during_path_inference() {
check_types(
r#"
//- minicore: fn, from
struct S<T>(T);
impl<T> S<T> {
fn foo<U: Into<S<T>>>(_: U) -> Self { loop {} }
}
fn map<T, U, F: FnOnce(T) -> U>(_: T, _: F) -> U { loop {} }
fn test(v: S<i32>) {
let res = map(v, S::foo);
//^^^ S<i32>
}
"#,
);
}