logicaffeine_compile/codegen_sva/
sva_to_proof.rs1use super::sva_to_verify::BoundedExpr;
15use logicaffeine_proof::ProofExpr;
16
17#[inline]
18fn boxed(e: ProofExpr) -> Box<ProofExpr> {
19 Box::new(e)
20}
21
22fn truth() -> ProofExpr {
27 let c = ProofExpr::Atom("__bool_const".to_string());
28 ProofExpr::Or(boxed(c.clone()), boxed(ProofExpr::Not(boxed(c))))
29}
30fn falsity() -> ProofExpr {
31 let c = ProofExpr::Atom("__bool_const".to_string());
32 ProofExpr::And(boxed(c.clone()), boxed(ProofExpr::Not(boxed(c))))
33}
34
35pub fn bounded_to_proof(e: &BoundedExpr) -> Option<ProofExpr> {
39 match e {
40 BoundedExpr::Var(name) => Some(ProofExpr::Atom(name.clone())),
41 BoundedExpr::Bool(true) => Some(truth()),
42 BoundedExpr::Bool(false) => Some(falsity()),
43 BoundedExpr::Not(p) => Some(ProofExpr::Not(boxed(bounded_to_proof(p)?))),
44 BoundedExpr::And(p, q) => {
45 Some(ProofExpr::And(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
46 }
47 BoundedExpr::Or(p, q) => {
48 Some(ProofExpr::Or(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
49 }
50 BoundedExpr::Implies(p, q) => {
51 Some(ProofExpr::Implies(boxed(bounded_to_proof(p)?), boxed(bounded_to_proof(q)?)))
52 }
53 BoundedExpr::Eq(p, q) => match (bounded_to_proof(p), bounded_to_proof(q)) {
56 (Some(a), Some(b)) => Some(ProofExpr::Iff(boxed(a), boxed(b))),
57 _ => super::bitblast::lower_bool(e),
58 },
59 _ => super::bitblast::lower_bool(e),
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use logicaffeine_proof::sat::{prove_equivalence, EquivOutcome};
69
70 fn v(s: &str) -> BoundedExpr {
71 BoundedExpr::Var(s.to_string())
72 }
73 fn b(e: BoundedExpr) -> Box<BoundedExpr> {
74 Box::new(e)
75 }
76
77 #[test]
78 fn overlapping_implication_proves_equivalent() {
79 let sva = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
81 let fol = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
82 let p = bounded_to_proof(&sva).unwrap();
83 let q = bounded_to_proof(&fol).unwrap();
84 assert_eq!(prove_equivalence(&p, &q), EquivOutcome::Equivalent);
85 }
86
87 #[test]
88 fn implication_vs_consequent_differ() {
89 let sva = BoundedExpr::Implies(b(v("req@0")), b(v("ack@0")));
90 let other = v("ack@0");
91 let p = bounded_to_proof(&sva).unwrap();
92 let q = bounded_to_proof(&other).unwrap();
93 match prove_equivalence(&p, &q) {
94 EquivOutcome::Differ(_) => {}
95 o => panic!("expected Differ, got {:?}", o),
96 }
97 }
98
99 #[test]
100 fn safety_demorgan_equivalent() {
101 let lhs = BoundedExpr::Not(b(BoundedExpr::And(b(v("a@0")), b(v("b@0")))));
103 let rhs = BoundedExpr::Or(
104 b(BoundedExpr::Not(b(v("a@0")))),
105 b(BoundedExpr::Not(b(v("b@0")))),
106 );
107 let p = bounded_to_proof(&lhs).unwrap();
108 let q = bounded_to_proof(&rhs).unwrap();
109 assert_eq!(prove_equivalence(&p, &q), EquivOutcome::Equivalent);
110 }
111
112 #[test]
113 fn boolean_constants_lower_and_differ() {
114 let t = bounded_to_proof(&BoundedExpr::Bool(true)).unwrap();
115 let f = bounded_to_proof(&BoundedExpr::Bool(false)).unwrap();
116 match prove_equivalence(&t, &f) {
117 EquivOutcome::Differ(_) => {}
118 o => panic!("True vs False must Differ, got {:?}", o),
119 }
120 }
121
122 #[test]
123 fn datapath_returns_none() {
124 assert!(bounded_to_proof(&BoundedExpr::Int(5)).is_none());
126 assert!(bounded_to_proof(&BoundedExpr::BitVecVar("data@0".to_string(), 8)).is_none());
127 let cmp = BoundedExpr::Lt(b(BoundedExpr::Int(1)), b(BoundedExpr::Int(2)));
129 assert!(bounded_to_proof(&cmp).is_none());
130 }
131}