diff --git a/crates/hir-ty/src/builder.rs b/crates/hir-ty/src/builder.rs index 03e9443599..97924569cc 100644 --- a/crates/hir-ty/src/builder.rs +++ b/crates/hir-ty/src/builder.rs @@ -18,7 +18,6 @@ use crate::{ 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 @@ impl TyBuilder> { pub fn impl_self_ty(db: &dyn HirDatabase, def: hir_def::ImplId) -> TyBuilder> { 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, - ) -> TyBuilder> { - 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) - } } diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 65c29b1cc9..4affe7424e 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -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 @@ impl<'a> InferenceContext<'a> { self.table.insert_type_vars_shallow(ty) } - fn insert_type_vars(&mut self, ty: Ty) -> Ty { + fn insert_type_vars(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { 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 /// 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(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { self.table.normalize_associated_types_in(ty) } 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 { - 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 { + fn resolve_value_path(&mut self, path: &Path, id: ExprOrPatId) -> Option { 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), +} diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 2328d97c3b..2988c71039 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -231,7 +231,10 @@ impl<'a> InferenceTable<'a> { /// 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(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { fold_tys( ty, |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(&mut self, ty: T) -> T + where + T: HasInterner + TypeFoldable, + { fold_tys_and_consts( ty, |x, _| match x { diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 857891a214..829a6ab189 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -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); +fn map S>(_: T, _: F) -> U { loop {} } + +fn test(v: S) { + 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); +impl S { + fn foo>>(_: U) -> Self { loop {} } +} +fn map U>(_: T, _: F) -> U { loop {} } + +fn test(v: S) { + let res = map(v, S::foo); + //^^^ S +} +"#, + ); +}