use rustc_middle::mir::{BasicBlock, Terminator, TerminatorKind};
use z3::ast::{Ast, Int};
use super::super::state::VmState;
pub(crate) fn exec_call<'z3, 'tcx>(
state: &mut VmState<'z3, 'tcx>,
terminator: &Terminator<'tcx>,
_switch_succ: Option<BasicBlock>,
) -> bool {
let TerminatorKind::Call {
func,
args,
destination,
..
} = &terminator.kind
else {
return false;
};
let caller_id = state.current_frame.current_def_id;
state.exec_call(func, args, destination.local, caller_id);
true
}
pub(crate) fn exec_switch_int<'z3, 'tcx>(
state: &mut VmState<'z3, 'tcx>,
terminator: &Terminator<'tcx>,
switch_succ: Option<BasicBlock>,
) -> bool {
let TerminatorKind::SwitchInt { discr, targets } = &terminator.kind else {
return false;
};
let discr_val = state.value_of_operand(discr);
let cmp_cond = discr_val.bool_cond().cloned();
if let Some(chosen) = switch_succ {
for (value, target) in targets.iter() {
if target == chosen {
let val_term = Int::from_u64(state.z3_ctx, value as u64);
state
.constraints
.assertions
.push(discr_val.z3_term._eq(&val_term));
if value != 0 {
if let Some(ref cond) = cmp_cond {
state.constraints.assertions.push(cond.clone());
}
state.infer_switch_guard(discr);
} else if let Some(ref cond) = cmp_cond {
state.constraints.assertions.push(cond.not());
}
return true;
}
}
if targets.otherwise() == chosen {
for (value, _) in targets.iter() {
let val_term = Int::from_u64(state.z3_ctx, value as u64);
state
.constraints
.assertions
.push(discr_val.z3_term._eq(&val_term).not());
}
if let Some(ref cond) = cmp_cond {
if targets.iter().any(|(v, _)| v == 0) {
state.constraints.assertions.push(cond.clone());
} else if targets.iter().any(|(v, _)| v == 1) {
state.constraints.assertions.push(cond.not());
}
}
}
}
true
}
pub(crate) fn exec_assert<'z3, 'tcx>(
state: &mut VmState<'z3, 'tcx>,
terminator: &Terminator<'tcx>,
_switch_succ: Option<BasicBlock>,
) -> bool {
let TerminatorKind::Assert {
cond, expected, ..
} = &terminator.kind
else {
return false;
};
let expected = *expected;
let cond_val = state.value_of_operand(cond);
let cmp_cond = cond_val.bool_cond().cloned();
let zero = Int::from_u64(state.z3_ctx, 0);
if expected {
state
.constraints
.assertions
.push(cond_val.z3_term._eq(&zero).not());
if let Some(c) = &cmp_cond {
state.constraints.assertions.push(c.clone());
}
} else {
state
.constraints
.assertions
.push(cond_val.z3_term._eq(&zero));
if let Some(c) = &cmp_cond {
state.constraints.assertions.push(c.not());
}
}
state.infer_guard_non_null(cond, expected);
state.infer_guard_align(cond, expected);
true
}
pub(crate) fn exec_drop<'z3, 'tcx>(
state: &mut VmState<'z3, 'tcx>,
terminator: &Terminator<'tcx>,
_switch_succ: Option<BasicBlock>,
) -> bool {
let TerminatorKind::Drop { place, .. } = &terminator.kind else {
return false;
};
state.exec_drop(place);
true
}
pub(crate) fn exec_nop<'z3, 'tcx>(
_state: &mut VmState<'z3, 'tcx>,
terminator: &Terminator<'tcx>,
_switch_succ: Option<BasicBlock>,
) -> bool {
matches!(
&terminator.kind,
TerminatorKind::Goto { .. }
| TerminatorKind::Return
| TerminatorKind::Unreachable
| TerminatorKind::UnwindResume
| TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Yield { .. }
| TerminatorKind::CoroutineDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::TailCall { .. }
)
}