charon_lib/transform/reconstruct_asserts.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
//! In the MIR AST, it seems `assert` are introduced to check preconditions
//! (for the binops for example). The `assert!` introduced by the user
//! introduce `if ... then { panic!(...) } else { ...}`.
//! This pass introduces `assert` instead in order to make the code shorter.
use std::collections::HashSet;
use crate::transform::TransformCtx;
use crate::ullbc_ast::*;
use super::ctx::UllbcPass;
pub struct Transform;
impl UllbcPass for Transform {
fn transform_body(&self, _ctx: &mut TransformCtx, b: &mut ExprBody) {
// Start by computing the set of blocks which are actually panics.
// Remark: doing this in two steps because reading the blocks at random
// while doing in-place updates is not natural to do in Rust.
let panics: HashSet<BlockId> = b
.body
.iter_indexed()
.filter_map(|(bid, block)| {
if block.statements.is_empty() && block.terminator.content.is_abort() {
Some(bid)
} else {
None
}
})
.collect();
for block in b.body.iter_mut() {
match &block.terminator.content {
RawTerminator::Switch {
discr: _,
targets: SwitchTargets::If(bid0, bid1),
} => {
let (nbid, expected) = if panics.contains(bid0) {
(*bid1, false)
} else if panics.contains(bid1) {
(*bid0, true)
} else {
continue;
};
let content = std::mem::replace(
&mut block.terminator.content,
RawTerminator::Goto { target: nbid },
);
let (discr, _) = content.as_switch().unwrap();
block.statements.push(Statement {
span: block.terminator.span,
content: RawStatement::Assert(Assert {
cond: discr.clone(),
expected,
}),
});
}
_ => (),
}
}
}
}