use rustc_hir::def_id::DefId;
use rustc_middle::ty::TyCtxt;
use crate::analysis::path_analysis::PathTree;
use super::{
contract::Property,
helpers::{Checkpoint, CheckpointLocation},
report::CheckResult,
slicer::{BackwardItem, BackwardSlicer},
smt_check::{SmtCheckResult, SmtChecker},
verifier::{ForwardVerifier, ForwardVisitResult},
};
pub struct InvariantCheckResult {
pub result: CheckResult,
pub slicing_diag: String,
pub verification_diag: String,
}
pub struct VerifyEngine<'tcx> {
slicer: BackwardSlicer<'tcx>,
verifier: ForwardVerifier<'tcx>,
checker: SmtChecker<'tcx>,
}
impl<'tcx> VerifyEngine<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
Self {
slicer: BackwardSlicer::new(tcx),
verifier: ForwardVerifier::new(tcx),
checker: SmtChecker::new(tcx),
}
}
pub fn check_invariant_from_tree(
&self,
def_id: DefId,
tree: &PathTree,
checkpoint: CheckpointLocation,
invariant: &Property<'tcx>,
entry_facts: &[BackwardItem<'tcx>],
) -> Vec<InvariantCheckResult> {
let target_block = checkpoint.block.as_usize();
let mut results = Vec::new();
let backward_items = self.slicer.visit_path_tree_for_checkpoint(
tree,
target_block,
def_id,
checkpoint,
invariant,
);
for mut backward in backward_items {
if !entry_facts.is_empty() {
let mut items: Vec<BackwardItem<'tcx>> = entry_facts.to_vec();
items.extend(backward.items.clone());
backward.items = items;
}
let forward = self.verifier.visit(&backward);
let smt = self
.checker
.check_for_checkpoint(def_id, invariant, &forward);
let tcx = self.slicer.tcx();
let slicing_diag = backward.describe_for_checkpoint(tcx, checkpoint, 0);
let verification_diag = format!("{}\n{}", forward.describe(), smt.describe());
results.push(InvariantCheckResult {
result: smt.result,
slicing_diag,
verification_diag,
});
}
results
}
pub fn check_callsite_from_tree(
&self,
tree: &PathTree,
checkpoint: &Checkpoint<'tcx>,
property: &Property<'tcx>,
caller_contracts: &[Property<'tcx>],
) -> Vec<(ForwardVisitResult<'tcx>, SmtCheckResult)> {
let target_block = checkpoint.block.as_usize();
let mut results = Vec::new();
let backward_items = self
.slicer
.visit_path_tree(tree, target_block, checkpoint, 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.verifier.visit(&backward);
let smt = self.checker.check(checkpoint, property, &forward);
results.push((forward, smt));
}
results
}
}