diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 709760b64fd3..4a181f4a325c 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -457,6 +457,7 @@ impl<'a> InferenceTable<'a> { } /// Unify two relatable values (e.g. `Ty`) and register new trait goals that arise from that. + #[tracing::instrument(skip_all)] pub(crate) fn unify>(&mut self, ty1: &T, ty2: &T) -> bool { let result = match self.try_unify(ty1, ty2) { Ok(r) => r, diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index a4baf572d9e3..583bc0903dd0 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1167,10 +1167,13 @@ fn iterate_trait_method_candidates( // trait, but if we find out it doesn't, we'll skip the rest of the // iteration let mut known_implemented = false; - for &(_, item) in data.items.iter() { + 'items: for &(_, item) in data.items.iter() { + if let AssocItemId::TypeAliasId(_) = item { + continue 'items; + } // Don't pass a `visible_from_module` down to `is_valid_candidate`, // since only inherent methods should be included into visibility checking. - let visible = match is_valid_candidate(table, name, receiver_ty, item, self_ty, None) { + let visible = match is_valid_method_candidate(table, name, receiver_ty, item, self_ty) { IsValidCandidate::Yes => true, IsValidCandidate::NotVisible => false, IsValidCandidate::No => continue, @@ -1414,6 +1417,75 @@ fn is_valid_candidate( } } +/// Checks whether a given `AssocItemId` is applicable for `receiver_ty`. +/// +/// This method should *only* be called by [`iterate_trait_method_candidates`], +/// as it is responsible for determining applicability in completions. +#[tracing::instrument(skip_all, fields(name))] +fn is_valid_method_candidate( + table: &mut InferenceTable<'_>, + name: Option<&Name>, + receiver_ty: Option<&Ty>, + item: AssocItemId, + self_ty: &Ty, +) -> IsValidCandidate { + let db = table.db; + match item { + AssocItemId::FunctionId(fn_id) => { + let db = table.db; + let data = db.function_data(fn_id); + + check_that!(name.map_or(true, |n| n == &data.name)); + + table.run_in_snapshot(|table| { + let container = fn_id.lookup(db.upcast()).container; + let (impl_subst, expect_self_ty) = match container { + ItemContainerId::ImplId(it) => { + let subst = TyBuilder::subst_for_def(db, it, None) + .fill_with_inference_vars(table) + .build(); + let self_ty = db.impl_self_ty(it).substitute(Interner, &subst); + (subst, self_ty) + } + ItemContainerId::TraitId(it) => { + let subst = TyBuilder::subst_for_def(db, it, None) + .fill_with_inference_vars(table) + .build(); + let self_ty = subst.at(Interner, 0).assert_ty_ref(Interner).clone(); + (subst, self_ty) + } + _ => unreachable!(), + }; + + check_that!(table.unify(&expect_self_ty, self_ty)); + + if let Some(receiver_ty) = receiver_ty { + check_that!(data.has_self_param()); + + let fn_subst = TyBuilder::subst_for_def(db, fn_id, Some(impl_subst.clone())) + .fill_with_inference_vars(table) + .build(); + + let sig = db.callable_item_signature(fn_id.into()); + let expected_receiver = + sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + + check_that!(table.unify(receiver_ty, &expected_receiver)); + } + + IsValidCandidate::Yes + }) + } + AssocItemId::ConstId(c) => { + check_that!(receiver_ty.is_none()); + check_that!(name.map_or(true, |n| db.const_data(c).name.as_ref() == Some(n))); + + IsValidCandidate::Yes + } + _ => IsValidCandidate::No, + } +} + enum IsValidCandidate { Yes, No, @@ -1441,6 +1513,8 @@ fn is_valid_fn_candidate( } table.run_in_snapshot(|table| { let container = fn_id.lookup(db.upcast()).container; + + let _p = tracing::span!(tracing::Level::INFO, "subst_for_def").entered(); let (impl_subst, expect_self_ty) = match container { ItemContainerId::ImplId(it) => { let subst = @@ -1459,6 +1533,7 @@ fn is_valid_fn_candidate( check_that!(table.unify(&expect_self_ty, self_ty)); + let _p = tracing::span!(tracing::Level::INFO, "check_receiver_ty").entered(); if let Some(receiver_ty) = receiver_ty { check_that!(data.has_self_param()); @@ -1473,6 +1548,7 @@ fn is_valid_fn_candidate( check_that!(table.unify(receiver_ty, &expected_receiver)); } + let _p = tracing::span!(tracing::Level::INFO, "check_item_container").entered(); if let ItemContainerId::ImplId(impl_id) = container { // We need to consider the bounds on the impl to distinguish functions of the same name // for a type. diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index b2232b920aa0..71a4ab020b35 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs @@ -139,6 +139,7 @@ fn solve( block: Option, goal: &chalk_ir::UCanonical>>, ) -> Option> { + let _p = tracing::span!(tracing::Level::INFO, "solve", ?krate, ?block).entered(); let context = ChalkContext { db, krate, block }; tracing::debug!("solve goal: {:?}", goal); let mut solver = create_chalk_solver(); diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 08f7bb14caa3..bae5717d3cd0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -40,7 +40,7 @@ use std::{iter, mem::discriminant, ops::ControlFlow}; use arrayvec::ArrayVec; use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId}; use either::Either; -use hir_def::{ +pub use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, @@ -60,7 +60,7 @@ use hir_def::{ TypeOrConstParamId, TypeParamId, UnionId, }; use hir_expand::{attrs::collect_attrs, name::name, proc_macro::ProcMacroKind, MacroCallKind}; -use hir_ty::{ +pub use hir_ty::{ all_super_traits, autoderef, check_orphan_rules, consteval::{try_const_usize, unknown_const_as_generic, ConstExt}, db::InternedClosure, @@ -266,6 +266,10 @@ impl Crate { let data = &db.crate_graph()[self.id]; data.potential_cfg_options.clone().unwrap_or_else(|| data.cfg_options.clone()) } + + pub fn id(&self) -> CrateId { + self.id + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -3691,7 +3695,7 @@ pub enum CaptureKind { #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub struct Type { env: Arc, - ty: Ty, + pub ty: Ty, } impl Type { diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index a71d8e9002d4..c61f52061a16 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs @@ -1,8 +1,10 @@ //! Look up accessible paths for items. use hir::{ - AsAssocItem, AssocItem, AssocItemContainer, Crate, ItemInNs, ModPath, Module, ModuleDef, Name, - PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, Type, + db::{DefDatabase, HirDatabase}, + AsAssocItem, AssocItem, AssocItemContainer, Crate, HasCrate, ItemInNs, ModPath, Module, + ModuleDef, Name, PathResolution, PrefixKind, ScopeDef, Semantics, SemanticsScope, TraitId, + TyFingerprint, Type, }; use itertools::{EitherOrBoth, Itertools}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -517,7 +519,7 @@ fn trait_applicable_items( let related_traits = inherent_traits.chain(env_traits).collect::>(); let mut required_assoc_items = FxHashSet::default(); - let trait_candidates: FxHashSet<_> = items_locator::items_with_name( + let trait_candidates: FxHashSet = items_locator::items_with_name( sema, current_crate, trait_candidate.assoc_item_name.clone(), @@ -536,6 +538,29 @@ fn trait_applicable_items( required_assoc_items.insert(assoc); Some(assoc_item_trait.into()) }) + .filter(|candidate_trait_id| { + // blanket `Trait` implementations for type `A` can only exist in the crate defining + // the trait. However, an implementation for `A`` can only exist in `A`'s or `Trait`'s crate, + // which allows us to reduce the search space substantially. + let candidate_trait_id: TraitId = *candidate_trait_id; + let definining_crate_for_trait = db.trait_environment(candidate_trait_id.into()).krate; + + let receiver_fingerprint = + TyFingerprint::for_trait_impl(&trait_candidate.receiver_ty.ty).unwrap(); + let definitions_exist_in_trait_crate = db + .trait_impls_in_crate(definining_crate_for_trait) + .for_trait_and_self_ty(candidate_trait_id, receiver_fingerprint) + .next() + .is_some(); + + let definitions_exist_in_receiver_crate = db + .trait_impls_in_crate(trait_candidate.receiver_ty.krate(db).id()) + .for_trait_and_self_ty(candidate_trait_id, receiver_fingerprint) + .next() + .is_some(); + + definitions_exist_in_trait_crate || definitions_exist_in_receiver_crate + }) .collect(); let mut located_imports = FxHashSet::default();