use crate::{
function_data_builder::FunctionDataBuilder,
function_target::FunctionData,
function_target_pipeline::{
FunctionTargetProcessor, FunctionTargetsHolder, FunctionVariant, VerificationFlavor,
},
options::ProverOptions,
stackless_bytecode::{Bytecode, PropKind},
};
use move_model::{exp_generator::ExpGenerator, model::FunctionEnv};
const EXPECTED_TO_FAIL: &str = "expected to fail";
pub struct InconsistencyCheckInstrumenter {}
impl InconsistencyCheckInstrumenter {
pub fn new() -> Box<Self> {
Box::new(Self {})
}
}
impl FunctionTargetProcessor for InconsistencyCheckInstrumenter {
fn process(
&self,
targets: &mut FunctionTargetsHolder,
fun_env: &FunctionEnv<'_>,
data: FunctionData,
) -> FunctionData {
if fun_env.is_native() || fun_env.is_intrinsic() {
return data;
}
let flavor = match &data.variant {
FunctionVariant::Baseline
| FunctionVariant::Verification(VerificationFlavor::Inconsistency(..)) => {
return data;
}
FunctionVariant::Verification(flavor) => flavor.clone(),
};
let options = ProverOptions::get(fun_env.module_env.env);
let new_data = data.fork(FunctionVariant::Verification(
VerificationFlavor::Inconsistency(Box::new(flavor)),
));
let mut builder = FunctionDataBuilder::new(fun_env, new_data);
let old_code = std::mem::take(&mut builder.data.code);
for bc in old_code {
if matches!(bc, Bytecode::Ret(..))
|| (matches!(bc, Bytecode::Abort(..))
&& !options.unconditional_abort_as_inconsistency)
{
let loc = builder.fun_env.get_spec_loc();
builder.set_loc_and_vc_info(loc, EXPECTED_TO_FAIL);
let exp = builder.mk_bool_const(false);
builder.emit_with(|id| Bytecode::Prop(id, PropKind::Assert, exp));
}
builder.emit(bc);
}
let new_data = builder.data;
targets.insert_target_data(
&fun_env.get_qualified_id(),
new_data.variant.clone(),
new_data,
);
data
}
fn name(&self) -> String {
"inconsistency_check_instrumenter".to_string()
}
}