logicaffeine_verify/
parameterized.rs1use crate::ir::{VerifyExpr, VerifyType};
7use crate::equivalence::Trace;
8use std::collections::HashMap;
9
10#[derive(Debug)]
11pub enum ParameterizedResult {
12 UniversallyValid,
13 ValidUpTo(u64),
14 Counterexample { param_value: u64, trace: Trace },
15 Unknown,
16}
17
18pub fn verify_parameterized(
23 property: &VerifyExpr,
24 parameter: &str,
25 param_type: VerifyType,
26 constraint: Option<&VerifyExpr>,
27) -> ParameterizedResult {
28
29 let body = if let Some(c) = constraint {
31 VerifyExpr::implies(c.clone(), property.clone())
32 } else {
33 property.clone()
34 };
35
36 let quantified = VerifyExpr::forall(
37 vec![(parameter.to_string(), param_type.clone())],
38 body,
39 );
40
41 let negated = VerifyExpr::not(quantified);
43 let solver = crate::solver::new_solver();
44 let encoded = encode_bool(&negated);
45 solver.assert(&encoded);
46
47 match solver.check() {
48 z3::SatResult::Unsat => ParameterizedResult::UniversallyValid,
49 z3::SatResult::Sat => {
50 for val in 0..=100u64 {
52 let specialized = substitute_param(property, parameter, val as i64);
53 let neg_spec = VerifyExpr::not(specialized);
54 let solver2 = crate::solver::new_solver();
55 solver2.assert(&encode_bool(&neg_spec));
56 if matches!(solver2.check(), z3::SatResult::Sat) {
57 return ParameterizedResult::Counterexample {
58 param_value: val,
59 trace: Trace { cycles: vec![] },
60 };
61 }
62 }
63 ParameterizedResult::ValidUpTo(100)
64 }
65 z3::SatResult::Unknown => {
66 for val in 0..=20u64 {
68 let specialized = substitute_param(property, parameter, val as i64);
69 let neg_spec = VerifyExpr::not(specialized);
70 let solver2 = crate::solver::new_solver();
71 solver2.assert(&encode_bool(&neg_spec));
72 if matches!(solver2.check(), z3::SatResult::Sat) {
73 return ParameterizedResult::Counterexample {
74 param_value: val,
75 trace: Trace { cycles: vec![] },
76 };
77 }
78 }
79 ParameterizedResult::ValidUpTo(20)
80 }
81 }
82}
83
84fn substitute_param(expr: &VerifyExpr, param: &str, value: i64) -> VerifyExpr {
85 match expr {
86 VerifyExpr::Var(name) if name == param => VerifyExpr::Int(value),
87 VerifyExpr::Var(_) => expr.clone(),
88 VerifyExpr::Binary { op, left, right } => VerifyExpr::binary(
89 *op,
90 substitute_param(left, param, value),
91 substitute_param(right, param, value),
92 ),
93 VerifyExpr::Not(inner) => VerifyExpr::not(substitute_param(inner, param, value)),
94 VerifyExpr::Bool(_) | VerifyExpr::Int(_) => expr.clone(),
95 VerifyExpr::Iff(l, r) => VerifyExpr::iff(
96 substitute_param(l, param, value),
97 substitute_param(r, param, value),
98 ),
99 VerifyExpr::ForAll { vars, body } => VerifyExpr::forall(
100 vars.clone(),
101 substitute_param(body, param, value),
102 ),
103 VerifyExpr::Exists { vars, body } => VerifyExpr::exists(
104 vars.clone(),
105 substitute_param(body, param, value),
106 ),
107 _ => expr.clone(),
108 }
109}
110
111fn encode_bool(expr: &VerifyExpr) -> z3::ast::Bool {
112 let mut bool_vars = HashMap::new();
113 let mut int_vars = HashMap::new();
114 let mut all_vars = std::collections::HashSet::new();
115 crate::equivalence::collect_vars_pub(expr, &mut all_vars);
116 for name in &all_vars {
117 bool_vars.insert(name.clone(), z3::ast::Bool::new_const(name.as_str()));
118 }
119 crate::equivalence::collect_int_vars_pub(expr, &mut int_vars);
120 let mut bv_vars = HashMap::new();
121 let mut array_vars = HashMap::new();
122 crate::equivalence::collect_quantifier_bound_vars_pub(expr, &mut int_vars, &mut bv_vars, &mut array_vars);
123 crate::kinduction::encode_expr_bool(expr, &bool_vars, &int_vars)
124}