(* * Copyright 2016, General Dynamics C4 Systems * * This software may be distributed and modified according to the terms of * the GNU General Public License version 2. Note that NO WARRANTY is provided. * See "LICENSE_GPLv2.txt" for details. * * @TAG(GD_GPL) *) text \ Framework for performing C refinement proofs on AutoCorres-generated specs. See AutoCorresTest for example usage. \ theory AutoCorres_C imports "../../tools/autocorres/AutoCorres" "../../tools/autocorres/L4VerifiedLinks" "../../lib/clib/AutoCorresModifiesProofs" "../../lib/clib/Corres_C" begin context kernel begin section \Proof frameworks\ subsection \Proving @{term ccorres}\ text \ From AutoCorres @{term ac_corres}, obtain @{term ccorres}. This requires a separate @{term corres_underlying} proof between the AutoCorres spec and design spec, in order to establish @{term cstate_relation}. \ lemma ac_corres_to_ccorres: "\ ac_corres globals check_termination \ ret_xf arg_rel (liftE ac_f) (Call f_'proc); arg_relS = Collect arg_rel; corres_underlying {(s, s'). cstate_relation s s'} True True R P \ dspec_f ac_f \ \ ccorres dc xfdc P arg_relS [] dspec_f (Call f_'proc)" by (fastforce simp: ccorres_underlying_def corres_underlying_def rf_sr_def Ball_def liftE_def in_liftE[simplified liftE_def] unif_rrel_def dest: ac_corres_ccorres_underlying split: xstate.splits) subsection \Importing @{term ccorres}\ lemma in_AC_call_simpl: fixes r s s' arg_pred globals ret_xf \ f_'proc shows "(r, s') \ fst (AC_call_L1 arg_pred globals ret_xf (L1_call_simpl check_termination \ f_'proc) s) \ \cs cs'. s = globals cs \ arg_pred cs \ (check_termination \ \ \ Call f_'proc \ Normal cs) \ \ \ \Call f_'proc, Normal cs\ \ Normal cs' \ s' = globals cs' \ r = ret_xf cs'" apply (clarsimp simp: AC_call_L1_def L2_call_L1_def L1_call_simpl_def) apply (monad_eq simp: liftM_def select_def select_f_def liftE_def split: xstate.splits sum.splits) apply (rename_tac cs cs' status) apply (case_tac status) apply auto done lemma ccorres_rrel_nat_unit: "ccorres op = (\s. ()) st_P arg_P hs m c = ccorres dc xfdc st_P arg_P hs m c" by (simp add: ccorres_underlying_def dc_def xfdc_def unif_rrel_def cong del: xstate.case_cong) text \ From @{term ccorres} obtain @{term corres}. This is for exporting existing ccorres theorems to be used in AutoCorres-based corres proofs. \ text \Partial correspondence is simple to prove, but not good enough.\ lemma assumes ac_def: "ac_f \ AC_call_L1 arg_rel globals ret_xf (L1_call_simpl check_termination \ f_'proc)" shows "\ ccorres R ret_xf P (Collect arg_rel) [] dspec_f (Call f_'proc) \ \ corres_underlying {(s, s'). cstate_relation s s'} True False (* \ partial *) R P \ dspec_f ac_f" by (fastforce simp: unif_rrel_def ac_def corres_underlying_def ccorres_underlying_def rf_sr_def intro: EHOther dest: in_AC_call_simpl) text \Requires termination proof for f_'proc. Used when no_c_termination is off.\ lemma ccorres_to_corres_with_termination: assumes ac_def: "ac_f \ AC_call_L1 arg_rel globals ret_xf (L1_call_simpl True \ f_'proc)" assumes terminates: "\s s'. \ cstate_relation s (globals s'); P s; \ snd (dspec_f s); arg_rel s' \ \ \ \ Call f_'proc \ Normal s'" shows "\ ccorres R ret_xf P (Collect arg_rel) [] dspec_f (Call f_'proc) \ \ corres_underlying {(s, s'). cstate_relation s s'} True True R P \ dspec_f ac_f" apply (clarsimp simp: ac_def corres_underlying_def ccorres_underlying_def rf_sr_def Ball_def Bex_def) apply (rule conjI) -- "proof for return values" apply (fastforce simp: unif_rrel_def intro: EHOther dest: in_AC_call_simpl) -- "proof for fail bit is trickier" apply (clarsimp simp: AC_call_L1_def L2_call_L1_def L1_call_simpl_def) apply (clarsimp simp: select_f_def select_def snd_bind snd_assert get_def split: sum.splits prod.splits) apply (erule impE) apply (blast intro: terminates) apply (erule disjE) apply (monad_eq split: xstate.splits sum.splits) apply (drule EHOther, fastforce) apply blast apply (drule EHOther, fastforce) apply blast apply (monad_eq split: xstate.splits) apply (fastforce dest: EHAbrupt[OF _ EHEmpty]) done text \Does not require termination proof for f_'proc. Used with no_c_termination.\ lemma ccorres_to_corres_no_termination: assumes ac_def: "ac_f \ AC_call_L1 arg_rel globals ret_xf (L1_call_simpl False \ f_'proc)" shows "\ ccorres R ret_xf P (Collect arg_rel) [] dspec_f (Call f_'proc) \ \ corres_underlying {(s, s'). cstate_relation s s'} True True R P \ dspec_f ac_f" apply (clarsimp simp: ac_def corres_underlying_def ccorres_underlying_def rf_sr_def Ball_def Bex_def) apply (rule conjI) -- "proof for return values" apply (fastforce simp: unif_rrel_def intro: EHOther dest: in_AC_call_simpl) -- "proof for fail bit is trickier" apply (clarsimp simp: AC_call_L1_def L2_call_L1_def L1_call_simpl_def) apply (clarsimp simp: select_f_def select_def snd_bind snd_assert get_def split: sum.splits prod.splits) apply (erule disjE) apply (monad_eq split: xstate.splits sum.splits) apply (drule EHOther, fastforce) apply blast apply (drule EHOther, fastforce) apply blast apply (monad_eq split: xstate.splits) apply (fastforce dest: EHAbrupt[OF _ EHEmpty]) done end section \Experiments with transferring bitfield proofs\ text \Generic transfer rules\ lemma autocorres_transfer_spec: assumes ac_def: "ac_f \ AC_call_L1 arg_rel globals ret_xf (L1_call_simpl check_termination \ f_'proc)" assumes c_spec: "\s0. \\ (Collect (\s. s0 = s \ P s)) Call f_'proc (Collect (Q s0))" assumes precond_deps: "\s t. \ arg_rel s; arg_rel t; globals s = globals t \ \ P s = P t" assumes postcond_deps: "\s0 s0' s t. \ arg_rel s0; arg_rel s0'; globals s0 = globals s0'; ret_xf s = ret_xf t; globals s = globals t \ \ Q s0 s = Q s0' t" shows "\\s. P cs \ s = globals cs \ arg_rel cs \ ac_f \\r s'. \cs'. s' = globals cs' \ r = ret_xf cs' \ Q cs cs' \" apply (clarsimp simp: valid_def ac_def AC_call_L1_def L2_call_L1_def L1_call_simpl_def in_monad' in_liftM select_f_def in_select in_fail split: sum.splits xstate.splits) apply (rename_tac r', case_tac r'; clarsimp) apply (rename_tac xst, case_tac xst; clarsimp) apply (drule_tac ?s0.1=s in exec_normal[OF _ _ c_spec[rule_format], rotated]) apply (blast dest: precond_deps) apply (blast dest: postcond_deps) done text \This one is probably more useful\ lemma autocorres_transfer_spec_no_modifies: assumes ac_def: "ac_f \ AC_call_L1 arg_rel globals ret_xf (L1_call_simpl check_termination \ f_'proc)" assumes c_spec: "\s0. hoarep \ {} {} (P' s0) (Call f_'proc) (Collect (Q s0)) A" -- \syntax parser barfs...\ assumes c_modifies: "\\. \\\<^bsub>/UNIV\<^esub> {\} Call f_'proc {t. t may_not_modify_globals \}" assumes c_spec_unify: "\s0. P' s0 = {s. s0 = s \ P s}" assumes precond_deps: "\s0 s t. \ arg_rel s; arg_rel t; globals s = globals t \ \ P s = P t" assumes postcond_deps: "\s0 s0' s t. \ arg_rel s0; arg_rel s0'; globals s0 = globals s0'; ret_xf s = ret_xf t; globals s = globals t \ \ Q s0 s = Q s0' t" shows "\\s. s = globals cs \ P cs \ arg_rel cs \ ac_f \\r s'. s' = globals cs \ (\cs'. r = ret_xf cs' \ Q cs cs') \" apply (clarsimp simp: valid_def ac_def AC_call_L1_def L2_call_L1_def L1_call_simpl_def in_monad' in_liftM select_f_def in_select in_fail split: sum.splits xstate.splits) apply (rename_tac r', case_tac r'; clarsimp) apply (rename_tac xst, case_tac xst; clarsimp) apply (frule_tac ?s0.1=s in exec_normal[OF _ _ c_spec[rule_format], rotated]) apply (clarsimp simp: c_spec_unify) apply (blast dest: precond_deps) apply (frule exec_normal[OF singletonI _ c_modifies[rule_format]]) apply (clarsimp simp: meq_def) apply (blast dest: postcond_deps) done subsection \Helpers\ lemma All_to_all': "(\x. P x) \ (\x. P x)" by simp text \ Convert references to global or local state variables, to the opposite one. FIXME: surely this has been proven already. \ lemma collect_lift: "Collect (\s. s0 = s \ f s) = Collect (\s. s0 = s \ f s0)" by blast lemma collect_unlift: "Collect (\s. s0 = s \ f s0 s) = Collect (\s. s0 = s \ f s s)" by blast subsection \Experiment with wrapping specs\ lemma exec_no_fault: assumes asms: "s \ P" and ce: "Gamma \ \c, Normal s\ \ Fault f" and valid: "Gamma \ P c Q, A" shows "False" using valid ce asms apply - apply (frule hoare_sound) apply (clarsimp simp: NonDetMonad.bind_def cvalid_def split_def HoarePartialDef.valid_def) apply (drule spec, drule spec, drule (1) mp) apply auto done lemma exec_no_stuck: assumes asms: "s \ P" and ce: "Gamma \ \c, Normal s\ \ Stuck" and valid: "Gamma \ P c Q, A" shows "False" using valid ce asms apply - apply (frule hoare_sound) apply (clarsimp simp: NonDetMonad.bind_def cvalid_def split_def HoarePartialDef.valid_def) apply (drule spec, drule spec, drule (1) mp) apply auto done definition L1_call_simpl_spec where "L1_call_simpl_spec check_term Gamma proc precond postcond = L1_spec (Collect (\(s, t). precond s s \ postcond s t))" lemma L1corres_call_simpl_spec: "\ \s0. Gamma\ (Collect (precond s0)) (Call proc) (Collect (postcond s0)); \s. ct \ Gamma\Call proc \ Normal s \ \ L1corres ct Gamma (L1_call_simpl_spec ct Gamma proc precond postcond) (Call proc)" apply (clarsimp simp: L1corres_def L1_call_simpl_spec_def L1_defs assert_def snd_select snd_liftE snd_spec in_monad' in_spec split: xstate.splits) apply (case_tac t) apply (blast dest: exec_normal[rotated]) apply (blast dest: exec_abrupt[rotated]) apply (blast intro: exec_no_fault[rotated]) apply (blast intro: exec_no_stuck[rotated]) done section \Termination proofs\ text \ Proving termination side conditions for @{thm kernel.ccorres_to_corres_with_termination}. To begin with, we can automatically prove terminates for most helper functions as they do not have recursion or loops. \ named_theorems terminates_trivial ML \ local fun terminates_intros ctxt = REPEAT_ALL_NEW (resolve_tac ctxt (Proof_Context.get_thms ctxt "terminates_trivial")); in fun terminates_trivial_tac ctxt n st = case Logic.concl_of_goal (Thm.prop_of st) n of @{term_pat "Trueprop (_ \ Call ?f_'proc \ _)"} => let val f = dest_Const f_'proc |> fst |> Long_Name.base_name |> unsuffix "_'proc"; val impl = Proof_Context.get_thm ctxt (f ^ "_impl"); val body = Proof_Context.get_thm ctxt (f ^ "_body_def"); in st |> (resolve_tac ctxt @{thms terminates.Call} n THEN resolve_tac ctxt [impl] n THEN simp_tac (put_simpset HOL_ss ctxt addsimps (body :: @{thms return_C_def lvar_nondet_init_def})) n THEN terminates_intros ctxt n) end | _ => terminates_intros ctxt n st end \ lemma [terminates_trivial]: "\ \s. \ \ c \ Normal s \ \ \ \ Guard F G c \ Normal s" apply (blast intro: terminates.Guard terminates.GuardFault) done lemma [terminates_trivial]: "\ \s. \ \ c1 \ Normal s; \s. \ \ c2 \ Normal s \ \ \ \ Cond C c1 c2 \ Normal s" apply (blast intro: terminates.CondTrue terminates.CondFalse) done lemma [terminates_trivial]: "\ \ \ c1 \ Normal s; \s. \ \ c2 \ Normal s \ \ \ \ c1;;c2 \ Normal s" apply (erule terminates.Seq) apply clarsimp apply (case_tac s'; auto) done lemma [terminates_trivial]: fixes \ return init shows "\ \s. \ \ Call p \ Normal s; \s t u. \ \ c s t \ Normal u \ \ \ \ call init p return c \ Normal s" apply (case_tac "\ p") apply (erule terminates_callUndefined) apply (fastforce simp: terminates_Call_body intro: terminates_call) done lemmas [terminates_trivial] = terminates.Basic terminates.Catch[rule_format] terminates.Throw terminates.Skip terminates.Spec end