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