| (* |
| * 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) |
| *) |
| |
| theory CogentHigherOrder imports |
| "Cogent.TypeProofGen" |
| "AutoCorres.AutoCorres" |
| begin |
| |
| (* |
| * Abstract semantics for guessing function arguments to higher-order function calls. |
| * These semantics only work when the hofun and funarg are hardcoded in the function |
| * that contains the hofun call site. |
| * |
| * Example: |
| * -- previously defined |
| * funarg x = ... |
| * hofun (f, x, n) = ... |
| * |
| * -- hofun-funarg call can be inferred correctly |
| * f x = let args = (funarg, x, 42) |
| * in hofun args |
| * |
| * -- multiple (all hardcoded) funargs can be detected inside compound values |
| * f = hofun2 ((funarg1, x), (y, z), Some funarg2) |
| * |
| * -- fails: funarg isn't statically known |
| * f funarg = hofun (funarg, 0, 42) |
| * |
| * -- also fails: farg's value isn't (syntactically) hardcoded |
| * f x = let farg = if true then funarg else funarg |
| * in hofun (farg, x, 42) |
| *) |
| |
| type_synonym 'f FunCall = "('f, 'f expr) sum" |
| |
| (* |
| * Function value annotations for Cogent values. |
| * An InValue lists known function pointers inside a Cogent value and their locations. |
| * Each "InData list" contains information about one part of the value, ending in |
| * an IsFunction that specifies the function name. |
| * (Eg. if nothing is known about a Cogent value, its Invalue is [].) |
| *) |
| datatype 'f InData = InProduct nat |
| | InRecord nat |
| | InSum string (* tag name; used by make_HO_call_hints *) |
| | IsFunction "'f FunCall" |
| |
| type_synonym 'f InValue = "'f InData list list" |
| |
| primrec InValue_of where |
| "InValue_of None = []" |
| | "InValue_of (Some tv) = snd tv" |
| |
| fun unzip :: "('a \<times> 'b) list \<Rightarrow> 'a list \<times> 'b list" where |
| "unzip [] = ([], [])" |
| | "unzip ((a,b)#xs) = (let (as, bs) = unzip xs in (a#as, b#bs))" |
| |
| definition "BadType = undefined" (* type mismatch detected *) |
| |
| (* |
| * FIXME: port this to ML for performance. |
| * |
| * Interpret an expression, returning a list of call sites and |
| * statically known funargs. Return value is (calls \<times> ptrs) where: |
| * \<bullet> calls is a list of (fun, fptrs) pairs describing function calls and their funargs. |
| * Note that first-order calls are included (fptrs will be null). |
| * \<bullet> ptrs points to known function values in the return value of the expression. |
| *) |
| fun funcall_sem :: "'f InValue env \<Rightarrow> 'f expr \<Rightarrow> ('f FunCall \<times> 'f InValue) list \<times> 'f InValue" where |
| "funcall_sem \<gamma> (Var v) = ([], \<gamma> ! v)" |
| | "funcall_sem \<gamma> (AFun absfun _) = ([], [[IsFunction (Inl absfun)]])" |
| | "funcall_sem \<gamma> (Fun f _) = ([], [[IsFunction (Inr f)]])" |
| | "funcall_sem \<gamma> (App (AFun absfun _) x) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x |
| in (calls @ [(Inl absfun, ptrs)], []))" |
| | "funcall_sem \<gamma> (App (Fun f _) x) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x |
| in (calls @ [(Inr f, ptrs)], []))" |
| | "funcall_sem \<gamma> (App f x) = (fst (funcall_sem \<gamma> f) @ fst (funcall_sem \<gamma> x), [])" |
| | "funcall_sem \<gamma> (Con _ tag x) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x |
| in (calls, map ((#) (InSum tag)) ptrs))" |
| | "funcall_sem \<gamma> (Struct tys xs) = |
| (let (callss, ptrss) = unzip (map (funcall_sem \<gamma>) xs) |
| in (concat callss, concat (map (\<lambda>(n, v). map ((#) (InRecord n)) v) (enumerate 0 ptrss))))" |
| | "funcall_sem \<gamma> (Member x f) = |
| (case funcall_sem \<gamma> x of (calls, ptrs) \<Rightarrow> |
| (calls, map tl (filter (\<lambda>ptr. case ptr of InRecord f' # _ \<Rightarrow> f = f' | _ \<Rightarrow> BadType) ptrs)))" |
| | "funcall_sem \<gamma> (Cast _ x) = funcall_sem \<gamma> x" |
| | "funcall_sem \<gamma> (Tuple x y) = (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem \<gamma> y |
| in (calls @ calls', map ((#) (InProduct 0)) ptrs @ map ((#) (InProduct 1)) ptrs'))" |
| | "funcall_sem \<gamma> (Put x f y) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem \<gamma> y |
| in (calls @ calls', |
| filter (\<lambda>ptr. case ptr of InRecord f' # _ \<Rightarrow> f \<noteq> f' | _ \<Rightarrow> BadType) ptrs @ |
| map ((#) (InRecord f)) ptrs'))" |
| | "funcall_sem \<gamma> (Let x y) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem (ptrs # \<gamma>) y |
| in (calls @ calls', ptrs'))" |
| | "funcall_sem \<gamma> (LetBang _ x y) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem (ptrs # \<gamma>) y |
| in (calls @ calls', ptrs'))" |
| | "funcall_sem \<gamma> (Case x c y z) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (ycalls, yptrs) = funcall_sem (map tl (filter (\<lambda>ptr. case ptr of InSum c' # _ \<Rightarrow> c = c' | _ \<Rightarrow> BadType) ptrs) # \<gamma>) y; |
| (zcalls, zptrs) = funcall_sem (map tl (filter (\<lambda>ptr. case ptr of InSum c' # _ \<Rightarrow> c \<noteq> c' | _ \<Rightarrow> BadType) ptrs) # \<gamma>) z |
| in (calls @ ycalls @ zcalls, []))" (* cannot determine ptrs statically *) (* TODO: warn if [yz]ptrs nonempty *) |
| | "funcall_sem \<gamma> (Esac x tag) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x |
| in (calls, map tl (filter (\<lambda>ptr. case ptr of InSum _ # _ \<Rightarrow> True | _ \<Rightarrow> BadType) ptrs)))" |
| | "funcall_sem \<gamma> (If x y z) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (ycalls, yptrs) = funcall_sem \<gamma> y; |
| (zcalls, zptrs) = funcall_sem \<gamma> z |
| in (calls @ ycalls @ zcalls, []))" (* cannot determine ptrs statically *) |
| | "funcall_sem \<gamma> (Take x f y) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem (map tl (filter (\<lambda>ptr. case ptr of InRecord f' # _ \<Rightarrow> f = f' | _ \<Rightarrow> BadType) ptrs) # ptrs # \<gamma>) y |
| in (calls @ calls', ptrs'))" |
| | "funcall_sem \<gamma> (Split x y) = |
| (let (calls, ptrs) = funcall_sem \<gamma> x; |
| (calls', ptrs') = funcall_sem |
| (map tl (filter (\<lambda>ptr. case ptr of InProduct 0 # _ \<Rightarrow> True | _ \<Rightarrow> BadType) ptrs) # |
| map tl (filter (\<lambda>ptr. case ptr of InProduct (Suc 0) # _ \<Rightarrow> True | _ \<Rightarrow> BadType) ptrs) # \<gamma>) y |
| in (calls @ calls', ptrs'))" |
| | "funcall_sem \<gamma> _ = ([], [])" |
| |
| (* Use the simplifier to extract higher-order function call information. *) |
| ML \<open> |
| structure CogentHigherOrder = struct |
| (* ML representation of InData type *) |
| datatype InData = InRecord of int |
| | InProduct of int |
| | InSum of string |
| | IsFunction of (string, string) Either |
| |
| fun dest_funcall (Const (@{const_name Inl}, _) $ absfun) = Left (HOLogic.dest_string absfun) |
| | dest_funcall (Const (@{const_name Inr}, _) $ Const (f, _)) = Right f |
| | dest_funcall f = raise TERM ("dest_funcall: unrecognised call", [f]) |
| |
| fun parse_ptr ptr = |
| HOLogic.dest_list ptr |
| |> map (fn p => |
| (case p of |
| Const (@{const_name InRecord}, _) $ n => InRecord (HOLogic.dest_nat n) |
| | Const (@{const_name InProduct}, _) $ n => InProduct (HOLogic.dest_nat n) |
| | Const (@{const_name InSum}, _) $ s => InSum (HOLogic.dest_string s) |
| | Const (@{const_name IsFunction}, _) $ f => IsFunction (dest_funcall f))) |
| |
| fun parse_calls calls = |
| HOLogic.dest_list calls |> map HOLogic.dest_prod |
| |> map (fn (f, ptrs) => (dest_funcall f, HOLogic.dest_list ptrs |> map parse_ptr)) |
| |
| fun get_HO_calls ctxt f = |
| (* Expression that evaluates to the higher-order call lists *) |
| @{mk_term "filter (\<lambda>call. snd call \<noteq> []) (fst (funcall_sem [[]] ?f))" f} |
| (Syntax.read_term ctxt f) |
| |> Thm.cterm_of ctxt |
| (* Unfold funcall_sem *) |
| |> Simplifier.rewrite (ctxt addsimps (Proof_Context.get_thms ctxt (f ^ "_def"))) |
| |> Thm.prop_of |> Utils.rhs_of_eq |
| |> parse_calls |
| |
| (* Post-processing: translate InData accessors to C datatype accessors, which can be plugged |
| * into the Corres_Tac code. *) |
| fun the'' str x = |
| (the x) handle Option => error (str ()) |
| |
| fun nth'' str xs n = |
| (nth xs n) handle Subscript => error (str ()) |
| |
| fun nth_field_of struct_info C_typ n = |
| nth'' (fn _ => raise TYPE ("Type does not have field #" ^ string_of_int n, [C_typ], [])) |
| (Typtab.lookup struct_info C_typ |
| |> the'' (fn _ => raise TYPE ("No such struct: ", [C_typ], [])) |
| |> #field_info) n |
| |
| fun ptrs_to_C_getter struct_info C_typ (InRecord n :: ptrs) = let |
| val field = nth_field_of struct_info C_typ n |
| val (getter', funarg) = ptrs_to_C_getter struct_info (#field_type field) ptrs |
| in (#name field :: getter', funarg) end |
| | ptrs_to_C_getter struct_info C_typ (InProduct n :: ptrs) = let |
| val field = nth_field_of struct_info C_typ n |
| val (getter', funarg) = ptrs_to_C_getter struct_info (#field_type field) ptrs |
| in (#name field :: getter', funarg) end |
| | ptrs_to_C_getter struct_info C_typ (InSum s :: ptrs) = let |
| val field_info = Typtab.lookup struct_info C_typ |
| |> the'' (fn _ => raise TYPE ("No such struct: ", [C_typ], [])) |> #field_info |
| val field = case filter (fn field => #name field = s ^ "_C") field_info of |
| [field] => field |
| | _ => raise TYPE ("Type does not have field " ^ quote s, [C_typ], []) |
| val (getter', funarg) = ptrs_to_C_getter struct_info (#field_type field) ptrs |
| in (#name field :: getter', funarg) end |
| | ptrs_to_C_getter struct_info C_typ [IsFunction f] = ([], f) |
| | ptrs_to_C_getter _ _ _ = error "ptrs_to_C_getter: failed to parse ptrs" |
| |
| fun make_HO_call_hints ctxt C_src f = let |
| val ACfunctions = Symtab.lookup (AutoCorresFunctionInfo.get (Proof_Context.theory_of ctxt)) C_src |
| |> Utils.the' ("No such C file: " ^ quote C_src) |
| |> (fn finfo => FunctionInfo.Phasetab.lookup finfo FunctionInfo.TS) |
| |> Utils.the' ("Failed to get type strengthening function table") |
| val struct_info = Symtab.lookup (HeapInfo.get (Proof_Context.theory_of ctxt)) C_src |
| |> Utils.the' ("No such C file: " ^ quote C_src) |
| |> #heap_info |
| |> #struct_types |
| in get_HO_calls ctxt f |
| |> map (fn (call, ptrs) => let |
| val hofun = (case call of Left f => f | Right f => f) |
| val [(_, hoarg_typ)] = Symtab.lookup ACfunctions hofun |
| |> Utils.the' ("No such C func: " ^ quote hofun) |> #args |
| in (call, map (ptrs_to_C_getter struct_info hoarg_typ) ptrs) end) |
| end |
| |
| end |
| \<close> |
| |
| end |