Use external stack in borrowck DFS

Because damnit, it can crash r-a. Why do people make this stupid DFSes anyway (I get it, it's easier until it blows).
This commit is contained in:
Chayim Refael Friedman 2024-10-06 23:48:16 +03:00
parent 5982d9c420
commit 13f853464c

View file

@ -386,82 +386,91 @@ fn ever_initialized_map(
fn dfs( fn dfs(
db: &dyn HirDatabase, db: &dyn HirDatabase,
body: &MirBody, body: &MirBody,
b: BasicBlockId,
l: LocalId, l: LocalId,
stack: &mut Vec<BasicBlockId>,
result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>, result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
) { ) {
let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs while let Some(b) = stack.pop() {
let block = &body.basic_blocks[b]; let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
for statement in &block.statements { let block = &body.basic_blocks[b];
match &statement.kind { for statement in &block.statements {
StatementKind::Assign(p, _) => { match &statement.kind {
if p.projection.lookup(&body.projection_store).is_empty() && p.local == l { StatementKind::Assign(p, _) => {
if p.projection.lookup(&body.projection_store).is_empty() && p.local == l {
is_ever_initialized = true;
}
}
StatementKind::StorageDead(p) => {
if *p == l {
is_ever_initialized = false;
}
}
StatementKind::Deinit(_)
| StatementKind::FakeRead(_)
| StatementKind::Nop
| StatementKind::StorageLive(_) => (),
}
}
let Some(terminator) = &block.terminator else {
never!(
"Terminator should be none only in construction.\nThe body:\n{}",
body.pretty_print(db)
);
return;
};
let mut process = |target, is_ever_initialized| {
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
result[target].insert(l, is_ever_initialized);
stack.push(target);
}
};
match &terminator.kind {
TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
TerminatorKind::SwitchInt { targets, .. } => {
targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable => (),
TerminatorKind::Call { target, cleanup, destination, .. } => {
if destination.projection.lookup(&body.projection_store).is_empty()
&& destination.local == l
{
is_ever_initialized = true; is_ever_initialized = true;
} }
target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
} }
StatementKind::StorageDead(p) => { TerminatorKind::Drop { target, unwind, place: _ } => {
if *p == l { iter::once(target)
is_ever_initialized = false; .chain(unwind)
} .for_each(|&it| process(it, is_ever_initialized));
} }
StatementKind::Deinit(_) TerminatorKind::DropAndReplace { .. }
| StatementKind::FakeRead(_) | TerminatorKind::Assert { .. }
| StatementKind::Nop | TerminatorKind::Yield { .. }
| StatementKind::StorageLive(_) => (), | TerminatorKind::CoroutineDrop
} | TerminatorKind::FalseEdge { .. }
} | TerminatorKind::FalseUnwind { .. } => {
let Some(terminator) = &block.terminator else { never!("We don't emit these MIR terminators yet");
never!(
"Terminator should be none only in construction.\nThe body:\n{}",
body.pretty_print(db)
);
return;
};
let mut process = |target, is_ever_initialized| {
if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
result[target].insert(l, is_ever_initialized);
dfs(db, body, target, l, result);
}
};
match &terminator.kind {
TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
TerminatorKind::SwitchInt { targets, .. } => {
targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::UnwindResume
| TerminatorKind::Abort
| TerminatorKind::Return
| TerminatorKind::Unreachable => (),
TerminatorKind::Call { target, cleanup, destination, .. } => {
if destination.projection.lookup(&body.projection_store).is_empty()
&& destination.local == l
{
is_ever_initialized = true;
} }
target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::Drop { target, unwind, place: _ } => {
iter::once(target).chain(unwind).for_each(|&it| process(it, is_ever_initialized));
}
TerminatorKind::DropAndReplace { .. }
| TerminatorKind::Assert { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::CoroutineDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => {
never!("We don't emit these MIR terminators yet");
} }
} }
} }
let mut stack = Vec::new();
for &l in &body.param_locals { for &l in &body.param_locals {
result[body.start_block].insert(l, true); result[body.start_block].insert(l, true);
dfs(db, body, body.start_block, l, &mut result); stack.clear();
stack.push(body.start_block);
dfs(db, body, l, &mut stack, &mut result);
} }
for l in body.locals.iter().map(|it| it.0) { for l in body.locals.iter().map(|it| it.0) {
db.unwind_if_cancelled(); db.unwind_if_cancelled();
if !result[body.start_block].contains_idx(l) { if !result[body.start_block].contains_idx(l) {
result[body.start_block].insert(l, false); result[body.start_block].insert(l, false);
dfs(db, body, body.start_block, l, &mut result); stack.clear();
stack.push(body.start_block);
dfs(db, body, l, &mut stack, &mut result);
} }
} }
result result