2 (** {1 McCarthy's "91" function}
4 authors: Jean-Christophe Filliâtre, Martin Clochard, Claude Marché
5 witness: Andrei Paskevich
13 (** {2 Specification} *)
15 function spec (x: int) : int = if x <= 100 then 91 else x-10
17 (** {2 traditional recursive implementation} *)
19 let rec f91 (n: int) : int
20 ensures { result = spec n } variant { 101 - n }
26 (** {2 manually-optimized tail call} *)
28 let rec f91_tco (n0: int) : int
29 ensures { result = spec n0 } variant { 101 - n0 }
32 invariant { n = n0 > 100 \/ n0 <= n <= 101 } variant { 101 - n }
37 (** {2 non-recursive implementation using a while loop} *)
42 let f91_nonrec (n0: int): int
43 ensures { result = spec n0 }
47 invariant { e >= 0 /\ iter spec e n = spec n0 }
48 variant { 101 - n + 10 * e, e }
59 (** {2 irrelevance of control flow}
61 We use a 'morally' irrelevant control flow from a recursive function
62 to ease proof (the recursive structure does not contribute to the
63 program execution). This is a technique for proving derecursified
64 programs. See [verifythis_2016_tree_traversal] for a more
69 let f91_pseudorec (n0: int) : int
70 ensures { result = spec n0 }
75 ensures { (old e) > 0 }
76 ensures { if (old n) > 100 then n = (old n) - 10 /\ e = (old e) - 1
77 else n = (old n) + 11 /\ e = (old e) + 1 }
78 raises { Stop -> e = (old e) = 0 /\ n = (old n) }
79 = if not (e > 0) then raise Stop;
91 ensures { e = (old e) - 1 /\ n = spec (old n) }
92 raises { Stop -> false }
93 = let u = n in bloc (); if u <= 100 then (aux (); aux ()) in
94 try aux (); bloc (); absurd
100 module McCarthyWithGhostMonitor
106 function spec (x: int) : int = if x <= 100 then 91 else x-10
108 (** {2 Variant using a general 'ghost coroutine' approach}
110 Assume we want to prove the imperative code:
114 if r > 100 { r <- r - 10; e <- e - 1; if e = 0 break }
115 else { r <- r + 11; e <- e + 1 }
118 we annotate the various program points:
123 { 2 } if r > 100 then { 3 } r <- r - 10; { 4 } e <- e - 1; { 5 } if e=0 then break;
124 else { 6 } r <- r + 11; { 7 } e <- e + 1;
129 we define the small-step semantics of this code by the following [step] function
140 requires { 0 <= pc < 8 }
142 ensures { old pc = 0 -> pc = 1 /\ e = 1 /\ r = old r }
143 ensures { old pc = 1 -> pc = 2 /\ e = old e /\ r = n }
144 ensures { old pc = 2 /\ old r > 100 -> pc = 3 /\ e = old e /\ r = old r }
145 ensures { old pc = 2 /\ old r <= 100 -> pc = 6 /\ e = old e /\ r = old r }
146 ensures { old pc = 3 -> pc = 4 /\ e = old e /\ r = old r - 10 }
147 ensures { old pc = 4 -> pc = 5 /\ e = old e - 1 /\ r = old r }
148 ensures { old pc = 5 /\ old e = 0 -> pc = 8 /\ e = old e /\ r = old r }
149 ensures { old pc = 5 /\ old e <> 0 -> pc = 2 /\ e = old e /\ r = old r }
150 ensures { old pc = 6 -> pc = 7 /\ e = old e /\ r = old r + 11 }
151 ensures { old pc = 7 -> pc = 2 /\ e = old e + 1 /\ r = old r }
153 let rec monitor () : unit
154 requires { pc = 2 /\ e > 0 }
156 ensures { pc = 5 /\ r = spec(old r) /\ e = old e - 1 }
157 = step (); (* execution of 'if r > 100' *)
159 step (); (* assignment r <- r - 10 *)
160 step (); (* assignment e <- e - 1 *)
163 step (); (* assignment r <- r + 11 *)
164 step (); (* assignment e <- e + 1 *)
166 step (); (* 'if e=0' must be false *)
171 requires { pc = 0 /\ n >= 0 }
172 ensures { pc = 8 /\ r = spec n }
173 = step (); (* assignment e <- 1 *)
174 step (); (* assignment r <- n *)
175 monitor (); (* loop *)
176 step() (* loop exit *)
178 (** {2 a variant with not-so-small steps}
180 we annotate the important program points:
185 { 1 } if r > 100 { r <- r - 10; e <- e - 1; { 2 } if e = 0 break; }
186 else { r <- r + 11; e <- e + 1; }
193 we define the not-so-small-step semantics of this code by the following [next] function
198 requires { 0 <= pc < 3 }
200 ensures { old pc = 0 -> pc = 1 /\ e = 1 /\ r = n }
201 ensures { old pc = 1 /\ old r > 100 ->
202 pc = 2 /\ r = old r - 10 /\ e = old e - 1 }
203 ensures { old pc = 1 /\ old r <= 100 ->
204 pc = 1 /\ r = old r + 11 /\ e = old e + 1 }
205 ensures { old pc = 2 /\ old e = 0 -> pc = 3 /\ r = old r /\ e = old e }
206 ensures { old pc = 2 /\ old e <> 0 -> pc = 1 /\ r = old r /\ e = old e }
208 (* [aux2] performs as may loop iterations as needed so as to reach program point 2
209 from program point 1 *)
210 let rec monitor2 () : unit
211 requires { pc = 1 /\ e > 0 }
213 ensures { pc = 2 /\ r = spec(old r) /\ e = old e - 1 }
215 if pc <> 2 then begin monitor2 (); next (); monitor2 () end
219 requires { pc = 0 /\ n >= 0 }
220 ensures { pc = 3 /\ r = spec n }
221 = next (); (* assignments e <- 1; r <- n *)
222 monitor2 (); (* loop *)
227 module McCarthy91Mach
232 function spec (x: int) : int = if x <= 100 then 91 else x-10
234 let rec f91 (n: int63) : int63
236 ensures { result = spec n }
243 use mach.int.Refint63
246 let f91_nonrec (n0: int63) : int63
247 ensures { result = spec n0 }
251 invariant { e >= 0 /\ iter spec e n = spec n0 }
252 variant { 101 - n + 10 * e, e:int }
253 if n > 100 then begin
265 let f91_pseudorec (n0: int63) : int63
266 ensures { result = spec n0 }
271 ensures { (old e) > 0 }
272 ensures { if (old n) > 100 then n = (old n) - 10 /\ e = (old e) - 1
273 else n = (old n) + 11 /\ e = (old e) + 1 }
274 raises { Stop -> e = (old e) = 0 /\ n = (old n) }
275 = if not (gt e zero) then raise Stop;
276 if n > 100 then begin
284 let rec aux () : unit
287 ensures { e = (old e) - 1 /\ n = spec (old n) }
288 raises { Stop -> false }
289 = let u = n in bloc (); if u <= 100 then (aux (); aux ()) in
290 try aux (); bloc (); absurd