fn bubble_sort(arr: Ptr<i32>, n: i64)
requires n > 0
ensures forall i in 0..(n-1) => arr[i] <= arr[i+1]
{
for i in 0..n {
invariant forall k in 0..(i-1) => arr[k] <= arr[k+1];
// bubble pass
}
}
Z3 proves the base case (vacuously true at i=0) and the inductive step for each iteration. Both loops have fixed trip counts regardless of data, so the frame axioms are concrete. At concrete sizes the compiler unrolls the loop: for i in 0..4 produces 8 Z3 checks (4 iterations × 2), all proven at compile time, outputting Z3: 8/8 checks proven (100%), 0 deferred to runtime. let mut i = 0;
while i < 5 {
arr[i] = val;
i += 1;
}
So in that case, the compiler detects `i < N` with monotonic increment and synthesizes `i >= 0 && i < N` automatically.
Salt has some different ergonomics and performance goals though, which makes this project unique.