Repository: rust-analyzer/rust-analyzer
Pull request #11743 (2022-03-17 16:19:15)

fix: Properly unify receivers during method resolution
This fixes our type inference problems with `DynMap`; the problem there were the projection types in
```rust
impl<P: Policy> KeyMap<Key<P::K, P::V, P>>
```
which messed up the naive type equality check we did. It also actually simplifies the logic though, IMO. I also added a fix for associated const resolution that I noticed (visibility not being taken into account).


Patch:
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index ee4ff0aebb..8121b27dcb 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1541,9 +1541,7 @@ impl SelfParam {
 
 impl HasVisibility for Function {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        let function_data = db.function_data(self.id);
-        let visibility = &function_data.visibility;
-        visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
+        db.function_visibility(self.id)
     }
 }
 
@@ -1594,9 +1592,7 @@ impl Const {
 
 impl HasVisibility for Const {
     fn visibility(&self, db: &dyn HirDatabase) -> Visibility {
-        let function_data = db.const_data(self.id);
-        let visibility = &function_data.visibility;
-        visibility.resolve(db.upcast(), &self.id.resolver(db.upcast()))
+        db.const_visibility(self.id)
     }
 }
 
diff --git a/crates/hir_def/src/db.rs b/crates/hir_def/src/db.rs
index 934d13c067..74bb7472d5 100644
--- a/crates/hir_def/src/db.rs
+++ b/crates/hir_def/src/db.rs
@@ -175,9 +175,13 @@ pub trait DefDatabase: InternDatabase + AstDatabase + Upcast<dyn AstDatabase> {
     #[salsa::invoke(visibility::field_visibilities_query)]
     fn field_visibilities(&self, var: VariantId) -> Arc<ArenaMap<LocalFieldId, Visibility>>;
 
+    // FIXME: unify function_visibility and const_visibility?
     #[salsa::invoke(visibility::function_visibility_query)]
     fn function_visibility(&self, def: FunctionId) -> Visibility;
 
+    #[salsa::invoke(visibility::const_visibility_query)]
+    fn const_visibility(&self, def: ConstId) -> Visibility;
+
     #[salsa::transparent]
     fn crate_limits(&self, crate_id: CrateId) -> CrateLimits;
 }
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs
index f76034a3e2..6e22a877a9 100644
--- a/crates/hir_def/src/visibility.rs
+++ b/crates/hir_def/src/visibility.rs
@@ -11,7 +11,7 @@ use crate::{
     nameres::DefMap,
     path::{ModPath, PathKind},
     resolver::HasResolver,
-    FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
+    ConstId, FunctionId, HasModule, LocalFieldId, ModuleId, VariantId,
 };
 
 /// Visibility of an item, not yet resolved.
@@ -234,3 +234,9 @@ pub(crate) fn function_visibility_query(db: &dyn DefDatabase, def: FunctionId) -
     let resolver = def.resolver(db);
     db.function_data(def).visibility.resolve(db, &resolver)
 }
+
+/// Resolve visibility of a const.
+pub(crate) fn const_visibility_query(db: &dyn DefDatabase, def: ConstId) -> Visibility {
+    let resolver = def.resolver(db);
+    db.const_data(def).visibility.resolve(db, &resolver)
+}
diff --git a/crates/hir_ty/src/builder.rs b/crates/hir_ty/src/builder.rs
index c507c42f5b..5c77c0cdf8 100644
--- a/crates/hir_ty/src/builder.rs
+++ b/crates/hir_ty/src/builder.rs
@@ -15,10 +15,10 @@ use hir_def::{
 use smallvec::SmallVec;
 
 use crate::{
-    consteval::unknown_const_as_generic, db::HirDatabase, primitive, to_assoc_type_id,
-    to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData, ConstValue, GenericArg,
-    GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty, TyDefId, TyExt, TyKind,
-    ValueTyDefId,
+    consteval::unknown_const_as_generic, db::HirDatabase, infer::unify::InferenceTable, primitive,
+    to_assoc_type_id, to_chalk_trait_id, utils::generics, Binders, CallableSig, ConstData,
+    ConstValue, GenericArg, GenericArgData, Interner, ProjectionTy, Substitution, TraitRef, Ty,
+    TyDefId, TyExt, TyKind, ValueTyDefId,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -111,6 +111,15 @@ impl<D> TyBuilder<D> {
         this
     }
 
+    pub(crate) fn fill_with_inference_vars(self, table: &mut InferenceTable) -> Self {
+        self.fill(|x| match x {
+            ParamKind::Type => GenericArgData::Ty(table.new_type_var()).intern(Interner),
+            ParamKind::Const(ty) => {
+                GenericArgData::Const(table.new_const_var(ty.clone())).intern(Interner)
+            }
+        })
+    }
+
     pub fn fill(mut self, filler: impl FnMut(&ParamKind) -> GenericArg) -> Self {
         self.vec.extend(self.param_kinds.iter().skip(self.vec.len()).map(filler));
         assert_eq!(self.remaining(), 0);
diff --git a/crates/hir_ty/src/infer.rs b/crates/hir_ty/src/infer.rs
index 4ee5391057..442774d0be 100644
--- a/crates/hir_ty/src/infer.rs
+++ b/crates/hir_ty/src/infer.rs
@@ -35,10 +35,9 @@ use rustc_hash::FxHashMap;
 use stdx::impl_from;
 
 use crate::{
-    builder::ParamKind, db::HirDatabase, fold_tys_and_consts, infer::coerce::CoerceMany,
-    lower::ImplTraitLoweringMode, to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal,
-    GenericArg, GenericArgData, Goal, InEnvironment, Interner, ProjectionTy, Substitution,
-    TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
+    db::HirDatabase, fold_tys_and_consts, infer::coerce::CoerceMany, lower::ImplTraitLoweringMode,
+    to_assoc_type_id, AliasEq, AliasTy, Const, DomainGoal, GenericArg, Goal, InEnvironment,
+    Interner, ProjectionTy, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind,
 };
 
 // This lint has a false positive here. See the link below for details.
@@ -46,7 +45,6 @@ use crate::{
 // https://github.com/rust-lang/rust/issues/57411
 #[allow(unreachable_pub)]
 pub use unify::could_unify;
-pub(crate) use unify::unify;
 
 pub(crate) mod unify;
 mod path;
@@ -657,15 +655,7 @@ impl<'a> InferenceContext<'a> {
             }
             TypeNs::TypeAliasId(it) => {
                 let ty = TyBuilder::def_ty(self.db, it.into())
-                    .fill(|x| match x {
-                        ParamKind::Type => {
-                            GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
-                        }
-                        ParamKind::Const(ty) => {
-                            GenericArgData::Const(self.table.new_const_var(ty.clone()))
-                                .intern(Interner)
-                        }
-                    })
+                    .fill_with_inference_vars(&mut self.table)
                     .build();
                 self.resolve_variant_on_alias(ty, unresolved, path)
             }
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs
index 1d23654576..53fe2131a3 100644
--- a/crates/hir_ty/src/infer/path.rs
+++ b/crates/hir_ty/src/infer/path.rs
@@ -104,9 +104,7 @@ impl<'a> InferenceContext<'a> {
                     ParamKind::Type => {
                         GenericArgData::Ty(TyKind::Error.intern(Interner)).intern(Interner)
                     }
-                    ParamKind::Const(_) => {
-                        GenericArgData::Const(consteval::usize_const(None)).intern(Interner)
-                    }
+                    ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
                 })
             })
             .build();
@@ -249,15 +247,7 @@ impl<'a> InferenceContext<'a> {
                 let substs = match container {
                     ItemContainerId::ImplId(impl_id) => {
                         let impl_substs = TyBuilder::subst_for_def(self.db, impl_id)
-                            .fill(|x| match x {
-                                ParamKind::Type => {
-                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
-                                }
-                                ParamKind::Const(ty) => {
-                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
-                                        .intern(Interner)
-                                }
-                            })
+                            .fill_with_inference_vars(&mut self.table)
                             .build();
                         let impl_self_ty =
                             self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
@@ -268,15 +258,7 @@ impl<'a> InferenceContext<'a> {
                         // we're picking this method
                         let trait_ref = TyBuilder::trait_ref(self.db, trait_)
                             .push(ty.clone())
-                            .fill(|x| match x {
-                                ParamKind::Type => {
-                                    GenericArgData::Ty(self.table.new_type_var()).intern(Interner)
-                                }
-                                ParamKind::Const(ty) => {
-                                    GenericArgData::Const(self.table.new_const_var(ty.clone()))
-                                        .intern(Interner)
-                                }
-                            })
+                            .fill_with_inference_vars(&mut self.table)
                             .build();
                         self.push_obligation(trait_ref.clone().cast(Interner));
                         Some(trait_ref.substitution)
diff --git a/crates/hir_ty/src/infer/unify.rs b/crates/hir_ty/src/infer/unify.rs
index deb536e511..ef0675d59f 100644
--- a/crates/hir_ty/src/infer/unify.rs
+++ b/crates/hir_ty/src/infer/unify.rs
@@ -146,7 +146,7 @@ pub(crate) struct InferenceTable<'a> {
 
 pub(crate) struct InferenceTableSnapshot {
     var_table_snapshot: chalk_solve::infer::InferenceSnapshot<Interner>,
-    // FIXME: snapshot pending_obligations?
+    pending_obligations: Vec<Canonicalized<InEnvironment<Goal>>>,
     type_variable_table_snapshot: Vec<TypeVariableData>,
 }
 
@@ -365,12 +365,25 @@ impl<'a> InferenceTable<'a> {
     pub(crate) fn snapshot(&mut self) -> InferenceTableSnapshot {
         let var_table_snapshot = self.var_unification_table.snapshot();
         let type_variable_table_snapshot = self.type_variable_table.clone();
-        InferenceTableSnapshot { var_table_snapshot, type_variable_table_snapshot }
+        let pending_obligations = self.pending_obligations.clone();
+        InferenceTableSnapshot {
+            var_table_snapshot,
+            pending_obligations,
+            type_variable_table_snapshot,
+        }
     }
 
     pub(crate) fn rollback_to(&mut self, snapshot: InferenceTableSnapshot) {
         self.var_unification_table.rollback_to(snapshot.var_table_snapshot);
         self.type_variable_table = snapshot.type_variable_table_snapshot;
+        self.pending_obligations = snapshot.pending_obligations;
+    }
+
+    pub(crate) fn run_in_snapshot<T>(&mut self, f: impl FnOnce(&mut InferenceTable) -> T) -> T {
+        let snapshot = self.snapshot();
+        let result = f(self);
+        self.rollback_to(snapshot);
+        result
     }
 
     /// Checks an obligation without registering it. Useful mostly to check
diff --git a/crates/hir_ty/src/lower.rs b/crates/hir_ty/src/lower.rs
index 0854d06548..41bb94c5d5 100644
--- a/crates/hir_ty/src/lower.rs
+++ b/crates/hir_ty/src/lower.rs
@@ -9,6 +9,8 @@ use std::cell::{Cell, RefCell};
 use std::{iter, sync::Arc};
 
 use base_db::CrateId;
+use chalk_ir::fold::Fold;
+use chalk_ir::interner::HasInterner;
 use chalk_ir::{cast::Cast, fold::Shift, Mutability, Safety};
 use hir_def::generics::TypeOrConstParamData;
 use hir_def::intern::Interned;
@@ -36,7 +38,6 @@ use stdx::{impl_from, never};
 use syntax::{ast, SmolStr};
 
 use crate::consteval::{path_to_const, unknown_const_as_generic, unknown_const_usize, usize_const};
-use crate::method_resolution::fallback_bound_vars;
 use crate::utils::Generics;
 use crate::{all_super_traits, make_binders, Const, GenericArgData, ParamKind};
 use crate::{
@@ -1701,3 +1702,28 @@ pub(crate) fn const_or_path_to_chalk(
         }
     }
 }
+
+/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
+/// num_vars_to_keep) by `TyKind::Unknown`.
+fn fallback_bound_vars<T: Fold<Interner> + HasInterner<Interner = Interner>>(
+    s: T,
+    num_vars_to_keep: usize,
+) -> T::Result {
+    crate::fold_free_vars(
+        s,
+        |bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                TyKind::Error.intern(Interner)
+            } else {
+                bound.shifted_in_from(binders).to_ty(Interner)
+            }
+        },
+        |ty, bound, binders| {
+            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
+                consteval::unknown_const(ty.clone())
+            } else {
+                bound.shifted_in_from(binders).to_const(Interner, ty)
+            }
+        },
+    )
+}
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs
index 06c834fbc8..1c939f3d8a 100644
--- a/crates/hir_ty/src/method_resolution.rs
+++ b/crates/hir_ty/src/method_resolution.rs
@@ -6,7 +6,7 @@ use std::{iter, ops::ControlFlow, sync::Arc};
 
 use arrayvec::ArrayVec;
 use base_db::{CrateId, Edition};
-use chalk_ir::{cast::Cast, fold::Fold, interner::HasInterner, Mutability, UniverseIndex};
+use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
 use hir_def::{
     item_scope::ItemScope, lang_item::LangItemTarget, nameres::DefMap, AssocItemId, BlockId,
     ConstId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, Lookup, ModuleDefId,
@@ -18,16 +18,14 @@ use stdx::never;
 
 use crate::{
     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,
-    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, GenericArgData,
-    InEnvironment, Interner, Scalar, Substitution, TraitEnvironment, TraitRefExt, Ty, TyBuilder,
-    TyExt, TyKind,
+    AdtId, Canonical, CanonicalVarKinds, DebruijnIndex, ForeignDefId, InEnvironment, Interner,
+    Scalar, TraitEnvironment, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
 };
 
 /// This is used as a key for indexing impls.
@@ -643,11 +641,10 @@ pub fn iterate_method_candidates_dyn(
             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);
 
-            let result = deref_chains.zip(adj).try_for_each(|(deref_chain, adj)| {
+            let result = deref_chain.into_iter().zip(adj).try_for_each(|(receiver_ty, adj)| {
                 iterate_method_candidates_with_autoref(
-                    deref_chain,
+                    &receiver_ty,
                     adj,
                     db,
                     env.clone(),
@@ -675,7 +672,7 @@ pub fn iterate_method_candidates_dyn(
 }
 
 fn iterate_method_candidates_with_autoref(
-    deref_chain: &[Canonical<Ty>],
+    receiver_ty: &Canonical<Ty>,
     first_adjustment: ReceiverAdjustments,
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
@@ -684,17 +681,9 @@ fn iterate_method_candidates_with_autoref(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let (receiver_ty, rest) = match deref_chain.split_first() {
-        Some((rec, rest)) => (rec, rest),
-        None => {
-            never!("received empty deref-chain");
-            return ControlFlow::Break(());
-        }
-    };
     iterate_method_candidates_by_receiver(
         receiver_ty,
         first_adjustment.clone(),
-        rest,
         db,
         env.clone(),
         traits_in_scope,
@@ -712,7 +701,6 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &refed,
         first_adjustment.with_autoref(Mutability::Not),
-        deref_chain,
         db,
         env.clone(),
         traits_in_scope,
@@ -730,7 +718,6 @@ fn iterate_method_candidates_with_autoref(
     iterate_method_candidates_by_receiver(
         &ref_muted,
         first_adjustment.with_autoref(Mutability::Mut),
-        deref_chain,
         db,
         env,
         traits_in_scope,
@@ -743,7 +730,6 @@ fn iterate_method_candidates_with_autoref(
 fn iterate_method_candidates_by_receiver(
     receiver_ty: &Canonical<Ty>,
     receiver_adjustments: ReceiverAdjustments,
-    rest_of_deref_chain: &[Canonical<Ty>],
     db: &dyn HirDatabase,
     env: Arc<TraitEnvironment>,
     traits_in_scope: &FxHashSet<TraitId>,
@@ -751,30 +737,35 @@ fn iterate_method_candidates_by_receiver(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
+    let mut table = InferenceTable::new(db, env);
+    let receiver_ty = table.instantiate_canonical(receiver_ty.clone());
+    let snapshot = table.snapshot();
     // 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
     // that.
-    for self_ty in iter::once(receiver_ty).chain(rest_of_deref_chain) {
+    let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
+    while let Some((self_ty, _)) = autoderef.next() {
         iterate_inherent_methods(
-            self_ty,
-            db,
-            env.clone(),
+            &self_ty,
+            &mut autoderef.table,
             name,
-            Some(receiver_ty),
+            Some(&receiver_ty),
             Some(receiver_adjustments.clone()),
             visible_from_module,
             &mut callback,
         )?
     }
 
-    for self_ty in iter::once(receiver_ty).chain(rest_of_deref_chain) {
+    table.rollback_to(snapshot);
+
+    let mut autoderef = autoderef::Autoderef::new(&mut table, receiver_ty.clone());
+    while let Some((self_ty, _)) = autoderef.next() {
         iterate_trait_method_candidates(
-            self_ty,
-            db,
-            env.clone(),
+            &self_ty,
+            &mut autoderef.table,
             traits_in_scope,
             name,
-            Some(receiver_ty),
+            Some(&receiver_ty),
             Some(receiver_adjustments.clone()),
             &mut callback,
         )?
@@ -792,43 +783,55 @@ fn iterate_method_candidates_for_self_ty(
     name: Option<&Name>,
     mut callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
+    let mut table = InferenceTable::new(db, env);
+    let self_ty = table.instantiate_canonical(self_ty.clone());
     iterate_inherent_methods(
-        self_ty,
-        db,
-        env.clone(),
+        &self_ty,
+        &mut table,
         name,
         None,
         None,
         visible_from_module,
         &mut callback,
     )?;
-    iterate_trait_method_candidates(self_ty, db, env, traits_in_scope, name, None, None, callback)
+    iterate_trait_method_candidates(
+        &self_ty,
+        &mut table,
+        traits_in_scope,
+        name,
+        None,
+        None,
+        callback,
+    )
 }
 
 fn iterate_trait_method_candidates(
-    self_ty: &Canonical<Ty>,
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
+    self_ty: &Ty,
+    table: &mut InferenceTable,
     traits_in_scope: &FxHashSet<TraitId>,
     name: Option<&Name>,
-    receiver_ty: Option<&Canonical<Ty>>,
+    receiver_ty: Option<&Ty>,
     receiver_adjustments: Option<ReceiverAdjustments>,
     callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let self_is_array = matches!(self_ty.value.kind(Interner), chalk_ir::TyKind::Array(..));
+    let db = table.db;
+    let env = table.trait_env.clone();
+    let self_is_array = matches!(self_ty.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));
-    let env_traits = matches!(self_ty.value.kind(Interner), TyKind::Placeholder(_))
+        self_ty.dyn_trait().into_iter().flat_map(|t| all_super_traits(db.upcast(), t));
+    let env_traits = matches!(self_ty.kind(Interner), TyKind::Placeholder(_))
         // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
         .then(|| {
-            env.traits_in_scope_from_clauses(self_ty.value.clone())
+            env.traits_in_scope_from_clauses(self_ty.clone())
                 .flat_map(|t| all_super_traits(db.upcast(), t))
         })
         .into_iter()
         .flatten();
     let traits = inherent_trait.chain(env_traits).chain(traits_in_scope.iter().copied());
 
+    let canonical_self_ty = table.canonicalize(self_ty.clone()).value;
+
     'traits: for t in traits {
         let data = db.trait_data(t);
 
@@ -852,11 +855,11 @@ fn iterate_trait_method_candidates(
         for &(_, item) in data.items.iter() {
             // Don't pass a `visible_from_module` down to `is_valid_candidate`,
             // since only inherent methods should be included into visibility checking.
-            if !is_valid_candidate(db, env.clone(), name, receiver_ty, item, self_ty, None) {
+            if !is_valid_candidate(table, name, receiver_ty, item, self_ty, None) {
                 continue;
             }
             if !known_implemented {
-                let goal = generic_implements_goal(db, env.clone(), t, self_ty);
+                let goal = generic_implements_goal(db, env.clone(), t, &canonical_self_ty);
                 if db.trait_solve(env.krate, goal.cast(Interner)).is_none() {
                     continue 'traits;
                 }
@@ -868,40 +871,18 @@ fn iterate_trait_method_candidates(
     ControlFlow::Continue(())
 }
 
-fn filter_inherent_impls_for_self_ty<'i>(
-    impls: &'i InherentImpls,
-    self_ty: &Ty,
-) -> impl Iterator<Item = &'i ImplId> {
-    // inherent methods on arrays are fingerprinted as [T; {unknown}], so we must also consider them when
-    // resolving a method call on an array with a known len
-    let array_impls = {
-        match self_ty.kind(Interner) {
-            TyKind::Array(parameters, array_len) if !array_len.is_unknown() => {
-                let unknown_array_len_ty =
-                    TyKind::Array(parameters.clone(), consteval::usize_const(None));
-
-                Some(impls.for_self_ty(&unknown_array_len_ty.intern(Interner)))
-            }
-            _ => None,
-        }
-    }
-    .into_iter()
-    .flatten();
-
-    impls.for_self_ty(self_ty).iter().chain(array_impls)
-}
-
 fn iterate_inherent_methods(
-    self_ty: &Canonical<Ty>,
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
+    self_ty: &Ty,
+    table: &mut InferenceTable,
     name: Option<&Name>,
-    receiver_ty: Option<&Canonical<Ty>>,
+    receiver_ty: Option<&Ty>,
     receiver_adjustments: Option<ReceiverAdjustments>,
     visible_from_module: VisibleFromModule,
     callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
 ) -> ControlFlow<()> {
-    let def_crates = match def_crates(db, &self_ty.value, env.krate) {
+    let db = table.db;
+    let env = table.trait_env.clone();
+    let def_crates = match def_crates(db, self_ty, env.krate) {
         Some(k) => k,
         None => return ControlFlow::Continue(()),
     };
@@ -917,8 +898,7 @@ fn iterate_inherent_methods(
             impls_for_self_ty(
                 &impls,
                 self_ty,
-                db,
-                env.clone(),
+                table,
                 name,
                 receiver_ty,
                 receiver_adjustments.clone(),
@@ -933,8 +913,7 @@ fn iterate_inherent_methods(
         impls_for_self_ty(
             &impls,
             self_ty,
-            db,
-            env.clone(),
+            table,
             name,
             receiver_ty,
             receiver_adjustments.clone(),
@@ -946,37 +925,20 @@ fn iterate_inherent_methods(
 
     fn impls_for_self_ty(
         impls: &InherentImpls,
-        self_ty: &Canonical<Ty>,
-        db: &dyn HirDatabase,
-        env: Arc<TraitEnvironment>,
+        self_ty: &Ty,
+        table: &mut InferenceTable,
         name: Option<&Name>,
-        receiver_ty: Option<&Canonical<Ty>>,
+        receiver_ty: Option<&Ty>,
         receiver_adjustments: Option<ReceiverAdjustments>,
         visible_from_module: Option<ModuleId>,
         callback: &mut dyn FnMut(ReceiverAdjustments, AssocItemId) -> ControlFlow<()>,
     ) -> ControlFlow<()> {
-        let impls_for_self_ty = filter_inherent_impls_for_self_ty(impls, &self_ty.value);
+        let db = table.db;
+        let impls_for_self_ty = impls.for_self_ty(self_ty);
         for &impl_def in impls_for_self_ty {
             for &item in &db.impl_data(impl_def).items {
-                if !is_valid_candidate(
-                    db,
-                    env.clone(),
-                    name,
-                    receiver_ty,
-                    item,
-                    self_ty,
-                    visible_from_module,
-                ) {
-                    continue;
-                }
-                // we have to check whether the self type unifies with the type
-                // that the impl is for. If we have a receiver type, this
-                // 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()
+                if !is_valid_candidate(table, name, receiver_ty, item, self_ty, visible_from_module)
                 {
-                    cov_mark::hit!(impl_self_type_match_without_receiver);
                     continue;
                 }
                 callback(receiver_adjustments.clone().unwrap_or_default(), item)?;
@@ -1005,37 +967,15 @@ pub fn resolve_indexing_op(
     None
 }
 
-fn is_transformed_receiver_ty_equal(transformed_receiver_ty: &Ty, receiver_ty: &Ty) -> bool {
-    if transformed_receiver_ty == receiver_ty {
-        return true;
-    }
-
-    // a transformed receiver may be considered equal (and a valid method call candidate) if it is an array
-    // with an unknown (i.e. generic) length, and the receiver is an array with the same item type but a known len,
-    // this allows inherent methods on arrays to be considered valid resolution candidates
-    match (transformed_receiver_ty.kind(Interner), receiver_ty.kind(Interner)) {
-        (
-            TyKind::Array(transformed_array_ty, transformed_array_len),
-            TyKind::Array(receiver_array_ty, receiver_array_len),
-        ) if transformed_array_ty == receiver_array_ty
-            && transformed_array_len.is_unknown()
-            && !receiver_array_len.is_unknown() =>
-        {
-            true
-        }
-        _ => false,
-    }
-}
-
 fn is_valid_candidate(
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
+    table: &mut InferenceTable,
     name: Option<&Name>,
-    receiver_ty: Option<&Canonical<Ty>>,
+    receiver_ty: Option<&Ty>,
     item: AssocItemId,
-    self_ty: &Canonical<Ty>,
+    self_ty: &Ty,
     visible_from_module: Option<ModuleId>,
 ) -> bool {
+    let db = table.db;
     match item {
         AssocItemId::FunctionId(m) => {
             let data = db.function_data(m);
@@ -1044,123 +984,82 @@ fn is_valid_candidate(
                     return false;
                 }
             }
-            if let Some(receiver_ty) = receiver_ty {
-                if !data.has_self_param() {
+            table.run_in_snapshot(|table| {
+                let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build();
+                let expected_self_ty = match m.lookup(db.upcast()).container {
+                    ItemContainerId::TraitId(_) => {
+                        subst.at(Interner, 0).assert_ty_ref(Interner).clone()
+                    }
+                    ItemContainerId::ImplId(impl_id) => {
+                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner)
+                    }
+                    // We should only get called for associated items (impl/trait)
+                    ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
+                        unreachable!()
+                    }
+                };
+                if !table.unify(&expected_self_ty, &self_ty) {
                     return false;
                 }
-                let transformed_receiver_ty = match transform_receiver_ty(db, env, m, self_ty) {
-                    Some(ty) => ty,
-                    None => return false,
-                };
+                if let Some(receiver_ty) = receiver_ty {
+                    if !data.has_self_param() {
+                        return false;
+                    }
+
+                    let sig = db.callable_item_signature(m.into());
+                    let expected_receiver =
+                        sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst);
+                    let receiver_matches = table.unify(&receiver_ty, &expected_receiver);
+
+                    if !receiver_matches {
+                        return false;
+                    }
+                }
+                if let Some(from_module) = visible_from_module {
+                    if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
+                        cov_mark::hit!(autoderef_candidate_not_visible);
+                        return false;
+                    }
+                }
 
-                if !is_transformed_receiver_ty_equal(&transformed_receiver_ty, &receiver_ty.value) {
+                true
+            })
+        }
+        AssocItemId::ConstId(c) => {
+            let data = db.const_data(c);
+            if receiver_ty.is_some() {
+                return false;
+            }
+            if let Some(name) = name {
+                if data.name.as_ref() != Some(name) {
                     return false;
                 }
             }
             if let Some(from_module) = visible_from_module {
-                if !db.function_visibility(m).is_visible_from(db.upcast(), from_module) {
-                    cov_mark::hit!(autoderef_candidate_not_visible);
+                if !db.const_visibility(c).is_visible_from(db.upcast(), from_module) {
+                    cov_mark::hit!(const_candidate_not_visible);
+                    return false;
+                }
+            }
+            if let ItemContainerId::ImplId(impl_id) = c.lookup(db.upcast()).container {
+                let self_ty_matches = table.run_in_snapshot(|table| {
+                    let subst =
+                        TyBuilder::subst_for_def(db, c).fill_with_inference_vars(table).build();
+                    let expected_self_ty =
+                        subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner);
+                    table.unify(&expected_self_ty, &self_ty)
+                });
+                if !self_ty_matches {
+                    cov_mark::hit!(const_candidate_self_type_mismatch);
                     return false;
                 }
             }
-
             true
         }
-        AssocItemId::ConstId(c) => {
-            let data = db.const_data(c);
-            name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none()
-        }
         _ => false,
     }
 }
 
-pub(crate) fn inherent_impl_substs(
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    impl_id: ImplId,
-    self_ty: &Canonical<Ty>,
-) -> Option<Substitution> {
-    // we create a var for each type parameter of the impl; we need to keep in
-    // mind here that `self_ty` might have vars of its own
-    let self_ty_vars = self_ty.binders.len(Interner);
-    let vars = TyBuilder::subst_for_def(db, impl_id)
-        .fill_with_bound_vars(DebruijnIndex::INNERMOST, self_ty_vars)
-        .build();
-    let self_ty_with_vars = db.impl_self_ty(impl_id).substitute(Interner, &vars);
-    let mut kinds = self_ty.binders.interned().to_vec();
-    kinds.extend(vars.iter(Interner).map(|x| {
-        let kind = match x.data(Interner) {
-            GenericArgData::Ty(_) => chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General),
-            GenericArgData::Const(c) => chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()),
-            GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime,
-        };
-        chalk_ir::WithKind::new(kind, UniverseIndex::ROOT)
-    }));
-    let tys = Canonical {
-        binders: CanonicalVarKinds::from_iter(Interner, kinds),
-        value: (self_ty_with_vars, self_ty.value.clone()),
-    };
-    let substs = super::infer::unify(db, env, &tys)?;
-    // We only want the substs for the vars we added, not the ones from self_ty.
-    // Also, if any of the vars we added are still in there, we replace them by
-    // Unknown. I think this can only really happen if self_ty contained
-    // Unknown, and in that case we want the result to contain Unknown in those
-    // places again.
-    let suffix =
-        Substitution::from_iter(Interner, substs.iter(Interner).skip(self_ty_vars).cloned());
-    Some(fallback_bound_vars(suffix, self_ty_vars))
-}
-
-/// This replaces any 'free' Bound vars in `s` (i.e. those with indices past
-/// num_vars_to_keep) by `TyKind::Unknown`.
-pub(crate) fn fallback_bound_vars<T: Fold<Interner> + HasInterner<Interner = Interner>>(
-    s: T,
-    num_vars_to_keep: usize,
-) -> T::Result {
-    crate::fold_free_vars(
-        s,
-        |bound, binders| {
-            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
-                TyKind::Error.intern(Interner)
-            } else {
-                bound.shifted_in_from(binders).to_ty(Interner)
-            }
-        },
-        |ty, bound, binders| {
-            if bound.index >= num_vars_to_keep && bound.debruijn == DebruijnIndex::INNERMOST {
-                consteval::usize_const(None)
-            } else {
-                bound.shifted_in_from(binders).to_const(Interner, ty)
-            }
-        },
-    )
-}
-
-fn transform_receiver_ty(
-    db: &dyn HirDatabase,
-    env: Arc<TraitEnvironment>,
-    function_id: FunctionId,
-    self_ty: &Canonical<Ty>,
-) -> Option<Ty> {
-    let substs = match function_id.lookup(db.upcast()).container {
-        ItemContainerId::TraitId(_) => TyBuilder::subst_for_def(db, function_id)
-            .push(self_ty.value.clone())
-            .fill_with_unknown()
-            .build(),
-        ItemContainerId::ImplId(impl_id) => {
-            let impl_substs = inherent_impl_substs(db, env, impl_id, self_ty)?;
-            TyBuilder::subst_for_def(db, function_id)
-                .use_parent_substs(&impl_substs)
-                .fill_with_unknown()
-                .build()
-        }
-        // No receiver
-        ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => unreachable!(),
-    };
-    let sig = db.callable_item_signature(function_id.into());
-    Some(sig.map(|s| s.params()[0].clone()).substitute(Interner, &substs))
-}
-
 pub fn implements_trait(
     ty: &Canonical<Ty>,
     db: &dyn HirDatabase,


Test patch:
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs
index 9700b388aa..56b8db1319 100644
--- a/crates/hir_ty/src/tests/method_resolution.rs
+++ b/crates/hir_ty/src/tests/method_resolution.rs
@@ -925,7 +925,6 @@ fn test() { S2.into(); }
 
 #[test]
 fn method_resolution_overloaded_method() {
-    cov_mark::check!(impl_self_type_match_without_receiver);
     check_types(
         r#"
 struct Wrapper<T>(T);
@@ -954,6 +953,33 @@ fn main() {
     );
 }
 
+#[test]
+fn method_resolution_overloaded_const() {
+    cov_mark::check!(const_candidate_self_type_mismatch);
+    check_types(
+        r#"
+struct Wrapper<T>(T);
+struct Foo<T>(T);
+struct Bar<T>(T);
+
+impl<T> Wrapper<Foo<T>> {
+    pub const VALUE: Foo<T>;
+}
+
+impl<T> Wrapper<Bar<T>> {
+    pub const VALUE: Bar<T>;
+}
+
+fn main() {
+    let a = Wrapper::<Foo<f32>>::VALUE;
+    let b = Wrapper::<Bar<f32>>::VALUE;
+    (a, b);
+  //^^^^^^ (Foo<f32>, Bar<f32>)
+}
+"#,
+    );
+}
+
 #[test]
 fn method_resolution_encountering_fn_type() {
     check_types(
@@ -1257,6 +1283,37 @@ mod b {
     )
 }
 
+#[test]
+fn trait_vs_private_inherent_const() {
+    cov_mark::check!(const_candidate_not_visible);
+    check(
+        r#"
+mod a {
+    pub struct Foo;
+    impl Foo {
+        const VALUE: u32 = 2;
+    }
+    pub trait Trait {
+        const VALUE: usize;
+    }
+    impl Trait for Foo {
+        const VALUE: usize = 3;
+    }
+
+    fn foo() {
+        let x = Foo::VALUE;
+            //  ^^^^^^^^^^ type: u32
+    }
+}
+use a::Trait;
+fn foo() {
+    let x = a::Foo::VALUE;
+         // ^^^^^^^^^^^^^ type: usize
+}
+"#,
+    )
+}
+
 #[test]
 fn trait_impl_in_unnamed_const() {
     check_types(
diff --git a/crates/hir_ty/src/tests/traits.rs b/crates/hir_ty/src/tests/traits.rs
index 5daffb2c5e..a82b8cb466 100644
--- a/crates/hir_ty/src/tests/traits.rs
+++ b/crates/hir_ty/src/tests/traits.rs
@@ -3609,3 +3609,40 @@ fn f<F: Foo>() {
         "#]],
     );
 }
+
+#[test]
+fn dyn_map() {
+    check_types(
+        r#"
+pub struct Key<K, V, P = (K, V)> {}
+
+pub trait Policy {
+    type K;
+    type V;
+}
+
+impl<K, V> Policy for (K, V) {
+    type K = K;
+    type V = V;
+}
+
+pub struct KeyMap<KEY> {}
+
+impl<P: Policy> KeyMap<Key<P::K, P::V, P>> {
+    pub fn get(&self, key: &P::K) -> P::V {
+        loop {}
+    }
+}
+
+struct Fn {}
+struct FunctionId {}
+
+fn test() {
+    let key_map: &KeyMap<Key<Fn, FunctionId>> = loop {};
+    let key;
+    let result = key_map.get(key);
+      //^^^^^^ FunctionId
+}
+"#,
+    )
+}