Skip to content

Commit 96bdac3

Browse files
committed
formal(P-037): harnesses draw small-width symbolic inputs (u8 indices, guards < 3) so CBMC stops bit-blasting 64-bit fields
The first full Kani run spent minutes on the election solver harness with symbolic usize callee indices and schedules; the harnesses now build systems from u8 symbols through properties::symbolic (any_system, any_election_system, any_schedule, any_index) and assume well-formedness. Kernel types untouched; cargo test 35 passed; clippy clean. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfeUtLRm6KFRWcTGQZxQQb
1 parent 0355bdd commit 96bdac3

5 files changed

Lines changed: 132 additions & 26 deletions

File tree

‎formal/p037-kernel/src/properties/election.rs‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,8 @@ mod tests {
151151

152152
#[cfg(kani)]
153153
mod proofs {
154-
use crate::{elect, elect_with, import, Election, ElectionSystem, GuardBinding, MAX_COORDS};
154+
use super::super::symbolic::{any_election_system, any_schedule};
155+
use crate::{elect, elect_with, import, Election, GuardBinding};
155156

156157
#[kani::proof]
157158
fn k4_import_is_monotone_and_conflict_propagates() {
@@ -168,11 +169,8 @@ mod proofs {
168169
#[kani::proof]
169170
#[kani::unwind(9)]
170171
fn k10_election_lfp_is_schedule_independent() {
171-
let sys: ElectionSystem = kani::any();
172-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
173-
let sched: [usize; MAX_COORDS] = kani::any();
174-
kani::assume(sched.iter().all(|&i| i < sys.n));
175-
kani::assume((0..sys.n).all(|i| sched.contains(&i)));
172+
let sys = any_election_system();
173+
let sched = any_schedule(sys.n);
176174
let jacobi = elect(&sys);
177175
assert!(jacobi.is_some(), "terminates within the height bound");
178176
assert_eq!(elect_with(&sys, &sched), jacobi);

‎formal/p037-kernel/src/properties/mod.rs‎

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,121 @@ pub fn random_election_system(rng: &mut Rng, n: usize) -> ElectionSystem {
277277
ElectionSystem { n, coords }
278278
}
279279

280+
/// Small-width symbolic inputs for the Kani harnesses.
281+
///
282+
/// CBMC bit-blasts every field, so a symbolic `usize` index costs 64 bits
283+
/// where two would do. The harnesses draw `u8` symbols, convert, and assume
284+
/// well-formedness — the kernel types are untouched.
285+
#[cfg(kani)]
286+
pub mod symbolic {
287+
use super::DEAD;
288+
use crate::{
289+
Coord, Edge, Election, ElectionCoord, ElectionEdge, ElectionSystem, Guard, GuardBinding,
290+
Shape, System, MAX_COORDS, MAX_EDGES,
291+
};
292+
293+
/// A symbolic index below `n`.
294+
pub fn any_index(n: usize) -> usize {
295+
let v: u8 = kani::any();
296+
kani::assume(usize::from(v) < n);
297+
usize::from(v)
298+
}
299+
300+
/// A guard index below 3.
301+
fn any_guard() -> Guard {
302+
let g: Guard = kani::any();
303+
kani::assume(g < 3);
304+
g
305+
}
306+
307+
fn any_edge(n: usize) -> Option<Edge> {
308+
let present: bool = kani::any();
309+
present.then(|| Edge {
310+
callee: any_index(n),
311+
transform: kani::any(),
312+
mask: kani::any(),
313+
})
314+
}
315+
316+
/// A well-formed system of 1..=3 coordinates with up to 2 edges each.
317+
pub fn any_system() -> System {
318+
let n = any_index(MAX_COORDS).saturating_add(1);
319+
let mut coords = [DEAD; MAX_COORDS];
320+
for c in coords.iter_mut().take(n) {
321+
let split: bool = kani::any();
322+
*c = Coord {
323+
shape: if split {
324+
Shape::Split(0)
325+
} else {
326+
Shape::Uncond
327+
},
328+
seed: kani::any(),
329+
edges: [any_edge(n), any_edge(n)],
330+
};
331+
}
332+
let sys = System { n, coords };
333+
kani::assume(sys.well_formed());
334+
sys
335+
}
336+
337+
/// A fair per-pass schedule over `0..n`.
338+
pub fn any_schedule(n: usize) -> [usize; MAX_COORDS] {
339+
let s = [any_index(n), any_index(n), any_index(n)];
340+
kani::assume((0..n).all(|i| s.contains(&i)));
341+
s
342+
}
343+
344+
fn any_election() -> Election {
345+
match any_index(3) {
346+
0 => Election::None,
347+
1 => Election::One(any_guard()),
348+
_ => Election::Conflict,
349+
}
350+
}
351+
352+
fn any_binding() -> GuardBinding {
353+
match any_index(4) {
354+
0 => GuardBinding::Const,
355+
1 => GuardBinding::Opaque,
356+
2 => GuardBinding::Id {
357+
callee: any_guard(),
358+
caller: any_guard(),
359+
},
360+
_ => GuardBinding::Neg {
361+
callee: any_guard(),
362+
caller: any_guard(),
363+
},
364+
}
365+
}
366+
367+
fn any_election_edge(n: usize) -> Option<ElectionEdge> {
368+
let present: bool = kani::any();
369+
present.then(|| ElectionEdge {
370+
callee: any_index(n),
371+
binding: any_binding(),
372+
})
373+
}
374+
375+
/// A well-formed election system of 1..=3 coordinates.
376+
pub fn any_election_system() -> ElectionSystem {
377+
let n = any_index(MAX_COORDS).saturating_add(1);
378+
let dead = ElectionCoord {
379+
seed: Election::None,
380+
edges: [None; MAX_EDGES],
381+
};
382+
let mut coords = [dead; MAX_COORDS];
383+
for c in coords.iter_mut().take(n) {
384+
*c = ElectionCoord {
385+
seed: any_election(),
386+
edges: [any_election_edge(n), any_election_edge(n)],
387+
};
388+
}
389+
let sys = ElectionSystem { n, coords };
390+
kani::assume(sys.well_formed());
391+
sys
392+
}
393+
}
394+
280395
/// All permutations of `0..n` for `n ≤ 3`.
281396
#[must_use]
282397
pub fn permutations(n: usize) -> Vec<Vec<usize>> {

‎formal/p037-kernel/src/properties/refinement.rs‎

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,9 @@ mod tests {
202202

203203
#[cfg(kani)]
204204
mod proofs {
205+
use super::super::symbolic::{any_index, any_system};
205206
use super::{no_unknown_seed, release_cells_have_no_edges};
206-
use crate::{
207-
lemma_guarded, lemma_today, solve, Cells, Grounding, System, Transfer, MAX_COORDS,
208-
};
207+
use crate::{lemma_guarded, lemma_today, solve, Cells, Grounding, Transfer, MAX_COORDS};
209208

210209
#[kani::proof]
211210
fn k9_residual_bottom_lemma_all_three_groundings() {
@@ -218,8 +217,7 @@ mod proofs {
218217

219218
#[kani::proof]
220219
fn k11_one_step_lax_simulation() {
221-
let sys: System = kani::any();
222-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
220+
let sys = any_system();
223221
let x: [Cells; MAX_COORDS] = kani::any();
224222
let [x0, x1, x2] = x;
225223
let cx = [
@@ -228,16 +226,14 @@ mod proofs {
228226
Cells::diag(x2.collapse()),
229227
];
230228
let c = sys.collapsed();
231-
let i: usize = kani::any();
232-
kani::assume(i < sys.n);
229+
let i = any_index(sys.n);
233230
assert!(sys.step(i, &x).collapse().leq(c.step(i, &cx).collapse()));
234231
}
235232

236233
#[kani::proof]
237234
#[kani::unwind(21)]
238235
fn k11_lfp_lax_simulation_against_the_collapsed_system() {
239-
let sys: System = kani::any();
240-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
236+
let sys = any_system();
241237
let g = solve(&sys);
242238
let t = solve(&sys.collapsed());
243239
assert!(g.is_some() && t.is_some());
@@ -251,8 +247,7 @@ mod proofs {
251247
#[kani::proof]
252248
#[kani::unwind(21)]
253249
fn k11_lfp_lax_simulation_against_today_post_finalization() {
254-
let sys: System = kani::any();
255-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
250+
let sys = any_system();
256251
kani::assume(release_cells_have_no_edges(&sys) && no_unknown_seed(&sys));
257252
let g = solve(&sys);
258253
let t = solve(&sys.today());

‎formal/p037-kernel/src/properties/solver.rs‎

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,14 @@ mod tests {
5252

5353
#[cfg(kani)]
5454
mod proofs {
55-
use crate::{solve, solve_with, System, MAX_COORDS};
55+
use super::super::symbolic::{any_schedule, any_system};
56+
use crate::{solve, solve_with};
5657

5758
#[kani::proof]
5859
#[kani::unwind(21)]
5960
fn k10_lfp_is_a_schedule_independent_fixpoint() {
60-
let sys: System = kani::any();
61-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
62-
let sched: [usize; MAX_COORDS] = kani::any();
63-
kani::assume(sched.iter().all(|&i| i < sys.n));
64-
kani::assume((0..sys.n).all(|i| sched.contains(&i)));
61+
let sys = any_system();
62+
let sched = any_schedule(sys.n);
6563
let lfp = solve(&sys);
6664
assert!(lfp.is_some(), "terminates within the height bound");
6765
if let Some(x) = lfp {

‎formal/p037-kernel/src/properties/transforms.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ mod tests {
8585

8686
#[cfg(kani)]
8787
mod proofs {
88-
use crate::{contribute, read, solve, Cells, Mask, Shape, System, Transform, MAX_COORDS};
88+
use super::super::symbolic::any_system;
89+
use crate::{contribute, read, solve, Cells, Mask, Shape, Transform};
8990

9091
#[kani::proof]
9192
fn k3_reads_and_contributions_are_monotone() {
@@ -115,8 +116,7 @@ mod proofs {
115116
#[kani::proof]
116117
#[kani::unwind(21)]
117118
fn k12_uncond_coordinates_stay_diagonal() {
118-
let sys: System = kani::any();
119-
kani::assume(sys.n >= 1 && sys.n <= MAX_COORDS && sys.well_formed());
119+
let sys = any_system();
120120
let x = solve(&sys);
121121
assert!(x.is_some(), "terminates within the height bound");
122122
if let Some(x) = x {

0 commit comments

Comments
 (0)