diff --git a/src/HOL/Hoare/Arith2.thy b/src/HOL/Hoare/Arith2.thy --- a/src/HOL/Hoare/Arith2.thy +++ b/src/HOL/Hoare/Arith2.thy @@ -1,89 +1,89 @@ (* Title: HOL/Hoare/Arith2.thy Author: Norbert Galm Copyright 1995 TUM - -More arithmetic. Much of this duplicates ex/Primes. *) +section \More arithmetic\ + theory Arith2 -imports Main + imports Main begin definition cd :: "[nat, nat, nat] \ bool" where "cd x m n \ x dvd m \ x dvd n" definition gcd :: "[nat, nat] \ nat" where "gcd m n = (SOME x. cd x m n & (\y.(cd y m n) \ y\x))" primrec fac :: "nat \ nat" where "fac 0 = Suc 0" | "fac (Suc n) = Suc n * fac n" -subsubsection \cd\ +subsection \cd\ lemma cd_nnn: "0 cd n n n" apply (simp add: cd_def) done lemma cd_le: "[| cd x m n; 0 x<=m & x<=n" apply (unfold cd_def) apply (blast intro: dvd_imp_le) done lemma cd_swap: "cd x m n = cd x n m" apply (unfold cd_def) apply blast done lemma cd_diff_l: "n\m \ cd x m n = cd x (m-n) n" apply (unfold cd_def) apply (fastforce dest: dvd_diffD) done lemma cd_diff_r: "m\n \ cd x m n = cd x m (n-m)" apply (unfold cd_def) apply (fastforce dest: dvd_diffD) done -subsubsection \gcd\ +subsection \gcd\ lemma gcd_nnn: "0 n = gcd n n" apply (unfold gcd_def) apply (frule cd_nnn) apply (rule some_equality [symmetric]) apply (blast dest: cd_le) apply (blast intro: le_antisym dest: cd_le) done lemma gcd_swap: "gcd m n = gcd n m" apply (simp add: gcd_def cd_swap) done lemma gcd_diff_l: "n\m \ gcd m n = gcd (m-n) n" apply (unfold gcd_def) apply (subgoal_tac "n\m \ \x. cd x m n = cd x (m-n) n") apply simp apply (rule allI) apply (erule cd_diff_l) done lemma gcd_diff_r: "m\n \ gcd m n = gcd m (n-m)" apply (unfold gcd_def) apply (subgoal_tac "m\n \ \x. cd x m n = cd x m (n-m) ") apply simp apply (rule allI) apply (erule cd_diff_r) done -subsubsection \pow\ +subsection \pow\ lemma sq_pow_div2 [simp]: "m mod 2 = 0 \ ((n::nat)*n)^(m div 2) = n^m" apply (simp add: power2_eq_square [symmetric] power_mult [symmetric] minus_mod_eq_mult_div [symmetric]) done end diff --git a/src/HOL/Hoare/Examples.thy b/src/HOL/Hoare/Examples.thy --- a/src/HOL/Hoare/Examples.thy +++ b/src/HOL/Hoare/Examples.thy @@ -1,347 +1,349 @@ (* Title: HOL/Hoare/Examples.thy Author: Norbert Galm Copyright 1998 TUM *) -chapter \Various examples\ +section \Various examples\ -theory Examples imports Hoare_Logic Arith2 begin +theory Examples + imports Hoare_Logic Arith2 +begin -section \ARITHMETIC\ +subsection \Arithmetic\ -subsection \multiplication by successive addition\ +subsubsection \Multiplication by successive addition\ lemma multiply_by_add: "VARS m s a b {a=A \ b=B} m := 0; s := 0; WHILE m\a INV {s=m*b \ a=A \ b=B} DO s := s+b; m := m+(1::nat) OD {s = A*B}" by vcg_simp lemma multiply_by_add_time: "VARS m s a b t {a=A \ b=B \ t=0} m := 0; t := t+1; s := 0; t := t+1; WHILE m\a INV {s=m*b \ a=A \ b=B \ t = 2*m + 2} DO s := s+b; t := t+1; m := m+(1::nat); t := t+1 OD {s = A*B \ t = 2*A + 2}" by vcg_simp lemma multiply_by_add2: "VARS M N P :: int {m=M \ n=N} IF M < 0 THEN M := -M; N := -N ELSE SKIP FI; P := 0; WHILE 0 < M INV {0 \ M \ (\p. p = (if m<0 then -m else m) & p*N = m*n & P = (p-M)*N)} DO P := P+N; M := M - 1 OD {P = m*n}" apply vcg_simp apply (auto simp add:int_distrib) done lemma multiply_by_add2_time: "VARS M N P t :: int {m=M \ n=N \ t=0} IF M < 0 THEN M := -M; t := t+1; N := -N; t := t+1 ELSE SKIP FI; P := 0; t := t+1; WHILE 0 < M INV {0 \ M & (\p. p = (if m<0 then -m else m) & p*N = m*n & P = (p-M)*N & t \ 0 & t \ 2*(p-M)+3)} DO P := P+N; t := t+1; M := M - 1; t := t+1 OD {P = m*n & t \ 2*abs m + 3}" apply vcg_simp apply (auto simp add:int_distrib) done -subsection \Euclid's algorithm for GCD\ +subsubsection \Euclid's algorithm for GCD\ lemma Euclid_GCD: "VARS a b {0 b INV {0 \Now prove the verification conditions\ apply auto apply(simp add: gcd_diff_r less_imp_le) apply(simp add: linorder_not_less gcd_diff_l) apply(erule gcd_nnn) done lemma Euclid_GCD_time: "VARS a b t {0 b INV {0A & b\B & t \ max A B - max a b + 2} DO IF a max A B + 2}" apply vcg \ \Now prove the verification conditions\ apply auto apply(simp add: gcd_diff_r less_imp_le) apply(simp add: linorder_not_less gcd_diff_l) apply(erule gcd_nnn) done -subsection \Dijkstra's extension of Euclid's algorithm for simultaneous GCD and SCM\ +subsubsection \Dijkstra's extension of Euclid's algorithm for simultaneous GCD and SCM\ text \ From E.W. Disjkstra. Selected Writings on Computing, p 98 (EWD474), where it is given without the invariant. Instead of defining \scm\ explicitly we have used the theorem \scm x y = x * y / gcd x y\ and avoided division by mupltiplying with \gcd x y\. \ lemmas distribs = diff_mult_distrib diff_mult_distrib2 add_mult_distrib add_mult_distrib2 lemma gcd_scm: "VARS a b x y {0Power by iterated squaring and multiplication\ +subsubsection \Power by iterated squaring and multiplication\ lemma power_by_mult: "VARS a b c {a=A & b=B} c := (1::nat); WHILE b ~= 0 INV {A^B = c * a^b} DO WHILE b mod 2 = 0 INV {A^B = c * a^b} DO a := a*a; b := b div 2 OD; c := c*a; b := b - 1 OD {c = A^B}" apply vcg_simp apply(case_tac "b") apply simp apply simp done -subsection \Factorial\ +subsubsection \Factorial\ lemma factorial: "VARS a b {a=A} b := 1; WHILE a > 0 INV {fac A = b * fac a} DO b := b*a; a := a - 1 OD {b = fac A}" apply vcg_simp apply(clarsimp split: nat_diff_split) done lemma factorial_time: "VARS a b t {a=A & t=0} b := 1; t := t+1; WHILE a > 0 INV {fac A = b * fac a & a \ A & t = 2*(A-a)+1} DO b := b*a; t := t+1; a := a - 1; t := t+1 OD {b = fac A & t = 2*A + 1}" apply vcg_simp apply(clarsimp split: nat_diff_split) done lemma [simp]: "1 \ i \ fac (i - Suc 0) * i = fac i" by(induct i, simp_all) lemma factorial2: "VARS i f {True} i := (1::nat); f := 1; WHILE i <= n INV {f = fac(i - 1) & 1 <= i & i <= n+1} DO f := f*i; i := i+1 OD {f = fac n}" apply vcg_simp apply(subgoal_tac "i = Suc n") apply simp apply arith done lemma factorial2_time: "VARS i f t {t=0} i := (1::nat); t := t+1; f := 1; t := t+1; WHILE i \ n INV {f = fac(i - 1) & 1 \ i & i \ n+1 & t = 2*(i-1)+2} DO f := f*i; t := t+1; i := i+1; t := t+1 OD {f = fac n & t = 2*n+2}" apply vcg_simp apply auto apply(subgoal_tac "i = Suc n") apply simp apply arith done -subsection \Square root\ +subsubsection \Square root\ \ \the easy way:\ lemma sqrt: "VARS r x {True} r := (0::nat); WHILE (r+1)*(r+1) <= X INV {r*r \ X} DO r := r+1 OD {r*r <= X & X < (r+1)*(r+1)}" apply vcg_simp done lemma sqrt_time: "VARS r t {t=0} r := (0::nat); t := t+1; WHILE (r+1)*(r+1) <= X INV {r*r \ X & t = r+1} DO r := r+1; t := t+1 OD {r*r <= X & X < (r+1)*(r+1) & (t-1)*(t-1) \ X}" apply vcg_simp done \ \without multiplication\ lemma sqrt_without_multiplication: "VARS u w r {x=X} u := 1; w := 1; r := (0::nat); WHILE w <= X INV {u = r+r+1 & w = (r+1)*(r+1) & r*r <= X} DO r := r + 1; w := w + u + 2; u := u + 2 OD {r*r <= X & X < (r+1)*(r+1)}" apply vcg_simp done -section \LISTS\ +subsection \Lists\ lemma imperative_reverse: "VARS y x {x=X} y:=[]; WHILE x ~= [] INV {rev(x)@y = rev(X)} DO y := (hd x # y); x := tl x OD {y=rev(X)}" apply vcg_simp apply(simp add: neq_Nil_conv) apply auto done lemma imperative_reverse_time: "VARS y x t {x=X & t=0} y:=[]; t := t+1; WHILE x ~= [] INV {rev(x)@y = rev(X) & t = 2*(length y) + 1} DO y := (hd x # y); t := t+1; x := tl x; t := t+1 OD {y=rev(X) & t = 2*length X + 1}" apply vcg_simp apply(simp add: neq_Nil_conv) apply auto done lemma imperative_append: "VARS x y {x=X & y=Y} x := rev(x); WHILE x~=[] INV {rev(x)@y = X@Y} DO y := (hd x # y); x := tl x OD {y = X@Y}" apply vcg_simp apply(simp add: neq_Nil_conv) apply auto done lemma imperative_append_time_no_rev: "VARS x y t {x=X & y=Y} x := rev(x); t := 0; WHILE x~=[] INV {rev(x)@y = X@Y & length x \ length X & t = 2 * (length X - length x)} DO y := (hd x # y); t := t+1; x := tl x; t := t+1 OD {y = X@Y & t = 2 * length X}" apply vcg_simp apply(simp add: neq_Nil_conv) apply auto done -section \ARRAYS\ +subsection \Arrays\ -subsection \Search for a key\ +subsubsection \Search for a key\ lemma zero_search: "VARS A i {True} i := 0; WHILE i < length A & A!i \ key INV {\j. j A!j \ key} DO i := i+1 OD {(i < length A --> A!i = key) & (i = length A --> (\j. j < length A \ A!j \ key))}" apply vcg_simp apply(blast elim!: less_SucE) done lemma zero_search_time: "VARS A i t {t=0} i := 0; t := t+1; WHILE i < length A \ A!i \ key INV {(\j. j A!j \ key) \ i \ length A \ t = i+1} DO i := i+1; t := t+1 OD {(i < length A \ A!i = key) \ (i = length A \ (\j. j < length A --> A!j \ key)) \ t \ length A + 1}" apply vcg_simp apply(blast elim!: less_SucE) done text \ The \partition\ procedure for quicksort. \<^item> \A\ is the array to be sorted (modelled as a list). \<^item> Elements of \A\ must be of class order to infer at the end that the elements between u and l are equal to pivot. Ambiguity warnings of parser are due to \:=\ being used both for assignment and list update. \ lemma lem: "m - Suc 0 < n ==> m < Suc n" by arith lemma Partition: "[| leq == \A i. \k. k A!k \ pivot; geq == \A i. \k. i k pivot \ A!k |] ==> VARS A u l {0 < length(A::('a::order)list)} l := 0; u := length A - Suc 0; WHILE l \ u INV {leq A l \ geq A u \ u l\length A} DO WHILE l < length A \ A!l \ pivot INV {leq A l & geq A u \ u l\length A} DO l := l+1 OD; WHILE 0 < u & pivot \ A!u INV {leq A l & geq A u \ u l\length A} DO u := u - 1 OD; IF l \ u THEN A := A[l := A!u, u := A!l] ELSE SKIP FI OD {leq A u & (\k. u k A!k = pivot) \ geq A l}" \ \expand and delete abbreviations first\ apply (simp) apply (erule thin_rl)+ apply vcg_simp apply (force simp: neq_Nil_conv) apply (blast elim!: less_SucE intro: Suc_leI) apply (blast elim!: less_SucE intro: less_imp_diff_less dest: lem) apply (force simp: nth_list_update) done end diff --git a/src/HOL/Hoare/ExamplesAbort.thy b/src/HOL/Hoare/ExamplesAbort.thy --- a/src/HOL/Hoare/ExamplesAbort.thy +++ b/src/HOL/Hoare/ExamplesAbort.thy @@ -1,28 +1,30 @@ (* Title: HOL/Hoare/ExamplesAbort.thy Author: Tobias Nipkow Copyright 1998 TUM - -Some small examples for programs that may abort. *) -theory ExamplesAbort imports Hoare_Logic_Abort begin +section \Some small examples for programs that may abort\ + +theory ExamplesAbort + imports Hoare_Logic_Abort +begin lemma "VARS x y z::nat {y = z & z \ 0} z \ 0 \ x := y div z {x = 1}" by vcg_simp lemma "VARS a i j {k <= length a & i < k & j < k} j < length a \ a[i] := a!j {True}" by vcg_simp lemma "VARS (a::int list) i {True} i := 0; WHILE i < length a INV {i <= length a} DO a[i] := 7; i := i+1 OD {True}" by vcg_simp end diff --git a/src/HOL/Hoare/ExamplesTC.thy b/src/HOL/Hoare/ExamplesTC.thy --- a/src/HOL/Hoare/ExamplesTC.thy +++ b/src/HOL/Hoare/ExamplesTC.thy @@ -1,118 +1,116 @@ (* Title: Examples using Hoare Logic for Total Correctness Author: Walter Guttmann *) section \Examples using Hoare Logic for Total Correctness\ theory ExamplesTC - -imports Hoare_Logic - + imports Hoare_Logic begin text \ This theory demonstrates a few simple partial- and total-correctness proofs. The first example is taken from HOL/Hoare/Examples.thy written by N. Galm. We have added the invariant \m \ a\. \ lemma multiply_by_add: "VARS m s a b {a=A \ b=B} m := 0; s := 0; WHILE m\a INV {s=m*b \ a=A \ b=B \ m\a} DO s := s+b; m := m+(1::nat) OD {s = A*B}" by vcg_simp text \ Here is the total-correctness proof for the same program. It needs the additional invariant \m \ a\. \ lemma multiply_by_add_tc: "VARS m s a b [a=A \ b=B] m := 0; s := 0; WHILE m\a INV {s=m*b \ a=A \ b=B \ m\a} VAR {a-m} DO s := s+b; m := m+(1::nat) OD [s = A*B]" apply vcg_tc_simp by auto text \ Next, we prove partial correctness of a program that computes powers. \ lemma power: "VARS (p::int) i { True } p := 1; i := 0; WHILE i < n INV { p = x^i \ i \ n } DO p := p * x; i := i + 1 OD { p = x^n }" apply vcg_simp by auto text \ Here is its total-correctness proof. \ lemma power_tc: "VARS (p::int) i [ True ] p := 1; i := 0; WHILE i < n INV { p = x^i \ i \ n } VAR { n - i } DO p := p * x; i := i + 1 OD [ p = x^n ]" apply vcg_tc by auto text \ The last example is again taken from HOL/Hoare/Examples.thy. We have modified it to integers so it requires precondition \0 \ x\. \ lemma sqrt_tc: "VARS r [0 \ (x::int)] r := 0; WHILE (r+1)*(r+1) <= x INV {r*r \ x} VAR {nat (x-r)} DO r := r+1 OD [r*r \ x \ x < (r+1)*(r+1)]" apply vcg_tc_simp by (smt (verit) div_pos_pos_trivial mult_less_0_iff nonzero_mult_div_cancel_left) text \ A total-correctness proof allows us to extract a function for further use. For every input satisfying the precondition the function returns an output satisfying the postcondition. \ lemma sqrt_exists: "0 \ (x::int) \ \r' . r'*r' \ x \ x < (r'+1)*(r'+1)" using tc_extract_function sqrt_tc by blast definition "sqrt (x::int) \ (SOME r' . r'*r' \ x \ x < (r'+1)*(r'+1))" lemma sqrt_function: assumes "0 \ (x::int)" and "r' = sqrt x" shows "r'*r' \ x \ x < (r'+1)*(r'+1)" proof - let ?P = "\r' . r'*r' \ x \ x < (r'+1)*(r'+1)" have "?P (SOME z . ?P z)" by (metis (mono_tags, lifting) assms(1) sqrt_exists some_eq_imp) thus ?thesis using assms(2) sqrt_def by auto qed end diff --git a/src/HOL/Hoare/Heap.thy b/src/HOL/Hoare/Heap.thy --- a/src/HOL/Hoare/Heap.thy +++ b/src/HOL/Hoare/Heap.thy @@ -1,188 +1,191 @@ (* Title: HOL/Hoare/Heap.thy Author: Tobias Nipkow Copyright 2002 TUM - -Pointers, heaps and heap abstractions. -See the paper by Mehta and Nipkow. *) -theory Heap imports Main begin +section \Pointers, heaps and heap abstractions\ + +text \See the paper by Mehta and Nipkow.\ + +theory Heap + imports Main +begin subsection "References" datatype 'a ref = Null | Ref 'a lemma not_Null_eq [iff]: "(x \ Null) = (\y. x = Ref y)" by (induct x) auto lemma not_Ref_eq [iff]: "(\y. x \ Ref y) = (x = Null)" by (induct x) auto primrec addr :: "'a ref \ 'a" where "addr (Ref a) = a" -section "The heap" +subsection "The heap" -subsection "Paths in the heap" +subsubsection "Paths in the heap" primrec Path :: "('a \ 'a ref) \ 'a ref \ 'a list \ 'a ref \ bool" where "Path h x [] y \ x = y" | "Path h x (a#as) y \ x = Ref a \ Path h (h a) as y" lemma [iff]: "Path h Null xs y = (xs = [] \ y = Null)" apply(case_tac xs) apply fastforce apply fastforce done lemma [simp]: "Path h (Ref a) as z = (as = [] \ z = Ref a \ (\bs. as = a#bs \ Path h (h a) bs z))" apply(case_tac as) apply fastforce apply fastforce done lemma [simp]: "\x. Path f x (as@bs) z = (\y. Path f x as y \ Path f y bs z)" by(induct as, simp+) lemma Path_upd[simp]: "\x. u \ set as \ Path (f(u := v)) x as y = Path f x as y" by(induct as, simp, simp add:eq_sym_conv) lemma Path_snoc: "Path (f(a := q)) p as (Ref a) \ Path (f(a := q)) p (as @ [a]) q" by simp -subsection "Non-repeating paths" +subsubsection "Non-repeating paths" definition distPath :: "('a \ 'a ref) \ 'a ref \ 'a list \ 'a ref \ bool" where "distPath h x as y \ Path h x as y \ distinct as" text\The term \<^term>\distPath h x as y\ expresses the fact that a non-repeating path \<^term>\as\ connects location \<^term>\x\ to location \<^term>\y\ by means of the \<^term>\h\ field. In the case where \x = y\, and there is a cycle from \<^term>\x\ to itself, \<^term>\as\ can be both \<^term>\[]\ and the non-repeating list of nodes in the cycle.\ lemma neq_dP: "p \ q \ Path h p Ps q \ distinct Ps \ \a Qs. p = Ref a \ Ps = a#Qs \ a \ set Qs" by (case_tac Ps, auto) lemma neq_dP_disp: "\ p \ q; distPath h p Ps q \ \ \a Qs. p = Ref a \ Ps = a#Qs \ a \ set Qs" apply (simp only:distPath_def) by (case_tac Ps, auto) -subsection "Lists on the heap" +subsubsection "Lists on the heap" -subsubsection "Relational abstraction" +paragraph "Relational abstraction" definition List :: "('a \ 'a ref) \ 'a ref \ 'a list \ bool" where "List h x as = Path h x as Null" lemma [simp]: "List h x [] = (x = Null)" by(simp add:List_def) lemma [simp]: "List h x (a#as) = (x = Ref a \ List h (h a) as)" by(simp add:List_def) lemma [simp]: "List h Null as = (as = [])" by(case_tac as, simp_all) lemma List_Ref[simp]: "List h (Ref a) as = (\bs. as = a#bs \ List h (h a) bs)" by(case_tac as, simp_all, fast) theorem notin_List_update[simp]: "\x. a \ set as \ List (h(a := y)) x as = List h x as" apply(induct as) apply simp apply(clarsimp simp add:fun_upd_apply) done lemma List_unique: "\x bs. List h x as \ List h x bs \ as = bs" by(induct as, simp, clarsimp) lemma List_unique1: "List h p as \ \!as. List h p as" by(blast intro:List_unique) lemma List_app: "\x. List h x (as@bs) = (\y. Path h x as y \ List h y bs)" by(induct as, simp, clarsimp) lemma List_hd_not_in_tl[simp]: "List h (h a) as \ a \ set as" apply (clarsimp simp add:in_set_conv_decomp) apply(frule List_app[THEN iffD1]) apply(fastforce dest: List_unique) done lemma List_distinct[simp]: "\x. List h x as \ distinct as" apply(induct as, simp) apply(fastforce dest:List_hd_not_in_tl) done lemma Path_is_List: "\Path h b Ps (Ref a); a \ set Ps\ \ List (h(a := Null)) b (Ps @ [a])" apply (induct Ps arbitrary: b) apply (auto simp add:fun_upd_apply) done -subsection "Functional abstraction" +subsubsection "Functional abstraction" definition islist :: "('a \ 'a ref) \ 'a ref \ bool" where "islist h p \ (\as. List h p as)" definition list :: "('a \ 'a ref) \ 'a ref \ 'a list" where "list h p = (SOME as. List h p as)" lemma List_conv_islist_list: "List h p as = (islist h p \ as = list h p)" apply(simp add:islist_def list_def) apply(rule iffI) apply(rule conjI) apply blast apply(subst some1_equality) apply(erule List_unique1) apply assumption apply(rule refl) apply simp apply(rule someI_ex) apply fast done lemma [simp]: "islist h Null" by(simp add:islist_def) lemma [simp]: "islist h (Ref a) = islist h (h a)" by(simp add:islist_def) lemma [simp]: "list h Null = []" by(simp add:list_def) lemma list_Ref_conv[simp]: "islist h (h a) \ list h (Ref a) = a # list h (h a)" apply(insert List_Ref[of h]) apply(fastforce simp:List_conv_islist_list) done lemma [simp]: "islist h (h a) \ a \ set(list h (h a))" apply(insert List_hd_not_in_tl[of h]) apply(simp add:List_conv_islist_list) done lemma list_upd_conv[simp]: "islist h p \ y \ set(list h p) \ list (h(y := q)) p = list h p" apply(drule notin_List_update[of _ _ h q p]) apply(simp add:List_conv_islist_list) done lemma islist_upd[simp]: "islist h p \ y \ set(list h p) \ islist (h(y := q)) p" apply(frule notin_List_update[of _ _ h q p]) apply(simp add:List_conv_islist_list) done end diff --git a/src/HOL/Hoare/HeapSyntax.thy b/src/HOL/Hoare/HeapSyntax.thy --- a/src/HOL/Hoare/HeapSyntax.thy +++ b/src/HOL/Hoare/HeapSyntax.thy @@ -1,38 +1,42 @@ (* Title: HOL/Hoare/HeapSyntax.thy Author: Tobias Nipkow Copyright 2002 TUM *) -theory HeapSyntax imports Hoare_Logic Heap begin +section \Heap syntax\ + +theory HeapSyntax + imports Hoare_Logic Heap +begin subsection "Field access and update" syntax "_refupdate" :: "('a \ 'b) \ 'a ref \ 'b \ ('a \ 'b)" ("_/'((_ \ _)')" [1000,0] 900) "_fassign" :: "'a ref => id => 'v => 's com" ("(2_^._ :=/ _)" [70,1000,65] 61) "_faccess" :: "'a ref => ('a ref \ 'v) => 'v" ("_^._" [65,1000] 65) translations "f(r \ v)" == "f(CONST addr r := v)" "p^.f := e" => "f := f(p \ e)" "p^.f" => "f(CONST addr p)" declare fun_upd_apply[simp del] fun_upd_same[simp] fun_upd_other[simp] text "An example due to Suzuki:" lemma "VARS v n {w = Ref w0 & x = Ref x0 & y = Ref y0 & z = Ref z0 & distinct[w0,x0,y0,z0]} w^.v := (1::int); w^.n := x; x^.v := 2; x^.n := y; y^.v := 3; y^.n := z; z^.v := 4; x^.n := z {w^.n^.n^.v = 4}" by vcg_simp end diff --git a/src/HOL/Hoare/HeapSyntaxAbort.thy b/src/HOL/Hoare/HeapSyntaxAbort.thy --- a/src/HOL/Hoare/HeapSyntaxAbort.thy +++ b/src/HOL/Hoare/HeapSyntaxAbort.thy @@ -1,46 +1,50 @@ (* Title: HOL/Hoare/HeapSyntaxAbort.thy Author: Tobias Nipkow Copyright 2002 TUM *) -theory HeapSyntaxAbort imports Hoare_Logic_Abort Heap begin +section \Heap syntax (abort)\ + +theory HeapSyntaxAbort + imports Hoare_Logic_Abort Heap +begin subsection "Field access and update" text\Heap update \p^.h := e\ is now guarded against \<^term>\p\ being Null. However, \<^term>\p\ may still be illegal, e.g. uninitialized or dangling. To guard against that, one needs a more detailed model of the heap where allocated and free addresses are distinguished, e.g. by making the heap a map, or by carrying the set of free addresses around. This is needed anyway as soon as we want to reason about storage allocation/deallocation.\ syntax "_refupdate" :: "('a \ 'b) \ 'a ref \ 'b \ ('a \ 'b)" ("_/'((_ \ _)')" [1000,0] 900) "_fassign" :: "'a ref => id => 'v => 's com" ("(2_^._ :=/ _)" [70,1000,65] 61) "_faccess" :: "'a ref => ('a ref \ 'v) => 'v" ("_^._" [65,1000] 65) translations "_refupdate f r v" == "f(CONST addr r := v)" "p^.f := e" => "(p \ CONST Null) \ (f := _refupdate f p e)" "p^.f" => "f(CONST addr p)" declare fun_upd_apply[simp del] fun_upd_same[simp] fun_upd_other[simp] text "An example due to Suzuki:" lemma "VARS v n {w = Ref w0 & x = Ref x0 & y = Ref y0 & z = Ref z0 & distinct[w0,x0,y0,z0]} w^.v := (1::int); w^.n := x; x^.v := 2; x^.n := y; y^.v := 3; y^.n := z; z^.v := 4; x^.n := z {w^.n^.n^.v = 4}" by vcg_simp end diff --git a/src/HOL/Hoare/Hoare_Logic.thy b/src/HOL/Hoare/Hoare_Logic.thy --- a/src/HOL/Hoare/Hoare_Logic.thy +++ b/src/HOL/Hoare/Hoare_Logic.thy @@ -1,216 +1,218 @@ (* Title: HOL/Hoare/Hoare_Logic.thy Author: Leonor Prensa Nieto & Tobias Nipkow Copyright 1998 TUM Author: Walter Guttmann (extension to total-correctness proofs) *) -section \Sugared semantic embedding of Hoare logic\ +section \Hoare logic\ + +theory Hoare_Logic + imports Hoare_Syntax Hoare_Tac +begin + +subsection \Sugared semantic embedding of Hoare logic\ text \ Strictly speaking a shallow embedding (as implemented by Norbert Galm following Mike Gordon) would suffice. Maybe the datatype com comes in useful later. \ -theory Hoare_Logic -imports Hoare_Syntax Hoare_Tac -begin - type_synonym 'a bexp = "'a set" type_synonym 'a assn = "'a set" type_synonym 'a var = "'a \ nat" datatype 'a com = Basic "'a \ 'a" | Seq "'a com" "'a com" | Cond "'a bexp" "'a com" "'a com" | While "'a bexp" "'a assn" "'a var" "'a com" abbreviation annskip ("SKIP") where "SKIP == Basic id" type_synonym 'a sem = "'a => 'a => bool" inductive Sem :: "'a com \ 'a sem" where "Sem (Basic f) s (f s)" | "Sem c1 s s'' \ Sem c2 s'' s' \ Sem (Seq c1 c2) s s'" | "s \ b \ Sem c1 s s' \ Sem (Cond b c1 c2) s s'" | "s \ b \ Sem c2 s s' \ Sem (Cond b c1 c2) s s'" | "s \ b \ Sem (While b x y c) s s" | "s \ b \ Sem c s s'' \ Sem (While b x y c) s'' s' \ Sem (While b x y c) s s'" definition Valid :: "'a bexp \ 'a com \ 'a bexp \ bool" where "Valid p c q \ \s s'. Sem c s s' \ s \ p \ s' \ q" definition ValidTC :: "'a bexp \ 'a com \ 'a bexp \ bool" where "ValidTC p c q \ \s. s \ p \ (\t. Sem c s t \ t \ q)" inductive_cases [elim!]: "Sem (Basic f) s s'" "Sem (Seq c1 c2) s s'" "Sem (Cond b c1 c2) s s'" lemma Sem_deterministic: assumes "Sem c s s1" and "Sem c s s2" shows "s1 = s2" proof - have "Sem c s s1 \ (\s2. Sem c s s2 \ s1 = s2)" by (induct rule: Sem.induct) (subst Sem.simps, blast)+ thus ?thesis using assms by simp qed lemma tc_implies_pc: "ValidTC p c q \ Valid p c q" by (metis Sem_deterministic Valid_def ValidTC_def) lemma tc_extract_function: "ValidTC p c q \ \f . \s . s \ p \ f s \ q" by (metis ValidTC_def) lemma SkipRule: "p \ q \ Valid p (Basic id) q" by (auto simp:Valid_def) lemma BasicRule: "p \ {s. f s \ q} \ Valid p (Basic f) q" by (auto simp:Valid_def) lemma SeqRule: "Valid P c1 Q \ Valid Q c2 R \ Valid P (Seq c1 c2) R" by (auto simp:Valid_def) lemma CondRule: "p \ {s. (s \ b \ s \ w) \ (s \ b \ s \ w')} \ Valid w c1 q \ Valid w' c2 q \ Valid p (Cond b c1 c2) q" by (auto simp:Valid_def) lemma While_aux: assumes "Sem (While b i v c) s s'" shows "\s s'. Sem c s s' \ s \ I \ s \ b \ s' \ I \ s \ I \ s' \ I \ s' \ b" using assms by (induct "While b i v c" s s') auto lemma WhileRule: "p \ i \ Valid (i \ b) c i \ i \ (-b) \ q \ Valid p (While b i v c) q" apply (clarsimp simp:Valid_def) apply(drule While_aux) apply assumption apply blast apply blast done lemma SkipRuleTC: assumes "p \ q" shows "ValidTC p (Basic id) q" by (metis assms Sem.intros(1) ValidTC_def id_apply subsetD) lemma BasicRuleTC: assumes "p \ {s. f s \ q}" shows "ValidTC p (Basic f) q" by (metis assms Ball_Collect Sem.intros(1) ValidTC_def) lemma SeqRuleTC: assumes "ValidTC p c1 q" and "ValidTC q c2 r" shows "ValidTC p (Seq c1 c2) r" by (meson assms Sem.intros(2) ValidTC_def) lemma CondRuleTC: assumes "p \ {s. (s \ b \ s \ w) \ (s \ b \ s \ w')}" and "ValidTC w c1 q" and "ValidTC w' c2 q" shows "ValidTC p (Cond b c1 c2) q" proof (unfold ValidTC_def, rule allI) fix s show "s \ p \ (\t . Sem (Cond b c1 c2) s t \ t \ q)" apply (cases "s \ b") apply (metis (mono_tags, lifting) assms(1,2) Ball_Collect Sem.intros(3) ValidTC_def) by (metis (mono_tags, lifting) assms(1,3) Ball_Collect Sem.intros(4) ValidTC_def) qed lemma WhileRuleTC: assumes "p \ i" and "\n::nat . ValidTC (i \ b \ {s . v s = n}) c (i \ {s . v s < n})" and "i \ uminus b \ q" shows "ValidTC p (While b i v c) q" proof - { fix s n have "s \ i \ v s = n \ (\t . Sem (While b i v c) s t \ t \ q)" proof (induction "n" arbitrary: s rule: less_induct) fix n :: nat fix s :: 'a assume 1: "\(m::nat) s::'a . m < n \ s \ i \ v s = m \ (\t . Sem (While b i v c) s t \ t \ q)" show "s \ i \ v s = n \ (\t . Sem (While b i v c) s t \ t \ q)" proof (rule impI, cases "s \ b") assume 2: "s \ b" and "s \ i \ v s = n" hence "s \ i \ b \ {s . v s = n}" using assms(1) by auto hence "\t . Sem c s t \ t \ i \ {s . v s < n}" by (metis assms(2) ValidTC_def) from this obtain t where 3: "Sem c s t \ t \ i \ {s . v s < n}" by auto hence "\u . Sem (While b i v c) t u \ u \ q" using 1 by auto thus "\t . Sem (While b i v c) s t \ t \ q" using 2 3 Sem.intros(6) by force next assume "s \ b" and "s \ i \ v s = n" thus "\t . Sem (While b i v c) s t \ t \ q" using Sem.intros(5) assms(3) by fastforce qed qed } thus ?thesis using assms(1) ValidTC_def by force qed -subsection \Concrete syntax\ +subsubsection \Concrete syntax\ setup \ Hoare_Syntax.setup {Basic = \<^const_syntax>\Basic\, Skip = \<^const_syntax>\annskip\, Seq = \<^const_syntax>\Seq\, Cond = \<^const_syntax>\Cond\, While = \<^const_syntax>\While\, Valid = \<^const_syntax>\Valid\, ValidTC = \<^const_syntax>\ValidTC\} \ -subsection \Proof methods: VCG\ +subsubsection \Proof methods: VCG\ declare BasicRule [Hoare_Tac.BasicRule] and SkipRule [Hoare_Tac.SkipRule] and SeqRule [Hoare_Tac.SeqRule] and CondRule [Hoare_Tac.CondRule] and WhileRule [Hoare_Tac.WhileRule] declare BasicRuleTC [Hoare_Tac.BasicRuleTC] and SkipRuleTC [Hoare_Tac.SkipRuleTC] and SeqRuleTC [Hoare_Tac.SeqRuleTC] and CondRuleTC [Hoare_Tac.CondRuleTC] and WhileRuleTC [Hoare_Tac.WhileRuleTC] method_setup vcg = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tac ctxt (K all_tac)))\ "verification condition generator" method_setup vcg_simp = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tac ctxt (asm_full_simp_tac ctxt)))\ "verification condition generator plus simplification" method_setup vcg_tc = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tc_tac ctxt (K all_tac)))\ "verification condition generator" method_setup vcg_tc_simp = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tc_tac ctxt (asm_full_simp_tac ctxt)))\ "verification condition generator plus simplification" end diff --git a/src/HOL/Hoare/Hoare_Logic_Abort.thy b/src/HOL/Hoare/Hoare_Logic_Abort.thy --- a/src/HOL/Hoare/Hoare_Logic_Abort.thy +++ b/src/HOL/Hoare/Hoare_Logic_Abort.thy @@ -1,236 +1,236 @@ (* Title: HOL/Hoare/Hoare_Logic_Abort.thy Author: Leonor Prensa Nieto & Tobias Nipkow Copyright 2003 TUM Author: Walter Guttmann (extension to total-correctness proofs) *) section \Hoare Logic with an Abort statement for modelling run time errors\ theory Hoare_Logic_Abort -imports Hoare_Syntax Hoare_Tac + imports Hoare_Syntax Hoare_Tac begin type_synonym 'a bexp = "'a set" type_synonym 'a assn = "'a set" type_synonym 'a var = "'a \ nat" datatype 'a com = Basic "'a \ 'a" | Abort | Seq "'a com" "'a com" | Cond "'a bexp" "'a com" "'a com" | While "'a bexp" "'a assn" "'a var" "'a com" abbreviation annskip ("SKIP") where "SKIP == Basic id" type_synonym 'a sem = "'a option => 'a option => bool" inductive Sem :: "'a com \ 'a sem" where "Sem (Basic f) None None" | "Sem (Basic f) (Some s) (Some (f s))" | "Sem Abort s None" | "Sem c1 s s'' \ Sem c2 s'' s' \ Sem (Seq c1 c2) s s'" | "Sem (Cond b c1 c2) None None" | "s \ b \ Sem c1 (Some s) s' \ Sem (Cond b c1 c2) (Some s) s'" | "s \ b \ Sem c2 (Some s) s' \ Sem (Cond b c1 c2) (Some s) s'" | "Sem (While b x y c) None None" | "s \ b \ Sem (While b x y c) (Some s) (Some s)" | "s \ b \ Sem c (Some s) s'' \ Sem (While b x y c) s'' s' \ Sem (While b x y c) (Some s) s'" inductive_cases [elim!]: "Sem (Basic f) s s'" "Sem (Seq c1 c2) s s'" "Sem (Cond b c1 c2) s s'" lemma Sem_deterministic: assumes "Sem c s s1" and "Sem c s s2" shows "s1 = s2" proof - have "Sem c s s1 \ (\s2. Sem c s s2 \ s1 = s2)" by (induct rule: Sem.induct) (subst Sem.simps, blast)+ thus ?thesis using assms by simp qed definition Valid :: "'a bexp \ 'a com \ 'a bexp \ bool" where "Valid p c q \ \s s'. Sem c s s' \ s \ Some ` p \ s' \ Some ` q" definition ValidTC :: "'a bexp \ 'a com \ 'a bexp \ bool" where "ValidTC p c q \ \s . s \ p \ (\t . Sem c (Some s) (Some t) \ t \ q)" lemma tc_implies_pc: "ValidTC p c q \ Valid p c q" by (smt Sem_deterministic ValidTC_def Valid_def image_iff) lemma tc_extract_function: "ValidTC p c q \ \f . \s . s \ p \ f s \ q" by (meson ValidTC_def) text \The proof rules for partial correctness\ lemma SkipRule: "p \ q \ Valid p (Basic id) q" by (auto simp:Valid_def) lemma BasicRule: "p \ {s. f s \ q} \ Valid p (Basic f) q" by (auto simp:Valid_def) lemma SeqRule: "Valid P c1 Q \ Valid Q c2 R \ Valid P (Seq c1 c2) R" by (auto simp:Valid_def) lemma CondRule: "p \ {s. (s \ b \ s \ w) \ (s \ b \ s \ w')} \ Valid w c1 q \ Valid w' c2 q \ Valid p (Cond b c1 c2) q" by (fastforce simp:Valid_def image_def) lemma While_aux: assumes "Sem (While b i v c) s s'" shows "\s s'. Sem c s s' \ s \ Some ` (I \ b) \ s' \ Some ` I \ s \ Some ` I \ s' \ Some ` (I \ -b)" using assms by (induct "While b i v c" s s') auto lemma WhileRule: "p \ i \ Valid (i \ b) c i \ i \ (-b) \ q \ Valid p (While b i v c) q" apply (clarsimp simp:Valid_def) apply(drule While_aux) apply assumption apply blast apply blast done lemma AbortRule: "p \ {s. False} \ Valid p Abort q" by(auto simp:Valid_def) text \The proof rules for total correctness\ lemma SkipRuleTC: assumes "p \ q" shows "ValidTC p (Basic id) q" by (metis Sem.intros(2) ValidTC_def assms id_def subsetD) lemma BasicRuleTC: assumes "p \ {s. f s \ q}" shows "ValidTC p (Basic f) q" by (metis Ball_Collect Sem.intros(2) ValidTC_def assms) lemma SeqRuleTC: assumes "ValidTC p c1 q" and "ValidTC q c2 r" shows "ValidTC p (Seq c1 c2) r" by (meson assms Sem.intros(4) ValidTC_def) lemma CondRuleTC: assumes "p \ {s. (s \ b \ s \ w) \ (s \ b \ s \ w')}" and "ValidTC w c1 q" and "ValidTC w' c2 q" shows "ValidTC p (Cond b c1 c2) q" proof (unfold ValidTC_def, rule allI) fix s show "s \ p \ (\t . Sem (Cond b c1 c2) (Some s) (Some t) \ t \ q)" apply (cases "s \ b") apply (metis (mono_tags, lifting) Ball_Collect Sem.intros(6) ValidTC_def assms(1,2)) by (metis (mono_tags, lifting) Ball_Collect Sem.intros(7) ValidTC_def assms(1,3)) qed lemma WhileRuleTC: assumes "p \ i" and "\n::nat . ValidTC (i \ b \ {s . v s = n}) c (i \ {s . v s < n})" and "i \ uminus b \ q" shows "ValidTC p (While b i v c) q" proof - { fix s n have "s \ i \ v s = n \ (\t . Sem (While b i v c) (Some s) (Some t) \ t \ q)" proof (induction "n" arbitrary: s rule: less_induct) fix n :: nat fix s :: 'a assume 1: "\(m::nat) s::'a . m < n \ s \ i \ v s = m \ (\t . Sem (While b i v c) (Some s) (Some t) \ t \ q)" show "s \ i \ v s = n \ (\t . Sem (While b i v c) (Some s) (Some t) \ t \ q)" proof (rule impI, cases "s \ b") assume 2: "s \ b" and "s \ i \ v s = n" hence "s \ i \ b \ {s . v s = n}" using assms(1) by auto hence "\t . Sem c (Some s) (Some t) \ t \ i \ {s . v s < n}" by (metis assms(2) ValidTC_def) from this obtain t where 3: "Sem c (Some s) (Some t) \ t \ i \ {s . v s < n}" by auto hence "\u . Sem (While b i v c) (Some t) (Some u) \ u \ q" using 1 by auto thus "\t . Sem (While b i v c) (Some s) (Some t) \ t \ q" using 2 3 Sem.intros(10) by force next assume "s \ b" and "s \ i \ v s = n" thus "\t . Sem (While b i v c) (Some s) (Some t) \ t \ q" using Sem.intros(9) assms(3) by fastforce qed qed } thus ?thesis using assms(1) ValidTC_def by force qed subsection \Concrete syntax\ setup \ Hoare_Syntax.setup {Basic = \<^const_syntax>\Basic\, Skip = \<^const_syntax>\annskip\, Seq = \<^const_syntax>\Seq\, Cond = \<^const_syntax>\Cond\, While = \<^const_syntax>\While\, Valid = \<^const_syntax>\Valid\, ValidTC = \<^const_syntax>\ValidTC\} \ \ \Special syntax for guarded statements and guarded array updates:\ syntax "_guarded_com" :: "bool \ 'a com \ 'a com" ("(2_ \/ _)" 71) "_array_update" :: "'a list \ nat \ 'a \ 'a com" ("(2_[_] :=/ _)" [70, 65] 61) translations "P \ c" \ "IF P THEN c ELSE CONST Abort FI" "a[i] := v" \ "(i < CONST length a) \ (a := CONST list_update a i v)" \ \reverse translation not possible because of duplicate \a\\ text \ Note: there is no special syntax for guarded array access. Thus you must write \j < length a \ a[i] := a!j\. \ subsection \Proof methods: VCG\ declare BasicRule [Hoare_Tac.BasicRule] and SkipRule [Hoare_Tac.SkipRule] and AbortRule [Hoare_Tac.AbortRule] and SeqRule [Hoare_Tac.SeqRule] and CondRule [Hoare_Tac.CondRule] and WhileRule [Hoare_Tac.WhileRule] declare BasicRuleTC [Hoare_Tac.BasicRuleTC] and SkipRuleTC [Hoare_Tac.SkipRuleTC] and SeqRuleTC [Hoare_Tac.SeqRuleTC] and CondRuleTC [Hoare_Tac.CondRuleTC] and WhileRuleTC [Hoare_Tac.WhileRuleTC] method_setup vcg = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tac ctxt (K all_tac)))\ "verification condition generator" method_setup vcg_simp = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tac ctxt (asm_full_simp_tac ctxt)))\ "verification condition generator plus simplification" method_setup vcg_tc = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tc_tac ctxt (K all_tac)))\ "verification condition generator" method_setup vcg_tc_simp = \ Scan.succeed (fn ctxt => SIMPLE_METHOD' (Hoare_Tac.hoare_tc_tac ctxt (asm_full_simp_tac ctxt)))\ "verification condition generator plus simplification" end diff --git a/src/HOL/Hoare/Hoare_Syntax.thy b/src/HOL/Hoare/Hoare_Syntax.thy --- a/src/HOL/Hoare/Hoare_Syntax.thy +++ b/src/HOL/Hoare/Hoare_Syntax.thy @@ -1,32 +1,32 @@ (* Title: HOL/Hoare/Hoare_Syntax.thy Author: Leonor Prensa Nieto & Tobias Nipkow Author: Walter Guttmann (extension to total-correctness proofs) +*) -Concrete syntax for Hoare logic, with translations for variables. -*) +section \Concrete syntax for Hoare logic, with translations for variables\ theory Hoare_Syntax imports Main begin syntax "_assign" :: "idt \ 'b \ 'com" ("(2_ :=/ _)" [70, 65] 61) "_Seq" :: "'com \ 'com \ 'com" ("(_;/ _)" [61,60] 60) "_Cond" :: "'bexp \ 'com \ 'com \ 'com" ("(1IF _/ THEN _ / ELSE _/ FI)" [0,0,0] 61) "_While" :: "'bexp \ 'assn \ 'var \ 'com \ 'com" ("(1WHILE _/ INV {_} / VAR {_} //DO _ /OD)" [0,0,0,0] 61) syntax "_While0" :: "'bexp \ 'assn \ 'com \ 'com" ("(1WHILE _/ INV {_} //DO _ /OD)" [0,0,0] 61) translations "WHILE b INV {x} DO c OD" \ "WHILE b INV {x} VAR {0} DO c OD" syntax "_hoare_vars" :: "[idts, 'assn,'com, 'assn] \ bool" ("VARS _// {_} // _ // {_}" [0,0,55,0] 50) "_hoare_vars_tc" :: "[idts, 'assn, 'com, 'assn] \ bool" ("VARS _// [_] // _ // [_]" [0,0,55,0] 50) syntax (output) "_hoare" :: "['assn, 'com, 'assn] \ bool" ("{_} // _ // {_}" [0,55,0] 50) "_hoare_tc" :: "['assn, 'com, 'assn] \ bool" ("[_] // _ // [_]" [0,55,0] 50) ML_file \hoare_syntax.ML\ end diff --git a/src/HOL/Hoare/Hoare_Tac.thy b/src/HOL/Hoare/Hoare_Tac.thy --- a/src/HOL/Hoare/Hoare_Tac.thy +++ b/src/HOL/Hoare/Hoare_Tac.thy @@ -1,35 +1,35 @@ (* Title: HOL/Hoare/Hoare_Tac.thy Author: Leonor Prensa Nieto & Tobias Nipkow Author: Walter Guttmann (extension to total-correctness proofs) +*) -Derivation of the proof rules and, most importantly, the VCG tactic. -*) +section \Hoare logic VCG tactic\ theory Hoare_Tac imports Main begin context begin qualified named_theorems BasicRule qualified named_theorems SkipRule qualified named_theorems AbortRule qualified named_theorems SeqRule qualified named_theorems CondRule qualified named_theorems WhileRule qualified named_theorems BasicRuleTC qualified named_theorems SkipRuleTC qualified named_theorems SeqRuleTC qualified named_theorems CondRuleTC qualified named_theorems WhileRuleTC lemma Compl_Collect: "-(Collect b) = {x. \(b x)}" by blast ML_file \hoare_tac.ML\ end end diff --git a/src/HOL/Hoare/Pointer_Examples.thy b/src/HOL/Hoare/Pointer_Examples.thy --- a/src/HOL/Hoare/Pointer_Examples.thy +++ b/src/HOL/Hoare/Pointer_Examples.thy @@ -1,501 +1,505 @@ (* Title: HOL/Hoare/Pointer_Examples.thy Author: Tobias Nipkow Copyright 2002 TUM - -Examples of verifications of pointer programs. *) -theory Pointer_Examples imports HeapSyntax begin +section \Examples of verifications of pointer programs\ + +theory Pointer_Examples + imports HeapSyntax +begin axiomatization where unproven: "PROP A" -section "Verifications" -subsection "List reversal" +subsection "Verifications" + +subsubsection "List reversal" text "A short but unreadable proof:" lemma "VARS tl p q r {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}} WHILE p \ Null INV {\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {List tl q (rev Ps @ Qs)}" apply vcg_simp apply fastforce apply(fastforce intro:notin_List_update[THEN iffD2]) \ \explicit:\ \<^cancel>\apply clarify\ \<^cancel>\apply(rename_tac ps b qs)\ \<^cancel>\apply clarsimp\ \<^cancel>\apply(rename_tac ps')\ \<^cancel>\apply(fastforce intro:notin_List_update[THEN iffD2])\ done text\And now with ghost variables \<^term>\ps\ and \<^term>\qs\. Even ``more automatic''.\ lemma "VARS next p ps q qs r {List next p Ps \ List next q Qs \ set Ps \ set Qs = {} \ ps = Ps \ qs = Qs} WHILE p \ Null INV {List next p ps \ List next q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := p^.next; r^.next := q; q := r; qs := (hd ps) # qs; ps := tl ps OD {List next q (rev Ps @ Qs)}" apply vcg_simp apply fastforce done text "A longer readable version:" lemma "VARS tl p q r {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}} WHILE p \ Null INV {\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {List tl q (rev Ps @ Qs)}" proof vcg fix tl p q r assume "List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}" thus "\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs" by fastforce next fix tl p q r assume "(\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs) \ p \ Null" (is "(\ps qs. ?I ps qs) \ _") then obtain ps qs a where I: "?I ps qs \ p = Ref a" by fast then obtain ps' where "ps = a # ps'" by fastforce hence "List (tl(p \ q)) (p^.tl) ps' \ List (tl(p \ q)) p (a#qs) \ set ps' \ set (a#qs) = {} \ rev ps' @ (a#qs) = rev Ps @ Qs" using I by fastforce thus "\ps' qs'. List (tl(p \ q)) (p^.tl) ps' \ List (tl(p \ q)) p qs' \ set ps' \ set qs' = {} \ rev ps' @ qs' = rev Ps @ Qs" by fast next fix tl p q r assume "(\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs) \ \ p \ Null" thus "List tl q (rev Ps @ Qs)" by fastforce qed text\Finaly, the functional version. A bit more verbose, but automatic!\ lemma "VARS tl p q r {islist tl p \ islist tl q \ Ps = list tl p \ Qs = list tl q \ set Ps \ set Qs = {}} WHILE p \ Null INV {islist tl p \ islist tl q \ set(list tl p) \ set(list tl q) = {} \ rev(list tl p) @ (list tl q) = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {islist tl q \ list tl q = rev Ps @ Qs}" apply vcg_simp apply clarsimp apply clarsimp done -subsection "Searching in a list" +subsubsection "Searching in a list" text\What follows is a sequence of successively more intelligent proofs that a simple loop finds an element in a linked list. We start with a proof based on the \<^term>\List\ predicate. This means it only works for acyclic lists.\ lemma "VARS tl p {List tl p Ps \ X \ set Ps} WHILE p \ Null \ p \ Ref X INV {\ps. List tl p ps \ X \ set ps} DO p := p^.tl OD {p = Ref X}" apply vcg_simp apply blast apply clarsimp apply clarsimp done text\Using \<^term>\Path\ instead of \<^term>\List\ generalizes the correctness statement to cyclic lists as well:\ lemma "VARS tl p {Path tl p Ps X} WHILE p \ Null \ p \ X INV {\ps. Path tl p ps X} DO p := p^.tl OD {p = X}" apply vcg_simp apply blast apply fastforce apply clarsimp done text\Now it dawns on us that we do not need the list witness at all --- it suffices to talk about reachability, i.e.\ we can use relations directly. The first version uses a relation on \<^typ>\'a ref\:\ lemma "VARS tl p {(p,X) \ {(Ref x,tl x) |x. True}\<^sup>*} WHILE p \ Null \ p \ X INV {(p,X) \ {(Ref x,tl x) |x. True}\<^sup>*} DO p := p^.tl OD {p = X}" apply vcg_simp apply clarsimp apply(erule converse_rtranclE) apply simp apply(clarsimp elim:converse_rtranclE) apply(fast elim:converse_rtranclE) done text\Finally, a version based on a relation on type \<^typ>\'a\:\ lemma "VARS tl p {p \ Null \ (addr p,X) \ {(x,y). tl x = Ref y}\<^sup>*} WHILE p \ Null \ p \ Ref X INV {p \ Null \ (addr p,X) \ {(x,y). tl x = Ref y}\<^sup>*} DO p := p^.tl OD {p = Ref X}" apply vcg_simp apply clarsimp apply(erule converse_rtranclE) apply simp apply clarsimp apply clarsimp done -subsection "Splicing two lists" + +subsubsection "Splicing two lists" lemma "VARS tl p q pp qq {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {} \ size Qs \ size Ps} pp := p; WHILE q \ Null INV {\as bs qs. distinct as \ Path tl p as pp \ List tl pp bs \ List tl q qs \ set bs \ set qs = {} \ set as \ (set bs \ set qs) = {} \ size qs \ size bs \ splice Ps Qs = as @ splice bs qs} DO qq := q^.tl; q^.tl := pp^.tl; pp^.tl := q; pp := q^.tl; q := qq OD {List tl p (splice Ps Qs)}" apply vcg_simp apply(rule_tac x = "[]" in exI) apply fastforce apply clarsimp apply(rename_tac y bs qqs) apply(case_tac bs) apply simp apply clarsimp apply(rename_tac x bbs) apply(rule_tac x = "as @ [x,y]" in exI) apply simp apply(rule_tac x = "bbs" in exI) apply simp apply(rule_tac x = "qqs" in exI) apply simp apply (fastforce simp:List_app) done -subsection "Merging two lists" +subsubsection "Merging two lists" text"This is still a bit rough, especially the proof." definition cor :: "bool \ bool \ bool" where "cor P Q \ (if P then True else Q)" definition cand :: "bool \ bool \ bool" where "cand P Q \ (if P then Q else False)" fun merge :: "'a list * 'a list * ('a \ 'a \ bool) \ 'a list" where "merge(x#xs,y#ys,f) = (if f x y then x # merge(xs,y#ys,f) else y # merge(x#xs,ys,f))" | "merge(x#xs,[],f) = x # merge(xs,[],f)" | "merge([],y#ys,f) = y # merge([],ys,f)" | "merge([],[],f) = []" text\Simplifies the proof a little:\ lemma [simp]: "({} = insert a A \ B) = (a \ B & {} = A \ B)" by blast lemma [simp]: "({} = A \ insert b B) = (b \ A & {} = A \ B)" by blast lemma [simp]: "({} = A \ (B \ C)) = ({} = A \ B & {} = A \ C)" by blast lemma "VARS hd tl p q r s {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {} \ (p \ Null \ q \ Null)} IF cor (q = Null) (cand (p \ Null) (p^.hd \ q^.hd)) THEN r := p; p := p^.tl ELSE r := q; q := q^.tl FI; s := r; WHILE p \ Null \ q \ Null INV {\rs ps qs a. Path tl r rs s \ List tl p ps \ List tl q qs \ distinct(a # ps @ qs @ rs) \ s = Ref a \ merge(Ps,Qs,\x y. hd x \ hd y) = rs @ a # merge(ps,qs,\x y. hd x \ hd y) \ (tl a = p \ tl a = q)} DO IF cor (q = Null) (cand (p \ Null) (p^.hd \ q^.hd)) THEN s^.tl := p; p := p^.tl ELSE s^.tl := q; q := q^.tl FI; s := s^.tl OD {List tl r (merge(Ps,Qs,\x y. hd x \ hd y))}" apply vcg_simp apply (simp_all add: cand_def cor_def) apply (fastforce) apply clarsimp apply(rule conjI) apply clarsimp apply(rule conjI) apply (fastforce intro!:Path_snoc intro:Path_upd[THEN iffD2] notin_List_update[THEN iffD2] simp:eq_sym_conv) apply clarsimp apply(rule conjI) apply (clarsimp) apply(rule_tac x = "rs @ [a]" in exI) apply(clarsimp simp:eq_sym_conv) apply(rule_tac x = "bs" in exI) apply(clarsimp simp:eq_sym_conv) apply(rule_tac x = "ya#bsa" in exI) apply(simp) apply(clarsimp simp:eq_sym_conv) apply(rule_tac x = "rs @ [a]" in exI) apply(clarsimp simp:eq_sym_conv) apply(rule_tac x = "y#bs" in exI) apply(clarsimp simp:eq_sym_conv) apply(rule_tac x = "bsa" in exI) apply(simp) apply (fastforce intro!:Path_snoc intro:Path_upd[THEN iffD2] notin_List_update[THEN iffD2] simp:eq_sym_conv) apply(clarsimp simp add:List_app) done text\And now with ghost variables:\ lemma "VARS elem next p q r s ps qs rs a {List next p Ps \ List next q Qs \ set Ps \ set Qs = {} \ (p \ Null \ q \ Null) \ ps = Ps \ qs = Qs} IF cor (q = Null) (cand (p \ Null) (p^.elem \ q^.elem)) THEN r := p; p := p^.next; ps := tl ps ELSE r := q; q := q^.next; qs := tl qs FI; s := r; rs := []; a := addr s; WHILE p \ Null \ q \ Null INV {Path next r rs s \ List next p ps \ List next q qs \ distinct(a # ps @ qs @ rs) \ s = Ref a \ merge(Ps,Qs,\x y. elem x \ elem y) = rs @ a # merge(ps,qs,\x y. elem x \ elem y) \ (next a = p \ next a = q)} DO IF cor (q = Null) (cand (p \ Null) (p^.elem \ q^.elem)) THEN s^.next := p; p := p^.next; ps := tl ps ELSE s^.next := q; q := q^.next; qs := tl qs FI; rs := rs @ [a]; s := s^.next; a := addr s OD {List next r (merge(Ps,Qs,\x y. elem x \ elem y))}" apply vcg_simp apply (simp_all add: cand_def cor_def) apply (fastforce) apply clarsimp apply(rule conjI) apply(clarsimp) apply(rule conjI) apply(clarsimp simp:neq_commute) apply(clarsimp simp:neq_commute) apply(clarsimp simp:neq_commute) apply(clarsimp simp add:List_app) done text\The proof is a LOT simpler because it does not need instantiations anymore, but it is still not quite automatic, probably because of this wrong orientation business.\ text\More of the previous proof without ghost variables can be automated, but the runtime goes up drastically. In general it is usually more efficient to give the witness directly than to have it found by proof. Now we try a functional version of the abstraction relation \<^term>\Path\. Since the result is not that convincing, we do not prove any of the lemmas.\ axiomatization ispath :: "('a \ 'a ref) \ 'a ref \ 'a ref \ bool" and path :: "('a \ 'a ref) \ 'a ref \ 'a ref \ 'a list" text"First some basic lemmas:" lemma [simp]: "ispath f p p" by (rule unproven) lemma [simp]: "path f p p = []" by (rule unproven) lemma [simp]: "ispath f p q \ a \ set(path f p q) \ ispath (f(a := r)) p q" by (rule unproven) lemma [simp]: "ispath f p q \ a \ set(path f p q) \ path (f(a := r)) p q = path f p q" by (rule unproven) text"Some more specific lemmas needed by the example:" lemma [simp]: "ispath (f(a := q)) p (Ref a) \ ispath (f(a := q)) p q" by (rule unproven) lemma [simp]: "ispath (f(a := q)) p (Ref a) \ path (f(a := q)) p q = path (f(a := q)) p (Ref a) @ [a]" by (rule unproven) lemma [simp]: "ispath f p (Ref a) \ f a = Ref b \ b \ set (path f p (Ref a))" by (rule unproven) lemma [simp]: "ispath f p (Ref a) \ f a = Null \ islist f p" by (rule unproven) lemma [simp]: "ispath f p (Ref a) \ f a = Null \ list f p = path f p (Ref a) @ [a]" by (rule unproven) lemma [simp]: "islist f p \ distinct (list f p)" by (rule unproven) lemma "VARS hd tl p q r s {islist tl p \ Ps = list tl p \ islist tl q \ Qs = list tl q \ set Ps \ set Qs = {} \ (p \ Null \ q \ Null)} IF cor (q = Null) (cand (p \ Null) (p^.hd \ q^.hd)) THEN r := p; p := p^.tl ELSE r := q; q := q^.tl FI; s := r; WHILE p \ Null \ q \ Null INV {\rs ps qs a. ispath tl r s \ rs = path tl r s \ islist tl p \ ps = list tl p \ islist tl q \ qs = list tl q \ distinct(a # ps @ qs @ rs) \ s = Ref a \ merge(Ps,Qs,\x y. hd x \ hd y) = rs @ a # merge(ps,qs,\x y. hd x \ hd y) \ (tl a = p \ tl a = q)} DO IF cor (q = Null) (cand (p \ Null) (p^.hd \ q^.hd)) THEN s^.tl := p; p := p^.tl ELSE s^.tl := q; q := q^.tl FI; s := s^.tl OD {islist tl r & list tl r = (merge(Ps,Qs,\x y. hd x \ hd y))}" apply vcg_simp apply (simp_all add: cand_def cor_def) apply (fastforce) apply (fastforce simp: eq_sym_conv) apply(clarsimp) done text"The proof is automatic, but requires a numbet of special lemmas." -subsection "Cyclic list reversal" +subsubsection "Cyclic list reversal" text\We consider two algorithms for the reversal of circular lists. \ lemma circular_list_rev_I: "VARS next root p q tmp {root = Ref r \ distPath next root (r#Ps) root} p := root; q := root^.next; WHILE q \ root INV {\ ps qs. distPath next p ps root \ distPath next q qs root \ root = Ref r \ r \ set Ps \ set ps \ set qs = {} \ Ps = (rev ps) @ qs } DO tmp := q; q := q^.next; tmp^.next := p; p:=tmp OD; root^.next := p { root = Ref r \ distPath next root (r#rev Ps) root}" apply (simp only:distPath_def) apply vcg_simp apply (rule_tac x="[]" in exI) apply auto apply (drule (2) neq_dP) apply clarsimp apply(rule_tac x="a # ps" in exI) apply clarsimp done text\In the beginning, we are able to assert \<^term>\distPath next root as root\, with \<^term>\as\ set to \<^term>\[]\ or \<^term>\[r,a,b,c]\. Note that \<^term>\Path next root as root\ would additionally give us an infinite number of lists with the recurring sequence \<^term>\[r,a,b,c]\. The precondition states that there exists a non-empty non-repeating path \mbox{\<^term>\r # Ps\} from pointer \<^term>\root\ to itself, given that \<^term>\root\ points to location \<^term>\r\. Pointers \<^term>\p\ and \<^term>\q\ are then set to \<^term>\root\ and the successor of \<^term>\root\ respectively. If \<^term>\q = root\, we have circled the loop, otherwise we set the \<^term>\next\ pointer field of \<^term>\q\ to point to \<^term>\p\, and shift \<^term>\p\ and \<^term>\q\ one step forward. The invariant thus states that \<^term>\p\ and \<^term>\q\ point to two disjoint lists \<^term>\ps\ and \<^term>\qs\, such that \<^term>\Ps = rev ps @ qs\. After the loop terminates, one extra step is needed to close the loop. As expected, the postcondition states that the \<^term>\distPath\ from \<^term>\root\ to itself is now \<^term>\r # (rev Ps)\. It may come as a surprise to the reader that the simple algorithm for acyclic list reversal, with modified annotations, works for cyclic lists as well:\ lemma circular_list_rev_II: "VARS next p q tmp {p = Ref r \ distPath next p (r#Ps) p} q:=Null; WHILE p \ Null INV { ((q = Null) \ (\ps. distPath next p (ps) (Ref r) \ ps = r#Ps)) \ ((q \ Null) \ (\ps qs. distPath next q (qs) (Ref r) \ List next p ps \ set ps \ set qs = {} \ rev qs @ ps = Ps@[r])) \ \ (p = Null \ q = Null) } DO tmp := p; p := p^.next; tmp^.next := q; q:=tmp OD {q = Ref r \ distPath next q (r # rev Ps) q}" apply (simp only:distPath_def) apply vcg_simp apply clarsimp apply (case_tac "(q = Null)") apply (fastforce intro: Path_is_List) apply clarsimp apply (rule_tac x= "bs" in exI) apply (rule_tac x= "y # qs" in exI) apply clarsimp apply (auto simp:fun_upd_apply) done -subsection "Storage allocation" +subsubsection "Storage allocation" definition new :: "'a set \ 'a" where "new A = (SOME a. a \ A)" lemma new_notin: "\ ~finite(UNIV::'a set); finite(A::'a set); B \ A \ \ new (A) \ B" apply(unfold new_def) apply(rule someI2_ex) apply (fast intro:ex_new_if_finite) apply (fast) done lemma "~finite(UNIV::'a set) \ VARS xs elem next alloc p q {Xs = xs \ p = (Null::'a ref)} WHILE xs \ [] INV {islist next p \ set(list next p) \ set alloc \ map elem (rev(list next p)) @ xs = Xs} DO q := Ref(new(set alloc)); alloc := (addr q)#alloc; q^.next := p; q^.elem := hd xs; xs := tl xs; p := q OD {islist next p \ map elem (rev(list next p)) = Xs}" apply vcg_simp apply (clarsimp simp: subset_insert_iff neq_Nil_conv fun_upd_apply new_notin) done end diff --git a/src/HOL/Hoare/Pointer_ExamplesAbort.thy b/src/HOL/Hoare/Pointer_ExamplesAbort.thy --- a/src/HOL/Hoare/Pointer_ExamplesAbort.thy +++ b/src/HOL/Hoare/Pointer_ExamplesAbort.thy @@ -1,28 +1,30 @@ (* Title: HOL/Hoare/Pointer_ExamplesAbort.thy Author: Tobias Nipkow Copyright 2002 TUM - -Examples of verifications of pointer programs *) -theory Pointer_ExamplesAbort imports HeapSyntaxAbort begin +section \Examples of verifications of pointer programs\ -section "Verifications" +theory Pointer_ExamplesAbort + imports HeapSyntaxAbort +begin -subsection "List reversal" +subsection "Verifications" + +subsubsection "List reversal" text "Interestingly, this proof is the same as for the unguarded program:" lemma "VARS tl p q r {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}} WHILE p \ Null INV {\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; (p \ Null \ p := p^.tl); r^.tl := q; q := r OD {List tl q (rev Ps @ Qs)}" apply vcg_simp apply fastforce apply(fastforce intro:notin_List_update[THEN iffD2]) done end diff --git a/src/HOL/Hoare/Pointers0.thy b/src/HOL/Hoare/Pointers0.thy --- a/src/HOL/Hoare/Pointers0.thy +++ b/src/HOL/Hoare/Pointers0.thy @@ -1,431 +1,434 @@ (* Title: HOL/Hoare/Pointers0.thy Author: Tobias Nipkow Copyright 2002 TUM This is like Pointers.thy, but instead of a type constructor 'a ref that adjoins Null to a type, Null is simply a distinguished element of the address type. This avoids the Ref constructor and thus simplifies specifications (a bit). However, the proofs don't seem to get simpler - in fact in some case they appear to get (a bit) more complicated. *) -theory Pointers0 imports Hoare_Logic begin +section \Alternative pointers\ + +theory Pointers0 + imports Hoare_Logic +begin subsection "References" class ref = fixes Null :: 'a subsection "Field access and update" syntax "_fassign" :: "'a::ref => id => 'v => 's com" ("(2_^._ :=/ _)" [70,1000,65] 61) "_faccess" :: "'a::ref => ('a::ref \ 'v) => 'v" ("_^._" [65,1000] 65) translations "p^.f := e" => "f := CONST fun_upd f p e" "p^.f" => "f p" text "An example due to Suzuki:" lemma "VARS v n {distinct[w,x,y,z]} w^.v := (1::int); w^.n := x; x^.v := 2; x^.n := y; y^.v := 3; y^.n := z; z^.v := 4; x^.n := z {w^.n^.n^.v = 4}" by vcg_simp -section "The heap" +subsection "The heap" -subsection "Paths in the heap" +subsubsection "Paths in the heap" primrec Path :: "('a::ref \ 'a) \ 'a \ 'a list \ 'a \ bool" where "Path h x [] y = (x = y)" | "Path h x (a#as) y = (x \ Null \ x = a \ Path h (h a) as y)" lemma [iff]: "Path h Null xs y = (xs = [] \ y = Null)" apply(case_tac xs) apply fastforce apply fastforce done lemma [simp]: "a \ Null \ Path h a as z = (as = [] \ z = a \ (\bs. as = a#bs \ Path h (h a) bs z))" apply(case_tac as) apply fastforce apply fastforce done lemma [simp]: "\x. Path f x (as@bs) z = (\y. Path f x as y \ Path f y bs z)" by(induct as, simp+) lemma [simp]: "\x. u \ set as \ Path (f(u := v)) x as y = Path f x as y" by(induct as, simp, simp add:eq_sym_conv) -subsection "Lists on the heap" +subsubsection "Lists on the heap" -subsubsection "Relational abstraction" +paragraph "Relational abstraction" definition List :: "('a::ref \ 'a) \ 'a \ 'a list \ bool" where "List h x as = Path h x as Null" lemma [simp]: "List h x [] = (x = Null)" by(simp add:List_def) lemma [simp]: "List h x (a#as) = (x \ Null \ x = a \ List h (h a) as)" by(simp add:List_def) lemma [simp]: "List h Null as = (as = [])" by(case_tac as, simp_all) lemma List_Ref[simp]: "a \ Null \ List h a as = (\bs. as = a#bs \ List h (h a) bs)" by(case_tac as, simp_all, fast) theorem notin_List_update[simp]: "\x. a \ set as \ List (h(a := y)) x as = List h x as" apply(induct as) apply simp apply(clarsimp simp add:fun_upd_apply) done declare fun_upd_apply[simp del]fun_upd_same[simp] fun_upd_other[simp] lemma List_unique: "\x bs. List h x as \ List h x bs \ as = bs" by(induct as, simp, clarsimp) lemma List_unique1: "List h p as \ \!as. List h p as" by(blast intro:List_unique) lemma List_app: "\x. List h x (as@bs) = (\y. Path h x as y \ List h y bs)" by(induct as, simp, clarsimp) lemma List_hd_not_in_tl[simp]: "List h (h a) as \ a \ set as" apply (clarsimp simp add:in_set_conv_decomp) apply(frule List_app[THEN iffD1]) apply(fastforce dest: List_unique) done lemma List_distinct[simp]: "\x. List h x as \ distinct as" apply(induct as, simp) apply(fastforce dest:List_hd_not_in_tl) done -subsection "Functional abstraction" +subsubsection "Functional abstraction" definition islist :: "('a::ref \ 'a) \ 'a \ bool" where "islist h p \ (\as. List h p as)" definition list :: "('a::ref \ 'a) \ 'a \ 'a list" where "list h p = (SOME as. List h p as)" lemma List_conv_islist_list: "List h p as = (islist h p \ as = list h p)" apply(simp add:islist_def list_def) apply(rule iffI) apply(rule conjI) apply blast apply(subst some1_equality) apply(erule List_unique1) apply assumption apply(rule refl) apply simp apply(rule someI_ex) apply fast done lemma [simp]: "islist h Null" by(simp add:islist_def) lemma [simp]: "a \ Null \ islist h a = islist h (h a)" by(simp add:islist_def) lemma [simp]: "list h Null = []" by(simp add:list_def) lemma list_Ref_conv[simp]: "\ a \ Null; islist h (h a) \ \ list h a = a # list h (h a)" apply(insert List_Ref[of _ h]) apply(fastforce simp:List_conv_islist_list) done lemma [simp]: "islist h (h a) \ a \ set(list h (h a))" apply(insert List_hd_not_in_tl[of h]) apply(simp add:List_conv_islist_list) done lemma list_upd_conv[simp]: "islist h p \ y \ set(list h p) \ list (h(y := q)) p = list h p" apply(drule notin_List_update[of _ _ h q p]) apply(simp add:List_conv_islist_list) done lemma islist_upd[simp]: "islist h p \ y \ set(list h p) \ islist (h(y := q)) p" apply(frule notin_List_update[of _ _ h q p]) apply(simp add:List_conv_islist_list) done -section "Verifications" +subsection "Verifications" -subsection "List reversal" +subsubsection "List reversal" text "A short but unreadable proof:" lemma "VARS tl p q r {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}} WHILE p \ Null INV {\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {List tl q (rev Ps @ Qs)}" apply vcg_simp apply fastforce apply(fastforce intro:notin_List_update[THEN iffD2]) \ \explicit:\ \<^cancel>\apply clarify\ \<^cancel>\apply(rename_tac ps qs)\ \<^cancel>\apply clarsimp\ \<^cancel>\apply(rename_tac ps')\ \<^cancel>\apply(rule_tac x = ps' in exI)\ \<^cancel>\apply simp\ \<^cancel>\apply(rule_tac x = "p#qs" in exI)\ \<^cancel>\apply simp\ done text "A longer readable version:" lemma "VARS tl p q r {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}} WHILE p \ Null INV {\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {List tl q (rev Ps @ Qs)}" proof vcg fix tl p q r assume "List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {}" thus "\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs" by fastforce next fix tl p q r assume "(\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs) \ p \ Null" (is "(\ps qs. ?I ps qs) \ _") then obtain ps qs where I: "?I ps qs \ p \ Null" by fast then obtain ps' where "ps = p # ps'" by fastforce hence "List (tl(p := q)) (p^.tl) ps' \ List (tl(p := q)) p (p#qs) \ set ps' \ set (p#qs) = {} \ rev ps' @ (p#qs) = rev Ps @ Qs" using I by fastforce thus "\ps' qs'. List (tl(p := q)) (p^.tl) ps' \ List (tl(p := q)) p qs' \ set ps' \ set qs' = {} \ rev ps' @ qs' = rev Ps @ Qs" by fast next fix tl p q r assume "(\ps qs. List tl p ps \ List tl q qs \ set ps \ set qs = {} \ rev ps @ qs = rev Ps @ Qs) \ \ p \ Null" thus "List tl q (rev Ps @ Qs)" by fastforce qed text\Finaly, the functional version. A bit more verbose, but automatic!\ lemma "VARS tl p q r {islist tl p \ islist tl q \ Ps = list tl p \ Qs = list tl q \ set Ps \ set Qs = {}} WHILE p \ Null INV {islist tl p \ islist tl q \ set(list tl p) \ set(list tl q) = {} \ rev(list tl p) @ (list tl q) = rev Ps @ Qs} DO r := p; p := p^.tl; r^.tl := q; q := r OD {islist tl q \ list tl q = rev Ps @ Qs}" apply vcg_simp apply clarsimp apply clarsimp done -subsection "Searching in a list" +subsubsection "Searching in a list" text\What follows is a sequence of successively more intelligent proofs that a simple loop finds an element in a linked list. We start with a proof based on the \<^term>\List\ predicate. This means it only works for acyclic lists.\ lemma "VARS tl p {List tl p Ps \ X \ set Ps} WHILE p \ Null \ p \ X INV {p \ Null \ (\ps. List tl p ps \ X \ set ps)} DO p := p^.tl OD {p = X}" apply vcg_simp apply(case_tac "p = Null") apply clarsimp apply fastforce apply clarsimp apply fastforce apply clarsimp done text\Using \<^term>\Path\ instead of \<^term>\List\ generalizes the correctness statement to cyclic lists as well:\ lemma "VARS tl p {Path tl p Ps X} WHILE p \ Null \ p \ X INV {\ps. Path tl p ps X} DO p := p^.tl OD {p = X}" apply vcg_simp apply blast apply fastforce apply clarsimp done text\Now it dawns on us that we do not need the list witness at all --- it suffices to talk about reachability, i.e.\ we can use relations directly.\ lemma "VARS tl p {(p,X) \ {(x,y). y = tl x & x \ Null}\<^sup>*} WHILE p \ Null \ p \ X INV {(p,X) \ {(x,y). y = tl x & x \ Null}\<^sup>*} DO p := p^.tl OD {p = X}" apply vcg_simp apply clarsimp apply(erule converse_rtranclE) apply simp apply(simp) apply(fastforce elim:converse_rtranclE) done -subsection "Merging two lists" +subsubsection "Merging two lists" text"This is still a bit rough, especially the proof." fun merge :: "'a list * 'a list * ('a \ 'a \ bool) \ 'a list" where "merge(x#xs,y#ys,f) = (if f x y then x # merge(xs,y#ys,f) else y # merge(x#xs,ys,f))" | "merge(x#xs,[],f) = x # merge(xs,[],f)" | "merge([],y#ys,f) = y # merge([],ys,f)" | "merge([],[],f) = []" lemma imp_disjCL: "(P|Q \ R) = ((P \ R) \ (~P \ Q \ R))" by blast declare disj_not1[simp del] imp_disjL[simp del] imp_disjCL[simp] lemma "VARS hd tl p q r s {List tl p Ps \ List tl q Qs \ set Ps \ set Qs = {} \ (p \ Null \ q \ Null)} IF if q = Null then True else p ~= Null & p^.hd \ q^.hd THEN r := p; p := p^.tl ELSE r := q; q := q^.tl FI; s := r; WHILE p \ Null \ q \ Null INV {\rs ps qs. Path tl r rs s \ List tl p ps \ List tl q qs \ distinct(s # ps @ qs @ rs) \ s \ Null \ merge(Ps,Qs,\x y. hd x \ hd y) = rs @ s # merge(ps,qs,\x y. hd x \ hd y) \ (tl s = p \ tl s = q)} DO IF if q = Null then True else p \ Null \ p^.hd \ q^.hd THEN s^.tl := p; p := p^.tl ELSE s^.tl := q; q := q^.tl FI; s := s^.tl OD {List tl r (merge(Ps,Qs,\x y. hd x \ hd y))}" apply vcg_simp apply (fastforce) apply clarsimp apply(rule conjI) apply clarsimp apply(simp add:eq_sym_conv) apply(rule_tac x = "rs @ [s]" in exI) apply simp apply(rule_tac x = "bs" in exI) apply (fastforce simp:eq_sym_conv) apply clarsimp apply(rule conjI) apply clarsimp apply(rule_tac x = "rs @ [s]" in exI) apply simp apply(rule_tac x = "bsa" in exI) apply(rule conjI) apply (simp add:eq_sym_conv) apply(rule exI) apply(rule conjI) apply(rule_tac x = bs in exI) apply(rule conjI) apply(rule refl) apply (simp add:eq_sym_conv) apply (simp add:eq_sym_conv) apply(rule conjI) apply clarsimp apply(rule_tac x = "rs @ [s]" in exI) apply simp apply(rule_tac x = bs in exI) apply (simp add:eq_sym_conv) apply clarsimp apply(rule_tac x = "rs @ [s]" in exI) apply (simp add:eq_sym_conv) apply(rule exI) apply(rule conjI) apply(rule_tac x = bsa in exI) apply(rule conjI) apply(rule refl) apply (simp add:eq_sym_conv) apply(rule_tac x = bs in exI) apply (simp add:eq_sym_conv) apply(clarsimp simp add:List_app) done (* TODO: merging with islist/list instead of List: an improvement? needs (is)path, which is not so easy to prove either. *) -subsection "Storage allocation" +subsubsection "Storage allocation" definition new :: "'a set \ 'a::ref" where "new A = (SOME a. a \ A & a \ Null)" lemma new_notin: "\ ~finite(UNIV::('a::ref)set); finite(A::'a set); B \ A \ \ new (A) \ B & new A \ Null" apply(unfold new_def) apply(rule someI2_ex) apply (fast dest:ex_new_if_finite[of "insert Null A"]) apply (fast) done lemma "~finite(UNIV::('a::ref)set) \ VARS xs elem next alloc p q {Xs = xs \ p = (Null::'a)} WHILE xs \ [] INV {islist next p \ set(list next p) \ set alloc \ map elem (rev(list next p)) @ xs = Xs} DO q := new(set alloc); alloc := q#alloc; q^.next := p; q^.elem := hd xs; xs := tl xs; p := q OD {islist next p \ map elem (rev(list next p)) = Xs}" apply vcg_simp apply (clarsimp simp: subset_insert_iff neq_Nil_conv fun_upd_apply new_notin) done - end diff --git a/src/HOL/Hoare/SchorrWaite.thy b/src/HOL/Hoare/SchorrWaite.thy --- a/src/HOL/Hoare/SchorrWaite.thy +++ b/src/HOL/Hoare/SchorrWaite.thy @@ -1,576 +1,575 @@ (* Title: HOL/Hoare/SchorrWaite.thy Author: Farhad Mehta Copyright 2003 TUM - -Proof of the Schorr-Waite graph marking algorithm. *) +section \Proof of the Schorr-Waite graph marking algorithm\ -theory SchorrWaite imports HeapSyntax begin +theory SchorrWaite + imports HeapSyntax +begin -section \Machinery for the Schorr-Waite proof\ +subsection \Machinery for the Schorr-Waite proof\ definition \ \Relations induced by a mapping\ rel :: "('a \ 'a ref) \ ('a \ 'a) set" where "rel m = {(x,y). m x = Ref y}" definition relS :: "('a \ 'a ref) set \ ('a \ 'a) set" where "relS M = (\m \ M. rel m)" definition addrs :: "'a ref set \ 'a set" where "addrs P = {a. Ref a \ P}" definition reachable :: "('a \ 'a) set \ 'a ref set \ 'a set" where "reachable r P = (r\<^sup>* `` addrs P)" lemmas rel_defs = relS_def rel_def text \Rewrite rules for relations induced by a mapping\ lemma self_reachable: "b \ B \ b \ R\<^sup>* `` B" apply blast done lemma oneStep_reachable: "b \ R``B \ b \ R\<^sup>* `` B" apply blast done lemma still_reachable: "\B\Ra\<^sup>*``A; \ (x,y) \ Rb-Ra. y\ (Ra\<^sup>*``A)\ \ Rb\<^sup>* `` B \ Ra\<^sup>* `` A " apply (clarsimp simp only:Image_iff) apply (erule rtrancl_induct) apply blast apply (subgoal_tac "(y, z) \ Ra\(Rb-Ra)") apply (erule UnE) apply (auto intro:rtrancl_into_rtrancl) apply blast done lemma still_reachable_eq: "\ A\Rb\<^sup>*``B; B\Ra\<^sup>*``A; \ (x,y) \ Ra-Rb. y \(Rb\<^sup>*``B); \ (x,y) \ Rb-Ra. y\ (Ra\<^sup>*``A)\ \ Ra\<^sup>*``A = Rb\<^sup>*``B " apply (rule equalityI) apply (erule still_reachable ,assumption)+ done lemma reachable_null: "reachable mS {Null} = {}" apply (simp add: reachable_def addrs_def) done lemma reachable_empty: "reachable mS {} = {}" apply (simp add: reachable_def addrs_def) done lemma reachable_union: "(reachable mS aS \ reachable mS bS) = reachable mS (aS \ bS)" apply (simp add: reachable_def rel_defs addrs_def) apply blast done lemma reachable_union_sym: "reachable r (insert a aS) = (r\<^sup>* `` addrs {a}) \ reachable r aS" apply (simp add: reachable_def rel_defs addrs_def) apply blast done lemma rel_upd1: "(a,b) \ rel (r(q:=t)) \ (a,b) \ rel r \ a=q" apply (rule classical) apply (simp add:rel_defs fun_upd_apply) done lemma rel_upd2: "(a,b) \ rel r \ (a,b) \ rel (r(q:=t)) \ a=q" apply (rule classical) apply (simp add:rel_defs fun_upd_apply) done definition \ \Restriction of a relation\ restr ::"('a \ 'a) set \ ('a \ bool) \ ('a \ 'a) set" ("(_/ | _)" [50, 51] 50) where "restr r m = {(x,y). (x,y) \ r \ \ m x}" text \Rewrite rules for the restriction of a relation\ lemma restr_identity[simp]: " (\x. \ m x) \ (R |m) = R" by (auto simp add:restr_def) lemma restr_rtrancl[simp]: " \m l\ \ (R | m)\<^sup>* `` {l} = {l}" by (auto simp add:restr_def elim:converse_rtranclE) lemma [simp]: " \m l\ \ (l,x) \ (R | m)\<^sup>* = (l=x)" by (auto simp add:restr_def elim:converse_rtranclE) lemma restr_upd: "((rel (r (q := t)))|(m(q := True))) = ((rel (r))|(m(q := True))) " apply (auto simp:restr_def rel_def fun_upd_apply) apply (rename_tac a b) apply (case_tac "a=q") apply auto done lemma restr_un: "((r \ s)|m) = (r|m) \ (s|m)" by (auto simp add:restr_def) lemma rel_upd3: "(a, b) \ (r|(m(q := t))) \ (a,b) \ (r|m) \ a = q " apply (rule classical) apply (simp add:restr_def fun_upd_apply) done definition \ \A short form for the stack mapping function for List\ S :: "('a \ bool) \ ('a \ 'a ref) \ ('a \ 'a ref) \ ('a \ 'a ref)" where "S c l r = (\x. if c x then r x else l x)" text \Rewrite rules for Lists using S as their mapping\ lemma [rule_format,simp]: "\p. a \ set stack \ List (S c l r) p stack = List (S (c(a:=x)) (l(a:=y)) (r(a:=z))) p stack" apply(induct_tac stack) apply(simp add:fun_upd_apply S_def)+ done lemma [rule_format,simp]: "\p. a \ set stack \ List (S c l (r(a:=z))) p stack = List (S c l r) p stack" apply(induct_tac stack) apply(simp add:fun_upd_apply S_def)+ done lemma [rule_format,simp]: "\p. a \ set stack \ List (S c (l(a:=z)) r) p stack = List (S c l r) p stack" apply(induct_tac stack) apply(simp add:fun_upd_apply S_def)+ done lemma [rule_format,simp]: "\p. a \ set stack \ List (S (c(a:=z)) l r) p stack = List (S c l r) p stack" apply(induct_tac stack) apply(simp add:fun_upd_apply S_def)+ done primrec \ \Recursive definition of what is means for a the graph/stack structure to be reconstructible\ stkOk :: "('a \ bool) \ ('a \ 'a ref) \ ('a \ 'a ref) \ ('a \ 'a ref) \ ('a \ 'a ref) \ 'a ref \'a list \ bool" where stkOk_nil: "stkOk c l r iL iR t [] = True" | stkOk_cons: "stkOk c l r iL iR t (p#stk) = (stkOk c l r iL iR (Ref p) (stk) \ iL p = (if c p then l p else t) \ iR p = (if c p then t else r p))" text \Rewrite rules for stkOk\ lemma [simp]: "\t. \ x \ set xs; Ref x\t \ \ stkOk (c(x := f)) l r iL iR t xs = stkOk c l r iL iR t xs" apply (induct xs) apply (auto simp:eq_sym_conv) done lemma [simp]: "\t. \ x \ set xs; Ref x\t \ \ stkOk c (l(x := g)) r iL iR t xs = stkOk c l r iL iR t xs" apply (induct xs) apply (auto simp:eq_sym_conv) done lemma [simp]: "\t. \ x \ set xs; Ref x\t \ \ stkOk c l (r(x := g)) iL iR t xs = stkOk c l r iL iR t xs" apply (induct xs) apply (auto simp:eq_sym_conv) done lemma stkOk_r_rewrite [simp]: "\x. x \ set xs \ stkOk c l (r(x := g)) iL iR (Ref x) xs = stkOk c l r iL iR (Ref x) xs" apply (induct xs) apply (auto simp:eq_sym_conv) done lemma [simp]: "\x. x \ set xs \ stkOk c (l(x := g)) r iL iR (Ref x) xs = stkOk c l r iL iR (Ref x) xs" apply (induct xs) apply (auto simp:eq_sym_conv) done lemma [simp]: "\x. x \ set xs \ stkOk (c(x := g)) l r iL iR (Ref x) xs = stkOk c l r iL iR (Ref x) xs" apply (induct xs) apply (auto simp:eq_sym_conv) done -section\The Schorr-Waite algorithm\ - +subsection \The Schorr-Waite algorithm\ theorem SchorrWaiteAlgorithm: "VARS c m l r t p q root {R = reachable (relS {l, r}) {root} \ (\x. \ m x) \ iR = r \ iL = l} t := root; p := Null; WHILE p \ Null \ t \ Null \ \ t^.m INV {\stack. List (S c l r) p stack \ \ \\i1\\ (\x \ set stack. m x) \ \ \\i2\\ R = reachable (relS{l, r}) {t,p} \ \ \\i3\\ (\x. x \ R \ \m x \ \ \\i4\\ x \ reachable (relS{l,r}|m) ({t}\set(map r stack))) \ (\x. m x \ x \ R) \ \ \\i5\\ (\x. x \ set stack \ r x = iR x \ l x = iL x) \ \ \\i6\\ (stkOk c l r iL iR t stack) \ \\i7\\} DO IF t = Null \ t^.m THEN IF p^.c THEN q := t; t := p; p := p^.r; t^.r := q \ \\pop\\ ELSE q := t; t := p^.r; p^.r := p^.l; \ \\swing\\ p^.l := q; p^.c := True FI ELSE q := p; p := t; t := t^.l; p^.l := q; \ \\push\\ p^.m := True; p^.c := False FI OD {(\x. (x \ R) = m x) \ (r = iR \ l = iL) }" (is "VARS c m l r t p q root {?Pre c m l r root} (?c1; ?c2; ?c3) {?Post c m l r}") proof (vcg) let "While {(c, m, l, r, t, p, q, root). ?whileB m t p} {(c, m, l, r, t, p, q, root). ?inv c m l r t p} _ ?body" = ?c3 { fix c m l r t p q root assume "?Pre c m l r root" thus "?inv c m l r root Null" by (auto simp add: reachable_def addrs_def) next fix c m l r t p q let "\stack. ?Inv stack" = "?inv c m l r t p" assume a: "?inv c m l r t p \ \(p \ Null \ t \ Null \ \ t^.m)" then obtain stack where inv: "?Inv stack" by blast from a have pNull: "p = Null" and tDisj: "t=Null \ (t\Null \ t^.m )" by auto let "?I1 \ _ \ _ \ ?I4 \ ?I5 \ ?I6 \ _" = "?Inv stack" from inv have i1: "?I1" and i4: "?I4" and i5: "?I5" and i6: "?I6" by simp+ from pNull i1 have stackEmpty: "stack = []" by simp from tDisj i4 have RisMarked[rule_format]: "\x. x \ R \ m x" by(auto simp: reachable_def addrs_def stackEmpty) from i5 i6 show "(\x.(x \ R) = m x) \ r = iR \ l = iL" by(auto simp: stackEmpty fun_eq_iff intro:RisMarked) next fix c m l r t p q root let "\stack. ?Inv stack" = "?inv c m l r t p" let "\stack. ?popInv stack" = "?inv c m l (r(p \ t)) p (p^.r)" let "\stack. ?swInv stack" = "?inv (c(p \ True)) m (l(p \ t)) (r(p \ p^.l)) (p^.r) p" let "\stack. ?puInv stack" = "?inv (c(t \ False)) (m(t \ True)) (l(t \ p)) r (t^.l) t" let "?ifB1" = "(t = Null \ t^.m)" let "?ifB2" = "p^.c" assume "(\stack.?Inv stack) \ (p \ Null \ t \ Null \ \ t^.m)" (is "_ \ ?whileB") then obtain stack where inv: "?Inv stack" and whileB: "?whileB" by blast let "?I1 \ ?I2 \ ?I3 \ ?I4 \ ?I5 \ ?I6 \ ?I7" = "?Inv stack" from inv have i1: "?I1" and i2: "?I2" and i3: "?I3" and i4: "?I4" and i5: "?I5" and i6: "?I6" and i7: "?I7" by simp+ have stackDist: "distinct (stack)" using i1 by (rule List_distinct) show "(?ifB1 \ (?ifB2 \ (\stack.?popInv stack)) \ (\?ifB2 \ (\stack.?swInv stack)) ) \ (\?ifB1 \ (\stack.?puInv stack))" proof - { assume ifB1: "t = Null \ t^.m" and ifB2: "p^.c" from ifB1 whileB have pNotNull: "p \ Null" by auto then obtain addr_p where addr_p_eq: "p = Ref addr_p" by auto with i1 obtain stack_tl where stack_eq: "stack = (addr p) # stack_tl" by auto with i2 have m_addr_p: "p^.m" by auto have stackDist: "distinct (stack)" using i1 by (rule List_distinct) from stack_eq stackDist have p_notin_stack_tl: "addr p \ set stack_tl" by simp let "?poI1\ ?poI2\ ?poI3\ ?poI4\ ?poI5\ ?poI6\ ?poI7" = "?popInv stack_tl" have "?popInv stack_tl" proof - \ \List property is maintained:\ from i1 p_notin_stack_tl ifB2 have poI1: "List (S c l (r(p \ t))) (p^.r) stack_tl" by(simp add: addr_p_eq stack_eq, simp add: S_def) moreover \ \Everything on the stack is marked:\ from i2 have poI2: "\ x \ set stack_tl. m x" by (simp add:stack_eq) moreover \ \Everything is still reachable:\ let "(R = reachable ?Ra ?A)" = "?I3" let "?Rb" = "(relS {l, r(p \ t)})" let "?B" = "{p, p^.r}" \ \Our goal is \R = reachable ?Rb ?B\.\ have "?Ra\<^sup>* `` addrs ?A = ?Rb\<^sup>* `` addrs ?B" (is "?L = ?R") proof show "?L \ ?R" proof (rule still_reachable) show "addrs ?A \ ?Rb\<^sup>* `` addrs ?B" by(fastforce simp:addrs_def relS_def rel_def addr_p_eq intro:oneStep_reachable Image_iff[THEN iffD2]) show "\(x,y) \ ?Ra-?Rb. y \ (?Rb\<^sup>* `` addrs ?B)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def dest:rel_upd1) qed show "?R \ ?L" proof (rule still_reachable) show "addrs ?B \ ?Ra\<^sup>* `` addrs ?A" by(fastforce simp:addrs_def rel_defs addr_p_eq intro:oneStep_reachable Image_iff[THEN iffD2]) next show "\(x, y)\?Rb-?Ra. y\(?Ra\<^sup>*``addrs ?A)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def dest:rel_upd2) qed qed with i3 have poI3: "R = reachable ?Rb ?B" by (simp add:reachable_def) moreover \ \If it is reachable and not marked, it is still reachable using...\ let "\x. x \ R \ \ m x \ x \ reachable ?Ra ?A" = ?I4 let "?Rb" = "relS {l, r(p \ t)} | m" let "?B" = "{p} \ set (map (r(p \ t)) stack_tl)" \ \Our goal is \\x. x \ R \ \ m x \ x \ reachable ?Rb ?B\.\ let ?T = "{t, p^.r}" have "?Ra\<^sup>* `` addrs ?A \ ?Rb\<^sup>* `` (addrs ?B \ addrs ?T)" proof (rule still_reachable) have rewrite: "\s\set stack_tl. (r(p \ t)) s = r s" by (auto simp add:p_notin_stack_tl intro:fun_upd_other) show "addrs ?A \ ?Rb\<^sup>* `` (addrs ?B \ addrs ?T)" by (fastforce cong:map_cong simp:stack_eq addrs_def rewrite intro:self_reachable) show "\(x, y)\?Ra-?Rb. y\(?Rb\<^sup>*``(addrs ?B \ addrs ?T))" by (clarsimp simp:restr_def relS_def) (fastforce simp add:rel_def Image_iff addrs_def dest:rel_upd1) qed \ \We now bring a term from the right to the left of the subset relation.\ hence subset: "?Ra\<^sup>* `` addrs ?A - ?Rb\<^sup>* `` addrs ?T \ ?Rb\<^sup>* `` addrs ?B" by blast have poI4: "\x. x \ R \ \ m x \ x \ reachable ?Rb ?B" proof (rule allI, rule impI) fix x assume a: "x \ R \ \ m x" \ \First, a disjunction on \<^term>\p^.r\ used later in the proof\ have pDisj:"p^.r = Null \ (p^.r \ Null \ p^.r^.m)" using poI1 poI2 by auto \ \\<^term>\x\ belongs to the left hand side of @{thm[source] subset}:\ have incl: "x \ ?Ra\<^sup>*``addrs ?A" using a i4 by (simp only:reachable_def, clarsimp) have excl: "x \ ?Rb\<^sup>*`` addrs ?T" using pDisj ifB1 a by (auto simp add:addrs_def) \ \And therefore also belongs to the right hand side of @{thm[source]subset},\ \ \which corresponds to our goal.\ from incl excl subset show "x \ reachable ?Rb ?B" by (auto simp add:reachable_def) qed moreover \ \If it is marked, then it is reachable\ from i5 have poI5: "\x. m x \ x \ R" . moreover \ \If it is not on the stack, then its \<^term>\l\ and \<^term>\r\ fields are unchanged\ from i7 i6 ifB2 have poI6: "\x. x \ set stack_tl \ (r(p \ t)) x = iR x \ l x = iL x" by(auto simp: addr_p_eq stack_eq fun_upd_apply) moreover \ \If it is on the stack, then its \<^term>\l\ and \<^term>\r\ fields can be reconstructed\ from p_notin_stack_tl i7 have poI7: "stkOk c l (r(p \ t)) iL iR p stack_tl" by (clarsimp simp:stack_eq addr_p_eq) ultimately show "?popInv stack_tl" by simp qed hence "\stack. ?popInv stack" .. } moreover \ \Proofs of the Swing and Push arm follow.\ \ \Since they are in principle simmilar to the Pop arm proof,\ \ \we show fewer comments and use frequent pattern matching.\ { \ \Swing arm\ assume ifB1: "?ifB1" and nifB2: "\?ifB2" from ifB1 whileB have pNotNull: "p \ Null" by clarsimp then obtain addr_p where addr_p_eq: "p = Ref addr_p" by clarsimp with i1 obtain stack_tl where stack_eq: "stack = (addr p) # stack_tl" by clarsimp with i2 have m_addr_p: "p^.m" by clarsimp from stack_eq stackDist have p_notin_stack_tl: "(addr p) \ set stack_tl" by simp let "?swI1\?swI2\?swI3\?swI4\?swI5\?swI6\?swI7" = "?swInv stack" have "?swInv stack" proof - \ \List property is maintained:\ from i1 p_notin_stack_tl nifB2 have swI1: "?swI1" by (simp add:addr_p_eq stack_eq, simp add:S_def) moreover \ \Everything on the stack is marked:\ from i2 have swI2: "?swI2" . moreover \ \Everything is still reachable:\ let "R = reachable ?Ra ?A" = "?I3" let "R = reachable ?Rb ?B" = "?swI3" have "?Ra\<^sup>* `` addrs ?A = ?Rb\<^sup>* `` addrs ?B" proof (rule still_reachable_eq) show "addrs ?A \ ?Rb\<^sup>* `` addrs ?B" by(fastforce simp:addrs_def rel_defs addr_p_eq intro:oneStep_reachable Image_iff[THEN iffD2]) next show "addrs ?B \ ?Ra\<^sup>* `` addrs ?A" by(fastforce simp:addrs_def rel_defs addr_p_eq intro:oneStep_reachable Image_iff[THEN iffD2]) next show "\(x, y)\?Ra-?Rb. y\(?Rb\<^sup>*``addrs ?B)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def fun_upd_apply dest:rel_upd1) next show "\(x, y)\?Rb-?Ra. y\(?Ra\<^sup>*``addrs ?A)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def fun_upd_apply dest:rel_upd2) qed with i3 have swI3: "?swI3" by (simp add:reachable_def) moreover \ \If it is reachable and not marked, it is still reachable using...\ let "\x. x \ R \ \ m x \ x \ reachable ?Ra ?A" = ?I4 let "\x. x \ R \ \ m x \ x \ reachable ?Rb ?B" = ?swI4 let ?T = "{t}" have "?Ra\<^sup>*``addrs ?A \ ?Rb\<^sup>*``(addrs ?B \ addrs ?T)" proof (rule still_reachable) have rewrite: "(\s\set stack_tl. (r(addr p := l(addr p))) s = r s)" by (auto simp add:p_notin_stack_tl intro:fun_upd_other) show "addrs ?A \ ?Rb\<^sup>* `` (addrs ?B \ addrs ?T)" by (fastforce cong:map_cong simp:stack_eq addrs_def rewrite intro:self_reachable) next show "\(x, y)\?Ra-?Rb. y\(?Rb\<^sup>*``(addrs ?B \ addrs ?T))" by (clarsimp simp:relS_def restr_def) (fastforce simp add:rel_def Image_iff addrs_def fun_upd_apply dest:rel_upd1) qed then have subset: "?Ra\<^sup>*``addrs ?A - ?Rb\<^sup>*``addrs ?T \ ?Rb\<^sup>*``addrs ?B" by blast have ?swI4 proof (rule allI, rule impI) fix x assume a: "x \ R \\ m x" with i4 addr_p_eq stack_eq have inc: "x \ ?Ra\<^sup>*``addrs ?A" by (simp only:reachable_def, clarsimp) with ifB1 a have exc: "x \ ?Rb\<^sup>*`` addrs ?T" by (auto simp add:addrs_def) from inc exc subset show "x \ reachable ?Rb ?B" by (auto simp add:reachable_def) qed moreover \ \If it is marked, then it is reachable\ from i5 have "?swI5" . moreover \ \If it is not on the stack, then its \<^term>\l\ and \<^term>\r\ fields are unchanged\ from i6 stack_eq have "?swI6" by clarsimp moreover \ \If it is on the stack, then its \<^term>\l\ and \<^term>\r\ fields can be reconstructed\ from stackDist i7 nifB2 have "?swI7" by (clarsimp simp:addr_p_eq stack_eq) ultimately show ?thesis by auto qed then have "\stack. ?swInv stack" by blast } moreover { \ \Push arm\ assume nifB1: "\?ifB1" from nifB1 whileB have tNotNull: "t \ Null" by clarsimp then obtain addr_t where addr_t_eq: "t = Ref addr_t" by clarsimp with i1 obtain new_stack where new_stack_eq: "new_stack = (addr t) # stack" by clarsimp from tNotNull nifB1 have n_m_addr_t: "\ (t^.m)" by clarsimp with i2 have t_notin_stack: "(addr t) \ set stack" by blast let "?puI1\?puI2\?puI3\?puI4\?puI5\?puI6\?puI7" = "?puInv new_stack" have "?puInv new_stack" proof - \ \List property is maintained:\ from i1 t_notin_stack have puI1: "?puI1" by (simp add:addr_t_eq new_stack_eq, simp add:S_def) moreover \ \Everything on the stack is marked:\ from i2 have puI2: "?puI2" by (simp add:new_stack_eq fun_upd_apply) moreover \ \Everything is still reachable:\ let "R = reachable ?Ra ?A" = "?I3" let "R = reachable ?Rb ?B" = "?puI3" have "?Ra\<^sup>* `` addrs ?A = ?Rb\<^sup>* `` addrs ?B" proof (rule still_reachable_eq) show "addrs ?A \ ?Rb\<^sup>* `` addrs ?B" by(fastforce simp:addrs_def rel_defs addr_t_eq intro:oneStep_reachable Image_iff[THEN iffD2]) next show "addrs ?B \ ?Ra\<^sup>* `` addrs ?A" by(fastforce simp:addrs_def rel_defs addr_t_eq intro:oneStep_reachable Image_iff[THEN iffD2]) next show "\(x, y)\?Ra-?Rb. y\(?Rb\<^sup>*``addrs ?B)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def dest:rel_upd1) next show "\(x, y)\?Rb-?Ra. y\(?Ra\<^sup>*``addrs ?A)" by (clarsimp simp:relS_def) (fastforce simp add:rel_def Image_iff addrs_def fun_upd_apply dest:rel_upd2) qed with i3 have puI3: "?puI3" by (simp add:reachable_def) moreover \ \If it is reachable and not marked, it is still reachable using...\ let "\x. x \ R \ \ m x \ x \ reachable ?Ra ?A" = ?I4 let "\x. x \ R \ \ ?new_m x \ x \ reachable ?Rb ?B" = ?puI4 let ?T = "{t}" have "?Ra\<^sup>*``addrs ?A \ ?Rb\<^sup>*``(addrs ?B \ addrs ?T)" proof (rule still_reachable) show "addrs ?A \ ?Rb\<^sup>* `` (addrs ?B \ addrs ?T)" by (fastforce simp:new_stack_eq addrs_def intro:self_reachable) next show "\(x, y)\?Ra-?Rb. y\(?Rb\<^sup>*``(addrs ?B \ addrs ?T))" by (clarsimp simp:relS_def new_stack_eq restr_un restr_upd) (fastforce simp add:rel_def Image_iff restr_def addrs_def fun_upd_apply addr_t_eq dest:rel_upd3) qed then have subset: "?Ra\<^sup>*``addrs ?A - ?Rb\<^sup>*``addrs ?T \ ?Rb\<^sup>*``addrs ?B" by blast have ?puI4 proof (rule allI, rule impI) fix x assume a: "x \ R \ \ ?new_m x" have xDisj: "x=(addr t) \ x\(addr t)" by simp with i4 a have inc: "x \ ?Ra\<^sup>*``addrs ?A" by (fastforce simp:addr_t_eq addrs_def reachable_def intro:self_reachable) have exc: "x \ ?Rb\<^sup>*`` addrs ?T" using xDisj a n_m_addr_t by (clarsimp simp add:addrs_def addr_t_eq) from inc exc subset show "x \ reachable ?Rb ?B" by (auto simp add:reachable_def) qed moreover \ \If it is marked, then it is reachable\ from i5 have "?puI5" by (auto simp:addrs_def i3 reachable_def addr_t_eq fun_upd_apply intro:self_reachable) moreover \ \If it is not on the stack, then its \<^term>\l\ and \<^term>\r\ fields are unchanged\ from i6 have "?puI6" by (simp add:new_stack_eq) moreover \ \If it is on the stack, then its \<^term>\l\ and \<^term>\r\ fields can be reconstructed\ from stackDist i6 t_notin_stack i7 have "?puI7" by (clarsimp simp:addr_t_eq new_stack_eq) ultimately show ?thesis by auto qed then have "\stack. ?puInv stack" by blast } ultimately show ?thesis by blast qed } qed end - diff --git a/src/HOL/Hoare/SepLogHeap.thy b/src/HOL/Hoare/SepLogHeap.thy --- a/src/HOL/Hoare/SepLogHeap.thy +++ b/src/HOL/Hoare/SepLogHeap.thy @@ -1,93 +1,95 @@ (* Title: HOL/Hoare/SepLogHeap.thy Author: Tobias Nipkow Copyright 2002 TUM - -Heap abstractions (at the moment only Path and List) -for Separation Logic. *) +section \Heap abstractions for Separation Logic\ + +text \(at the moment only Path and List)\ + theory SepLogHeap -imports Main + imports Main begin type_synonym heap = "(nat \ nat option)" text\\Some\ means allocated, \None\ means free. Address \0\ serves as the null reference.\ + subsection "Paths in the heap" primrec Path :: "heap \ nat \ nat list \ nat \ bool" where "Path h x [] y = (x = y)" | "Path h x (a#as) y = (x\0 \ a=x \ (\b. h x = Some b \ Path h b as y))" lemma [iff]: "Path h 0 xs y = (xs = [] \ y = 0)" by (cases xs) simp_all lemma [simp]: "x\0 \ Path h x as z = (as = [] \ z = x \ (\y bs. as = x#bs \ h x = Some y & Path h y bs z))" by (cases as) auto lemma [simp]: "\x. Path f x (as@bs) z = (\y. Path f x as y \ Path f y bs z)" by (induct as) auto lemma Path_upd[simp]: "\x. u \ set as \ Path (f(u := v)) x as y = Path f x as y" by (induct as) simp_all subsection "Lists on the heap" definition List :: "heap \ nat \ nat list \ bool" where "List h x as = Path h x as 0" lemma [simp]: "List h x [] = (x = 0)" by (simp add: List_def) lemma [simp]: "List h x (a#as) = (x\0 \ a=x \ (\y. h x = Some y \ List h y as))" by (simp add: List_def) lemma [simp]: "List h 0 as = (as = [])" by (cases as) simp_all lemma List_non_null: "a\0 \ List h a as = (\b bs. as = a#bs \ h a = Some b \ List h b bs)" by (cases as) simp_all theorem notin_List_update[simp]: "\x. a \ set as \ List (h(a := y)) x as = List h x as" by (induct as) simp_all lemma List_unique: "\x bs. List h x as \ List h x bs \ as = bs" by (induct as) (auto simp add:List_non_null) lemma List_unique1: "List h p as \ \!as. List h p as" by (blast intro: List_unique) lemma List_app: "\x. List h x (as@bs) = (\y. Path h x as y \ List h y bs)" by (induct as) auto lemma List_hd_not_in_tl[simp]: "List h b as \ h a = Some b \ a \ set as" apply (clarsimp simp add:in_set_conv_decomp) apply(frule List_app[THEN iffD1]) apply(fastforce dest: List_unique) done lemma List_distinct[simp]: "\x. List h x as \ distinct as" by (induct as) (auto dest:List_hd_not_in_tl) lemma list_in_heap: "\p. List h p ps \ set ps \ dom h" by (induct ps) auto lemma list_ortho_sum1[simp]: "\p. \ List h1 p ps; dom h1 \ dom h2 = {}\ \ List (h1++h2) p ps" by (induct ps) (auto simp add:map_add_def split:option.split) lemma list_ortho_sum2[simp]: "\p. \ List h2 p ps; dom h1 \ dom h2 = {}\ \ List (h1++h2) p ps" by (induct ps) (auto simp add:map_add_def split:option.split) end diff --git a/src/HOL/Hoare/Separation.thy b/src/HOL/Hoare/Separation.thy --- a/src/HOL/Hoare/Separation.thy +++ b/src/HOL/Hoare/Separation.thy @@ -1,216 +1,219 @@ (* Title: HOL/Hoare/Separation.thy Author: Tobias Nipkow Copyright 2003 TUM A first attempt at a nice syntactic embedding of separation logic. Already builds on the theory for list abstractions. If we suppress the H parameter for "List", we have to hardwired this into parser and pretty printer, which is not very modular. Alternative: some syntax like

which stands for P H. No more compact, but avoids the funny H. - *) -theory Separation imports Hoare_Logic_Abort SepLogHeap begin +section \Separation logic\ + +theory Separation + imports Hoare_Logic_Abort SepLogHeap +begin text\The semantic definition of a few connectives:\ definition ortho :: "heap \ heap \ bool" (infix "\" 55) where "h1 \ h2 \ dom h1 \ dom h2 = {}" definition is_empty :: "heap \ bool" where "is_empty h \ h = Map.empty" definition singl:: "heap \ nat \ nat \ bool" where "singl h x y \ dom h = {x} & h x = Some y" definition star:: "(heap \ bool) \ (heap \ bool) \ (heap \ bool)" where "star P Q = (\h. \h1 h2. h = h1++h2 \ h1 \ h2 \ P h1 \ Q h2)" definition wand:: "(heap \ bool) \ (heap \ bool) \ (heap \ bool)" where "wand P Q = (\h. \h'. h' \ h \ P h' \ Q(h++h'))" text\This is what assertions look like without any syntactic sugar:\ lemma "VARS x y z w h {star (%h. singl h x y) (%h. singl h z w) h} SKIP {x \ z}" apply vcg apply(auto simp:star_def ortho_def singl_def) done text\Now we add nice input syntax. To suppress the heap parameter of the connectives, we assume it is always called H and add/remove it upon parsing/printing. Thus every pointer program needs to have a program variable H, and assertions should not contain any locally bound Hs - otherwise they may bind the implicit H.\ syntax "_emp" :: "bool" ("emp") "_singl" :: "nat \ nat \ bool" ("[_ \ _]") "_star" :: "bool \ bool \ bool" (infixl "**" 60) "_wand" :: "bool \ bool \ bool" (infixl "-*" 60) (* FIXME does not handle "_idtdummy" *) ML \ \ \\free_tr\ takes care of free vars in the scope of separation logic connectives: they are implicitly applied to the heap\ fun free_tr(t as Free _) = t $ Syntax.free "H" \<^cancel>\| free_tr((list as Free("List",_))$ p $ ps) = list $ Syntax.free "H" $ p $ ps\ | free_tr t = t fun emp_tr [] = Syntax.const \<^const_syntax>\is_empty\ $ Syntax.free "H" | emp_tr ts = raise TERM ("emp_tr", ts); fun singl_tr [p, q] = Syntax.const \<^const_syntax>\singl\ $ Syntax.free "H" $ p $ q | singl_tr ts = raise TERM ("singl_tr", ts); fun star_tr [P,Q] = Syntax.const \<^const_syntax>\star\ $ absfree ("H", dummyT) (free_tr P) $ absfree ("H", dummyT) (free_tr Q) $ Syntax.free "H" | star_tr ts = raise TERM ("star_tr", ts); fun wand_tr [P, Q] = Syntax.const \<^const_syntax>\wand\ $ absfree ("H", dummyT) P $ absfree ("H", dummyT) Q $ Syntax.free "H" | wand_tr ts = raise TERM ("wand_tr", ts); \ parse_translation \ [(\<^syntax_const>\_emp\, K emp_tr), (\<^syntax_const>\_singl\, K singl_tr), (\<^syntax_const>\_star\, K star_tr), (\<^syntax_const>\_wand\, K wand_tr)] \ text\Now it looks much better:\ lemma "VARS H x y z w {[x\y] ** [z\w]} SKIP {x \ z}" apply vcg apply(auto simp:star_def ortho_def singl_def) done lemma "VARS H x y z w {emp ** emp} SKIP {emp}" apply vcg apply(auto simp:star_def ortho_def is_empty_def) done text\But the output is still unreadable. Thus we also strip the heap parameters upon output:\ ML \ local fun strip (Abs(_,_,(t as Const("_free",_) $ Free _) $ Bound 0)) = t | strip (Abs(_,_,(t as Free _) $ Bound 0)) = t \<^cancel>\| strip (Abs(_,_,((list as Const("List",_))$ Bound 0 $ p $ ps))) = list$p$ps\ | strip (Abs(_,_,(t as Const("_var",_) $ Var _) $ Bound 0)) = t | strip (Abs(_,_,P)) = P | strip (Const(\<^const_syntax>\is_empty\,_)) = Syntax.const \<^syntax_const>\_emp\ | strip t = t; in fun is_empty_tr' [_] = Syntax.const \<^syntax_const>\_emp\ fun singl_tr' [_,p,q] = Syntax.const \<^syntax_const>\_singl\ $ p $ q fun star_tr' [P,Q,_] = Syntax.const \<^syntax_const>\_star\ $ strip P $ strip Q fun wand_tr' [P,Q,_] = Syntax.const \<^syntax_const>\_wand\ $ strip P $ strip Q end \ print_translation \ [(\<^const_syntax>\is_empty\, K is_empty_tr'), (\<^const_syntax>\singl\, K singl_tr'), (\<^const_syntax>\star\, K star_tr'), (\<^const_syntax>\wand\, K wand_tr')] \ text\Now the intermediate proof states are also readable:\ lemma "VARS H x y z w {[x\y] ** [z\w]} y := w {x \ z}" apply vcg apply(auto simp:star_def ortho_def singl_def) done lemma "VARS H x y z w {emp ** emp} SKIP {emp}" apply vcg apply(auto simp:star_def ortho_def is_empty_def) done text\So far we have unfolded the separation logic connectives in proofs. Here comes a simple example of a program proof that uses a law of separation logic instead.\ \ \a law of separation logic\ lemma star_comm: "P ** Q = Q ** P" by(auto simp add:star_def ortho_def dest: map_add_comm) lemma "VARS H x y z w {P ** Q} SKIP {Q ** P}" apply vcg apply(simp add: star_comm) done lemma "VARS H {p\0 \ [p \ x] ** List H q qs} H := H(p \ q) {List H p (p#qs)}" apply vcg apply(simp add: star_def ortho_def singl_def) apply clarify apply(subgoal_tac "p \ set qs") prefer 2 apply(blast dest:list_in_heap) apply simp done lemma "VARS H p q r {List H p Ps ** List H q Qs} WHILE p \ 0 INV {\ps qs. (List H p ps ** List H q qs) \ rev ps @ qs = rev Ps @ Qs} DO r := p; p := the(H p); H := H(r \ q); q := r OD {List H q (rev Ps @ Qs)}" apply vcg apply(simp_all add: star_def ortho_def singl_def) apply fastforce apply (clarsimp simp add:List_non_null) apply(rename_tac ps') apply(rule_tac x = ps' in exI) apply(rule_tac x = "p#qs" in exI) apply simp apply(rule_tac x = "h1(p:=None)" in exI) apply(rule_tac x = "h2(p\q)" in exI) apply simp apply(rule conjI) apply(rule ext) apply(simp add:map_add_def split:option.split) apply(rule conjI) apply blast apply(simp add:map_add_def split:option.split) apply(rule conjI) apply(subgoal_tac "p \ set qs") prefer 2 apply(blast dest:list_in_heap) apply(simp) apply fast apply(fastforce) done end diff --git a/src/HOL/Hoare/document/root.tex b/src/HOL/Hoare/document/root.tex --- a/src/HOL/Hoare/document/root.tex +++ b/src/HOL/Hoare/document/root.tex @@ -1,37 +1,46 @@ -\documentclass[11pt,a4paper]{report} +\documentclass[11pt,a4paper]{article} \usepackage{graphicx} \usepackage[english]{babel} \usepackage{isabelle,isabellesym} \usepackage{pdfsetup} \urlstyle{rm} \isabellestyle{it} \begin{document} \title{Hoare Logic} -\author{Various} +\author{ + Norbert Galm \\ + Walter Guttmann \\ + Farhad Mehta \\ + Tobias Nipkow \\ + Leonor Prensa Nieto} \maketitle \begin{abstract} These theories contain a Hoare logic for a simple imperative programming language with while-loops, including a verification condition generator. Special infrastructure for modelling and reasoning about pointer programs is provided, together with many examples, including Schorr-Waite. See \cite{MehtaN-CADE03,MehtaN-IC05} for an excellent exposition. \end{abstract} \pagestyle{plain} \thispagestyle{empty} \tableofcontents -\newpage +\begin{center} + \includegraphics[scale=0.5]{session_graph} +\end{center} + +\clearpage \input{session} \bibliographystyle{plain} \bibliography{root} \end{document}