You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add support for loop-contract historic values (#3951)
Add support for `old` and `prev` to refers to historic values in loop
contracts where
- `old(expr)` refers to the value of the expr before the loop.
- `prev(expr)` refers to the value of the expr at the previous
iteration.
Resolves#3697
By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.
Copy file name to clipboardExpand all lines: library/kani_macros/src/sysroot/loop_contracts/mod.rs
+295-5Lines changed: 295 additions & 5 deletions
Original file line number
Diff line number
Diff line change
@@ -9,8 +9,72 @@ use proc_macro_error2::abort_call_site;
9
9
use quote::{format_ident, quote};
10
10
use syn::spanned::Spanned;
11
11
use syn::token::AndAnd;
12
-
use syn::{BinOp,Expr,ExprBinary,Stmt};
12
+
use syn::{BinOp,Block,Expr,ExprBinary,Ident,Stmt, visit_mut::VisitMut};
13
13
14
+
/*
15
+
Transform the loop to support on_entry(expr) : the value of expr before entering the loop
16
+
1. For each on_entry(expr) in the loop variant, replace it with a newly generated "memory" variable old_k
17
+
2. Add the declaration of i before the loop: let old_k = expr
18
+
For example:
19
+
#[kani::loop_invariant(on_entry(x+y) = x + y -1)]
20
+
while(....)
21
+
22
+
is transformed into
23
+
let old_1 = x + y
24
+
#[kani::loop_invariant(old_1 = x + y -1)]
25
+
while(....)
26
+
27
+
Then the loop_invartiant is transformed
28
+
29
+
*/
30
+
31
+
/*
32
+
Transform the loop to support prev(expr) : the value of expr at the end of the previous iteration
33
+
Semantic: If the loop has at least 1 iteration: prev(expr) is the value of expr at the end of the previous iteration. Otherwise, just remove the loop (without check for the invariant too).
34
+
35
+
Transformation: basically, if the loop has at least 1 iteration (loop_quard is satisfied at the beginning), we unfold the loop once, declare the variables for prev values and update them at the beginning of the loop body.
36
+
Otherwise, we remove the loop.
37
+
If there is a prev(expr) in the loop_invariant:
38
+
1. Firstly, add an if block whose condition is the loop_quard, inside its body add/do the followings:
39
+
2. For each prev(expr) in the loop variant, replace it with a newly generated "memory" variable prev_k
40
+
3. Add the declaration of prev_k before the loop: let mut prev_k = expr
41
+
4. Define a mut closure whose body is exactly the loop body, but replace all continue/break statements with return true/false statements,
42
+
then add a final return true statement at the end of it
43
+
5. Add an if statement with condition to be the that closure's call (the same as run the loop once):
44
+
True block: add the loop with expanded macros (see next section) and inside the loop body:
45
+
add the assignment statements (exactly the same as the declarations without the "let mut") on the top to update the "memory" variables
46
+
Else block: Add the assertion for the loop_invariant (not includes the loop_quard): check if the loop_invariant holds after the first iteration.
47
+
48
+
For example:
49
+
#[kani::loop_invariant(prev(x+y) = x + y -1 && ...)]
50
+
while(loop_guard)
51
+
{
52
+
loop_body
53
+
}
54
+
55
+
is transformed into
56
+
57
+
assert!(loop_guard);
58
+
let mut prev_1 = x + y;
59
+
let mut loop_body_closure = || {
60
+
loop_body_replaced //replace breaks/continues in loop_body with returns
61
+
};
62
+
if loop_body_closure(){
63
+
#[kani::loop_invariant(prev_1 = x + y -1)]
64
+
while(loop_guard)
65
+
{
66
+
prev_1 = x + y;
67
+
loop_body
68
+
}
69
+
}
70
+
else{
71
+
assert!(prev_1 = x + y -1 && ...);
72
+
}
73
+
74
+
75
+
*/
76
+
77
+
/// After that:
14
78
/// Expand loop contracts macros.
15
79
///
16
80
/// A while loop of the form
@@ -30,6 +94,152 @@ use syn::{BinOp, Expr, ExprBinary, Stmt};
30
94
/// body
31
95
/// }
32
96
/// ```
97
+
98
+
structTransformationResult{
99
+
transformed_expr:Expr,
100
+
declarations_block:Block,
101
+
assignments_block:Block,
102
+
}
103
+
104
+
structCallReplacer{
105
+
old_name:String,
106
+
replacements:Vec<(Expr, proc_macro2::Ident)>,
107
+
counter:usize,
108
+
var_prefix:String,
109
+
}
110
+
111
+
// This impl replaces any function call of a function name : old_name with a newly generated variable.
0 commit comments