use rustc_middle::ty::TyCtxt;
use crate::analysis::path_analysis::PathTree;
use super::{
contract::Property,
forward_visit::{ForwardVisitResult, ForwardVisitor},
helpers::{Callsite, CallsiteLocation},
path::Path,
path_refine::{BackwardItem, BackwardVisitor, RelevantMirItems},
smt_check::{SmtCheckResult, SmtChecker},
};
pub struct VerifyEngine<'tcx> {
backward: BackwardVisitor<'tcx>,
forward: ForwardVisitor<'tcx>,
smt: SmtChecker<'tcx>,
}
impl<'tcx> VerifyEngine<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
Self {
backward: BackwardVisitor::new(tcx),
forward: ForwardVisitor::new(tcx),
smt: SmtChecker::new(tcx),
}
}
pub fn check_invariant(
&self,
def_id: rustc_hir::def_id::DefId,
checkpoint: CallsiteLocation,
path: &Path,
property: &Property<'tcx>,
invariants: &[Property<'tcx>],
is_constructor: bool,
caller_contracts: &[Property<'tcx>],
) -> (
RelevantMirItems<'tcx>,
ForwardVisitResult<'tcx>,
SmtCheckResult,
) {
let mut backward = self
.backward
.visit_for_checkpoint(def_id, checkpoint, path, property);
if is_constructor {
let mut items: Vec<BackwardItem<'tcx>> = caller_contracts
.iter()
.filter(|c| !matches!(c.kind, super::contract::PropertyKind::Unknown))
.map(|c| BackwardItem::ContractFact {
property: c.clone(),
})
.collect();
items.extend(backward.items.clone());
backward.items = items;
} else {
let mut items: Vec<BackwardItem<'tcx>> = invariants
.iter()
.map(|inv| BackwardItem::ContractFact {
property: inv.clone(),
})
.collect();
items.extend(backward.items.clone());
backward.items = items;
}
let forward = self.forward.visit(&backward);
let smt = self.smt.check_for_checkpoint(def_id, property, &forward);
(backward, forward, smt)
}
pub fn check_callsite_from_tree(
&self,
tree: &PathTree,
target_block: usize,
callsite: &Callsite<'tcx>,
property: &Property<'tcx>,
caller_contracts: &[Property<'tcx>],
) -> Vec<(ForwardVisitResult<'tcx>, SmtCheckResult)> {
let mut results = Vec::new();
let backward_items = self
.backward
.visit_path_tree(tree, target_block, callsite, property);
for mut backward in backward_items {
if !caller_contracts.is_empty() {
let mut items: Vec<BackwardItem<'tcx>> = caller_contracts
.iter()
.filter(|c| !matches!(c.kind, super::contract::PropertyKind::Unknown))
.map(|c| BackwardItem::ContractFact {
property: c.clone(),
})
.collect();
items.extend(backward.items.clone());
backward.items = items;
}
let forward = self.forward.visit(&backward);
let smt = self.smt.check(callsite, property, &forward);
results.push((forward, smt));
}
results
}
}