1(* 2 * Copyright 2014, NICTA 3 * 4 * This software may be distributed and modified according to the terms of 5 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 6 * See "LICENSE_GPLv2.txt" for details. 7 * 8 * @TAG(NICTA_GPL) 9 *) 10 11structure CtacImpl = 12struct 13 14exception TACTIC of string; 15 16(* FIXME: avoid refs *) 17val time_ctac = ref false 18 19type trace_opts = { 20 trace_this : bool, 21 trace_simp : bool, 22 trace_ceqv : bool, 23 trace_xpres : bool 24} 25 26val default_trace_opts : trace_opts = {trace_this = false, trace_simp = false, trace_ceqv = false, trace_xpres = false} 27val all_trace_opts : trace_opts = {trace_this = true, trace_simp = true, trace_ceqv = true, trace_xpres = true} 28fun set_ceqv_trace_opts {trace_this, ...} = {trace_this = trace_this, trace_simp = true, trace_ceqv = true, trace_xpres = true} 29 30fun tracet b s ctxt = 31 if b 32 then SUBGOAL (fn (p, _) => 33 let 34 val _ = tracing (s ^ Syntax.string_of_term ctxt p) 35 in 36 no_tac 37 end) 38 else (fn _ => no_tac) 39 40 41fun wrap_accum f i xq = 42 Seq.make (fn () => 43 (case f i (fn () => Seq.pull xq) of 44 (_, NONE) => NONE 45 | (i', SOME (x, xq')) => SOME (x, wrap_accum f i' xq'))); 46 47fun prim_trace_tac' b depth s ctxt t n thm = let 48 val depth_str = "[" ^ Int.toString depth ^ "] " 49 val _ = tracet b (depth_str ^ s ^ ":\n") ctxt n thm 50 fun wrapper i f = case f () of 51 NONE => let val _ = if b then tracing (depth_str ^ s ^ " FAIL") else () 52 in (i, NONE) end 53 | SOME (x, xq) => let val _ = if b then tracing (depth_str ^ s ^ " (" ^ Int.toString i ^ ")") else () 54 in (i + 1, SOME (x, xq)) end 55 val res = t n thm 56in 57 wrap_accum wrapper 0 res 58end; 59 60fun prim_trace_tac b depth s ctxt t thm = prim_trace_tac' b depth s ctxt (fn _ => t) 1 thm 61 62(* Presumably you want to be careful with this ... don't use it when the results 63 are inifinite, for example *) 64fun nres_trace_tac ctxt s t thm = let 65 val r = t thm 66 fun p1 (t, s) = s ^ " ALT:\n" ^ Thm.string_of_thm ctxt t ^ "\n" 67 val _ = tracing (foldl p1 s (Seq.list_of r)) 68in 69 r 70end; 71 72fun nres_trace_tac' ctxt s t n thm = nres_trace_tac ctxt s (t n) thm 73 74structure ctac_rules = Generic_Data 75(struct 76 type T = thm list 77 val empty = [] 78 val extend = I 79 val merge = Thm.merge_thms; 80end); 81 82structure ctac_pre = Generic_Data 83(struct 84 type T = thm list 85 val empty = [] 86 val extend = I 87 val merge = Thm.merge_thms; 88end); 89 90structure ctac_post = Generic_Data 91(struct 92 type T = thm list 93 val empty = [] 94 val extend = I 95 val merge = Thm.merge_thms; 96end); 97 98val ctac_add = Thm.declaration_attribute 99 (fn thm => 100 (ctac_rules.map (Thm.add_thm thm))); 101 102val ctac_del = Thm.declaration_attribute 103 (fn thm => 104 (ctac_rules.map (Thm.del_thm thm))); 105 106val ctac_clear = ctac_rules.map (fn _ => []) 107 108val ctac_pre_add = Thm.declaration_attribute 109 (fn thm => 110 (ctac_pre.map (Thm.add_thm thm))); 111 112val ctac_pre_del = Thm.declaration_attribute 113 (fn thm => 114 (ctac_pre.map (Thm.del_thm thm))); 115 116val ctac_pre_clear = ctac_pre.map (fn _ => []) 117 118val ctac_post_add = Thm.declaration_attribute 119 (fn thm => 120 (ctac_post.map (Thm.add_thm thm))); 121 122val ctac_post_del = Thm.declaration_attribute 123 (fn thm => 124 (ctac_post.map (Thm.del_thm thm))); 125 126val ceqv_simpl_sequence_pair = Attrib.config_bool 127 @{binding ceqv_simpl_sequence} (K false) 128fun ceqv_simpl_seq ctxt = Config.get ctxt (fst ceqv_simpl_sequence_pair) 129 130val setup = 131 Attrib.setup @{binding "corres"} (Attrib.add_del ctac_add ctac_del) 132 "correspondence rules" 133 #> Attrib.setup @{binding "corres_pre"} 134 (Attrib.add_del ctac_pre_add ctac_pre_del) 135 "correspondence preprocessing rules" 136 #> Attrib.setup @{binding "corres_post"} 137 (Attrib.add_del ctac_post_add ctac_post_del) 138 "correspondence postprocessing rules" 139 #> snd ceqv_simpl_sequence_pair; 140 141(* tacticals *) 142 143fun REPEAT_DETERM' t n = REPEAT_DETERM (t n) 144 145fun TRY' t = t ORELSE' (fn _ => all_tac) 146 147(* Then all new with the first being distinguished --- this is 148 * complex because we want to execute ftac _first_, then the new goals. *) 149infix 1 THEN_ALL_NEW_DIST_FIRST; 150fun (tac1 THEN_ALL_NEW_DIST_FIRST (ftac, tac2)) n st = 151 st |> (tac1 n THEN (fn st' => (ftac n THEN (fn st'' => 152 Seq.INTERVAL tac2 153 (n + Thm.nprems_of st'' - Thm.nprems_of st' + 1) 154 (n + (Thm.nprems_of st'' - Thm.nprems_of st)) st'')) st')); 155 156(* Collapses a list down to THEN_ELSE (_, _ THEN_ELSE (_, ...)) *) 157fun FIRST_COMMIT' tacs n = 158 foldr (fn ((t, match), nomatch) => t n THEN_ELSE (match n, nomatch)) no_tac tacs 159 160(* Return the name of the extraction function and its return type. *) 161fun xfu_to_xf s = unsuffix "_update" s 162 163fun update_to_xf s tp = 164 (* tp should be (a -> a) -> cstate -> cstate *) 165 (unsuffix "_update" s, domain_type (domain_type tp)) 166 167val cstate = @{typ "cstate"}; 168 169fun xf_to_update (s, tp) = 170 (s ^ "_update", (tp --> tp) --> cstate --> cstate) 171 172(* tacticals *) 173 174fun SOLVE' tac n = SELECT_GOAL (SOLVE (tac 1)) n; 175 176(* call stuff *) 177fun call_name (Const ("Language.call", _) $ _ $ (Const (f, _)) $ _ $ _) = f 178 | call_name t = raise TERM ("call_name: expecting Language.call", [t]); 179 180(* ccorres stuff *) 181type ccorres = { 182 strel : term, 183 gamma : term, 184 rrel : term, 185 xf : term, 186 absg : term, 187 concg : term, 188 ehs : term, 189 abs : term, 190 conc : term 191} 192 193fun is_ccorres c = 194 case head_of c of 195 Const x => fst x = "Corres_UL_C.ccorres_underlying" 196 | _ => false 197 198fun dest_ccorres (lhs $ strel $ gamma $ vrrel $ vxf $ vg $ vg' $ vhs $ va $ vc) 199 = if is_ccorres lhs then 200 { strel = strel, gamma = gamma, rrel = vrrel, 201 xf = vxf, absg = vg, concg = vg', ehs = vhs, abs = va, conc = vc } 202 else 203 raise TERM ("Expected head to be ccorres", [lhs]) 204 | dest_ccorres t = raise TERM ("Expected fully applied ccorres", [t]); 205 206fun extract_seq_lhs (Const ("Language.com.Seq", _) $ a $ _) = SOME a 207 | extract_seq_lhs _ = NONE; 208 209val xfdc = let 210 val (xf, tp) = dest_Const @{term "xfdc"} 211in 212 (xf, range_type tp) 213end; 214 215(* Cases we care about (xf_update is top level xf): 216 * xf_update (foo_update v) 217 * xf_update (\<lambda>x. foo_update v (g x)) 218 * 219 * Note the first doesn't really work with this framework 220 *) 221 222fun field_update_to_xfru_maybe xf t = let 223 224 val xf_occurs = member (op =) (Term.add_const_names t []) xf 225 226 fun doit (Const (c, tp)) 227 = if (String.isSuffix "_update" c) then SOME ((c, tp), xf_occurs) else NONE 228 | doit (Abs (_, _, c)) = doit c 229 | doit (c $ _) = doit c 230 | doit _ = NONE 231in 232 doit t 233end; 234 235fun extract_basic_xf 236 (Const ("Language.com.Basic", _) $ (Abs (_, _, (Const (c, tp) $ body $ _)))) 237 = let 238 val xf_tp = update_to_xf c tp 239 in 240 (SOME xf_tp, field_update_to_xfru_maybe (fst xf_tp) body) 241 end 242 | extract_basic_xf (Const ("Language.com.Basic", _) $ (Const (c, tp) $ body)) 243 = let 244 val xf_tp = update_to_xf c tp 245 in 246 (SOME xf_tp, field_update_to_xfru_maybe (fst xf_tp) body) 247 end 248 (* In the unknown case, return xfdc *) 249 | extract_basic_xf _ = (NONE, NONE) 250(* | extract_basic_xf t = raise TERM ("extract_basic_xf", [t]) *) 251 252(* Not used at the moment *) 253 254fun nothrows (Const ("Language.com.Skip", _)) = true 255 | nothrows (Const ("Language.com.Basic", _) $ _) = true 256 | nothrows (Const ("Language.com.Spec", _) $ _) = true 257 | nothrows (Const ("Language.com.Seq", _) $ a $ b) = 258 nothrows a andalso nothrows b 259 | nothrows (Const ("Language.com.Cond", _) $ _ $ a $ b) = 260 nothrows a andalso nothrows b 261 | nothrows (Const ("Language.com.While", _) $ _ $ a) = nothrows a 262 | nothrows (Const ("Language.com.Call", _) $ _) = true 263 | nothrows (Const ("Language.com.DynCom", _) $ (Abs (_, _, c))) = nothrows c 264 | nothrows (Const ("Language.com.Guard", _) $ _ $ _ $ b) = nothrows b 265 | nothrows (Const ("Language.com.Throw", _)) = false 266 | nothrows (Const ("Language.com.Catch", _) $ a $ b) = 267 nothrows a orelse nothrows b 268 | nothrows (Const ("Language.call", _) $ i $ f $ r $ (Abs (_, _, (Abs (_, _, c))))) 269 = nothrows c 270 | nothrows _ = false (* who knows *) 271 272fun extract_iscall c = 273 let 274 fun f (Const ("Language.call", _)) = true 275 | f _ = false 276 in 277 f (head_of c) 278 end; 279 280fun ceqv_thms ctxt = Proof_Context.get_thms ctxt "ceqv_rules"; 281fun xpres_thms ctxt = Proof_Context.get_thms ctxt "xpres_rules"; 282 283fun my_thm ctxt name = Proof_Context.get_thm ctxt name 284fun my_thms ctxt name = Proof_Context.get_thm ctxt name 285 286fun my_simp_tac trace depth ctxt n = 287 prim_trace_tac' (#trace_simp trace) depth "my_simp_tac" ctxt (SOLVE' (asm_full_simp_tac ctxt)) n 288 289fun xpres_tac trace depth ctxt n = let 290 fun rest_tac n = (resolve_tac ctxt @{thms "match_xpres"} n) 291 THEN_ELSE 292 (xpres_tac trace (depth + 1) ctxt n, my_simp_tac trace (depth + 1) ctxt n) 293in 294 prim_trace_tac' (#trace_xpres trace) depth "XP" ctxt ((fn n => DETERM (resolve_tac ctxt (xpres_thms ctxt) n)) THEN_ALL_NEW rest_tac) n 295end; 296 297fun FOCUS_PREMS_ctxt tac = Subgoal.FOCUS_PREMS (fn focus => tac (#context focus) (#prems focus)) 298 299fun corres_ceqv_tac trace depth ctxt n = let 300 val depth' = depth + 1 301 302 (* This ugliness is so we can do this very quickly --- the 303 simplifier was just taking too long. It seems that the refl rule 304 produces multiple results, hence the DETERM *) 305 fun tac ctxt [p] = (rewrite_goals_tac ctxt [p RS @{thm "eq_reflection"}]) 306 THEN ((DETERM (resolve_tac ctxt [refl] 1))) 307 | tac _ _ = let val _ = tracing "WARNING: ceqv non-singleton case" in no_tac end; 308 val my_eq_tac = prim_trace_tac' (#trace_ceqv trace) depth' "CEQV xfI" ctxt (FOCUS_PREMS_ctxt tac ctxt) 309 310 (* This ugliness makes back-tracking more efficient, I hope *) 311 val rest_tac = FIRST_COMMIT' [(resolve_tac ctxt @{thms "match_ceqv"}, corres_ceqv_tac trace depth' ctxt), 312 (resolve_tac ctxt @{thms "match_xpres"}, xpres_tac trace depth' ctxt), 313 (resolve_tac ctxt @{thms "rewrite_xfI"}, my_eq_tac), 314 (fn _ => all_tac, my_simp_tac trace depth' ctxt)] 315in 316 prim_trace_tac' (#trace_this trace) depth "CEQV" ctxt (resolve_tac ctxt (ceqv_thms ctxt) THEN_ALL_NEW rest_tac) n 317end; 318 319fun corres_solve_ceqv_old (trace : trace_opts) (depth : int) ctxt = 320 EVERY' [REPEAT_DETERM' (Rule_Insts.thin_tac ctxt "_" []), 321 SOLVE' (corres_ceqv_tac trace depth ctxt)] 322 323val ceqv_xpres_ceqvI = @{thm ceqv_xpres_ceqvI}; 324val ceqv_xpres_rules = @{thms ceqv_xpres_rules}; 325val ceqv_xpres_FalseI = @{thm ceqv_xpres_FalseI}; 326val ceqv_xpres_rewrite_basic_left_cong = @{thm ceqv_xpres_rewrite_basic_left_cong}; 327val ceqv_xpres_rewrite_basic_refl = @{thm ceqv_xpres_rewrite_basic_refl}; 328val ceqv_xpres_basic_preserves_TrueI = @{thm ceqv_xpres_basic_preserves_TrueI}; 329val ceqv_xpres_basic_preserves_FalseI = @{thm ceqv_xpres_basic_preserves_FalseI}; 330val ceqv_xpres_lvar_nondet_init_TrueI = @{thm ceqv_xpres_lvar_nondet_init_TrueI}; 331val ceqv_xpres_lvar_nondet_init_FalseI = @{thm ceqv_xpres_lvar_nondet_init_FalseI}; 332val ceqv_xpres_rewrite_set_rules = @{thms ceqv_xpres_rewrite_set_rules}; 333val ceqv_xpres_eq_If_rules = @{thms ceqv_xpres_eq_If_rules}; 334 335val apply_ceqv_xpres_rules_trace = ref @{term True}; 336 337fun apply_ceqv_xpres_rules1 ctxt rules _ n = DETERM 338 (resolve_tac ctxt rules n 339 ORELSE (SUBGOAL (fn (t, n) => 340 (warning ("apply_ceqv_xpres_rules: no rule applied" 341 ^ " - see CtacImpl.apply_ceqv_xpres_rules_trace"); 342 apply_ceqv_xpres_rules_trace := t; 343 resolve_tac ctxt [ceqv_xpres_FalseI] n)) n)); 344 345fun apply_ceqv_xpres_rules ctxt = let 346 val seq = ceqv_simpl_seq ctxt 347 val ex_rules = if seq then [@{thm ceqv_xpres_While_simpl_sequence}] 348 else [] 349 in apply_ceqv_xpres_rules1 ctxt (ex_rules @ ceqv_xpres_rules) end 350 351fun addcongs thms ss = foldl (uncurry Simplifier.add_cong) ss thms 352fun delsplits thms ss = foldl (uncurry Splitter.del_split) ss thms 353 354fun solve_ceqv_xpres_rewrite_basic ctxt n = DETERM 355 (safe_simp_tac (addcongs [ceqv_xpres_rewrite_basic_left_cong] (put_simpset HOL_basic_ss ctxt)) n 356 THEN resolve_tac ctxt [ceqv_xpres_rewrite_basic_refl] n); 357 358fun solve_ceqv_xpres_basic_preserves ctxt n = DETERM 359 ((resolve_tac ctxt [ceqv_xpres_basic_preserves_TrueI] n 360 THEN SOLVE' (safe_simp_tac ctxt) n) 361 ORELSE resolve_tac ctxt [ceqv_xpres_basic_preserves_FalseI] n); 362 363fun solve_ceqv_xpres_lvar ctxt n = DETERM 364 ((resolve_tac ctxt [ceqv_xpres_lvar_nondet_init_TrueI] n 365 THEN SOLVE' (safe_simp_tac ctxt) n) 366 ORELSE resolve_tac ctxt [ceqv_xpres_lvar_nondet_init_FalseI] n); 367 368fun abstract_upds ctxt t = let 369 val sT = domain_type (fastype_of t) 370 fun inner (upd $ updf $ t) = upd :: inner t 371 | inner (Bound 0) = [] 372 | inner t = raise TERM ("strip_upds", [t]) 373 val upds = inner (betapply (t, Bound 0)) |> List.rev 374 val xs = map (dest_Const #> snd #> domain_type 375 #> curry (op -->) sT #> pair "x") upds 376 |> Variable.variant_frees ctxt [] |> map Free 377 val upd = Abs ("s", sT, fold (fn (u, x) => fn s => u $ (x $ Bound 0) $ s) 378 (upds ~~ xs) (Bound 0)) 379 in (upd, xs) end 380 381fun ceqv_restore_args_tac ctxt = SUBGOAL (fn (t, n) => case 382 Envir.beta_eta_contract (Logic.strip_assums_concl t) 383 of @{term Trueprop} 384 $ (Const (@{const_name ceqv_xpres_call_restore_args}, _) $ i $ f $ _) 385 => let 386 val cnames = Term.add_const_names f [] 387 val (f, xs) = abstract_upds ctxt f 388 val sT = domain_type (fastype_of f) 389 390 val proc = dest_Const i |> fst |> Long_Name.base_name 391 val pinfo = Hoare.get_data ctxt |> #proc_info 392 val params = Symtab.lookup pinfo proc |> the |> #params 393 |> filter (fn (v, _) => v = HoarePackage.In) 394 val new_upds = map (snd #> suffix Record.updateN #> Syntax.read_term ctxt 395 #> dest_Const #> fst) params 396 |> filter_out (member (op =) cnames) 397 398 fun add_upd upd f = let 399 val updC = Syntax.parse_term ctxt upd 400 val accC = Syntax.parse_term ctxt (unsuffix Record.updateN upd) 401 in Abs ("s", sT, updC $ Abs ("x", dummyT, accC $ Bound 1) 402 $ (f $ Bound 0)) end 403 val g = fold add_upd new_upds f |> Syntax.check_term ctxt |> Thm.cterm_of ctxt 404 val thm = infer_instantiate ctxt [(("f",0), Thm.cterm_of ctxt f), 405 (("g",0), g)] 406 @{thm ceqv_xpres_call_restore_argsI} 407 |> Drule.generalize ([], map (dest_Free #> fst) xs) 408 in resolve_tac ctxt [thm] n THEN simp_tac ctxt n end) 409 410fun ceqv2_consts_and_tacs ctxt = map (apfst (fst o dest_Const)) [ 411 (@{term ceqv_xpres}, apply_ceqv_xpres_rules ctxt), 412 (@{term ceqv_xpres_rewrite_basic}, solve_ceqv_xpres_rewrite_basic), 413 (@{term ceqv_xpres_rewrite_set}, K (resolve_tac ctxt ceqv_xpres_rewrite_set_rules)), 414 (@{term ceqv_xpres_basic_preserves}, solve_ceqv_xpres_basic_preserves), 415 (@{term ceqv_xpres_lvar_nondet_init}, solve_ceqv_xpres_lvar), 416 (@{term ceqv_xpres_eq_If}, K (resolve_tac ctxt ceqv_xpres_eq_If_rules)), 417 (@{term ceqv_xpres_call_restore_args}, ceqv_restore_args_tac) 418]; 419 420fun ceqv2_all_tacs ctxt = SUBGOAL (fn (t, n) => 421 case head_of (HOLogic.dest_Trueprop (Logic.strip_assums_concl t)) of 422 Const (s, T) => (case (filter (fn v => fst v = s) (ceqv2_consts_and_tacs ctxt)) of 423 (v :: _) => (snd v) ctxt n 424 | [] => raise TERM ("ceqv2_all_tacs: unknown head const " ^ s, [Const (s, T), t])) 425 | _ => raise TERM ("ceqv2_all_tacs: unknown form", [t])); 426 427fun ceqv2_tac ctxt n = let 428in 429 resolve_tac ctxt [ceqv_xpres_ceqvI] n THEN 430 SELECT_GOAL (REPEAT_DETERM (eresolve_tac ctxt [thin_rl] 1) 431 THEN REPEAT_DETERM (ceqv2_all_tacs ctxt 1)) n 432end; 433 434fun corres_solve_ceqv (trace : trace_opts) (depth : int) 435 ctxt = ceqv2_tac ctxt; 436 437 438 439fun rename_fresh_tac nm i thm = let 440 (* FIXME: proper name context handling *) 441 val frees = Name.make_context (Term.add_free_names (Thm.prop_of thm) []) 442in 443 rename_tac [#1 (Name.variant nm frees)] i thm 444end; 445 446(* If we are updating globals, we just ignore the new variable *) 447fun corres_pre_lift_tac lift_thm trace depth ctxt xf = 448 prim_trace_tac' (#trace_this trace) depth ("PRE_LIFT_TAC " ^ xf) ctxt 449 let 450 val base_xf = Long_Name.base_name xf 451 val var_name = (unsuffix "_'" base_xf handle Fail _ => "rv") 452in if base_xf = "globals" 453 then resolve_tac ctxt [my_thm ctxt "ccorres_special_trim_Int"] 454 else EVERY' [TRY o resolve_tac ctxt [my_thm ctxt "ccorres_introduce_UNIV_Int_when_needed"], 455 Rule_Insts.res_inst_tac ctxt [((("xf'", 0), Position.none), xf)] [] (my_thm ctxt "ccorres_tmp_lift1"), 456 rename_fresh_tac var_name, 457 resolve_tac ctxt [my_thm ctxt lift_thm], 458 corres_solve_ceqv trace depth ctxt] 459end; 460 461val corres_lift_tac = corres_pre_lift_tac "ccorres_tmp_lift2"; 462 463fun extract_lhs_xf (ct as (Const ("Language.com.Basic", _) $ _)) = extract_basic_xf ct 464 | extract_lhs_xf (Const ("Language.call", _) $ i $ f $ r $ (Abs (_, _, (Abs (_, _, c))))) 465 = extract_basic_xf c 466 | extract_lhs_xf t = (NONE, NONE) (* raise TERM ("extract_lhs_xf", [t]) *) 467 468fun Call_name (Const ("Language.com.Call", _) $ (Const (name, _))) = name 469 | Call_name t = raise TERM ("Call_name", [t]) 470 471(* eta bites us here *) 472fun isBindE t = 473 case head_of t of 474 (Const ("NonDetMonad.bindE", _)) => true 475 | (Abs (_, _, c)) => isBindE c 476 | _ => false 477 478fun is_call t = 479 case head_of t of 480 (Const ("Language.call", _)) => true 481 | _ => false 482 483fun extract_Seq (Const ("Language.com.Seq", _) $ a $ b) = (a, b) 484 | extract_Seq t = raise TERM ("extract_Seq", [t]) 485 486val concl_dest_ccorres = dest_ccorres o HOLogic.dest_Trueprop o Logic.strip_assums_concl 487val first_prem_dest_ccorres = dest_ccorres o HOLogic.dest_Trueprop o hd o Thm.prems_of 488 489fun xf_of_thm_prem corr = let 490 (* Extract the name of the extraction function. 491 * The xf can be either a variable or a liftxf *) 492 val major_cc = first_prem_dest_ccorres corr 493 val xf_name = case (#xf major_cc) of 494 Var ((x, _), _) => x 495 | _ $ Var ((x, _), _) => x 496 | _ => raise TERM ("Expecting an extraction function", [#xf major_cc]) 497in 498 xf_name 499end; 500 501val guard_is_UNIVI = @{thms "guard_is_UNIVI"} 502 503fun ccorres_cleanup_rest (trace : trace_opts) 504 (depth : int) 505 (ctxt : Proof.context) 506 (n : int) = let 507 val match_ceqv = Proof_Context.get_thms ctxt "match_ceqv" 508 val my_clarsimp_tac = prim_trace_tac' (#trace_this trace) depth "my_clarsimp_tac" ctxt (SOLVE' (clarsimp_tac ctxt)) 509in 510 (FIRST_COMMIT' [(resolve_tac ctxt match_ceqv, corres_solve_ceqv trace depth ctxt), 511 (resolve_tac ctxt guard_is_UNIVI, fn _ => all_tac), (* maybe we should try simp? *) 512 (fn _ => all_tac, my_clarsimp_tac)]) n 513end; 514 515 516fun ccorres_norename_cleanup (trace : trace_opts) 517 (depth : int) 518 (ctxt : Proof.context) 519 (n : int) = let 520 val match_cc = Proof_Context.get_thms ctxt "match_ccorres" 521in 522 prim_trace_tac (#trace_this trace) depth "CLEANUP (no rename)" ctxt 523 ((resolve_tac ctxt match_cc n) THEN_ELSE (all_tac, ccorres_cleanup_rest trace (depth + 1) ctxt n)) 524end; 525 526fun ccorres_rename_cleanup trace depth xf ctxt n = let 527 val match_cc = Proof_Context.get_thms ctxt "match_ccorres" 528 529 val base_xf = Long_Name.base_name xf 530 val var_name = (unsuffix "_'" base_xf handle Fail _ => base_xf) 531in 532 prim_trace_tac (#trace_this trace) depth "CLEANUP" ctxt 533 (resolve_tac ctxt match_cc n THEN_ELSE (rename_fresh_tac var_name n, ccorres_cleanup_rest trace (depth + 1) ctxt n)) 534end; 535 536fun abstract_record_xf_if_required trace depth ctxt xf o_xfr = let 537 (* Used to abstract out record variables *) 538 val abstract_thm = Proof_Context.get_thm ctxt "ccorres_abstract" 539in 540 case o_xfr of 541 SOME (_, true) => prim_trace_tac' (#trace_this trace) depth "ccorres_abstract" ctxt 542 (Rule_Insts.res_inst_tac ctxt [((("xf'", 0), Position.none), xf)] [] abstract_thm 543 THEN_ALL_NEW ccorres_rename_cleanup trace (depth + 1) xf ctxt) 544 | _ => fn _ => all_tac 545end; 546 547(* Returns (is concrete, xf, o_xfr) where xf and o_xfr have been sanitised to remove globals *) 548fun normalise_xf (o_xf, o_xfru) = 549 case o_xf of 550 NONE => (false, xfdc, NONE) 551 | SOME (xf_tp as (xf, tp)) => if (Long_Name.base_name xf) = "globals" then (false, xfdc, NONE) else (true, xf_tp, o_xfru) 552 553(* Options *) 554 555type ctac_opts = { 556 trace : trace_opts, (* debug or not *) 557 use_simp : bool, (* use simplifier or not *) 558 vcg_suffix : string, (* suffix for vcg rule --- "" for use vcg, "_novcg" otherwise *) 559 c_lines : int (* Number of lines of C to use (# times ccorres_rhs_assoc2 + 1) *) 560} 561 562val default_ctac_opts : ctac_opts = { trace = default_trace_opts, use_simp = true, vcg_suffix = "", c_lines = 1 } 563 564fun ctac_opts_trace_update new_trace {trace, use_simp, vcg_suffix, c_lines} = 565 {trace = new_trace trace, use_simp = use_simp, vcg_suffix = vcg_suffix, c_lines = c_lines} 566 567fun ctac_opts_simp_update new_use_simp {trace, use_simp, vcg_suffix, c_lines} = 568 {trace = trace, use_simp = new_use_simp use_simp, vcg_suffix = vcg_suffix, c_lines = c_lines} 569 570fun ctac_opts_vcg_update new_vcg_suffix {trace, use_simp, vcg_suffix, c_lines} = 571 {trace = trace, use_simp = use_simp, vcg_suffix = new_vcg_suffix vcg_suffix, c_lines = c_lines} 572 573fun ctac_opts_c_lines_update new_c_lines {trace, use_simp, vcg_suffix, c_lines} = 574 {trace = trace, use_simp = use_simp, vcg_suffix = vcg_suffix, c_lines = new_c_lines c_lines} 575 576type csymbr_opts = { 577 trace : trace_opts (* debug or not *) 578} 579 580val default_csymbr_opts : csymbr_opts = { trace = default_trace_opts } 581 582fun csymbr_opts_trace_update new_trace {trace} = {trace = new_trace trace} 583 584type ceqv_opts = { 585 trace : trace_opts (* debug or not *) 586} 587 588val default_ceqv_opts : ceqv_opts = { trace = default_trace_opts } 589 590fun ceqv_opts_trace_update new_trace {trace} = {trace = new_trace trace} 591 592(* These are the filters used to determine which assumptions we substitute *) 593fun substp_is_var_eq (Const ("op =", _) $ Var (_, _) $ _) = true 594 | substp_is_var_eq _ = false 595 596fun substp_is_eq (Const ("op =", _) $ _ $ _) = true 597 | substp_is_eq _ = false 598 599fun substp_never _ = false 600 601type cinit_opts = { 602 subst_asms : term -> bool, (* subst assumptions or not *) 603 ignore_call : bool, (* use call_ignore_cong or not *) 604 ccorres_rewrite : bool, (* perform ccorres_rewrite or not *) 605 trace : trace_opts (* debug or not *) 606} 607 608val default_cinit_opts : cinit_opts = 609 {trace = default_trace_opts, subst_asms = substp_is_eq, ccorres_rewrite = true, ignore_call = true} 610 611fun cinit_opts_subst_update new_subst_asms {subst_asms, ignore_call, ccorres_rewrite, trace} = 612 {subst_asms = new_subst_asms subst_asms, ignore_call = ignore_call, ccorres_rewrite = ccorres_rewrite, trace = trace} 613 614fun cinit_opts_call_update new_ignore_call {subst_asms, ignore_call, ccorres_rewrite, trace} = 615 {subst_asms = subst_asms, ignore_call = new_ignore_call ignore_call, ccorres_rewrite = ccorres_rewrite, trace = trace} 616 617fun cinit_opts_ccorres_rewrite_update new_ccorres_rewrite {subst_asms, ignore_call, ccorres_rewrite, trace} = 618 {subst_asms = subst_asms, ignore_call = ignore_call, ccorres_rewrite = new_ccorres_rewrite ccorres_rewrite, trace = trace} 619 620fun cinit_opts_trace_update new_trace {subst_asms, ignore_call, ccorres_rewrite, trace} = 621 {subst_asms = subst_asms, ignore_call = ignore_call, ccorres_rewrite = ccorres_rewrite, trace = new_trace trace} 622 623fun ccorres_doit (opts : ctac_opts) 624 (depth : int) 625 (ctxt : Proof.context) 626 (lhs : term) 627 (record_splits : thm list) 628 (call_splits : thm list) 629 (non_call_splits : thm list) : int -> tactic = 630 let 631 val trace = #trace opts 632 val depth' = depth + 1 633 634 (* Theorems *) 635 val skips = Proof_Context.get_thms ctxt "ctac_skips" 636 val splits = call_splits @ non_call_splits 637 638 val thms = ctac_rules.get (Context.Proof ctxt); 639 640 (* xf stuff *) 641 val (xf_is_concrete, (xf, _), o_xfru) = normalise_xf (extract_lhs_xf lhs) 642 643 val _ = if isSome o_xfru andalso not xf_is_concrete 644 then raise TACTIC ("BUG: not xf_is_concrete && isSome o_xfru") 645 else () 646 647 val xfN = "xf'" 648 val xfrN = "xfr" 649 val xfruN = "xfru" 650 651 (* We try to instantiate the xf if possible: 652 653 * - if we are doing a call, we always need an xf. If the one 654 * we detected was globals (or we didn't detect one), we 655 * use xfdc --- this means that ceqv_xfdc will fire as well 656 657 * - if we are looking at a record, we need to instantiate the 658 * lot. If we detected globals, then we treat it as a non-record case. 659 660 * - otherwise we instantiate the xf if it was a concrete value. 661 *) 662 val split_tac = case o_xfru of 663 SOME ((xfru, _), _) => let 664 val insts = [(((xfN, 0), Position.none), xf), 665 (((xfrN, 0), Position.none), xfu_to_xf xfru), 666 (((xfruN, 0), Position.none), xfru)] 667 in 668 prim_trace_tac' (#trace_this trace) depth "SPLIT (record)" ctxt 669 (FIRST' (map (Rule_Insts.res_inst_tac ctxt insts []) record_splits)) 670 end 671 | NONE => if xf_is_concrete orelse is_call lhs 672 then 673 prim_trace_tac' (#trace_this trace) depth "SPLIT (concrete/call)" ctxt 674 (FIRST' (map (Rule_Insts.res_inst_tac ctxt [(((xfN, 0), Position.none), xf)] []) splits)) 675 else 676 prim_trace_tac' (#trace_this trace) depth "SPLIT (other)" ctxt 677 (resolve_tac ctxt non_call_splits) 678 679 val cctac_simp = if (#use_simp opts) then my_simp_tac trace (depth' + 1) ctxt else (fn _ => all_tac); 680 681 val cctac = prim_trace_tac' (#trace_this trace) depth' "CCTAC" ctxt 682 ((resolve_tac ctxt thms) THEN_ALL_NEW cctac_simp) 683 684 (* solve the remaining subgoals *) 685 val resttac = (resolve_tac ctxt skips) (* Ignore any valids etc. *) 686 ORELSE' 687 ccorres_rename_cleanup trace depth' xf ctxt 688 in 689 (abstract_record_xf_if_required trace depth ctxt xf o_xfru) 690 THEN' 691 (split_tac THEN_ALL_NEW_DIST_FIRST (cctac, resttac)) 692 end; 693 694fun corres_split_tac opts ctxt = 695 SUBGOAL (fn (prem, i) => 696 let 697 val trace = #trace opts 698 val cc = concl_dest_ccorres prem; 699 val (lhs, rhs) = extract_Seq (#conc cc) 700 val vcg_suffix = #vcg_suffix opts 701 val record_splits = Proof_Context.get_thms ctxt ("ctac_splits_record" ^ vcg_suffix) 702 val call_splits = Proof_Context.get_thms ctxt ("ctac_splits_call" ^ vcg_suffix) 703 val non_call_splits = Proof_Context.get_thms ctxt ("ctac_splits_non_call" ^ vcg_suffix) 704 705 in 706 prim_trace_tac' (#trace_this trace) 0 "SPLIT" ctxt 707 (ccorres_doit opts 1 ctxt lhs record_splits call_splits non_call_splits) i 708 end); 709 710(* We ignore vcg options here *) 711fun corres_nosplit_tac opts ctxt = 712 SUBGOAL (fn (prem, i) => 713 let 714 val trace = #trace opts 715 val cc = concl_dest_ccorres prem; 716 val lhs = #conc cc 717 val record_splits = Proof_Context.get_thms ctxt "ctac_nosplit_record" 718 val call_splits = Proof_Context.get_thms ctxt "ctac_nosplit_call" 719 val non_call_splits = Proof_Context.get_thms ctxt "ctac_nosplit_non_call" 720 in 721 prim_trace_tac' (#trace_this trace) 0 "NOSPLIT" ctxt 722 (ccorres_doit opts 1 ctxt lhs record_splits call_splits non_call_splits) i 723 end); 724 725fun corres_ctac opts ctxt n = let 726 val ctxt = Context_Position.set_visible false ctxt 727 val match_seq = Proof_Context.get_thms ctxt "match_ccorres_Seq"; 728 val rhs_assoc2 = Proof_Context.get_thms ctxt "ccorres_rhs_assoc2"; 729 val ctac_pre_rules = ctac_pre.get (Context.Proof ctxt); 730 val ctac_post_rules = ctac_post.get (Context.Proof ctxt); 731in 732 (* Apply as many pre rules as possible *) 733 (REPEAT_DETERM (resolve_tac ctxt ctac_pre_rules n)) 734 THEN (REPEAT_DETERM_N (#c_lines opts - 1) (resolve_tac ctxt rhs_assoc2 n)) 735 THEN 736 ((resolve_tac ctxt match_seq n) THEN_ELSE (corres_split_tac opts ctxt n, corres_nosplit_tac opts ctxt n)) 737 THEN REPEAT_DETERM (resolve_tac ctxt ctac_post_rules n) 738end; 739 740 741(* We get multiple unifiers because of the P in ccorres_lift_rhs, 742 * so we need to pick any that matches. Unfortunately, only one of these 743 * is type correct, so we can't just use unification, as we then get an 744 * exception :( *) 745 746fun instantiate_xf_type ctxt tp which thms = let 747 val ctp = Thm.ctyp_of ctxt tp 748 fun inst_type_vars thm = let 749 val (Const ("Pure.all", _) $ Abs (_, tpv, _)) = nth (Thm.prems_of thm) which 750 in 751 Thm.instantiate ([(dest_TVar tpv, ctp)], []) thm 752 end; 753in 754 map inst_type_vars thms 755end handle Bind => raise TACTIC ("Exception Bind in instantiate_xf_type: check order of assumptions to rhss lemmas in Corres_C and function corres_symb_rhs_tac"); 756 757fun corres_symb_rhs_exec_tac (trace : trace_opts) 758 (depth : int) 759 (ctxt : Proof.context) 760 (lhs : term) 761 (insts : ((indexname * Position.T) * string) list) 762 (lift_rhss : thm list) 763 (xf : string) 764 (n : int) (* Ugh. This makes the function sufficiently lazy *) 765 (t : thm) : thm Seq.seq = let 766 val depth' = depth + 1 767 768 val proc_name = (Long_Name.base_name o unsuffix "_'proc" o call_name) lhs 769 770 (* You would think this exists in hoare_package. I can't find it ... *) 771 val proc_spec = Proof_Context.get_thm ctxt (suffix "_spec" proc_name) 772 val proc_mod = Proof_Context.get_thm ctxt (suffix "_modifies" proc_name) 773 774 val spec_rules = [proc_spec] RLN (3, lift_rhss) 775 val rule = case ([proc_mod] RLN (3, spec_rules)) of 776 [rule] => rule 777 | [] => raise THM ("corres_symb_rhs_exec_tac: No unifiers for " ^ proc_name, 778 3, [proc_spec, proc_mod] @ [@{thm TrueI}] @ spec_rules @ [@{thm TrueI}] @ lift_rhss) 779 | xs => raise THM ("corres_symb_rhs_exec_tac: Multiple unifiers for " ^ proc_name, 780 3, [proc_spec, proc_mod] @ [@{thm TrueI}] @ spec_rules @ [@{thm TrueI}] @ lift_rhss 781 @[@{thm TrueI}] @ xs) 782in 783 prim_trace_tac' (#trace_this trace) depth ("CSYMBR (" ^ proc_name ^ ")") ctxt 784 (Rule_Insts.res_inst_tac ctxt insts [] rule THEN_ALL_NEW ccorres_rename_cleanup trace depth' xf ctxt) n t 785end; (* handle TERM e => no_tac 786 | ERROR e => no_tac *) 787 788fun corres_trim_lvar_nondet_init_tac trace depth ctxt known_guard = let 789 val match_seq = Proof_Context.get_thms ctxt "match_ccorres_Seq"; 790 791 fun tac (prem, i) = let 792 val remove_lvar_init = Proof_Context.get_thm ctxt 793 (if known_guard 794 then "ccorres_lift_rhs_remove_lvar_init" 795 else "ccorres_lift_rhs_remove_lvar_init_unknown_guard") 796 in 797 prim_trace_tac' (#trace_this trace) depth "LVAR_INIT" ctxt 798 ((resolve_tac ctxt [remove_lvar_init]) 799 THEN_ALL_NEW (ccorres_norename_cleanup trace depth ctxt)) i 800 end; 801in 802 resolve_tac ctxt match_seq THEN' SUBGOAL tac 803end; 804 805fun corres_symb_rhs_tac (opts : ceqv_opts) ctxt = 806let 807 val ctxt = Context_Position.set_visible false ctxt 808 val trace = #trace opts 809 val depth = 0 810 val depth' = depth + 1 811 812 val match_seq = Proof_Context.get_thms ctxt "match_ccorres_Seq"; 813 fun tac (prem, i) = let 814 (* Theorems *) 815 val gen_asm = Proof_Context.get_thms ctxt "ccorres_gen_asm2" 816 (* Only simplify the rhs of the intersection in the concrete guard *) 817 val cc_cong = Proof_Context.get_thms ctxt "ccorres_special_Int_cong" 818 (* Used to discriminate between call and Basic *) 819 val match_call_Seq = Proof_Context.get_thms ctxt "match_ccorres_call_Seq" 820 821 (* xf and friends *) 822 val cc = concl_dest_ccorres prem; 823 val (lhs, rhs) = extract_Seq (#conc cc) 824 val (_, (xf, tp), o_xfru) = normalise_xf (extract_lhs_xf lhs) 825 826 val xfN = "xf'" (* _should_ be OK here *) 827 val xfrN = "xfr" 828 val xfruN = "xfru" 829 830 val inst_xf_tp = instantiate_xf_type ctxt tp 1 (* xfxfr *) 831 832 (* If we are in a record, return the appropriate thm names and the instantiation for xfr, 833 * otherwise return those for the non-record case *) 834 val (xfr_inst, inst_tp, record_suffix, new_xf_name) 835 = case o_xfru of 836 NONE => ([], 837 inst_xf_tp, 838 "", 839 xf) 840 | SOME ((xfru, tpru), _) => ([(((xfrN, 0), Position.none), xfu_to_xf xfru), (((xfruN, 0), Position.none), xfru)], 841 inst_xf_tp o instantiate_xf_type ctxt (domain_type (domain_type tpru)) 4, (* xfrxfru *) 842 "_record", 843 xfu_to_xf xfru) 844 845 val xf_inst = [(((xfN, 0), Position.none), xf)] 846 847 val ss = addcongs cc_cong ctxt |> delsplits @{thms if_splits} 848 849 val basic_thm = Proof_Context.get_thm ctxt ("ccorres_lift_rhs_Basic" ^ record_suffix) 850 (* The Basic_record lemmas don't seem to require xfr and xfru to be instantiated *) 851 val basic_tac = 852 prim_trace_tac' (#trace_this trace) depth "CSYMBR (Basic)" ctxt 853 ((Rule_Insts.res_inst_tac ctxt xf_inst [] basic_thm) THEN_ALL_NEW (ccorres_norename_cleanup trace depth' ctxt)) 854 855 val call_thms' = Proof_Context.get_thms ctxt ("ccorres_lift_rhss" ^ record_suffix) 856 val call_thms = inst_tp call_thms' 857 val _ = forall (fn (t, t') => Thm.concl_of t <> Thm.concl_of t') (call_thms ~~ call_thms') 858 orelse raise THM ("corres_symb_rhs_tac: failed to inst_tp", 1, call_thms @ call_thms') 859 val call_tac = EVERY' [corres_symb_rhs_exec_tac trace depth ctxt lhs (xf_inst @ xfr_inst) call_thms new_xf_name, 860 (* Yes, simp should ignore assumptions -- this simplifies away the state dep. on i *) 861 TRY' (simp_tac ss), 862 TRY' (resolve_tac ctxt gen_asm)] 863 in 864 (abstract_record_xf_if_required trace depth ctxt xf o_xfru i) 865 THEN 866 (* prim_trace_tac trace_ctac "POST abstract:\n" ctxt *) 867 ((resolve_tac ctxt match_call_Seq i) THEN_ELSE (call_tac i, basic_tac i)) 868 end; 869in 870 (resolve_tac ctxt match_seq THEN' SUBGOAL tac) ORELSE' (corres_trim_lvar_nondet_init_tac trace depth ctxt false) 871end; 872 873fun substutite_asm_eqs ctxt f = let 874 val concl = HOLogic.dest_Trueprop o Thm.concl_of 875 876 fun mk_meta_eq p = p RS @{thm "eq_reflection"} 877 878 fun tac ctxt ps = rewrite_goals_tac ctxt (map mk_meta_eq (filter (f o concl) ps)) 879in 880 FOCUS_PREMS_ctxt tac ctxt 881end; 882 883(* tactical which solves the current subgoal and all subsequent subgoals using the same tac --- like 884 REPEAT_ALL_NEW but without the TRY *) 885fun MY_REPEAT_ALL_NEW tac = 886 tac THEN_ALL_NEW (fn i => MY_REPEAT_ALL_NEW tac i); 887 888fun corres_boilerplate_tac opts unfold_haskell_p xfs ctxt = let 889 val ctxt = Context_Position.set_visible false ctxt 890 val trace = #trace opts 891 val depth = 0 892 val depth' = depth + 1 893 894 fun tac (prem, i) = let 895 (* xf and friends *) 896 val cc = concl_dest_ccorres prem; 897 val conc_proc_name = (Long_Name.base_name o unsuffix "_'proc" o Call_name) (#conc cc) 898 899 (* theorems *) 900 val Call_thm = Proof_Context.get_thms ctxt "ccorres_Call" 901 val boilerplace_simp_dels = Proof_Context.get_thms ctxt "ccorres_boilerplace_simp_dels" 902 val ccorres_rhs_assoc = Proof_Context.get_thms ctxt "ccorres_rhs_assoc" 903 val ccorres_guard_imp2 = Proof_Context.get_thms ctxt "ccorres_guard_imp2" 904 val call_ignore_cong = @{thms "call_ignore_cong"} 905 906 val ccorres_trim_redundant_throw = Proof_Context.get_thms ctxt "ccorres_trim_redundant_throw" 907 val trim_redundant_throw_tac = resolve_tac ctxt ccorres_trim_redundant_throw THEN_ALL_NEW ccorres_norename_cleanup trace depth' ctxt 908 909 val ccorres_trim_DontReach = Proof_Context.get_thms ctxt "ccorres_special_trim_guard_DontReach_pis" 910 val pis_thms = Proof_Context.get_thms ctxt "push_in_stmt_rules" 911 val trim_DontReach_tac = resolve_tac ctxt ccorres_trim_DontReach 912 THEN' (SOLVE' (MY_REPEAT_ALL_NEW (resolve_tac ctxt pis_thms))) 913 914 val conc_impl = Proof_Context.get_thm ctxt (conc_proc_name ^ "_impl") 915 val conc_body = Proof_Context.get_thms ctxt (conc_proc_name ^ "_body_def") 916 (* Unfold body def in the impl rule --- maybe get the parser to do this? *) 917 val conc_impl' = Simplifier.rewrite_rule ctxt conc_body conc_impl 918 919 val abs_unfold_tac = 920 if unfold_haskell_p then let 921 val abs_proc_name = (Long_Name.base_name o fst o dest_Const o head_of) (#abs cc) 922 val abs_proc_def = Proof_Context.get_thms ctxt (abs_proc_name ^ "_def") 923 in 924 Simplifier.rewrite_goal_tac ctxt abs_proc_def 925 end 926 else 927 (fn _ => all_tac) 928 929 val ccorres_rewrite_tac = Method.NO_CONTEXT_TACTIC ctxt 930 (Method_Closure.apply_method ctxt @{method "ccorres_rewrite"} [] [] [] ctxt []) 931 932 val ss = simpset_of (ctxt delsimps boilerplace_simp_dels |> addcongs 933 (if #ignore_call opts then call_ignore_cong else [])) 934 in 935 prim_trace_tac' (#trace_this trace) depth "CINIT" ctxt 936 (EVERY' [ 937 (* Unfold abstract side *) 938 abs_unfold_tac, 939 (* Get function body *) 940 resolve_tac ctxt Call_thm THEN' resolve_tac ctxt [conc_impl'], 941 (* Remove any superfluous return/Guard DontReach at the end *) 942 REPEAT_DETERM' (FIRST' [trim_redundant_throw_tac , trim_DontReach_tac]), 943 (* Fix associativity of ;; *) 944 REPEAT_DETERM' (resolve_tac ctxt ccorres_rhs_assoc), 945 (* Simplify bodies, ignoring parts of calls *) 946 TRY' (asm_full_simp_tac (put_simpset ss ctxt)), 947 (* Simplify, using ccorres_rewrite *) 948 TRY' (fn _ => if #ccorres_rewrite opts then ccorres_rewrite_tac else all_tac), 949 (* Lift any variables *) 950 EVERY' (map (corres_pre_lift_tac "ccorres_init_tmp_lift2" trace depth' ctxt) (rev xfs)), 951 (* Substitute any lifted variable equalities. The option is the predicate to use on assumptions *) 952 substutite_asm_eqs ctxt (#subst_asms opts), 953 (* Remove local variable initialisers *) 954 REPEAT_DETERM' (corres_trim_lvar_nondet_init_tac trace depth' ctxt true), 955 (* Do guard implication *) 956 resolve_tac ctxt ccorres_guard_imp2]) i 957 end; 958in 959 SUBGOAL tac 960end; 961 962(* Exported tactics *) 963 964val ccorresN = "ccorres" 965val preN = "pre" 966val onlyN = "only" 967val liftoptN = "lift" 968 969val trace_allN = "trace" (* trace everything *) 970val trace_ceqvN = "trace_ceqv" (* trace ceqv, xpres, and simp *) 971 972val use_simpN = "use_simp" (* default *) 973val no_simpN = "no_simp" 974 975val subst_asm_varsN = "subst_asm_vars" 976val subst_asmN = "subst_asm" (* default *) 977val no_subst_asmN = "no_subst_asm" 978 979val ignore_callN = "ignore_call" (* default *) 980val no_ignore_callN = "no_ignore_call" 981 982val no_vcgN = "no_vcg" 983val use_vcgN = "use_vcg" (* default *) 984 985val c_linesN = "c_lines" 986 987val ccorres_rewriteN = "ccorres_rewrite" (* default *) 988val no_ccorres_rewriteN = "no_ccorres_rewrite" 989 990val C_simpN = "C_simp" 991val C_simpThm = @{named_theorems "C_simp"} 992 993(* val auto_vcgN = "auto_vcg" *) 994 995fun thm_mod_add_del_only name att_add att_del att_clear = 996 [Args.$$$ name -- Scan.option Args.add -- Args.colon >> K (Method.modifier att_add @{here}), 997 Args.$$$ name -- Args.del -- Args.colon >> K (Method.modifier att_del @{here}), 998 Args.$$$ name -- Args.$$$ onlyN -- Args.colon >> K {init = Context.proof_map att_clear, attribute = att_add, pos = @{here}}] 999 1000val ctac_add_del_only = 1001 [Args.add -- Args.colon >> K (Method.modifier ctac_add @{here}), 1002 Args.del -- Args.colon >> K (Method.modifier ctac_del @{here}), 1003 Args.$$$ onlyN -- Args.colon >> K {init = Context.proof_map ctac_clear, attribute = ctac_add, pos = @{here}}] 1004 1005val ctac_modifiers = 1006 Simplifier.simp_modifiers 1007 @ thm_mod_add_del_only ccorresN ctac_add ctac_del ctac_clear 1008 @ thm_mod_add_del_only preN ctac_pre_add ctac_pre_del ctac_pre_clear 1009 @ ctac_add_del_only 1010 1011val csymbr_modifiers = 1012 Simplifier.simp_modifiers 1013 1014val C_simp_add = Thm.declaration_attribute (Named_Theorems.add_thm C_simpThm) 1015val C_simp_del = Thm.declaration_attribute (Named_Theorems.del_thm C_simpThm) 1016val C_simp_clear = Named_Theorems.clear C_simpThm 1017 1018val boilerplate_modifiers = 1019 Simplifier.simp_modifiers 1020 @ thm_mod_add_del_only C_simpN C_simp_add C_simp_del C_simp_clear 1021 1022structure P = Parse; 1023 1024val ctac_options = 1025 [Args.$$$ trace_allN >> K (ctac_opts_trace_update (K all_trace_opts)), 1026 Args.$$$ trace_ceqvN >> K (ctac_opts_trace_update set_ceqv_trace_opts), 1027 Args.$$$ use_simpN >> K (ctac_opts_simp_update (K true)), 1028 Args.$$$ no_simpN >> K (ctac_opts_simp_update (K false)), 1029 Args.$$$ use_vcgN >> K (ctac_opts_vcg_update (K "")), 1030 Args.$$$ no_vcgN >> K (ctac_opts_vcg_update (K "_novcg")), 1031 Args.$$$ c_linesN |-- P.nat >> (fn n => (ctac_opts_c_lines_update (K n))) 1032(* , Args.$$$ auto_vcgN >> K (ctac_opts_vcg_update NONE) *)] 1033 1034val csymbr_options = 1035 [Args.$$$ trace_allN >> K (csymbr_opts_trace_update (K all_trace_opts)), 1036 Args.$$$ trace_ceqvN >> K (csymbr_opts_trace_update set_ceqv_trace_opts)] 1037 1038val cinit_options = 1039 [Args.$$$ subst_asmN >> K (cinit_opts_subst_update (K substp_is_eq)), 1040 Args.$$$ no_subst_asmN >> K (cinit_opts_subst_update (K substp_never)), 1041 Args.$$$ subst_asm_varsN >> K (cinit_opts_subst_update (K substp_is_var_eq)), 1042 Args.$$$ ignore_callN >> K (cinit_opts_call_update (K true)), 1043 Args.$$$ no_ignore_callN >> K (cinit_opts_call_update (K false)), 1044 Args.$$$ no_ccorres_rewriteN >> K (cinit_opts_ccorres_rewrite_update (K false)), 1045 Args.$$$ trace_allN >> K (cinit_opts_trace_update (K all_trace_opts)), 1046 Args.$$$ trace_ceqvN >> K (cinit_opts_trace_update set_ceqv_trace_opts)] 1047 1048val ceqv_options = 1049 [Args.$$$ trace_allN >> K (ceqv_opts_trace_update (K all_trace_opts)), 1050 Args.$$$ trace_ceqvN >> K (ceqv_opts_trace_update set_ceqv_trace_opts)] 1051 1052fun apply [] x = x 1053 | apply (f :: fs) x = apply fs (f x); 1054 1055val corres_ctac_tactic = let 1056 fun tac upds ctxt = Method.SIMPLE_METHOD' (corres_ctac (apply upds default_ctac_opts) ctxt); 1057 1058 val option_args = Args.parens (P.list (Scan.first ctac_options)) 1059 val opt_option_args = Scan.lift (Scan.optional option_args []) 1060in 1061 opt_option_args --| Method.sections ctac_modifiers 1062 >> tac 1063end; 1064 1065fun corres_pre_abstract_args lift_fn = 1066 let 1067 fun tac (xfs : string list) (ctxt : Proof.context) 1068 = Method.SIMPLE_METHOD' (EVERY' (map (lift_fn ctxt) (rev xfs))) 1069 in 1070 Args.context |-- Scan.lift (Scan.repeat1 Args.name) >> tac 1071 end; 1072 1073(* These differ only in the behaviour wrt the concrete guards --- the first abstracts the new variable, the second just drops it *) 1074val corres_abstract_args = corres_pre_abstract_args (corres_pre_lift_tac "ccorres_tmp_lift2" default_trace_opts 0); 1075val corres_abstract_init_args = corres_pre_abstract_args (corres_pre_lift_tac "ccorres_init_tmp_lift2" default_trace_opts 0); 1076 1077val corres_symb_rhs = let 1078 fun tac upds ctxt = Method.SIMPLE_METHOD' (corres_symb_rhs_tac (apply upds default_csymbr_opts) ctxt); 1079 1080 val option_args = Args.parens (P.list (Scan.first csymbr_options)) 1081 val opt_option_args = Scan.lift (Scan.optional option_args []) 1082in 1083 opt_option_args --| Method.sections csymbr_modifiers >> tac 1084end; 1085 1086val corres_ceqv = let 1087 fun tac upds ctxt = Method.SIMPLE_METHOD' (corres_solve_ceqv (#trace (apply upds default_ceqv_opts)) 0 ctxt); 1088 1089 val option_args = Args.parens (P.list (Scan.first ceqv_options)) 1090 val opt_option_args = Scan.lift (Scan.optional option_args []) 1091in 1092 opt_option_args --| Method.sections [] >> tac 1093end; 1094 1095(* Does all the annoying boilerplate stuff at the start of a ccorres rule. 1096 * We should be able to get the xfs from the goal ... *) 1097fun corres_boilerplate unfold_haskell_p = let 1098 fun tac (upds, xfs : string list) ctxt 1099 = Method.SIMPLE_METHOD' (corres_boilerplate_tac (apply upds default_cinit_opts) unfold_haskell_p xfs ctxt) 1100 1101 val var_lift_args = Args.$$$ liftoptN |-- Args.colon |-- 1102 Scan.repeat (Scan.unless (Scan.first boilerplate_modifiers) Args.name) 1103 val option_args = Args.parens (P.list (Scan.first cinit_options)) 1104 val opt_var_lift_args = Scan.lift (Scan.optional option_args [] -- Scan.optional var_lift_args []) 1105in 1106 opt_var_lift_args --| Method.sections boilerplate_modifiers >> tac 1107end; 1108 1109(* Debugging *) 1110 1111val corres_print_xf = let 1112 fun tac (ctxt : Proof.context) (prem, i) = let 1113 val cc = concl_dest_ccorres prem; 1114 val (lhs, rhs) = extract_Seq (#conc cc) 1115 val (is_concr, (xf, _), o_xfru) = normalise_xf (extract_lhs_xf lhs) 1116 1117 fun b_to_s b = if b then "true" else "false" 1118 1119 val _ = tracing ("xf: " ^ xf ^ " (concrete: " ^ b_to_s is_concr ^ ")") 1120 val _ = case o_xfru of 1121 NONE => tracing ("xfru: NONE") 1122 | SOME ((xfru', _), b) => tracing ("xfru: " ^ xfru' ^ " (abstract required: " ^ b_to_s b ^ ")") 1123 in 1124 all_tac 1125 end; 1126 fun method ctxt = Method.SIMPLE_METHOD' (SUBGOAL (tac ctxt)) 1127in 1128 Args.context >> K method 1129end; 1130 1131end; 1132