Skip to content

Commit be97428

Browse files
Merge #9244
9244: feat: Make block-local trait impls work r=flodiebold a=flodiebold As long as either the trait or the implementing type are defined in the same block. CC #8961 Co-authored-by: Florian Diebold <flodiebold@gmail.com>
2 parents e29a4c3 + 5ca71a1 commit be97428

File tree

5 files changed

+154
-36
lines changed

5 files changed

+154
-36
lines changed

crates/hir_def/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ impl ModuleId {
112112
self.def_map(db).containing_module(self.local_id)
113113
}
114114

115+
pub fn containing_block(&self) -> Option<BlockId> {
116+
self.block
117+
}
118+
115119
/// Returns `true` if this module represents a block expression.
116120
///
117121
/// Returns `false` if this module is a submodule *inside* a block expression
@@ -581,6 +585,18 @@ impl HasModule for GenericDefId {
581585
}
582586
}
583587

588+
impl HasModule for TypeAliasId {
589+
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
590+
self.lookup(db).module(db)
591+
}
592+
}
593+
594+
impl HasModule for TraitId {
595+
fn module(&self, db: &dyn db::DefDatabase) -> ModuleId {
596+
self.lookup(db).container
597+
}
598+
}
599+
584600
impl HasModule for StaticLoc {
585601
fn module(&self, _db: &dyn db::DefDatabase) -> ModuleId {
586602
self.container

crates/hir_ty/src/chalk_db.rs

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,16 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
1010
use base_db::CrateId;
1111
use hir_def::{
1212
lang_item::{lang_attr, LangItemTarget},
13-
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, TypeAliasId,
13+
AssocContainerId, AssocItemId, GenericDefId, HasModule, Lookup, ModuleId, TypeAliasId,
1414
};
1515
use hir_expand::name::name;
1616

1717
use crate::{
1818
db::HirDatabase,
1919
display::HirDisplay,
20-
from_assoc_type_id, from_chalk_trait_id, make_only_type_binders,
20+
from_assoc_type_id, from_chalk_trait_id, from_foreign_def_id, make_only_type_binders,
2121
mapping::{from_chalk, ToChalk, TypeAliasAsValue},
22-
method_resolution::{TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
22+
method_resolution::{TraitImpls, TyFingerprint, ALL_FLOAT_FPS, ALL_INT_FPS},
2323
to_assoc_type_id, to_chalk_trait_id,
2424
traits::ChalkContext,
2525
utils::generics,
@@ -105,27 +105,47 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> {
105105
_ => self_ty_fp.as_ref().map(std::slice::from_ref).unwrap_or(&[]),
106106
};
107107

108+
fn local_impls(db: &dyn HirDatabase, module: ModuleId) -> Option<Arc<TraitImpls>> {
109+
db.trait_impls_in_block(module.containing_block()?)
110+
}
111+
108112
// Note: Since we're using impls_for_trait, only impls where the trait
109-
// can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that
113+
// can be resolved should ever reach Chalk. impl_datum relies on that
110114
// and will panic if the trait can't be resolved.
111115
let in_deps = self.db.trait_impls_in_deps(self.krate);
112116
let in_self = self.db.trait_impls_in_crate(self.krate);
113-
let impl_maps = [in_deps, in_self];
117+
let trait_module = trait_.module(self.db.upcast());
118+
let type_module = match self_ty_fp {
119+
Some(TyFingerprint::Adt(adt_id)) => Some(adt_id.module(self.db.upcast())),
120+
Some(TyFingerprint::ForeignType(type_id)) => {
121+
Some(from_foreign_def_id(type_id).module(self.db.upcast()))
122+
}
123+
Some(TyFingerprint::Dyn(trait_id)) => Some(trait_id.module(self.db.upcast())),
124+
_ => None,
125+
};
126+
let impl_maps = [
127+
Some(in_deps),
128+
Some(in_self),
129+
local_impls(self.db, trait_module),
130+
type_module.and_then(|m| local_impls(self.db, m)),
131+
];
114132

115133
let id_to_chalk = |id: hir_def::ImplId| id.to_chalk(self.db);
116134

117135
let result: Vec<_> = if fps.is_empty() {
118136
debug!("Unrestricted search for {:?} impls...", trait_);
119137
impl_maps
120138
.iter()
121-
.flat_map(|crate_impl_defs| crate_impl_defs.for_trait(trait_).map(id_to_chalk))
139+
.filter_map(|o| o.as_ref())
140+
.flat_map(|impls| impls.for_trait(trait_).map(id_to_chalk))
122141
.collect()
123142
} else {
124143
impl_maps
125144
.iter()
126-
.flat_map(|crate_impl_defs| {
145+
.filter_map(|o| o.as_ref())
146+
.flat_map(|impls| {
127147
fps.iter().flat_map(move |fp| {
128-
crate_impl_defs.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
148+
impls.for_trait_and_self_ty(trait_, *fp).map(id_to_chalk)
129149
})
130150
})
131151
.collect()

crates/hir_ty/src/db.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::sync::Arc;
55

66
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
77
use hir_def::{
8-
db::DefDatabase, expr::ExprId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId, ImplId,
9-
LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
8+
db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
9+
ImplId, LifetimeParamId, LocalFieldId, TypeParamId, VariantId,
1010
};
1111
use la_arena::ArenaMap;
1212

@@ -79,6 +79,9 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
7979
#[salsa::invoke(TraitImpls::trait_impls_in_crate_query)]
8080
fn trait_impls_in_crate(&self, krate: CrateId) -> Arc<TraitImpls>;
8181

82+
#[salsa::invoke(TraitImpls::trait_impls_in_block_query)]
83+
fn trait_impls_in_block(&self, krate: BlockId) -> Option<Arc<TraitImpls>>;
84+
8285
#[salsa::invoke(TraitImpls::trait_impls_in_deps_query)]
8386
fn trait_impls_in_deps(&self, krate: CrateId) -> Arc<TraitImpls>;
8487

crates/hir_ty/src/method_resolution.rs

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use arrayvec::ArrayVec;
88
use base_db::{CrateId, Edition};
99
use chalk_ir::{cast::Cast, Mutability, UniverseIndex};
1010
use hir_def::{
11-
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, FunctionId,
11+
lang_item::LangItemTarget, nameres::DefMap, AssocContainerId, AssocItemId, BlockId, FunctionId,
1212
GenericDefId, HasModule, ImplId, Lookup, ModuleId, TraitId,
1313
};
1414
use hir_expand::name::Name;
@@ -139,35 +139,47 @@ impl TraitImpls {
139139
let mut impls = Self { map: FxHashMap::default() };
140140

141141
let crate_def_map = db.crate_def_map(krate);
142-
collect_def_map(db, &crate_def_map, &mut impls);
142+
impls.collect_def_map(db, &crate_def_map);
143143

144144
return Arc::new(impls);
145+
}
145146

146-
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut TraitImpls) {
147-
for (_module_id, module_data) in def_map.modules() {
148-
for impl_id in module_data.scope.impls() {
149-
let target_trait = match db.impl_trait(impl_id) {
150-
Some(tr) => tr.skip_binders().hir_trait_id(),
151-
None => continue,
152-
};
153-
let self_ty = db.impl_self_ty(impl_id);
154-
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
155-
impls
156-
.map
157-
.entry(target_trait)
158-
.or_default()
159-
.entry(self_ty_fp)
160-
.or_default()
161-
.push(impl_id);
162-
}
147+
pub(crate) fn trait_impls_in_block_query(
148+
db: &dyn HirDatabase,
149+
block: BlockId,
150+
) -> Option<Arc<Self>> {
151+
let _p = profile::span("trait_impls_in_block_query");
152+
let mut impls = Self { map: FxHashMap::default() };
163153

164-
// To better support custom derives, collect impls in all unnamed const items.
165-
// const _: () = { ... };
166-
for konst in module_data.scope.unnamed_consts() {
167-
let body = db.body(konst.into());
168-
for (_, block_def_map) in body.blocks(db.upcast()) {
169-
collect_def_map(db, &block_def_map, impls);
170-
}
154+
let block_def_map = db.block_def_map(block)?;
155+
impls.collect_def_map(db, &block_def_map);
156+
157+
return Some(Arc::new(impls));
158+
}
159+
160+
fn collect_def_map(&mut self, db: &dyn HirDatabase, def_map: &DefMap) {
161+
for (_module_id, module_data) in def_map.modules() {
162+
for impl_id in module_data.scope.impls() {
163+
let target_trait = match db.impl_trait(impl_id) {
164+
Some(tr) => tr.skip_binders().hir_trait_id(),
165+
None => continue,
166+
};
167+
let self_ty = db.impl_self_ty(impl_id);
168+
let self_ty_fp = TyFingerprint::for_trait_impl(self_ty.skip_binders());
169+
self.map
170+
.entry(target_trait)
171+
.or_default()
172+
.entry(self_ty_fp)
173+
.or_default()
174+
.push(impl_id);
175+
}
176+
177+
// To better support custom derives, collect impls in all unnamed const items.
178+
// const _: () = { ... };
179+
for konst in module_data.scope.unnamed_consts() {
180+
let body = db.body(konst.into());
181+
for (_, block_def_map) in body.blocks(db.upcast()) {
182+
self.collect_def_map(db, &block_def_map);
171183
}
172184
}
173185
}

crates/hir_ty/src/tests/traits.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3740,3 +3740,70 @@ mod future {
37403740
"#,
37413741
);
37423742
}
3743+
3744+
#[test]
3745+
fn local_impl_1() {
3746+
check_types(
3747+
r#"
3748+
trait Trait<T> {
3749+
fn foo(&self) -> T;
3750+
}
3751+
3752+
fn test() {
3753+
struct S;
3754+
impl Trait<u32> for S {
3755+
fn foo(&self) { 0 }
3756+
}
3757+
3758+
S.foo();
3759+
// ^^^^^^^ u32
3760+
}
3761+
"#,
3762+
);
3763+
}
3764+
3765+
#[test]
3766+
fn local_impl_2() {
3767+
check_types(
3768+
r#"
3769+
struct S;
3770+
3771+
fn test() {
3772+
trait Trait<T> {
3773+
fn foo(&self) -> T;
3774+
}
3775+
impl Trait<u32> for S {
3776+
fn foo(&self) { 0 }
3777+
}
3778+
3779+
S.foo();
3780+
// ^^^^^^^ u32
3781+
}
3782+
"#,
3783+
);
3784+
}
3785+
3786+
#[test]
3787+
fn local_impl_3() {
3788+
check_types(
3789+
r#"
3790+
trait Trait<T> {
3791+
fn foo(&self) -> T;
3792+
}
3793+
3794+
fn test() {
3795+
struct S1;
3796+
{
3797+
struct S2;
3798+
3799+
impl Trait<S1> for S2 {
3800+
fn foo(&self) { S1 }
3801+
}
3802+
3803+
S2.foo();
3804+
// ^^^^^^^^ S1
3805+
}
3806+
}
3807+
"#,
3808+
);
3809+
}

0 commit comments

Comments
 (0)