diff options
Diffstat (limited to 'compiler/Extract.ml')
-rw-r--r-- | compiler/Extract.ml | 1197 |
1 files changed, 906 insertions, 291 deletions
diff --git a/compiler/Extract.ml b/compiler/Extract.ml index 03c256ec..0decbff1 100644 --- a/compiler/Extract.ml +++ b/compiler/Extract.ml @@ -16,7 +16,7 @@ module F = Format let int_name (int_ty : integer_type) = let isize, usize, i_format, u_format = match !backend with - | FStar | Coq -> + | FStar | Coq | HOL4 -> ("isize", "usize", format_of_string "i%d", format_of_string "u%d") | Lean -> ("Isize", "Usize", format_of_string "I%d", format_of_string "U%d") in @@ -37,14 +37,15 @@ let int_name (int_ty : integer_type) = (** Small helper to compute the name of a unary operation *) let unop_name (unop : unop) : string = match unop with - | Not -> ( match !backend with FStar | Lean -> "not" | Coq -> "negb") + | Not -> ( + match !backend with FStar | Lean -> "not" | Coq -> "negb" | HOL4 -> "~") | Neg (int_ty : integer_type) -> ( match !backend with Lean -> "-" | _ -> int_name int_ty ^ "_neg") | Cast _ -> raise (Failure "Unsupported") (** Small helper to compute the name of a binary operation (note that many binary operations like "less than" are extracted to primitive operations, - like [<]. + like [<]). *) let named_binop_name (binop : E.binop) (int_ty : integer_type) : string = let binop = @@ -54,12 +55,16 @@ let named_binop_name (binop : E.binop) (int_ty : integer_type) : string = | Add -> "add" | Sub -> "sub" | Mul -> "mul" + | Lt -> "lt" + | Le -> "le" + | Ge -> "ge" + | Gt -> "gt" | _ -> raise (Failure "Unreachable") in (* Remark: the Lean case is actually not used *) match !backend with | Lean -> int_name int_ty ^ "." ^ binop - | FStar | Coq -> int_name int_ty ^ "_" ^ binop + | FStar | Coq | HOL4 -> int_name int_ty ^ "_" ^ binop (** A list of keywords/identifiers used by the backend and with which we want to check collision. @@ -185,6 +190,28 @@ let keywords () = "with"; "opaque_defs"; ] + | HOL4 -> + [ + "Axiom"; + "case"; + "Definition"; + "else"; + "End"; + "fix"; + "fix_exec"; + "fn"; + "fun"; + "if"; + "in"; + "int"; + "Inductive"; + "let"; + "of"; + "Proof"; + "QED"; + "then"; + "Theorem"; + ] in List.concat [ named_unops; named_binops; misc ] @@ -215,6 +242,15 @@ let assumed_adts () : (assumed_ty * string) list = (Fuel, "nat"); (Option, "option"); (Vec, "vec"); + ] + | HOL4 -> + [ + (State, "state"); + (Result, "result"); + (Error, "error"); + (Fuel, "num"); + (Option, "OPTION"); + (Vec, "vec"); ]) let assumed_structs : (assumed_ty * string) list = [] @@ -253,6 +289,16 @@ let assumed_variants () : (assumed_ty * VariantId.id * string) list = (Option, option_some_id, "some"); (Option, option_none_id, "none"); ] + | HOL4 -> + [ + (Result, result_return_id, "Return"); + (Result, result_fail_id, "Fail"); + (Error, error_failure_id, "Failure"); + (* No Fuel::Zero on purpose *) + (* No Fuel::Succ on purpose *) + (Option, option_some_id, "SOME"); + (Option, option_none_id, "NONE"); + ] let assumed_llbc_functions : (A.assumed_fun_id * T.RegionGroupId.id option * string) list = @@ -288,6 +334,9 @@ let assumed_pure_functions () : (pure_assumed_fun_id * string) list = | Lean -> (* We don't provide [FuelDecrease] and [FuelEqZero] on purpose *) [ (Return, "return"); (Fail, "fail_"); (Assert, "massert") ] + | HOL4 -> + (* We don't provide [FuelDecrease] and [FuelEqZero] on purpose *) + [ (Return, "return"); (Fail, "fail"); (Assert, "massert") ] let names_map_init () : names_map_init = { @@ -310,37 +359,60 @@ let extract_unop (extract_expr : bool -> texpression -> unit) F.pp_print_space fmt (); extract_expr true arg; if inside then F.pp_print_string fmt ")" - | Cast (src, tgt) -> - (* The source type is an implicit parameter *) - if inside then F.pp_print_string fmt "("; - let cast_str = - match !backend with - | Coq | FStar -> "scalar_cast" - | Lean -> (* TODO: I8.cast, I16.cast, etc.*) "Scalar.cast" - in - F.pp_print_string fmt cast_str; - F.pp_print_space fmt (); - if !backend <> Lean then ( - F.pp_print_string fmt - (StringUtils.capitalize_first_letter - (PrintPure.integer_type_to_string src)); - F.pp_print_space fmt ()); - if !backend = Lean then F.pp_print_string fmt ("." ^ int_name tgt) - else - F.pp_print_string fmt - (StringUtils.capitalize_first_letter - (PrintPure.integer_type_to_string tgt)); - F.pp_print_space fmt (); - extract_expr true arg; - if inside then F.pp_print_string fmt ")" + | Cast (src, tgt) -> ( + (* HOL4 has a special treatment: because it doesn't support dependent + types, we don't have a specific operator for the cast *) + match !backend with + | HOL4 -> + (* Casting, say, an u32 to an i32 would be done as follows: + {[ + mk_i32 (u32_to_int x) + ]} + *) + if inside then F.pp_print_string fmt "("; + F.pp_print_string fmt ("mk_" ^ int_name tgt); + F.pp_print_space fmt (); + F.pp_print_string fmt "("; + F.pp_print_string fmt (int_name src ^ "_to_int"); + F.pp_print_space fmt (); + extract_expr true arg; + F.pp_print_string fmt ")"; + if inside then F.pp_print_string fmt ")" + | FStar | Coq | Lean -> + (* Rem.: the source type is an implicit parameter *) + if inside then F.pp_print_string fmt "("; + let cast_str = + match !backend with + | Coq | FStar -> "scalar_cast" + | Lean -> (* TODO: I8.cast, I16.cast, etc.*) "Scalar.cast" + | HOL4 -> raise (Failure "Unreachable") + in + F.pp_print_string fmt cast_str; + F.pp_print_space fmt (); + if !backend <> Lean then ( + F.pp_print_string fmt + (StringUtils.capitalize_first_letter + (PrintPure.integer_type_to_string src)); + F.pp_print_space fmt ()); + if !backend = Lean then F.pp_print_string fmt ("." ^ int_name tgt) + else + F.pp_print_string fmt + (StringUtils.capitalize_first_letter + (PrintPure.integer_type_to_string tgt)); + F.pp_print_space fmt (); + extract_expr true arg; + if inside then F.pp_print_string fmt ")") +(** [extract_expr] : the boolean argument is [inside] *) let extract_binop (extract_expr : bool -> texpression -> unit) (fmt : F.formatter) (inside : bool) (binop : E.binop) (int_ty : integer_type) (arg0 : texpression) (arg1 : texpression) : unit = if inside then F.pp_print_string fmt "("; (* Some binary operations have a special notation depending on the backend *) (match (!backend, binop) with - | _, (Eq | Lt | Le | Ne | Ge | Gt) | Lean, (Div | Rem | Add | Sub | Mul) -> + | HOL4, (Eq | Ne) + | (FStar | Coq | Lean), (Eq | Lt | Le | Ne | Ge | Gt) + | Lean, (Div | Rem | Add | Sub | Mul) -> let binop = match binop with | Eq -> "=" @@ -357,87 +429,99 @@ let extract_binop (extract_expr : bool -> texpression -> unit) | _ -> raise (Failure "Unreachable") in let binop = - match !backend with FStar | Lean -> binop | Coq -> "s" ^ binop + match !backend with FStar | Lean | HOL4 -> binop | Coq -> "s" ^ binop in extract_expr false arg0; F.pp_print_space fmt (); F.pp_print_string fmt binop; F.pp_print_space fmt (); extract_expr false arg1 - | _, (Div | Rem | Add | Sub | Mul) -> + | _, (Lt | Le | Ge | Gt | Div | Rem | Add | Sub | Mul) -> let binop = named_binop_name binop int_ty in F.pp_print_string fmt binop; F.pp_print_space fmt (); - extract_expr false arg0; + extract_expr true arg0; F.pp_print_space fmt (); - extract_expr false arg1 + extract_expr true arg1 | _, (BitXor | BitAnd | BitOr | Shl | Shr) -> raise Unimplemented); if inside then F.pp_print_string fmt ")" let type_decl_kind_to_qualif (kind : decl_kind) - (type_kind : type_decl_kind option) : string = + (type_kind : type_decl_kind option) : string option = match !backend with | FStar -> ( match kind with - | SingleNonRec -> "type" - | SingleRec -> "type" - | MutRecFirst -> "type" - | MutRecInner -> "and" - | MutRecLast -> "and" - | Assumed -> "assume type" - | Declared -> "val") + | SingleNonRec -> Some "type" + | SingleRec -> Some "type" + | MutRecFirst -> Some "type" + | MutRecInner -> Some "and" + | MutRecLast -> Some "and" + | Assumed -> Some "assume type" + | Declared -> Some "val") | Coq -> ( match (kind, type_kind) with - | SingleNonRec, Some Enum -> "Inductive" - | SingleNonRec, Some Struct -> "Record" - | (SingleRec | MutRecFirst), Some _ -> "Inductive" + | SingleNonRec, Some Enum -> Some "Inductive" + | SingleNonRec, Some Struct -> Some "Record" + | (SingleRec | MutRecFirst), Some _ -> Some "Inductive" | (MutRecInner | MutRecLast), Some _ -> (* Coq doesn't support groups of mutually recursive definitions which mix * records and inducties: we convert everything to records if this happens *) - "with" - | (Assumed | Declared), None -> "Axiom" + Some "with" + | (Assumed | Declared), None -> Some "Axiom" | _ -> raise (Failure "Unexpected")) | Lean -> ( match kind with | SingleNonRec -> - if type_kind = Some Struct then "structure" else "inductive" - | SingleRec -> "inductive" - | MutRecFirst -> "mutual inductive" - | MutRecInner -> "inductive" - | MutRecLast -> "inductive" (* TODO: need to print end afterwards *) - | Assumed -> "axiom" - | Declared -> "axiom") - -let fun_decl_kind_to_qualif (kind : decl_kind) = + if type_kind = Some Struct then Some "structure" else Some "inductive" + | SingleRec -> Some "inductive" + | MutRecFirst -> Some "inductive" + | MutRecInner -> Some "inductive" + | MutRecLast -> Some "inductive" + | Assumed -> Some "axiom" + | Declared -> Some "axiom") + | HOL4 -> None + +let fun_decl_kind_to_qualif (kind : decl_kind) : string option = match !backend with | FStar -> ( match kind with - | SingleNonRec -> "let" - | SingleRec -> "let rec" - | MutRecFirst -> "let rec" - | MutRecInner -> "and" - | MutRecLast -> "and" - | Assumed -> "assume val" - | Declared -> "val") + | SingleNonRec -> Some "let" + | SingleRec -> Some "let rec" + | MutRecFirst -> Some "let rec" + | MutRecInner -> Some "and" + | MutRecLast -> Some "and" + | Assumed -> Some "assume val" + | Declared -> Some "val") | Coq -> ( match kind with - | SingleNonRec -> "Definition" - | SingleRec -> "Fixpoint" - | MutRecFirst -> "Fixpoint" - | MutRecInner -> "with" - | MutRecLast -> "with" - | Assumed -> "Axiom" - | Declared -> "Axiom") + | SingleNonRec -> Some "Definition" + | SingleRec -> Some "Fixpoint" + | MutRecFirst -> Some "Fixpoint" + | MutRecInner -> Some "with" + | MutRecLast -> Some "with" + | Assumed -> Some "Axiom" + | Declared -> Some "Axiom") | Lean -> ( match kind with - | SingleNonRec -> "def" - | SingleRec -> "def" - | MutRecFirst -> "mutual def" - | MutRecInner -> "def" - | MutRecLast -> "def" (* TODO: need to print end afterwards *) - | Assumed -> "axiom" - | Declared -> "axiom") + | SingleNonRec -> Some "def" + | SingleRec -> Some "def" + | MutRecFirst -> Some "mutual def" + | MutRecInner -> Some "def" + | MutRecLast -> Some "def" + | Assumed -> Some "axiom" + | Declared -> Some "axiom") + | HOL4 -> None + +(** The type of types. + + TODO: move inside the formatter? + *) +let type_keyword () = + match !backend with + | FStar -> "Type0" + | Coq | Lean -> "Type" + | HOL4 -> raise (Failure "Unexpected") (** [ctx]: we use the context to lookup type definitions, to retrieve type names. @@ -514,7 +598,7 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) let name = List.map to_snake_case name in let name = String.concat "_" name in match !backend with - | FStar | Lean -> name + | FStar | Lean | HOL4 -> name | Coq -> capitalize_first_letter name in let type_name name = type_name_to_snake_case name ^ "_t" in @@ -533,7 +617,9 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) in let struct_constructor (basename : name) : string = let tname = type_name basename in - let prefix = match !backend with FStar -> "Mk" | Lean | Coq -> "mk" in + let prefix = + match !backend with FStar -> "Mk" | Lean | Coq | HOL4 -> "mk" + in prefix ^ tname in let get_fun_name = get_name in @@ -569,7 +655,7 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) match !Config.backend with | FStar -> "_decreases" | Lean -> "_terminates" - | Coq -> raise (Failure "Unexpected") + | Coq | HOL4 -> raise (Failure "Unexpected") in (* Concatenate *) fname ^ lp_suffix ^ suffix @@ -583,14 +669,16 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) let suffix = match !Config.backend with | Lean -> "_decreases" - | FStar | Coq -> raise (Failure "Unexpected") + | FStar | Coq | HOL4 -> raise (Failure "Unexpected") in (* Concatenate *) fname ^ lp_suffix ^ suffix in let opaque_pre () = - match !Config.backend with FStar | Coq -> "" | Lean -> "opaque_defs." + match !Config.backend with + | FStar | Coq | HOL4 -> "" + | Lean -> "opaque_defs." in let var_basename (_varset : StringSet.t) (basename : string option) (ty : ty) @@ -637,7 +725,7 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) (* TODO: use "t" also for F* *) match !backend with | FStar -> "x" (* lacking inspiration here... *) - | Coq | Lean -> "t" (* lacking inspiration here... *)) + | Coq | Lean | HOL4 -> "t" (* lacking inspiration here... *)) | Bool -> "b" | Char -> "c" | Integer _ -> "i" @@ -651,6 +739,9 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) | FStar -> (* This is *not* a no-op: this removes the capital letter *) to_snake_case basename + | HOL4 -> + (* In HOL4, type variable names must start with "'" *) + "'" ^ to_snake_case basename | Coq | Lean -> basename in let append_index (basename : string) (i : int) : string = @@ -663,14 +754,24 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) | Scalar sv -> ( match !backend with | FStar -> F.pp_print_string fmt (Z.to_string sv.PV.value) - | Coq -> - if inside then F.pp_print_string fmt "("; + | Coq | HOL4 -> + let print_brackets = inside && !backend = HOL4 in + if print_brackets then F.pp_print_string fmt "("; + (match !backend with + | Coq -> () + | HOL4 -> + F.pp_print_string fmt ("int_to_" ^ int_name sv.PV.int_ty); + F.pp_print_space fmt () + | _ -> raise (Failure "Unreachable")); (* We need to add parentheses if the value is negative *) if sv.PV.value >= Z.of_int 0 then F.pp_print_string fmt (Z.to_string sv.PV.value) else F.pp_print_string fmt ("(" ^ Z.to_string sv.PV.value ^ ")"); - F.pp_print_string fmt ("%" ^ int_name sv.PV.int_ty); - if inside then F.pp_print_string fmt ")" + (match !backend with + | Coq -> F.pp_print_string fmt ("%" ^ int_name sv.PV.int_ty) + | HOL4 -> () + | _ -> raise (Failure "Unreachable")); + if print_brackets then F.pp_print_string fmt ")" | Lean -> F.pp_print_string fmt "("; F.pp_print_string fmt (int_name sv.int_ty); @@ -694,6 +795,9 @@ let mk_formatter (ctx : trans_ctx) (crate_name : string) F.pp_print_string fmt b | Char c -> ( match !backend with + | HOL4 -> + (* [#"a"] is a notation for [CHR 97] (97 is the ASCII code for 'a') *) + F.pp_print_string fmt ("#\"" ^ String.make 1 c ^ "\"") | FStar | Lean -> F.pp_print_string fmt ("'" ^ String.make 1 c ^ "'") | Coq -> if inside then F.pp_print_string fmt "("; @@ -750,32 +854,183 @@ let mk_formatter_and_names_map (ctx : trans_ctx) (crate_name : string) let names_map = initialize_names_map fmt (names_map_init ()) in (fmt, names_map) +let is_single_opaque_fun_decl_group (dg : Pure.fun_decl list) : bool = + match dg with [ d ] -> d.body = None | _ -> false + +let is_single_opaque_type_decl_group (dg : Pure.type_decl list) : bool = + match dg with [ d ] -> d.kind = Opaque | _ -> false + +let is_empty_record_type_decl (d : Pure.type_decl) : bool = d.kind = Struct [] + +let is_empty_record_type_decl_group (dg : Pure.type_decl list) : bool = + match dg with [ d ] -> is_empty_record_type_decl d | _ -> false + (** In some provers, groups of definitions must be delimited. - in Coq, *every* group (including singletons) must end with "." - in Lean, groups of mutually recursive definitions must end with "end" + - in HOL4 (in most situations) the whole group must be within a `Define` command + + Calls to {!extract_fun_decl} should be inserted between calls to + {!start_fun_decl_group} and {!end_fun_decl_group}. + + TODO: maybe those [{start/end}_decl_group] functions are not that much a good + idea and we should merge them with the corresponding [extract_decl] functions. *) -let print_decl_end_delimiter (fmt : F.formatter) (kind : decl_kind) = - if !backend = Coq && decl_is_last_from_group kind then ( - F.pp_print_cut fmt (); - F.pp_print_string fmt ".") - else if !backend = Lean && kind = MutRecLast then ( - F.pp_print_cut fmt (); - F.pp_print_string fmt "end") +let start_fun_decl_group (ctx : extraction_ctx) (fmt : F.formatter) + (is_rec : bool) (dg : Pure.fun_decl list) = + match !backend with + | FStar | Coq | Lean -> () + | HOL4 -> + (* In HOL4, opaque functions have a special treatment *) + if is_single_opaque_fun_decl_group dg then () + else + let with_opaque_pre = false in + let compute_fun_def_name (def : Pure.fun_decl) : string = + ctx_get_local_function with_opaque_pre def.def_id def.loop_id + def.back_id ctx + ^ "_def" + in + let names = List.map compute_fun_def_name dg in + (* Add a break before *) + F.pp_print_break fmt 0 0; + (* Open the box for the delimiters *) + F.pp_open_vbox fmt 0; + (* Open the box for the definitions themselves *) + F.pp_open_vbox fmt ctx.indent_incr; + (* Print the delimiters *) + if is_rec then + F.pp_print_string fmt + ("val [" ^ String.concat ", " names ^ "] = DefineDiv ‘") + else ( + assert (List.length names = 1); + let name = List.hd names in + F.pp_print_string fmt ("val " ^ name ^ " = Define ‘")); + F.pp_print_cut fmt () + +(** See {!start_fun_decl_group}. *) +let end_fun_decl_group (fmt : F.formatter) (is_rec : bool) + (dg : Pure.fun_decl list) = + match !backend with + | FStar -> () + | Coq -> + (* For aesthetic reasons, we print the Coq end group delimiter directly + in {!extract_fun_decl}. *) + () + | Lean -> + (* We must add the "end" keyword to groups of mutually recursive functions *) + if is_rec && List.length dg > 1 then ( + F.pp_print_cut fmt (); + F.pp_print_string fmt "end"; + (* Add breaks to insert new lines between definitions *) + F.pp_print_break fmt 0 0) + else () + | HOL4 -> + (* In HOL4, opaque functions have a special treatment *) + if is_single_opaque_fun_decl_group dg then () + else ( + (* Close the box for the definitions *) + F.pp_close_box fmt (); + (* Print the end delimiter *) + F.pp_print_cut fmt (); + F.pp_print_string fmt "’"; + (* Close the box for the delimiters *) + F.pp_close_box fmt (); + (* Add breaks to insert new lines between definitions *) + F.pp_print_break fmt 0 0) -let unit_name () = match !backend with Lean -> "Unit" | Coq | FStar -> "unit" +(** See {!start_fun_decl_group}: similar usage, but for the type declarations. *) +let start_type_decl_group (ctx : extraction_ctx) (fmt : F.formatter) + (is_rec : bool) (dg : Pure.type_decl list) = + match !backend with + | FStar | Coq -> () + | Lean -> + if is_rec && List.length dg > 1 then ( + F.pp_print_space fmt (); + F.pp_print_string fmt "mutual"; + F.pp_print_space fmt ()) + | HOL4 -> + (* In HOL4, opaque types and empty records have a special treatment *) + if + is_single_opaque_type_decl_group dg + || is_empty_record_type_decl_group dg + then () + else ( + (* Add a break before *) + F.pp_print_break fmt 0 0; + (* Open the box for the delimiters *) + F.pp_open_vbox fmt 0; + (* Open the box for the definitions themselves *) + F.pp_open_vbox fmt ctx.indent_incr; + (* Print the delimiters *) + F.pp_print_string fmt "Datatype:"; + F.pp_print_cut fmt ()) + +(** See {!start_fun_decl_group}. *) +let end_type_decl_group (fmt : F.formatter) (is_rec : bool) + (dg : Pure.type_decl list) = + match !backend with + | FStar -> () + | Coq -> + (* For aesthetic reasons, we print the Coq end group delimiter directly + in {!extract_fun_decl}. *) + () + | Lean -> + (* We must add the "end" keyword to groups of mutually recursive functions *) + if is_rec && List.length dg > 1 then ( + F.pp_print_cut fmt (); + F.pp_print_string fmt "end"; + (* Add breaks to insert new lines between definitions *) + F.pp_print_break fmt 0 0) + else () + | HOL4 -> + (* In HOL4, opaque types and empty records have a special treatment *) + if + is_single_opaque_type_decl_group dg + || is_empty_record_type_decl_group dg + then () + else ( + (* Close the box for the definitions *) + F.pp_close_box fmt (); + (* Print the end delimiter *) + F.pp_print_cut fmt (); + F.pp_print_string fmt "End"; + (* Close the box for the delimiters *) + F.pp_close_box fmt (); + (* Add breaks to insert new lines between definitions *) + F.pp_print_break fmt 0 0) + +let unit_name () = + match !backend with Lean -> "Unit" | Coq | FStar | HOL4 -> "unit" (** [inside] constrols whether we should add parentheses or not around type applications (if [true] we add parentheses). + + [no_params_tys]: for all the types inside this set, do not print the type parameters. + This is used for HOL4. As polymorphism is uniform in HOL4, printing the + type parameters in the recursive definitions is useless (and actually + forbidden). + + For instance, where in F* we would write: + {[ + type list a = | Nil : list a | Cons : a -> list a -> list a + ]} + + In HOL4 we would simply write: + {[ + Datatype: + list = Nil 'a | Cons 'a list + End + ]} *) -let rec extract_ty (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) - (ty : ty) : unit = - let extract_rec = extract_ty ctx fmt in +let rec extract_ty (ctx : extraction_ctx) (fmt : F.formatter) + (no_params_tys : TypeDeclId.Set.t) (inside : bool) (ty : ty) : unit = + let extract_rec = extract_ty ctx fmt no_params_tys in match ty with | Adt (type_id, tys) -> ( match type_id with | Tuple -> - (* This is a bit annoying, but in F*/Coq [()] is not the unit type: + (* This is a bit annoying, but in F*/Coq/HOL4 [()] is not the unit type: * we have to write [unit]... *) if tys = [] then F.pp_print_string fmt (unit_name ()) else ( @@ -784,23 +1039,54 @@ let rec extract_ty (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) (fun () -> F.pp_print_space fmt (); let product = - match !backend with FStar -> "&" | Coq -> "*" | Lean -> "×" + match !backend with + | FStar -> "&" + | Coq -> "*" + | Lean -> "×" + | HOL4 -> "#" in F.pp_print_string fmt product; F.pp_print_space fmt ()) (extract_rec true) tys; F.pp_print_string fmt ")") - | AdtId _ | Assumed _ -> - let print_paren = inside && tys <> [] in - if print_paren then F.pp_print_string fmt "("; - (* TODO: for now, only the opaque *functions* are extracted in the - opaque module. The opaque *types* are assumed. *) + | AdtId _ | Assumed _ -> ( + (* HOL4 behaves differently. Where in Coq/FStar/Lean we would write: + `tree a b` + + In HOL4 we would write: + `('a, 'b) tree` + *) let with_opaque_pre = false in - F.pp_print_string fmt (ctx_get_type with_opaque_pre type_id ctx); - if tys <> [] then F.pp_print_space fmt (); - Collections.List.iter_link (F.pp_print_space fmt) (extract_rec true) - tys; - if print_paren then F.pp_print_string fmt ")") + match !backend with + | FStar | Coq | Lean -> + let print_paren = inside && tys <> [] in + if print_paren then F.pp_print_string fmt "("; + (* TODO: for now, only the opaque *functions* are extracted in the + opaque module. The opaque *types* are assumed. *) + F.pp_print_string fmt (ctx_get_type with_opaque_pre type_id ctx); + if tys <> [] then ( + F.pp_print_space fmt (); + Collections.List.iter_link (F.pp_print_space fmt) + (extract_rec true) tys); + if print_paren then F.pp_print_string fmt ")" + | HOL4 -> + let print_tys = + match type_id with + | AdtId id -> not (TypeDeclId.Set.mem id no_params_tys) + | Assumed _ -> true + | _ -> raise (Failure "Unreachable") + in + if tys <> [] && print_tys then ( + let print_paren = List.length tys > 1 in + if print_paren then F.pp_print_string fmt "("; + Collections.List.iter_link + (fun () -> + F.pp_print_string fmt ","; + F.pp_print_space fmt ()) + (extract_rec true) tys; + if print_paren then F.pp_print_string fmt ")"; + F.pp_print_space fmt ()); + F.pp_print_string fmt (ctx_get_type with_opaque_pre type_id ctx))) | TypeVar vid -> F.pp_print_string fmt (ctx_get_type_var vid ctx) | Bool -> F.pp_print_string fmt ctx.fmt.bool_name | Char -> F.pp_print_string fmt ctx.fmt.char_name @@ -855,18 +1141,20 @@ let extract_type_decl_register_names (ctx : extraction_ctx) (def : type_decl) : (** Print the variants *) let extract_type_decl_variant (ctx : extraction_ctx) (fmt : F.formatter) - (type_name : string) (type_params : string list) (cons_name : string) - (fields : field list) : unit = + (type_decl_group : TypeDeclId.Set.t) (type_name : string) + (type_params : string list) (cons_name : string) (fields : field list) : + unit = F.pp_print_space fmt (); (* variant box *) F.pp_open_hvbox fmt ctx.indent_incr; (* [| Cons :] * Note that we really don't want any break above so we print everything * at once. *) - F.pp_print_string fmt ("| " ^ cons_name ^ " :"); - F.pp_print_space fmt (); + let opt_colon = if !backend <> HOL4 then " :" else "" in + F.pp_print_string fmt ("| " ^ cons_name ^ opt_colon); let print_field (fid : FieldId.id) (f : field) (ctx : extraction_ctx) : extraction_ctx = + F.pp_print_space fmt (); (* Open the field box *) F.pp_open_box fmt ctx.indent_incr; (* Print the field names, if the backend accepts it. @@ -888,16 +1176,17 @@ let extract_type_decl_variant (ctx : extraction_ctx) (fmt : F.formatter) F.pp_print_string fmt (field_name ^ " :"); F.pp_print_space fmt (); ctx) - | Coq | Lean -> ctx + | Coq | Lean | HOL4 -> ctx in (* Print the field type *) - extract_ty ctx fmt false f.field_ty; - (* Print the arrow [->]*) - F.pp_print_space fmt (); - F.pp_print_string fmt "->"; + let inside = !backend = HOL4 in + extract_ty ctx fmt type_decl_group inside f.field_ty; + (* Print the arrow [->] *) + if !backend <> HOL4 then ( + F.pp_print_space fmt (); + F.pp_print_string fmt "->"); (* Close the field box *) F.pp_close_box fmt (); - F.pp_print_space fmt (); (* Return *) ctx in @@ -907,21 +1196,23 @@ let extract_type_decl_variant (ctx : extraction_ctx) (fmt : F.formatter) List.fold_left (fun ctx (fid, f) -> print_field fid f ctx) ctx fields in (* Print the final type *) - F.pp_open_hovbox fmt 0; - F.pp_print_string fmt type_name; - List.iter - (fun type_param -> - F.pp_print_space fmt (); - F.pp_print_string fmt type_param) - type_params; - F.pp_close_box fmt (); + if !backend <> HOL4 then ( + F.pp_print_space fmt (); + F.pp_open_hovbox fmt 0; + F.pp_print_string fmt type_name; + List.iter + (fun type_param -> + F.pp_print_space fmt (); + F.pp_print_string fmt type_param) + type_params; + F.pp_close_box fmt ()); (* Close the variant box *) F.pp_close_box fmt () (* TODO: we don' need the [def_name] paramter: it can be retrieved from the context *) let extract_type_decl_enum_body (ctx : extraction_ctx) (fmt : F.formatter) - (def : type_decl) (def_name : string) (type_params : string list) - (variants : variant list) : unit = + (type_decl_group : TypeDeclId.Set.t) (def : type_decl) (def_name : string) + (type_params : string list) (variants : variant list) : unit = (* We want to generate a definition which looks like this (taking F* as example): {[ type list a = | Cons : a -> list a -> list a | Nil : list a @@ -959,15 +1250,16 @@ let extract_type_decl_enum_body (ctx : extraction_ctx) (fmt : F.formatter) id (in the case of Lean) *) let cons_name = ctx.fmt.variant_name def.name v.variant_name in let fields = v.fields in - extract_type_decl_variant ctx fmt def_name type_params cons_name fields + extract_type_decl_variant ctx fmt type_decl_group def_name type_params + cons_name fields in (* Print the variants *) let variants = VariantId.mapi (fun vid v -> (vid, v)) variants in List.iter (fun (vid, v) -> print_variant vid v) variants let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) - (kind : decl_kind) (def : type_decl) (type_params : string list) - (fields : field list) : unit = + (type_decl_group : TypeDeclId.Set.t) (kind : decl_kind) (def : type_decl) + (type_params : string list) (fields : field list) : unit = (* We want to generate a definition which looks like this (taking F* as example): {[ type t = { x : int; y : bool; } @@ -1007,6 +1299,20 @@ let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) expanded which introduces let bindings of the form: [let RecordCons ... = x in ...]. As a consequence, we never use the record projectors (unless we reconstruct them in the micro passes of course). + + HOL4: + ===== + Type definitions are written as follows: + {[ + Datatype: + tree = + TLeaf 'a + | TNode node ; + + node = + Node (tree list) + End + ]} *) (* Note that we already printed: [type t =] *) let is_rec = decl_is_from_rec_group kind in @@ -1026,12 +1332,15 @@ let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) F.pp_print_string fmt (ctx_get_struct with_opaque_pre (AdtId def.def_id) ctx); F.pp_print_string fmt " "); - if !backend <> Lean then F.pp_print_string fmt "{"; + (match !backend with + | Lean -> () + | FStar | Coq -> F.pp_print_string fmt "{" + | HOL4 -> F.pp_print_string fmt "<|"); F.pp_print_break fmt 1 ctx.indent_incr; (* The body itself *) (* Open a box for the body *) (match !backend with - | Coq | FStar -> F.pp_open_hvbox fmt 0 + | Coq | FStar | HOL4 -> F.pp_open_hvbox fmt 0 | Lean -> F.pp_open_vbox fmt 0); (* Print the fields *) let print_field (field_id : FieldId.id) (f : field) : unit = @@ -1042,7 +1351,7 @@ let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) F.pp_print_space fmt (); F.pp_print_string fmt ":"; F.pp_print_space fmt (); - extract_ty ctx fmt false f.field_ty; + extract_ty ctx fmt type_decl_group false f.field_ty; if !backend <> Lean then F.pp_print_string fmt ";"; (* Close the box for the field *) F.pp_close_box fmt () @@ -1053,9 +1362,14 @@ let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) fields; (* Close the box for the body *) F.pp_close_box fmt (); - if !backend <> Lean then ( - F.pp_print_space fmt (); - F.pp_print_string fmt "}")) + match !backend with + | Lean -> () + | FStar | Coq -> + F.pp_print_space fmt (); + F.pp_print_string fmt "}" + | HOL4 -> + F.pp_print_space fmt (); + F.pp_print_string fmt "|>") else ( (* We extract for Coq or Lean, and we have a recursive record, or a record in a group of mutually recursive types: we extract it as an inductive type *) @@ -1063,14 +1377,15 @@ let extract_type_decl_struct_body (ctx : extraction_ctx) (fmt : F.formatter) let with_opaque_pre = false in let cons_name = ctx_get_struct with_opaque_pre (AdtId def.def_id) ctx in let def_name = ctx_get_local_type with_opaque_pre def.def_id ctx in - extract_type_decl_variant ctx fmt def_name type_params cons_name fields) + extract_type_decl_variant ctx fmt type_decl_group def_name type_params + cons_name fields) in () (** Extract a nestable, muti-line comment *) let extract_comment (fmt : F.formatter) (s : string) : unit = match !backend with - | Coq | FStar -> + | Coq | FStar | HOL4 -> F.pp_print_string fmt "(** "; F.pp_print_string fmt s; F.pp_print_string fmt " *)" @@ -1081,16 +1396,16 @@ let extract_comment (fmt : F.formatter) (s : string) : unit = (** Extract a type declaration. - Note that all the names used for extraction should already have been - registered. + This function is for all type declarations and all backends **at the exception** + of opaque (assumed/declared) types format4 HOL4. + + See {!extract_type_decl}. *) -let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) - (kind : decl_kind) (def : type_decl) : unit = - let extract_body = - match kind with - | SingleNonRec | SingleRec | MutRecFirst | MutRecInner | MutRecLast -> true - | Assumed | Declared -> false - in +let extract_type_decl_gen (ctx : extraction_ctx) (fmt : F.formatter) + (type_decl_group : TypeDeclId.Set.t) (kind : decl_kind) (def : type_decl) + (extract_body : bool) : unit = + (* Sanity check *) + assert (extract_body || !backend <> HOL4); let type_kind = if extract_body then match def.kind with @@ -1114,7 +1429,8 @@ let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) * body translation (they are not top-level) *) let ctx_body, type_params = ctx_add_type_params def.type_params ctx in (* Add a break before *) - F.pp_print_break fmt 0 0; + if !backend <> HOL4 || not (decl_is_first_from_group kind) then + F.pp_print_break fmt 0 0; (* Print a comment to link the extracted type to its original rust definition *) extract_comment fmt ("[" ^ Print.name_to_string def.name ^ "]"); F.pp_print_break fmt 0 0; @@ -1122,18 +1438,17 @@ let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) * one line. Note however that in the case of Lean line breaks are important * for parsing: we thus use a hovbox. *) (match !backend with - | Coq | FStar -> F.pp_open_hvbox fmt 0 + | Coq | FStar | HOL4 -> F.pp_open_hvbox fmt 0 | Lean -> F.pp_open_vbox fmt 0); (* Open a box for "type TYPE_NAME (TYPE_PARAMS) =" *) F.pp_open_hovbox fmt ctx.indent_incr; (* > "type TYPE_NAME" *) let qualif = ctx.fmt.type_decl_kind_to_qualif kind type_kind in - F.pp_print_string fmt (qualif ^ " " ^ def_name); + (match qualif with + | Some qualif -> F.pp_print_string fmt (qualif ^ " " ^ def_name) + | None -> F.pp_print_string fmt def_name); (* Print the type parameters *) - let type_keyword = - match !backend with FStar -> "Type0" | Coq | Lean -> "Type" - in - if def.type_params <> [] then ( + if def.type_params <> [] && !backend <> HOL4 then ( if use_forall then ( F.pp_print_space fmt (); F.pp_print_string fmt ":"; @@ -1149,7 +1464,7 @@ let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) def.type_params; F.pp_print_string fmt ":"; F.pp_print_space fmt (); - F.pp_print_string fmt (type_keyword ^ ")")); + F.pp_print_string fmt (type_keyword () ^ ")")); (* Print the "=" if we extract the body*) if extract_body then ( F.pp_print_space fmt (); @@ -1160,32 +1475,107 @@ let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) | Lean -> if type_kind = Some Struct && kind = SingleNonRec then "where" else ":=" + | HOL4 -> "=" in F.pp_print_string fmt eq) else ( - (* Otherwise print ": Type" *) + (* Otherwise print ": Type", unless it is the HOL4 backend (in + which case we declare the type with `new_type`) *) if use_forall then F.pp_print_string fmt "," else ( F.pp_print_space fmt (); F.pp_print_string fmt ":"); F.pp_print_space fmt (); - F.pp_print_string fmt type_keyword); + F.pp_print_string fmt (type_keyword ())); (* Close the box for "type TYPE_NAME (TYPE_PARAMS) =" *) F.pp_close_box fmt (); (if extract_body then - match def.kind with - | Struct fields -> - extract_type_decl_struct_body ctx_body fmt kind def type_params fields - | Enum variants -> - extract_type_decl_enum_body ctx_body fmt def def_name type_params - variants - | Opaque -> raise (Failure "Unreachable")); - (* If Coq: end the definition with a "." *) - print_decl_end_delimiter fmt kind; + match def.kind with + | Struct fields -> + extract_type_decl_struct_body ctx_body fmt type_decl_group kind def + type_params fields + | Enum variants -> + extract_type_decl_enum_body ctx_body fmt type_decl_group def def_name + type_params variants + | Opaque -> raise (Failure "Unreachable")); + (* Add the definition end delimiter *) + if !backend = HOL4 && decl_is_not_last_from_group kind then ( + F.pp_print_space fmt (); + F.pp_print_string fmt ";") + else if !backend = Coq && decl_is_last_from_group kind then ( + (* This is actually an end of group delimiter. For aesthetic reasons + we print it here instead of in {!end_type_decl_group}. *) + F.pp_print_cut fmt (); + F.pp_print_string fmt "."); (* Close the box for the definition *) F.pp_close_box fmt (); (* Add breaks to insert new lines between definitions *) - F.pp_print_break fmt 0 0 + if !backend <> HOL4 || decl_is_not_last_from_group kind then + F.pp_print_break fmt 0 0 + +(** Extract an opaque type declaration to HOL4. + + Remark (SH): having to treat this specific case separately is very annoying, + but I could not find a better way. + *) +let extract_type_decl_hol4_opaque (ctx : extraction_ctx) (fmt : F.formatter) + (def : type_decl) : unit = + (* Retrieve the definition name *) + let with_opaque_pre = false in + let def_name = ctx_get_local_type with_opaque_pre def.def_id ctx in + (* Count the number of parameters *) + let num_params = List.length def.type_params in + (* Generate the declaration *) + F.pp_print_space fmt (); + F.pp_print_string fmt + ("val _ = new_type (\"" ^ def_name ^ ", " ^ string_of_int num_params ^ ")"); + F.pp_print_space fmt () + +(** Extract an empty record type declaration to HOL4. + + Empty records are not supported in HOL4, so we extract them as type + abbreviations to the unit type. + + Remark (SH): having to treat this specific case separately is very annoying, + but I could not find a better way. + *) +let extract_type_decl_hol4_empty_record (ctx : extraction_ctx) + (fmt : F.formatter) (def : type_decl) : unit = + (* Retrieve the definition name *) + let with_opaque_pre = false in + let def_name = ctx_get_local_type with_opaque_pre def.def_id ctx in + (* Sanity check *) + assert (def.type_params = []); + (* Generate the declaration *) + F.pp_print_space fmt (); + F.pp_print_string fmt ("Type " ^ def_name ^ " = “: unit”"); + F.pp_print_space fmt () + +(** Extract a type declaration. + + Note that all the names used for extraction should already have been + registered. + + This function should be inserted between calls to {!start_type_decl_group} + and {!end_type_decl_group}. + *) +let extract_type_decl (ctx : extraction_ctx) (fmt : F.formatter) + (type_decl_group : TypeDeclId.Set.t) (kind : decl_kind) (def : type_decl) : + unit = + let extract_body = + match kind with + | SingleNonRec | SingleRec | MutRecFirst | MutRecInner | MutRecLast -> true + | Assumed | Declared -> false + in + if extract_body then + if !backend = HOL4 && is_empty_record_type_decl def then + extract_type_decl_hol4_empty_record ctx fmt def + else extract_type_decl_gen ctx fmt type_decl_group kind def extract_body + else + match !backend with + | FStar | Coq | Lean -> + extract_type_decl_gen ctx fmt type_decl_group kind def extract_body + | HOL4 -> extract_type_decl_hol4_opaque ctx fmt def (** Auxiliary function. @@ -1370,7 +1760,9 @@ let extract_type_decl_record_field_projectors (ctx : extraction_ctx) (* Close the inner box projector *) F.pp_close_box fmt (); (* If Coq: end the definition with a "." *) - print_decl_end_delimiter fmt kind; + if !backend = Coq then ( + F.pp_print_cut fmt (); + F.pp_print_string fmt "."); (* Close the outer box projector *) F.pp_close_box fmt (); (* Add breaks to insert new lines between definitions *) @@ -1401,7 +1793,9 @@ let extract_type_decl_record_field_projectors (ctx : extraction_ctx) (* Close the inner box projector *) F.pp_close_box fmt (); (* If Coq: end the definition with a "." *) - print_decl_end_delimiter fmt kind; + if !backend = Coq then ( + F.pp_print_cut fmt (); + F.pp_print_string fmt "."); (* Close the outer box projector *) F.pp_close_box fmt (); (* Add breaks to insert new lines between definitions *) @@ -1424,7 +1818,7 @@ let extract_type_decl_record_field_projectors (ctx : extraction_ctx) let extract_type_decl_extra_info (ctx : extraction_ctx) (fmt : F.formatter) (kind : decl_kind) (decl : type_decl) : unit = match !backend with - | FStar | Lean -> () + | FStar | Lean | HOL4 -> () | Coq -> extract_type_decl_coq_arguments ctx fmt kind decl; extract_type_decl_record_field_projectors ctx fmt kind decl @@ -1448,7 +1842,7 @@ let extract_state_type (fmt : F.formatter) (ctx : extraction_ctx) match !backend with | Coq -> "Axiom" | Lean -> "axiom" - | FStar -> raise (Failure "Unexpected") + | FStar | HOL4 -> raise (Failure "Unexpected") in F.pp_print_string fmt axiom; F.pp_print_space fmt (); @@ -1475,6 +1869,8 @@ let extract_state_type (fmt : F.formatter) (ctx : extraction_ctx) F.pp_print_string fmt ":"; F.pp_print_space fmt (); F.pp_print_string fmt "Type0" + | HOL4 -> + F.pp_print_string fmt ("val _ = new_type (" ^ state_name ^ ", 0)") | Coq | Lean -> print_axiom ()) | Declared -> ( match !backend with @@ -1486,12 +1882,24 @@ let extract_state_type (fmt : F.formatter) (ctx : extraction_ctx) F.pp_print_string fmt ":"; F.pp_print_space fmt (); F.pp_print_string fmt "Type0" + | HOL4 -> + F.pp_print_string fmt ("val _ = new_type (" ^ state_name ^ ", 0)") | Coq | Lean -> print_axiom ())); (* Close the box for the definition *) F.pp_close_box fmt (); (* Add breaks to insert new lines between definitions *) F.pp_print_break fmt 0 0 +(** In some provers like HOL4, we don't print the type parameters when calling + functions (and the polymorphism is uniform...). + + TODO: we may want to check that the polymorphism is indeed uniform when + generating code for such backends, and print at least a warning to the + user when it is not the case. + *) +let print_fun_type_params () : bool = + match !backend with FStar | Coq | Lean -> true | HOL4 -> false + (** Compute the names for all the pure functions generated from a rust function (forward function and backward functions). *) @@ -1507,6 +1915,7 @@ let extract_fun_decl_register_names (ctx : extraction_ctx) (keep_fwd : bool) (* Add the decreases proof for Lean only *) match !Config.backend with | Coq | FStar -> ctx + | HOL4 -> raise (Failure "Unexpected") | Lean -> ctx_add_decreases_proof def ctx else ctx in @@ -1729,12 +2138,13 @@ and extract_function_call (ctx : extraction_ctx) (fmt : F.formatter) let with_opaque_pre = ctx.use_opaque_pre in let fun_name = ctx_get_function with_opaque_pre fun_id ctx in F.pp_print_string fmt fun_name; - (* Print the type parameters *) - List.iter - (fun ty -> - F.pp_print_space fmt (); - extract_ty ctx fmt true ty) - type_args; + (* Print the type parameters, if the backend is not HOL4 *) + if !backend <> HOL4 then + List.iter + (fun ty -> + F.pp_print_space fmt (); + extract_ty ctx fmt TypeDeclId.Set.empty true ty) + type_args; (* Print the arguments *) List.iter (fun ve -> @@ -1787,9 +2197,8 @@ and extract_field_projector (ctx : extraction_ctx) (fmt : F.formatter) F.pp_print_string fmt "."; (* If in Coq, the field projection has to be parenthesized *) (match !backend with - | FStar -> F.pp_print_string fmt field_name - | Coq -> F.pp_print_string fmt ("(" ^ field_name ^ ")") - | Lean -> F.pp_print_string fmt field_name); + | FStar | Lean | HOL4 -> F.pp_print_string fmt field_name + | Coq -> F.pp_print_string fmt ("(" ^ field_name ^ ")")); (* Close the box *) F.pp_close_box fmt () | arg :: args -> @@ -1843,7 +2252,6 @@ and extract_lets (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) (re : texpression) : extraction_ctx = (* Open a box for the let-binding *) F.pp_open_hovbox fmt ctx.indent_incr; - let is_fstar_monadic = monadic && !backend = FStar in let ctx = (* There are two cases: * - do we use a notation like [x <-- y;] @@ -1861,7 +2269,7 @@ and extract_lets (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) let arrow = match !backend with | Coq -> "<-" - | FStar | Lean -> failwith "impossible" + | FStar | Lean | HOL4 -> raise (Failure "impossible") in F.pp_print_string fmt arrow; F.pp_print_space fmt (); @@ -1869,24 +2277,42 @@ and extract_lets (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) F.pp_print_string fmt ";"; ctx) else ( - F.pp_print_string fmt (if is_fstar_monadic then "let*" else "let"); - F.pp_print_space fmt (); + (* Print the "let" *) + if monadic then + match !backend with + | FStar -> + F.pp_print_string fmt "let*"; + F.pp_print_space fmt () + | Coq | Lean -> + F.pp_print_string fmt "let"; + F.pp_print_space fmt () + | HOL4 -> () + else ( + F.pp_print_string fmt "let"; + F.pp_print_space fmt ()); let ctx = extract_typed_pattern ctx fmt true lv in F.pp_print_space fmt (); let eq = match !backend with | FStar -> "=" | Coq -> ":=" - (* TODO: switch to ⟵ once issues are fixed *) - | Lean -> if monadic then "←" else ":=" + | Lean -> + (* TODO: switch to ⟵ once issues are fixed *) + if monadic then "←" else ":=" + | HOL4 -> if monadic then "<-" else "=" in F.pp_print_string fmt eq; F.pp_print_space fmt (); extract_texpression ctx fmt false re; - (* In Lean, monadic let-bindings don't require to end with "in" *) - if !backend <> Lean then ( - F.pp_print_space fmt (); - F.pp_print_string fmt "in"); + (* End the monadic let-binding *) + (match !backend with + | Lean -> + (* In Lean, monadic let-bindings don't require to end with anything *) + () + | Coq | FStar -> + F.pp_print_space fmt (); + F.pp_print_string fmt "in" + | HOL4 -> F.pp_print_string fmt ";"); ctx) in (* Close the box for the let-binding *) @@ -1896,10 +2322,10 @@ and extract_lets (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) ctx in let exists_monadic = List.exists (fun (m, _, _) -> m) lets in - (* If Lean, we rely on monadic blocks, so we insert a do and open a new box + (* If Lean and HOL4, we rely on monadic blocks, so we insert a do and open a new box immediately *) - if !backend = Lean && exists_monadic then ( - F.pp_open_vbox fmt ctx.indent_incr; + if (!backend = Lean || !backend = HOL4) && exists_monadic then ( + F.pp_open_vbox fmt (if !backend = Lean then ctx.indent_incr else 0); F.pp_print_string fmt "do"; F.pp_print_space fmt ()); let ctx = @@ -1913,23 +2339,29 @@ and extract_lets (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) extract_texpression ctx fmt false next_e; (* Close the box for the next expression *) F.pp_close_box fmt (); - (* do-box (Lean only) *) - if !backend = Lean && exists_monadic then F.pp_close_box fmt (); + + (* do-box (Lean and HOL4 only) *) + if (!backend = Lean || !backend = HOL4) && exists_monadic then ( + if !backend = HOL4 then ( + F.pp_print_space fmt (); + F.pp_print_string fmt "od"); + F.pp_close_box fmt ()); (* Close parentheses *) if inside && !backend <> Lean then F.pp_print_string fmt ")"; (* Close the box for the whole expression *) F.pp_close_box fmt () -and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) +and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (_inside : bool) (scrut : texpression) (body : switch_body) : unit = + (* Remark: we don't use the [inside] parameter because we extract matches in + a conservative manner: we always make sure they are parenthesized/delimited + with keywords such as [end] *) (* Open a box for the whole expression. In the case of Lean, we rely on indentation to delimit the end of the branches, and need to insert line breaks: we thus use a vbox. *) if !Config.backend = Lean then F.pp_open_vbox fmt 0 else F.pp_open_hvbox fmt 0; - (* Open parentheses *) - if inside then F.pp_print_string fmt "("; (* Extract the switch *) (match body with | If (e_then, e_else) -> @@ -1961,7 +2393,7 @@ and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) F.pp_print_space fmt (); F.pp_print_string fmt "begin"; F.pp_print_space fmt - | Coq | Lean -> + | Coq | Lean | HOL4 -> F.pp_print_space fmt (); F.pp_print_string fmt "("; F.pp_print_cut fmt) @@ -1978,18 +2410,18 @@ and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) F.pp_close_box fmt (); (* Close the parenthesized expression *) (if parenth then - match !backend with - | FStar -> - F.pp_print_space fmt (); - F.pp_print_string fmt "end" - | Coq | Lean -> F.pp_print_string fmt ")"); + match !backend with + | FStar -> + F.pp_print_space fmt (); + F.pp_print_string fmt "end" + | Coq | Lean | HOL4 -> F.pp_print_string fmt ")"); (* Close the box for the then/else+branch *) F.pp_close_box fmt () in extract_branch true e_then; extract_branch false e_else - | Match branches -> + | Match branches -> ( (* Open a box for the [match ... with] *) F.pp_open_hovbox fmt ctx.indent_incr; (* Print the [match ... with] *) @@ -1998,13 +2430,19 @@ and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) | FStar -> "begin match" | Coq -> "match" | Lean -> "match h:" + | HOL4 -> + (* We're being extra safe in the case of HOL4 *) + "(case" in F.pp_print_string fmt match_begin; F.pp_print_space fmt (); let scrut_inside = PureUtils.texpression_requires_parentheses scrut in extract_texpression ctx fmt scrut_inside scrut; F.pp_print_space fmt (); - F.pp_print_string fmt "with"; + let match_scrut_end = + match !backend with FStar | Coq | Lean -> "with" | HOL4 -> "of" + in + F.pp_print_string fmt match_scrut_end; (* Close the box for the [match ... with] *) F.pp_close_box fmt (); @@ -2020,7 +2458,9 @@ and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) F.pp_print_space fmt (); let ctx = extract_typed_pattern ctx fmt false br.pat in F.pp_print_space fmt (); - let arrow = match !backend with FStar -> "->" | Coq | Lean -> "=>" in + let arrow = + match !backend with FStar -> "->" | Coq | Lean | HOL4 -> "=>" + in F.pp_print_string fmt arrow; (* Close the box for the pattern *) F.pp_close_box fmt (); @@ -2037,58 +2477,89 @@ and extract_Switch (ctx : extraction_ctx) (fmt : F.formatter) (inside : bool) List.iter extract_branch branches; - (* End the match - we rely on indentation in Lean *) - if !backend <> Lean then ( - F.pp_print_space fmt (); - F.pp_print_string fmt "end")); - (* Close parentheses *) - if inside then F.pp_print_string fmt ")"; + (* End the match *) + match !backend with + | Lean -> (*We rely on indentation in Lean *) () + | FStar | Coq -> + F.pp_print_space fmt (); + F.pp_print_string fmt "end" + | HOL4 -> F.pp_print_string fmt ")")); (* Close the box for the whole expression *) F.pp_close_box fmt () and extract_StructUpdate (ctx : extraction_ctx) (fmt : F.formatter) - (_inside : bool) (supd : struct_update) : unit = + (inside : bool) (supd : struct_update) : unit = (* We can't update a subset of the fields in Coq (i.e., we can do [{| x:= 3; y := 4 |}], but there is no syntax for [{| s with x := 3 |}]) *) assert (!backend <> Coq || supd.init = None); - F.pp_open_hvbox fmt 0; - F.pp_open_hvbox fmt ctx.indent_incr; - let lb, rb = - match !backend with Lean | FStar -> ("{", "}") | Coq -> ("{|", "|}") + (* In the case of HOL4, records with no fields are not supported and are + thus extracted to unit. We need to check that by looking up the definition *) + let extract_as_unit = + if !backend = HOL4 then + let d = + TypeDeclId.Map.find supd.struct_id ctx.trans_ctx.type_context.type_decls + in + d.kind = Struct [] + else false in - F.pp_print_string fmt lb; - F.pp_print_space fmt (); - F.pp_open_hvbox fmt ctx.indent_incr; - if supd.init <> None then ( - let var_name = ctx_get_var (Option.get supd.init) ctx in - F.pp_print_string fmt var_name; - F.pp_print_space fmt (); - F.pp_print_string fmt "with"; - F.pp_print_space fmt ()); - F.pp_open_hvbox fmt 0; - let delimiter = match !backend with Lean -> "," | Coq | FStar -> ";" in - let assign = match !backend with Coq | Lean -> ":=" | FStar -> "=" in - Collections.List.iter_link - (fun () -> - F.pp_print_string fmt delimiter; - F.pp_print_space fmt ()) - (fun (fid, fe) -> - F.pp_open_hvbox fmt ctx.indent_incr; - let f = ctx_get_field (AdtId supd.struct_id) fid ctx in - F.pp_print_string fmt f; - F.pp_print_string fmt (" " ^ assign); + if extract_as_unit then + (* Remark: this is only valid for HOL4 (for instance the Coq unit value is [tt]) *) + F.pp_print_string fmt "()" + else ( + F.pp_open_hvbox fmt 0; + F.pp_open_hvbox fmt ctx.indent_incr; + let lb, rb = + match !backend with + | Lean | FStar -> (Some "{", Some "}") + | Coq -> (Some "{|", Some "|}") + | HOL4 -> (None, None) + in + (match lb with + | Some lb -> + F.pp_print_string fmt lb; + F.pp_print_space fmt () + | None -> ()); + let need_paren = inside && !backend = HOL4 in + if need_paren then F.pp_print_string fmt "("; + F.pp_open_hvbox fmt ctx.indent_incr; + if supd.init <> None then ( + let var_name = ctx_get_var (Option.get supd.init) ctx in + F.pp_print_string fmt var_name; F.pp_print_space fmt (); - F.pp_open_hvbox fmt ctx.indent_incr; - extract_texpression ctx fmt true fe; - F.pp_close_box fmt (); - F.pp_close_box fmt ()) - supd.updates; - F.pp_close_box fmt (); - F.pp_close_box fmt (); - F.pp_close_box fmt (); - F.pp_print_space fmt (); - F.pp_print_string fmt rb; - F.pp_close_box fmt () + F.pp_print_string fmt "with"; + F.pp_print_space fmt ()); + F.pp_open_hvbox fmt 0; + let delimiter = + match !backend with Lean -> "," | Coq | FStar | HOL4 -> ";" + in + let assign = + match !backend with Coq | Lean | HOL4 -> ":=" | FStar -> "=" + in + Collections.List.iter_link + (fun () -> + F.pp_print_string fmt delimiter; + F.pp_print_space fmt ()) + (fun (fid, fe) -> + F.pp_open_hvbox fmt ctx.indent_incr; + let f = ctx_get_field (AdtId supd.struct_id) fid ctx in + F.pp_print_string fmt f; + F.pp_print_string fmt (" " ^ assign); + F.pp_print_space fmt (); + F.pp_open_hvbox fmt ctx.indent_incr; + extract_texpression ctx fmt true fe; + F.pp_close_box fmt (); + F.pp_close_box fmt ()) + supd.updates; + F.pp_close_box fmt (); + F.pp_close_box fmt (); + F.pp_close_box fmt (); + if need_paren then F.pp_print_string fmt ")"; + (match rb with + | Some rb -> + F.pp_print_space fmt (); + F.pp_print_string fmt rb + | None -> ()); + F.pp_close_box fmt ()) (** Insert a space, if necessary *) let insert_req_space (fmt : F.formatter) (space : bool ref) : unit = @@ -2098,17 +2569,27 @@ let insert_req_space (fmt : F.formatter) (space : bool ref) : unit = We return two contexts: - the context augmented with bindings for the type parameters - - the previous context augmented with bindings for the input values + - the context augmented with bindings for the type parameters *and* + bindings for the input values + + TODO: do we really need the first one? We should probably always use + the second one. + It comes from the fact that when we print the input values for the + decrease clause, we introduce bindings in the context (because we print + patterns, not the variables). We should figure a cleaner way. *) let extract_fun_parameters (space : bool ref) (ctx : extraction_ctx) (fmt : F.formatter) (def : fun_decl) : extraction_ctx * extraction_ctx = (* Add the type parameters - note that we need those bindings only for the * body translation (they are not top-level) *) let ctx, _ = ctx_add_type_params def.signature.type_params ctx in - (* Print the parameters - rk.: we should have filtered the functions + (* Print the parameters - rem.: we should have filtered the functions * with no input parameters *) - (* The type parameters *) - if def.signature.type_params <> [] then ( + (* The type parameters. + + Note that in HOL4 we don't print the type parameters. + *) + if def.signature.type_params <> [] && !backend <> HOL4 then ( (* Open a box for the type parameters *) F.pp_open_hovbox fmt 0; insert_req_space fmt space; @@ -2122,7 +2603,10 @@ let extract_fun_parameters (space : bool ref) (ctx : extraction_ctx) F.pp_print_string fmt ":"; F.pp_print_space fmt (); let type_keyword = - match !backend with FStar -> "Type0" | Coq | Lean -> "Type" + match !backend with + | FStar -> "Type0" + | Coq | Lean -> "Type" + | HOL4 -> raise (Failure "Unreachable") in F.pp_print_string fmt (type_keyword ^ ")"); (* Close the box for the type parameters *) @@ -2142,7 +2626,7 @@ let extract_fun_parameters (space : bool ref) (ctx : extraction_ctx) F.pp_print_space fmt (); F.pp_print_string fmt ":"; F.pp_print_space fmt (); - extract_ty ctx fmt false lv.ty; + extract_ty ctx fmt TypeDeclId.Set.empty false lv.ty; F.pp_print_string fmt ")"; (* Close the box for the input parameters *) F.pp_close_box fmt (); @@ -2161,7 +2645,7 @@ let extract_fun_input_parameters_types (ctx : extraction_ctx) (fmt : F.formatter) (def : fun_decl) : unit = let extract_param (ty : ty) : unit = let inside = false in - extract_ty ctx fmt inside ty; + extract_ty ctx fmt TypeDeclId.Set.empty inside ty; F.pp_print_space fmt (); F.pp_print_string fmt "->"; F.pp_print_space fmt () @@ -2171,7 +2655,9 @@ let extract_fun_input_parameters_types (ctx : extraction_ctx) let assert_backend_supports_decreases_clauses () = match !backend with | FStar | Lean -> () - | _ -> failwith "decreases clauses only supported for the Lean & F* backends" + | _ -> + raise + (Failure "decreases clauses only supported for the Lean & F* backends") (** Extract a decreases clause function template body. @@ -2346,14 +2832,12 @@ let extract_template_lean_termination_and_decreasing (ctx : extraction_ctx) (** Extract a function declaration. - Note that all the names used for extraction should already have been - registered. + This function is for all function declarations and all backends **at the exception** + of opaque (assumed/declared) functions for HOL4. - We take the definition of the forward translation as parameter (which is - equal to the definition to extract, if we extract a forward function) because - it is useful for the decrease clause. + See {!extract_fun_decl}. *) -let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) +let extract_fun_decl_gen (ctx : extraction_ctx) (fmt : F.formatter) (kind : decl_kind) (has_decreases_clause : bool) (def : fun_decl) : unit = assert (not def.is_global_decl_body); (* Retrieve the function name *) @@ -2363,7 +2847,8 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) ctx in (* Add a break before *) - F.pp_print_break fmt 0 0; + if !backend <> HOL4 || not (decl_is_first_from_group kind) then + F.pp_print_break fmt 0 0; (* Print a comment to link the extracted type to its original rust definition *) extract_comment fmt ("[" ^ Print.fun_name_to_string def.basename ^ "]"); F.pp_print_space fmt (); @@ -2371,6 +2856,9 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) * one line and indents are correct *) F.pp_open_hvbox fmt 0; F.pp_open_vbox fmt ctx.indent_incr; + (* For HOL4: we may need to put parentheses around the definition *) + let parenthesize = !backend = HOL4 && decl_is_not_last_from_group kind in + if parenthesize then F.pp_print_string fmt "("; (* Open a box for "let FUN_NAME (PARAMS) : EFFECT =" *) F.pp_open_hovbox fmt ctx.indent_incr; (* > "let FUN_NAME" *) @@ -2382,12 +2870,16 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) *) let is_opaque_coq = !backend = Coq && is_opaque in let use_forall = is_opaque_coq && def.signature.type_params <> [] in - (* *) - let qualif = ctx.fmt.fun_decl_kind_to_qualif kind in - (* For Lean: we generate a record of assumed functions *) - if not (!backend = Lean && (kind = Assumed || kind = Declared)) then ( - F.pp_print_string fmt qualif; - F.pp_print_space fmt ()); + (* Print the qualifier ("assume", etc.). + + For Lean: we generate a record of assumed functions *) + (if not (!backend = Lean && (kind = Assumed || kind = Declared)) then + let qualif = ctx.fmt.fun_decl_kind_to_qualif kind in + match qualif with + | Some qualif -> + F.pp_print_string fmt qualif; + F.pp_print_space fmt () + | None -> ()); F.pp_print_string fmt def_name; F.pp_print_space fmt (); if use_forall then ( @@ -2428,7 +2920,8 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) if !backend = FStar then ( F.pp_print_string fmt "Tot"; F.pp_print_space fmt ())); - extract_ty ctx fmt has_decreases_clause def.signature.output; + extract_ty ctx fmt TypeDeclId.Set.empty has_decreases_clause + def.signature.output; (* Close the box for the return type *) F.pp_close_box fmt (); (* Print the decrease clause - rk.: a function with a decreases clause @@ -2468,6 +2961,8 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) in Collections.List.prefix num_fwd_inputs all_inputs in + (* TODO: we should probably print the input variables, not the typed + patterns *) let _ = List.fold_left (fun ctx (lv : typed_pattern) -> @@ -2487,7 +2982,7 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) (* Print the "=" *) if not is_opaque then ( F.pp_print_space fmt (); - let eq = match !backend with FStar -> "=" | Coq | Lean -> ":=" in + let eq = match !backend with FStar | HOL4 -> "=" | Coq | Lean -> ":=" in F.pp_print_string fmt eq); (* Close the box for "(PARAMS) : EFFECT =" *) F.pp_close_box fmt (); @@ -2569,17 +3064,82 @@ let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) F.pp_close_box fmt (); (* Close the box for the [decreasing by ...] *) F.pp_close_box fmt ()); - (* Add the definition group end delimiter *) - print_decl_end_delimiter fmt kind; + (* Close the parentheses *) + if parenthesize then F.pp_print_string fmt ")"; + (* Add the definition end delimiter *) + if !backend = HOL4 && decl_is_not_last_from_group kind then ( + F.pp_print_space fmt (); + F.pp_print_string fmt "/\\") + else if !backend = Coq && decl_is_last_from_group kind then ( + (* This is actually an end of group delimiter. For aesthetic reasons + we print it here instead of in {!end_fun_decl_group}. *) + F.pp_print_cut fmt (); + F.pp_print_string fmt "."); (* Close the outer box for the definition *) F.pp_close_box fmt (); (* Add breaks to insert new lines between definitions *) - F.pp_print_break fmt 0 0 + if !backend <> HOL4 || decl_is_not_last_from_group kind then + F.pp_print_break fmt 0 0 + +(** Extract an opaque function declaration to HOL4. + + Remark (SH): having to treat this specific case separately is very annoying, + but I could not find a better way. + *) +let extract_fun_decl_hol4_opaque (ctx : extraction_ctx) (fmt : F.formatter) + (def : fun_decl) : unit = + (* Retrieve the definition name *) + let with_opaque_pre = false in + let def_name = + ctx_get_local_function with_opaque_pre def.def_id None None ctx + in + (* Add the type parameters - note that we need those bindings only for the + * generation of the type (they are not top-level) *) + let ctx, _ = ctx_add_type_params def.signature.type_params ctx in + (* Open a box for the whole definition *) + F.pp_open_hovbox fmt ctx.indent_incr; + (* Generate: `val _ = new_constant ("...",` *) + F.pp_print_string fmt ("val _ = new_constant (\"" ^ def_name ^ ", "); + (* Open a box for the type *) + F.pp_open_hvbox fmt 0; + (* Generate the type *) + extract_fun_input_parameters_types ctx fmt def; + extract_ty ctx fmt TypeDeclId.Set.empty false def.signature.output; + (* Close the box for the type *) + F.pp_close_box fmt (); + (* Close the parenthesis opened for the inputs of `new_constant` *) + F.pp_print_string fmt ")"; + (* Close the box for the definition *) + F.pp_close_box fmt () + +(** Extract a function declaration. + + Note that all the names used for extraction should already have been + registered. + + We take the definition of the forward translation as parameter (which is + equal to the definition to extract, if we extract a forward function) because + it is useful for the decrease clause. + + This function should be inserted between calls to {!start_fun_decl_group} + and {!end_fun_decl_group}. + *) +let extract_fun_decl (ctx : extraction_ctx) (fmt : F.formatter) + (kind : decl_kind) (has_decreases_clause : bool) (def : fun_decl) : unit = + assert (not def.is_global_decl_body); + (* We treat HOL4 opaque functions in a specific manner *) + if !backend = HOL4 && Option.is_none def.body then + extract_fun_decl_hol4_opaque ctx fmt def + else extract_fun_decl_gen ctx fmt kind has_decreases_clause def (** Extract a global declaration body of the shape "QUALIF NAME : TYPE = BODY" - with a custom body extractor + with a custom body extractor. + + We introduce this helper because every (non opaque) global declaration gets + extracted to two declarations, and we can actually factor out the generation + of those declarations. See {!extract_global_decl} for more explanations. *) -let extract_global_decl_body (ctx : extraction_ctx) (fmt : F.formatter) +let extract_global_decl_body_gen (ctx : extraction_ctx) (fmt : F.formatter) (kind : decl_kind) (name : string) (ty : ty) (extract_body : (F.formatter -> unit) Option.t) : unit = let is_opaque = Option.is_none extract_body in @@ -2591,8 +3151,11 @@ let extract_global_decl_body (ctx : extraction_ctx) (fmt : F.formatter) (* Open "QUALIF NAME : TYPE =" box (depth=1) *) F.pp_open_hovbox fmt ctx.indent_incr; (* Print "QUALIF NAME " *) - F.pp_print_string fmt (ctx.fmt.fun_decl_kind_to_qualif kind); - F.pp_print_space fmt (); + (match ctx.fmt.fun_decl_kind_to_qualif kind with + | Some qualif -> + F.pp_print_string fmt qualif; + F.pp_print_space fmt () + | None -> ()); F.pp_print_string fmt name; F.pp_print_space fmt (); @@ -2605,14 +3168,14 @@ let extract_global_decl_body (ctx : extraction_ctx) (fmt : F.formatter) (* Open "TYPE" box (depth=3) *) F.pp_open_hovbox fmt ctx.indent_incr; (* Print "TYPE" *) - extract_ty ctx fmt false ty; + extract_ty ctx fmt TypeDeclId.Set.empty false ty; (* Close "TYPE" box (depth=3) *) F.pp_close_box fmt (); if not is_opaque then ( (* Print " =" *) F.pp_print_space fmt (); - let eq = match !backend with FStar -> "=" | Coq | Lean -> ":=" in + let eq = match !backend with FStar | HOL4 -> "=" | Coq | Lean -> ":=" in F.pp_print_string fmt eq); (* Close ": TYPE =" box (depth=2) *) F.pp_close_box fmt (); @@ -2632,22 +3195,52 @@ let extract_global_decl_body (ctx : extraction_ctx) (fmt : F.formatter) F.pp_close_box fmt (); (* Coq: add a "." *) - print_decl_end_delimiter fmt Declared; + if !backend = Coq then ( + F.pp_print_cut fmt (); + F.pp_print_string fmt "."); (* Close the outer definition box (depth=0) *) F.pp_close_box fmt () +(** Extract an opaque global declaration for HOL4. + + Remark (SH): having to treat this specific case separately is very annoying, + but I could not find a better way. + *) +let extract_global_decl_hol4_opaque (ctx : extraction_ctx) (fmt : F.formatter) + (name : string) (ty : ty) : unit = + (* Open the definition boxe (depth=0) *) + F.pp_open_hvbox fmt ctx.indent_incr; + (* [val ..._def = new_constant ("...",] *) + F.pp_open_hovbox fmt 0; + F.pp_print_string fmt + ("val " ^ name ^ "_def = new_constant (\"" ^ name ^ "\", "); + F.pp_close_box fmt (); + (* Print the type *) + F.pp_open_hovbox fmt 0; + extract_ty ctx fmt TypeDeclId.Set.empty false ty; + F.pp_close_box fmt (); + (* Close the definition boxe *) F.pp_close_box fmt () + (** Extract a global declaration. - We generate the body which computes the global value separately from the + We generate the body which *computes* the global value separately from the value declaration itself. For example in Rust, [static X: u32 = 3;] will be translated to the following F*: - [let x_body : result u32 = Return 3] - [let x_c : u32 = eval_global x_body] + [let x_body : result u32 = Return 3] (* this has type [result u32] *) + [let x_c : u32 = eval_global x_body] (* this has type [u32] (no [result]!) *) + + This function generates the two declarations. + + Remark: because global declaration groups are always singletons (i.e., + there are no groups of mutually recursive global declarations), this function + doesn't need to be called between calls to functions of the shape + [{start,end}_gloabl_decl_group], contrary to {!extract_type_decl} + and {!extract_fun_decl}. *) let extract_global_decl (ctx : extraction_ctx) (fmt : F.formatter) (global : A.global_decl) (body : fun_decl) (interface : bool) : unit = @@ -2677,13 +3270,19 @@ let extract_global_decl (ctx : extraction_ctx) (fmt : F.formatter) in match body.body with | None -> + (* No body: only generate a [val x_c : u32] declaration *) let kind = if interface then Declared else Assumed in - extract_global_decl_body ctx fmt kind decl_name decl_ty None + if !backend = HOL4 then + extract_global_decl_hol4_opaque ctx fmt decl_name decl_ty + else extract_global_decl_body_gen ctx fmt kind decl_name decl_ty None | Some body -> - extract_global_decl_body ctx fmt SingleNonRec body_name body_ty + (* There is a body *) + (* Generate: [let x_body : result u32 = Return 3] *) + extract_global_decl_body_gen ctx fmt SingleNonRec body_name body_ty (Some (fun fmt -> extract_texpression ctx fmt false body.body)); F.pp_print_break fmt 0 0; - extract_global_decl_body ctx fmt SingleNonRec decl_name decl_ty + (* Generate: [let x_c : u32 = eval_global x_body] *) + extract_global_decl_body_gen ctx fmt SingleNonRec decl_name decl_ty (Some (fun fmt -> let body = @@ -2691,6 +3290,7 @@ let extract_global_decl (ctx : extraction_ctx) (fmt : F.formatter) | FStar -> "eval_global " ^ body_name | Lean -> "eval_global " ^ body_name ^ " (by simp)" | Coq -> body_name ^ "%global" + | HOL4 -> "get_return_value " ^ body_name in F.pp_print_string fmt body)); (* Add a break to insert lines between declarations *) @@ -2790,7 +3390,22 @@ let extract_unit_test_if_unit_fun (ctx : extraction_ctx) (fmt : F.formatter) F.pp_print_string fmt "=="; F.pp_print_space fmt (); let success = ctx_get_variant (Assumed Result) result_return_id ctx in - F.pp_print_string fmt ("." ^ success ^ " ())")); + F.pp_print_string fmt ("." ^ success ^ " ())") + | HOL4 -> + F.pp_print_string fmt "val _ = assert_return ("; + F.pp_print_string fmt "“"; + (* Note that if the function is opaque, the unit test will fail + because the normalizer will get stuck *) + let with_opaque_pre = ctx.use_opaque_pre in + let fun_name = + ctx_get_local_function with_opaque_pre def.def_id def.loop_id + def.back_id ctx + in + F.pp_print_string fmt fun_name; + if sg.inputs <> [] then ( + F.pp_print_space fmt (); + F.pp_print_string fmt "()"); + F.pp_print_string fmt "”)"); (* Close the box for the test *) F.pp_close_box fmt (); (* Add a break after *) |