diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 4d19d89b3ce24..da59f9f9ebd5d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -20,7 +20,7 @@ use rustc_middle::bug;
 use rustc_middle::hir::nested_filter::OnlyBodies;
 use rustc_middle::mir::tcx::PlaceTy;
 use rustc_middle::mir::{
-    self, AggregateKind, BindingForm, BorrowKind, CallSource, ClearCrossCrate, ConstraintCategory,
+    self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
     FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,
     Operand, Place, PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
     TerminatorKind, VarBindingForm, VarDebugInfoContents,
@@ -30,13 +30,13 @@ use rustc_middle::ty::{
     self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast,
     suggest_constraining_type_params,
 };
-use rustc_middle::util::CallKind;
 use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::{BytePos, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
+use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
@@ -46,7 +46,7 @@ use super::explain_borrow::{BorrowExplanation, LaterUseKind};
 use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans};
 use crate::borrow_set::{BorrowData, TwoPhaseActivation};
 use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
-use crate::diagnostics::{CapturedMessageOpt, Instance, find_all_local_uses};
+use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses};
 use crate::prefixes::IsPrefixOf;
 use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors};
 
@@ -305,7 +305,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             }
 
             if let UseSpans::FnSelfUse {
-                kind: CallKind::DerefCoercion { deref_target, deref_target_ty, .. },
+                kind: CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. },
                 ..
             } = use_spans
             {
@@ -315,8 +315,10 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 ));
 
                 // Check first whether the source is accessible (issue #87060)
-                if self.infcx.tcx.sess.source_map().is_span_accessible(deref_target) {
-                    err.span_note(deref_target, "deref defined here");
+                if let Some(deref_target_span) = deref_target_span
+                    && self.infcx.tcx.sess.source_map().is_span_accessible(deref_target_span)
+                {
+                    err.span_note(deref_target_span, "deref defined here");
                 }
             }
 
@@ -3765,38 +3767,27 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
     fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diag<'_>) {
         let tcx = self.infcx.tcx;
-        if let (
-            Some(Terminator {
-                kind: TerminatorKind::Call { call_source: CallSource::OverloadedOperator, .. },
-                ..
-            }),
-            Some((method_did, method_args)),
-        ) = (
-            &self.body[loan.reserve_location.block].terminator,
-            rustc_middle::util::find_self_call(
+        if let Some(Terminator { kind: TerminatorKind::Call { call_source, fn_span, .. }, .. }) =
+            &self.body[loan.reserve_location.block].terminator
+            && let Some((method_did, method_args)) = rustc_middle::util::find_self_call(
                 tcx,
                 self.body,
                 loan.assigned_place.local,
                 loan.reserve_location.block,
-            ),
-        ) {
-            if tcx.is_diagnostic_item(sym::deref_method, method_did) {
-                let deref_target =
-                    tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
-                        Instance::try_resolve(
-                            tcx,
-                            self.infcx.typing_env(self.infcx.param_env),
-                            deref_target,
-                            method_args,
-                        )
-                        .transpose()
-                    });
-                if let Some(Ok(instance)) = deref_target {
-                    let deref_target_ty =
-                        instance.ty(tcx, self.infcx.typing_env(self.infcx.param_env));
-                    err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`"));
-                    err.span_note(tcx.def_span(instance.def_id()), "deref defined here");
-                }
+            )
+            && let CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. } = call_kind(
+                self.infcx.tcx,
+                self.infcx.typing_env(self.infcx.param_env),
+                method_did,
+                method_args,
+                *fn_span,
+                call_source.from_hir_call(),
+                Some(self.infcx.tcx.fn_arg_names(method_did)[0]),
+            )
+        {
+            err.note(format!("borrow occurs due to deref coercion to `{deref_target_ty}`"));
+            if let Some(deref_target_span) = deref_target_span {
+                err.span_note(deref_target_span, "deref defined here");
             }
         }
     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index a52dc447d7685..5c0c1d0eb86af 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -16,9 +16,9 @@ use rustc_middle::mir::{
 };
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
-use rustc_middle::util::CallKind;
 use rustc_span::{DesugaringKind, Span, kw, sym};
 use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
+use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
 use tracing::{debug, instrument};
 
 use super::{RegionName, UseSpans, find_use};
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index d9d9ea75994f1..bd6f77156ca99 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -20,13 +20,13 @@ use rustc_middle::mir::{
     StatementKind, Terminator, TerminatorKind,
 };
 use rustc_middle::ty::print::Print;
-use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
-use rustc_middle::util::{CallDesugaringKind, call_kind};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_mir_dataflow::move_paths::{InitLocation, LookupResult, MoveOutIndex};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
 use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
+use rustc_trait_selection::error_reporting::traits::call_kind::{CallDesugaringKind, call_kind};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{
     FulfillmentErrorCode, type_known_to_meet_bound_modulo_regions,
@@ -63,7 +63,7 @@ pub(crate) use mutability_errors::AccessKind;
 pub(crate) use outlives_suggestion::OutlivesSuggestionBuilder;
 pub(crate) use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
 pub(crate) use region_name::{RegionName, RegionNameSource};
-pub(crate) use rustc_middle::util::CallKind;
+pub(crate) use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;
 
 pub(super) struct DescribePlaceOpt {
     including_downcast: bool,
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index 6707ebe7d1c84..7d103055a7c43 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -14,9 +14,11 @@ use rustc_middle::ty::{
     self, Closure, FnDef, FnPtr, GenericArgKind, GenericArgsRef, Param, TraitRef, Ty,
     suggest_constraining_type_param,
 };
-use rustc_middle::util::{CallDesugaringKind, CallKind, call_kind};
 use rustc_session::parse::add_feature_diagnostics;
 use rustc_span::{BytePos, Pos, Span, Symbol, sym};
+use rustc_trait_selection::error_reporting::traits::call_kind::{
+    CallDesugaringKind, CallKind, call_kind,
+};
 use rustc_trait_selection::traits::SelectionContext;
 use tracing::debug;
 
@@ -324,10 +326,12 @@ fn build_error_for_const_call<'tcx>(
             note_trait_if_possible(&mut err, self_ty, trait_id);
             err
         }
-        CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
+        CallKind::DerefCoercion { deref_target_span, deref_target_ty, self_ty } => {
             // Check first whether the source is accessible (issue #87060)
-            let target = if tcx.sess.source_map().is_span_accessible(deref_target) {
-                Some(deref_target)
+            let target = if let Some(deref_target_span) = deref_target_span
+                && tcx.sess.source_map().is_span_accessible(deref_target_span)
+            {
+                Some(deref_target_span)
             } else {
                 None
             };
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 49b5588e261af..e4ded2c30f539 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -5,7 +5,7 @@ use std::path::PathBuf;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
-use rustc_hir::def::Namespace;
+use rustc_hir::def::{CtorKind, DefKind, Namespace};
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::FiniteBitSet;
@@ -498,7 +498,8 @@ impl<'tcx> Instance<'tcx> {
 
     /// Resolves a `(def_id, args)` pair to an (optional) instance -- most commonly,
     /// this is used to find the precise code that will run for a trait method invocation,
-    /// if known.
+    /// if known. This should only be used for functions and consts. If you want to
+    /// resolve an associated type, use [`TyCtxt::try_normalize_erasing_regions`].
     ///
     /// Returns `Ok(None)` if we cannot resolve `Instance` to a specific instance.
     /// For example, in a context like this,
@@ -527,6 +528,23 @@ impl<'tcx> Instance<'tcx> {
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
     ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+        assert_matches!(
+            tcx.def_kind(def_id),
+            DefKind::Fn
+                | DefKind::AssocFn
+                | DefKind::Const
+                | DefKind::AssocConst
+                | DefKind::AnonConst
+                | DefKind::InlineConst
+                | DefKind::Static { .. }
+                | DefKind::Ctor(_, CtorKind::Fn)
+                | DefKind::Closure
+                | DefKind::SyntheticCoroutineBody,
+            "`Instance::try_resolve` should only be used to resolve instances of \
+            functions, statics, and consts; to resolve associated types, use \
+            `try_normalize_erasing_regions`."
+        );
+
         // Rust code can easily create exponentially-long types using only a
         // polynomial recursion depth. Even with the default recursion
         // depth, you can easily get cases that take >2^60 steps to run,
diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs
index 8dafc42264485..097a868191c15 100644
--- a/compiler/rustc_middle/src/util/mod.rs
+++ b/compiler/rustc_middle/src/util/mod.rs
@@ -1,9 +1,7 @@
 pub mod bug;
-pub mod call_kind;
 pub mod common;
 pub mod find_self_call;
 
-pub use call_kind::{CallDesugaringKind, CallKind, call_kind};
 pub use find_self_call::find_self_call;
 
 #[derive(Default, Copy, Clone)]
diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
similarity index 55%
rename from compiler/rustc_middle/src/util/call_kind.rs
rename to compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
index 0e39533168792..1c3e570b67695 100644
--- a/compiler/rustc_middle/src/util/call_kind.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
@@ -2,12 +2,14 @@
 //! as well as errors when attempting to call a non-const function in a const
 //! context.
 
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{LangItem, lang_items};
+use rustc_middle::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
 use rustc_span::{DesugaringKind, Ident, Span, sym};
 use tracing::debug;
 
-use crate::ty::{AssocItemContainer, GenericArgsRef, Instance, Ty, TyCtxt, TypingEnv};
+use crate::traits::specialization_graph;
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
 pub enum CallDesugaringKind {
@@ -55,7 +57,7 @@ pub enum CallKind<'tcx> {
     DerefCoercion {
         /// The `Span` of the `Target` associated type
         /// in the `Deref` impl we are using.
-        deref_target: Span,
+        deref_target_span: Option<Span>,
         /// The type `T::Deref` we are dereferencing to
         deref_target_ty: Ty<'tcx>,
         self_ty: Ty<'tcx>,
@@ -89,61 +91,65 @@ pub fn call_kind<'tcx>(
         None
     };
 
-    let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did);
-
     // Check for a 'special' use of 'self' -
     // an FnOnce call, an operator (e.g. `<<`), or a
     // deref coercion.
-    let kind = if let Some(trait_id) = fn_call {
-        Some(CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) })
+    if let Some(trait_id) = fn_call {
+        return CallKind::FnCall { fn_trait_id: trait_id, self_ty: method_args.type_at(0) };
     } else if let Some(trait_id) = operator {
-        Some(CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) })
-    } else if is_deref {
-        let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
-            Instance::try_resolve(tcx, typing_env, deref_target, method_args).transpose()
-        });
-        if let Some(Ok(instance)) = deref_target {
-            let deref_target_ty = instance.ty(tcx, typing_env);
-            Some(CallKind::DerefCoercion {
-                deref_target: tcx.def_span(instance.def_id()),
-                deref_target_ty,
-                self_ty: method_args.type_at(0),
-            })
+        return CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) };
+    } else if !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did) {
+        let deref_target_def_id =
+            tcx.get_diagnostic_item(sym::deref_target).expect("deref method but no deref target");
+        let deref_target_ty = tcx.normalize_erasing_regions(
+            typing_env,
+            Ty::new_projection(tcx, deref_target_def_id, method_args),
+        );
+        let deref_target_span = if let Ok(Some(instance)) =
+            Instance::try_resolve(tcx, typing_env, method_did, method_args)
+            && let instance_parent_def_id = tcx.parent(instance.def_id())
+            && matches!(tcx.def_kind(instance_parent_def_id), DefKind::Impl { .. })
+            && let Ok(instance) =
+                specialization_graph::assoc_def(tcx, instance_parent_def_id, deref_target_def_id)
+            && instance.is_final()
+        {
+            Some(tcx.def_span(instance.item.def_id))
+        } else {
+            None
+        };
+        return CallKind::DerefCoercion {
+            deref_target_ty,
+            deref_target_span,
+            self_ty: method_args.type_at(0),
+        };
+    }
+
+    // This isn't a 'special' use of `self`
+    debug!(?method_did, ?fn_call_span);
+    let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
+        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+    {
+        Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
+    } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
+        && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
+    {
+        Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
+    } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
+        if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
+            Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
+        } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
+            Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
         } else {
             None
         }
+    } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
+        && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
+    {
+        Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
+    } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
+        Some((CallDesugaringKind::Await, method_args.type_at(0)))
     } else {
         None
     };
-
-    kind.unwrap_or_else(|| {
-        // This isn't a 'special' use of `self`
-        debug!(?method_did, ?fn_call_span);
-        let desugaring = if tcx.is_lang_item(method_did, LangItem::IntoIterIntoIter)
-            && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
-        {
-            Some((CallDesugaringKind::ForLoopIntoIter, method_args.type_at(0)))
-        } else if tcx.is_lang_item(method_did, LangItem::IteratorNext)
-            && fn_call_span.desugaring_kind() == Some(DesugaringKind::ForLoop)
-        {
-            Some((CallDesugaringKind::ForLoopNext, method_args.type_at(0)))
-        } else if fn_call_span.desugaring_kind() == Some(DesugaringKind::QuestionMark) {
-            if tcx.is_lang_item(method_did, LangItem::TryTraitBranch) {
-                Some((CallDesugaringKind::QuestionBranch, method_args.type_at(0)))
-            } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromResidual) {
-                Some((CallDesugaringKind::QuestionFromResidual, method_args.type_at(0)))
-            } else {
-                None
-            }
-        } else if tcx.is_lang_item(method_did, LangItem::TryTraitFromOutput)
-            && fn_call_span.desugaring_kind() == Some(DesugaringKind::TryBlock)
-        {
-            Some((CallDesugaringKind::TryBlockFromOutput, method_args.type_at(0)))
-        } else if fn_call_span.is_desugaring(DesugaringKind::Await) {
-            Some((CallDesugaringKind::Await, method_args.type_at(0)))
-        } else {
-            None
-        };
-        CallKind::Normal { self_arg, desugaring, method_did, method_args }
-    })
+    CallKind::Normal { self_arg, desugaring, method_did, method_args }
 }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
index b108a9352a53d..cd4f77bb4cfed 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -1,4 +1,5 @@
 pub mod ambiguity;
+pub mod call_kind;
 mod fulfillment_errors;
 pub mod on_unimplemented;
 mod overflow;
diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.rs b/tests/ui/self/arbitrary-self-from-method-substs-ice.rs
index 2c0f25fc6ff93..2d6df816bb1ec 100644
--- a/tests/ui/self/arbitrary-self-from-method-substs-ice.rs
+++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.rs
@@ -11,7 +11,7 @@ impl Foo {
         //~^ ERROR invalid generic `self` parameter type
         //~| ERROR destructor of `R` cannot be evaluated at compile-time
         self.0
-        //~^ ERROR cannot call conditionally-const method `<R as Deref>::deref` in constant function
+        //~^ ERROR cannot perform conditionally-const deref coercion on `R` in constant functions
     }
 }
 
diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
index 90b63249eca1a..e6319d5a2c9c4 100644
--- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
+++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr
@@ -1,9 +1,10 @@
-error[E0658]: cannot call conditionally-const method `<R as Deref>::deref` in constant functions
+error[E0658]: cannot perform conditionally-const deref coercion on `R` in constant functions
   --> $DIR/arbitrary-self-from-method-substs-ice.rs:13:9
    |
 LL |         self.0
    |         ^^^^^^
    |
+   = note: attempting to deref into `Foo`
    = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
    = note: see issue #67792 <https://github.com/rust-lang/rust/issues/67792> for more information
    = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable