Skip to content

Commit ff13264

Browse files
Merge pull request #22055 from ChayimFriedman2/upvars-expr-store
fix: Some fixes for `upvars_mentioned()`
2 parents 0d654c8 + 7fda174 commit ff13264

2 files changed

Lines changed: 114 additions & 20 deletions

File tree

crates/hir-ty/src/infer/coerce.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1718,9 +1718,6 @@ fn coerce<'db>(
17181718

17191719
fn is_capturing_closure(db: &dyn HirDatabase, closure: InternedClosureId) -> bool {
17201720
let InternedClosure(owner, expr) = closure.loc(db);
1721-
let Some(body_owner) = owner.as_def_with_body() else {
1722-
return false;
1723-
};
1724-
upvars_mentioned(db, body_owner)
1721+
upvars_mentioned(db, owner)
17251722
.is_some_and(|upvars| upvars.get(&expr).is_some_and(|upvars| !upvars.is_empty()))
17261723
}

crates/hir-ty/src/upvars.rs

Lines changed: 113 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! A simple query to collect tall locals (upvars) a closure use.
22
33
use hir_def::{
4-
DefWithBodyId,
5-
expr_store::{Body, path::Path},
4+
DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId,
5+
expr_store::{ExpressionStore, path::Path},
66
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
77
resolver::{HasResolver, Resolver, ValueNs},
88
};
@@ -36,18 +36,89 @@ impl Upvars {
3636
pub fn is_empty(&self) -> bool {
3737
self.0.is_empty()
3838
}
39+
40+
#[inline]
41+
pub fn as_ref(&self) -> UpvarsRef<'_> {
42+
UpvarsRef(&self.0)
43+
}
44+
}
45+
46+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
47+
// Kept sorted.
48+
pub struct UpvarsRef<'db>(&'db [BindingId]);
49+
50+
impl UpvarsRef<'_> {
51+
#[inline]
52+
pub fn contains(self, local: BindingId) -> bool {
53+
self.0.binary_search(&local).is_ok()
54+
}
55+
56+
#[inline]
57+
pub fn iter(self) -> impl ExactSizeIterator<Item = BindingId> {
58+
self.0.iter().copied()
59+
}
60+
61+
#[inline]
62+
pub fn is_empty(self) -> bool {
63+
self.0.is_empty()
64+
}
65+
66+
#[inline]
67+
pub const fn empty() -> Self {
68+
UpvarsRef(&[])
69+
}
3970
}
4071

4172
/// Returns a map from `Expr::Closure` to its upvars.
42-
#[salsa::tracked(returns(as_deref))]
4373
pub fn upvars_mentioned(
4474
db: &dyn HirDatabase,
45-
owner: DefWithBodyId,
75+
owner: ExpressionStoreOwnerId,
76+
) -> Option<&FxHashMap<ExprId, Upvars>> {
77+
return match owner {
78+
ExpressionStoreOwnerId::Signature(owner) => signature_upvars_mentioned(db, owner),
79+
ExpressionStoreOwnerId::Body(owner) => body_upvars_mentioned(db, owner),
80+
ExpressionStoreOwnerId::VariantFields(owner) => variant_fields_upvars_mentioned(db, owner),
81+
};
82+
83+
#[salsa::tracked(returns(as_deref))]
84+
pub fn signature_upvars_mentioned(
85+
db: &dyn HirDatabase,
86+
owner: GenericDefId,
87+
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
88+
upvars_mentioned_impl(db, owner.into())
89+
}
90+
91+
#[salsa::tracked(returns(as_deref))]
92+
pub fn body_upvars_mentioned(
93+
db: &dyn HirDatabase,
94+
owner: DefWithBodyId,
95+
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
96+
upvars_mentioned_impl(db, owner.into())
97+
}
98+
99+
#[salsa::tracked(returns(as_deref))]
100+
pub fn variant_fields_upvars_mentioned(
101+
db: &dyn HirDatabase,
102+
owner: VariantId,
103+
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
104+
upvars_mentioned_impl(db, owner.into())
105+
}
106+
}
107+
108+
pub fn upvars_mentioned_impl(
109+
db: &dyn HirDatabase,
110+
owner: ExpressionStoreOwnerId,
46111
) -> Option<Box<FxHashMap<ExprId, Upvars>>> {
47-
let body = Body::of(db, owner);
112+
let store = ExpressionStore::of(db, owner);
113+
if store.const_expr_origins().is_empty() {
114+
// Save constructing a Resolver.
115+
return None;
116+
}
48117
let mut resolver = owner.resolver(db);
49118
let mut result = FxHashMap::default();
50-
handle_expr_outside_closure(db, &mut resolver, owner, body, body.root_expr(), &mut result);
119+
for root_expr in store.expr_roots() {
120+
handle_expr_outside_closure(db, &mut resolver, owner, store, root_expr, &mut result);
121+
}
51122
return if result.is_empty() {
52123
None
53124
} else {
@@ -58,8 +129,8 @@ pub fn upvars_mentioned(
58129
fn handle_expr_outside_closure<'db>(
59130
db: &'db dyn HirDatabase,
60131
resolver: &mut Resolver<'db>,
61-
owner: DefWithBodyId,
62-
body: &Body,
132+
owner: ExpressionStoreOwnerId,
133+
body: &ExpressionStore,
63134
expr: ExprId,
64135
closures_map: &mut FxHashMap<ExprId, Upvars>,
65136
) {
@@ -89,8 +160,8 @@ pub fn upvars_mentioned(
89160
fn handle_expr_inside_closure<'db>(
90161
db: &'db dyn HirDatabase,
91162
resolver: &mut Resolver<'db>,
92-
owner: DefWithBodyId,
93-
body: &Body,
163+
owner: ExpressionStoreOwnerId,
164+
body: &ExpressionStore,
94165
current_closure: ExprId,
95166
expr: ExprId,
96167
upvars: &mut FxHashSet<BindingId>,
@@ -170,17 +241,18 @@ pub fn upvars_mentioned(
170241
fn resolve_maybe_upvar<'db>(
171242
db: &'db dyn HirDatabase,
172243
resolver: &mut Resolver<'db>,
173-
owner: DefWithBodyId,
174-
body: &Body,
244+
owner: ExpressionStoreOwnerId,
245+
body: &ExpressionStore,
175246
current_closure: ExprId,
176247
expr: ExprId,
177248
id: ExprOrPatId,
178249
upvars: &mut FxHashSet<BindingId>,
179250
path: &Path,
180251
) {
181252
if let Path::BarePath(mod_path) = path
182-
&& matches!(mod_path.kind, PathKind::Plain)
183-
&& mod_path.segments().len() == 1
253+
&& matches!(mod_path.kind, PathKind::Plain | PathKind::SELF)
254+
// `self` is length zero.
255+
&& mod_path.segments().len() <= 1
184256
{
185257
// Could be a variable.
186258
let guard = resolver.update_to_inner_scope(db, owner, expr);
@@ -198,7 +270,9 @@ fn resolve_maybe_upvar<'db>(
198270
#[cfg(test)]
199271
mod tests {
200272
use expect_test::{Expect, expect};
201-
use hir_def::{ModuleDefId, expr_store::Body, nameres::crate_def_map};
273+
use hir_def::{
274+
AssocItemId, DefWithBodyId, ModuleDefId, expr_store::Body, nameres::crate_def_map,
275+
};
202276
use itertools::Itertools;
203277
use span::Edition;
204278
use test_fixture::WithFixture;
@@ -217,10 +291,18 @@ mod tests {
217291
ModuleDefId::FunctionId(func) => Some(func),
218292
_ => None,
219293
})
294+
.chain(def_map.modules().flat_map(|(_, module)| {
295+
module.scope.impls().flat_map(|impl_| &*impl_.impl_items(&db).items).filter_map(
296+
|&(_, item)| match item {
297+
AssocItemId::FunctionId(it) => Some(it),
298+
_ => None,
299+
},
300+
)
301+
}))
220302
.exactly_one()
221303
.unwrap_or_else(|_| panic!("expected one function"));
222304
let (body, source_map) = Body::with_source_map(&db, func.into());
223-
let Some(upvars) = upvars_mentioned(&db, func.into()) else {
305+
let Some(upvars) = upvars_mentioned(&db, DefWithBodyId::from(func).into()) else {
224306
expectation.assert_eq("");
225307
return;
226308
};
@@ -316,4 +398,19 @@ fn foo() {
316398
49..110: a, b"#]],
317399
);
318400
}
401+
402+
#[test]
403+
fn self_upvar() {
404+
check(
405+
r#"
406+
struct Foo(i32);
407+
impl Foo {
408+
fn foo(&self) {
409+
|| self.0;
410+
}
411+
}
412+
"#,
413+
expect!["56..65: self"],
414+
);
415+
}
319416
}

0 commit comments

Comments
 (0)