Skip to content

Commit 7570212

Browse files
Merge #8569
8569: Support inherent impls in unnamed consts r=jonas-schievink a=jonas-schievink It turns out that some proc. macros not only generate *trait* impls wrapped in `const _: () = { ... };`, but inherent impls too. Even though it is questionable whether *custom derives* should produce non-trait impls, this is useful for procedural attribute macros once we support them. bors r+ Co-authored-by: Jonas Schievink <[email protected]>
2 parents d39873e + 20c27db commit 7570212

File tree

4 files changed

+83
-18
lines changed

4 files changed

+83
-18
lines changed

crates/hir_def/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,18 @@ impl ModuleId {
108108
pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> {
109109
self.def_map(db).containing_module(self.local_id)
110110
}
111+
112+
/// Returns `true` if this module represents a block expression.
113+
///
114+
/// Returns `false` if this module is a submodule *inside* a block expression
115+
/// (eg. `m` in `{ mod m {} }`).
116+
pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool {
117+
if self.block.is_none() {
118+
return false;
119+
}
120+
121+
self.def_map(db)[self.local_id].parent.is_none()
122+
}
111123
}
112124

113125
/// An ID of a module, **local** to a specific crate

crates/hir_def/src/visibility.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,19 @@ impl Visibility {
123123
def_map: &DefMap,
124124
mut from_module: crate::LocalModuleId,
125125
) -> bool {
126-
let to_module = match self {
126+
let mut to_module = match self {
127127
Visibility::Module(m) => m,
128128
Visibility::Public => return true,
129129
};
130130

131+
// `to_module` might be the root module of a block expression. Those have the same
132+
// visibility as the containing module (even though no items are directly nameable from
133+
// there, getting this right is important for method resolution).
134+
// In that case, we adjust the visibility of `to_module` to point to the containing module.
135+
if to_module.is_block_root(db) {
136+
to_module = to_module.containing_module(db).unwrap();
137+
}
138+
131139
// from_module needs to be a descendant of to_module
132140
let mut def_map = def_map;
133141
let mut parent_arc;

crates/hir_ty/src/method_resolution.rs

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -246,29 +246,39 @@ pub struct InherentImpls {
246246

247247
impl InherentImpls {
248248
pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> {
249-
let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default();
249+
let mut impls = Self { map: FxHashMap::default() };
250250

251251
let crate_def_map = db.crate_def_map(krate);
252-
for (_module_id, module_data) in crate_def_map.modules() {
253-
for impl_id in module_data.scope.impls() {
254-
let data = db.impl_data(impl_id);
255-
if data.target_trait.is_some() {
256-
continue;
252+
collect_def_map(db, &crate_def_map, &mut impls);
253+
254+
return Arc::new(impls);
255+
256+
fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) {
257+
for (_module_id, module_data) in def_map.modules() {
258+
for impl_id in module_data.scope.impls() {
259+
let data = db.impl_data(impl_id);
260+
if data.target_trait.is_some() {
261+
continue;
262+
}
263+
264+
let self_ty = db.impl_self_ty(impl_id);
265+
let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
266+
if let Some(fp) = fp {
267+
impls.map.entry(fp).or_default().push(impl_id);
268+
}
269+
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
257270
}
258271

259-
let self_ty = db.impl_self_ty(impl_id);
260-
let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders());
261-
if let Some(fp) = fp {
262-
map.entry(fp).or_default().push(impl_id);
272+
// To better support custom derives, collect impls in all unnamed const items.
273+
// const _: () = { ... };
274+
for konst in module_data.scope.unnamed_consts() {
275+
let body = db.body(konst.into());
276+
for (_, block_def_map) in body.blocks(db.upcast()) {
277+
collect_def_map(db, &block_def_map, impls);
278+
}
263279
}
264-
// `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution)
265280
}
266281
}
267-
268-
// NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only
269-
// support trait impls there.
270-
271-
Arc::new(Self { map })
272282
}
273283

274284
pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] {

crates/hir_ty/src/tests/method_resolution.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1294,7 +1294,7 @@ mod b {
12941294
}
12951295

12961296
#[test]
1297-
fn impl_in_unnamed_const() {
1297+
fn trait_impl_in_unnamed_const() {
12981298
check_types(
12991299
r#"
13001300
struct S;
@@ -1314,3 +1314,38 @@ fn f() {
13141314
"#,
13151315
);
13161316
}
1317+
1318+
#[test]
1319+
fn inherent_impl_in_unnamed_const() {
1320+
check_types(
1321+
r#"
1322+
struct S;
1323+
1324+
const _: () = {
1325+
impl S {
1326+
fn method(&self) -> u16 { 0 }
1327+
1328+
pub(super) fn super_method(&self) -> u16 { 0 }
1329+
1330+
pub(crate) fn crate_method(&self) -> u16 { 0 }
1331+
1332+
pub fn pub_method(&self) -> u16 { 0 }
1333+
}
1334+
};
1335+
1336+
fn f() {
1337+
S.method();
1338+
//^^^^^^^^^^ u16
1339+
1340+
S.super_method();
1341+
//^^^^^^^^^^^^^^^^ u16
1342+
1343+
S.crate_method();
1344+
//^^^^^^^^^^^^^^^^ u16
1345+
1346+
S.pub_method();
1347+
//^^^^^^^^^^^^^^ u16
1348+
}
1349+
"#,
1350+
);
1351+
}

0 commit comments

Comments
 (0)