diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index ce22ab937391e..a50e69369617a 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -15,7 +15,7 @@ use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; -use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, SimplifiedType, TreatParams, simplify_type}; use rustc_middle::ty::{ self, AssocContainer, AssocItem, GenericArgs, GenericArgsRef, GenericParamDefKind, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, Unnormalized, Upcast, @@ -1709,6 +1709,36 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { None } + /// A cheap pre-filter: if both the receiver type and the candidate + /// method's declared self type simplify to concrete, different type + /// heads, the full `consider_probe` relation cannot succeed. This makes + /// the by-value probe pass nearly free for `&self`/`&mut self` methods — + /// the overwhelmingly common kind — which would otherwise pay for a + /// snapshot, fresh args and normalization per candidate just to fail the + /// self type unification. + fn candidate_self_head_may_unify( + &self, + self_ty_head: Option, + candidate: &Candidate<'tcx>, + ) -> bool { + // Only method-call probes relate the receiver against the method's + // transformed self type; path probes (UFCS) relate against the + // impl's self type, where the method's own self shape is irrelevant. + if self.mode != Mode::MethodCall { + return true; + } + let Some(self_ty_head) = self_ty_head else { return true }; + if !candidate.item.is_method() { + return true; + } + let declared_self = + self.tcx.fn_sig(candidate.item.def_id).skip_binder().skip_binder().inputs()[0]; + match simplify_type(self.tcx, declared_self, TreatParams::InstantiateWithInfer) { + Some(declared_head) => declared_head == self_ty_head, + None => true, + } + } + fn consider_candidates( &self, self_ty: Ty<'tcx>, @@ -1717,6 +1747,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { pick_diag_hints: &mut PickDiagHints<'_, 'tcx>, pick_constraints: Option<&PickConstraintsForShadowed>, ) -> Option> { + let self_ty_head = simplify_type(self.tcx, self_ty, TreatParams::InstantiateWithInfer); let mut applicable_candidates: Vec<_> = candidates .iter() .filter(|candidate| { @@ -1724,6 +1755,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> { .map(|pick_constraints| pick_constraints.candidate_may_shadow(&candidate)) .unwrap_or(true) }) + .filter(|candidate| self.candidate_self_head_may_unify(self_ty_head, candidate)) .map(|probe| { ( probe,