1use std::collections::{HashMap, HashSet};
7use std::hash::{Hash, Hasher};
8
9use serde::{Deserialize, Serialize};
10
11use crate::types::*;
12use crate::verify::{
13 Obligation, VerifyError, analyze_obligations, collect_module_call_names, verify_edge_guard,
14 verify_function, verify_invariant,
15};
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct VerifyCache {
20 pub module_name: String,
22 pub context_hash: u64,
25 pub structs: HashMap<String, u64>,
28 pub functions: HashMap<String, CachedItem>,
30 pub invariants: HashMap<String, CachedItem>,
32 pub edge_guards: CachedItem,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct CachedItem {
39 pub content_hash: u64,
40 pub errors: Vec<VerifyError>,
41}
42
43#[derive(Debug, Clone, Serialize)]
45pub struct IncrementalResult {
46 pub errors: Vec<VerifyError>,
48 pub obligations: Vec<Obligation>,
50 pub cache: VerifyCache,
52 pub stats: IncrementalStats,
54}
55
56#[derive(Debug, Clone, Serialize)]
58pub struct IncrementalStats {
59 pub total_items: usize,
60 pub reverified: usize,
61 pub cached: usize,
62 pub full_reverify: bool,
64}
65
66pub fn incremental_verify(module: &Module, cache: Option<&VerifyCache>) -> IncrementalResult {
74 let current_ctx = context_hash(module);
75 let total_items = module.functions.len()
76 + module.invariants.len()
77 + if module.edge_guards.is_empty() { 0 } else { 1 };
78
79 let known_types: HashSet<&str> = module
81 .structs
82 .iter()
83 .map(|s| s.name.as_str())
84 .chain(module.functions.iter().map(|f| f.name.as_str()))
85 .collect();
86 let mut call_names = HashSet::new();
87 collect_module_call_names(module, &mut call_names);
88
89 let cache_valid = cache
91 .map(|c| c.module_name == module.name && c.context_hash == current_ctx)
92 .unwrap_or(false);
93
94 let mut errors = Vec::new();
95 let mut reverified = 0;
96 let mut cached_count = 0;
97
98 let mut new_struct_hashes = HashMap::new();
100 let mut new_func_items = HashMap::new();
101 let mut new_inv_items = HashMap::new();
102
103 let mut struct_changed = false;
105 for s in &module.structs {
106 let h = content_hash_struct(s);
107 if cache_valid {
108 if let Some(old_h) = cache.and_then(|c| c.structs.get(&s.name)) {
109 if *old_h != h {
110 struct_changed = true;
111 }
112 } else {
113 struct_changed = true;
114 }
115 }
116 new_struct_hashes.insert(s.name.clone(), h);
117 }
118 if cache_valid && let Some(c) = cache {
120 for name in c.structs.keys() {
121 if !new_struct_hashes.contains_key(name) {
122 struct_changed = true;
123 }
124 }
125 }
126
127 for func in &module.functions {
129 let h = content_hash_function(func);
130 let use_cached = cache_valid
131 && !struct_changed
132 && cache
133 .and_then(|c| c.functions.get(&func.name))
134 .map(|ci| ci.content_hash == h)
135 .unwrap_or(false);
136
137 if use_cached {
138 let cached_errors = &cache.unwrap().functions[&func.name].errors;
139 errors.extend(cached_errors.iter().cloned());
140 cached_count += 1;
141 new_func_items.insert(
142 func.name.clone(),
143 CachedItem {
144 content_hash: h,
145 errors: cached_errors.clone(),
146 },
147 );
148 } else {
149 let mut item_errors = Vec::new();
150 verify_function(func, &known_types, &call_names, &mut item_errors);
151 errors.extend(item_errors.iter().cloned());
152 reverified += 1;
153 new_func_items.insert(
154 func.name.clone(),
155 CachedItem {
156 content_hash: h,
157 errors: item_errors,
158 },
159 );
160 }
161 }
162
163 for inv in &module.invariants {
165 let h = content_hash_invariant(inv);
166 let use_cached = cache_valid
167 && !struct_changed
168 && cache
169 .and_then(|c| c.invariants.get(&inv.name))
170 .map(|ci| ci.content_hash == h)
171 .unwrap_or(false);
172
173 if use_cached {
174 let cached_errors = &cache.unwrap().invariants[&inv.name].errors;
175 errors.extend(cached_errors.iter().cloned());
176 cached_count += 1;
177 new_inv_items.insert(
178 inv.name.clone(),
179 CachedItem {
180 content_hash: h,
181 errors: cached_errors.clone(),
182 },
183 );
184 } else {
185 let mut item_errors = Vec::new();
186 verify_invariant(inv, &known_types, &call_names, &mut item_errors);
187 errors.extend(item_errors.iter().cloned());
188 reverified += 1;
189 new_inv_items.insert(
190 inv.name.clone(),
191 CachedItem {
192 content_hash: h,
193 errors: item_errors,
194 },
195 );
196 }
197 }
198
199 let eg_hash = content_hash_edge_guards(&module.edge_guards);
201 let eg_use_cached = cache_valid
202 && !struct_changed
203 && cache
204 .map(|c| c.edge_guards.content_hash == eg_hash)
205 .unwrap_or(false);
206 let new_eg = if !module.edge_guards.is_empty() {
207 if eg_use_cached {
208 let cached_errors = &cache.unwrap().edge_guards.errors;
209 errors.extend(cached_errors.iter().cloned());
210 cached_count += 1;
211 CachedItem {
212 content_hash: eg_hash,
213 errors: cached_errors.clone(),
214 }
215 } else {
216 let mut item_errors = Vec::new();
217 for guard in &module.edge_guards {
218 verify_edge_guard(guard, &known_types, &mut item_errors);
219 }
220 errors.extend(item_errors.iter().cloned());
221 if total_items > 0 {
222 reverified += 1;
223 }
224 CachedItem {
225 content_hash: eg_hash,
226 errors: item_errors,
227 }
228 }
229 } else {
230 CachedItem {
231 content_hash: 0,
232 errors: vec![],
233 }
234 };
235
236 let obligations = analyze_obligations(module);
238
239 let new_cache = VerifyCache {
240 module_name: module.name.clone(),
241 context_hash: current_ctx,
242 structs: new_struct_hashes,
243 functions: new_func_items,
244 invariants: new_inv_items,
245 edge_guards: new_eg,
246 };
247
248 IncrementalResult {
249 errors,
250 obligations,
251 cache: new_cache,
252 stats: IncrementalStats {
253 total_items,
254 reverified,
255 cached: cached_count,
256 full_reverify: !cache_valid || struct_changed,
257 },
258 }
259}
260
261fn context_hash(module: &Module) -> u64 {
266 let mut names: Vec<&str> = module
267 .structs
268 .iter()
269 .map(|s| s.name.as_str())
270 .chain(module.functions.iter().map(|f| f.name.as_str()))
271 .chain(module.invariants.iter().map(|i| i.name.as_str()))
272 .collect();
273 names.sort();
274 hash_parts(&names)
275}
276
277fn content_hash_struct(s: &Struct) -> u64 {
279 let fields_json: Vec<String> = s
280 .fields
281 .iter()
282 .map(|f| format!("{}:{}", f.name, serde_json::to_string(&f.ty).unwrap()))
283 .collect();
284 let mut parts = vec![s.name.clone()];
285 parts.extend(fields_json);
286 let refs: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
287 hash_parts(&refs)
288}
289
290fn content_hash_function(func: &Function) -> u64 {
293 let mut parts = vec![func.name.clone()];
294 for p in &func.params {
295 parts.push(format!(
296 "param:{}:{}",
297 p.name,
298 serde_json::to_string(&p.ty).unwrap()
299 ));
300 }
301 for pre in &func.preconditions {
302 parts.push(format!("pre:{}", expr_signature(&pre.expr)));
303 }
304 for post in &func.postconditions {
305 match post {
306 Postcondition::Always { expr, .. } => {
307 parts.push(format!("post:{}", expr_signature(expr)));
308 }
309 Postcondition::When { guard, expr, .. } => {
310 parts.push(format!(
311 "post-when:{}:{}",
312 expr_signature(guard),
313 expr_signature(expr)
314 ));
315 }
316 }
317 }
318 for prop in &func.properties {
319 parts.push(format!(
320 "prop:{}:{}",
321 prop.key,
322 serde_json::to_string(&prop.value).unwrap()
323 ));
324 }
325 let refs: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
326 hash_parts(&refs)
327}
328
329fn content_hash_invariant(inv: &Invariant) -> u64 {
331 let parts = [inv.name.as_str(), &expr_signature(&inv.expr)];
332 hash_parts(&parts)
333}
334
335fn content_hash_edge_guards(guards: &[EdgeGuard]) -> u64 {
337 let mut parts = Vec::new();
338 for g in guards {
339 parts.push(format!(
340 "guard:{}:{}",
341 expr_signature(&g.condition),
342 g.action
343 ));
344 for (k, v) in &g.args {
345 parts.push(format!("arg:{}:{}", k, expr_signature(v)));
346 }
347 }
348 let refs: Vec<&str> = parts.iter().map(|s| s.as_str()).collect();
349 hash_parts(&refs)
350}
351
352fn expr_signature(expr: &IrExpr) -> String {
354 serde_json::to_string(expr).unwrap()
355}
356
357fn hash_parts(parts: &[&str]) -> u64 {
359 let mut hasher = std::collections::hash_map::DefaultHasher::new();
360 for part in parts {
361 part.hash(&mut hasher);
362 }
363 hasher.finish()
364}