Skip to content

Commit d7da9e6

Browse files
committed
Add storage dead for let bindings without initializer
1 parent 4cbb940 commit d7da9e6

File tree

4 files changed

+79
-44
lines changed

4 files changed

+79
-44
lines changed

crates/hir-def/src/body.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ use syntax::{ast, AstPtr, SyntaxNode, SyntaxNodePtr};
2424
use crate::{
2525
attr::Attrs,
2626
db::DefDatabase,
27-
expr::{dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId},
27+
expr::{
28+
dummy_expr_id, Binding, BindingId, Expr, ExprId, Label, LabelId, Pat, PatId, RecordFieldPat,
29+
},
2830
item_scope::BuiltinShadowMode,
2931
macro_id_to_def_id,
3032
nameres::DefMap,
@@ -432,6 +434,44 @@ impl Body {
432434
pats.shrink_to_fit();
433435
bindings.shrink_to_fit();
434436
}
437+
438+
pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
439+
self.walk_pats(pat_id, &mut |pat| {
440+
if let Pat::Bind { id, .. } = pat {
441+
f(*id);
442+
}
443+
});
444+
}
445+
446+
pub fn walk_pats(&self, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
447+
let pat = &self[pat_id];
448+
f(pat);
449+
match pat {
450+
Pat::Range { .. }
451+
| Pat::Lit(..)
452+
| Pat::Path(..)
453+
| Pat::ConstBlock(..)
454+
| Pat::Wild
455+
| Pat::Missing => {}
456+
&Pat::Bind { subpat, .. } => {
457+
if let Some(subpat) = subpat {
458+
self.walk_pats(subpat, f);
459+
}
460+
}
461+
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
462+
args.iter().copied().for_each(|p| self.walk_pats(p, f));
463+
}
464+
Pat::Ref { pat, .. } => self.walk_pats(*pat, f),
465+
Pat::Slice { prefix, slice, suffix } => {
466+
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
467+
total_iter.copied().for_each(|p| self.walk_pats(p, f));
468+
}
469+
Pat::Record { args, .. } => {
470+
args.iter().for_each(|RecordFieldPat { pat, .. }| self.walk_pats(*pat, f));
471+
}
472+
Pat::Box { inner } => self.walk_pats(*inner, f),
473+
}
474+
}
435475
}
436476

437477
impl Default for Body {

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

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,7 @@ use std::iter::repeat_with;
55
use chalk_ir::Mutability;
66
use hir_def::{
77
body::Body,
8-
expr::{
9-
Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId,
10-
RecordFieldPat,
11-
},
8+
expr::{Binding, BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, Literal, Pat, PatId},
129
path::Path,
1310
};
1411
use hir_expand::name::Name;
@@ -439,38 +436,8 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool {
439436

440437
pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
441438
let mut res = false;
442-
walk_pats(body, pat_id, &mut |pat| {
439+
body.walk_pats(pat_id, &mut |pat| {
443440
res |= matches!(pat, Pat::Bind { id, .. } if body.bindings[*id].mode == BindingAnnotation::Ref);
444441
});
445442
res
446443
}
447-
448-
fn walk_pats(body: &Body, pat_id: PatId, f: &mut impl FnMut(&Pat)) {
449-
let pat = &body[pat_id];
450-
f(pat);
451-
match pat {
452-
Pat::Range { .. }
453-
| Pat::Lit(..)
454-
| Pat::Path(..)
455-
| Pat::ConstBlock(..)
456-
| Pat::Wild
457-
| Pat::Missing => {}
458-
&Pat::Bind { subpat, .. } => {
459-
if let Some(subpat) = subpat {
460-
walk_pats(body, subpat, f);
461-
}
462-
}
463-
Pat::Or(args) | Pat::Tuple { args, .. } | Pat::TupleStruct { args, .. } => {
464-
args.iter().copied().for_each(|p| walk_pats(body, p, f));
465-
}
466-
Pat::Ref { pat, .. } => walk_pats(body, *pat, f),
467-
Pat::Slice { prefix, slice, suffix } => {
468-
let total_iter = prefix.iter().chain(slice.iter()).chain(suffix.iter());
469-
total_iter.copied().for_each(|p| walk_pats(body, p, f));
470-
}
471-
Pat::Record { args, .. } => {
472-
args.iter().for_each(|RecordFieldPat { pat, .. }| walk_pats(body, *pat, f));
473-
}
474-
Pat::Box { inner } => walk_pats(body, *inner, f),
475-
}
476-
}

crates/hir-ty/src/mir/lower.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ impl MirLowerCtx<'_> {
11131113
if matches!(mode, BindingAnnotation::Ref | BindingAnnotation::RefMut) {
11141114
binding_mode = mode;
11151115
}
1116-
self.push_storage_live(*id, current)?;
1116+
self.push_storage_live(*id, current);
11171117
self.push_assignment(
11181118
current,
11191119
target_place.into(),
@@ -1327,8 +1327,9 @@ impl MirLowerCtx<'_> {
13271327
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
13281328
}
13291329

1330-
/// This function push `StorageLive` statements for each binding in the pattern.
1331-
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) -> Result<()> {
1330+
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` in
1331+
/// the appropriated places.
1332+
fn push_storage_live(&mut self, b: BindingId, current: BasicBlockId) {
13321333
// Current implementation is wrong. It adds no `StorageDead` at the end of scope, and before each break
13331334
// and continue. It just add a `StorageDead` before the `StorageLive`, which is not wrong, but unneeeded in
13341335
// the proper implementation. Due this limitation, implementing a borrow checker on top of this mir will falsely
@@ -1356,7 +1357,6 @@ impl MirLowerCtx<'_> {
13561357
let l = self.result.binding_locals[b];
13571358
self.push_statement(current, StatementKind::StorageDead(l).with_span(span));
13581359
self.push_statement(current, StatementKind::StorageLive(l).with_span(span));
1359-
Ok(())
13601360
}
13611361

13621362
fn resolve_lang_item(&self, item: LangItem) -> Result<LangItemTarget> {
@@ -1381,10 +1381,10 @@ impl MirLowerCtx<'_> {
13811381
if let Some(expr_id) = initializer {
13821382
let else_block;
13831383
let Some((init_place, c)) =
1384-
self.lower_expr_as_place(current, *expr_id, true)?
1385-
else {
1386-
return Ok(None);
1387-
};
1384+
self.lower_expr_as_place(current, *expr_id, true)?
1385+
else {
1386+
return Ok(None);
1387+
};
13881388
current = c;
13891389
(current, else_block) = self.pattern_match(
13901390
current,
@@ -1407,6 +1407,10 @@ impl MirLowerCtx<'_> {
14071407
}
14081408
}
14091409
}
1410+
} else {
1411+
self.body.walk_bindings_in_pat(*pat, |b| {
1412+
self.push_storage_live(b, current);
1413+
});
14101414
}
14111415
}
14121416
hir_def::expr::Statement::Expr { expr, has_semi: _ } => {

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,30 @@ fn main() {
505505
);
506506
}
507507

508+
#[test]
509+
fn initialization_is_not_mutation_in_loop() {
510+
check_diagnostics(
511+
r#"
512+
fn main() {
513+
let a;
514+
loop {
515+
let c @ (
516+
mut b,
517+
//^^^^^ 💡 weak: variable does not need to be mutable
518+
mut d
519+
//^^^^^ 💡 weak: variable does not need to be mutable
520+
);
521+
a = 1;
522+
//^^^^^ 💡 error: cannot mutate immutable variable `a`
523+
b = 1;
524+
c = (2, 3);
525+
d = 3;
526+
}
527+
}
528+
"#,
529+
);
530+
}
531+
508532
#[test]
509533
fn function_arguments_are_initialized() {
510534
check_diagnostics(

0 commit comments

Comments
 (0)