From 68586940010137233fb46f6c28d19abdbe291727 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 5 Sep 2021 16:40:46 +0200 Subject: [PATCH 1/4] Add some tests --- crates/hir_ty/src/tests/method_resolution.rs | 82 ++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 9c0c00da3b1..4de58cc1d81 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1460,3 +1460,85 @@ fn main() { "#, ); } + +#[test] +fn deref_fun_1() { + check_types(r#" +//- minicore: deref + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &C { loop {} } +} + +impl C { + fn thing(&self) -> T { self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), make()); + let _: usize = (*a1).0; + a1; + //^^ A, u32> + + let a2 = A(make(), make()); + a2.thing(); + //^^^^^^^^^^ isize + a2; + //^^ A, u32> +} +"#); +} + +#[test] +fn deref_fun_2() { + check_types(r#" +//- minicore: deref + +struct A(T, U); +struct B(T); +struct C(T); + +impl core::ops::Deref for A, u32> { + type Target = B; + fn deref(&self) -> &B { &self.0 } +} +impl core::ops::Deref for B { + type Target = C; + fn deref(&self) -> &C { loop {} } +} + +impl core::ops::Deref for A, i32> { + type Target = C; + fn deref(&self) -> &C { &self.0 } +} + +impl C { + fn thing(&self) -> T { self.0 } +} + +fn make() -> T { loop {} } + +fn test() { + let a1 = A(make(), 1u32); + a1.thing(); + a1; + //^^ A, u32> + + let a2 = A(make(), 1i32); + let _: &str = a2.thing(); + a2; + //^^ A, i32> +} +"#); +} From 6fb5abbc03b67ac49265841e0eaf66752351585d Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Wed, 16 Feb 2022 17:44:03 +0100 Subject: [PATCH 2/4] Refactor autoderef and method resolution - don't return the receiver type from method resolution; instead just return the autorefs/autoderefs that happened and repeat them. This ensures all the effects like trait obligations and whatever we learned about type variables from derefing them are actually applied. Also, it allows us to get rid of `decanonicalize_ty`, which was just wrong in principle. - Autoderef itself now directly works with an inference table. Sadly this has the effect of making it harder to use as an iterator, often requiring manual `while let` loops. (rustc works around this by using inner mutability in the inference context, so that things like unifying types don't require a unique reference.) - We now record the adjustments (autoref/deref) for method receivers and index expressions, which we didn't before. - Removed the redundant crate parameter from method resolution, since the trait_env contains the crate as well. - in the HIR API, the methods now take a scope to determine the trait env. `Type` carries a trait env, but I think that's probably a bad decision because it's easy to create it with the wrong env, e.g. by using `Adt::ty`. This mostly didn't matter so far because `iterate_method_candidates` took a crate parameter and ignored `self.krate`, but the trait env would still have been wrong in those cases, which I think would give some wrong results in some edge cases. Fixes #10058. --- crates/hir/src/lib.rs | 92 +++--- crates/hir/src/semantics.rs | 4 + crates/hir_ty/src/autoderef.rs | 220 +++----------- crates/hir_ty/src/infer.rs | 16 +- crates/hir_ty/src/infer/coerce.rs | 50 ++- crates/hir_ty/src/infer/expr.rs | 267 ++++++---------- crates/hir_ty/src/infer/path.rs | 2 - crates/hir_ty/src/infer/unify.rs | 98 ++++-- crates/hir_ty/src/method_resolution.rs | 287 ++++++++++++------ crates/hir_ty/src/tests.rs | 1 + crates/hir_ty/src/tests/method_resolution.rs | 44 ++- crates/hir_ty/src/tests/traits.rs | 46 +++ crates/hir_ty/src/traits.rs | 3 +- .../handlers/convert_iter_for_each_to_for.rs | 4 +- .../src/handlers/destructure_tuple_binding.rs | 2 +- .../handlers/generate_is_empty_from_len.rs | 3 +- crates/ide_completion/src/completions/dot.rs | 42 ++- .../ide_completion/src/completions/pattern.rs | 60 ++-- .../src/completions/qualified_path.rs | 56 ++-- crates/ide_db/src/helpers/import_assets.rs | 21 +- crates/ide_ssr/src/resolving.rs | 4 +- 21 files changed, 655 insertions(+), 667 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index bd1e8278d51..54f35cf0a6e 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2440,7 +2440,7 @@ impl Impl { #[derive(Clone, PartialEq, Eq, Debug)] pub struct Type { - krate: CrateId, + krate: CrateId, // FIXME this is probably redundant with the TraitEnvironment env: Arc, ty: Ty, } @@ -2533,12 +2533,9 @@ impl Type { /// Checks that particular type `ty` implements `std::future::Future`. /// This function is used in `.await` syntax completion. pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { - // No special case for the type of async block, since Chalk can figure it out. - - let krate = self.krate; - - let std_future_trait = - db.lang_item(krate, SmolStr::new_inline("future_trait")).and_then(|it| it.as_trait()); + let std_future_trait = db + .lang_item(self.krate, SmolStr::new_inline("future_trait")) + .and_then(|it| it.as_trait()); let std_future_trait = match std_future_trait { Some(it) => it, None => return false, @@ -2546,13 +2543,7 @@ impl Type { let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait( - &canonical_ty, - db, - self.env.clone(), - krate, - std_future_trait, - ) + method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. @@ -2560,9 +2551,7 @@ impl Type { /// This function can be used to check if a particular type is callable, since FnOnce is a /// supertrait of Fn and FnMut, so all callable types implements at least FnOnce. pub fn impls_fnonce(&self, db: &dyn HirDatabase) -> bool { - let krate = self.krate; - - let fnonce_trait = match FnTrait::FnOnce.get_id(db, krate) { + let fnonce_trait = match FnTrait::FnOnce.get_id(db, self.krate) { Some(it) => it, None => return false, }; @@ -2573,7 +2562,6 @@ impl Type { &canonical_ty, db, self.env.clone(), - krate, fnonce_trait, ) } @@ -2744,9 +2732,8 @@ impl Type { pub fn autoderef_<'a>(&'a self, db: &'a dyn HirDatabase) -> impl Iterator + 'a { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let environment = self.env.env.clone(); - let ty = InEnvironment { goal: canonical, environment }; - autoderef(db, Some(self.krate), ty).map(|canonical| canonical.value) + let environment = self.env.clone(); + autoderef(db, environment, canonical).map(|canonical| canonical.value) } // This would be nicer if it just returned an iterator, but that runs into @@ -2801,24 +2788,26 @@ impl Type { pub fn iterate_method_candidates( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, + // FIXME this can be retrieved from `scope`, except autoimport uses this + // to specify a different set, so the method needs to be split traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - mut callback: impl FnMut(Type, Function) -> Option, + mut callback: impl FnMut(Function) -> Option, ) -> Option { let _p = profile::span("iterate_method_candidates"); let mut slot = None; self.iterate_method_candidates_dyn( db, - krate, + scope, traits_in_scope, with_local_impls, name, - &mut |ty, assoc_item_id| { + &mut |assoc_item_id| { if let AssocItemId::FunctionId(func) = assoc_item_id { - if let Some(res) = callback(self.derived(ty.clone()), func.into()) { + if let Some(res) = callback(func.into()) { slot = Some(res); return ControlFlow::Break(()); } @@ -2832,50 +2821,55 @@ impl Type { fn iterate_method_candidates_dyn( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, ) { // There should be no inference vars in types passed here let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let env = self.env.clone(); - let krate = krate.id; + let krate = match scope.krate() { + Some(k) => k, + None => return, + }; + let environment = scope.resolver().generic_def().map_or_else( + || Arc::new(TraitEnvironment::empty(krate.id)), + |d| db.trait_environment(d), + ); method_resolution::iterate_method_candidates_dyn( &canonical, db, - env, - krate, + environment, traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, method_resolution::LookupMode::MethodCall, - &mut |ty, id| callback(&ty.value, id), + &mut |_adj, id| callback(id), ); } pub fn iterate_path_candidates( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - mut callback: impl FnMut(Type, AssocItem) -> Option, + mut callback: impl FnMut(AssocItem) -> Option, ) -> Option { let _p = profile::span("iterate_path_candidates"); let mut slot = None; self.iterate_path_candidates_dyn( db, - krate, + scope, traits_in_scope, with_local_impls, name, - &mut |ty, assoc_item_id| { - if let Some(res) = callback(self.derived(ty.clone()), assoc_item_id.into()) { + &mut |assoc_item_id| { + if let Some(res) = callback(assoc_item_id.into()) { slot = Some(res); return ControlFlow::Break(()); } @@ -2888,27 +2882,31 @@ impl Type { fn iterate_path_candidates_dyn( &self, db: &dyn HirDatabase, - krate: Crate, + scope: &SemanticsScope, traits_in_scope: &FxHashSet, with_local_impls: Option, name: Option<&Name>, - callback: &mut dyn FnMut(&Ty, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, ) { let canonical = hir_ty::replace_errors_with_variables(&self.ty); - let env = self.env.clone(); - let krate = krate.id; + let krate = match scope.krate() { + Some(k) => k, + None => return, + }; + let environment = scope.resolver().generic_def().map_or_else( + || Arc::new(TraitEnvironment::empty(krate.id)), + |d| db.trait_environment(d), + ); - method_resolution::iterate_method_candidates_dyn( + method_resolution::iterate_path_candidates( &canonical, db, - env, - krate, + environment, traits_in_scope, with_local_impls.and_then(|b| b.id.containing_block()).into(), name, - method_resolution::LookupMode::Path, - &mut |ty, id| callback(&ty.value, id), + &mut |id| callback(id), ); } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 20c360e302e..2e0dbf82b77 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -1230,6 +1230,10 @@ impl<'a> SemanticsScope<'a> { Some(Crate { id: self.resolver.krate()? }) } + pub(crate) fn resolver(&self) -> &Resolver { + &self.resolver + } + /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type pub fn visible_traits(&self) -> FxHashSet { let resolver = &self.resolver; diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index 6266554ecf6..ea56bfb974b 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -3,20 +3,16 @@ //! reference to a type with the field `bar`. This is an approximation of the //! logic in rustc (which lives in librustc_typeck/check/autoderef.rs). -use std::iter::successors; +use std::sync::Arc; -use base_db::CrateId; -use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, VariableKind}; -use hir_def::lang_item::LangItemTarget; +use chalk_ir::cast::Cast; use hir_expand::name::name; use limit::Limit; use syntax::SmolStr; -use tracing::{info, warn}; use crate::{ - db::HirDatabase, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, CanonicalVarKinds, - ConstrainedSubst, DebruijnIndex, Environment, Guidance, InEnvironment, Interner, - ProjectionTyExt, Solution, Substitution, Ty, TyBuilder, TyKind, + db::HirDatabase, infer::unify::InferenceTable, Canonical, Goal, Interner, ProjectionTyExt, + TraitEnvironment, Ty, TyBuilder, TyKind, }; static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); @@ -26,40 +22,34 @@ pub(crate) enum AutoderefKind { Overloaded, } -pub(crate) struct Autoderef<'db> { - db: &'db dyn HirDatabase, - ty: Canonical, +pub(crate) struct Autoderef<'a, 'db> { + pub table: &'a mut InferenceTable<'db>, + ty: Ty, at_start: bool, - krate: Option, - environment: Environment, steps: Vec<(AutoderefKind, Ty)>, } -impl<'db> Autoderef<'db> { - pub(crate) fn new( - db: &'db dyn HirDatabase, - krate: Option, - ty: InEnvironment>, - ) -> Self { - let InEnvironment { goal: ty, environment } = ty; - Autoderef { db, ty, at_start: true, environment, krate, steps: Vec::new() } +impl<'a, 'db> Autoderef<'a, 'db> { + pub(crate) fn new(table: &'a mut InferenceTable<'db>, ty: Ty) -> Self { + let ty = table.resolve_ty_shallow(&ty); + Autoderef { table, ty, at_start: true, steps: Vec::new() } } pub(crate) fn step_count(&self) -> usize { self.steps.len() } - pub(crate) fn steps(&self) -> &[(AutoderefKind, chalk_ir::Ty)] { + pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { &self.steps } pub(crate) fn final_ty(&self) -> Ty { - self.ty.value.clone() + self.ty.clone() } } -impl Iterator for Autoderef<'_> { - type Item = (Canonical, usize); +impl Iterator for Autoderef<'_, '_> { + type Item = (Ty, usize); fn next(&mut self) -> Option { if self.at_start { @@ -71,54 +61,42 @@ impl Iterator for Autoderef<'_> { return None; } - let (kind, new_ty) = if let Some(derefed) = builtin_deref(&self.ty.value) { - ( - AutoderefKind::Builtin, - Canonical { value: derefed.clone(), binders: self.ty.binders.clone() }, - ) - } else { - ( - AutoderefKind::Overloaded, - deref_by_trait( - self.db, - self.krate?, - InEnvironment { goal: &self.ty, environment: self.environment.clone() }, - )?, - ) - }; + let (kind, new_ty) = autoderef_step(self.table, self.ty.clone())?; - self.steps.push((kind, self.ty.value.clone())); + self.steps.push((kind, self.ty.clone())); self.ty = new_ty; Some((self.ty.clone(), self.step_count())) } } +pub(crate) fn autoderef_step(table: &mut InferenceTable, ty: Ty) -> Option<(AutoderefKind, Ty)> { + if let Some(derefed) = builtin_deref(&ty) { + Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) + } else { + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + } +} + // FIXME: replace uses of this with Autoderef above pub fn autoderef<'a>( db: &'a dyn HirDatabase, - krate: Option, - ty: InEnvironment>, + env: Arc, + ty: Canonical, ) -> impl Iterator> + 'a { - let InEnvironment { goal: ty, environment } = ty; - successors(Some(ty), move |ty| { - deref(db, krate?, InEnvironment { goal: ty, environment: environment.clone() }) - }) - .take(AUTODEREF_RECURSION_LIMIT.inner()) + let mut table = InferenceTable::new(db, env); + let ty = table.instantiate_canonical(ty); + let mut autoderef = Autoderef::new(&mut table, ty); + let mut v = Vec::new(); + while let Some((ty, _steps)) = autoderef.next() { + v.push(autoderef.table.canonicalize(ty).value); + } + v.into_iter() } -pub(crate) fn deref( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment<&Canonical>, -) -> Option> { +pub(crate) fn deref(table: &mut InferenceTable, ty: Ty) -> Option { let _p = profile::span("deref"); - match builtin_deref(&ty.goal.value) { - Some(derefed) => { - Some(Canonical { value: derefed.clone(), binders: ty.goal.binders.clone() }) - } - None => deref_by_trait(db, krate, ty), - } + autoderef_step(table, ty).map(|(_, ty)| ty) } fn builtin_deref(ty: &Ty) -> Option<&Ty> { @@ -129,16 +107,12 @@ fn builtin_deref(ty: &Ty) -> Option<&Ty> { } } -fn deref_by_trait( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment<&Canonical>, -) -> Option> { +fn deref_by_trait(table: &mut InferenceTable, ty: Ty) -> Option { + let db = table.db; let _p = profile::span("deref_by_trait"); - let deref_trait = match db.lang_item(krate, SmolStr::new_inline("deref"))? { - LangItemTarget::TraitId(it) => it, - _ => return None, - }; + let deref_trait = db + .lang_item(table.trait_env.krate, SmolStr::new_inline("deref")) + .and_then(|l| l.as_trait())?; let target = db.trait_data(deref_trait).associated_type_by_name(&name![Target])?; let projection = { @@ -148,114 +122,16 @@ fn deref_by_trait( // namely Deref's Self type return None; } - b.push(ty.goal.value.clone()).build() + b.push(ty).build() }; - // FIXME make the Canonical / bound var handling nicer - // Check that the type implements Deref at all let trait_ref = projection.trait_ref(db); - let implements_goal = Canonical { - binders: ty.goal.binders.clone(), - value: InEnvironment { - goal: trait_ref.cast(Interner), - environment: ty.environment.clone(), - }, - }; - if db.trait_solve(krate, implements_goal).is_none() { - return None; - } + let implements_goal: Goal = trait_ref.cast(Interner); + table.try_obligation(implements_goal.clone())?; - // Now do the assoc type projection - let alias_eq = AliasEq { - alias: AliasTy::Projection(projection), - ty: TyKind::BoundVar(BoundVar::new( - DebruijnIndex::INNERMOST, - ty.goal.binders.len(Interner), - )) - .intern(Interner), - }; + table.register_obligation(implements_goal); - let in_env = InEnvironment { goal: alias_eq.cast(Interner), environment: ty.environment }; - - let canonical = Canonical { - value: in_env, - binders: CanonicalVarKinds::from_iter( - Interner, - ty.goal.binders.iter(Interner).cloned().chain(Some(chalk_ir::WithKind::new( - VariableKind::Ty(chalk_ir::TyVariableKind::General), - chalk_ir::UniverseIndex::ROOT, - ))), - ), - }; - - let solution = db.trait_solve(krate, canonical)?; - - match &solution { - Solution::Unique(Canonical { value: ConstrainedSubst { subst, .. }, binders }) - | Solution::Ambig(Guidance::Definite(Canonical { value: subst, binders })) => { - // FIXME: vars may contain solutions for any inference variables - // that happened to be inside ty. To correctly handle these, we - // would have to pass the solution up to the inference context, but - // that requires a larger refactoring (especially if the deref - // happens during method resolution). So for the moment, we just - // check that we're not in the situation where we would actually - // need to handle the values of the additional variables, i.e. - // they're just being 'passed through'. In the 'standard' case where - // we have `impl Deref for Foo { Target = T }`, that should be - // the case. - - // FIXME: if the trait solver decides to truncate the type, these - // assumptions will be broken. We would need to properly introduce - // new variables in that case - - for i in 1..binders.len(Interner) { - if subst.at(Interner, i - 1).assert_ty_ref(Interner).kind(Interner) - != &TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, i - 1)) - { - warn!("complex solution for derefing {:?}: {:?}, ignoring", ty.goal, solution); - return None; - } - } - // FIXME: we remove lifetime variables here since they can confuse - // the method resolution code later - Some(fixup_lifetime_variables(Canonical { - value: subst.at(Interner, subst.len(Interner) - 1).assert_ty_ref(Interner).clone(), - binders: binders.clone(), - })) - } - Solution::Ambig(_) => { - info!("Ambiguous solution for derefing {:?}: {:?}", ty.goal, solution); - None - } - } -} - -fn fixup_lifetime_variables + HasInterner>( - c: Canonical, -) -> Canonical { - // Removes lifetime variables from the Canonical, replacing them by static lifetimes. - let mut i = 0; - let subst = Substitution::from_iter( - Interner, - c.binders.iter(Interner).map(|vk| match vk.kind { - VariableKind::Ty(_) => { - let index = i; - i += 1; - BoundVar::new(DebruijnIndex::INNERMOST, index).to_ty(Interner).cast(Interner) - } - VariableKind::Lifetime => static_lifetime().cast(Interner), - VariableKind::Const(_) => unimplemented!(), - }), - ); - let binders = CanonicalVarKinds::from_iter( - Interner, - c.binders.iter(Interner).filter(|vk| match vk.kind { - VariableKind::Ty(_) => true, - VariableKind::Lifetime => false, - VariableKind::Const(_) => true, - }), - ); - let value = subst.apply(c.value, Interner); - Canonical { binders, value } + let result = table.normalize_projection_ty(projection); + Some(table.resolve_ty_shallow(&result)) } diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs index 0a50df493fc..c84604d69bf 100644 --- a/crates/hir_ty/src/infer.rs +++ b/crates/hir_ty/src/infer.rs @@ -13,8 +13,8 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. +use std::ops::Index; use std::sync::Arc; -use std::{collections::hash_map::Entry, ops::Index}; use chalk_ir::{cast::Cast, DebruijnIndex, Mutability, Safety, Scalar, TypeFlags}; use hir_def::{ @@ -46,7 +46,7 @@ use crate::{ pub use unify::could_unify; pub(crate) use unify::unify; -mod unify; +pub(crate) mod unify; mod path; mod expr; mod pat; @@ -228,7 +228,7 @@ pub enum Adjust { /// The target type is `U` in both cases, with the region and mutability /// being those shared by both the receiver and the returned reference. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct OverloadedDeref(Mutability); +pub struct OverloadedDeref(pub Mutability); #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum AutoBorrow { @@ -455,16 +455,6 @@ impl<'a> InferenceContext<'a> { self.result.method_resolutions.insert(expr, (func, subst)); } - fn write_field_resolution(&mut self, expr: ExprId, field: FieldId) { - self.result.field_resolutions.insert(expr, field); - } - - fn write_field_resolution_if_empty(&mut self, expr: ExprId, field: FieldId) { - if let Entry::Vacant(entry) = self.result.field_resolutions.entry(expr) { - entry.insert(field); - } - } - fn write_variant_resolution(&mut self, id: ExprOrPatId, variant: VariantId) { self.result.variant_resolutions.insert(id, variant); } diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index bddb79c5015..036941c0df0 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -259,27 +259,17 @@ impl<'a> InferenceContext<'a> { // details of coercion errors though, so I think it's useful to leave // the structure like it is. - let canonicalized = self.canonicalize(from_ty.clone()); - let mut autoderef = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), - }, - ); + let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone()); let mut first_error = None; let mut found = None; - for (referent_ty, autoderefs) in autoderef.by_ref() { + while let Some((referent_ty, autoderefs)) = autoderef.next() { if autoderefs == 0 { // Don't let this pass, otherwise it would cause // &T to autoref to &&T. continue; } - let referent_ty = canonicalized.decanonicalize_ty(&mut self.table, referent_ty); - // At this point, we have deref'd `a` to `referent_ty`. So // imagine we are coercing from `&'a mut Vec` to `&'b mut [T]`. // In the autoderef loop for `&'a mut Vec`, we would get @@ -304,7 +294,7 @@ impl<'a> InferenceContext<'a> { // from `&mut T` to `&U`. let lt = static_lifetime(); // FIXME: handle lifetimes correctly, see rustc let derefd_from_ty = TyKind::Ref(to_mt, lt, referent_ty).intern(Interner); - match self.table.try_unify(&derefd_from_ty, to_ty) { + match autoderef.table.try_unify(&derefd_from_ty, to_ty) { Ok(result) => { found = Some(result.map(|()| derefd_from_ty)); break; @@ -345,29 +335,13 @@ impl<'a> InferenceContext<'a> { return success(vec![], ty, goals); } - let mut adjustments = self.auto_deref_adjust_steps(&autoderef); + let mut adjustments = auto_deref_adjust_steps(&autoderef); adjustments .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(to_mt)), target: ty.clone() }); success(adjustments, ty, goals) } - pub(super) fn auto_deref_adjust_steps(&self, autoderef: &Autoderef<'_>) -> Vec { - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); - steps - .iter() - .map(|(kind, _source)| match kind { - // We do not know what kind of deref we require at this point yet - AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), - AutoderefKind::Builtin => None, - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect() - } - /// Attempts to coerce from the type of a Rust function item into a function pointer. fn coerce_from_fn_item(&mut self, from_ty: Ty, to_ty: &Ty) -> CoerceResult { match to_ty.kind(Interner) { @@ -620,3 +594,19 @@ fn coerce_mutabilities(from: Mutability, to: Mutability) -> Result<(), TypeError (Mutability::Not, Mutability::Mut) => Err(TypeError), } } + +pub(super) fn auto_deref_adjust_steps(autoderef: &Autoderef<'_, '_>) -> Vec { + let steps = autoderef.steps(); + let targets = + steps.iter().skip(1).map(|(_, ty)| ty.clone()).chain(iter::once(autoderef.final_ty())); + steps + .iter() + .map(|(kind, _source)| match kind { + // We do not know what kind of deref we require at this point yet + AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), + AutoderefKind::Builtin => None, + }) + .zip(targets) + .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) + .collect() +} diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index b08a9618d8c..c305af2e912 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -1,6 +1,7 @@ //! Type inference for expressions. use std::{ + collections::hash_map::Entry, iter::{repeat, repeat_with}, mem, sync::Arc, @@ -26,15 +27,14 @@ use crate::{ method_resolution, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, - traits::FnTrait, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, InEnvironment, Interner, - ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + AdtId, Binders, CallableDefId, FnPointer, FnSig, FnSubst, Interner, Rawness, Scalar, + Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, }; use super::{ - find_breakable, BindingMode, BreakableContext, Diverges, Expectation, InferenceContext, - InferenceDiagnostic, TypeMismatch, + coerce::auto_deref_adjust_steps, find_breakable, BindingMode, BreakableContext, Diverges, + Expectation, InferenceContext, InferenceDiagnostic, TypeMismatch, }; impl<'a> InferenceContext<'a> { @@ -77,51 +77,6 @@ impl<'a> InferenceContext<'a> { } } - fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { - let krate = self.resolver.krate()?; - let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; - let output_assoc_type = - self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - - let mut arg_tys = vec![]; - let arg_ty = TyBuilder::tuple(num_args) - .fill(repeat_with(|| { - let arg = self.table.new_type_var(); - arg_tys.push(arg.clone()); - arg - })) - .build(); - - let projection = { - let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type); - if b.remaining() != 2 { - return None; - } - b.push(ty.clone()).push(arg_ty).build() - }; - - let trait_env = self.trait_env.env.clone(); - let obligation = InEnvironment { - goal: projection.trait_ref(self.db).cast(Interner), - environment: trait_env, - }; - let canonical = self.canonicalize(obligation.clone()); - if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { - self.push_obligation(obligation.goal); - let return_ty = self.table.normalize_projection_ty(projection); - Some((arg_tys, return_ty)) - } else { - None - } - } - - pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { - match ty.callable_sig(self.db) { - Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())), - None => self.callable_sig_from_fn_trait(ty, num_args), - } - } - fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { self.db.unwind_if_cancelled(); @@ -319,22 +274,19 @@ impl<'a> InferenceContext<'a> { } Expr::Call { callee, args } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); - let canonicalized = self.canonicalize(callee_ty.clone()); - let mut derefs = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.table.trait_env.env.clone(), - }, - ); - let res = derefs.by_ref().find_map(|(callee_deref_ty, _)| { - let ty = &canonicalized.decanonicalize_ty(&mut self.table, callee_deref_ty); - self.callable_sig(ty, args.len()) - }); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone()); + let mut res = None; + // manual loop to be able to access `derefs.table` + while let Some((callee_deref_ty, _)) = derefs.next() { + res = derefs.table.callable_sig(&callee_deref_ty, args.len()); + if res.is_some() { + break; + } + } let (param_tys, ret_ty): (Vec, Ty) = match res { Some(res) => { - self.write_expr_adj(*callee, self.auto_deref_adjust_steps(&derefs)); + let adjustments = auto_deref_adjust_steps(&derefs); + self.write_expr_adj(*callee, adjustments); res } None => (Vec::new(), self.err_ty()), @@ -489,88 +441,67 @@ impl<'a> InferenceContext<'a> { } Expr::Field { expr, name } => { let receiver_ty = self.infer_expr_inner(*expr, &Expectation::none()); - let canonicalized = self.canonicalize(receiver_ty); - let mut autoderef = Autoderef::new( - self.db, - self.resolver.krate(), - InEnvironment { - goal: canonicalized.value.clone(), - environment: self.trait_env.env.clone(), - }, - ); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty); let ty = autoderef.by_ref().find_map(|(derefed_ty, _)| { - let module = self.resolver.module(); - let db = self.db; - let is_visible = |field_id: &FieldId| { - module - .map(|mod_id| { - db.field_visibilities(field_id.parent)[field_id.local_id] - .is_visible_from(db.upcast(), mod_id) - }) - .unwrap_or(true) - }; - match canonicalized - .decanonicalize_ty(&mut self.table, derefed_ty) - .kind(Interner) - { - TyKind::Tuple(_, substs) => name.as_tuple_index().and_then(|idx| { - substs - .as_slice(Interner) - .get(idx) - .map(|a| a.assert_ty_ref(Interner)) - .cloned() - }), + let (field_id, parameters) = match derefed_ty.kind(Interner) { + TyKind::Tuple(_, substs) => { + return name.as_tuple_index().and_then(|idx| { + substs + .as_slice(Interner) + .get(idx) + .map(|a| a.assert_ty_ref(Interner)) + .cloned() + }); + } TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), parameters) => { let local_id = self.db.struct_data(*s).variant_data.field(name)?; let field = FieldId { parent: (*s).into(), local_id }; - if is_visible(&field) { - self.write_field_resolution(tgt_expr, field); - Some( - self.db.field_types((*s).into())[field.local_id] - .clone() - .substitute(Interner, ¶meters), - ) - } else { - // Write down the first field resolution even if it is not visible - // This aids IDE features for private fields like goto def and in - // case of autoderef finding an applicable field, this will be - // overwritten in a following cycle - self.write_field_resolution_if_empty(tgt_expr, field); - None - } + (field, parameters.clone()) } TyKind::Adt(AdtId(hir_def::AdtId::UnionId(u)), parameters) => { let local_id = self.db.union_data(*u).variant_data.field(name)?; let field = FieldId { parent: (*u).into(), local_id }; - if is_visible(&field) { - self.write_field_resolution(tgt_expr, field); - Some( - self.db.field_types((*u).into())[field.local_id] - .clone() - .substitute(Interner, ¶meters), - ) - } else { - // Write down the first field resolution even if it is not visible - // This aids IDE features for private fields like goto def and in - // case of autoderef finding an applicable field, this will be - // overwritten in a following cycle - self.write_field_resolution_if_empty(tgt_expr, field); - None - } + (field, parameters.clone()) } - _ => None, + _ => return None, + }; + let module = self.resolver.module(); + let is_visible = module + .map(|mod_id| { + self.db.field_visibilities(field_id.parent)[field_id.local_id] + .is_visible_from(self.db.upcast(), mod_id) + }) + .unwrap_or(true); + if !is_visible { + // Write down the first field resolution even if it is not visible + // This aids IDE features for private fields like goto def and in + // case of autoderef finding an applicable field, this will be + // overwritten in a following cycle + if let Entry::Vacant(entry) = self.result.field_resolutions.entry(tgt_expr) + { + entry.insert(field_id); + } + return None; } + // can't have `write_field_resolution` here because `self.table` is borrowed :( + self.result.field_resolutions.insert(tgt_expr, field_id); + let ty = self.db.field_types(field_id.parent)[field_id.local_id] + .clone() + .substitute(Interner, ¶meters); + Some(ty) }); let ty = match ty { Some(ty) => { - self.write_expr_adj(*expr, self.auto_deref_adjust_steps(&autoderef)); + let adjustments = auto_deref_adjust_steps(&autoderef); + self.write_expr_adj(*expr, adjustments); + let ty = self.insert_type_vars(ty); + let ty = self.normalize_associated_types_in(ty); ty } - None => self.err_ty(), + _ => self.err_ty(), }; - let ty = self.insert_type_vars(ty); - self.normalize_associated_types_in(ty) + ty } Expr::Await { expr } => { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); @@ -628,25 +559,9 @@ impl<'a> InferenceContext<'a> { let inner_ty = self.infer_expr_inner(*expr, &Expectation::none()); let inner_ty = self.resolve_ty_shallow(&inner_ty); match op { - UnaryOp::Deref => match self.resolver.krate() { - Some(krate) => { - let canonicalized = self.canonicalize(inner_ty); - match autoderef::deref( - self.db, - krate, - InEnvironment { - goal: &canonicalized.value, - environment: self.trait_env.env.clone(), - }, - ) { - Some(derefed_ty) => { - canonicalized.decanonicalize_ty(&mut self.table, derefed_ty) - } - None => self.err_ty(), - } - } - None => self.err_ty(), - }, + UnaryOp::Deref => { + autoderef::deref(&mut self.table, inner_ty).unwrap_or_else(|| self.err_ty()) + } UnaryOp::Neg => { match inner_ty.kind(Interner) { // Fast path for builtins @@ -732,20 +647,19 @@ impl<'a> InferenceContext<'a> { let base_ty = self.infer_expr_inner(*base, &Expectation::none()); let index_ty = self.infer_expr(*index, &Expectation::none()); - if let (Some(index_trait), Some(krate)) = - (self.resolve_ops_index(), self.resolver.krate()) - { - let canonicalized = self.canonicalize(base_ty); - let self_ty = method_resolution::resolve_indexing_op( + if let Some(index_trait) = self.resolve_ops_index() { + let canonicalized = self.canonicalize(base_ty.clone()); + let receiver_adjustments = method_resolution::resolve_indexing_op( self.db, - &canonicalized.value, self.trait_env.clone(), - krate, + canonicalized.value, index_trait, ); - let self_ty = self_ty.map_or(self.err_ty(), |t| { - canonicalized.decanonicalize_ty(&mut self.table, t) - }); + let (self_ty, adj) = receiver_adjustments + .map_or((self.err_ty(), Vec::new()), |adj| { + adj.apply(&mut self.table, base_ty) + }); + self.write_expr_adj(*base, adj); self.resolve_associated_type_with_params( self_ty, self.resolve_ops_index_output(), @@ -992,22 +906,20 @@ impl<'a> InferenceContext<'a> { let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); - let resolved = self.resolver.krate().and_then(|krate| { - method_resolution::lookup_method( - &canonicalized_receiver.value, - self.db, - self.trait_env.clone(), - krate, - &traits_in_scope, - self.resolver.module().into(), - method_name, - ) - }); + let resolved = method_resolution::lookup_method( + &canonicalized_receiver.value, + self.db, + self.trait_env.clone(), + &traits_in_scope, + self.resolver.module().into(), + method_name, + ); let (receiver_ty, method_ty, substs) = match resolved { - Some((ty, func)) => { - let ty = canonicalized_receiver.decanonicalize_ty(&mut self.table, ty); + Some((adjust, func)) => { + let (ty, adjustments) = adjust.apply(&mut self.table, receiver_ty); let generics = generics(self.db.upcast(), func.into()); - let substs = self.substs_for_method_call(generics, generic_args, &ty); + let substs = self.substs_for_method_call(generics, generic_args); + self.write_expr_adj(receiver, adjustments); self.write_method_resolution(tgt_expr, func, substs.clone()); (ty, self.db.value_ty(func.into()), substs) } @@ -1120,20 +1032,15 @@ impl<'a> InferenceContext<'a> { &mut self, def_generics: Generics, generic_args: Option<&GenericArgs>, - receiver_ty: &Ty, ) -> Substitution { let (parent_params, self_params, type_params, impl_trait_params) = def_generics.provenance_split(); assert_eq!(self_params, 0); // method shouldn't have another Self param let total_len = parent_params + type_params + impl_trait_params; let mut substs = Vec::with_capacity(total_len); - // Parent arguments are unknown, except for the receiver type - for (_id, param) in def_generics.iter_parent() { - if param.provenance == hir_def::generics::TypeParamProvenance::TraitSelf { - substs.push(receiver_ty.clone()); - } else { - substs.push(self.table.new_type_var()); - } + // Parent arguments are unknown + for _ in def_generics.iter_parent() { + substs.push(self.table.new_type_var()); } // handle provided type arguments if let Some(generic_args) = generic_args { diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs index b63ef2ffdc8..0d6c8f12d24 100644 --- a/crates/hir_ty/src/infer/path.rs +++ b/crates/hir_ty/src/infer/path.rs @@ -218,14 +218,12 @@ impl<'a> InferenceContext<'a> { } let canonical_ty = self.canonicalize(ty.clone()); - let krate = self.resolver.krate()?; let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); method_resolution::iterate_method_candidates( &canonical_ty.value, self.db, self.table.trait_env.clone(), - krate, &traits_in_scope, self.resolver.module().into(), Some(name), diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs index bb7cdb677e3..21b48b9d802 100644 --- a/crates/hir_ty/src/infer/unify.rs +++ b/crates/hir_ty/src/infer/unify.rs @@ -1,6 +1,6 @@ //! Unification and canonicalization logic. -use std::{fmt, mem, sync::Arc}; +use std::{fmt, iter, mem, sync::Arc}; use chalk_ir::{ cast::Cast, fold::Fold, interner::HasInterner, zip::Zip, FloatTy, IntTy, NoSolution, @@ -8,12 +8,14 @@ use chalk_ir::{ }; use chalk_solve::infer::ParameterEnaVariableExt; use ena::unify::UnifyKey; +use hir_expand::name; use super::{InferOk, InferResult, InferenceContext, TypeError}; use crate::{ - db::HirDatabase, fold_tys, static_lifetime, AliasEq, AliasTy, BoundVar, Canonical, Const, - DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, - ProjectionTy, Scalar, Solution, Substitution, TraitEnvironment, Ty, TyKind, VariableKind, + db::HirDatabase, fold_tys, static_lifetime, traits::FnTrait, AliasEq, AliasTy, BoundVar, + Canonical, Const, DebruijnIndex, GenericArg, Goal, Guidance, InEnvironment, InferenceVar, + Interner, Lifetime, ProjectionTy, ProjectionTyExt, Scalar, Solution, Substitution, + TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, }; impl<'a> InferenceContext<'a> { @@ -24,32 +26,20 @@ impl<'a> InferenceContext<'a> { where T::Result: HasInterner, { - // try to resolve obligations before canonicalizing, since this might - // result in new knowledge about variables - self.resolve_obligations_as_possible(); self.table.canonicalize(t) } } #[derive(Debug, Clone)] -pub(super) struct Canonicalized +pub(crate) struct Canonicalized where T: HasInterner, { - pub(super) value: Canonical, + pub(crate) value: Canonical, free_vars: Vec, } impl> Canonicalized { - /// this method is wrong and shouldn't exist - pub(super) fn decanonicalize_ty(&self, table: &mut InferenceTable, ty: Canonical) -> Ty { - let mut vars = self.free_vars.clone(); - while ty.binders.len(Interner) > vars.len() { - vars.push(table.new_type_var().cast(Interner)); - } - chalk_ir::Substitute::apply(&vars, ty.value, Interner) - } - pub(super) fn apply_solution( &self, ctx: &mut InferenceTable, @@ -203,13 +193,16 @@ impl<'a> InferenceTable<'a> { .intern(Interner) } - pub(super) fn canonicalize + HasInterner>( + pub(crate) fn canonicalize + HasInterner>( &mut self, t: T, ) -> Canonicalized where T::Result: HasInterner, { + // try to resolve obligations before canonicalizing, since this might + // result in new knowledge about variables + self.resolve_obligations_as_possible(); let result = self.var_unification_table.canonicalize(Interner, t); let free_vars = result .free_vars @@ -225,7 +218,7 @@ 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(super) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { + pub(crate) fn normalize_associated_types_in(&mut self, ty: Ty) -> Ty { fold_tys( ty, |ty, _| match ty.kind(Interner) { @@ -238,7 +231,7 @@ impl<'a> InferenceTable<'a> { ) } - pub(super) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { + pub(crate) fn normalize_projection_ty(&mut self, proj_ty: ProjectionTy) -> Ty { let var = self.new_type_var(); let alias_eq = AliasEq { alias: AliasTy::Projection(proj_ty), ty: var.clone() }; let obligation = alias_eq.cast(Interner); @@ -299,6 +292,13 @@ impl<'a> InferenceTable<'a> { self.resolve_with_fallback_inner(&mut Vec::new(), t, &fallback) } + pub(crate) fn instantiate_canonical(&mut self, canonical: Canonical) -> T::Result + where + T: HasInterner + Fold + std::fmt::Debug, + { + self.var_unification_table.instantiate_canonical(Interner, canonical) + } + fn resolve_with_fallback_inner( &mut self, var_stack: &mut Vec, @@ -351,6 +351,7 @@ impl<'a> InferenceTable<'a> { /// If `ty` is a type variable with known type, returns that type; /// otherwise, return ty. pub(crate) fn resolve_ty_shallow(&mut self, ty: &Ty) -> Ty { + self.resolve_obligations_as_possible(); self.var_unification_table.normalize_ty_shallow(Interner, ty).unwrap_or_else(|| ty.clone()) } @@ -363,6 +364,16 @@ impl<'a> InferenceTable<'a> { self.var_unification_table.rollback_to(snapshot.var_table_snapshot); } + /// Checks an obligation without registering it. Useful mostly to check + /// whether a trait *might* be implemented before deciding to 'lock in' the + /// choice (during e.g. method resolution or deref). + pub(crate) fn try_obligation(&mut self, goal: Goal) -> Option { + let in_env = InEnvironment::new(&self.trait_env.env, goal); + let canonicalized = self.canonicalize(in_env); + let solution = self.db.trait_solve(self.trait_env.krate, canonicalized.value); + solution + } + pub(crate) fn register_obligation(&mut self, goal: Goal) { let in_env = InEnvironment::new(&self.trait_env.env, goal); self.register_obligation_in_env(in_env) @@ -522,6 +533,51 @@ impl<'a> InferenceTable<'a> { } } } + + pub(crate) fn callable_sig(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { + match ty.callable_sig(self.db) { + Some(sig) => Some((sig.params().to_vec(), sig.ret().clone())), + None => self.callable_sig_from_fn_trait(ty, num_args), + } + } + + fn callable_sig_from_fn_trait(&mut self, ty: &Ty, num_args: usize) -> Option<(Vec, Ty)> { + let krate = self.trait_env.krate; + let fn_once_trait = FnTrait::FnOnce.get_id(self.db, krate)?; + let output_assoc_type = + self.db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + + let mut arg_tys = vec![]; + let arg_ty = TyBuilder::tuple(num_args) + .fill(iter::repeat_with(|| { + let arg = self.new_type_var(); + arg_tys.push(arg.clone()); + arg + })) + .build(); + + let projection = { + let b = TyBuilder::assoc_type_projection(self.db, output_assoc_type); + if b.remaining() != 2 { + return None; + } + b.push(ty.clone()).push(arg_ty).build() + }; + + let trait_env = self.trait_env.env.clone(); + let obligation = InEnvironment { + goal: projection.trait_ref(self.db).cast(Interner), + environment: trait_env, + }; + let canonical = self.canonicalize(obligation.clone()); + if self.db.trait_solve(krate, canonical.value.cast(Interner)).is_some() { + self.register_obligation(obligation.goal); + let return_ty = self.normalize_projection_ty(projection); + Some((arg_tys, return_ty)) + } else { + None + } + } } impl<'a> fmt::Debug for InferenceTable<'a> { diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index c91b6f2e82d..44ece57a8e0 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs @@ -17,10 +17,11 @@ use rustc_hash::{FxHashMap, FxHashSet}; use stdx::never; use crate::{ - autoderef, + autoderef::{self, AutoderefKind}, consteval::{self, ConstExt}, db::HirDatabase, from_foreign_def_id, + infer::{unify::InferenceTable, Adjust, Adjustment, AutoBorrow, OverloadedDeref, PointerCast}, primitive::{self, FloatTy, IntTy, UintTy}, static_lifetime, utils::all_super_traits, @@ -429,28 +430,25 @@ pub fn def_crates( Some(res) } -/// Look up the method with the given name, returning the actual autoderefed -/// receiver type (but without autoref applied yet). +/// Look up the method with the given name. pub(crate) fn lookup_method( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: &Name, -) -> Option<(Canonical, FunctionId)> { +) -> Option<(ReceiverAdjustments, FunctionId)> { iterate_method_candidates( ty, db, env, - krate, traits_in_scope, visible_from_module, Some(name), LookupMode::MethodCall, - |ty, f| match f { - AssocItemId::FunctionId(f) => Some((ty.clone(), f)), + |adjustments, f| match f { + AssocItemId::FunctionId(f) => Some((adjustments, f)), _ => None, }, ) @@ -496,33 +494,89 @@ impl From> for VisibleFromModule { } } +#[derive(Debug, Clone, Default)] +pub struct ReceiverAdjustments { + autoref: Option, + autoderefs: usize, + unsize_array: bool, +} + +impl ReceiverAdjustments { + pub(crate) fn apply(&self, table: &mut InferenceTable, ty: Ty) -> (Ty, Vec) { + let mut ty = ty; + let mut adjust = Vec::new(); + for _ in 0..self.autoderefs { + match autoderef::autoderef_step(table, ty.clone()) { + None => { + never!("autoderef not possible for {:?}", ty); + ty = TyKind::Error.intern(Interner); + break; + } + Some((kind, new_ty)) => { + ty = new_ty.clone(); + adjust.push(Adjustment { + kind: Adjust::Deref(match kind { + // FIXME should we know the mutability here? + AutoderefKind::Overloaded => Some(OverloadedDeref(Mutability::Not)), + AutoderefKind::Builtin => None, + }), + target: new_ty, + }); + } + } + } + if self.unsize_array { + ty = match ty.kind(Interner) { + TyKind::Array(inner, _) => TyKind::Slice(inner.clone()).intern(Interner), + _ => { + never!("unsize_array with non-array {:?}", ty); + ty + } + }; + // FIXME this is kind of wrong since the unsize needs to happen to a pointer/reference + adjust.push(Adjustment { + kind: Adjust::Pointer(PointerCast::Unsize), + target: ty.clone(), + }); + } + if let Some(m) = self.autoref { + ty = TyKind::Ref(m, static_lifetime(), ty).intern(Interner); + adjust + .push(Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(m)), target: ty.clone() }); + } + (ty, adjust) + } + + fn with_autoref(&self, m: Mutability) -> ReceiverAdjustments { + Self { autoref: Some(m), ..*self } + } +} + // This would be nicer if it just returned an iterator, but that runs into // lifetime problems, because we need to borrow temp `CrateImplDefs`. // FIXME add a context type here? -pub fn iterate_method_candidates( +pub(crate) fn iterate_method_candidates( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - mut callback: impl FnMut(&Canonical, AssocItemId) -> Option, + mut callback: impl FnMut(ReceiverAdjustments, AssocItemId) -> Option, ) -> Option { let mut slot = None; iterate_method_candidates_dyn( ty, db, env, - krate, traits_in_scope, visible_from_module, name, mode, - &mut |ty, item| { + &mut |adj, item| { assert!(slot.is_none()); - if let Some(it) = callback(ty, item) { + if let Some(it) = callback(adj, item) { slot = Some(it); return ControlFlow::Break(()); } @@ -532,28 +586,45 @@ pub fn iterate_method_candidates( slot } +pub fn iterate_path_candidates( + ty: &Canonical, + db: &dyn HirDatabase, + env: Arc, + traits_in_scope: &FxHashSet, + visible_from_module: VisibleFromModule, + name: Option<&Name>, + callback: &mut dyn FnMut(AssocItemId) -> ControlFlow<()>, +) -> ControlFlow<()> { + iterate_method_candidates_dyn( + ty, + db, + env, + traits_in_scope, + visible_from_module, + name, + LookupMode::Path, + // the adjustments are not relevant for path lookup + &mut |_, id| callback(id), + ) +} + pub fn iterate_method_candidates_dyn( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, mode: LookupMode, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { match mode { LookupMode::MethodCall => { - // For method calls, rust first does any number of autoderef, and then one - // autoref (i.e. when the method takes &self or &mut self). We just ignore - // the autoref currently -- when we find a method matching the given name, - // we assume it fits. - - // Also note that when we've got a receiver like &S, even if the method we - // find in the end takes &self, we still do the autoderef step (just as - // rustc does an autoderef and then autoref again). - let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; + // For method calls, rust first does any number of autoderef, and + // then one autoref (i.e. when the method takes &self or &mut self). + // Note that when we've got a receiver like &S, even if the method + // we find in the end takes &self, we still do the autoderef step + // (just as rustc does an autoderef and then autoref again). // We have to be careful about the order we're looking at candidates // in here. Consider the case where we're resolving `x.clone()` @@ -568,29 +639,31 @@ pub fn iterate_method_candidates_dyn( // the methods by autoderef order of *receiver types*, not *self // types*. - let deref_chain = autoderef_method_receiver(db, krate, ty); - let mut deref_chains = stdx::slice_tails(&deref_chain); + let mut table = InferenceTable::new(db, env.clone()); + let ty = table.instantiate_canonical(ty.clone()); + let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty); + let deref_chains = stdx::slice_tails(&deref_chain); - deref_chains.try_for_each(|deref_chain| { + let result = deref_chains.zip(adj).try_for_each(|(deref_chain, adj)| { iterate_method_candidates_with_autoref( deref_chain, + adj, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, callback, ) - }) + }); + result } LookupMode::Path => { // No autoderef for path lookups iterate_method_candidates_for_self_ty( ty, db, - env, - krate, + env.clone(), traits_in_scope, visible_from_module, name, @@ -602,27 +675,27 @@ pub fn iterate_method_candidates_dyn( fn iterate_method_candidates_with_autoref( deref_chain: &[Canonical], + first_adjustment: ReceiverAdjustments, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { let (receiver_ty, rest) = match deref_chain.split_first() { - Some((rec, rest)) => (rec.clone(), rest), + Some((rec, rest)) => (rec, rest), None => { never!("received empty deref-chain"); return ControlFlow::Break(()); } }; iterate_method_candidates_by_receiver( - &receiver_ty, + receiver_ty, + first_adjustment.clone(), &rest, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, @@ -630,17 +703,17 @@ fn iterate_method_candidates_with_autoref( )?; let refed = Canonical { - binders: receiver_ty.binders.clone(), value: TyKind::Ref(Mutability::Not, static_lifetime(), receiver_ty.value.clone()) .intern(Interner), + binders: receiver_ty.binders.clone(), }; iterate_method_candidates_by_receiver( &refed, + first_adjustment.with_autoref(Mutability::Not), deref_chain, db, env.clone(), - krate, traits_in_scope, visible_from_module, name, @@ -648,16 +721,17 @@ fn iterate_method_candidates_with_autoref( )?; let ref_muted = Canonical { - binders: receiver_ty.binders, - value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value).intern(Interner), + value: TyKind::Ref(Mutability::Mut, static_lifetime(), receiver_ty.value.clone()) + .intern(Interner), + binders: receiver_ty.binders.clone(), }; iterate_method_candidates_by_receiver( &ref_muted, + first_adjustment.with_autoref(Mutability::Mut), deref_chain, db, - env, - krate, + env.clone(), traits_in_scope, visible_from_module, name, @@ -667,14 +741,14 @@ fn iterate_method_candidates_with_autoref( fn iterate_method_candidates_by_receiver( receiver_ty: &Canonical, + receiver_adjustments: ReceiverAdjustments, rest_of_deref_chain: &[Canonical], db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { // We're looking for methods with *receiver* type receiver_ty. These could // be found in any of the derefs of receiver_ty, so we have to go through @@ -686,7 +760,7 @@ fn iterate_method_candidates_by_receiver( env.clone(), name, Some(receiver_ty), - krate, + Some(receiver_adjustments.clone()), visible_from_module, &mut callback, )? @@ -697,10 +771,10 @@ fn iterate_method_candidates_by_receiver( self_ty, db, env.clone(), - krate, traits_in_scope, name, Some(receiver_ty), + Some(receiver_adjustments.clone()), &mut callback, )? } @@ -712,11 +786,10 @@ fn iterate_method_candidates_for_self_ty( self_ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, visible_from_module: VisibleFromModule, name: Option<&Name>, - mut callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { iterate_inherent_methods( self_ty, @@ -724,24 +797,24 @@ fn iterate_method_candidates_for_self_ty( env.clone(), name, None, - krate, + None, visible_from_module, &mut callback, )?; - iterate_trait_method_candidates(self_ty, db, env, krate, traits_in_scope, name, None, callback) + iterate_trait_method_candidates(self_ty, db, env, traits_in_scope, name, None, None, callback) } fn iterate_trait_method_candidates( self_ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, traits_in_scope: &FxHashSet, name: Option<&Name>, receiver_ty: Option<&Canonical>, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + receiver_adjustments: Option, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { - let receiver_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..)); + let self_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..)); // if ty is `dyn Trait`, the trait doesn't need to be in scope let inherent_trait = self_ty.value.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t)); @@ -763,10 +836,10 @@ fn iterate_trait_method_candidates( // 2021. // This is to make `[a].into_iter()` not break code with the new `IntoIterator` impl for // arrays. - if data.skip_array_during_method_dispatch && receiver_is_array { + if data.skip_array_during_method_dispatch && self_is_array { // FIXME: this should really be using the edition of the method name's span, in case it // comes from a macro - if db.crate_graph()[krate].edition < Edition::Edition2021 { + if db.crate_graph()[env.krate].edition < Edition::Edition2021 { continue; } } @@ -782,14 +855,13 @@ fn iterate_trait_method_candidates( continue; } if !known_implemented { - let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); - if db.trait_solve(krate, goal.cast(Interner)).is_none() { + let goal = generic_implements_goal(db, env.clone(), t, self_ty); + if db.trait_solve(env.krate, goal.cast(Interner)).is_none() { continue 'traits; } } known_implemented = true; - // FIXME: we shouldn't be ignoring the binders here - callback(self_ty, item)? + callback(receiver_adjustments.clone().unwrap_or_default(), item)?; } } ControlFlow::Continue(()) @@ -824,11 +896,11 @@ fn iterate_inherent_methods( env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, - krate: CrateId, + receiver_adjustments: Option, visible_from_module: VisibleFromModule, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { - let def_crates = match def_crates(db, &self_ty.value, krate) { + let def_crates = match def_crates(db, &self_ty.value, env.krate) { Some(k) => k, None => return ControlFlow::Continue(()), }; @@ -848,6 +920,7 @@ fn iterate_inherent_methods( env.clone(), name, receiver_ty, + receiver_adjustments.clone(), module, callback, )?; @@ -856,7 +929,17 @@ fn iterate_inherent_methods( for krate in def_crates { let impls = db.inherent_impls_in_crate(krate); - impls_for_self_ty(&impls, self_ty, db, env.clone(), name, receiver_ty, module, callback)?; + impls_for_self_ty( + &impls, + self_ty, + db, + env.clone(), + name, + receiver_ty, + receiver_adjustments.clone(), + module, + callback, + )?; } return ControlFlow::Continue(()); @@ -867,8 +950,9 @@ fn iterate_inherent_methods( env: Arc, name: Option<&Name>, receiver_ty: Option<&Canonical>, + receiver_adjustments: Option, visible_from_module: Option, - callback: &mut dyn FnMut(&Canonical, AssocItemId) -> ControlFlow<()>, + callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>, ) -> ControlFlow<()> { let impls_for_self_ty = filter_inherent_impls_for_self_ty(impls, &self_ty.value); for &impl_def in impls_for_self_ty { @@ -889,33 +973,32 @@ fn iterate_inherent_methods( // already happens in `is_valid_candidate` above; if not, we // check it here if receiver_ty.is_none() - && inherent_impl_substs(db, env.clone(), impl_def, self_ty).is_none() + && inherent_impl_substs(db, env.clone(), impl_def, &self_ty).is_none() { cov_mark::hit!(impl_self_type_match_without_receiver); continue; } - let receiver_ty = receiver_ty.unwrap_or(self_ty); - callback(receiver_ty, item)?; + callback(receiver_adjustments.clone().unwrap_or_default(), item)?; } } ControlFlow::Continue(()) } } -/// Returns the self type for the index trait call. +/// Returns the receiver type for the index trait call. pub fn resolve_indexing_op( db: &dyn HirDatabase, - ty: &Canonical, env: Arc, - krate: CrateId, + ty: Canonical, index_trait: TraitId, -) -> Option> { - let ty = InEnvironment { goal: ty.clone(), environment: env.env.clone() }; - let deref_chain = autoderef_method_receiver(db, krate, ty); - for ty in deref_chain { - let goal = generic_implements_goal(db, env.clone(), index_trait, ty.clone()); - if db.trait_solve(krate, goal.cast(Interner)).is_some() { - return Some(ty); +) -> Option { + let mut table = InferenceTable::new(db, env.clone()); + let ty = table.instantiate_canonical(ty); + let (deref_chain, adj) = autoderef_method_receiver(&mut table, ty); + for (ty, adj) in deref_chain.into_iter().zip(adj) { + let goal = generic_implements_goal(db, env.clone(), index_trait, &ty); + if db.trait_solve(env.krate, goal.cast(Interner)).is_some() { + return Some(adj); } } None @@ -1067,11 +1150,10 @@ pub fn implements_trait( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal.cast(Interner)); + let goal = generic_implements_goal(db, env.clone(), trait_, &ty); + let solution = db.trait_solve(env.krate, goal.cast(Interner)); solution.is_some() } @@ -1080,11 +1162,10 @@ pub fn implements_trait_unique( ty: &Canonical, db: &dyn HirDatabase, env: Arc, - krate: CrateId, trait_: TraitId, ) -> bool { - let goal = generic_implements_goal(db, env, trait_, ty.clone()); - let solution = db.trait_solve(krate, goal.cast(Interner)); + let goal = generic_implements_goal(db, env.clone(), trait_, &ty); + let solution = db.trait_solve(env.krate, goal.cast(Interner)); matches!(solution, Some(crate::Solution::Unique(_))) } @@ -1095,11 +1176,11 @@ fn generic_implements_goal( db: &dyn HirDatabase, env: Arc, trait_: TraitId, - self_ty: Canonical, + self_ty: &Canonical, ) -> Canonical> { let mut kinds = self_ty.binders.interned().to_vec(); let trait_ref = TyBuilder::trait_ref(db, trait_) - .push(self_ty.value) + .push(self_ty.value.clone()) .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) .build(); kinds.extend( @@ -1117,17 +1198,27 @@ fn generic_implements_goal( } fn autoderef_method_receiver( - db: &dyn HirDatabase, - krate: CrateId, - ty: InEnvironment>, -) -> Vec> { - let mut deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty).collect(); - // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) - if let Some(TyKind::Array(parameters, _)) = deref_chain.last().map(|ty| ty.value.kind(Interner)) - { - let kinds = deref_chain.last().unwrap().binders.clone(); - let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); - deref_chain.push(Canonical { value: unsized_ty, binders: kinds }) + table: &mut InferenceTable, + ty: Ty, +) -> (Vec>, Vec) { + let (mut deref_chain, mut adjustments): (Vec<_>, Vec<_>) = (Vec::new(), Vec::new()); + let mut autoderef = autoderef::Autoderef::new(table, ty); + while let Some((ty, derefs)) = autoderef.next() { + deref_chain.push(autoderef.table.canonicalize(ty).value); + adjustments.push(ReceiverAdjustments { + autoref: None, + autoderefs: derefs, + unsize_array: false, + }); } - deref_chain + // As a last step, we can do array unsizing (that's the only unsizing that rustc does for method receivers!) + if let (Some((TyKind::Array(parameters, _), binders)), Some(adj)) = ( + deref_chain.last().map(|ty| (ty.value.kind(Interner), ty.binders.clone())), + adjustments.last().cloned(), + ) { + let unsized_ty = TyKind::Slice(parameters.clone()).intern(Interner); + deref_chain.push(Canonical { value: unsized_ty, binders }); + adjustments.push(ReceiverAdjustments { unsize_array: true, ..adj }); + } + (deref_chain, adjustments) } diff --git a/crates/hir_ty/src/tests.rs b/crates/hir_ty/src/tests.rs index 29250dca00c..7385da56622 100644 --- a/crates/hir_ty/src/tests.rs +++ b/crates/hir_ty/src/tests.rs @@ -100,6 +100,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour .trim_start_matches("adjustments: ") .split(',') .map(|it| it.trim().to_string()) + .filter(|it| !it.is_empty()) .collect(), ); } else { diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 4de58cc1d81..c118ae24cfe 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs @@ -1463,7 +1463,8 @@ fn main() { #[test] fn deref_fun_1() { - check_types(r#" + check_types( + r#" //- minicore: deref struct A(T, U); @@ -1497,12 +1498,14 @@ fn test() { a2; //^^ A, u32> } -"#); +"#, + ); } #[test] fn deref_fun_2() { - check_types(r#" + check_types( + r#" //- minicore: deref struct A(T, U); @@ -1540,5 +1543,38 @@ fn test() { a2; //^^ A, i32> } -"#); +"#, + ); +} + +#[test] +fn receiver_adjustment_autoref() { + check( + r#" +struct Foo; +impl Foo { + fn foo(&self) {} +} +fn test() { + Foo.foo(); + //^^^ adjustments: Borrow(Ref(Not)) + (&Foo).foo(); + // ^^^^ adjustments: , +} +"#, + ); +} + +#[test] +fn receiver_adjustment_unsize_array() { + // FIXME not quite correct + check( + r#" +//- minicore: slice +fn test() { + let a = [1, 2, 3]; + a.len(); +} //^ adjustments: Pointer(Unsize), Borrow(Ref(Not)) +"#, + ); } diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs index c2669646e22..04d8b91e359 100644 --- a/crates/hir_ty/src/tests/traits.rs +++ b/crates/hir_ty/src/tests/traits.rs @@ -540,6 +540,52 @@ fn test() { ); } +#[test] +fn infer_ops_index_field() { + check_types( + r#" +//- minicore: index +struct Bar; +struct Foo { + field: u32; +} + +impl core::ops::Index for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = a[1u32].field; + b; +} //^ u32 +"#, + ); +} + +#[test] +fn infer_ops_index_field_autoderef() { + check_types( + r#" +//- minicore: index +struct Bar; +struct Foo { + field: u32; +} + +impl core::ops::Index for Bar { + type Output = Foo; +} + +fn test() { + let a = Bar; + let b = (&a[1u32]).field; + b; +} //^ u32 +"#, + ); +} + #[test] fn infer_ops_index_int() { check_types( diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index bd280ba774d..b139edbee94 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs @@ -40,8 +40,7 @@ fn create_chalk_solver() -> chalk_recursive::RecursiveSolver { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct TraitEnvironment { pub krate: CrateId, - // When we're using Chalk's Ty we can make this a BTreeMap since it's Ord, - // but for now it's too annoying... + // FIXME make this a BTreeMap pub(crate) traits_from_clauses: Vec<(Ty, TraitId)>, pub env: chalk_ir::Environment, } diff --git a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs index 0d2daa8dc30..4a4ad984db1 100644 --- a/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs @@ -154,11 +154,11 @@ fn is_ref_and_impls_iter_method( let has_wanted_method = ty .iterate_method_candidates( sema.db, - krate, + &scope, &traits_in_scope, None, Some(&wanted_method), - |_, func| { + |func| { if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) { return Some(()); } diff --git a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs index 5f361f01dc1..ac17e3f057a 100644 --- a/crates/ide_assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide_assists/src/handlers/destructure_tuple_binding.rs @@ -371,7 +371,7 @@ fn handle_ref_field_usage(ctx: &AssistContext, field_expr: &FieldExpr) -> RefDat fn is_auto_ref(ctx: &AssistContext, call_expr: &MethodCallExpr) -> bool { fn impl_(ctx: &AssistContext, call_expr: &MethodCallExpr) -> Option { let rec = call_expr.receiver()?; - let rec_ty = ctx.sema.type_of_expr(&rec)?.adjusted(); + let rec_ty = ctx.sema.type_of_expr(&rec)?.original(); // input must be actual value if rec_ty.is_reference() { return Some(false); diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs index cb3fbed2199..db0cfbe2ec0 100644 --- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs +++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs @@ -90,10 +90,9 @@ fn get_impl_method( let impl_def: hir::Impl = ctx.sema.to_def(impl_)?; let scope = ctx.sema.scope(impl_.syntax()); - let krate = impl_def.module(db).krate(); let ty = impl_def.self_ty(db); let traits_in_scope = scope.visible_traits(); - ty.iterate_method_candidates(db, krate, &traits_in_scope, None, Some(fn_name), |_, func| { + ty.iterate_method_candidates(db, &scope, &traits_in_scope, None, Some(fn_name), |func| { Some(func) }) } diff --git a/crates/ide_completion/src/completions/dot.rs b/crates/ide_completion/src/completions/dot.rs index 3bb3f883cdb..981e25b55c7 100644 --- a/crates/ide_completion/src/completions/dot.rs +++ b/crates/ide_completion/src/completions/dot.rs @@ -74,30 +74,28 @@ fn complete_methods( receiver: &hir::Type, mut f: impl FnMut(hir::Function), ) { - if let Some(krate) = ctx.krate { - let mut seen_methods = FxHashSet::default(); - let mut traits_in_scope = ctx.scope.visible_traits(); + let mut seen_methods = FxHashSet::default(); + let mut traits_in_scope = ctx.scope.visible_traits(); - // Remove drop from the environment as calling `Drop::drop` is not allowed - if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { - cov_mark::hit!(dot_remove_drop_trait); - traits_in_scope.remove(&drop_trait.into()); - } - - receiver.iterate_method_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, func| { - if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { - f(func); - } - None::<()> - }, - ); + // Remove drop from the environment as calling `Drop::drop` is not allowed + if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { + cov_mark::hit!(dot_remove_drop_trait); + traits_in_scope.remove(&drop_trait.into()); } + + receiver.iterate_method_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |func| { + if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) { + f(func); + } + None::<()> + }, + ); } #[cfg(test)] diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs index f1b4fa72059..c8a9cf21da1 100644 --- a/crates/ide_completion/src/completions/pattern.rs +++ b/crates/ide_completion/src/completions/pattern.rs @@ -134,39 +134,37 @@ fn pattern_path_completion( .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); } res @ (hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_)) => { - if let Some(krate) = ctx.krate { - let ty = match res { - hir::PathResolution::TypeParam(param) => param.ty(ctx.db), - hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), - _ => return, - }; + let ty = match res { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - e.variants(ctx.db) - .into_iter() - .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); - } - - let traits_in_scope = ctx.scope.visible_traits(); - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, item| { - // Note associated consts cannot be referenced in patterns - if let AssocItem::TypeAlias(ta) = item { - // We might iterate candidates of a trait multiple times here, so deduplicate them. - if seen.insert(item) { - acc.add_type_alias(ctx, ta); - } - } - None::<()> - }, - ); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + e.variants(ctx.db) + .into_iter() + .for_each(|variant| acc.add_enum_variant(ctx, variant, None)); } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |item| { + // Note associated consts cannot be referenced in patterns + if let AssocItem::TypeAlias(ta) = item { + // We might iterate candidates of a trait multiple times here, so deduplicate them. + if seen.insert(item) { + acc.add_type_alias(ctx, ta); + } + } + None::<()> + }, + ); } _ => {} } diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs index cf78f7c1adf..d63aacbadbc 100644 --- a/crates/ide_completion/src/completions/qualified_path.rs +++ b/crates/ide_completion/src/completions/qualified_path.rs @@ -138,11 +138,11 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon let traits_in_scope = ctx.scope.visible_traits(); ty.iterate_path_candidates( ctx.db, - krate, + &ctx.scope, &traits_in_scope, ctx.module, None, - |_ty, item| { + |item| { add_assoc_item(acc, ctx, item); None::<()> }, @@ -164,35 +164,33 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon } } hir::PathResolution::TypeParam(_) | hir::PathResolution::SelfType(_) => { - if let Some(krate) = ctx.krate { - let ty = match resolution { - hir::PathResolution::TypeParam(param) => param.ty(ctx.db), - hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), - _ => return, - }; + let ty = match resolution { + hir::PathResolution::TypeParam(param) => param.ty(ctx.db), + hir::PathResolution::SelfType(impl_def) => impl_def.self_ty(ctx.db), + _ => return, + }; - if let Some(hir::Adt::Enum(e)) = ty.as_adt() { - add_enum_variants(acc, ctx, e); - } - - let traits_in_scope = ctx.scope.visible_traits(); - let mut seen = FxHashSet::default(); - ty.iterate_path_candidates( - ctx.db, - krate, - &traits_in_scope, - ctx.module, - None, - |_ty, item| { - // We might iterate candidates of a trait multiple times here, so deduplicate - // them. - if seen.insert(item) { - add_assoc_item(acc, ctx, item); - } - None::<()> - }, - ); + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + add_enum_variants(acc, ctx, e); } + + let traits_in_scope = ctx.scope.visible_traits(); + let mut seen = FxHashSet::default(); + ty.iterate_path_candidates( + ctx.db, + &ctx.scope, + &traits_in_scope, + ctx.module, + None, + |item| { + // We might iterate candidates of a trait multiple times here, so deduplicate + // them. + if seen.insert(item) { + add_assoc_item(acc, ctx, item); + } + None::<()> + }, + ); } _ => {} } diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 319a2173529..d91627e0a92 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs @@ -1,7 +1,7 @@ //! Look up accessible paths for items. use hir::{ AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, MacroDef, ModPath, Module, - ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, Type, + ModuleDef, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type, }; use itertools::Itertools; use rustc_hash::FxHashSet; @@ -239,7 +239,6 @@ impl ImportAssets { let _p = profile::span("import_assets::search_for"); let scope_definitions = self.scope_definitions(sema); - let current_crate = self.module_with_candidate.krate(); let mod_path = |item| { get_mod_path( sema.db, @@ -249,15 +248,18 @@ impl ImportAssets { ) }; + let krate = self.module_with_candidate.krate(); + let scope = sema.scope(&self.candidate_node); + match &self.import_candidate { ImportCandidate::Path(path_candidate) => { - path_applicable_imports(sema, current_crate, path_candidate, mod_path) + path_applicable_imports(sema, krate, path_candidate, mod_path) } ImportCandidate::TraitAssocItem(trait_candidate) => { - trait_applicable_items(sema, current_crate, trait_candidate, true, mod_path) + trait_applicable_items(sema, krate, &scope, trait_candidate, true, mod_path) } ImportCandidate::TraitMethod(trait_candidate) => { - trait_applicable_items(sema, current_crate, trait_candidate, false, mod_path) + trait_applicable_items(sema, krate, &scope, trait_candidate, false, mod_path) } } .into_iter() @@ -447,6 +449,7 @@ fn module_with_segment_name( fn trait_applicable_items( sema: &Semantics, current_crate: Crate, + scope: &SemanticsScope, trait_candidate: &TraitImportCandidate, trait_assoc_item: bool, mod_path: impl Fn(ItemInNs) -> Option, @@ -484,11 +487,11 @@ fn trait_applicable_items( if trait_assoc_item { trait_candidate.receiver_ty.iterate_path_candidates( db, - current_crate, + scope, &trait_candidates, None, None, - |_, assoc| { + |assoc| { if required_assoc_items.contains(&assoc) { if let AssocItem::Function(f) = assoc { if f.self_param(db).is_some() { @@ -511,11 +514,11 @@ fn trait_applicable_items( } else { trait_candidate.receiver_ty.iterate_method_candidates( db, - current_crate, + scope, &trait_candidates, None, None, - |_, function| { + |function| { let assoc = function.as_assoc_item(db)?; if required_assoc_items.contains(&assoc) { let located_trait = assoc.containing_trait(db)?; diff --git a/crates/ide_ssr/src/resolving.rs b/crates/ide_ssr/src/resolving.rs index 844b19779a8..e15cb874efb 100644 --- a/crates/ide_ssr/src/resolving.rs +++ b/crates/ide_ssr/src/resolving.rs @@ -222,11 +222,11 @@ impl<'db> ResolutionScope<'db> { let module = self.scope.module()?; adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, - module.krate(), + &self.scope, &self.scope.visible_traits(), Some(module), None, - |_ty, assoc_item| { + |assoc_item| { let item_name = assoc_item.name(self.scope.db)?; if item_name.to_smol_str().as_str() == name.text() { Some(hir::PathResolution::AssocItem(assoc_item)) From 187e6bacac2c01b9bc28f8c0480d807434abd4f7 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 25 Feb 2022 12:09:49 +0100 Subject: [PATCH 3/4] Fix unreachable pub --- crates/hir_ty/src/autoderef.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/hir_ty/src/autoderef.rs b/crates/hir_ty/src/autoderef.rs index ea56bfb974b..dffb36b5de3 100644 --- a/crates/hir_ty/src/autoderef.rs +++ b/crates/hir_ty/src/autoderef.rs @@ -23,7 +23,7 @@ pub(crate) enum AutoderefKind { } pub(crate) struct Autoderef<'a, 'db> { - pub table: &'a mut InferenceTable<'db>, + pub(crate) table: &'a mut InferenceTable<'db>, ty: Ty, at_start: bool, steps: Vec<(AutoderefKind, Ty)>, From 6fc3d3aa4cbf637d748c60cfdbb98b5f6a040576 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 25 Feb 2022 15:46:02 +0100 Subject: [PATCH 4/4] Avoid autoderef coercions leaking if they don't apply --- crates/hir_ty/src/infer/coerce.rs | 3 +++ crates/hir_ty/src/tests/coercion.rs | 39 +++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/hir_ty/src/infer/coerce.rs b/crates/hir_ty/src/infer/coerce.rs index 036941c0df0..c24772f29b4 100644 --- a/crates/hir_ty/src/infer/coerce.rs +++ b/crates/hir_ty/src/infer/coerce.rs @@ -259,6 +259,8 @@ impl<'a> InferenceContext<'a> { // details of coercion errors though, so I think it's useful to leave // the structure like it is. + let snapshot = self.table.snapshot(); + let mut autoderef = Autoderef::new(&mut self.table, from_ty.clone()); let mut first_error = None; let mut found = None; @@ -315,6 +317,7 @@ impl<'a> InferenceContext<'a> { let InferOk { value: ty, goals } = match found { Some(d) => d, None => { + self.table.rollback_to(snapshot); let err = first_error.expect("coerce_borrowed_pointer had no error"); return Err(err); } diff --git a/crates/hir_ty/src/tests/coercion.rs b/crates/hir_ty/src/tests/coercion.rs index dd3b86f0503..c0dddb608ea 100644 --- a/crates/hir_ty/src/tests/coercion.rs +++ b/crates/hir_ty/src/tests/coercion.rs @@ -242,6 +242,45 @@ fn test() { ); } +#[test] +fn coerce_autoderef_implication_1() { + check_no_mismatches( + r" +//- minicore: deref +struct Foo; +impl core::ops::Deref for Foo { type Target = (); } + +fn takes_ref_foo(x: &Foo) {} +fn test() { + let foo = Foo; + //^^^ type: Foo<{unknown}> + takes_ref_foo(&foo); + + let foo = Foo; + //^^^ type: Foo + let _: &() = &foo; +}", + ); +} + +#[test] +fn coerce_autoderef_implication_2() { + check( + r" +//- minicore: deref +struct Foo; +impl core::ops::Deref for Foo { type Target = (); } + +fn takes_ref_foo(x: &Foo) {} +fn test() { + let foo = Foo; + //^^^ type: Foo<{unknown}> + let _: &u32 = &Foo; + //^^^^ expected &u32, got &Foo<{unknown}> +}", + ); +} + #[test] fn closure_return_coerce() { check_no_mismatches(