blob: 799bfb3b4d15ce12f523a759f9fe16214084f38e [file] [log] [blame]
//! This module is concerned with finding methods that a given type provides.
//! For details about how this works in rustc, see the method lookup page in the
//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
//! and the corresponding code mostly in rustc_hir_analysis/check/method/probe.rs.
mod confirm;
mod probe;
use either::Either;
use hir_expand::name::Name;
use span::Edition;
use tracing::{debug, instrument};
use base_db::Crate;
use hir_def::{
AssocItemId, BlockId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId,
ModuleId, TraitId,
expr_store::path::GenericArgs as HirGenericArgs,
hir::ExprId,
nameres::{DefMap, block_def_map, crate_def_map},
resolver::Resolver,
};
use intern::{Symbol, sym};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_type_ir::{
TypeVisitableExt,
fast_reject::{TreatParams, simplify_type},
inherent::{BoundExistentialPredicates, IntoKind, SliceLike},
};
use stdx::impl_from;
use triomphe::Arc;
use crate::{
TraitEnvironment, all_super_traits,
db::HirDatabase,
infer::{InferenceContext, unify::InferenceTable},
lower::GenericPredicates,
next_solver::{
Binder, ClauseKind, DbInterner, FnSig, GenericArgs, PredicateKind, SimplifiedType,
SolverDefId, TraitRef, Ty, TyKind, TypingMode,
infer::{
BoundRegionConversionTime, DbInternerInferExt, InferCtxt, InferOk,
select::ImplSource,
traits::{Obligation, ObligationCause, PredicateObligations},
},
obligation_ctxt::ObligationCtxt,
util::clauses_as_obligations,
},
};
pub use self::probe::{
Candidate, CandidateKind, CandidateStep, CandidateWithPrivate, Mode, Pick, PickKind,
};
#[derive(Debug, Clone)]
pub struct MethodResolutionUnstableFeatures {
arbitrary_self_types: bool,
arbitrary_self_types_pointers: bool,
supertrait_item_shadowing: bool,
}
impl MethodResolutionUnstableFeatures {
pub fn from_def_map(def_map: &DefMap) -> Self {
Self {
arbitrary_self_types: def_map.is_unstable_feature_enabled(&sym::arbitrary_self_types),
arbitrary_self_types_pointers: def_map
.is_unstable_feature_enabled(&sym::arbitrary_self_types_pointers),
supertrait_item_shadowing: def_map
.is_unstable_feature_enabled(&sym::supertrait_item_shadowing),
}
}
}
pub struct MethodResolutionContext<'a, 'db> {
pub infcx: &'a InferCtxt<'db>,
pub resolver: &'a Resolver<'db>,
pub env: &'a TraitEnvironment<'db>,
pub traits_in_scope: &'a FxHashSet<TraitId>,
pub edition: Edition,
pub unstable_features: &'a MethodResolutionUnstableFeatures,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CandidateId {
FunctionId(FunctionId),
ConstId(ConstId),
}
impl_from!(FunctionId, ConstId for CandidateId);
impl CandidateId {
fn container(self, db: &dyn HirDatabase) -> ItemContainerId {
match self {
CandidateId::FunctionId(id) => id.loc(db).container,
CandidateId::ConstId(id) => id.loc(db).container,
}
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct MethodCallee<'db> {
/// Impl method ID, for inherent methods, or trait method ID, otherwise.
pub def_id: FunctionId,
pub args: GenericArgs<'db>,
/// Instantiated method signature, i.e., it has been
/// instantiated, normalized, and has had late-bound
/// lifetimes replaced with inference variables.
pub sig: FnSig<'db>,
}
#[derive(Debug)]
pub enum MethodError<'db> {
/// Did not find an applicable method.
NoMatch,
/// Multiple methods might apply.
Ambiguity(Vec<CandidateSource>),
/// Found an applicable method, but it is not visible.
PrivateMatch(Pick<'db>),
/// Found a `Self: Sized` bound where `Self` is a trait object.
IllegalSizedBound { candidates: Vec<FunctionId>, needs_mut: bool },
/// Error has already been emitted, no need to emit another one.
ErrorReported,
}
// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum CandidateSource {
Impl(ImplId),
Trait(TraitId),
}
impl<'a, 'db> InferenceContext<'a, 'db> {
/// Performs method lookup. If lookup is successful, it will return the callee
/// and store an appropriate adjustment for the self-expr. In some cases it may
/// report an error (e.g., invoking the `drop` method).
#[instrument(level = "debug", skip(self))]
pub(crate) fn lookup_method_including_private(
&mut self,
self_ty: Ty<'db>,
name: Name,
generic_args: Option<&HirGenericArgs>,
receiver: ExprId,
call_expr: ExprId,
) -> Result<(MethodCallee<'db>, bool), MethodError<'db>> {
let (pick, is_visible) = match self.lookup_probe(name, self_ty) {
Ok(it) => (it, true),
Err(MethodError::PrivateMatch(it)) => {
// FIXME: Report error.
(it, false)
}
Err(err) => return Err(err),
};
let result = self.confirm_method(&pick, self_ty, call_expr, generic_args);
debug!("result = {:?}", result);
if result.illegal_sized_bound {
// FIXME: Report an error.
}
self.write_expr_adj(receiver, result.adjustments);
self.write_method_resolution(call_expr, result.callee.def_id, result.callee.args);
Ok((result.callee, is_visible))
}
#[instrument(level = "debug", skip(self))]
pub(crate) fn lookup_probe(
&self,
method_name: Name,
self_ty: Ty<'db>,
) -> probe::PickResult<'db> {
self.with_method_resolution(|ctx| {
let pick = ctx.probe_for_name(probe::Mode::MethodCall, method_name, self_ty)?;
Ok(pick)
})
}
pub(crate) fn with_method_resolution<R>(
&self,
f: impl FnOnce(&MethodResolutionContext<'_, 'db>) -> R,
) -> R {
let traits_in_scope = self.get_traits_in_scope();
let traits_in_scope = match &traits_in_scope {
Either::Left(it) => it,
Either::Right(it) => *it,
};
let ctx = MethodResolutionContext {
infcx: &self.table.infer_ctxt,
resolver: &self.resolver,
env: &self.table.trait_env,
traits_in_scope,
edition: self.edition,
unstable_features: &self.unstable_features,
};
f(&ctx)
}
}
/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`.
///
/// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while
/// `AsInfer` just treats it as ambiguous and succeeds. This is necessary
/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque
/// types as rigid to support `impl Deref<Target = impl FnOnce()>` and
/// `Box<impl FnOnce()>`.
///
/// We only want to treat opaque types as rigid if we need to eagerly choose
/// between multiple candidates. We otherwise treat them as ordinary inference
/// variable to avoid rejecting otherwise correct code.
#[derive(Debug)]
#[expect(dead_code)]
pub(super) enum TreatNotYetDefinedOpaques {
AsInfer,
AsRigid,
}
impl<'db> InferenceTable<'db> {
/// `lookup_method_in_trait` is used for overloaded operators.
/// It does a very narrow slice of what the normal probe/confirm path does.
/// In particular, it doesn't really do any probing: it simply constructs
/// an obligation for a particular trait with the given self type and checks
/// whether that trait is implemented.
#[instrument(level = "debug", skip(self))]
pub(super) fn lookup_method_for_operator(
&self,
cause: ObligationCause,
method_name: Symbol,
trait_def_id: TraitId,
self_ty: Ty<'db>,
opt_rhs_ty: Option<Ty<'db>>,
treat_opaques: TreatNotYetDefinedOpaques,
) -> Option<InferOk<'db, MethodCallee<'db>>> {
// Construct a trait-reference `self_ty : Trait<input_tys>`
let args = GenericArgs::for_item(
self.interner(),
trait_def_id.into(),
|param_idx, param_id, _| match param_id {
GenericParamId::LifetimeParamId(_) | GenericParamId::ConstParamId(_) => {
unreachable!("did not expect operator trait to have lifetime/const")
}
GenericParamId::TypeParamId(_) => {
if param_idx == 0 {
self_ty.into()
} else if let Some(rhs_ty) = opt_rhs_ty {
assert_eq!(param_idx, 1, "did not expect >1 param on operator trait");
rhs_ty.into()
} else {
// FIXME: We should stop passing `None` for the failure case
// when probing for call exprs. I.e. `opt_rhs_ty` should always
// be set when it needs to be.
self.next_var_for_param(param_id)
}
}
},
);
let obligation = Obligation::new(
self.interner(),
cause,
self.trait_env.env,
TraitRef::new_from_args(self.interner(), trait_def_id.into(), args),
);
// Now we want to know if this can be matched
let matches_trait = match treat_opaques {
TreatNotYetDefinedOpaques::AsInfer => self.infer_ctxt.predicate_may_hold(&obligation),
TreatNotYetDefinedOpaques::AsRigid => {
self.infer_ctxt.predicate_may_hold_opaque_types_jank(&obligation)
}
};
if !matches_trait {
debug!("--> Cannot match obligation");
// Cannot be matched, no such method resolution is possible.
return None;
}
// Trait must have a method named `m_name` and it should not have
// type parameters or early-bound regions.
let interner = self.interner();
// We use `Ident::with_dummy_span` since no built-in operator methods have
// any macro-specific hygiene, so the span's context doesn't really matter.
let Some(method_item) =
trait_def_id.trait_items(self.db).method_by_name(&Name::new_symbol_root(method_name))
else {
panic!("expected associated item for operator trait")
};
let def_id = method_item;
debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
let mut obligations = PredicateObligations::new();
// Instantiate late-bound regions and instantiate the trait
// parameters into the method type to get the actual method type.
//
// N.B., instantiate late-bound regions before normalizing the
// function signature so that normalization does not need to deal
// with bound regions.
let fn_sig =
self.db.callable_item_signature(method_item.into()).instantiate(interner, args);
let fn_sig = self
.infer_ctxt
.instantiate_binder_with_fresh_vars(BoundRegionConversionTime::FnCall, fn_sig);
// Register obligations for the parameters. This will include the
// `Self` parameter, which in turn has a bound of the main trait,
// so this also effectively registers `obligation` as well. (We
// used to register `obligation` explicitly, but that resulted in
// double error messages being reported.)
//
// Note that as the method comes from a trait, it should not have
// any late-bound regions appearing in its bounds.
let bounds = GenericPredicates::query_all(self.db, method_item.into());
let bounds = clauses_as_obligations(
bounds.iter_instantiated_copied(interner, args.as_slice()),
ObligationCause::new(),
self.trait_env.env,
);
obligations.extend(bounds);
// Also add an obligation for the method type being well-formed.
debug!(
"lookup_method_in_trait: matched method fn_sig={:?} obligation={:?}",
fn_sig, obligation
);
for ty in fn_sig.inputs_and_output {
obligations.push(Obligation::new(
interner,
obligation.cause.clone(),
self.trait_env.env,
Binder::dummy(PredicateKind::Clause(ClauseKind::WellFormed(ty.into()))),
));
}
let callee = MethodCallee { def_id, args, sig: fn_sig };
debug!("callee = {:?}", callee);
Some(InferOk { obligations, value: callee })
}
}
pub fn lookup_impl_const<'db>(
infcx: &InferCtxt<'db>,
env: Arc<TraitEnvironment<'db>>,
const_id: ConstId,
subs: GenericArgs<'db>,
) -> (ConstId, GenericArgs<'db>) {
let interner = infcx.interner;
let db = interner.db;
let trait_id = match const_id.loc(db).container {
ItemContainerId::TraitId(id) => id,
_ => return (const_id, subs),
};
let trait_ref = TraitRef::new(interner, trait_id.into(), subs);
let const_signature = db.const_signature(const_id);
let name = match const_signature.name.as_ref() {
Some(name) => name,
None => return (const_id, subs),
};
lookup_impl_assoc_item_for_trait_ref(infcx, trait_ref, env, name)
.and_then(
|assoc| if let (AssocItemId::ConstId(id), s) = assoc { Some((id, s)) } else { None },
)
.unwrap_or((const_id, subs))
}
/// Checks if the self parameter of `Trait` method is the `dyn Trait` and we should
/// call the method using the vtable.
pub fn is_dyn_method<'db>(
interner: DbInterner<'db>,
_env: Arc<TraitEnvironment<'db>>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> Option<usize> {
let db = interner.db;
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
return None;
};
let trait_params = db.generic_params(trait_id.into()).len();
let fn_params = fn_subst.len() - trait_params;
let trait_ref = TraitRef::new(
interner,
trait_id.into(),
GenericArgs::new_from_iter(interner, fn_subst.iter().take(trait_params)),
);
let self_ty = trait_ref.self_ty();
if let TyKind::Dynamic(d, _) = self_ty.kind() {
// rustc doesn't accept `impl Foo<2> for dyn Foo<5>`, so if the trait id is equal, no matter
// what the generics are, we are sure that the method is come from the vtable.
let is_my_trait_in_bounds = d
.principal_def_id()
.is_some_and(|trait_| all_super_traits(db, trait_.0).contains(&trait_id));
if is_my_trait_in_bounds {
return Some(fn_params);
}
}
None
}
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
pub(crate) fn lookup_impl_method_query<'db>(
db: &'db dyn HirDatabase,
env: Arc<TraitEnvironment<'db>>,
func: FunctionId,
fn_subst: GenericArgs<'db>,
) -> (FunctionId, GenericArgs<'db>) {
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
let infcx = interner.infer_ctxt().build(TypingMode::PostAnalysis);
let ItemContainerId::TraitId(trait_id) = func.loc(db).container else {
return (func, fn_subst);
};
let trait_params = db.generic_params(trait_id.into()).len();
let trait_ref = TraitRef::new(
interner,
trait_id.into(),
GenericArgs::new_from_iter(interner, fn_subst.iter().take(trait_params)),
);
let name = &db.function_signature(func).name;
let Some((impl_fn, impl_subst)) =
lookup_impl_assoc_item_for_trait_ref(&infcx, trait_ref, env, name).and_then(|assoc| {
if let (AssocItemId::FunctionId(id), subst) = assoc { Some((id, subst)) } else { None }
})
else {
return (func, fn_subst);
};
(
impl_fn,
GenericArgs::new_from_iter(
interner,
impl_subst.iter().chain(fn_subst.iter().skip(trait_params)),
),
)
}
fn lookup_impl_assoc_item_for_trait_ref<'db>(
infcx: &InferCtxt<'db>,
trait_ref: TraitRef<'db>,
env: Arc<TraitEnvironment<'db>>,
name: &Name,
) -> Option<(AssocItemId, GenericArgs<'db>)> {
let (impl_id, impl_subst) = find_matching_impl(infcx, &env, trait_ref)?;
let item =
impl_id.impl_items(infcx.interner.db).items.iter().find_map(|(n, it)| match *it {
AssocItemId::FunctionId(f) => (n == name).then_some(AssocItemId::FunctionId(f)),
AssocItemId::ConstId(c) => (n == name).then_some(AssocItemId::ConstId(c)),
AssocItemId::TypeAliasId(_) => None,
})?;
Some((item, impl_subst))
}
pub(crate) fn find_matching_impl<'db>(
infcx: &InferCtxt<'db>,
env: &TraitEnvironment<'db>,
trait_ref: TraitRef<'db>,
) -> Option<(ImplId, GenericArgs<'db>)> {
let trait_ref =
infcx.at(&ObligationCause::dummy(), env.env).deeply_normalize(trait_ref).ok()?;
let obligation = Obligation::new(infcx.interner, ObligationCause::dummy(), env.env, trait_ref);
let selection = infcx.select(&obligation).ok()??;
// Currently, we use a fulfillment context to completely resolve
// all nested obligations. This is because they can inform the
// inference of the impl's type parameters.
let mut ocx = ObligationCtxt::new(infcx);
let impl_source = selection.map(|obligation| ocx.register_obligation(obligation));
let errors = ocx.evaluate_obligations_error_on_ambiguity();
if !errors.is_empty() {
return None;
}
let impl_source = infcx.resolve_vars_if_possible(impl_source);
if impl_source.has_non_region_infer() {
return None;
}
match impl_source {
ImplSource::UserDefined(impl_source) => Some((impl_source.impl_def_id, impl_source.args)),
ImplSource::Param(_) | ImplSource::Builtin(..) => None,
}
}
#[salsa::tracked(returns(ref))]
fn crates_containing_incoherent_inherent_impls(db: &dyn HirDatabase) -> Box<[Crate]> {
// We assume that only sysroot crates contain `#[rustc_has_incoherent_inherent_impls]`
// impls, since this is an internal feature and only std uses it.
db.all_crates().iter().copied().filter(|krate| krate.data(db).origin.is_lang()).collect()
}
pub fn incoherent_inherent_impls(db: &dyn HirDatabase, self_ty: SimplifiedType) -> &[ImplId] {
let has_incoherent_impls = match self_ty.def() {
Some(def_id) => match def_id.try_into() {
Ok(def_id) => {
db.attrs(def_id).by_key(sym::rustc_has_incoherent_inherent_impls).exists()
}
Err(()) => true,
},
_ => true,
};
return if !has_incoherent_impls {
&[]
} else {
incoherent_inherent_impls_query(db, (), self_ty)
};
#[salsa::tracked(returns(ref))]
fn incoherent_inherent_impls_query(
db: &dyn HirDatabase,
_force_query_input_to_be_interned: (),
self_ty: SimplifiedType,
) -> Box<[ImplId]> {
let _p = tracing::info_span!("incoherent_inherent_impl_crates").entered();
let mut result = Vec::new();
for &krate in crates_containing_incoherent_inherent_impls(db) {
let impls = InherentImpls::for_crate(db, krate);
result.extend_from_slice(impls.for_self_ty(&self_ty));
}
result.into_boxed_slice()
}
}
pub fn simplified_type_module(db: &dyn HirDatabase, ty: &SimplifiedType) -> Option<ModuleId> {
match ty.def()? {
SolverDefId::AdtId(id) => Some(id.module(db)),
SolverDefId::TypeAliasId(id) => Some(id.module(db)),
SolverDefId::TraitId(id) => Some(id.module(db)),
_ => None,
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct InherentImpls {
map: FxHashMap<SimplifiedType, Box<[ImplId]>>,
}
#[salsa::tracked]
impl InherentImpls {
#[salsa::tracked(returns(ref))]
pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Self {
let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
let crate_def_map = crate_def_map(db, krate);
Self::collect_def_map(db, crate_def_map)
}
#[salsa::tracked(returns(ref))]
pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
let block_def_map = block_def_map(db, block);
let result = Self::collect_def_map(db, block_def_map);
if result.map.is_empty() { None } else { Some(Box::new(result)) }
}
}
impl InherentImpls {
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
let mut map = FxHashMap::default();
collect(db, def_map, &mut map);
let mut map = map
.into_iter()
.map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
.collect::<FxHashMap<_, _>>();
map.shrink_to_fit();
return Self { map };
fn collect(
db: &dyn HirDatabase,
def_map: &DefMap,
map: &mut FxHashMap<SimplifiedType, Vec<ImplId>>,
) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
let data = db.impl_signature(impl_id);
if data.target_trait.is_some() {
continue;
}
let interner = DbInterner::new_with(db, None, None);
let self_ty = db.impl_self_ty(impl_id);
let self_ty = self_ty.instantiate_identity();
if let Some(self_ty) =
simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer)
{
map.entry(self_ty).or_default().push(impl_id);
}
}
// To better support custom derives, collect impls in all unnamed const items.
// const _: () = { ... };
for konst in module_data.scope.unnamed_consts() {
let body = db.body(konst.into());
for (_, block_def_map) in body.blocks(db) {
collect(db, block_def_map, map);
}
}
}
}
}
pub fn for_self_ty(&self, self_ty: &SimplifiedType) -> &[ImplId] {
self.map.get(self_ty).map(|it| &**it).unwrap_or_default()
}
pub fn for_each_crate_and_block(
db: &dyn HirDatabase,
krate: Crate,
block: Option<BlockId>,
for_each: &mut dyn FnMut(&InherentImpls),
) {
let blocks = std::iter::successors(block, |block| block.loc(db).module.containing_block());
blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
for_each(Self::for_crate(db, krate));
}
}
#[derive(Debug, PartialEq)]
struct OneTraitImpls {
non_blanket_impls: FxHashMap<SimplifiedType, Box<[ImplId]>>,
blanket_impls: Box<[ImplId]>,
}
#[derive(Default)]
struct OneTraitImplsBuilder {
non_blanket_impls: FxHashMap<SimplifiedType, Vec<ImplId>>,
blanket_impls: Vec<ImplId>,
}
impl OneTraitImplsBuilder {
fn finish(self) -> OneTraitImpls {
let mut non_blanket_impls = self
.non_blanket_impls
.into_iter()
.map(|(self_ty, impls)| (self_ty, impls.into_boxed_slice()))
.collect::<FxHashMap<_, _>>();
non_blanket_impls.shrink_to_fit();
let blanket_impls = self.blanket_impls.into_boxed_slice();
OneTraitImpls { non_blanket_impls, blanket_impls }
}
}
#[derive(Debug, PartialEq)]
pub struct TraitImpls {
map: FxHashMap<TraitId, OneTraitImpls>,
}
#[salsa::tracked]
impl TraitImpls {
#[salsa::tracked(returns(ref))]
pub fn for_crate(db: &dyn HirDatabase, krate: Crate) -> Arc<Self> {
let _p = tracing::info_span!("inherent_impls_in_crate_query", ?krate).entered();
let crate_def_map = crate_def_map(db, krate);
let result = Self::collect_def_map(db, crate_def_map);
Arc::new(result)
}
#[salsa::tracked(returns(ref))]
pub fn for_block(db: &dyn HirDatabase, block: BlockId) -> Option<Box<Self>> {
let _p = tracing::info_span!("inherent_impls_in_block_query").entered();
let block_def_map = block_def_map(db, block);
let result = Self::collect_def_map(db, block_def_map);
if result.map.is_empty() { None } else { Some(Box::new(result)) }
}
#[salsa::tracked(returns(ref))]
pub fn for_crate_and_deps(db: &dyn HirDatabase, krate: Crate) -> Box<[Arc<Self>]> {
db.transitive_deps(krate).iter().map(|&dep| Self::for_crate(db, dep).clone()).collect()
}
}
impl TraitImpls {
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap) -> Self {
let mut map = FxHashMap::default();
collect(db, def_map, &mut map);
let mut map = map
.into_iter()
.map(|(trait_id, trait_map)| (trait_id, trait_map.finish()))
.collect::<FxHashMap<_, _>>();
map.shrink_to_fit();
return Self { map };
fn collect(
db: &dyn HirDatabase,
def_map: &DefMap,
map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
// Reservation impls should be ignored during trait resolution, so we never need
// them during type analysis. See rust-lang/rust#64631 for details.
//
// FIXME: Reservation impls should be considered during coherence checks. If we are
// (ever) to implement coherence checks, this filtering should be done by the trait
// solver.
if db.attrs(impl_id.into()).by_key(sym::rustc_reservation_impl).exists() {
continue;
}
let trait_ref = match db.impl_trait(impl_id) {
Some(tr) => tr.instantiate_identity(),
None => continue,
};
let self_ty = trait_ref.self_ty();
let interner = DbInterner::new_with(db, None, None);
let entry = map.entry(trait_ref.def_id.0).or_default();
match simplify_type(interner, self_ty, TreatParams::InstantiateWithInfer) {
Some(self_ty) => {
entry.non_blanket_impls.entry(self_ty).or_default().push(impl_id)
}
None => entry.blanket_impls.push(impl_id),
}
}
// To better support custom derives, collect impls in all unnamed const items.
// const _: () = { ... };
for konst in module_data.scope.unnamed_consts() {
let body = db.body(konst.into());
for (_, block_def_map) in body.blocks(db) {
collect(db, block_def_map, map);
}
}
}
}
}
pub fn blanket_impls(&self, for_trait: TraitId) -> &[ImplId] {
self.map.get(&for_trait).map(|it| &*it.blanket_impls).unwrap_or_default()
}
/// Queries whether `self_ty` has potentially applicable implementations of `trait_`.
pub fn has_impls_for_trait_and_self_ty(
&self,
trait_: TraitId,
self_ty: &SimplifiedType,
) -> bool {
self.map.get(&trait_).is_some_and(|trait_impls| {
trait_impls.non_blanket_impls.contains_key(self_ty)
|| !trait_impls.blanket_impls.is_empty()
})
}
pub fn for_trait_and_self_ty(&self, trait_: TraitId, self_ty: &SimplifiedType) -> &[ImplId] {
self.map
.get(&trait_)
.and_then(|map| map.non_blanket_impls.get(self_ty))
.map(|it| &**it)
.unwrap_or_default()
}
pub fn for_trait(&self, trait_: TraitId, mut callback: impl FnMut(&[ImplId])) {
if let Some(impls) = self.map.get(&trait_) {
callback(&impls.blanket_impls);
for impls in impls.non_blanket_impls.values() {
callback(impls);
}
}
}
pub fn for_self_ty(&self, self_ty: &SimplifiedType, mut callback: impl FnMut(&[ImplId])) {
for for_trait in self.map.values() {
if let Some(for_ty) = for_trait.non_blanket_impls.get(self_ty) {
callback(for_ty);
}
}
}
pub fn for_each_crate_and_block(
db: &dyn HirDatabase,
krate: Crate,
block: Option<BlockId>,
for_each: &mut dyn FnMut(&TraitImpls),
) {
let blocks = std::iter::successors(block, |block| block.loc(db).module.containing_block());
blocks.filter_map(|block| Self::for_block(db, block).as_deref()).for_each(&mut *for_each);
Self::for_crate_and_deps(db, krate).iter().map(|it| &**it).for_each(for_each);
}
/// Like [`Self::for_each_crate_and_block()`], but takes in account two blocks, one for a trait and one for a self type.
pub fn for_each_crate_and_block_trait_and_type(
db: &dyn HirDatabase,
krate: Crate,
type_block: Option<BlockId>,
trait_block: Option<BlockId>,
for_each: &mut dyn FnMut(&TraitImpls),
) {
let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate);
in_self_and_deps.iter().for_each(|impls| for_each(impls));
// We must not provide duplicate impls to the solver. Therefore we work with the following strategy:
// start from each block, and walk ancestors until you meet the other block. If they never meet,
// that means there can't be duplicate impls; if they meet, we stop the search of the deeper block.
// This breaks when they are equal (both will stop immediately), therefore we handle this case
// specifically.
let blocks_iter = |block: Option<BlockId>| {
std::iter::successors(block, |block| block.loc(db).module.containing_block())
};
let for_each_block = |current_block: Option<BlockId>, other_block: Option<BlockId>| {
blocks_iter(current_block)
.take_while(move |&block| {
other_block.is_none_or(|other_block| other_block != block)
})
.filter_map(move |block| TraitImpls::for_block(db, block).as_deref())
};
if trait_block == type_block {
blocks_iter(trait_block)
.filter_map(|block| TraitImpls::for_block(db, block).as_deref())
.for_each(for_each);
} else {
for_each_block(trait_block, type_block).for_each(&mut *for_each);
for_each_block(type_block, trait_block).for_each(for_each);
}
}
}