Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 9 additions & 125 deletions compiler/rustc_middle/src/ty/context/impl_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<R: VisitorResult>(
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<R: VisitorResult>(
self,
Expand Down
164 changes: 138 additions & 26 deletions compiler/rustc_middle/src/ty/trait_def.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::iter;
use std::ops::ControlFlow;

use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::ErrorGuaranteed;
Expand All @@ -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)]
Expand Down Expand Up @@ -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<R: VisitorResult>(
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<T>`, `Vec<T>`, `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.
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/methods/inherent-bound-in-probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
}

Expand Down
4 changes: 2 additions & 2 deletions tests/ui/methods/inherent-bound-in-probe.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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<HashMap<_, _, _, _>>` to implement `IntoIterator`
--> $DIR/inherent-bound-in-probe.rs:33:13
|
LL | impl<'a, T> IntoIterator for &'a BitReaderWrapper<T>
Expand Down
Loading