-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Derive the body invariant from the loop condition #448
Comments
I can confirm this has happened to me as well, in the following example: #[requires(n > 0)]
#[ensures(result.len() == n)]
#[ensures(forall(|i: usize| (i < result.len()) ==> result.lookup(i) == elem))]
pub fn from_elem(elem: u64/*T*/, n: usize) -> Self {
let mut i = 0usize;
let mut result = VecWrapper::with_capacity(n);
while i < n {
body_invariant!(result.len() == i);
body_invariant!(i < n);
body_invariant!(forall(|k: usize| (i > 0 && k < result.len()) ==> result.lookup(k) == elem));
result.push(elem);
i += 1;
}
result
} Specifically, |
This would be nice in the long term. I don't think we can infer the invariant given the loop condition in general, because the loop condition itself can have side effects or complex control flow (e.g. if there is a |
I think this can actually be done without too much difficulty; rather than "inferring" the condition in the sense that we take the boolean expression while {
guard_invariant!(I_G); // Introduce this macro
g // possibly non-pure
} {
body_invariant!(I_B);
B
}
assert!( !g ); as var b0 = ⟦g⟧
if b0 {
assert ⟦I_B⟧
exhale loop_perms
havoc loop_stack
inhale loop_perms
assume ⟦I_B⟧
⟦B⟧
var bn = ⟦g⟧
if bn {
assert ⟦I_B⟧
inhale false
}
}
var t = ⟦!g⟧
assert t Clearly the But what if we added: ...
inhale loop_perms
assume ⟦I_G⟧
var bi = ⟦g⟧
assume bi
assume ⟦I_B⟧
... As in, To go back to the original concern with |
A minimal example is the following. The goal of this issue is to verify it out-of-the-box. fn main() {
let mut i = 0;
while i < 10 {
assert!(i < 99);
i += 1;
}
} Interestingly, the following is already supported and could be used as default when no loop body invariant is specified. fn main() {
let mut i = 0;
while { body_invariant!(true); i < 10 } {
assert!(i < 99);
i += 1;
}
} The semantics should be equivalent to the following (which, however, fails due to bug #389): fn main() {
let mut i = 5;
assert!(true); // check the invariant before evaluating the first loop guard...
while i < 10 {
assert!(i < 99);
i += 1;
body_invariant!(true); // ... and check the same invariant at the end of every loop iteration.
}
} |
Currently, the following fails to check:
Though adding
body_invariant!(i < slice_len(slice))
into the loop body fixes the issue, Prusti could derive the invariant from the loop condition.The text was updated successfully, but these errors were encountered: