Skip to content

Commit 25278ad

Browse files
authored
Merge pull request #21209 from ChayimFriedman2/stale-expr
internal: Do not create stale expressions in body lowering
2 parents 35d2fc4 + c72df45 commit 25278ad

File tree

2 files changed

+74
-99
lines changed

2 files changed

+74
-99
lines changed

crates/hir-def/src/expr_store/lower.rs

Lines changed: 69 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ pub struct ExprCollector<'db> {
434434
current_try_block_label: Option<LabelId>,
435435

436436
label_ribs: Vec<LabelRib>,
437-
current_binding_owner: Option<ExprId>,
437+
unowned_bindings: Vec<BindingId>,
438438

439439
awaitable_context: Option<Awaitable>,
440440
krate: base_db::Crate,
@@ -538,7 +538,7 @@ impl<'db> ExprCollector<'db> {
538538
current_try_block_label: None,
539539
is_lowering_coroutine: false,
540540
label_ribs: Vec::new(),
541-
current_binding_owner: None,
541+
unowned_bindings: Vec::new(),
542542
awaitable_context: None,
543543
current_block_legacy_macro_defs_count: FxHashMap::default(),
544544
outer_impl_trait: false,
@@ -1065,12 +1065,10 @@ impl<'db> ExprCollector<'db> {
10651065
Some(ast::BlockModifier::Const(_)) => {
10661066
self.with_label_rib(RibKind::Constant, |this| {
10671067
this.with_awaitable_block(Awaitable::No("constant block"), |this| {
1068-
let (result_expr_id, prev_binding_owner) =
1069-
this.initialize_binding_owner(syntax_ptr);
1070-
let inner_expr = this.collect_block(e);
1071-
this.store.exprs[result_expr_id] = Expr::Const(inner_expr);
1072-
this.current_binding_owner = prev_binding_owner;
1073-
result_expr_id
1068+
this.with_binding_owner(|this| {
1069+
let inner_expr = this.collect_block(e);
1070+
this.alloc_expr(Expr::Const(inner_expr), syntax_ptr)
1071+
})
10741072
})
10751073
})
10761074
}
@@ -1281,64 +1279,65 @@ impl<'db> ExprCollector<'db> {
12811279
}
12821280
}
12831281
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
1284-
let (result_expr_id, prev_binding_owner) =
1285-
this.initialize_binding_owner(syntax_ptr);
1286-
let mut args = Vec::new();
1287-
let mut arg_types = Vec::new();
1288-
if let Some(pl) = e.param_list() {
1289-
let num_params = pl.params().count();
1290-
args.reserve_exact(num_params);
1291-
arg_types.reserve_exact(num_params);
1292-
for param in pl.params() {
1293-
let pat = this.collect_pat_top(param.pat());
1294-
let type_ref =
1295-
param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it));
1296-
args.push(pat);
1297-
arg_types.push(type_ref);
1282+
this.with_binding_owner(|this| {
1283+
let mut args = Vec::new();
1284+
let mut arg_types = Vec::new();
1285+
if let Some(pl) = e.param_list() {
1286+
let num_params = pl.params().count();
1287+
args.reserve_exact(num_params);
1288+
arg_types.reserve_exact(num_params);
1289+
for param in pl.params() {
1290+
let pat = this.collect_pat_top(param.pat());
1291+
let type_ref =
1292+
param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it));
1293+
args.push(pat);
1294+
arg_types.push(type_ref);
1295+
}
12981296
}
1299-
}
1300-
let ret_type = e
1301-
.ret_type()
1302-
.and_then(|r| r.ty())
1303-
.map(|it| this.lower_type_ref_disallow_impl_trait(it));
1297+
let ret_type = e
1298+
.ret_type()
1299+
.and_then(|r| r.ty())
1300+
.map(|it| this.lower_type_ref_disallow_impl_trait(it));
13041301

1305-
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
1306-
let prev_try_block_label = this.current_try_block_label.take();
1302+
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
1303+
let prev_try_block_label = this.current_try_block_label.take();
13071304

1308-
let awaitable = if e.async_token().is_some() {
1309-
Awaitable::Yes
1310-
} else {
1311-
Awaitable::No("non-async closure")
1312-
};
1313-
let body =
1314-
this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
1305+
let awaitable = if e.async_token().is_some() {
1306+
Awaitable::Yes
1307+
} else {
1308+
Awaitable::No("non-async closure")
1309+
};
1310+
let body = this
1311+
.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
13151312

1316-
let closure_kind = if this.is_lowering_coroutine {
1317-
let movability = if e.static_token().is_some() {
1318-
Movability::Static
1313+
let closure_kind = if this.is_lowering_coroutine {
1314+
let movability = if e.static_token().is_some() {
1315+
Movability::Static
1316+
} else {
1317+
Movability::Movable
1318+
};
1319+
ClosureKind::Coroutine(movability)
1320+
} else if e.async_token().is_some() {
1321+
ClosureKind::Async
13191322
} else {
1320-
Movability::Movable
1323+
ClosureKind::Closure
13211324
};
1322-
ClosureKind::Coroutine(movability)
1323-
} else if e.async_token().is_some() {
1324-
ClosureKind::Async
1325-
} else {
1326-
ClosureKind::Closure
1327-
};
1328-
let capture_by =
1329-
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
1330-
this.is_lowering_coroutine = prev_is_lowering_coroutine;
1331-
this.current_binding_owner = prev_binding_owner;
1332-
this.current_try_block_label = prev_try_block_label;
1333-
this.store.exprs[result_expr_id] = Expr::Closure {
1334-
args: args.into(),
1335-
arg_types: arg_types.into(),
1336-
ret_type,
1337-
body,
1338-
closure_kind,
1339-
capture_by,
1340-
};
1341-
result_expr_id
1325+
let capture_by =
1326+
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
1327+
this.is_lowering_coroutine = prev_is_lowering_coroutine;
1328+
this.current_try_block_label = prev_try_block_label;
1329+
this.alloc_expr(
1330+
Expr::Closure {
1331+
args: args.into(),
1332+
arg_types: arg_types.into(),
1333+
ret_type,
1334+
body,
1335+
closure_kind,
1336+
capture_by,
1337+
},
1338+
syntax_ptr,
1339+
)
1340+
})
13421341
}),
13431342
ast::Expr::BinExpr(e) => {
13441343
let op = e.op_kind();
@@ -1374,11 +1373,7 @@ impl<'db> ExprCollector<'db> {
13741373
let initializer = self.collect_expr_opt(initializer);
13751374
let repeat = self.with_label_rib(RibKind::Constant, |this| {
13761375
if let Some(repeat) = repeat {
1377-
let syntax_ptr = AstPtr::new(&repeat);
1378-
this.collect_as_a_binding_owner_bad(
1379-
|this| this.collect_expr(repeat),
1380-
syntax_ptr,
1381-
)
1376+
this.with_binding_owner(|this| this.collect_expr(repeat))
13821377
} else {
13831378
this.missing_expr()
13841379
}
@@ -1635,31 +1630,13 @@ impl<'db> ExprCollector<'db> {
16351630
}
16361631
}
16371632

1638-
fn initialize_binding_owner(
1639-
&mut self,
1640-
syntax_ptr: AstPtr<ast::Expr>,
1641-
) -> (ExprId, Option<ExprId>) {
1642-
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr);
1643-
let prev_binding_owner = self.current_binding_owner.take();
1644-
self.current_binding_owner = Some(result_expr_id);
1645-
1646-
(result_expr_id, prev_binding_owner)
1647-
}
1648-
1649-
/// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
1650-
/// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
1651-
/// their own body. Don't add more usage for this function so that we can remove this function after
1652-
/// separating those bodies.
1653-
fn collect_as_a_binding_owner_bad(
1654-
&mut self,
1655-
job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
1656-
syntax_ptr: AstPtr<ast::Expr>,
1657-
) -> ExprId {
1658-
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
1659-
let tmp = job(self);
1660-
self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
1661-
self.current_binding_owner = prev_owner;
1662-
id
1633+
fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
1634+
let prev_unowned_bindings_len = self.unowned_bindings.len();
1635+
let expr_id = create_expr(self);
1636+
for binding in self.unowned_bindings.drain(prev_unowned_bindings_len..) {
1637+
self.store.binding_owners.insert(binding, expr_id);
1638+
}
1639+
expr_id
16631640
}
16641641

16651642
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@@ -2371,11 +2348,7 @@ impl<'db> ExprCollector<'db> {
23712348
ast::Pat::ConstBlockPat(const_block_pat) => {
23722349
if let Some(block) = const_block_pat.block_expr() {
23732350
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
2374-
let syntax_ptr = AstPtr::new(&block.clone().into());
2375-
this.collect_as_a_binding_owner_bad(
2376-
|this| this.collect_block(block),
2377-
syntax_ptr,
2378-
)
2351+
this.with_binding_owner(|this| this.collect_block(block))
23792352
});
23802353
Pat::ConstBlock(expr_id)
23812354
} else {
@@ -3379,9 +3352,7 @@ impl ExprCollector<'_> {
33793352
hygiene: HygieneId,
33803353
) -> BindingId {
33813354
let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene });
3382-
if let Some(owner) = self.current_binding_owner {
3383-
self.store.binding_owners.insert(binding, owner);
3384-
}
3355+
self.unowned_bindings.push(binding);
33853356
binding
33863357
}
33873358

crates/ide-diagnostics/src/handlers/mutability_errors.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -995,6 +995,10 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
995995
}
996996
"#,
997997
);
998+
// FIXME: There should be no "unused variable" here, and there should be a mutability error,
999+
// but our MIR infra is horribly broken and due to the order in which expressions are lowered
1000+
// there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable
1001+
// of the closure, the environment should be, but as I said, our MIR infra is horribly broken).
9981002
check_diagnostics(
9991003
r#"
10001004
//- minicore: copy, fn
@@ -1003,8 +1007,8 @@ fn f() {
10031007
|| {
10041008
|| {
10051009
let x = 2;
1010+
// ^ 💡 warn: unused variable
10061011
|| { || { x = 5; } }
1007-
//^^^^^ 💡 error: cannot mutate immutable variable `x`
10081012
}
10091013
}
10101014
};

0 commit comments

Comments
 (0)