blob: 1e23df1a64a3383c4bc292e411c0b411bcda019d [file]
(*
* Copyright 2018, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*)
(*
* Check for Cogent constructs that we can't handle yet.
* This is meant to document known limitations in the Cogent-C refinement tool
* and give clearer error messages.
*)
theory Cogent_Corres_Sanity_Check imports
"Cogent.Cogent"
begin
ML \<open>
structure Cogent_Corres_Sanity_Check = struct
(* Indirect calls are not supported. *)
fun check_indirect_calls Cogent_functions ctxt =
Cogent_functions
|> filter (fn f => let
val def = Proof_Context.get_thm ctxt (f ^ "_def")
in exists_subterm (fn t => case t of
Const (@{const_name App}, _) $ (Const (@{const_name Fun}, _) $ _ $ _) => false
| Const (@{const_name App}, _) $ (Const (@{const_name AFun}, _) $ _ $ _) => false
| Const (@{const_name App}, _) $ _ => true
| _ => false)
(Thm.prop_of def) end)
|> (fn fs => if null fs then [] else
map (fn f => "Not supported: indirect function application in " ^ f) fs
@ ["Hint: avoid \"let f = expr in f x\"; instead use \"(expr) x\""])
(* Most higher-order function calls are not supported.
* We inspect each function's argument type to determine its order.
* NB: this checks all functions, even those that are not called. *)
fun max a b = if a < b then b else a
fun maximum [] = error "maximum: empty list"
| maximum [x] = x
| maximum (x::xs) = max x (maximum xs)
fun Cogent_function_order (Const (@{const_name TFun}, _) $ argT $ _) =
Cogent_function_order argT + 1
| Cogent_function_order (Const (@{const_name TVar}, _) $ _) =
error "Impossible: monomorphised program should have no TVars"
| Cogent_function_order (Const (@{const_name TVarBang}, _) $ _) =
error "Impossible: monomorphised program should have no TVars"
| Cogent_function_order (Const (@{const_name TCon}, _) $ _ $ _ $ _) =
(* Abstract types can contain function pointers. However, let's not
* worry about that for now. *)
0
| Cogent_function_order (Const (@{const_name TPrim}, _) $ _) =
0
| Cogent_function_order (Const (@{const_name TSum}, _) $ variants) =
HOLogic.dest_list variants |> map (HOLogic.dest_prod #> snd #> HOLogic.dest_prod #> fst #> Cogent_function_order) |> maximum
| Cogent_function_order (Const (@{const_name TProduct}, _) $ T1 $ T2) =
max (Cogent_function_order T1) (Cogent_function_order T2)
| Cogent_function_order (Const (@{const_name TRecord}, _) $ fields $ _) =
HOLogic.dest_list fields |> map (HOLogic.dest_prod #> snd #> HOLogic.dest_prod #> fst #> Cogent_function_order) |> maximum
| Cogent_function_order (Const (@{const_name TUnit}, _)) =
0
| Cogent_function_order t = raise TERM ("Cogent_function_order: expected a Cogent.Type", [t])
fun rhs_of_eq (Const (@{const_name "Trueprop"}, _) $ eq) = rhs_of_eq eq
| rhs_of_eq (Const (@{const_name "Pure.eq"}, _) $ _ $ r) = r
| rhs_of_eq (Const (@{const_name "HOL.eq"}, _) $ _ $ r) = r
| rhs_of_eq t = raise (TERM ("rhs_of_eq", [t]))
fun check_function_order functions max_order ctxt = let
val type_abbrevs = Proof_Context.get_thms ctxt "abbreviated_type_defs"
in functions
|> maps (fn f => let
val fT = Proof_Context.get_thm ctxt (f ^ "_type_def")
|> Simplifier.rewrite_rule ctxt type_abbrevs
|> Thm.prop_of |> rhs_of_eq
val fArgT = HOLogic.dest_prod fT |> snd |> HOLogic.dest_prod |> fst
val ord = Cogent_function_order fArgT
in if ord <= max_order then [] else [(f, ord)] end)
|> map (fn (f, ord) => "Not supported: function " ^ f ^ " is order-" ^ string_of_int ord ^
", maximum is order-" ^ string_of_int max_order)
end
(* Cogent allows function names to contain the prime symbol (').
* But the prime mark isn't legal in C, so the compiler mangles it to "_prime".
* Unfortunately, parts of the verification tools aren't aware of this yet. *)
fun check_function_names all_functions _ =
all_functions
|> filter (fn f => member op= (String.explode f) #"'")
|> map (fn f => "Not supported: name of function " ^ f ^ " contains prime mark (')")
fun check_all Cogent_functions abstract_functions ctxt = let
fun join_errors err_blocks =
filter (not o null) err_blocks
|> maps (fn b => b @ [""])
val err1 = check_indirect_calls Cogent_functions ctxt
val err2 = check_function_order Cogent_functions 0 ctxt
val err3 = check_function_order abstract_functions 1 ctxt
val err4 = check_function_names (Cogent_functions @ abstract_functions) ctxt
in case join_errors [err1, err2, err3, err4] of
[] => ()
| errs => error (space_implode "\n" errs) end
end
\<close>
end