-
Notifications
You must be signed in to change notification settings - Fork 108
/
simpl_conv.ML
605 lines (544 loc) · 24.3 KB
/
simpl_conv.ML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*)
(*
* Automatically convert SIMPL code fragments into a monadic form, with proofs
* of correspondence between the two.
*
* The main interface to this module is translate (and helper functions
* convert and define). See AutoCorresUtil for a conceptual overview.
*)
structure SimplConv =
struct
(* Convenience shortcuts. *)
val warning = Utils.ac_warning
val apply_tac = Utils.apply_tac
val the' = Utils.the'
exception FunctionNotFound of string
val simpl_conv_ss = AUTOCORRES_SIMPSET
(*
* Given a function constant name such as "Blah.foo_'proc", guess the underlying
* function name "foo".
*)
fun guess_function_name const_name =
const_name |> unsuffix "_'proc" |> Long_Name.base_name
(* Generate a L1 monad type. *)
fun mk_l1monadT stateT =
Utils.gen_typ @{typ "'a L1_monad"} [stateT]
(*
* Extract the L1 monadic term out of a L1corres constant.
*)
fun get_L1corres_monad @{term_pat "L1corres _ _ ?l1_monad _"} = l1_monad
| get_L1corres_monad t = raise TERM ("get_L1corres_monad", [t])
(*
* Generate a SIMPL term that calls the given function.
*
* For instance, we might return:
*
* "Call foo_'proc"
*)
fun mk_SIMPL_call_term ctxt prog_info target_fn =
@{mk_term "Call ?proc :: (?'s, int, strictc_errortype) com" (proc, 's)}
(#const target_fn, #state_type prog_info)
(*
* Construct a correspondence lemma between a given monadic term and a SIMPL fragment.
*
* The term is of the form:
*
* L1corres check_termination \<Gamma> monad simpl
*)
fun mk_L1corres_prop prog_info check_termination monad_term simpl_term =
@{mk_term "L1corres ?ct ?gamma ?monad ?simpl" (ct, gamma, monad, simpl)}
(Utils.mk_bool check_termination, #gamma prog_info, monad_term, simpl_term)
(*
* Construct a prop claiming that the given term is equivalent to
* a call to the given SIMPL function:
*
* L1corres ct \<Gamma> <term> (Call foo_'proc)
*
*)
fun mk_L1corres_call_prop ctxt prog_info check_termination target_fn term =
mk_L1corres_prop prog_info check_termination term
(mk_SIMPL_call_term ctxt prog_info target_fn)
|> HOLogic.mk_Trueprop
(*
* Convert a SIMPL fragment into a monadic term.
*
* We return the monadic version of the input fragment and a tactic
* to prove correspondence.
*)
fun simpl_conv'
(prog_info : ProgramInfo.prog_info)
(simpl_defs : FunctionInfo.function_info Symtab.table)
(const_to_function : string Termtab.table)
(ctxt : Proof.context)
(callee_terms : (bool * term * thm) Symtab.table)
(measure_var : term)
(simpl_term : term) =
let
fun prove_term subterms base_thm result_term =
let
val subterms' = map (simpl_conv' prog_info simpl_defs const_to_function ctxt
callee_terms measure_var) subterms;
val converted_terms = map fst subterms';
val subproofs = map snd subterms';
val new_term = (result_term converted_terms);
in
(new_term, (resolve_tac ctxt [base_thm] 1) THEN (EVERY subproofs))
end
(* Construct a "L1 monad" term with the given arguments applied to it. *)
fun mk_l1 (Const (a, _)) args =
Term.betapplys (Const (a, map fastype_of args
---> mk_l1monadT (#state_type prog_info)), args)
(* Convert a set construct into a predicate construct. *)
fun set_to_pred t =
(Const (@{const_name L1_set_to_pred},
fastype_of t --> (HOLogic.dest_setT (fastype_of t) --> @{typ bool})) $ t)
in
(case simpl_term of
(*
* Various easy cases of SIMPL to monadic conversion.
*)
(Const (@{const_name Skip}, _)) =>
prove_term [] @{thm L1corres_skip}
(fn _ => mk_l1 @{term "L1_skip"} [])
| (Const (@{const_name Seq}, _) $ left $ right) =>
prove_term [left, right] @{thm L1corres_seq}
(fn [l, r] => mk_l1 @{term "L1_seq"} [l, r])
| (Const (@{const_name Basic}, _) $ m) =>
prove_term [] @{thm L1corres_modify}
(fn _ => mk_l1 @{term "L1_modify"} [m])
| (Const (@{const_name Cond}, _) $ c $ left $ right) =>
prove_term [left, right] @{thm L1corres_condition}
(fn [l, r] => mk_l1 @{term "L1_condition"} [set_to_pred c, l, r])
| (Const (@{const_name Catch}, _) $ left $ right) =>
prove_term [left, right] @{thm L1corres_catch}
(fn [l, r] => mk_l1 @{term "L1_catch"} [l, r])
| (Const (@{const_name While}, _) $ c $ body) =>
prove_term [body] @{thm L1corres_while}
(fn [body] => mk_l1 @{term "L1_while"} [set_to_pred c, body])
| (Const (@{const_name Throw}, _)) =>
prove_term [] @{thm L1corres_throw}
(fn _ => mk_l1 @{term "L1_throw"} [])
| (Const (@{const_name Guard}, _) $ _ $ c $ body) =>
prove_term [body] @{thm L1corres_guard}
(fn [body] => mk_l1 @{term "L1_seq"} [mk_l1 @{term "L1_guard"} [set_to_pred c], body])
| @{term_pat "lvar_nondet_init _ ?upd"} =>
prove_term [] @{thm L1corres_init}
(fn _ => mk_l1 @{term "L1_init"} [upd])
| (Const (@{const_name Spec}, _) $ s) =>
prove_term [] @{thm L1corres_spec}
(fn _ => mk_l1 @{term "L1_spec"} [s])
| (Const (@{const_name guarded_spec_body}, _) $ _ $ s) =>
prove_term [] @{thm L1corres_guarded_spec}
(fn _ => mk_l1 @{term "L1_spec"} [s])
(*
* "call": This is primarily what is output by the C parser. We
* accept input terms of the form:
*
* "call <argument_setup> <proc_to_call> <locals_reset> (%_ s. Basic (<store return value> s))".
*
* In particular, the last argument needs to be of precisely the
* form above. SIMPL, in theory, supports complex expressions in
* the last argument. In practice, the C parser only outputs
* the form above, and supporting more would be a pain.
*)
| (Const (@{const_name call}, _) $ a $ (fn_const as Const (b, _)) $ c $ (Abs (_, _, Abs (_, _, (Const (@{const_name Basic}, _) $ d))))) =>
let
val state_type = #state_type prog_info
val target_fn_name = Termtab.lookup const_to_function fn_const
in
case Option.mapPartial (Symtab.lookup callee_terms) target_fn_name of
NONE =>
(* If no proof of our callee could be found, we emit a call to
* "fail". This may happen for functions without bodies. *)
let
val _ = warning ("Function '" ^ guess_function_name b ^ "' contains no body. "
^ "Replacing the function call with a \"fail\" command.")
in
prove_term [] @{thm L1corres_fail} (fn _ => mk_l1 @{term "L1_fail"} [])
end
| SOME (is_rec, term, thm) =>
let
(*
* If this is an internal recursive call, decrement the measure.
* Or if this is calling a recursive function, use measure_call.
* If the callee isn't recursive, it doesn't use the measure var
* and we can just give an arbitrary value.
*)
val target_fn_name = the target_fn_name
val target_fn = Utils.the' ("missing SIMPL def for " ^ target_fn_name)
(Symtab.lookup simpl_defs target_fn_name)
val target_rec = FunctionInfo.is_function_recursive target_fn
val term' =
if is_rec then
term $ (@{term "recguard_dec"} $ measure_var)
else if target_rec then
@{mk_term "measure_call ?f" f} term
else
term $ @{term "undefined :: nat"}
in
(* Generate the term. *)
(mk_l1 @{term "L1_call"}
[a, term', c, absdummy state_type d],
resolve_tac ctxt [if is_rec orelse not target_rec then
@{thm L1corres_reccall} else @{thm L1corres_call}] 1
THEN resolve_tac ctxt [thm] 1)
end
end
(* TODO : Don't currently support DynCom *)
| other => Utils.invalid_term "a SIMPL term" other)
end
(* Perform post-processing on a theorem. *)
fun cleanup_thm ctxt do_opt trace_opt prog_info fn_name thm =
let
(* For each function, we want to prepend a statement that sets its return
* value undefined. It is actually always defined, but our analysis isn't
* sophisticated enough to realise. *)
fun prepend_undef thm fn_name =
let
val ret_var_name =
Symtab.lookup (ProgramAnalysis.get_fninfo (#csenv prog_info)) fn_name
|> the
|> (fn (ctype, _, _) => NameGeneration.return_var_name ctype |> MString.dest)
val ret_var_setter = Symtab.lookup (#var_setters prog_info) ret_var_name
val ret_var_getter = Symtab.lookup (#var_getters prog_info) ret_var_name
fun try_unify (x::xs) =
((x ()) handle THM _ => try_unify xs)
in
case ret_var_setter of
SOME _ =>
(* Prepend the L1_init code. *)
Utils.named_cterm_instantiate ctxt
[("X", Thm.cterm_of ctxt (the ret_var_setter)),
("X'", Thm.cterm_of ctxt (the ret_var_getter))]
(try_unify [
(fn _ => @{thm L1corres_prepend_unknown_var_recguard} OF [thm]),
(fn _ => @{thm L1corres_prepend_unknown_var} OF [thm]),
(fn _ => @{thm L1corres_prepend_unknown_var'} OF [thm])])
(* Discharge the given proof obligation. *)
|> simp_tac (put_simpset simpl_conv_ss ctxt) 1 |> Seq.hd
| NONE => thm
end
val thm = prepend_undef thm fn_name
(* Conversion combinator to apply a conversion only to the L1 subterm of a
* L1corres term. *)
fun l1conv conv = (Conv.arg_conv (Utils.nth_arg_conv 3 conv))
(* Conversion to simplify guards. *)
fun guard_conv' c =
case (Thm.term_of c) of
(Const (@{const_name "L1_guard"}, _) $ _) =>
Simplifier.asm_full_rewrite (put_simpset simpl_conv_ss ctxt) c
| _ =>
Conv.all_conv c
val guard_conv = Conv.top_conv (K guard_conv') ctxt
(* Apply all the conversions on the generated term. *)
val (thm, guard_opt_trace) = AutoCorresTrace.fconv_rule_maybe_traced ctxt (l1conv guard_conv) thm trace_opt
val (thm, peephole_opt_trace) =
AutoCorresTrace.fconv_rule_maybe_traced ctxt
(l1conv (Simplifier.rewrite (put_simpset HOL_basic_ss ctxt addsimps
(if do_opt then Utils.get_rules ctxt @{named_theorems L1opt} else []))))
thm trace_opt
(* Rewrite exceptions. *)
val (thm, exn_opt_trace) = AutoCorresTrace.fconv_rule_maybe_traced ctxt
(l1conv (ExceptionRewrite.except_rewrite_conv ctxt do_opt)) thm trace_opt
in
(thm,
[("L1 guard opt", guard_opt_trace), ("L1 peephole opt", peephole_opt_trace), ("L1 exception opt", exn_opt_trace)]
|> List.mapPartial (fn (n, tr) => case tr of NONE => NONE | SOME x => SOME (n, AutoCorresData.SimpTrace x))
)
end
(*
* Get theorems about a SIMPL body in a format convenient to reason about.
*
* In particular, we unfold parts of SIMPL where we would prefer to reason
* about raw definitions instead of more abstract constructs generated
* by the C parser.
*)
fun get_simpl_body ctxt simpl_defs fn_name =
let
(* Find the definition of the given function. *)
val simpl_thm = #definition (Utils.the' ("SimplConv.get_simpl_body: no such function: " ^ fn_name)
(Symtab.lookup simpl_defs fn_name))
handle ERROR _ => raise FunctionNotFound fn_name;
(* Unfold terms in the body which we don't want to deal with. *)
val unfolded_simpl_thm =
Conv.fconv_rule (Utils.rhs_conv
(Simplifier.rewrite (put_simpset HOL_basic_ss ctxt addsimps
(Utils.get_rules ctxt @{named_theorems L1unfold}))))
simpl_thm
val unfolded_simpl_term = Thm.concl_of unfolded_simpl_thm |> Utils.rhs_of;
(*
* Get the implementation definition for this function. These rules are of
* the form "Gamma foo_'proc = Some foo_body".
*)
val impl_thm =
Proof_Context.get_thm ctxt (fn_name ^ "_impl")
|> Local_Defs.unfold ctxt [unfolded_simpl_thm]
|> SOME
handle (ERROR _) => NONE
in
(unfolded_simpl_term, unfolded_simpl_thm, impl_thm)
end
fun get_l1corres_thm prog_info simpl_defs const_to_function
check_termination ctxt do_opt trace_opt
fn_name callee_terms measure_var = let
val fn_def = Utils.the' ("SimplConv.get_l1corres_thm: no such function: " ^ fn_name)
(Symtab.lookup simpl_defs fn_name);
val thy = Proof_Context.theory_of ctxt
val (simpl_term, simpl_thm, impl_thm) = get_simpl_body ctxt simpl_defs fn_name
(*
* Do the conversion. We receive a new monadic version of the SIMPL
* term and a tactic for proving correspondence.
*)
val (monad, tactic) = simpl_conv' prog_info simpl_defs const_to_function ctxt
callee_terms measure_var simpl_term
(*
* Wrap the monad in a "L1_recguard" statement, which triggers
* failure when the measure reaches zero. This lets us automatically
* prove termination of the recursive function.
*)
val is_recursive = FunctionInfo.is_function_recursive fn_def
val (monad, tactic) =
if is_recursive then
(Utils.mk_term thy @{term "L1_recguard"} [measure_var, monad],
(resolve_tac ctxt @{thms L1corres_recguard} 1 THEN tactic))
else
(monad, tactic)
(*
* Return a new theorem of correspondence between the original
* SIMPL body (with folded constants) and the output monad term.
*)
in
mk_L1corres_call_prop ctxt prog_info check_termination fn_def monad
|> Thm.cterm_of ctxt
|> Goal.init
|> (case impl_thm of
NONE => apply_tac "unfold SIMPL body" (resolve_tac ctxt @{thms L1corres_undefined_call} 1)
| SOME def => apply_tac "unfold SIMPL body" (resolve_tac ctxt @{thms L1corres_Call} 1 THEN
resolve_tac ctxt [def] 1)
#> apply_tac "solve L1corres" tactic)
|> Goal.finish ctxt
(* Apply simplifications to the L1 term. *)
|> cleanup_thm ctxt do_opt trace_opt prog_info fn_name
end
fun get_body_of_l1corres_thm thm =
(* Extract the monad from the thm. *)
Thm.concl_of thm
|> HOLogic.dest_Trueprop
|> get_L1corres_monad
fun split_conj thm =
(thm RS @{thm conjunct1}) :: split_conj (thm RS @{thm conjunct2})
handle THM _ => [thm]
(* Prove monad_mono for recursive functions. *)
fun l1_monad_mono lthy (l1_defs : FunctionInfo.function_info Symtab.table) =
let
val l1_defs' = Symtab.dest l1_defs;
fun mk_stmt [func] = @{mk_term "monad_mono ?f" f} func
| mk_stmt (func :: funcs) = @{mk_term "monad_mono ?f \<and> ?g" (f, g)} (func, mk_stmt funcs);
val mono_thm = @{term "Trueprop"} $ mk_stmt (map (#const o snd) l1_defs');
val func_expand = map (fn (_, l1_def) =>
EqSubst.eqsubst_tac lthy [0] [Utils.abs_def lthy (#definition l1_def)]) l1_defs';
val tac =
REPEAT (EqSubst.eqsubst_tac lthy [0]
[@{thm monad_mono_alt_def}, @{thm all_conj_distrib} RS @{thm sym}] 1)
THEN resolve_tac lthy @{thms allI} 1 THEN resolve_tac lthy @{thms nat.induct} 1
THEN EVERY (map (fn expand =>
TRY (resolve_tac lthy @{thms conjI} 1)
THEN expand 1
THEN resolve_tac lthy @{thms monad_mono_step_L1_recguard_0} 1) func_expand)
THEN REPEAT (eresolve_tac lthy @{thms conjE} 1)
THEN EVERY (map (fn expand =>
TRY (resolve_tac lthy @{thms conjI} 1)
THEN expand 1
THEN REPEAT (FIRST [assume_tac lthy 1,
resolve_tac lthy @{thms L1_monad_mono_step_rules} 1]))
func_expand);
in
Goal.prove lthy [] [] mono_thm (K tac)
|> split_conj
|> (fn thms => map fst l1_defs' ~~ thms)
|> Symtab.make
end
(* For functions that are not translated, just generate a trivial wrapper. *)
fun mk_l1corres_call_simpl_thm check_termination ctxt simpl_def = let
val const = #const simpl_def
val impl_thm = Proof_Context.get_thm ctxt (#name simpl_def ^ "_impl")
val gamma = safe_mk_meta_eq impl_thm |> Thm.concl_of |> Logic.dest_equals
|> fst |> (fn (f $ _) => f | t => raise TERM ("gamma", [t]))
val thm = Utils.named_cterm_instantiate ctxt
[("ct", Thm.cterm_of ctxt (Utils.mk_bool check_termination)),
("proc", Thm.cterm_of ctxt const),
("Gamma", Thm.cterm_of ctxt gamma)]
@{thm L1corres_call_simpl}
in thm end
(* All L1 functions have the same signature: measure \<Rightarrow> L1_monad *)
fun l1_fn_type prog_info = AutoCorresUtil.measureT --> mk_l1monadT (#state_type prog_info);
(* L1corres for f's callees. *)
fun get_l1_fn_assumption prog_info check_termination simpl_infos ctxt fn_name free _ _ measure_var =
mk_L1corres_call_prop ctxt prog_info check_termination
(Utils.the' ("SimplConv: missing callee def for " ^ fn_name)
(Symtab.lookup simpl_infos fn_name)) (betapply (free, measure_var));
(*
* Convert a single function. Returns a thm that looks like
* \<lbrakk> L1corres ?callee1 (Call callee1_'proc); ... \<rbrakk> \<Longrightarrow>
* L1corres (conversion result...) (Call f_'proc)
* i.e. with assumptions for called functions, which are parameterised as Vars.
*)
fun convert
(lthy: local_theory)
(prog_info: ProgramInfo.prog_info)
(simpl_infos: FunctionInfo.function_info Symtab.table)
(const_to_function: string Termtab.table)
(check_termination: bool)
(do_opt: bool)
(trace_opt: bool)
(l1_function_name: string -> string)
(f_name: string)
: AutoCorresUtil.convert_result =
let
val f_info = Utils.the' ("SimplConv: missing SIMPL def for " ^ f_name) (Symtab.lookup simpl_infos f_name);
(* Fix measure variable. *)
val ([measure_var_name], lthy') = Variable.variant_fixes ["rec_measure'"] lthy;
val measure_var = Free (measure_var_name, AutoCorresUtil.measureT);
(* Add callee assumptions. Note that our define code has to use the same assumption order. *)
val (lthy'', export_thm, callee_terms) =
AutoCorresUtil.assume_called_functions_corres lthy'
(#callees f_info) (#rec_callees f_info)
(K (l1_fn_type prog_info))
(get_l1_fn_assumption prog_info check_termination simpl_infos)
(K [])
l1_function_name
measure_var;
val (thm, opt_traces) =
if #is_simpl_wrapper f_info
then (mk_l1corres_call_simpl_thm check_termination lthy'' f_info, [])
else get_l1corres_thm prog_info simpl_infos const_to_function check_termination lthy''
do_opt trace_opt f_name (Symtab.make callee_terms) measure_var;
val f_body = get_L1corres_monad (HOLogic.dest_Trueprop (Thm.concl_of thm));
(* Get actual recursive callees *)
val rec_callees = AutoCorresUtil.get_rec_callees callee_terms f_body;
(* Return the constants that we fixed. This will be used to process the returned body. *)
val callee_consts =
callee_terms |> map (fn (callee, (_, const, _)) => (callee, const)) |> Symtab.make;
in
{ body = f_body,
(* Expose callee assumptions and generalizes callee vars *)
proof = Morphism.thm export_thm thm,
rec_callees = rec_callees,
callee_consts = callee_consts,
arg_frees = [dest_Free measure_var],
traces = opt_traces
}
end
(* Define a previously-converted function (or recursive function group).
* lthy must include all definitions from l1_callees.
* simpl_defs must include current function set and its immediate callees. *)
fun define
(filename: string)
(prog_info: ProgramInfo.prog_info)
(check_termination: bool)
(l1_function_name: string -> string)
(lthy: local_theory)
(simpl_infos: FunctionInfo.function_info Symtab.table)
(l1_callees: FunctionInfo.function_info Symtab.table)
(funcs: AutoCorresUtil.convert_result Symtab.table)
: FunctionInfo.function_info Symtab.table * local_theory = let
val funcs' = Symtab.dest funcs |>
map (fn result as (name, {proof, arg_frees, ...}) =>
(name, (AutoCorresUtil.abstract_fn_body simpl_infos result,
proof, arg_frees)));
val (new_thms, lthy') =
AutoCorresUtil.define_funcs
FunctionInfo.L1 filename simpl_infos l1_function_name
(K (l1_fn_type prog_info))
(get_l1_fn_assumption prog_info check_termination simpl_infos)
(K [])
@{thm L1corres_recguard_0}
lthy
(Symtab.map (K #corres_thm) l1_callees)
funcs';
val new_defs = Symtab.map (fn f_name => fn (const, def, corres_thm) => let
val f_info = the (Symtab.lookup simpl_infos f_name);
in f_info
|> FunctionInfo.function_info_upd_phase FunctionInfo.L1
|> FunctionInfo.function_info_upd_definition def
|> FunctionInfo.function_info_upd_corres_thm corres_thm
|> FunctionInfo.function_info_upd_const const
|> FunctionInfo.function_info_upd_mono_thm NONE (* done in translate *)
end) new_thms;
in (new_defs, lthy') end;
(*
* Top level translation from SIMPL to a monadic spec.
*
* We accept a filename (the same filename passed to the C parser; the
* parser stashes away important information using this filename as the
* key) and a local theory.
*
* We define a number of new functions (the converted monadic
* specifications of the SIMPL functions) and theorems (proving
* correspondence between our generated specs and the original SIMPL
* code).
*)
fun translate
(filename: string)
(prog_info: ProgramInfo.prog_info)
(simpl_infos: FunctionInfo.function_info Symtab.table)
(existing_simpl_infos: FunctionInfo.function_info Symtab.table)
(existing_l1_infos: FunctionInfo.function_info Symtab.table)
(check_termination: bool)
(do_opt: bool)
(trace_opt: bool)
(add_trace: string -> string -> AutoCorresData.Trace -> unit)
(l1_function_name: string -> string)
(lthy: local_theory)
: FunctionInfo.phase_results =
let
val (simpl_call_graph, simpl_infos) = FunctionInfo.calc_call_graph simpl_infos;
(* Initial function groups, in topological order *)
val initial_results =
#topo_sorted_functions simpl_call_graph
|> map (fn f_names => let
val f_infos =
Symset.dest f_names
|> List.mapPartial (fn f => Option.map (pair f) (Symtab.lookup simpl_infos f))
|> Symtab.make;
in (lthy, f_infos) end)
|> FSeq.of_list;
(* We also need to update the const_to_function table *)
val const_to_function =
Termtab.merge (K false)
(#const_to_function simpl_call_graph,
Symtab.dest existing_simpl_infos
|> map (fn (f, info) => (#raw_const info, f))
|> Termtab.make);
(* Do conversions in parallel. *)
val converted_groups =
AutoCorresUtil.par_convert
(fn lthy => fn simpl_infos =>
convert lthy prog_info simpl_infos const_to_function check_termination
do_opt trace_opt l1_function_name)
existing_simpl_infos initial_results add_trace;
(* Sequence of new function_infos and intermediate lthys *)
val def_results = AutoCorresUtil.define_funcs_sequence
lthy (define filename prog_info check_termination l1_function_name)
existing_simpl_infos existing_l1_infos converted_groups;
(* Produce a mapping from each function group to its L1 phase_infos and the
* earliest intermediate lthy where it is defined. *)
val results =
def_results
|> FSeq.map (fn (lthy, f_defs) => let
(* Add monad_mono proofs. These are done in parallel as well
* (though in practice, they already run pretty quickly). *)
val mono_thms = if FunctionInfo.is_function_recursive (snd (hd (Symtab.dest f_defs)))
then l1_monad_mono lthy f_defs
else Symtab.empty;
val f_defs' = f_defs |> Symtab.map (fn f =>
FunctionInfo.function_info_upd_mono_thm (Symtab.lookup mono_thms f));
in (lthy, f_defs') end);
in
results
end
end