use rustc_hir::def_id::DefId;
use rustc_middle::mir::Local;
use z3::ast::Int;
use crate::{
compat::FxHashSet,
verify::slicer::RelevantItem,
};
use super::state::{ValueFacts, ValueSource, VmState, VmValue};
impl<'z3, 'tcx> VmState<'z3, 'tcx> {
pub(crate) fn execute_items(&mut self, items: &[RelevantItem<'tcx>]) {
self.init_parameters();
for item in items {
if self.path_infeasible {
break;
}
match item {
RelevantItem::Statement {
def_id,
block,
statement_index,
} => {
let body = self.tcx.optimized_mir(*def_id);
let statement = &body.basic_blocks[*block].statements[*statement_index];
crate::verify::vm::rules::exec_statement(self, statement);
}
RelevantItem::Terminator { def_id, block, switch_succ } => {
let body = self.tcx.optimized_mir(*def_id);
let terminator = body.basic_blocks[*block].terminator();
crate::verify::vm::rules::exec_terminator(self, terminator, *switch_succ);
}
RelevantItem::CalleeEntry { callee, args, consts, field_offsets } => {
self.handle_callee_entry(*callee, args, consts, field_offsets);
}
RelevantItem::CalleeExit { dest } => {
self.handle_callee_exit(*dest);
}
RelevantItem::ContractFact { property } => {
self.assert_contract_fact(property);
}
}
}
}
fn handle_callee_entry(
&mut self,
callee: DefId,
arg_locals: &[usize],
arg_consts: &[(usize, crate::analysis::path::graph::InlineConstArg)],
arg_field_offsets: &[usize],
) {
let method_call = arg_locals.first().and_then(|arg| {
self.local_value(Local::from_usize(*arg))
.and_then(|receiver| self.begin_method_call(callee, receiver, Some(Local::from_usize(*arg))))
});
self.active_method_calls.push(method_call);
let arg_referents: Vec<Option<Local>> = arg_locals
.iter()
.map(|arg| {
self.local_value(Local::from_usize(*arg))
.and_then(|value| self.find_local_by_address(&value.z3_term))
})
.collect();
let saved_referents = std::mem::replace(&mut self.inline.arg_referents, arg_referents);
self.inline.path_saved_arg_referents.push(saved_referents);
let saved_writes = std::mem::take(&mut self.inline.deferred_field_writes);
self.inline.path_saved_deferred_writes.push(saved_writes);
let frame = self.save_frame();
let mut arg_fields: Vec<(usize, Vec<usize>, VmValue<'z3, 'tcx>)> = Vec::new();
for (i, arg) in arg_locals.iter().enumerate() {
let caller_local = Local::from_usize(*arg);
let mut source_locals: Vec<(Local, Vec<usize>)> = Vec::new();
let mut seen: FxHashSet<(Local, Vec<usize>)> = FxHashSet::default();
let mut stack = vec![(caller_local, Vec::new())];
while let Some((cur, prefix)) = stack.pop() {
if !seen.insert((cur, prefix.clone())) {
continue;
}
source_locals.push((cur, prefix.clone()));
if let Some(r) = self.find_whole_reborrow_referent(cur) {
stack.push((r, prefix.clone()));
}
if let Some((r, fields)) = self.find_field_reborrow_referent(cur) {
let mut new_prefix = prefix.clone();
new_prefix.extend(fields);
stack.push((r, new_prefix));
}
if let Some(c) = self.find_copy_root(cur) {
stack.push((c, prefix.clone()));
}
}
for (src, prefix) in source_locals {
let keys: Vec<Vec<usize>> = self
.frame_field_paths(&frame, src)
.into_iter()
.filter(|f| {
prefix.is_empty()
|| (f.len() >= prefix.len() && f[..prefix.len()] == prefix[..])
})
.collect();
for fields in keys {
if let Some(fv) = self.frame_field_value(&frame, src, &fields).cloned() {
let stripped = if prefix.is_empty() {
fields
} else {
fields[prefix.len()..].to_vec()
};
arg_fields.push((i + 1, stripped, fv));
}
}
}
}
self.current_frame.current_def_id = callee;
for (i, arg) in arg_locals.iter().enumerate() {
if let Some(v) = self.frame_local_value(&frame, Local::from_usize(*arg)).cloned() {
self.set_local(Local::from_usize(i + 1), v);
}
}
for (param, val) in arg_consts {
let local = Local::from_usize(*param);
let ty = self.body().local_decls[local].ty;
let (term, source) = match val {
crate::analysis::path::graph::InlineConstArg::Literal(value) => (
Int::from_u64(self.z3_ctx, *value),
ValueSource::CallsiteConst,
),
crate::analysis::path::graph::InlineConstArg::Symbol(text) => {
(self.symbolic_const_term_text(text), ValueSource::None)
}
};
let mut value = VmValue::new(term, ty);
value.source = source;
self.set_local(local, value);
}
for ¶m in arg_field_offsets {
let local = Local::from_usize(param);
let ty = self.body().local_decls[local].ty;
self.set_local(
local,
VmValue {
z3_term: self.fresh_int("field_offset"),
ty,
provenance: None,
facts: ValueFacts::default(),
source: ValueSource::FieldOffset,
},
);
}
for (callee_param, fields, fv) in arg_fields {
self.set_field_value(Local::from_usize(callee_param), fields, fv);
}
self.caller_frames.push(frame);
}
fn handle_callee_exit(&mut self, dest: usize) {
let method_call = self.active_method_calls.pop().flatten();
let ret = self.local_value(Local::from_usize(0)).cloned();
let ret_fields: Vec<(Vec<usize>, VmValue<'z3, 'tcx>)> = self
.field_paths(Local::from_usize(0))
.into_iter()
.filter_map(|f| {
self.field_value(Local::from_usize(0), &f)
.cloned()
.map(|v| (f, v))
})
.collect();
if let Some(frame) = self.caller_frames.pop() {
self.restore_frame(frame);
}
let writes = std::mem::take(&mut self.inline.deferred_field_writes);
self.inline.arg_referents = self.inline.path_saved_arg_referents.pop().unwrap_or_default();
self.inline.deferred_field_writes = self
.inline
.path_saved_deferred_writes
.pop()
.unwrap_or_default();
self.replay_deferred_writes(writes);
if let Some(mut v) = ret.clone() {
let dest_ty = self.body().local_decls[Local::from_usize(dest)].ty;
v.ty = dest_ty;
let at_base = v
.provenance
.as_ref()
.is_some_and(|p| p.offset.as_u64() == Some(0));
if at_base {
v.facts.non_null = true;
self.mark_initialized(&mut v);
}
self.set_local(Local::from_usize(dest), v);
if let Some(dest_alloc_id) = self.current_frame.local_alloc.get(&Local::from_usize(dest)).copied()
{
self.content_mut(dest_alloc_id).facts.initialized = true;
}
}
for (fields, fv) in ret_fields {
self.set_field_value(Local::from_usize(dest), fields, fv);
}
self.finish_method_call(method_call, ret.as_ref());
}
}