Skip to content

Commit 11d400b

Browse files
Merge #3944
3944: Look up trait impls by self type r=matklad a=flodiebold This speeds up inference in analysis-stats by ~30% (even more with the recursive solver). There's a slight difference in inferred types, which I think comes from pre-existing wrong handling of error types in impls, so I think it's fine. Co-authored-by: Florian Diebold <[email protected]>
2 parents e7a68c8 + a2783df commit 11d400b

File tree

4 files changed

+65
-14
lines changed

4 files changed

+65
-14
lines changed

crates/ra_hir_ty/src/db.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use ra_db::{impl_intern_key, salsa, CrateId, Upcast};
1111
use ra_prof::profile;
1212

1313
use crate::{
14-
method_resolution::CrateImplDefs,
14+
method_resolution::{CrateImplDefs, TyFingerprint},
1515
traits::{chalk, AssocTyValue, Impl},
1616
Binders, CallableDef, GenericPredicate, InferenceResult, PolyFnSig, Substs, TraitRef, Ty,
1717
TyDefId, TypeCtor, ValueTyDefId,
@@ -65,7 +65,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
6565
fn impls_in_crate(&self, krate: CrateId) -> Arc<CrateImplDefs>;
6666

6767
#[salsa::invoke(crate::traits::impls_for_trait_query)]
68-
fn impls_for_trait(&self, krate: CrateId, trait_: TraitId) -> Arc<[ImplId]>;
68+
fn impls_for_trait(
69+
&self,
70+
krate: CrateId,
71+
trait_: TraitId,
72+
self_ty_fp: Option<TyFingerprint>,
73+
) -> Arc<[ImplId]>;
6974

7075
// Interned IDs for Chalk integration
7176
#[salsa::interned]

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ impl TyFingerprint {
3434
/// Creates a TyFingerprint for looking up an impl. Only certain types can
3535
/// have impls: if we have some `struct S`, we can have an `impl S`, but not
3636
/// `impl &S`. Hence, this will return `None` for reference types and such.
37-
fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
37+
pub(crate) fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
3838
match ty {
3939
Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)),
4040
_ => None,
@@ -45,7 +45,7 @@ impl TyFingerprint {
4545
#[derive(Debug, PartialEq, Eq)]
4646
pub struct CrateImplDefs {
4747
impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
48-
impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>,
48+
impls_by_trait: FxHashMap<TraitId, FxHashMap<Option<TyFingerprint>, Vec<ImplId>>>,
4949
}
5050

5151
impl CrateImplDefs {
@@ -59,7 +59,14 @@ impl CrateImplDefs {
5959
for impl_id in module_data.scope.impls() {
6060
match db.impl_trait(impl_id) {
6161
Some(tr) => {
62-
res.impls_by_trait.entry(tr.value.trait_).or_default().push(impl_id);
62+
let self_ty = db.impl_self_ty(impl_id);
63+
let self_ty_fp = TyFingerprint::for_impl(&self_ty.value);
64+
res.impls_by_trait
65+
.entry(tr.value.trait_)
66+
.or_default()
67+
.entry(self_ty_fp)
68+
.or_default()
69+
.push(impl_id);
6370
}
6471
None => {
6572
let self_ty = db.impl_self_ty(impl_id);
@@ -79,11 +86,39 @@ impl CrateImplDefs {
7986
}
8087

8188
pub fn lookup_impl_defs_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
82-
self.impls_by_trait.get(&tr).into_iter().flatten().copied()
89+
self.impls_by_trait
90+
.get(&tr)
91+
.into_iter()
92+
.flat_map(|m| m.values().flat_map(|v| v.iter().copied()))
93+
}
94+
95+
pub fn lookup_impl_defs_for_trait_and_ty(
96+
&self,
97+
tr: TraitId,
98+
fp: TyFingerprint,
99+
) -> impl Iterator<Item = ImplId> + '_ {
100+
self.impls_by_trait
101+
.get(&tr)
102+
.and_then(|m| m.get(&Some(fp)))
103+
.into_iter()
104+
.flatten()
105+
.copied()
106+
.chain(
107+
self.impls_by_trait
108+
.get(&tr)
109+
.and_then(|m| m.get(&None))
110+
.into_iter()
111+
.flatten()
112+
.copied(),
113+
)
83114
}
84115

85116
pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a {
86-
self.impls.values().chain(self.impls_by_trait.values()).flatten().copied()
117+
self.impls
118+
.values()
119+
.chain(self.impls_by_trait.values().flat_map(|m| m.values()))
120+
.flatten()
121+
.copied()
87122
}
88123
}
89124

crates/ra_hir_ty/src/traits.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ra_db::{impl_intern_key, salsa, CrateId};
77
use ra_prof::profile;
88
use rustc_hash::FxHashSet;
99

10-
use crate::{db::HirDatabase, DebruijnIndex};
10+
use crate::{db::HirDatabase, method_resolution::TyFingerprint, DebruijnIndex};
1111

1212
use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};
1313

@@ -40,18 +40,26 @@ pub(crate) fn impls_for_trait_query(
4040
db: &dyn HirDatabase,
4141
krate: CrateId,
4242
trait_: TraitId,
43+
self_ty_fp: Option<TyFingerprint>,
4344
) -> Arc<[ImplId]> {
45+
// FIXME: We could be a lot smarter here - because of the orphan rules and
46+
// the fact that the trait and the self type need to be in the dependency
47+
// tree of a crate somewhere for an impl to exist, we could skip looking in
48+
// a lot of crates completely
4449
let mut impls = FxHashSet::default();
4550
// We call the query recursively here. On the one hand, this means we can
4651
// reuse results from queries for different crates; on the other hand, this
4752
// will only ever get called for a few crates near the root of the tree (the
4853
// ones the user is editing), so this may actually be a waste of memory. I'm
4954
// doing it like this mainly for simplicity for now.
5055
for dep in &db.crate_graph()[krate].dependencies {
51-
impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
56+
impls.extend(db.impls_for_trait(dep.crate_id, trait_, self_ty_fp).iter());
5257
}
5358
let crate_impl_defs = db.impls_in_crate(krate);
54-
impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_));
59+
match self_ty_fp {
60+
Some(fp) => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait_and_ty(trait_, fp)),
61+
None => impls.extend(crate_impl_defs.lookup_impl_defs_for_trait(trait_)),
62+
}
5563
impls.into_iter().collect()
5664
}
5765

crates/ra_hir_ty/src/traits/chalk.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use ra_db::{
1616

1717
use super::{builtin, AssocTyValue, Canonical, ChalkContext, Impl, Obligation};
1818
use crate::{
19-
db::HirDatabase, display::HirDisplay, utils::generics, ApplicationTy, GenericPredicate,
20-
ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
19+
db::HirDatabase, display::HirDisplay, method_resolution::TyFingerprint, utils::generics,
20+
ApplicationTy, GenericPredicate, ProjectionTy, Substs, TraitRef, Ty, TypeCtor,
2121
};
2222

2323
pub(super) mod tls;
@@ -647,19 +647,22 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
647647
debug!("impls_for_trait {:?}", trait_id);
648648
let trait_: hir_def::TraitId = from_chalk(self.db, trait_id);
649649

650+
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
651+
652+
let self_ty_fp = TyFingerprint::for_impl(&ty);
653+
650654
// Note: Since we're using impls_for_trait, only impls where the trait
651655
// can be resolved should ever reach Chalk. `impl_datum` relies on that
652656
// and will panic if the trait can't be resolved.
653657
let mut result: Vec<_> = self
654658
.db
655-
.impls_for_trait(self.krate, trait_)
659+
.impls_for_trait(self.krate, trait_, self_ty_fp)
656660
.iter()
657661
.copied()
658662
.map(Impl::ImplDef)
659663
.map(|impl_| impl_.to_chalk(self.db))
660664
.collect();
661665

662-
let ty: Ty = from_chalk(self.db, parameters[0].assert_ty_ref(&Interner).clone());
663666
let arg: Option<Ty> =
664667
parameters.get(1).map(|p| from_chalk(self.db, p.assert_ty_ref(&Interner).clone()));
665668

0 commit comments

Comments
 (0)