Merge pull request #21209 from ChayimFriedman2/stale-expr
Some checks are pending
metrics / build_metrics (push) Waiting to run
metrics / other_metrics (diesel-1.4.8) (push) Blocked by required conditions
metrics / other_metrics (hyper-0.14.18) (push) Blocked by required conditions
metrics / other_metrics (ripgrep-13.0.0) (push) Blocked by required conditions
metrics / other_metrics (self) (push) Blocked by required conditions
metrics / other_metrics (webrender-2022) (push) Blocked by required conditions
metrics / generate_final_metrics (push) Blocked by required conditions
rustdoc / rustdoc (push) Waiting to run

internal: Do not create stale expressions in body lowering
This commit is contained in:
Shoyu Vanilla (Flint) 2025-12-08 16:28:46 +00:00 committed by GitHub
commit 25278ada1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 100 deletions

View file

@ -434,7 +434,7 @@ pub struct ExprCollector<'db> {
current_try_block_label: Option<LabelId>, current_try_block_label: Option<LabelId>,
label_ribs: Vec<LabelRib>, label_ribs: Vec<LabelRib>,
current_binding_owner: Option<ExprId>, unowned_bindings: Vec<BindingId>,
awaitable_context: Option<Awaitable>, awaitable_context: Option<Awaitable>,
krate: base_db::Crate, krate: base_db::Crate,
@ -538,7 +538,7 @@ impl<'db> ExprCollector<'db> {
current_try_block_label: None, current_try_block_label: None,
is_lowering_coroutine: false, is_lowering_coroutine: false,
label_ribs: Vec::new(), label_ribs: Vec::new(),
current_binding_owner: None, unowned_bindings: Vec::new(),
awaitable_context: None, awaitable_context: None,
current_block_legacy_macro_defs_count: FxHashMap::default(), current_block_legacy_macro_defs_count: FxHashMap::default(),
outer_impl_trait: false, outer_impl_trait: false,
@ -1065,12 +1065,10 @@ impl<'db> ExprCollector<'db> {
Some(ast::BlockModifier::Const(_)) => { Some(ast::BlockModifier::Const(_)) => {
self.with_label_rib(RibKind::Constant, |this| { self.with_label_rib(RibKind::Constant, |this| {
this.with_awaitable_block(Awaitable::No("constant block"), |this| { this.with_awaitable_block(Awaitable::No("constant block"), |this| {
let (result_expr_id, prev_binding_owner) = this.with_binding_owner(|this| {
this.initialize_binding_owner(syntax_ptr);
let inner_expr = this.collect_block(e); let inner_expr = this.collect_block(e);
this.store.exprs[result_expr_id] = Expr::Const(inner_expr); this.alloc_expr(Expr::Const(inner_expr), syntax_ptr)
this.current_binding_owner = prev_binding_owner; })
result_expr_id
}) })
}) })
} }
@ -1281,8 +1279,7 @@ impl<'db> ExprCollector<'db> {
} }
} }
ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| { ast::Expr::ClosureExpr(e) => self.with_label_rib(RibKind::Closure, |this| {
let (result_expr_id, prev_binding_owner) = this.with_binding_owner(|this| {
this.initialize_binding_owner(syntax_ptr);
let mut args = Vec::new(); let mut args = Vec::new();
let mut arg_types = Vec::new(); let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() { if let Some(pl) = e.param_list() {
@ -1310,8 +1307,8 @@ impl<'db> ExprCollector<'db> {
} else { } else {
Awaitable::No("non-async closure") Awaitable::No("non-async closure")
}; };
let body = let body = this
this.with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body())); .with_awaitable_block(awaitable, |this| this.collect_expr_opt(e.body()));
let closure_kind = if this.is_lowering_coroutine { let closure_kind = if this.is_lowering_coroutine {
let movability = if e.static_token().is_some() { let movability = if e.static_token().is_some() {
@ -1328,17 +1325,19 @@ impl<'db> ExprCollector<'db> {
let capture_by = let capture_by =
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
this.is_lowering_coroutine = prev_is_lowering_coroutine; this.is_lowering_coroutine = prev_is_lowering_coroutine;
this.current_binding_owner = prev_binding_owner;
this.current_try_block_label = prev_try_block_label; this.current_try_block_label = prev_try_block_label;
this.store.exprs[result_expr_id] = Expr::Closure { this.alloc_expr(
Expr::Closure {
args: args.into(), args: args.into(),
arg_types: arg_types.into(), arg_types: arg_types.into(),
ret_type, ret_type,
body, body,
closure_kind, closure_kind,
capture_by, capture_by,
}; },
result_expr_id syntax_ptr,
)
})
}), }),
ast::Expr::BinExpr(e) => { ast::Expr::BinExpr(e) => {
let op = e.op_kind(); let op = e.op_kind();
@ -1374,11 +1373,7 @@ impl<'db> ExprCollector<'db> {
let initializer = self.collect_expr_opt(initializer); let initializer = self.collect_expr_opt(initializer);
let repeat = self.with_label_rib(RibKind::Constant, |this| { let repeat = self.with_label_rib(RibKind::Constant, |this| {
if let Some(repeat) = repeat { if let Some(repeat) = repeat {
let syntax_ptr = AstPtr::new(&repeat); this.with_binding_owner(|this| this.collect_expr(repeat))
this.collect_as_a_binding_owner_bad(
|this| this.collect_expr(repeat),
syntax_ptr,
)
} else { } else {
this.missing_expr() this.missing_expr()
} }
@ -1635,31 +1630,13 @@ impl<'db> ExprCollector<'db> {
} }
} }
fn initialize_binding_owner( fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId) -> ExprId {
&mut self, let prev_unowned_bindings_len = self.unowned_bindings.len();
syntax_ptr: AstPtr<ast::Expr>, let expr_id = create_expr(self);
) -> (ExprId, Option<ExprId>) { for binding in self.unowned_bindings.drain(prev_unowned_bindings_len..) {
let result_expr_id = self.alloc_expr(Expr::Missing, syntax_ptr); self.store.binding_owners.insert(binding, expr_id);
let prev_binding_owner = self.current_binding_owner.take();
self.current_binding_owner = Some(result_expr_id);
(result_expr_id, prev_binding_owner)
} }
expr_id
/// FIXME: This function is bad. It will produce a dangling `Missing` expr which wastes memory. Currently
/// it is used only for const blocks and repeat expressions, which are also hacky and ideally should have
/// their own body. Don't add more usage for this function so that we can remove this function after
/// separating those bodies.
fn collect_as_a_binding_owner_bad(
&mut self,
job: impl FnOnce(&mut ExprCollector<'_>) -> ExprId,
syntax_ptr: AstPtr<ast::Expr>,
) -> ExprId {
let (id, prev_owner) = self.initialize_binding_owner(syntax_ptr);
let tmp = job(self);
self.store.exprs[id] = mem::replace(&mut self.store.exprs[tmp], Expr::Missing);
self.current_binding_owner = prev_owner;
id
} }
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`, /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
@ -2371,11 +2348,7 @@ impl<'db> ExprCollector<'db> {
ast::Pat::ConstBlockPat(const_block_pat) => { ast::Pat::ConstBlockPat(const_block_pat) => {
if let Some(block) = const_block_pat.block_expr() { if let Some(block) = const_block_pat.block_expr() {
let expr_id = self.with_label_rib(RibKind::Constant, |this| { let expr_id = self.with_label_rib(RibKind::Constant, |this| {
let syntax_ptr = AstPtr::new(&block.clone().into()); this.with_binding_owner(|this| this.collect_block(block))
this.collect_as_a_binding_owner_bad(
|this| this.collect_block(block),
syntax_ptr,
)
}); });
Pat::ConstBlock(expr_id) Pat::ConstBlock(expr_id)
} else { } else {
@ -3379,9 +3352,7 @@ impl ExprCollector<'_> {
hygiene: HygieneId, hygiene: HygieneId,
) -> BindingId { ) -> BindingId {
let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene }); let binding = self.store.bindings.alloc(Binding { name, mode, problems: None, hygiene });
if let Some(owner) = self.current_binding_owner { self.unowned_bindings.push(binding);
self.store.binding_owners.insert(binding, owner);
}
binding binding
} }

View file

@ -995,6 +995,10 @@ fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 {
} }
"#, "#,
); );
// FIXME: There should be no "unused variable" here, and there should be a mutability error,
// but our MIR infra is horribly broken and due to the order in which expressions are lowered
// there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable
// of the closure, the environment should be, but as I said, our MIR infra is horribly broken).
check_diagnostics( check_diagnostics(
r#" r#"
//- minicore: copy, fn //- minicore: copy, fn
@ -1003,8 +1007,8 @@ fn f() {
|| { || {
|| { || {
let x = 2; let x = 2;
// ^ 💡 warn: unused variable
|| { || { x = 5; } } || { || { x = 5; } }
//^^^^^ 💡 error: cannot mutate immutable variable `x`
} }
} }
}; };