mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 14:21:44 +00:00
Register obligations during path inference
This commit is contained in:
parent
c6aea8c2f9
commit
12ba5cab11
5 changed files with 132 additions and 37 deletions
|
@ -18,7 +18,6 @@ use crate::{
|
||||||
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
|
consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
|
||||||
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
|
to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, BoundVar, CallableSig,
|
||||||
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
|
GenericArg, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
|
||||||
ValueTyDefId,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -362,21 +361,4 @@ impl TyBuilder<Binders<Ty>> {
|
||||||
pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder<Binders<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))
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,10 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{convert::identity, ops::Index};
|
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 either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::Body,
|
body::Body,
|
||||||
|
@ -798,7 +801,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.table.insert_type_vars_shallow(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)
|
self.table.insert_type_vars(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,7 +881,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
/// type annotation (e.g. from a let type annotation, field type or function
|
/// 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
|
/// call). `make_ty` handles this already, but e.g. for field types we need
|
||||||
/// to do it as well.
|
/// 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)
|
self.table.normalize_associated_types_in(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use chalk_ir::cast::Cast;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
path::{Path, PathSegment},
|
path::{Path, PathSegment},
|
||||||
resolver::{ResolveValueResult, TypeNs, ValueNs},
|
resolver::{ResolveValueResult, TypeNs, ValueNs},
|
||||||
AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
|
AdtId, AssocItemId, EnumVariantId, GenericDefId, ItemContainerId, Lookup,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
@ -13,6 +13,7 @@ use crate::{
|
||||||
builder::ParamKind,
|
builder::ParamKind,
|
||||||
consteval,
|
consteval,
|
||||||
method_resolution::{self, VisibleFromModule},
|
method_resolution::{self, VisibleFromModule},
|
||||||
|
to_chalk_trait_id,
|
||||||
utils::generics,
|
utils::generics,
|
||||||
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
|
||||||
ValueTyDefId,
|
ValueTyDefId,
|
||||||
|
@ -20,15 +21,25 @@ use crate::{
|
||||||
|
|
||||||
use super::{ExprOrPatId, InferenceContext, TraitRef};
|
use super::{ExprOrPatId, InferenceContext, TraitRef};
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl InferenceContext<'_> {
|
||||||
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
|
pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty> {
|
||||||
let ty = self.resolve_value_path(path, id)?;
|
let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
|
||||||
let ty = self.insert_type_vars(ty);
|
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);
|
let ty = self.normalize_associated_types_in(ty);
|
||||||
Some(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 (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
|
||||||
let last = path.segments().last()?;
|
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) {
|
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 => {
|
None => {
|
||||||
never!("uninferred pattern?");
|
never!("uninferred pattern?");
|
||||||
return None;
|
return None;
|
||||||
|
@ -82,28 +93,45 @@ impl<'a> InferenceContext<'a> {
|
||||||
let substs = generics.placeholder_subst(self.db);
|
let substs = generics.placeholder_subst(self.db);
|
||||||
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
|
||||||
if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
|
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(ValuePathResolution::GenericDef(
|
||||||
return Some(ty);
|
struct_id.into(),
|
||||||
|
struct_id.into(),
|
||||||
|
substs.clone(),
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
// FIXME: report error, invalid Self reference
|
// FIXME: report error, invalid Self reference
|
||||||
return None;
|
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 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 substs = substs.as_slice(Interner);
|
||||||
let parent_substs = self_subst.or_else(|| {
|
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_params_len = generics.parent_generics()?.len();
|
||||||
let parent_args = &substs[substs.len() - parent_params_len..];
|
let parent_args = &substs[substs.len() - parent_params_len..];
|
||||||
Some(Substitution::from_iter(Interner, parent_args))
|
Some(Substitution::from_iter(Interner, parent_args))
|
||||||
});
|
});
|
||||||
let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
|
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 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| {
|
.fill(|x| {
|
||||||
it.next().unwrap_or_else(|| match x {
|
it.next().unwrap_or_else(|| match x {
|
||||||
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
|
ParamKind::Type => self.result.standard_types.unknown.clone().cast(Interner),
|
||||||
|
@ -111,7 +139,35 @@ impl<'a> InferenceContext<'a> {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.build();
|
.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(
|
fn resolve_assoc_item(
|
||||||
|
@ -307,3 +363,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
Some((ValueNs::EnumVariantId(variant), subst.clone()))
|
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),
|
||||||
|
}
|
||||||
|
|
|
@ -231,7 +231,10 @@ impl<'a> InferenceTable<'a> {
|
||||||
/// type annotation (e.g. from a let type annotation, field type or function
|
/// 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
|
/// call). `make_ty` handles this already, but e.g. for field types we need
|
||||||
/// to do it as well.
|
/// 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(
|
fold_tys(
|
||||||
ty,
|
ty,
|
||||||
|ty, _| match ty.kind(Interner) {
|
|ty, _| match ty.kind(Interner) {
|
||||||
|
@ -720,7 +723,10 @@ impl<'a> InferenceTable<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
fold_tys_and_consts(
|
||||||
ty,
|
ty,
|
||||||
|x, _| match x {
|
|x, _| match x {
|
||||||
|
|
|
@ -4375,3 +4375,38 @@ fn derive_macro_bounds() {
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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>
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue