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