(* Title: HOL/Hoare/Hoare.thy ID: $Id$ Author: Leonor Prensa Nieto & Tobias Nipkow Copyright 1998 TUM Sugared semantic embedding of Hoare logic. 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 imports Main uses ("~~/src/HOL/Hoare/hoare_tac.ML") begin types 'a bexp = "'a set" 'a assn = "'a set" datatype 'a com = Basic "'a \ 'a" | Seq "'a com" "'a com" ("(_;/ _)" [61,60] 60) | Cond "'a bexp" "'a com" "'a com" ("(1IF _/ THEN _ / ELSE _/ FI)" [0,0,0] 61) | While "'a bexp" "'a assn" "'a com" ("(1WHILE _/ INV {_} //DO _ /OD)" [0,0,0] 61) abbreviation annskip ("SKIP") where "SKIP == Basic id" types '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 (c1;c2) s s'" | "s \ b \ Sem c1 s s' \ Sem (IF b THEN c1 ELSE c2 FI) s s'" | "s \ b \ Sem c2 s s' \ Sem (IF b THEN c1 ELSE c2 FI) s s'" | "s \ b \ Sem (While b x c) s s" | "s \ b \ Sem c s s'' \ Sem (While b x c) s'' s' \ Sem (While b x c) s s'" inductive_cases [elim!]: "Sem (Basic f) s s'" "Sem (c1;c2) s s'" "Sem (IF b THEN c1 ELSE c2 FI) 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" (** parse translations **) syntax "_assign" :: "id => 'b => 'a com" ("(2_ :=/ _)" [70,65] 61) syntax "_hoare_vars" :: "[idts, 'a assn,'a com,'a assn] => bool" ("VARS _// {_} // _ // {_}" [0,0,55,0] 50) syntax ("" output) "_hoare" :: "['a assn,'a com,'a assn] => bool" ("{_} // _ // {_}" [0,55,0] 50) ML {* local fun abs((a,T),body) = let val a = absfree(a, dummyT, body) in if T = Bound 0 then a else Const(Syntax.constrainAbsC,dummyT) $ a $ T end in fun mk_abstuple [x] body = abs (x, body) | mk_abstuple (x::xs) body = Syntax.const @{const_syntax split} $ abs (x, mk_abstuple xs body); fun mk_fbody a e [x as (b,_)] = if a=b then e else Syntax.free b | mk_fbody a e ((b,_)::xs) = Syntax.const @{const_syntax Pair} $ (if a=b then e else Syntax.free b) $ mk_fbody a e xs; fun mk_fexp a e xs = mk_abstuple xs (mk_fbody a e xs) end *} (* bexp_tr & assn_tr *) (*all meta-variables for bexp except for TRUE are translated as if they were boolean expressions*) ML{* fun bexp_tr (Const ("TRUE", _)) xs = Syntax.const "TRUE" (* FIXME !? *) | bexp_tr b xs = Syntax.const @{const_syntax Collect} $ mk_abstuple xs b; fun assn_tr r xs = Syntax.const @{const_syntax Collect} $ mk_abstuple xs r; *} (* com_tr *) ML{* fun com_tr (Const(@{syntax_const "_assign"},_) $ Free (a,_) $ e) xs = Syntax.const @{const_syntax Basic} $ mk_fexp a e xs | com_tr (Const (@{const_syntax Basic},_) $ f) xs = Syntax.const @{const_syntax Basic} $ f | com_tr (Const (@{const_syntax Seq},_) $ c1 $ c2) xs = Syntax.const @{const_syntax Seq} $ com_tr c1 xs $ com_tr c2 xs | com_tr (Const (@{const_syntax Cond},_) $ b $ c1 $ c2) xs = Syntax.const @{const_syntax Cond} $ bexp_tr b xs $ com_tr c1 xs $ com_tr c2 xs | com_tr (Const (@{const_syntax While},_) $ b $ I $ c) xs = Syntax.const @{const_syntax While} $ bexp_tr b xs $ assn_tr I xs $ com_tr c xs | com_tr t _ = t (* if t is just a Free/Var *) *} (* triple_tr *) (* FIXME does not handle "_idtdummy" *) ML{* local fun var_tr(Free(a,_)) = (a,Bound 0) (* Bound 0 = dummy term *) | var_tr(Const (@{syntax_const "_constrain"}, _) $ (Free (a,_)) $ T) = (a,T); fun vars_tr (Const (@{syntax_const "_idts"}, _) $ idt $ vars) = var_tr idt :: vars_tr vars | vars_tr t = [var_tr t] in fun hoare_vars_tr [vars, pre, prg, post] = let val xs = vars_tr vars in Syntax.const @{const_syntax Valid} $ assn_tr pre xs $ com_tr prg xs $ assn_tr post xs end | hoare_vars_tr ts = raise TERM ("hoare_vars_tr", ts); end *} parse_translation {* [(@{syntax_const "_hoare_vars"}, hoare_vars_tr)] *} (*****************************************************************************) (*** print translations ***) ML{* fun dest_abstuple (Const (@{const_syntax split},_) $ (Abs(v,_, body))) = subst_bound (Syntax.free v, dest_abstuple body) | dest_abstuple (Abs(v,_, body)) = subst_bound (Syntax.free v, body) | dest_abstuple trm = trm; fun abs2list (Const (@{const_syntax split},_) $ (Abs(x,T,t))) = Free (x, T)::abs2list t | abs2list (Abs(x,T,t)) = [Free (x, T)] | abs2list _ = []; fun mk_ts (Const (@{const_syntax split},_) $ (Abs(x,_,t))) = mk_ts t | mk_ts (Abs(x,_,t)) = mk_ts t | mk_ts (Const (@{const_syntax Pair},_) $ a $ b) = a::(mk_ts b) | mk_ts t = [t]; fun mk_vts (Const (@{const_syntax split},_) $ (Abs(x,_,t))) = ((Syntax.free x)::(abs2list t), mk_ts t) | mk_vts (Abs(x,_,t)) = ([Syntax.free x], [t]) | mk_vts t = raise Match; fun find_ch [] i xs = (false, (Syntax.free "not_ch", Syntax.free "not_ch")) | find_ch ((v,t)::vts) i xs = if t = Bound i then find_ch vts (i-1) xs else (true, (v, subst_bounds (xs, t))); fun is_f (Const (@{const_syntax split},_) $ (Abs(x,_,t))) = true | is_f (Abs(x,_,t)) = true | is_f t = false; *} (* assn_tr' & bexp_tr'*) ML{* fun assn_tr' (Const (@{const_syntax Collect},_) $ T) = dest_abstuple T | assn_tr' (Const (@{const_syntax inter}, _) $ (Const (@{const_syntax Collect},_) $ T1) $ (Const (@{const_syntax Collect},_) $ T2)) = Syntax.const @{const_syntax inter} $ dest_abstuple T1 $ dest_abstuple T2 | assn_tr' t = t; fun bexp_tr' (Const (@{const_syntax Collect},_) $ T) = dest_abstuple T | bexp_tr' t = t; *} (*com_tr' *) ML{* fun mk_assign f = let val (vs, ts) = mk_vts f; val (ch, which) = find_ch (vs~~ts) ((length vs)-1) (rev vs) in if ch then Syntax.const @{syntax_const "_assign"} $ fst which $ snd which else Syntax.const @{const_syntax annskip} end; fun com_tr' (Const (@{const_syntax Basic},_) $ f) = if is_f f then mk_assign f else Syntax.const @{const_syntax Basic} $ f | com_tr' (Const (@{const_syntax Seq},_) $ c1 $ c2) = Syntax.const @{const_syntax Seq} $ com_tr' c1 $ com_tr' c2 | com_tr' (Const (@{const_syntax Cond},_) $ b $ c1 $ c2) = Syntax.const @{const_syntax Cond} $ bexp_tr' b $ com_tr' c1 $ com_tr' c2 | com_tr' (Const (@{const_syntax While},_) $ b $ I $ c) = Syntax.const @{const_syntax While} $ bexp_tr' b $ assn_tr' I $ com_tr' c | com_tr' t = t; fun spec_tr' [p, c, q] = Syntax.const @{syntax_const "_hoare"} $ assn_tr' p $ com_tr' c $ assn_tr' q *} <<<<<<< local <<<<<<< local text {* The following specification of syntax and translations is for Isabelle experts only; feel free to ignore it. While the first part is still a somewhat intelligible specification of the concrete syntactic representation of our Hoare language, the actual ``ML drivers'' is quite involved. Just note that the we re-use the basic quote/antiquote translations as already defined in Isabelle/Pure (see \verb,Syntax.quote_tr, and \verb,Syntax.quote_tr',). *} syntax "_quote" :: "'b => ('a => 'b)" ("(.'(_').)" [0] 1000) "_antiquote" :: "('a => 'b) => 'b" ("\_" [1000] 1000) "_Subst" :: "'a bexp \ 'b \ idt \ 'a bexp" ("_[_'/\_]" [1000] 999) "_Assert" :: "'a => 'a set" ("(.{_}.)" [0] 1000) "_Assign" :: "idt => 'b => 'a com" ("(\_ :=/ _)" [70, 65] 61) "_Cond" :: "'a bexp => 'a com => 'a com => 'a com" ("(0IF _/ THEN _/ ELSE _/ FI)" [0, 0, 0] 61) "_While_inv" :: "'a bexp => 'a assn => 'a com => 'a com" ("(0WHILE _/ INV _ //DO _ /OD)" [0, 0, 0] 61) "_While" :: "'a bexp => 'a com => 'a com" ("(0WHILE _ //DO _ /OD)" [0, 0] 61) syntax (xsymbols) "_Assert" :: "'a => 'a set" ("(\_\)" [0] 1000) translations ".{b}." => "Collect .(b)." "B [a/\x]" => ".{\(_update_name x (\_. a)) \ B}." "\x := a" => "Basic .(\(_update_name x (\_. a)))." "IF b THEN c1 ELSE c2 FI" => "Cond .{b}. c1 c2" "WHILE b INV i DO c OD" => "While .{b}. i c" parse_translation {* let fun quote_tr [t] = Syntax.quote_tr "_antiquote" t | quote_tr ts = raise TERM ("quote_tr", ts); in [("_quote", quote_tr)] end *} text {* As usual in Isabelle syntax translations, the part for printing is more complicated --- we cannot express parts as macro rules as above. Don't look here, unless you have to do similar things for yourself. *} print_translation {* let fun quote_tr' f (t :: ts) = Term.list_comb (f $ Syntax.quote_tr' "_antiquote" t, ts) | quote_tr' _ _ = raise Match; val assert_tr' = quote_tr' (Syntax.const "_Assert"); fun bexp_tr' name ((Const ("Collect", _) $ t) :: ts) = quote_tr' (Syntax.const name) (t :: ts) | bexp_tr' _ _ = raise Match; fun upd_tr' (x_upd, T) = (case try (unsuffix RecordPackage.updateN) x_upd of SOME x => (x, if T = dummyT then T else Term.domain_type T) | NONE => raise Match); fun update_name_tr' (Free x) = Free (upd_tr' x) | update_name_tr' ((c as Const ("_free", _)) $ Free x) = c $ Free (upd_tr' x) | update_name_tr' (Const x) = Const (upd_tr' x) | update_name_tr' _ = raise Match; fun K_tr' (Abs (_,_,t)) = if null (loose_bnos t) then t else raise Match | K_tr' (Abs (_,_,Abs (_,_,t)$Bound 0)) = if null (loose_bnos t) then t else raise Match | K_tr' _ = raise Match; fun assign_tr' (Abs (x, _, f $ k $ Bound 0) :: ts) = quote_tr' (Syntax.const "_Assign" $ update_name_tr' f) (Abs (x, dummyT, K_tr' k) :: ts) | assign_tr' _ = raise Match; in [("Collect", assert_tr'), ("Basic", assign_tr'), ("Cond", bexp_tr' "_Cond"), ("While", bexp_tr' "_While_inv")] end *} subsection {* Rules for single-step proof \label{sec:hoare-isar} *} text {* We are now ready to introduce a set of Hoare rules to be used in single-step structured proofs in Isabelle/Isar. We refer to the concrete syntax introduce above. \medskip Assertions of Hoare Logic may be manipulated in calculational proofs, with the inclusion expressed in terms of sets or predicates. Reversed order is supported as well. *} lemma [trans]: "|- P c Q ==> P' <= P ==> |- P' c Q" by (unfold Valid_def) blast lemma [trans] : "P' <= P ==> |- P c Q ==> |- P' c Q" by (unfold Valid_def) blast lemma [trans]: "Q <= Q' ==> |- P c Q ==> |- P c Q'" by (unfold Valid_def) blast lemma [trans]: "|- P c Q ==> Q <= Q' ==> |- P c Q'" by (unfold Valid_def) blast lemma [trans]: "|- .{\P}. c Q ==> (!!s. P' s --> P s) ==> |- .{\P'}. c Q" by (simp add: Valid_def) lemma [trans]: "(!!s. P' s --> P s) ==> |- .{\P}. c Q ==> |- .{\P'}. c Q" by (simp add: Valid_def) lemma [trans]: "|- P c .{\Q}. ==> (!!s. Q s --> Q' s) ==> |- P c .{\Q'}." by (simp add: Valid_def) lemma [trans]: "(!!s. Q s --> Q' s) ==> |- P c .{\Q}. ==> |- P c .{\Q'}." by (simp add: Valid_def) text {* Identity and basic assignments.\footnote{The $\idt{hoare}$ method introduced in \S\ref{sec:hoare-vcg} is able to provide proper instances for any number of basic assignments, without producing additional verification conditions.} *} lemma skip [intro?]: "|- P SKIP P" proof - have "|- {s. id s : P} SKIP P" by (rule basic) thus ?thesis by simp qed lemma assign: "|- P [\a/\x] \x := \a P" by (rule basic) text {* Note that above formulation of assignment corresponds to our preferred way to model state spaces, using (extensible) record types in HOL \cite{Naraschewski-Wenzel:1998:HOOL}. For any record field $x$, Isabelle/HOL provides a functions $x$ (selector) and $\idt{x{\dsh}update}$ (update). Above, there is only a place-holder appearing for the latter kind of function: due to concrete syntax \isa{\'x := \'a} also contains \isa{x\_update}.\footnote{Note that due to the external nature of HOL record fields, we could not even state a general theorem relating selector and update functions (if this were required here); this would only work for any particular instance of record fields introduced so far.} *} text {* Sequential composition --- normalizing with associativity achieves proper of chunks of code verified separately. *} lemmas [trans, intro?] = seq lemma seq_assoc [simp]: "( |- P c1;(c2;c3) Q) = ( |- P (c1;c2);c3 Q)" by (auto simp add: Valid_def) text {* Conditional statements. *} lemmas [trans, intro?] = cond lemma [trans, intro?]: "|- .{\P & \b}. c1 Q ==> |- .{\P & ~ \b}. c2 Q ==> |- .{\P}. IF \b THEN c1 ELSE c2 FI Q" by (rule cond) (simp_all add: Valid_def) text {* While statements --- with optional invariant. *} lemma [intro?]: "|- (P Int b) c P ==> |- P (While b P c) (P Int -b)" by (rule while) lemma [intro?]: "|- (P Int b) c P ==> |- P (While b arbitrary c) (P Int -b)" by (rule while) lemma [intro?]: "|- .{\P & \b}. c .{\P}. ==> |- .{\P}. WHILE \b INV .{\P}. DO c OD .{\P & ~ \b}." by (simp add: while Collect_conj_eq Collect_neg_eq) lemma [intro?]: "|- .{\P & \b}. c .{\P}. ==> |- .{\P}. WHILE \b DO c OD .{\P & ~ \b}." by (simp add: while Collect_conj_eq Collect_neg_eq) subsection {* Verification conditions \label{sec:hoare-vcg} *} ======= print_translation {* [("Valid", spec_tr')] *} >>>>>>> other ======= print_translation {* [(@{const_syntax Valid}, spec_tr')] *} >>>>>>> other 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 (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 INV {i} DO c OD) 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 INV {i} DO c OD" s s') auto lemma WhileRule: "p \ i \ Valid (i \ b) c i \ i \ (-b) \ q \ Valid p (While b i c) q" apply (clarsimp simp:Valid_def) apply(drule While_aux) apply assumption apply blast apply blast done lemma Compl_Collect: "-(Collect b) = {x. ~(b x)}" by blast lemmas AbortRule = SkipRule -- "dummy version" use "~~/src/HOL/Hoare/hoare_tac.ML" method_setup vcg = {* Scan.succeed (fn ctxt => SIMPLE_METHOD' (hoare_tac ctxt (K all_tac))) *} "verification condition generator" method_setup vcg_simp = {* Scan.succeed (fn ctxt => SIMPLE_METHOD' (hoare_tac ctxt (asm_full_simp_tac (simpset_of ctxt)))) *} "verification condition generator plus simplification" end