From a070b70f5fccf19e6ac3889f2454439e609be02f Mon Sep 17 00:00:00 2001 From: Adwin White Date: Fri, 24 Jul 2026 18:05:13 +0800 Subject: [PATCH] unify `for_each_relevant_impl` --- .../src/ty/context/impl_interner.rs | 134 +------------- compiler/rustc_middle/src/ty/trait_def.rs | 164 +++++++++++++++--- tests/ui/methods/inherent-bound-in-probe.rs | 2 +- .../ui/methods/inherent-bound-in-probe.stderr | 4 +- 4 files changed, 150 insertions(+), 154 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 052a937cf5017..25b0f89339acc 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -537,135 +537,19 @@ impl<'tcx> Interner for TyCtxt<'tcx> { .map(|assoc_item| assoc_item.def_id) } - // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`, - // since we want to skip over blanket impls for non-rigid aliases, and also we - // only want to consider types that *actually* unify with float/int vars. + // This signature is a bit different from `TyCtxt::for_each_relevant_impl`. + // While rustc only needs self_ty, rust-analyzer's impl needs to use all the args. fn for_each_relevant_impl( self, trait_ref: ty::TraitRef<'tcx>, - mut f: impl FnMut(DefId) -> R, + f: impl FnMut(DefId) -> R, ) -> R { - macro_rules! ret { - ($e: expr) => { - match $e.branch() { - ControlFlow::Break(b) => return R::from_residual(b), - ControlFlow::Continue(()) => {} - } - }; - } - - let trait_def_id = trait_ref.def_id; - let self_ty = trait_ref.self_ty(); - let tcx = self; - let trait_impls = tcx.trait_impls_of(trait_def_id); - let mut consider_impls_for_simplified_type = |simp| { - if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { - for &impl_def_id in impls_for_type { - ret!(f(impl_def_id)) - } - } - - R::output() - }; - - match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Foreign(_) - | ty::Str - | ty::Array(_, _) - | ty::Pat(_, _) - | ty::Slice(_) - | ty::RawPtr(_, _) - | ty::Ref(_, _, _) - | ty::FnDef(_, _) - | ty::FnPtr(..) - | ty::Dynamic(_, _) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(_, _) - | ty::Never - | ty::Tuple(_) - | ty::UnsafeBinder(_) => { - if let Some(simp) = ty::fast_reject::simplify_type( - tcx, - self_ty, - ty::fast_reject::TreatParams::AsRigid, - ) { - ret!(consider_impls_for_simplified_type(simp)); - } - } - - // HACK: For integer and float variables we have to manually look at all impls - // which have some integer or float as a self type. - ty::Infer(ty::IntVar(_)) => { - use ty::IntTy::*; - use ty::UintTy::*; - // This causes a compiler error if any new integer kinds are added. - let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; - let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; - let possible_integers = [ - // signed integers - ty::SimplifiedType::Int(I8), - ty::SimplifiedType::Int(I16), - ty::SimplifiedType::Int(I32), - ty::SimplifiedType::Int(I64), - ty::SimplifiedType::Int(I128), - ty::SimplifiedType::Int(Isize), - // unsigned integers - ty::SimplifiedType::Uint(U8), - ty::SimplifiedType::Uint(U16), - ty::SimplifiedType::Uint(U32), - ty::SimplifiedType::Uint(U64), - ty::SimplifiedType::Uint(U128), - ty::SimplifiedType::Uint(Usize), - ]; - for simp in possible_integers { - ret!(consider_impls_for_simplified_type(simp)); - } - } - - ty::Infer(ty::FloatVar(_)) => { - // This causes a compiler error if any new float kinds are added. - let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); - let possible_floats = [ - ty::SimplifiedType::Float(ty::FloatTy::F16), - ty::SimplifiedType::Float(ty::FloatTy::F32), - ty::SimplifiedType::Float(ty::FloatTy::F64), - ty::SimplifiedType::Float(ty::FloatTy::F128), - ]; - - for simp in possible_floats { - ret!(consider_impls_for_simplified_type(simp)); - } - } - - // The only traits applying to aliases and placeholders are blanket impls. - // - // Impls which apply to an alias after normalization are handled by - // `assemble_candidates_after_normalizing_self_ty`. - ty::Alias(ty::IsRigid::Yes, _) | ty::Placeholder(..) | ty::Error(_) => (), - // FIXME(-Znext-solver=no): Need to support aliases not marked as - // rigid for the old solver. - ty::Alias(ty::IsRigid::No, _) => (), - - // FIXME: These should ideally not exist as a self type. It would be nice for - // the builtin auto trait impls of coroutines to instead directly recurse - // into the witness. - ty::CoroutineWitness(..) => (), - - // These variants should not exist as a self type. - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Param(_) - | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"), - } - - #[allow(rustc::usage_of_type_ir_traits)] - self.for_each_blanket_impl(trait_def_id, f) + let self_ty = trait_ref.args.type_at(0); + debug_assert!( + !matches!(self_ty.kind(), ty::Infer(ty::TyVar(_)) | ty::Param(_) | ty::Bound(_, _)), + "we should not have them as self ty in the next solver" + ); + TyCtxt::for_each_relevant_impl(self, trait_ref.def_id, self_ty, f) } fn for_each_blanket_impl( self, diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs index 29e9d1243f0ca..681a8793c6886 100644 --- a/compiler/rustc_middle/src/ty/trait_def.rs +++ b/compiler/rustc_middle/src/ty/trait_def.rs @@ -1,4 +1,5 @@ use std::iter; +use std::ops::ControlFlow; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::ErrorGuaranteed; @@ -13,7 +14,7 @@ use crate::query::LocalCrate; use crate::traits::specialization_graph; use crate::ty::fast_reject::{self, SimplifiedType, TreatParams}; use crate::ty::print::{with_crate_prefix, with_no_trimmed_paths}; -use crate::ty::{Ident, Ty, TyCtxt}; +use crate::ty::{self, Ident, Interner, Ty, TyCtxt, VisitorResult}; /// A trait's definition with type information. #[derive(StableHash, Encodable, Decodable)] @@ -182,40 +183,151 @@ impl<'tcx> TyCtxt<'tcx> { /// Iterate over every impl that could possibly match the self type `self_ty`. /// /// `trait_def_id` MUST BE the `DefId` of a trait. - pub fn for_each_relevant_impl( + pub fn for_each_relevant_impl( self, trait_def_id: DefId, self_ty: Ty<'tcx>, - mut f: impl FnMut(DefId), - ) { - // FIXME: This depends on the set of all impls for the trait. That is - // unfortunate wrt. incremental compilation. - // - // If we want to be faster, we could have separate queries for - // blanket and non-blanket impls, and compare them separately. - let impls = self.trait_impls_of(trait_def_id); - - for &impl_def_id in impls.blanket_impls.iter() { - f(impl_def_id); + mut f: impl FnMut(DefId) -> R, + ) -> R { + macro_rules! ret { + ($e: expr) => { + match $e.branch() { + ControlFlow::Break(b) => return R::from_residual(b), + ControlFlow::Continue(()) => {} + } + }; } - // This way, when searching for some impl for `T: Trait`, we do not look at any impls - // whose outer level is not a parameter or projection. Especially for things like - // `T: Clone` this is incredibly useful as we would otherwise look at all the impls - // of `Clone` for `Option`, `Vec`, `ConcreteType` and so on. - // Note that we're using `TreatParams::AsRigid` to query `non_blanket_impls` while using - // `TreatParams::InstantiateWithInfer` while actually adding them. - if let Some(simp) = fast_reject::simplify_type(self, self_ty, TreatParams::AsRigid) { - if let Some(impls) = impls.non_blanket_impls.get(&simp) { - for &impl_def_id in impls { - f(impl_def_id); + let tcx = self; + let trait_impls = tcx.trait_impls_of(trait_def_id); + let mut consider_impls_for_simplified_type = |simp| { + if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) { + for &impl_def_id in impls_for_type { + ret!(f(impl_def_id)) } } - } else { - for &impl_def_id in impls.non_blanket_impls.values().flatten() { - f(impl_def_id); + + R::output() + }; + + match self_ty.kind() { + ty::Bool + | ty::Char + | ty::Int(_) + | ty::Uint(_) + | ty::Float(_) + | ty::Adt(_, _) + | ty::Foreign(_) + | ty::Str + | ty::Array(_, _) + | ty::Slice(_) + | ty::RawPtr(_, _) + | ty::Ref(_, _, _) + | ty::FnDef(_, _) + | ty::FnPtr(..) + | ty::Dynamic(_, _) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Coroutine(_, _) + | ty::Never + | ty::Tuple(_) + | ty::UnsafeBinder(_) => { + let simp = ty::fast_reject::simplify_type( + tcx, + self_ty, + ty::fast_reject::TreatParams::AsRigid, + ) + .unwrap(); + ret!(consider_impls_for_simplified_type(simp)); + } + + // HACK: For integer and float variables we have to manually look at all impls + // which have some integer or float as a self type. + ty::Infer(ty::IntVar(_)) => { + use ty::IntTy::*; + use ty::UintTy::*; + // This causes a compiler error if any new integer kinds are added. + let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy; + let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy; + let possible_integers = [ + // signed integers + ty::SimplifiedType::Int(I8), + ty::SimplifiedType::Int(I16), + ty::SimplifiedType::Int(I32), + ty::SimplifiedType::Int(I64), + ty::SimplifiedType::Int(I128), + ty::SimplifiedType::Int(Isize), + // unsigned integers + ty::SimplifiedType::Uint(U8), + ty::SimplifiedType::Uint(U16), + ty::SimplifiedType::Uint(U32), + ty::SimplifiedType::Uint(U64), + ty::SimplifiedType::Uint(U128), + ty::SimplifiedType::Uint(Usize), + ]; + for simp in possible_integers { + ret!(consider_impls_for_simplified_type(simp)); + } + } + + ty::Infer(ty::FloatVar(_)) => { + // This causes a compiler error if any new float kinds are added. + let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128); + let possible_floats = [ + ty::SimplifiedType::Float(ty::FloatTy::F16), + ty::SimplifiedType::Float(ty::FloatTy::F32), + ty::SimplifiedType::Float(ty::FloatTy::F64), + ty::SimplifiedType::Float(ty::FloatTy::F128), + ]; + + for simp in possible_floats { + ret!(consider_impls_for_simplified_type(simp)); + } + } + + // Pattern type might not have a simplified type. + ty::Pat(_, _) => { + if let Some(simp) = ty::fast_reject::simplify_type( + tcx, + self_ty, + ty::fast_reject::TreatParams::AsRigid, + ) { + ret!(consider_impls_for_simplified_type(simp)); + } + } + + // This is only for diagnostics and normally ty vars should be handled by the callers. + ty::Infer(ty::TyVar(_)) => { + for &impl_def_id in trait_impls.non_blanket_impls().values().flatten() { + ret!(f(impl_def_id)); + } + } + + // The only traits applying to aliases and placeholders are blanket impls. + // + // Impls which apply to an alias after normalization are handled by + // `assemble_candidates_after_normalizing_self_ty`. + ty::Alias(ty::IsRigid::Yes, _) | ty::Placeholder(..) | ty::Error(_) => (), + // FIXME(-Znext-solver=no): Need to support aliases not marked as + // rigid for the old solver. + ty::Alias(ty::IsRigid::No, _) => (), + + // FIXME: These should ideally not exist as a self type. It would be nice for + // the builtin auto trait impls of coroutines to instead directly recurse + // into the witness. + ty::CoroutineWitness(..) => (), + + // These are used in diagnostics or in the old solver. + ty::Param(_) | ty::Bound(_, _) => (), + + // These variants should not exist as a self type. + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("unexpected self type: {self_ty:?}"); } } + + #[allow(rustc::usage_of_type_ir_traits)] + self.for_each_blanket_impl(trait_def_id, f) } /// `trait_def_id` MUST BE the `DefId` of a trait. diff --git a/tests/ui/methods/inherent-bound-in-probe.rs b/tests/ui/methods/inherent-bound-in-probe.rs index 39b4ba983e442..43ccca394a52e 100644 --- a/tests/ui/methods/inherent-bound-in-probe.rs +++ b/tests/ui/methods/inherent-bound-in-probe.rs @@ -41,7 +41,7 @@ where fn into_iter(self) -> Self::IntoIter { Helper::new(&self.0) - //~^ ERROR overflow evaluating the requirement `&_: IntoIterator` + //~^ ERROR: overflow evaluating the requirement `&HashMap<_, _, _, _>: IntoIterator` } } diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index 8564fcaf8eac2..dacb4523234a5 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -12,14 +12,14 @@ LL | struct Helper<'a, T> note: required by a bound in `std::iter::IntoIterator::IntoIter` --> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL -error[E0275]: overflow evaluating the requirement `&_: IntoIterator` +error[E0275]: overflow evaluating the requirement `&HashMap<_, _, _, _>: IntoIterator` --> $DIR/inherent-bound-in-probe.rs:43:9 | LL | Helper::new(&self.0) | ^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inherent_bound_in_probe`) -note: required for `&BitReaderWrapper<_>` to implement `IntoIterator` +note: required for `&BitReaderWrapper>` to implement `IntoIterator` --> $DIR/inherent-bound-in-probe.rs:33:13 | LL | impl<'a, T> IntoIterator for &'a BitReaderWrapper