1
Fork 0

Rollup merge of #110514 - compiler-errors:remove-find_map_relevant_impl, r=b-naber

Remove `find_map_relevant_impl`

Fixes #108895
This commit is contained in:
Matthias Krüger 2023-04-24 07:53:24 +02:00 committed by GitHub
commit d60c64a0c5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 168 additions and 156 deletions

View file

@ -139,11 +139,38 @@ impl<'tcx> TyCtxt<'tcx> {
treat_projections: TreatProjections, treat_projections: TreatProjections,
mut f: impl FnMut(DefId), mut f: impl FnMut(DefId),
) { ) {
let _: Option<()> = // FIXME: This depends on the set of all impls for the trait. That is
self.find_map_relevant_impl(trait_def_id, self_ty, treat_projections, |did| { // unfortunate wrt. incremental compilation.
f(did); //
None // 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);
}
// Note that we're using `TreatParams::ForLookup` to query `non_blanket_impls` while using
// `TreatParams::AsCandidateKey` while actually adding them.
let treat_params = match treat_projections {
TreatProjections::NextSolverLookup => TreatParams::NextSolverLookup,
TreatProjections::ForLookup => TreatParams::ForLookup,
};
// 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.
if let Some(simp) = fast_reject::simplify_type(self, self_ty, treat_params) {
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
for &impl_def_id in impls {
f(impl_def_id);
}
}
} else {
for &impl_def_id in impls.non_blanket_impls.values().flatten() {
f(impl_def_id);
}
}
} }
/// `trait_def_id` MUST BE the `DefId` of a trait. /// `trait_def_id` MUST BE the `DefId` of a trait.
@ -162,59 +189,6 @@ impl<'tcx> TyCtxt<'tcx> {
[].iter().copied() [].iter().copied()
} }
/// Applies function to every impl that could possibly match the self type `self_ty` and returns
/// the first non-none value.
///
/// `trait_def_id` MUST BE the `DefId` of a trait.
pub fn find_map_relevant_impl<T>(
self,
trait_def_id: DefId,
self_ty: Ty<'tcx>,
treat_projections: TreatProjections,
mut f: impl FnMut(DefId) -> Option<T>,
) -> Option<T> {
// 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() {
if let result @ Some(_) = f(impl_def_id) {
return result;
}
}
// Note that we're using `TreatParams::ForLookup` to query `non_blanket_impls` while using
// `TreatParams::AsCandidateKey` while actually adding them.
let treat_params = match treat_projections {
TreatProjections::NextSolverLookup => TreatParams::NextSolverLookup,
TreatProjections::ForLookup => TreatParams::ForLookup,
};
// 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.
if let Some(simp) = fast_reject::simplify_type(self, self_ty, treat_params) {
if let Some(impls) = impls.non_blanket_impls.get(&simp) {
for &impl_def_id in impls {
if let result @ Some(_) = f(impl_def_id) {
return result;
}
}
}
} else {
for &impl_def_id in impls.non_blanket_impls.values().flatten() {
if let result @ Some(_) = f(impl_def_id) {
return result;
}
}
}
None
}
/// Returns an iterator containing all impls for `trait_def_id`. /// Returns an iterator containing all impls for `trait_def_id`.
/// ///
/// `trait_def_id` MUST BE the `DefId` of a trait. /// `trait_def_id` MUST BE the `DefId` of a trait.

View file

@ -2,7 +2,6 @@
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags; use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use crate::mir; use crate::mir;
use crate::ty::fast_reject::TreatProjections;
use crate::ty::layout::IntegerExt; use crate::ty::layout::IntegerExt;
use crate::ty::{ use crate::ty::{
self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
@ -359,21 +358,29 @@ impl<'tcx> TyCtxt<'tcx> {
self.ensure().coherent_trait(drop_trait); self.ensure().coherent_trait(drop_trait);
let ty = self.type_of(adt_did).subst_identity(); let ty = self.type_of(adt_did).subst_identity();
let (did, constness) = self.find_map_relevant_impl( let mut dtor_candidate = None;
drop_trait, self.for_each_relevant_impl(drop_trait, ty, |impl_did| {
ty, let Some(item_id) = self.associated_item_def_ids(impl_did).first() else {
// FIXME: This could also be some other mode, like "unexpected" self.sess.delay_span_bug(self.def_span(impl_did), "Drop impl without drop function");
TreatProjections::ForLookup, return;
|impl_did| { };
if let Some(item_id) = self.associated_item_def_ids(impl_did).first() {
if validate(self, impl_did).is_ok() {
return Some((*item_id, self.constness(impl_did)));
}
}
None
},
)?;
if validate(self, impl_did).is_err() {
// Already `ErrorGuaranteed`, no need to delay a span bug here.
return;
}
if let Some((old_item_id, _)) = dtor_candidate {
self.sess
.struct_span_err(self.def_span(item_id), "multiple drop impls found")
.span_note(self.def_span(old_item_id), "other impl here")
.delay_as_bug();
}
dtor_candidate = Some((*item_id, self.constness(impl_did)));
});
let (did, constness) = dtor_candidate?;
Some(ty::Destructor { did, constness }) Some(ty::Destructor { did, constness })
} }

View file

@ -645,12 +645,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
// FIXME: Handling opaques here is kinda sus. Especially because we // FIXME: Handling opaques here is kinda sus. Especially because we
// simplify them to PlaceholderSimplifiedType. // simplify them to PlaceholderSimplifiedType.
| ty::Alias(ty::Opaque, _) => { | ty::Alias(ty::Opaque, _) => {
if let Some(def_id) = self.tcx().find_map_relevant_impl( let mut disqualifying_impl = None;
self.tcx().for_each_relevant_impl_treating_projections(
goal.predicate.def_id(), goal.predicate.def_id(),
goal.predicate.self_ty(), goal.predicate.self_ty(),
TreatProjections::NextSolverLookup, TreatProjections::NextSolverLookup,
Some, |impl_def_id| {
) { disqualifying_impl = Some(impl_def_id);
},
);
if let Some(def_id) = disqualifying_impl {
debug!(?def_id, ?goal, "disqualified auto-trait implementation"); debug!(?def_id, ?goal, "disqualified auto-trait implementation");
// No need to actually consider the candidate here, // No need to actually consider the candidate here,
// since we do that in `consider_impl_candidate`. // since we do that in `consider_impl_candidate`.

View file

@ -32,7 +32,6 @@ use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_middle::traits::select::OverflowError; use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable; use rustc_middle::ty::abstract_const::NotConstEvaluatable;
use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fast_reject::TreatProjections;
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable}; use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print}; use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{ use rustc_middle::ty::{
@ -1836,30 +1835,34 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}); });
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}"); let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
let secondary_span = match predicate.kind().skip_binder() { let secondary_span = (|| {
ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => self let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) =
.tcx predicate.kind().skip_binder()
.opt_associated_item(proj.projection_ty.def_id) else {
.and_then(|trait_assoc_item| { return None;
self.tcx };
.trait_of_item(proj.projection_ty.def_id)
.map(|id| (trait_assoc_item, id)) let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_ty.def_id)?;
})
.and_then(|(trait_assoc_item, id)| {
let trait_assoc_ident = trait_assoc_item.ident(self.tcx); let trait_assoc_ident = trait_assoc_item.ident(self.tcx);
self.tcx.find_map_relevant_impl(
id, let mut associated_items = vec![];
self.tcx.for_each_relevant_impl(
self.tcx.trait_of_item(proj.projection_ty.def_id)?,
proj.projection_ty.self_ty(), proj.projection_ty.self_ty(),
TreatProjections::ForLookup, |impl_def_id| {
|did| { associated_items.extend(
self.tcx self.tcx
.associated_items(did) .associated_items(impl_def_id)
.in_definition_order() .in_definition_order()
.find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident) .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident),
);
}, },
) );
})
.and_then(|item| match self.tcx.hir().get_if_local(item.def_id) { let [associated_item]: &[ty::AssocItem] = &associated_items[..] else {
return None;
};
match self.tcx.hir().get_if_local(associated_item.def_id) {
Some( Some(
hir::Node::TraitItem(hir::TraitItem { hir::Node::TraitItem(hir::TraitItem {
kind: hir::TraitItemKind::Type(_, Some(ty)), kind: hir::TraitItemKind::Type(_, Some(ty)),
@ -1884,9 +1887,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
)), )),
)), )),
_ => None, _ => None,
}), }
_ => None, })();
};
self.note_type_err( self.note_type_err(
&mut diag, &mut diag,
&obligation.cause, &obligation.cause,
@ -2228,14 +2231,18 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic, err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>, trait_ref: &ty::PolyTraitRef<'tcx>,
) -> bool { ) -> bool {
let get_trait_impl = |trait_def_id| { let get_trait_impls = |trait_def_id| {
self.tcx.find_map_relevant_impl( let mut trait_impls = vec![];
self.tcx.for_each_relevant_impl(
trait_def_id, trait_def_id,
trait_ref.skip_binder().self_ty(), trait_ref.skip_binder().self_ty(),
TreatProjections::ForLookup, |impl_def_id| {
Some, trait_impls.push(impl_def_id);
) },
);
trait_impls
}; };
let required_trait_path = self.tcx.def_path_str(trait_ref.def_id()); let required_trait_path = self.tcx.def_path_str(trait_ref.def_id());
let traits_with_same_path: std::collections::BTreeSet<_> = self let traits_with_same_path: std::collections::BTreeSet<_> = self
.tcx .tcx
@ -2245,9 +2252,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.collect(); .collect();
let mut suggested = false; let mut suggested = false;
for trait_with_same_path in traits_with_same_path { for trait_with_same_path in traits_with_same_path {
if let Some(impl_def_id) = get_trait_impl(trait_with_same_path) { let trait_impls = get_trait_impls(trait_with_same_path);
let impl_span = self.tcx.def_span(impl_def_id); if trait_impls.is_empty() {
err.span_help(impl_span, "trait impl with same name found"); continue;
}
let impl_spans: Vec<_> =
trait_impls.iter().map(|impl_def_id| self.tcx.def_span(*impl_def_id)).collect();
err.span_help(
impl_spans,
format!("trait impl{} with same name found", pluralize!(trait_impls.len())),
);
let trait_crate = self.tcx.crate_name(trait_with_same_path.krate); let trait_crate = self.tcx.crate_name(trait_with_same_path.krate);
let crate_msg = format!( let crate_msg = format!(
"perhaps two different versions of crate `{}` are being used?", "perhaps two different versions of crate `{}` are being used?",
@ -2256,7 +2270,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note(&crate_msg); err.note(&crate_msg);
suggested = true; suggested = true;
} }
}
suggested suggested
} }

View file

@ -11,7 +11,7 @@ use hir::LangItem;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_infer::traits::ObligationCause; use rustc_infer::traits::ObligationCause;
use rustc_infer::traits::{Obligation, SelectionError, TraitObligation}; use rustc_infer::traits::{Obligation, SelectionError, TraitObligation};
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_middle::ty::{self, Ty, TypeVisitableExt};
use crate::traits; use crate::traits;
@ -875,12 +875,24 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
} }
ty::Adt(..) => { ty::Adt(..) => {
// Find a custom `impl Drop` impl, if it exists let mut relevant_impl = None;
let relevant_impl = self.tcx().find_map_relevant_impl( self.tcx().for_each_relevant_impl(
self.tcx().require_lang_item(LangItem::Drop, None), self.tcx().require_lang_item(LangItem::Drop, None),
obligation.predicate.skip_binder().trait_ref.self_ty(), obligation.predicate.skip_binder().trait_ref.self_ty(),
TreatProjections::ForLookup, |impl_def_id| {
Some, if let Some(old_impl_def_id) = relevant_impl {
self.tcx()
.sess
.struct_span_err(
self.tcx().def_span(impl_def_id),
"multiple drop impls found",
)
.span_note(self.tcx().def_span(old_impl_def_id), "other impl here")
.delay_as_bug();
}
relevant_impl = Some(impl_def_id);
},
); );
if let Some(impl_def_id) = relevant_impl { if let Some(impl_def_id) = relevant_impl {

View file

@ -13,7 +13,7 @@ use rustc_hir::def::Namespace::*;
use rustc_hir::def::{DefKind, Namespace, PerNS}; use rustc_hir::def::{DefKind, Namespace, PerNS};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID}; use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
use rustc_hir::Mutability; use rustc_hir::Mutability;
use rustc_middle::ty::{fast_reject::TreatProjections, Ty, TyCtxt}; use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{bug, ty}; use rustc_middle::{bug, ty};
use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution}; use rustc_resolve::rustdoc::{has_primitive_or_keyword_docs, prepare_to_doc_link_resolution};
use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics}; use rustc_resolve::rustdoc::{strip_generics_from_path, MalformedGenerics};
@ -772,11 +772,10 @@ fn trait_impls_for<'a>(
module: DefId, module: DefId,
) -> FxHashSet<(DefId, DefId)> { ) -> FxHashSet<(DefId, DefId)> {
let tcx = cx.tcx; let tcx = cx.tcx;
let iter = tcx.doc_link_traits_in_scope(module).iter().flat_map(|&trait_| { let mut impls = FxHashSet::default();
trace!("considering explicit impl for trait {:?}", trait_);
// Look at each trait implementation to see if it's an impl for `did` for &trait_ in tcx.doc_link_traits_in_scope(module) {
tcx.find_map_relevant_impl(trait_, ty, TreatProjections::ForLookup, |impl_| { tcx.for_each_relevant_impl(trait_, ty, |impl_| {
let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl"); let trait_ref = tcx.impl_trait_ref(impl_).expect("this is not an inherent impl");
// Check if these are the same type. // Check if these are the same type.
let impl_type = trait_ref.skip_binder().self_ty(); let impl_type = trait_ref.skip_binder().self_ty();
@ -800,10 +799,13 @@ fn trait_impls_for<'a>(
_ => false, _ => false,
}; };
if saw_impl { Some((impl_, trait_)) } else { None } if saw_impl {
}) impls.insert((impl_, trait_));
}
}); });
iter.collect() }
impls
} }
/// Check for resolve collisions between a trait and its derive. /// Check for resolve collisions between a trait and its derive.