Skip to content

Commit c72df45

Browse files
Do not create stale expressions in body lowering
1 parent d4f45d7 commit c72df45

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
}
@@ -536,7 +536,7 @@ impl<'db> ExprCollector<'db> {
536536
current_try_block_label: None,
537537
is_lowering_coroutine: false,
538538
label_ribs: Vec::new(),
539-
current_binding_owner: None,
539+
unowned_bindings: Vec::new(),
540540
awaitable_context: None,
541541
current_block_legacy_macro_defs_count: FxHashMap::default(),
542542
outer_impl_trait: false,
@@ -1062,12 +1062,10 @@ impl<'db> ExprCollector<'db> {
10621062
Some(ast::BlockModifier::Const(_)) => {
10631063
self.with_label_rib(RibKind::Constant, |this| {
10641064
this.with_awaitable_block(Awaitable::No("constant block"), |this| {
1065-
let (result_expr_id, prev_binding_owner) =
1066-
this.initialize_binding_owner(syntax_ptr);
1067-
let inner_expr = this.collect_block(e);
1068-
this.store.exprs[result_expr_id] = Expr::Const(inner_expr);
1069-
this.current_binding_owner = prev_binding_owner;
1070-
result_expr_id
1065+
this.with_binding_owner(|this| {
1066+
let inner_expr = this.collect_block(e);
1067+
this.alloc_expr(Expr::Const(inner_expr), syntax_ptr)
1068+
})
10711069
})
10721070
})
10731071
}
@@ -1278,64 +1276,65 @@ impl<'db> ExprCollector<'db> {
12781276
}
12791277
}
12801278
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
1281-
let (result_expr_id, prev_binding_owner) =
1282-
this.initialize_binding_owner(syntax_ptr);
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);
1279+
this.with_binding_owner(|this| {
1280+
let mut args = Vec::new();
1281+
let mut arg_types = Vec::new();
1282+
if let Some(pl) = e.param_list() {
1283+
let num_params = pl.params().count();
1284+
args.reserve_exact(num_params);
1285+
arg_types.reserve_exact(num_params);
1286+
for param in pl.params() {
1287+
let pat = this.collect_pat_top(param.pat());
1288+
let type_ref =
1289+
param.ty().map(|it| this.lower_type_ref_disallow_impl_trait(it));
1290+
args.push(pat);
1291+
arg_types.push(type_ref);
1292+
}
12951293
}
1296-
}
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));
1294+
let ret_type = e
1295+
.ret_type()
1296+
.and_then(|r| r.ty())
1297+
.map(|it| this.lower_type_ref_disallow_impl_trait(it));
13011298

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();
1299+
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
1300+
let prev_try_block_label = this.current_try_block_label.take();
13041301

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

1313-
let closure_kind = if this.is_lowering_coroutine {
1314-
let movability = if e.static_token().is_some() {
1315-
Movability::Static
1310+
let closure_kind = if this.is_lowering_coroutine {
1311+
let movability = if e.static_token().is_some() {
1312+
Movability::Static
1313+
} else {
1314+
Movability::Movable
1315+
};
1316+
ClosureKind::Coroutine(movability)
1317+
} else if e.async_token().is_some() {
1318+
ClosureKind::Async
13161319
} else {
1317-
Movability::Movable
1320+
ClosureKind::Closure
13181321
};
1319-
ClosureKind::Coroutine(movability)
1320-
} else if e.async_token().is_some() {
1321-
ClosureKind::Async
1322-
} else {
1323-
ClosureKind::Closure
1324-
};
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_binding_owner = prev_binding_owner;
1329-
this.current_try_block_label = prev_try_block_label;
1330-
this.store.exprs[result_expr_id] = 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-
result_expr_id
1322+
let capture_by =
1323+
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
1324+
this.is_lowering_coroutine = prev_is_lowering_coroutine;
1325+
this.current_try_block_label = prev_try_block_label;
1326+
this.alloc_expr(
1327+
Expr::Closure {
1328+
args: args.into(),
1329+
arg_types: arg_types.into(),
1330+
ret_type,
1331+
body,
1332+
closure_kind,
1333+
capture_by,
1334+
},
1335+
syntax_ptr,
1336+
)
1337+
})
13391338
}),
13401339
ast::Expr::BinExpr(e) => {
13411340
let op = e.op_kind();
@@ -1371,11 +1370,7 @@ impl<'db> ExprCollector<'db> {
13711370
let initializer = self.collect_expr_opt(initializer);
13721371
let repeat = self.with_label_rib(RibKind::Constant, |this| {
13731372
if let Some(repeat) = repeat {
1374-
let syntax_ptr = AstPtr::new(&repeat);
1375-
this.collect_as_a_binding_owner_bad(
1376-
|this| this.collect_expr(repeat),
1377-
syntax_ptr,
1378-
)
1373+
this.with_binding_owner(|this| this.collect_expr(repeat))
13791374
} else {
13801375
this.missing_expr()
13811376
}
@@ -1632,31 +1627,13 @@ impl<'db> ExprCollector<'db> {
16321627
}
16331628
}
16341629

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

16621639
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@@ -2368,11 +2345,7 @@ impl<'db> ExprCollector<'db> {
23682345
ast::Pat::ConstBlockPat(const_block_pat) => {
23692346
if let Some(block) = const_block_pat.block_expr() {
23702347
let expr_id = self.with_label_rib(RibKind::Constant, |this| {
2371-
let syntax_ptr = AstPtr::new(&block.clone().into());
2372-
this.collect_as_a_binding_owner_bad(
2373-
|this| this.collect_block(block),
2374-
syntax_ptr,
2375-
)
2348+
this.with_binding_owner(|this| this.collect_block(block))
23762349
});
23772350
Pat::ConstBlock(expr_id)
23782351
} else {
@@ -3376,9 +3349,7 @@ impl ExprCollector<'_> {
33763349
hygiene: HygieneId,
33773350
) -> BindingId {
33783351
let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene });
3379-
if let Some(owner) = self.current_binding_owner {
3380-
self.store.binding_owners.insert(binding, owner);
3381-
}
3352+
self.unowned_bindings.push(binding);
33823353
binding
33833354
}
33843355

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)