1(* SPDX-License-Identifier: BSD-3-Clause *)
2(*
3 * Copyright (C) 2002 Tobias Nipkow (TUM)
4 * Copyright (C) 2013--2014 Japheth Lim (NICTA)
5 * Copyright (C) 2013--2014 David Greenaway (NICTA)
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * * Neither the name of the University of Cambridge or the Technische
19 * Universitaet Muenchen nor the names of their contributors may be used
20 * to endorse or promote products derived from this software without
21 * specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
29 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
33 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *)
35
36theory CList imports
37  "AutoCorres.AutoCorres"
38begin
39
40external_file "list.c"
41install_C_file "list.c"
42autocorres "list.c"
43
44context list begin
45
46thm reverse'_def
47thm revappend'_def
48
49section "The heap"
50
51subsection "Paths in the heap"
52
53definition node_next where "node_next s p = next_C (heap_node_C s p)"
54definition node_exists where "node_exists s p = (p \<noteq> NULL \<and> is_valid_node_C s p)"
55
56definition node_next_upd where
57  "node_next_upd s p q = heap_node_C_update (\<lambda>old. old(p := next_C_update (\<lambda>_. q) (old p))) s"
58
59lemma "p \<noteq> x \<Longrightarrow> heap_node_C (node_next_upd s x q) p = heap_node_C s p"
60by (simp add: node_next_def node_next_upd_def fun_upd_apply)
61lemma [simp]: "node_next (node_next_upd s p q) p = q"
62by (simp add: node_next_def node_next_upd_def fun_upd_apply)
63lemma [simp]: "p \<noteq> x \<Longrightarrow> node_next (node_next_upd s x q) p = node_next s p"
64by (simp add: node_next_def node_next_upd_def fun_upd_apply)
65lemma "node_next_upd (node_next_upd s p a) p b = node_next_upd s p b"
66by (simp add: node_next_def node_next_upd_def fun_upd_apply)
67lemma [simp]: "node_exists (node_next_upd s q x) p = node_exists s p"
68by (simp add: node_exists_def node_next_upd_def fun_upd_apply)
69lemma "p \<noteq> q \<Longrightarrow> node_next (node_next_upd s q x) p = node_next s p"
70by (simp add: node_next_def node_next_upd_def fun_upd_apply)
71
72primrec Path :: "lifted_globals \<Rightarrow> node_C ptr \<Rightarrow> node_C ptr list \<Rightarrow> node_C ptr \<Rightarrow> bool" where
73  "Path s x [] y \<longleftrightarrow> x = y"
74| "Path s x (a#as) y \<longleftrightarrow> node_exists s x \<and> x = a \<and> Path s (node_next s x) as y"
75
76lemma [iff]: "Path s NULL xs y = (xs = [] \<and> y = NULL)"
77by (case_tac xs, fastforce, fastforce simp: node_exists_def)
78
79lemma [simp]: "node_exists s a \<Longrightarrow> Path s a as z =
80(as = [] \<and> a = z   \<or>   (\<exists>bs. as = a # bs \<and> Path s (node_next s a) bs z))"
81by (case_tac as, fastforce, fastforce simp: node_exists_def)
82
83lemma [simp]: "\<And>x. Path s x (as@bs) z =
84(\<exists>y. Path s x as y \<and> Path s y bs z)"
85by (induct as, simp+)
86
87lemma Path_upd[simp]:
88"\<And>x. u \<notin> set as \<Longrightarrow>
89Path (node_next_upd s u v) x as y = Path s x as y"
90apply (induct as, simp)
91apply (simp add: node_exists_def node_next_def node_next_upd_def fun_upd_apply)
92done
93
94lemma Path_snoc:
95"\<lbrakk> node_exists s a; Path (node_next_upd s a q) p as a \<rbrakk> \<Longrightarrow> Path (node_next_upd s a q) p (as @ [a]) q"
96by simp
97
98
99subsection "Non-repeating paths"
100
101definition distPath ::
102  "lifted_globals \<Rightarrow> node_C ptr \<Rightarrow> node_C ptr list \<Rightarrow> node_C ptr \<Rightarrow> bool"
103  where "distPath s x as y \<longleftrightarrow> Path s x as y \<and> distinct as"
104
105text\<open>The term @{term "distPath s x as y"} expresses the fact that a
106non-repeating path @{term as} connects location @{term x} to location
107@{term y} over the heap @{term s}. In the case where @{text "x = y"},
108and there is a cycle from @{term x} to itself, @{term as} can
109be both @{term "[]"} and the non-repeating list of nodes in the
110cycle.\<close>
111
112lemma neq_dP: "p \<noteq> q \<Longrightarrow> Path s p Ps q \<Longrightarrow> distinct Ps \<Longrightarrow>
113 \<exists> a Qs. p = a & Ps = a#Qs & a \<notin> set Qs"
114by (case_tac Ps, auto)
115
116
117lemma neq_dP_disp: "\<lbrakk> p \<noteq> q; distPath s p Ps q \<rbrakk> \<Longrightarrow>
118 \<exists> a Qs. p = a \<and> Ps = a#Qs \<and> a \<notin> set Qs"
119apply (simp only:distPath_def)
120by (case_tac Ps, auto)
121
122
123subsection "Lists on the heap"
124
125subsubsection "Relational abstraction"
126
127definition List :: "lifted_globals \<Rightarrow> node_C ptr \<Rightarrow> node_C ptr list \<Rightarrow> bool"
128  where "List s x as = Path s x as NULL"
129
130lemma [simp]: "List s x [] = (x = NULL)"
131by(simp add:List_def)
132
133lemma List_case [simp]: "List s x (a#as) = (x = a \<and> node_exists s a \<and> List s (node_next s a) as)"
134by(auto simp:List_def)
135
136lemma [simp]: "List s NULL as = (as = [])"
137by(case_tac as, simp_all add: node_exists_def)
138
139lemma List_Ref[simp]: "node_exists s a \<Longrightarrow> List s a as = (\<exists>bs. as = a#bs \<and> List s (node_next s a) bs)"
140by(case_tac as, simp_all add: node_exists_def, fast)
141
142theorem notin_List_update[simp]:
143 "\<And>x. \<lbrakk> node_exists s a; a \<notin> set as \<rbrakk> \<Longrightarrow> List (node_next_upd s a y) x as = List s x as"
144apply(induct as)
145apply simp
146apply(clarsimp simp add:fun_upd_apply)
147done
148
149lemma List_unique: "\<And>x bs. List s x as \<Longrightarrow> List s x bs \<Longrightarrow> as = bs"
150by(induct as, simp, clarsimp)
151
152lemma List_unique1: "List s p as \<Longrightarrow> \<exists>!as. List s p as"
153by(blast intro:List_unique)
154
155lemma List_app: "\<And>x. List s x (as@bs) = (\<exists>y. Path s x as y \<and> List s y bs)"
156by(induct as, simp, auto)
157
158lemma List_hd_not_in_tl[simp]: "List s (node_next s a) as \<Longrightarrow> a \<notin> set as"
159apply(clarsimp simp add:in_set_conv_decomp)
160apply(frule List_app[THEN iffD1])
161apply(fastforce dest: List_unique)
162done
163
164lemma List_distinct[simp]: "\<And>x. List s x as \<Longrightarrow> distinct as"
165apply(induct as, simp)
166apply(fastforce dest:List_hd_not_in_tl)
167done
168
169lemma Path_is_List:
170  "\<lbrakk>node_exists s a; Path s b Ps a; a \<notin> set Ps\<rbrakk> \<Longrightarrow> List (node_next_upd s a NULL) b (Ps @ [a])"
171apply (induct Ps arbitrary: b)
172apply (auto simp: node_exists_def node_next_upd_def node_next_def fun_upd_apply)
173done
174
175
176
177subsection "Functional abstraction"
178
179definition islist :: "lifted_globals \<Rightarrow> node_C ptr \<Rightarrow> bool"
180  where "islist s p \<longleftrightarrow> (\<exists>as. List s p as)"
181
182definition list :: "lifted_globals \<Rightarrow> node_C ptr \<Rightarrow> node_C ptr list"
183  where "list s p = (SOME as. List s p as)"
184
185lemma List_conv_islist_list: "List s p as = (islist s p \<and> as = list s p)"
186apply(simp add:islist_def list_def)
187apply(rule iffI)
188apply(rule conjI)
189apply blast
190apply(subst some1_equality)
191  apply(erule List_unique1)
192 apply assumption
193apply(rule refl)
194apply simp
195apply(rule someI_ex)
196apply fast
197done
198
199lemma [simp]: "islist s NULL"
200by(simp add:islist_def)
201
202lemma [simp]: "node_exists s a \<Longrightarrow> islist s a = islist s (node_next s a)"
203by(simp add:islist_def)
204
205lemma [simp]: "\<lbrakk> node_exists s a; islist s a \<rbrakk> \<Longrightarrow> islist s (node_next s a)"
206apply (clarsimp simp: islist_def)
207done
208
209lemma [simp]: "list s NULL = []"
210by(simp add:list_def)
211
212lemma list_Ref_conv[simp]:
213 "\<lbrakk> node_exists s a; islist s (node_next s a) \<rbrakk> \<Longrightarrow> list s a = a # list s (node_next s a)"
214apply(insert List_Ref[of s])
215apply(metis List_conv_islist_list)
216done
217
218lemma [simp]: "\<lbrakk> node_exists s a; islist s (node_next s a) \<rbrakk> \<Longrightarrow> a \<notin> set(list s (node_next s a))"
219apply(insert List_hd_not_in_tl[of s])
220apply(metis List_conv_islist_list)
221done
222
223lemma list_upd_conv[simp]:
224 "islist s p \<Longrightarrow> y \<notin> set(list s p) \<Longrightarrow> list (node_next_upd s y q) p = list s p"
225  apply (clarsimp simp: islist_def node_next_upd_def)
226  by (metis (mono_tags) list.List_def list.List_unique list.Path_upd list.list_def list.node_next_upd_def some_eq_ex)
227
228lemma islist_upd[simp]:
229 "islist s p \<Longrightarrow> y \<notin> set(list s p) \<Longrightarrow> islist (node_next_upd s y q) p"
230  apply (clarsimp simp: islist_def node_next_upd_def)
231  by (metis (mono_tags) list.List_def list.List_unique list.Path_upd list.list_def list.node_next_upd_def some_eq_ex)
232
233
234subsection "List reversal"
235
236subsubsection "Program representation"
237text \<open>This is AutoCorres's representation of revappend.\<close>
238thm revappend'_def
239
240text \<open>The heap operations are a bit too low level,
241        so let's lift them using our heap accessors.\<close>
242schematic_goal revappend'_abstract: "revappend' p q = ?new_definition"
243  apply (subst revappend'_def)
244  unfolding node_next_def[symmetric]
245            node_next_upd_def[symmetric]
246            modify_def
247  apply (rule refl)
248  done
249
250
251text \<open>node_exists is a prerequisite for most of our abstraction rules.
252        But we cannot extract this prerequisite from the program text because it is
253        implied by our Hoare preconditions (below) which are not a part of the program
254        text. Here we arrange for Isabelle's tactics to infer it when needed.\<close>
255
256(* FIXME: figure out why we need to take this out of the simpset for
257          the loop termination subgoals. *)
258lemma node_exists_imp_valid[simp]: "node_exists s b \<Longrightarrow> is_valid_node_C s b"
259  by (simp add: node_exists_def)
260
261lemma List_node_exists [dest!]: "\<lbrakk> p \<noteq> NULL; List s p as \<rbrakk> \<Longrightarrow> node_exists s p"
262by (case_tac as, auto simp: node_exists_def)
263
264lemma islist_node_exists [simp]: "\<lbrakk> p \<noteq> NULL; islist s p \<rbrakk> \<Longrightarrow> node_exists s p"
265  apply (clarsimp simp: islist_def)
266  apply (rename_tac a, case_tac a, simp_all add: node_exists_def)
267  done
268
269
270
271subsubsection "Verification with relational abstraction"
272text \<open>Almost automatic proof using the relational abstraction.
273        (The termination proof uses functional abstraction.)\<close>
274
275lemma
276"\<lbrace> \<lambda>s. List s p Ps \<and> List s q Qs \<and> set Ps \<inter> set Qs = {} \<rbrace>
277   revappend' p q
278 \<lbrace> \<lambda>r s. List s r (rev Ps @ Qs) \<rbrace>!"
279  txt \<open>We verify the abstracted version of the program.\<close>
280  unfolding revappend'_abstract
281
282  txt \<open>Add the loop invariant and measure.\<close>
283  apply (subst whileLoop_add_inv
284    [where I = "\<lambda>(dest', list') s.
285                 \<exists>ps qs. List s list' ps \<and> List s dest' qs \<and> set ps \<inter> set qs = {} \<and>
286                         rev ps @ qs = rev Ps @ Qs"
287       and M = " (\<lambda>((_, list'), s). length (list s list'))"])
288  apply (wp validNF_whileLoop_inv_measure_twosteps)
289
290  txt \<open>Loop invariant.\<close>
291    apply (fastforce intro: notin_List_update[THEN iffD2])
292
293  txt \<open>Loop measure.\<close>
294    apply wp
295    apply (fastforce simp: List_conv_islist_list simp del: node_exists_imp_valid)
296
297  txt \<open>Loop entrance and exit.\<close>
298   apply fastforce+
299  done
300
301
302
303subsubsection "Verification with functional abstraction"
304text \<open>Fully automatic proof using the functional abstraction.\<close>
305lemma
306"\<lbrace> \<lambda>s. islist s p \<and> islist s q \<and> Ps = list s p \<and> Qs = list s q \<and> set Ps \<inter> set Qs = {} \<rbrace>
307   revappend' p q
308 \<lbrace> \<lambda>r s. islist s r \<and> list s r = rev Ps @ Qs \<rbrace>!"
309  unfolding revappend'_abstract
310
311  apply (subst whileLoop_add_inv
312    [where I = "\<lambda>(q, p) s. islist s p \<and> islist s q \<and>
313                           set(list s p) \<inter> set(list s q) = {} \<and>
314                           rev(list s p) @ (list s q) = rev Ps @ Qs"
315       and M = "(\<lambda>((q, p), s). length (list s p))"])
316  apply (wp validNF_whileLoop_inv_measure_twosteps)
317
318  txt \<open>Loop invariant.\<close>
319     apply clarsimp
320
321  txt \<open>Loop measure.\<close>
322    apply wp
323    apply (clarsimp simp del: node_exists_imp_valid)
324
325  txt \<open>Loop entrance and exit.\<close>
326   apply clarsimp+
327  done
328
329text \<open>Provide the node_exists condition manually.\<close>
330lemma
331"\<lbrace> \<lambda>s. islist s p \<and> islist s q \<and> Ps = list s p \<and> Qs = list s q \<and> set Ps \<inter> set Qs = {} \<rbrace>
332   revappend' p q
333 \<lbrace> \<lambda>r s. islist s r \<and> list s r = rev Ps @ Qs \<rbrace>!"
334  unfolding revappend'_abstract
335
336  apply (subst whileLoop_add_inv
337    [where I = "\<lambda>(q, p) s. islist s p \<and> islist s q \<and>
338                           set(list s p) \<inter> set(list s q) = {} \<and>
339                           rev(list s p) @ (list s q) = rev Ps @ Qs"
340       and M = "(\<lambda>((q, p), s). length (list s p))"])
341  apply (wp validNF_whileLoop_inv_measure_twosteps)
342     apply (clarsimp simp del: islist_node_exists)
343     apply (frule islist_node_exists, assumption)
344     apply (clarsimp simp del: islist_node_exists)
345
346    apply wp
347    apply (clarsimp simp del: node_exists_imp_valid)
348
349   apply clarsimp+
350  done
351
352end (* context list *)
353
354end
355