diff options
Diffstat (limited to 'compiler/Translate.ml')
-rw-r--r-- | compiler/Translate.ml | 871 |
1 files changed, 871 insertions, 0 deletions
diff --git a/compiler/Translate.ml b/compiler/Translate.ml new file mode 100644 index 00000000..8f3b94c4 --- /dev/null +++ b/compiler/Translate.ml @@ -0,0 +1,871 @@ +open InterpreterStatements +open Interpreter +module L = Logging +module T = Types +module A = LlbcAst +module SA = SymbolicAst +module Micro = PureMicroPasses +open PureUtils +open TranslateCore + +(** The local logger *) +let log = TranslateCore.log + +type config = { + eval_config : Contexts.partial_config; + mp_config : Micro.config; + use_state : bool; + (** Controls whether we need to use a state to model the external world + (I/O, for instance). + *) + split_files : bool; + (** Controls whether we split the generated definitions between different + files for the types, clauses and functions, or if we group them in + one file. + *) + test_unit_functions : bool; + (** If true, insert tests in the generated files to check that the + unit functions normalize to [Success _]. + + For instance, in F* it generates code like this: + {[ + let _ = assert_norm (FUNCTION () = Success ()) + ]} + *) + extract_decreases_clauses : bool; + (** If [true], insert [decreases] clauses for all the recursive definitions. + + The body of such clauses must be defined by the user. + *) + extract_template_decreases_clauses : bool; + (** In order to help the user, we can generate "template" decrease clauses + (i.e., definitions with proper signatures but dummy bodies) in a + dedicated file. + *) +} + +(** The result of running the symbolic interpreter on a function: + - the list of symbolic values used for the input values + - the generated symbolic AST +*) +type symbolic_fun_translation = V.symbolic_value list * SA.expression + +(** Execute the symbolic interpreter on a function to generate a list of symbolic ASTs, + for the forward function and the backward functions. +*) +let translate_function_to_symbolics (config : C.partial_config) + (trans_ctx : trans_ctx) (fdef : A.fun_decl) : + (symbolic_fun_translation * symbolic_fun_translation list) option = + (* Debug *) + log#ldebug + (lazy + ("translate_function_to_symbolics: " + ^ Print.fun_name_to_string fdef.A.name)); + + let { type_context; fun_context; global_context } = trans_ctx in + let fun_context = { C.fun_decls = fun_context.fun_decls } in + + match fdef.body with + | None -> None + | Some _ -> + (* Evaluate *) + let synthesize = true in + let evaluate gid = + let inputs, symb = + evaluate_function_symbolic config synthesize type_context fun_context + global_context fdef gid + in + (inputs, Option.get symb) + in + (* Execute the forward function *) + let forward = evaluate None in + (* Execute the backward functions *) + let backwards = + T.RegionGroupId.mapi + (fun gid _ -> evaluate (Some gid)) + fdef.signature.regions_hierarchy + in + + (* Return *) + Some (forward, backwards) + +(** Translate a function, by generating its forward and backward translations. + + [fun_sigs]: maps the forward/backward functions to their signatures. In case + of backward functions, we also provide names for the outputs. + TODO: maybe we should introduce a record for this. +*) +let translate_function_to_pure (config : C.partial_config) + (mp_config : Micro.config) (trans_ctx : trans_ctx) + (fun_sigs : SymbolicToPure.fun_sig_named_outputs RegularFunIdMap.t) + (pure_type_decls : Pure.type_decl Pure.TypeDeclId.Map.t) (fdef : A.fun_decl) + : pure_fun_translation = + (* Debug *) + log#ldebug + (lazy + ("translate_function_to_pure: " ^ Print.fun_name_to_string fdef.A.name)); + + let { type_context; fun_context; global_context } = trans_ctx in + let def_id = fdef.def_id in + + (* Compute the symbolic ASTs, if the function is transparent *) + let symbolic_trans = translate_function_to_symbolics config trans_ctx fdef in + let symbolic_forward, symbolic_backwards = + match symbolic_trans with + | None -> (None, None) + | Some (fwd, backs) -> (Some fwd, Some backs) + in + + (* Convert the symbolic ASTs to pure ASTs: *) + + (* Initialize the context *) + let forward_sig = RegularFunIdMap.find (A.Regular def_id, None) fun_sigs in + let sv_to_var = V.SymbolicValueId.Map.empty in + let var_counter = Pure.VarId.generator_zero in + let state_var, var_counter = Pure.VarId.fresh var_counter in + let calls = V.FunCallId.Map.empty in + let abstractions = V.AbstractionId.Map.empty in + let type_context = + { + SymbolicToPure.types_infos = type_context.type_infos; + llbc_type_decls = type_context.type_decls; + type_decls = pure_type_decls; + } + in + let fun_context = + { + SymbolicToPure.llbc_fun_decls = fun_context.fun_decls; + fun_sigs; + fun_infos = fun_context.fun_infos; + } + in + let global_context = + { SymbolicToPure.llbc_global_decls = global_context.global_decls } + in + let ctx = + { + SymbolicToPure.bid = None; + (* Dummy for now *) + sg = forward_sig.sg; + (* Will need to be updated for the backward functions *) + sv_to_var; + var_counter; + state_var; + type_context; + fun_context; + global_context; + fun_decl = fdef; + forward_inputs = []; + (* Empty for now *) + backward_inputs = T.RegionGroupId.Map.empty; + (* Empty for now *) + backward_outputs = T.RegionGroupId.Map.empty; + (* Empty for now *) + calls; + abstractions; + } + in + + (* We need to initialize the input/output variables *) + let num_forward_inputs = List.length fdef.signature.inputs in + let add_forward_inputs input_svs ctx = + match fdef.body with + | None -> ctx + | Some body -> + let forward_input_vars = LlbcAstUtils.fun_body_get_input_vars body in + let forward_input_varnames = + List.map (fun (v : A.var) -> v.name) forward_input_vars + in + let input_svs = List.combine forward_input_varnames input_svs in + let ctx, forward_inputs = + SymbolicToPure.fresh_named_vars_for_symbolic_values input_svs ctx + in + { ctx with forward_inputs } + in + + (* The symbolic to pure config *) + let sp_config = + { + SymbolicToPure.filter_useless_back_calls = + mp_config.filter_useless_monadic_calls; + } + in + + (* Translate the forward function *) + let pure_forward = + match symbolic_forward with + | None -> SymbolicToPure.translate_fun_decl sp_config ctx None + | Some (fwd_svs, fwd_ast) -> + SymbolicToPure.translate_fun_decl sp_config + (add_forward_inputs fwd_svs ctx) + (Some fwd_ast) + in + + (* Translate the backward functions *) + let translate_backward (rg : T.region_var_group) : Pure.fun_decl = + (* For the backward inputs/outputs initialization: we use the fact that + * there are no nested borrows for now, and so that the region groups + * can't have parents *) + assert (rg.parents = []); + let back_id = rg.id in + + match symbolic_backwards with + | None -> + (* Initialize the context - note that the ret_ty is not really + * useful as we don't translate a body *) + let backward_sg = + RegularFunIdMap.find (A.Regular def_id, Some back_id) fun_sigs + in + let ctx = { ctx with bid = Some back_id; sg = backward_sg.sg } in + + (* Translate *) + SymbolicToPure.translate_fun_decl sp_config ctx None + | Some symbolic_backwards -> + let input_svs, symbolic = + T.RegionGroupId.nth symbolic_backwards back_id + in + let ctx = add_forward_inputs input_svs ctx in + (* TODO: the computation of the backward inputs is a bit awckward... *) + let backward_sg = + RegularFunIdMap.find (A.Regular def_id, Some back_id) fun_sigs + in + (* We need to ignore the forward inputs, and the state input (if there is) *) + let fun_info = + SymbolicToPure.get_fun_effect_info fun_context.fun_infos + (A.Regular def_id) (Some back_id) + in + let _, backward_inputs = + Collections.List.split_at backward_sg.sg.inputs + (num_forward_inputs + if fun_info.input_state then 1 else 0) + in + (* As we forbid nested borrows, the additional inputs for the backward + * functions come from the borrows in the return value of the rust function: + * we thus use the name "ret" for those inputs *) + let backward_inputs = + List.map (fun ty -> (Some "ret", ty)) backward_inputs + in + let ctx, backward_inputs = + SymbolicToPure.fresh_vars backward_inputs ctx + in + (* The outputs for the backward functions, however, come from borrows + * present in the input values of the rust function: for those we reuse + * the names of the input values. *) + let backward_outputs = + List.combine backward_sg.output_names backward_sg.sg.doutputs + in + let ctx, backward_outputs = + SymbolicToPure.fresh_vars backward_outputs ctx + in + let backward_inputs = + T.RegionGroupId.Map.singleton back_id backward_inputs + in + let backward_outputs = + T.RegionGroupId.Map.singleton back_id backward_outputs + in + + (* Put everything in the context *) + let ctx = + { + ctx with + bid = Some back_id; + sg = backward_sg.sg; + backward_inputs; + backward_outputs; + } + in + + (* Translate *) + SymbolicToPure.translate_fun_decl sp_config ctx (Some symbolic) + in + let pure_backwards = + List.map translate_backward fdef.signature.regions_hierarchy + in + + (* Return *) + (pure_forward, pure_backwards) + +let translate_module_to_pure (config : C.partial_config) + (mp_config : Micro.config) (use_state : bool) (crate : Crates.llbc_crate) : + trans_ctx * Pure.type_decl list * (bool * pure_fun_translation) list = + (* Debug *) + log#ldebug (lazy "translate_module_to_pure"); + + (* Compute the type and function contexts *) + let type_context, fun_context, global_context = + compute_type_fun_global_contexts crate + in + let fun_infos = + FA.analyze_module crate fun_context.C.fun_decls + global_context.C.global_decls use_state + in + let fun_context = { fun_decls = fun_context.fun_decls; fun_infos } in + let trans_ctx = { type_context; fun_context; global_context } in + + (* Translate all the type definitions *) + let type_decls = SymbolicToPure.translate_type_decls crate.types in + + (* Compute the type definition map *) + let type_decls_map = + Pure.TypeDeclId.Map.of_list + (List.map (fun (def : Pure.type_decl) -> (def.def_id, def)) type_decls) + in + + (* Translate all the function *signatures* *) + let assumed_sigs = + List.map + (fun (id, sg, _, _) -> + (A.Assumed id, List.map (fun _ -> None) (sg : A.fun_sig).inputs, sg)) + Assumed.assumed_infos + in + let local_sigs = + List.map + (fun (fdef : A.fun_decl) -> + let input_names = + match fdef.body with + | None -> List.map (fun _ -> None) fdef.signature.inputs + | Some body -> + List.map + (fun (v : A.var) -> v.name) + (LlbcAstUtils.fun_body_get_input_vars body) + in + (A.Regular fdef.def_id, input_names, fdef.signature)) + crate.functions + in + let sigs = List.append assumed_sigs local_sigs in + let fun_sigs = + SymbolicToPure.translate_fun_signatures fun_context.fun_infos + type_context.type_infos sigs + in + + (* Translate all the *transparent* functions *) + let pure_translations = + List.map + (translate_function_to_pure config mp_config trans_ctx fun_sigs + type_decls_map) + crate.functions + in + + (* Apply the micro-passes *) + let pure_translations = + List.map + (Micro.apply_passes_to_pure_fun_translation mp_config trans_ctx) + pure_translations + in + + (* Return *) + (trans_ctx, type_decls, pure_translations) + +(** Extraction context *) +type gen_ctx = { + crate : Crates.llbc_crate; + extract_ctx : PureToExtract.extraction_ctx; + trans_types : Pure.type_decl Pure.TypeDeclId.Map.t; + trans_funs : (bool * pure_fun_translation) A.FunDeclId.Map.t; + functions_with_decreases_clause : A.FunDeclId.Set.t; +} + +type gen_config = { + mp_config : Micro.config; + use_state : bool; + extract_types : bool; + extract_decreases_clauses : bool; + extract_template_decreases_clauses : bool; + extract_fun_decls : bool; + extract_transparent : bool; + (** If [true], extract the transparent declarations, otherwise ignore. *) + extract_opaque : bool; + (** If [true], extract the opaque declarations, otherwise ignore. *) + extract_state_type : bool; + (** If [true], generate a definition/declaration for the state type *) + interface : bool; + (** [true] if we generate an interface file, [false] otherwise. + For now, this only impacts whether we use [val] or [assume val] for the + opaque definitions. In the future, we might want to extract all the + declarations in an interface file, together with an implementation file + if needed. + *) + test_unit_functions : bool; +} + +(** Returns the pair: (has opaque type decls, has opaque fun decls) *) +let module_has_opaque_decls (ctx : gen_ctx) : bool * bool = + let has_opaque_types = + Pure.TypeDeclId.Map.exists + (fun _ (d : Pure.type_decl) -> + match d.kind with Opaque -> true | _ -> false) + ctx.trans_types + in + let has_opaque_funs = + A.FunDeclId.Map.exists + (fun _ ((_, (t_fwd, _)) : bool * pure_fun_translation) -> + Option.is_none t_fwd.body) + ctx.trans_funs + in + (has_opaque_types, has_opaque_funs) + +(** A generic utility to generate the extracted definitions: as we may want to + split the definitions between different files (or not), we can control + what is precisely extracted. + *) +let extract_definitions (fmt : Format.formatter) (config : gen_config) + (ctx : gen_ctx) : unit = + (* Export the definition groups to the file, in the proper order *) + let export_type (qualif : ExtractToFStar.type_decl_qualif) + (id : Pure.TypeDeclId.id) : unit = + (* Retrive the declaration *) + let def = Pure.TypeDeclId.Map.find id ctx.trans_types in + (* Update the qualifier, if the type is opaque *) + let is_opaque, qualif = + match def.kind with + | Enum _ | Struct _ -> (false, qualif) + | Opaque -> + let qualif = + if config.interface then ExtractToFStar.TypeVal + else ExtractToFStar.AssumeType + in + (true, qualif) + in + (* Extract, if the config instructs to do so (depending on whether the type + * is opaque or not) *) + if + (is_opaque && config.extract_opaque) + || ((not is_opaque) && config.extract_transparent) + then ExtractToFStar.extract_type_decl ctx.extract_ctx fmt qualif def + in + + (* Utility to check a function has a decrease clause *) + let has_decreases_clause (def : Pure.fun_decl) : bool = + A.FunDeclId.Set.mem def.def_id ctx.functions_with_decreases_clause + in + + (* In case of (non-mutually) recursive functions, we use a simple procedure to + * check if the forward and backward functions are mutually recursive. + *) + let export_functions (is_rec : bool) + (pure_ls : (bool * pure_fun_translation) list) : unit = + (* Concatenate the function definitions, filtering the useless forward + * functions. We also make pairs: (forward function, backward function) + * (the forward function contains useful information that we want to keep) *) + let fls = + List.concat + (List.map + (fun (keep_fwd, (fwd, back_ls)) -> + let back_ls = List.map (fun back -> (fwd, back)) back_ls in + if keep_fwd then (fwd, fwd) :: back_ls else back_ls) + pure_ls) + in + (* Extract the decrease clauses template bodies *) + if config.extract_template_decreases_clauses then + List.iter + (fun (_, (fwd, _)) -> + let has_decr_clause = has_decreases_clause fwd in + if has_decr_clause then + ExtractToFStar.extract_template_decreases_clause ctx.extract_ctx fmt + fwd) + pure_ls; + (* Extract the function definitions *) + (if config.extract_fun_decls then + (* Check if the functions are mutually recursive - this really works + * to check if the forward and backward translations of a single + * recursive function are mutually recursive *) + let is_mut_rec = + if is_rec then + if List.length pure_ls <= 1 then + not (PureUtils.functions_not_mutually_recursive (List.map fst fls)) + else true + else false + in + List.iteri + (fun i (fwd_def, def) -> + let is_opaque = Option.is_none fwd_def.Pure.body in + let qualif = + if is_opaque then + if config.interface then ExtractToFStar.Val + else ExtractToFStar.AssumeVal + else if not is_rec then ExtractToFStar.Let + else if is_mut_rec then + if i = 0 then ExtractToFStar.LetRec else ExtractToFStar.And + else ExtractToFStar.LetRec + in + let has_decr_clause = + has_decreases_clause def && config.extract_decreases_clauses + in + (* Check if the definition needs to be filtered or not *) + if + ((not is_opaque) && config.extract_transparent) + || (is_opaque && config.extract_opaque) + then + ExtractToFStar.extract_fun_decl ctx.extract_ctx fmt qualif + has_decr_clause def) + fls); + (* Insert unit tests if necessary *) + if config.test_unit_functions then + List.iter + (fun (keep_fwd, (fwd, _)) -> + if keep_fwd then + ExtractToFStar.extract_unit_test_if_unit_fun ctx.extract_ctx fmt fwd) + pure_ls + in + + (* TODO: Check correct behaviour with opaque globals *) + let export_global (id : A.GlobalDeclId.id) : unit = + let global_decls = ctx.extract_ctx.trans_ctx.global_context.global_decls in + let global = A.GlobalDeclId.Map.find id global_decls in + let _, (body, body_backs) = + A.FunDeclId.Map.find global.body_id ctx.trans_funs + in + assert (List.length body_backs = 0); + + let is_opaque = Option.is_none body.Pure.body in + if + ((not is_opaque) && config.extract_transparent) + || (is_opaque && config.extract_opaque) + then + ExtractToFStar.extract_global_decl ctx.extract_ctx fmt global body + config.interface + in + + let export_state_type () : unit = + let qualif = + if config.interface then ExtractToFStar.TypeVal + else ExtractToFStar.AssumeType + in + ExtractToFStar.extract_state_type fmt ctx.extract_ctx qualif + in + + let export_decl (decl : Crates.declaration_group) : unit = + match decl with + | Type (NonRec id) -> + if config.extract_types then export_type ExtractToFStar.Type id + | Type (Rec ids) -> + (* Rk.: we shouldn't have (mutually) recursive opaque types *) + if config.extract_types then + List.iteri + (fun i id -> + let qualif = + if i = 0 then ExtractToFStar.Type else ExtractToFStar.And + in + export_type qualif id) + ids + | Fun (NonRec id) -> + (* Lookup *) + let pure_fun = A.FunDeclId.Map.find id ctx.trans_funs in + (* Translate *) + export_functions false [ pure_fun ] + | Fun (Rec ids) -> + (* General case of mutually recursive functions *) + (* Lookup *) + let pure_funs = + List.map (fun id -> A.FunDeclId.Map.find id ctx.trans_funs) ids + in + (* Translate *) + export_functions true pure_funs + | Global id -> export_global id + in + + (* If we need to export the state type: we try to export it after we defined + * the type definitions, because if the user wants to define a model for the + * type, he might want to reuse them in the state type. + * More specifically: if we extract functions, we have no choice but to define + * the state type before the functions, because they may reuse this state + * type: in this case, we define/declare it at the very beginning. Otherwise, + * we define/declare it at the very end. + *) + if config.extract_state_type && config.extract_fun_decls then + export_state_type (); + List.iter export_decl ctx.crate.declarations; + if config.extract_state_type && not config.extract_fun_decls then + export_state_type () + +let extract_file (config : gen_config) (ctx : gen_ctx) (filename : string) + (rust_module_name : string) (module_name : string) (custom_msg : string) + (custom_imports : string list) (custom_includes : string list) : unit = + (* Open the file and create the formatter *) + let out = open_out filename in + let fmt = Format.formatter_of_out_channel out in + + (* Print the headers. + * Note that we don't use the OCaml formatter for purpose: we want to control + * line insertion (we have to make sure that some instructions like [open MODULE] + * are printed on one line!). + * This is ok as long as we end up with a line break, so that the formatter's + * internal count is consistent with the state of the file. + *) + (* Create the header *) + Printf.fprintf out "(** THIS FILE WAS AUTOMATICALLY GENERATED BY AENEAS *)\n"; + Printf.fprintf out "(** [%s]%s *)\n" rust_module_name custom_msg; + Printf.fprintf out "module %s\n" module_name; + Printf.fprintf out "open Primitives\n"; + (* Add the custom imports *) + List.iter (fun m -> Printf.fprintf out "open %s\n" m) custom_imports; + (* Add the custom includes *) + List.iter (fun m -> Printf.fprintf out "include %s\n" m) custom_includes; + (* Z3 options - note that we use fuel 1 because it its useful for the decrease clauses *) + Printf.fprintf out "\n#set-options \"--z3rlimit 50 --fuel 1 --ifuel 1\"\n"; + + (* From now onwards, we use the formatter *) + (* Set the margin *) + Format.pp_set_margin fmt 80; + + (* Create a vertical box *) + Format.pp_open_vbox fmt 0; + + (* Extract the definitions *) + extract_definitions fmt config ctx; + + (* Close the box and end the formatting *) + Format.pp_close_box fmt (); + Format.pp_print_newline fmt (); + + (* Some logging *) + log#linfo (lazy ("Generated: " ^ filename)); + + (* Flush and close the file *) + close_out out + +(** Translate a module and write the synthesized code to an output file. + TODO: rename to translate_crate + *) +let translate_module (filename : string) (dest_dir : string) (config : config) + (crate : Crates.llbc_crate) : unit = + (* Translate the module to the pure AST *) + let trans_ctx, trans_types, trans_funs = + translate_module_to_pure config.eval_config config.mp_config + config.use_state crate + in + + (* Initialize the extraction context - for now we extract only to F* *) + let names_map = + PureToExtract.initialize_names_map ExtractToFStar.fstar_names_map_init + in + let variant_concatenate_type_name = true in + let fstar_fmt = + ExtractToFStar.mk_formatter trans_ctx crate.name + variant_concatenate_type_name + in + let ctx = + { PureToExtract.trans_ctx; names_map; fmt = fstar_fmt; indent_incr = 2 } + in + + (* We need to compute which functions are recursive, in order to know + * whether we should generate a decrease clause or not. *) + let rec_functions = + A.FunDeclId.Set.of_list + (List.concat + (List.map + (fun decl -> + match decl with Crates.Fun (Rec ids) -> ids | _ -> []) + crate.declarations)) + in + + (* Register unique names for all the top-level types, globals and functions. + * Note that the order in which we generate the names doesn't matter: + * we just need to generate a mapping from identifier to name, and make + * sure there are no name clashes. *) + let ctx = + List.fold_left + (fun ctx def -> ExtractToFStar.extract_type_decl_register_names ctx def) + ctx trans_types + in + + let ctx = + List.fold_left + (fun ctx (keep_fwd, def) -> + (* We generate a decrease clause for all the recursive functions *) + let gen_decr_clause = + A.FunDeclId.Set.mem (fst def).Pure.def_id rec_functions + in + (* Register the names, only if the function is not a global body - + * those are handled later *) + let is_global = (fst def).Pure.is_global_decl_body in + if is_global then ctx + else + ExtractToFStar.extract_fun_decl_register_names ctx keep_fwd + gen_decr_clause def) + ctx trans_funs + in + + let ctx = + List.fold_left ExtractToFStar.extract_global_decl_register_names ctx + crate.globals + in + + (* Open the output file *) + (* First compute the filename by replacing the extension and converting the + * case (rust module names are snake case) *) + let module_name, extract_filebasename = + match Filename.chop_suffix_opt ~suffix:".llbc" filename with + | None -> + (* Note that we already checked the suffix upon opening the file *) + failwith "Unreachable" + | Some filename -> + (* Retrieve the file basename *) + let basename = Filename.basename filename in + (* Convert the case *) + let module_name = StringUtils.to_camel_case basename in + (* Concatenate *) + (module_name, Filename.concat dest_dir module_name) + in + + (* Put the translated definitions in maps *) + let trans_types = + Pure.TypeDeclId.Map.of_list + (List.map (fun (d : Pure.type_decl) -> (d.def_id, d)) trans_types) + in + let trans_funs = + A.FunDeclId.Map.of_list + (List.map + (fun ((keep_fwd, (fd, bdl)) : bool * pure_fun_translation) -> + (fd.def_id, (keep_fwd, (fd, bdl)))) + trans_funs) + in + + (* Create the directory, if necessary *) + if not (Sys.file_exists dest_dir) then ( + log#linfo (lazy ("Creating missing directory: " ^ dest_dir)); + (* Create a directory with *default* permissions *) + Core_unix.mkdir_p dest_dir); + + (* Copy "Primitives.fst" - I couldn't find a "cp" function in the OCaml + * libraries... *) + let _ = + let src = open_in "fstar/Primitives.fst" in + let tgt_filename = Filename.concat dest_dir "Primitives.fst" in + let tgt = open_out tgt_filename in + try + while true do + (* We copy line by line *) + let line = input_line src in + Printf.fprintf tgt "%s\n" line + done + with End_of_file -> + close_in src; + close_out tgt; + log#linfo (lazy ("Copied: " ^ tgt_filename)) + in + + (* Extract the file(s) *) + let gen_ctx = + { + crate; + extract_ctx = ctx; + trans_types; + trans_funs; + functions_with_decreases_clause = rec_functions; + } + in + + let use_state = config.use_state in + + (* Extract one or several files, depending on the configuration *) + if config.split_files then ( + let base_gen_config = + { + mp_config = config.mp_config; + use_state; + extract_types = false; + extract_decreases_clauses = config.extract_decreases_clauses; + extract_template_decreases_clauses = false; + extract_fun_decls = false; + extract_transparent = true; + extract_opaque = false; + extract_state_type = false; + interface = false; + test_unit_functions = false; + } + in + + (* Check if there are opaque types and functions - in which case we need + * to split *) + let has_opaque_types, has_opaque_funs = module_has_opaque_decls gen_ctx in + let has_opaque_types = has_opaque_types || use_state in + + (* Extract the types *) + (* If there are opaque types, we extract in an interface *) + let types_filename_ext = if has_opaque_types then ".fsti" else ".fst" in + let types_filename = extract_filebasename ^ ".Types" ^ types_filename_ext in + let types_module = module_name ^ ".Types" in + let types_config = + { + base_gen_config with + extract_types = true; + extract_opaque = true; + extract_state_type = use_state; + interface = has_opaque_types; + } + in + extract_file types_config gen_ctx types_filename crate.Crates.name + types_module ": type definitions" [] []; + + (* Extract the template clauses *) + let needs_clauses_module = + config.extract_decreases_clauses + && not (A.FunDeclId.Set.is_empty rec_functions) + in + (if needs_clauses_module && config.extract_template_decreases_clauses then + let clauses_filename = extract_filebasename ^ ".Clauses.Template.fst" in + let clauses_module = module_name ^ ".Clauses.Template" in + let clauses_config = + { base_gen_config with extract_template_decreases_clauses = true } + in + extract_file clauses_config gen_ctx clauses_filename crate.Crates.name + clauses_module ": templates for the decreases clauses" [ types_module ] + []); + + (* Extract the opaque functions, if needed *) + let opaque_funs_module = + if has_opaque_funs then ( + let opaque_filename = extract_filebasename ^ ".Opaque.fsti" in + let opaque_module = module_name ^ ".Opaque" in + let opaque_config = + { + base_gen_config with + extract_fun_decls = true; + extract_transparent = false; + extract_opaque = true; + interface = true; + } + in + extract_file opaque_config gen_ctx opaque_filename crate.Crates.name + opaque_module ": opaque function definitions" [] [ types_module ]; + [ opaque_module ]) + else [] + in + + (* Extract the functions *) + let fun_filename = extract_filebasename ^ ".Funs.fst" in + let fun_module = module_name ^ ".Funs" in + let fun_config = + { + base_gen_config with + extract_fun_decls = true; + test_unit_functions = config.test_unit_functions; + } + in + let clauses_module = + if needs_clauses_module then [ module_name ^ ".Clauses" ] else [] + in + extract_file fun_config gen_ctx fun_filename crate.Crates.name fun_module + ": function definitions" [] + ([ types_module ] @ opaque_funs_module @ clauses_module)) + else + let gen_config = + { + mp_config = config.mp_config; + use_state; + extract_types = true; + extract_decreases_clauses = config.extract_decreases_clauses; + extract_template_decreases_clauses = + config.extract_template_decreases_clauses; + extract_fun_decls = true; + extract_transparent = true; + extract_opaque = true; + extract_state_type = use_state; + interface = false; + test_unit_functions = config.test_unit_functions; + } + in + (* Add the extension for F* *) + let extract_filename = extract_filebasename ^ ".fst" in + extract_file gen_config gen_ctx extract_filename crate.Crates.name + module_name "" [] [] |