diff --git a/TODO.org b/TODO.org index 91c9830663698c12c37b736ac48506a702a5523a..87199e28b41bf3c05d18e75d69918b1b59fe835c 100644 --- a/TODO.org +++ b/TODO.org @@ -141,7 +141,9 @@ But also some not-so-nice features (ploc's remarks): ** Development *** Done - (ploc) retirer le parser Kind2 et revenir à celui de lustrec + *** To be done + **** Court terme - (CG) etendre la syntaxe pour coller à la definition donnée ci-dessus - lexeur/parseur lustreSpec + document latex de grammaire diff --git a/src/arrow.ml b/src/arrow.ml index fc38ad5f3e8377c978452c58098fd2cbdcd49f1d..1ce3b268e1843be36510f033dbe97af8de1980e9 100644 --- a/src/arrow.ml +++ b/src/arrow.ml @@ -19,7 +19,9 @@ let arrow_desc = node_dec_stateless = false; node_stateless = Some false; node_spec = None; - node_annot = []; } + node_annot = []; + node_iscontract = false; +} let arrow_top_decl = { diff --git a/src/automata.ml b/src/automata.ml index d543a1e7034856ec4d8400c88571698b87b46ec8..97686e04b92cc519f6de4d800b48eddb2aa81707 100644 --- a/src/automata.ml +++ b/src/automata.ml @@ -162,7 +162,8 @@ let node_of_unless nused used node aut_id aut_state handler = node_dec_stateless = false; node_stateless = None; node_spec = None; - node_annot = [] + node_annot = []; + node_iscontract = false; }, mkexpr handler.hand_loc (Expr_appl (node_id, mkexpr handler.hand_loc (Expr_tuple args), reset)) @@ -213,7 +214,8 @@ let node_of_assign_until nused used node aut_id aut_state handler = node_dec_stateless = false; node_stateless = None; node_spec = None; - node_annot = handler.hand_annots + node_annot = handler.hand_annots; + node_iscontract = false; }, mkexpr handler.hand_loc (Expr_appl (node_id, mkexpr handler.hand_loc (Expr_tuple args), reset)) diff --git a/src/backends/EMF/EMF_backend.ml b/src/backends/EMF/EMF_backend.ml index 4a4913be136b3089baafb99e182058c85d48b294..801815020e6a40575620a8e7ab81f08b07f2f731 100644 --- a/src/backends/EMF/EMF_backend.ml +++ b/src/backends/EMF/EMF_backend.ml @@ -393,9 +393,9 @@ let pp_emf_spec_import fmt i = fprintf fmt "\"contract\": \"%s\",@ " i.import_nodeid; fprintf fmt "\"inputs\": [%a],@ " - pp_emf_exprs i.inputs; + pp_emf_expr i.inputs; fprintf fmt "\"outputs\": [%a],@ " - pp_emf_exprs i.outputs; + pp_emf_expr i.outputs; fprintf fmt "@]}" let pp_emf_spec_imports = pp_emf_list pp_emf_spec_import @@ -421,8 +421,14 @@ let pp_emf_spec fmt spec = let pp_emf_annots cpt fmt annots = fprintf_list ~sep:",@ " (pp_emf_annot cpt) fmt annots.annots let pp_emf_annots_list cpt fmt annots_list = fprintf_list ~sep:",@ " (pp_emf_annots cpt) fmt annots_list +let pp_emf_contract fmt nd = + let c = Printers.node_as_contract nd in + fprintf fmt "@[v 2>\"%a\": {@ " + print_protect (fun fmt -> pp_print_string fmt nd.node_id); + fprintf fmt "\"contract\": %a@ " + pp_emf_spec c; + fprintf fmt "@]@ }" - let pp_machine fmt m = let instrs = (*merge_branches*) m.mstep.step_instrs in try @@ -445,7 +451,10 @@ let pp_machine fmt m = fprintf fmt "\"original_name\": \"%s\",@ " m.mname.node_id; fprintf fmt "\"instrs\": {@[<v 0> %a@]@ },@ " (pp_emf_instrs m) instrs; - (match m.mspec with None -> () | Some spec -> fprintf fmt "\"spec\": %a,@ " pp_emf_spec spec); + (match m.mspec with | None -> () + | Some (Contract _) -> assert false + | Some (NodeSpec id) -> fprintf fmt "\"coco_contract\": %s" id + ); fprintf fmt "\"annots\": {@[<v 0> %a@]@ }" (pp_emf_annots_list (ref 0)) m.mannot; fprintf fmt "@]@ }" with Unhandled msg -> ( @@ -455,9 +464,14 @@ let pp_machine fmt m = eprintf "node skipped - no output generated@ @]@." ) +let pp_machine fmt m = + match m.mspec with + | None | Some (NodeSpec _) -> pp_machine fmt m + | Some (Contract _) -> pp_emf_contract fmt m.mname + let pp_emf_imported_node fmt top = let ind = Corelang.imported_node_of_top top in - try + try fprintf fmt "@[<v 2>\"%a\": {@ " print_protect (fun fmt -> pp_print_string fmt ind.nodei_id); fprintf fmt "\"imported\": \"true\",@ "; @@ -466,7 +480,10 @@ let pp_emf_imported_node fmt top = fprintf fmt "\"outputs\": [%a],@ " pp_emf_vars_decl ind.nodei_outputs; fprintf fmt "\"original_name\": \"%s\",@ " ind.nodei_id; - (match ind.nodei_spec with None -> () | Some spec -> fprintf fmt "\"spec\": %a" pp_emf_spec spec); + (match ind.nodei_spec with None -> () + | Some (Contract _) -> assert false (* should have been processed *) + | Some (NodeSpec id) -> fprintf fmt "\"coco_contract\": %s" id + ); fprintf fmt "@]@ }" with Unhandled msg -> ( eprintf "[Error] @[<v 0>EMF backend@ Issues while translating node %s@ " diff --git a/src/causality.ml b/src/causality.ml index a11db4293d282604336104a52e7f5dc84b6a2095..97f65972663a71a2d73bee67e80d47996c535e79 100644 --- a/src/causality.ml +++ b/src/causality.ml @@ -148,7 +148,15 @@ module ExprDep = struct List.fold_left eq_memory_variables ISet.empty (get_node_eqs nd) let node_input_variables nd = - List.fold_left (fun inputs v -> ISet.add v.var_id inputs) ISet.empty nd.node_inputs + List.fold_left (fun inputs v -> ISet.add v.var_id inputs) ISet.empty + (if nd.node_iscontract then + nd.node_inputs@nd.node_outputs + else + nd.node_inputs) + + let node_output_variables nd = + List.fold_left (fun outputs v -> ISet.add v.var_id outputs) ISet.empty + (if nd.node_iscontract then [] else nd.node_outputs) let node_local_variables nd = List.fold_left (fun locals v -> ISet.add v.var_id locals) ISet.empty nd.node_locals @@ -156,9 +164,6 @@ module ExprDep = struct let node_constant_variables nd = List.fold_left (fun locals v -> if v.var_dec_const then ISet.add v.var_id locals else locals) ISet.empty nd.node_locals - let node_output_variables nd = - List.fold_left (fun outputs v -> ISet.add v.var_id outputs) ISet.empty nd.node_outputs - let node_auxiliary_variables nd = ISet.diff (node_local_variables nd) (node_memory_variables nd) @@ -170,15 +175,17 @@ module ExprDep = struct (* computes the equivalence relation relating variables in the same equation lhs, under the form of a table of class representatives *) - let node_eq_equiv nd = + let eqs_eq_equiv eqs = let eq_equiv = Hashtbl.create 23 in List.iter (fun eq -> let first = List.hd eq.eq_lhs in List.iter (fun v -> Hashtbl.add eq_equiv v first) eq.eq_lhs ) - (get_node_eqs nd); + eqs; eq_equiv - + + let node_eq_equiv nd = eqs_eq_equiv (get_node_eqs nd) + (* Create a tuple of right dimension, according to [expr] type, *) (* filled with variable [v] *) let adjust_tuple v expr = @@ -624,6 +631,9 @@ let add_external_dependency outputs mems g = ISet.iter (fun m -> IdentDepGraph.add_edge g world m) mems; end +(* Takes a node and return a pair (node', graph) where node' is node + rebuilt with the equations enriched with new ones introduced to + "break cycles" *) let global_dependency node = let mems = ExprDep.node_memory_variables node in let inputs = diff --git a/src/checks/stateless.ml b/src/checks/stateless.ml index efceddd46ecdecf1a63738995ff655e55b02c82d..e58bb639d7b5f40fbdaec54b5938ed04cc699348 100644 --- a/src/checks/stateless.ml +++ b/src/checks/stateless.ml @@ -76,17 +76,28 @@ let force_prog decls = List.iter (fun td -> ignore (force_node td)) decls let check_compat_decl decl = - match decl.top_decl_desc with - | ImportedNode nd -> - let td = Corelang.node_from_name nd.nodei_id in - (match td.top_decl_desc with - | Node nd' -> let stateless = check_node td in - if nd.nodei_stateless && (not stateless) - then raise (Error (td.top_decl_loc, Stateful_imp nd.nodei_id)) - else nd'.node_dec_stateless <- nd.nodei_stateless - | _ -> assert false) - | Node _ -> assert false - | _ -> () + match decl.top_decl_desc with + | ImportedNode nd -> (* A node declared in the header (lusi) shall + be locally defined with compatible stateless + flag *) + begin + let td = Corelang.node_from_name nd.nodei_id in + (match td.top_decl_desc with + | Node nd' -> let stateless = check_node td in + if nd.nodei_stateless && (not stateless) + then raise (Error (td.top_decl_loc, Stateful_imp nd.nodei_id)) + else nd'.node_dec_stateless <- nd.nodei_stateless + | _ -> assert false) + end + | Node nd -> ( + match nd.node_spec with + Some (Contract _) -> (* A contract element in a header does not + need to be provided in the associed lus + file *) + () + | _ -> assert false) + + | _ -> () let check_compat header = List.iter check_compat_decl header diff --git a/src/clock_calculus.ml b/src/clock_calculus.ml index cee0793345a7647fe9aa5b54456fa54e2201d6b0..520704efc3f2c26bb8d4c3a40f08deebe96d2359 100644 --- a/src/clock_calculus.ml +++ b/src/clock_calculus.ml @@ -794,10 +794,17 @@ let uneval_prog_generics prog = let check_env_compat header declared computed = uneval_prog_generics header; - Env.iter declared (fun k decl_clock_k -> - let computed_c = instantiate (ref []) (ref []) (Env.lookup_value computed k) in - try_semi_unify decl_clock_k computed_c Location.dummy_loc - ) + Env.iter declared (fun k decl_clock_k -> + try + let computed_c = instantiate (ref []) (ref []) (Env.lookup_value computed k) in + try_semi_unify decl_clock_k computed_c Location.dummy_loc + with Not_found -> (* If the lookup failed then either an actual + required element should have been declared + and is missing but typing should have catch + it, or it was a contract and does not + require this additional check. *) + () + ) (* Local Variables: *) (* compile-command:"make -C .." *) (* End: *) diff --git a/src/compiler_common.ml b/src/compiler_common.ml index 40c2ff2a49f2d474ffb350b8d47582354f800564..2e347c31b9a9f7be06a16a4c6772f63139a93c87 100644 --- a/src/compiler_common.ml +++ b/src/compiler_common.ml @@ -187,7 +187,182 @@ let check_compatibility (prog, computed_types_env, computed_clocks_env) (header, Location.pp_loc loc; raise exc +(* Process each node/imported node and introduce the associated contract node *) +let resolve_contracts prog = + (* Bind a fresh node with a new name according to existing nodes and freshly binded contract node. Clean the contract to remove the stmts *) + let process_contract new_contracts prog c = + (* Resolve first the imports *) + let stmts, locals, c = + List.fold_left ( + fun (stmts, locals, c) import -> + (* Search for contract in prog. + The node have to be processed already. Otherwise raise an error. + Each stmts in injected with a fresh name + Inputs are renamed and associated to the expression in1 + Same thing for outputs. + Last the contracts elements are replaced with the renamed vars and merged with c contract. + *) + let name = import.import_nodeid in + assert false; (* TODO: we do not handle imports yet. The + algorithm is sketched below *) + (* + try + let imp_nd = xxx in (* Get the contract node in prog *) + (* checking that it's actually a (processed) contract *) + let imp_c = + match imp_nd.node_spec with + Some (Contract imp_c) -> + if imp_c.imports = [] then + imp_c + else + assert false (* should be processed *) + | _ -> assert false (* should be a contract *) + in + (* Preparing vars: renaming them *) + let imp_in = rename imp_nd.node_inputs in + let imp_out = rename imp_nd.node_outputs in + let imp_locals = rename imp_nd.node_locals in + let locals = imp_in@imp_out@imp_locals@locals in + (* Assinging in and out *) + let in_assigns = + xxx imp_in = import.inputs + in + let out_assigns = + xxx imp_out = import.outputs + in + let stmts = stmts @ (rename imp_nd.stmts) in + let imp_c = rename imp_c in + let c = merge_contracts c imp_c in + stmts, locals, c + with Not_found -> raise (Error.Unbound_symbol ("contract " ^ name)) + + *) + ) ([], c.consts@c.locals, c) c.imports + in + (* Other contract elements will be normalized later *) + let c = { c with + locals = []; + consts = []; + stmts = []; + imports = [] + } + in + stmts, locals, c + + in + let process_contract_new_node accu_contracts prog top = + let id, spec, inputs, outputs = + match top.top_decl_desc with + | Node nd -> nd.node_id, nd.node_spec, nd.node_inputs, nd.node_outputs + | ImportedNode ind -> ind.nodei_id, ind.nodei_spec, ind.nodei_inputs, ind.nodei_outputs + | _ -> assert false + in + let stmts, locals, c = + match spec with + | None | Some (NodeSpec _) -> assert false + | Some (Contract c) -> + process_contract accu_contracts prog c + in + (* Create a fresh name *) + let used v = List.exists (fun top -> + match top.top_decl_desc with + | Node _ + | ImportedNode _ -> + (node_name top) = v + | _ -> false + ) (accu_contracts@prog) + in + let new_nd_id = mk_new_name used (id ^ "_coco") in + let new_nd = + mktop_decl + c.spec_loc + top.top_decl_owner + top.top_decl_itf + (Node { + node_id = new_nd_id; + node_type = Types.new_var (); + node_clock = Clocks.new_var true; + node_inputs = inputs; + node_outputs = outputs; + node_locals = locals; + node_gencalls = []; + node_checks = []; + node_asserts = []; + node_stmts = stmts; + node_dec_stateless = false; + node_stateless = None; + node_spec = Some (Contract c); + node_annot = []; + node_iscontract = true; + }) in + new_nd + in + (* Processing nodes in order. Should have been sorted by now + + Each top level contract is processed: stmts pushed in node + + Each regular imported node or node associated with a contract is + replaced with a simplidfied contract and its contract is bound to + a fresh node. + + *) + let new_contracts, prog = + List.fold_left + ( + fun (accu_contracts, accu_nodes) top -> + match top.top_decl_desc with + + | Node nd when nd.node_iscontract -> ( + match nd.node_spec with + | None | Some (NodeSpec _) -> assert false + | Some (Contract c) -> + let stmts, locals, c = process_contract accu_contracts prog c in + let nd = + { nd with + node_locals = nd.node_locals @ locals; + node_stmts = nd.node_stmts @ stmts; + node_spec = Some (Contract c); + } + in + { top with top_decl_desc = Node nd }::accu_contracts, + accu_nodes + + ) + | Node nd -> ( + match nd.node_spec with + | None -> accu_contracts, top::accu_nodes (* A boring node: no contract *) + | Some (NodeSpec id) -> (* shall not happen, its too early *) + assert false + | Some (Contract c) -> (* A contract: processing it *) + (* we bind a fresh node *) + let new_nd = process_contract_new_node accu_contracts prog top in + Format.eprintf "Creating new contract node %s@." (node_name new_nd); + let nd = { nd with node_spec = (Some (NodeSpec (node_name new_nd))) } in + new_nd::accu_contracts, + { top with top_decl_desc = Node nd }::accu_nodes + + ) + + | ImportedNode ind -> ( (* Similar treatment for imported nodes *) + match ind.nodei_spec with + None -> accu_contracts, top::accu_nodes (* A boring node: no contract *) + | Some (NodeSpec id) -> (* shall not happen, its too early *) + assert false + | Some (Contract c) -> (* A contract: processing it *) + (* we bind a fresh node *) + let new_nd = process_contract_new_node accu_contracts prog top in + let ind = { ind with nodei_spec = (Some (NodeSpec (node_name new_nd))) } in + new_nd::accu_contracts, + { top with top_decl_desc = ImportedNode ind }::accu_nodes + ) + | _ -> accu_contracts, top::accu_nodes + ) ([],[]) prog + in + (List.rev new_contracts) @ (List.rev prog) + + + let track_exception () = if !Options.track_exceptions then (Printexc.print_backtrace stdout; flush stdout) diff --git a/src/compiler_stages.ml b/src/compiler_stages.ml index a6ac627223cb5be28bf23d10457f7afd6c74fbb4..bc7923a4561d74e4679d3d6a4b6fda0620482484 100644 --- a/src/compiler_stages.ml +++ b/src/compiler_stages.ml @@ -78,6 +78,9 @@ let stage1 params prog dirname basename extension = (* Sorting nodes *) let prog = SortProg.sort prog in + (* Consolidating contracts *) + let prog = resolve_contracts prog in + (* Perform inlining before any analysis *) let orig, prog = if !Options.global_inline && !Options.main_node <> "" then diff --git a/src/corelang.ml b/src/corelang.ml index cd57e20c021f8f9fb07e93d1bc3166e03b412ec5..561c2e9e525998d84cd50656ca8ed7f7b3adb33d 100644 --- a/src/corelang.ml +++ b/src/corelang.ml @@ -324,6 +324,15 @@ let is_imported_node td = | ImportedNode nd -> true | _ -> assert false +let is_contract td = + match td.top_decl_desc with + | Node nd -> ( + match nd.node_spec with + | Some (Contract _) -> true + | _ -> false + ) + | _ -> false + (* alias and type definition table *) @@ -855,6 +864,7 @@ and rename_eexpr f_node f_var ee = node_stateless = nd.node_stateless; node_spec = spec; node_annot = annot; + node_iscontract = nd.node_iscontract; } @@ -1255,6 +1265,9 @@ let find_eq xl eqs = in aux [] eqs + + + (* Local Variables: *) (* compile-command:"make -C .." *) (* End: *) diff --git a/src/corelang.mli b/src/corelang.mli index 77ded12a878e37c449d56b1c8a9895433bf06b71..fc099b5006b694a5cbf60cecd76c28464205a394 100644 --- a/src/corelang.mli +++ b/src/corelang.mli @@ -58,7 +58,8 @@ val node_from_name: ident -> top_decl val update_node: ident -> top_decl -> unit val is_generic_node: top_decl -> bool val is_imported_node: top_decl -> bool - +val is_contract: top_decl -> bool + val consts_table: (ident, top_decl) Hashtbl.t val print_consts_table: Format.formatter -> unit -> unit val type_table: (type_dec_desc, top_decl) Hashtbl.t @@ -167,7 +168,7 @@ val mk_contract_var: ident -> bool -> type_dec option -> expr -> Location.t -> c val mk_contract_guarantees: eexpr -> contract_desc val mk_contract_assume: eexpr -> contract_desc val mk_contract_mode: ident -> eexpr list -> eexpr list -> Location.t -> contract_desc -val mk_contract_import: ident -> expr list -> expr list -> Location.t -> contract_desc +val mk_contract_import: ident -> expr -> expr -> Location.t -> contract_desc val merge_contracts: contract_desc -> contract_desc -> contract_desc val extend_eexpr: (quantifier_type * var_decl list) list -> eexpr -> eexpr val update_expr_annot: ident -> expr -> expr_annot -> expr diff --git a/src/error.ml b/src/error.ml index 1ec7b9e6a89250a65d0904262da028b93afee28b..507b9948490a7a5316a886d27dd3b2871b2eec07 100644 --- a/src/error.ml +++ b/src/error.ml @@ -24,6 +24,7 @@ let return_code kind = | AlgebraicLoop -> 9 | LoadError _ -> 10 + let pp_error_msg fmt = function | Main_not_found -> fprintf fmt "Could not find the definition of main node %s.@." diff --git a/src/inliner.ml b/src/inliner.ml index 06923735bbf0820193bf39e68e3653f8d719f4de..8e4f2988b854c281638c9df8c8a7bf9caf46a225 100644 --- a/src/inliner.ml +++ b/src/inliner.ml @@ -421,9 +421,14 @@ let witness filename main_name orig inlined type_env clock_env = node_dec_stateless = false; node_stateless = None; node_spec = Some - (mk_contract_guarantees (mkeexpr loc (mkexpr loc (Expr_ident ok_ident)))); - node_annot = []; - } + (Contract + (mk_contract_guarantees + (mkeexpr loc (mkexpr loc (Expr_ident ok_ident))) + ) + ); + node_annot = []; + node_iscontract = true; + } in let main = [{ top_decl_desc = Node main_node; top_decl_loc = loc; top_decl_owner = filename; top_decl_itf = false }] in let new_prog = others@nodes_origs@nodes_inlined@main in diff --git a/src/lustre_types.ml b/src/lustre_types.ml index 829a93445698b9ffc355fcac0d42b309a97056b2..b19566444216b1f62d852a3dd13bfdaabb58493e 100644 --- a/src/lustre_types.ml +++ b/src/lustre_types.ml @@ -129,7 +129,10 @@ type contract_mode = { mode_id: ident; require: eexpr list; ensure: eexpr list; mode_loc: Location.t} type contract_import = - { import_nodeid: ident; inputs: expr list; outputs: expr list; import_loc: Location.t } + { import_nodeid: ident; + inputs: expr; + outputs: expr; + import_loc: Location.t } @@ -174,6 +177,9 @@ type contract_desc = spec_loc: Location.t; } +type node_spec_t = Contract of contract_desc + | NodeSpec of ident + type node_desc = {node_id: ident; mutable node_type: Types.type_expr; @@ -187,8 +193,9 @@ type node_desc = node_stmts: statement list; mutable node_dec_stateless: bool; mutable node_stateless: bool option; - node_spec: contract_desc option; + node_spec: node_spec_t option; node_annot: expr_annot list; + node_iscontract: bool; } type imported_node_desc = @@ -198,7 +205,7 @@ type imported_node_desc = nodei_inputs: var_decl list; nodei_outputs: var_decl list; nodei_stateless: bool; - nodei_spec: contract_desc option; + nodei_spec: node_spec_t option; (* nodei_annot: expr_annot list; *) nodei_prototype: string option; nodei_in_lib: string list; @@ -213,14 +220,14 @@ type const_desc = type top_decl_desc = -| Node of node_desc -| Const of const_desc -| ImportedNode of imported_node_desc -| Open of bool * string (* the boolean set to true denotes a local + | Node of node_desc + | Const of const_desc + | ImportedNode of imported_node_desc + | Open of bool * string (* the boolean set to true denotes a local lusi vs a lusi installed at system level *) -| Include of string (* the boolean set to true denotes a local + | Include of string (* the boolean set to true denotes a local lus vs a lus installed at system level *) -| TypeDef of typedef_desc + | TypeDef of typedef_desc type top_decl = {top_decl_desc: top_decl_desc; (* description of the symbol *) @@ -237,6 +244,10 @@ type dep_t = { is_stateful: bool } +type spec_types = + | LocalContract of contract_desc + | TopContract of top_decl list + (* Local Variables: *) diff --git a/src/machine_code.ml b/src/machine_code.ml index 5b995ae3ea75e719a919a223c98e7e017dd32da9..d789639c631597b611803f202575797a59970d26 100644 --- a/src/machine_code.ml +++ b/src/machine_code.ml @@ -375,6 +375,7 @@ let translate_core sorted_eqs locals other_vars = ctx, ctx0.s + let translate_decl nd sch = (* Extracting eqs, variables .. *) let eqs, auts = get_node_eqs nd in @@ -395,36 +396,9 @@ let translate_decl nd sch = let sorted_eqs = Scheduling.sort_equations_from_schedule eqs schedule in let ctx, ctx0_s = translate_core (assert_instrs@sorted_eqs) locals inout_vars in -(* CODE QUI MARCHE - let constant_eqs = constant_equations locals in - (* Compute constants' instructions *) - let ctx0 = translate_eqs env ctx_init constant_eqs in - assert (VSet.is_empty ctx0.m); - assert (ctx0.si = []); - assert (Utils.IMap.is_empty ctx0.j); - - (* Compute ctx for all eqs *) - let ctx = translate_eqs env ctx_init (assert_instrs@sorted_eqs) in - *) - (* - (* Processing spec: locals would be actual locals of the spec while - non locals would be inputs/ouptpus of the node. Locals of the - node are not visible. *) - let spec = - match nd.node_spec with - | None -> None - | Some spec -> translate_spec inout_vars spec - in - - - inout_vars spec = - let locals = VSet.of_list spec.locals in - let spec_consts = VSet.of_list spec.consts in - let other_spec_vars = VSet.union inout_vars spec_consts in - let spec_env = build_env spec_locals other_spec_vars in - *) + let mmap = Utils.IMap.elements ctx.j in { mname = nd; @@ -454,6 +428,12 @@ let translate_decl nd sch = ); step_asserts = List.map (translate_expr env) nd_node_asserts; }; + + (* Processing spec: there is no processing performed here. Contract + have been processed already. Either one of the other machine is a + cocospec node, or the current one is a cocospec node. Contract do + not contain any statement or import. *) + mspec = nd.node_spec; mannot = nd.node_annot; msch = Some sch; @@ -462,13 +442,15 @@ let translate_decl nd sch = (** takes the global declarations and the scheduling associated to each node *) let translate_prog decls node_schs = let nodes = get_nodes decls in - List.map + let machines = + List.map (fun decl -> let node = node_of_top decl in let sch = Utils.IMap.find node.node_id node_schs in translate_decl node sch ) nodes - + in + machines (* Local Variables: *) (* compile-command:"make -C .." *) diff --git a/src/machine_code.mli b/src/machine_code.mli index aaeab7c2e9f2ba0bf93f6c4463764c8130859306..f646b982850e10b60a1caa0b720b4ed7f6e9ac35 100644 --- a/src/machine_code.mli +++ b/src/machine_code.mli @@ -1 +1,4 @@ -val translate_prog: Lustre_types.program_t -> Scheduling_type.schedule_report Utils.IMap.t -> Machine_code_types.machine_t list +val translate_prog: + Lustre_types.program_t -> + Scheduling_type.schedule_report Utils.IMap.t -> + Machine_code_types.machine_t list diff --git a/src/machine_code_common.ml b/src/machine_code_common.ml index f784d10fedac9a85d844a104bac44921b33bc7f0..8612bc62a21b2852a8846099090a2e9573f98f88 100644 --- a/src/machine_code_common.ml +++ b/src/machine_code_common.ml @@ -105,7 +105,9 @@ let pp_machine fmt m = (Utils.fprintf_list ~sep:"@ " (pp_instr m)) m.minit (Utils.fprintf_list ~sep:"@ " (pp_instr m)) m.mconst (pp_step m) m.mstep - (fun fmt -> match m.mspec with | None -> () | Some spec -> Printers.pp_spec fmt spec) + (fun fmt -> match m.mspec with | None -> () + | Some (NodeSpec id) -> Format.fprintf fmt "cocospec: %s" id + | Some (Contract spec) -> Printers.pp_spec fmt spec) (Utils.fprintf_list ~sep:"@ " Printers.pp_expr_annot) m.mannot let pp_machines fmt ml = @@ -190,7 +192,9 @@ let empty_desc = node_dec_stateless = true; node_stateless = Some true; node_spec = None; - node_annot = []; } + node_annot = []; + node_iscontract = false; +} let empty_machine = { diff --git a/src/machine_code_types.ml b/src/machine_code_types.ml index 4d962b66e284c8c3419905744a3eb610c59abbe4..1283f39658520789b777698ce5bf36a2ce26ed45 100644 --- a/src/machine_code_types.ml +++ b/src/machine_code_types.ml @@ -30,18 +30,18 @@ and instr_t_desc = | MBranch of value_t * (label * instr_t list) list | MComment of string - type step_t = { - step_checks: (Location.t * value_t) list; - step_inputs: var_decl list; - step_outputs: var_decl list; - step_locals: var_decl list; - step_instrs: instr_t list; - step_asserts: value_t list; -} +type step_t = { + step_checks: (Location.t * value_t) list; + step_inputs: var_decl list; + step_outputs: var_decl list; + step_locals: var_decl list; + step_instrs: instr_t list; + step_asserts: value_t list; + } type static_call = top_decl * (Dimension.dim_expr list) - + type machine_t = { mname: node_desc; mmemory: var_decl list; @@ -51,7 +51,7 @@ type machine_t = { mstatic: var_decl list; (* static inputs only *) mconst: instr_t list; (* assignments of node constant locals *) mstep: step_t; - mspec: contract_desc option; + mspec: node_spec_t option; mannot: expr_annot list; msch: Scheduling_type.schedule_report option; (* Equations scheduling *) } diff --git a/src/modules.ml b/src/modules.ml index 7cf8ce46c08434fcf8084a2c1569360c517a4a87..548b0e0591de54cba427f0de5eed5a10a8e1ca7b 100644 --- a/src/modules.ml +++ b/src/modules.ml @@ -49,7 +49,7 @@ let add_node name value = let itf = value.top_decl_itf in match value'.top_decl_desc, value.top_decl_desc with | ImportedNode _, Node _ when owner = owner' && itf' && (not itf) -> () - | Node _ , Node _ -> raise (Error (value.top_decl_loc, Error.Already_bound_symbol ("node " ^ name))) + | Node _ , Node _ -> assert false; raise (Error (value.top_decl_loc, Error.Already_bound_symbol ("node " ^ name))) | _ -> assert false with Not_found -> update_node name value diff --git a/src/normalization.ml b/src/normalization.ml index ca5cfd76c3e2905c8aeddc2d86e7fcf8a76a9d50..710262156736193e63ef0cd232f0de9afaf597a5 100644 --- a/src/normalization.ml +++ b/src/normalization.ml @@ -554,7 +554,7 @@ let normalize_pred_eexpr decls norm_ctx (def,vars) ee = (* We use node local vars to make sure we are creating fresh variables *) let normalize_spec decls parentid (in_vars, out_vars, l_vars) s = - (* Original set of variables actually visible from here: iun/out and + (* Original set of variables actually visible from here: in/out and spec locals (no node locals) *) let orig_vars = in_vars @ out_vars @ s.locals in let not_is_orig_var v = @@ -593,11 +593,10 @@ let normalize_spec decls parentid (in_vars, out_vars, l_vars) s = in let new_locals = List.filter not_is_orig_var vars in (* removing inouts and initial locals ones *) - - + new_locals, defs, {s with locals = s.locals @ new_locals; - stmts = List.map (fun eq -> Eq eq) defs; + stmts = []; assume = assume'; guarantees = guarantees'; modes = modes' @@ -644,15 +643,35 @@ let normalize_node decls node = is_output = (fun vid -> List.exists (fun v -> v.var_id = vid) node.node_outputs); } in - + + let eqs, auts = get_node_eqs node in + if auts != [] then assert false; (* Automata should be expanded by now. *) + let spec, new_vars, eqs = + begin + (* Update mutable fields of eexpr to perform normalization of + specification. + + Careful: we do not normalize annotations, since they can have the form + x = (a, b, c) *) + match node.node_spec with + | None + | Some (NodeSpec _) -> node.node_spec, [], eqs + | Some (Contract s) -> + let new_locals, new_stmts, s' = normalize_spec + decls + node.node_id + (node.node_inputs, node.node_outputs, node.node_locals) + s + in + Some (Contract s'), new_locals, new_stmts@eqs + end + in let defs, vars = - let eqs, auts = get_node_eqs node in - if auts != [] then assert false; (* Automata should be expanded by now. *) - List.fold_left (normalize_eq norm_ctx) ([], orig_vars) eqs in + List.fold_left (normalize_eq norm_ctx) ([], new_vars@orig_vars) eqs in (* Normalize the asserts *) let vars, assert_defs, asserts = List.fold_left ( - fun (vars, def_accu, assert_accu) assert_ -> + fun (vars, def_accu, assert_accu) assert_ -> let assert_expr = assert_.assert_expr in let (defs, vars'), expr = normalize_expr @@ -662,9 +681,9 @@ let normalize_node decls node = ([], vars) (* defvar only contains vars *) assert_expr in - (*Format.eprintf "New assert vars: %a@.@?" (fprintf_list ~sep:", " Printers.pp_var) vars';*) + (*Format.eprintf "New assert vars: %a@.@?" (fprintf_list ~sep:", " Printers.pp_var) vars';*) vars', defs@def_accu, {assert_ with assert_expr = expr}::assert_accu - ) (vars, [], []) node.node_asserts in + ) (vars, [], []) node.node_asserts in let new_locals = List.filter not_is_orig_var vars in (* we filter out inout vars and initial locals ones *) @@ -679,29 +698,29 @@ let normalize_node decls node = (* Compute traceability info: - gather newly bound variables - compute the associated expression without aliases - *) + *) let new_annots = if !Options.traces then begin let diff_vars = List.filter (fun v -> not (List.mem v node.node_locals) ) all_locals in let norm_traceability = { - annots = List.map (fun v -> - let eq = - try - List.find (fun eq -> List.exists (fun v' -> v' = v.var_id ) eq.eq_lhs) (defs@assert_defs) - with Not_found -> - ( - Format.eprintf "Traceability annotation generation: var %s not found@." v.var_id; - assert false - ) - in - let expr = substitute_expr diff_vars (defs@assert_defs) eq.eq_rhs in - let pair = mkeexpr expr.expr_loc (mkexpr expr.expr_loc (Expr_tuple [expr_of_ident v.var_id expr.expr_loc; expr])) in - Annotations.add_expr_ann node.node_id pair.eexpr_tag ["traceability"]; - (["traceability"], pair) - ) diff_vars; - annot_loc = Location.dummy_loc - } + annots = List.map (fun v -> + let eq = + try + List.find (fun eq -> List.exists (fun v' -> v' = v.var_id ) eq.eq_lhs) (defs@assert_defs) + with Not_found -> + ( + Format.eprintf "Traceability annotation generation: var %s not found@." v.var_id; + assert false + ) + in + let expr = substitute_expr diff_vars (defs@assert_defs) eq.eq_rhs in + let pair = mkeexpr expr.expr_loc (mkexpr expr.expr_loc (Expr_tuple [expr_of_ident v.var_id expr.expr_loc; expr])) in + Annotations.add_expr_ann node.node_id pair.eexpr_tag ["traceability"]; + (["traceability"], pair) + ) diff_vars; + annot_loc = Location.dummy_loc + } in norm_traceability::node.node_annot end @@ -711,33 +730,23 @@ let normalize_node decls node = let new_annots = List.fold_left (fun annots v -> - if Machine_types.is_active && Machine_types.is_exportable v then - let typ = Machine_types.get_specified_type v in - let typ_name = Machine_types.type_name typ in - - let loc = v.var_loc in - let typ_as_string = - mkexpr - loc - (Expr_const - (Const_string typ_name)) - in - let pair = expr_to_eexpr (expr_of_expr_list loc [expr_of_vdecl v; typ_as_string]) in - Annotations.add_expr_ann node.node_id pair.eexpr_tag Machine_types.keyword; - {annots = [Machine_types.keyword, pair]; annot_loc = loc}::annots - else - annots - ) new_annots new_locals - in - let spec = - begin - (* Update mutable fields of eexpr to perform normalization of - specification. - - Careful: we do not normalize annotations, since they can have the form - x = (a, b, c) *) - match node.node_spec with None -> None | Some s -> Some (normalize_spec decls node.node_id (node.node_inputs, node.node_outputs, node.node_locals) s) - end + if Machine_types.is_active && Machine_types.is_exportable v then + let typ = Machine_types.get_specified_type v in + let typ_name = Machine_types.type_name typ in + + let loc = v.var_loc in + let typ_as_string = + mkexpr + loc + (Expr_const + (Const_string typ_name)) + in + let pair = expr_to_eexpr (expr_of_expr_list loc [expr_of_vdecl v; typ_as_string]) in + Annotations.add_expr_ann node.node_id pair.eexpr_tag Machine_types.keyword; + {annots = [Machine_types.keyword, pair]; annot_loc = loc}::annots + else + annots + ) new_annots new_locals in @@ -750,17 +759,15 @@ let normalize_node decls node = node_spec = spec; } in ((*Printers.pp_node Format.err_formatter node;*) - node - ) + node + ) let normalize_inode decls nd = reset_cpt_fresh (); match nd.nodei_spec with - None -> nd - | Some s -> - let s = normalize_spec decls nd.nodei_id (nd.nodei_inputs, nd.nodei_outputs, []) s in - { nd with nodei_spec = Some s } - + None | Some (NodeSpec _) -> nd + | Some (Contract _) -> assert false + let normalize_decl (decls: program_t) (decl: top_decl) : top_decl = match decl.top_decl_desc with | Node nd -> diff --git a/src/parsers/lexerLustreSpec.mll b/src/parsers/lexerLustreSpec.mll index dffeedbe38c01e57fa7fa25038ca0dfbf35e4a9a..e4ef79b06ba1aa6090fbb51ff564a8bffb627b02 100644 --- a/src/parsers/lexerLustreSpec.mll +++ b/src/parsers/lexerLustreSpec.mll @@ -40,6 +40,7 @@ let keyword_table = "tel", TEL; "returns", RETURNS; "var", VAR; + "import", IMPORT; "imported", IMPORTED; "int", TINT; "bool", TBOOL; diff --git a/src/parsers/lexer_lustre.mll b/src/parsers/lexer_lustre.mll index f9727e9c434ed5810acd49e4b93619f697359f71..5d81cff71ad64a8df17927305fb07ae66991e046 100644 --- a/src/parsers/lexer_lustre.mll +++ b/src/parsers/lexer_lustre.mll @@ -41,6 +41,7 @@ let keyword_table = "returns", RETURNS; "var", VAR; "imported", IMPORTED; + "import", IMPORT; "type", TYPE; "int", TINT; "bool", TBOOL; diff --git a/src/parsers/parserLustreSpec.mly b/src/parsers/parserLustreSpec.mly index 8fb24bfb0c022494d427b9537f76640c970fc6f6..2be9d71f58706811c17d823636bba9f76fac0446 100644 --- a/src/parsers/parserLustreSpec.mly +++ b/src/parsers/parserLustreSpec.mly @@ -95,7 +95,7 @@ contract: { Guarantees($2)::$4 } | MODE ident LPAR mode_content RPAR SCOL contract { Mode($2,$4)::$7 } -| IMPORT ident LPAR tuple_expr RPAR returns LPAR tuple_expr RPAR SCOL contract +| IMPORT ident LPAR expr RPAR returns LPAR expr RPAR SCOL contract { Import($2, $4, $8)::$11 } diff --git a/src/parsers/parser_lustre.mly b/src/parsers/parser_lustre.mly index 3befe345abf9280c2858c532e63bdbda5303d7de..31323d7b89c3dc3538f792a5df8bbb35c7740a33 100644 --- a/src/parsers/parser_lustre.mly +++ b/src/parsers/parser_lustre.mly @@ -49,7 +49,8 @@ let rec fby expr n init = mkexpr (Expr_arrow (init, mkexpr (Expr_pre expr))) else mkexpr (Expr_arrow (init, mkexpr (Expr_pre (fby expr (n-1) init)))) - + + %} %token <int> INT @@ -62,7 +63,7 @@ let rec fby expr n init = %token <string> UIDENT %token TRUE FALSE %token <Lustre_types.expr_annot> ANNOT -%token <Lustre_types.contract_desc> NODESPEC +%token <Lustre_types.spec_types> NODESPEC %token LBRACKET RBRACKET LCUR RCUR LPAR RPAR SCOL COL COMMA COLCOL %token AMPERAMPER BARBAR NOT POWER %token IF THEN ELSE @@ -113,7 +114,7 @@ let rec fby expr n init = %type <Lustre_types.expr_annot> lustre_annot %start lustre_spec -%type <Lustre_types.contract_desc> lustre_spec +%type <Lustre_types.spec_types> lustre_spec %start signed_const %type <Lustre_types.constant> signed_const @@ -203,32 +204,25 @@ state_annot: top_decl_header: | CONST cdecl_list { List.rev ($2 true) } -| nodespec_list state_annot node_ident LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR prototype_opt in_lib_list SCOL - {let nd = mktop_decl true (ImportedNode +| nodespecs state_annot node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR prototype_opt in_lib_list SCOL + { + let inputs = List.rev $5 in + let outputs = List.rev $10 in + let nd = mktop_decl true (ImportedNode {nodei_id = $3; nodei_type = Types.new_var (); nodei_clock = Clocks.new_var true; - nodei_inputs = List.rev $5; - nodei_outputs = List.rev $10; + nodei_inputs = inputs; + nodei_outputs = outputs; nodei_stateless = $2; nodei_spec = $1; nodei_prototype = $13; nodei_in_lib = $14;}) in - (*add_imported_node $3 nd;*) [nd] } -| CONTRACT node_ident LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt LET contract TEL - {let nd = mktop_decl true (ImportedNode - {nodei_id = $2; - nodei_type = Types.new_var (); - nodei_clock = Clocks.new_var true; - nodei_inputs = List.rev $4; - nodei_outputs = List.rev $9; - nodei_stateless = false (* By default we assume contracts as stateful *); - nodei_spec = Some $14; - nodei_prototype = None; - nodei_in_lib = [];}) - in - (*add_imported_node $3 nd;*) [nd] } + pop_node (); + [nd] } +| top_contract { [$1] } + prototype_opt: { None } @@ -240,7 +234,7 @@ in_lib_list: top_decl: | CONST cdecl_list { List.rev ($2 false) } -| state_annot node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt nodespec_list locals LET stmt_list TEL +| state_annot node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt nodespecs locals LET stmt_list TEL { let stmts, asserts, annots = $16 in (* Declaring eqs annots *) @@ -249,13 +243,15 @@ top_decl: Annotations.add_node_ann $2 key ) ann.annots ) annots; - (* Building the node *) + (* Building the node *) + let inputs = List.rev $4 in + let outputs = List.rev $9 in let nd = mktop_decl false (Node {node_id = $2; node_type = Types.new_var (); node_clock = Clocks.new_var true; - node_inputs = List.rev $4; - node_outputs = List.rev $9; + node_inputs = inputs; + node_outputs = outputs; node_locals = List.rev $14; node_gencalls = []; node_checks = []; @@ -264,46 +260,36 @@ top_decl: node_dec_stateless = $1; node_stateless = None; node_spec = $13; - node_annot = annots}) + node_annot = annots; + node_iscontract = false; + }) in pop_node (); (*add_node $3 nd;*) [nd] } -| state_annot IMPORTED node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt LET contract TEL - {let nd = mktop_decl true (ImportedNode - {nodei_id = $3; - nodei_type = Types.new_var (); - nodei_clock = Clocks.new_var true; - nodei_inputs = List.rev $5; - nodei_outputs = List.rev $10; - nodei_stateless = $1; - nodei_spec = Some $15; - nodei_prototype = None; - nodei_in_lib = [];}) - in - pop_node (); - (*add_imported_node $3 nd;*) [nd] } -| state_annot IMPORTED node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL - {let nd = mktop_decl true (ImportedNode - {nodei_id = $3; - nodei_type = Types.new_var (); - nodei_clock = Clocks.new_var true; - nodei_inputs = List.rev $5; - nodei_outputs = List.rev $10; - nodei_stateless = $1; - nodei_spec = None; - nodei_prototype = None; - nodei_in_lib = [];}) - in - pop_node (); - (*add_imported_node $3 nd;*) [nd] } + +| NODESPEC + { match $1 with + | LocalContract c -> assert false + | TopContract c -> c + + } + +nodespecs: +nodespec_list { + match $1 with + | None -> None + | Some c -> Some (Contract c) +} nodespec_list: { None } -| NODESPEC nodespec_list { - (function - | None -> (fun s1 -> Some s1) - | Some s2 -> (fun s1 -> Some (merge_contracts s1 s2))) $2 $1 } + | NODESPEC nodespec_list { + let extract x = match x with LocalContract c -> c | _ -> assert false in + let s1 = extract $1 in + match $2 with + | None -> Some s1 + | Some s2 -> Some (merge_contracts s1 s2) } typ_def_list: /* empty */ { (fun itf -> []) } @@ -382,26 +368,58 @@ eq: | LPAR ident_list RPAR EQ expr SCOL {mkeq (List.rev (List.map fst $2), $5)} lustre_spec: -| contract EOF { $1 } +| top_contracts EOF { TopContract $1 } +| contract_content EOF { LocalContract $1} + +top_contracts: +| top_contract { [$1] } +| top_contract top_contracts { $1::$2 } + +top_contract: +| CONTRACT node_ident_decl LPAR vdecl_list SCOL_opt RPAR RETURNS LPAR vdecl_list SCOL_opt RPAR SCOL_opt LET contract_content TEL + { + let nd = mktop_decl true (Node + {node_id = $2; + node_type = Types.new_var (); + node_clock = Clocks.new_var true; + node_inputs = List.rev $4; + node_outputs = List.rev $9; + node_locals = []; (* will be filled later *) + node_gencalls = []; + node_checks = []; + node_asserts = []; + node_stmts = []; (* will be filled later *) + node_dec_stateless = false; + (* By default we assume contracts as stateful *) + node_stateless = None; + node_spec = Some (Contract $14); + node_annot = []; + node_iscontract = true; + } + ) + in + pop_node (); + (*add_imported_node $3 nd;*) + nd } -contract: +contract_content: { empty_contract } -| CONTRACT contract { $2 } -| CONST IDENT EQ expr SCOL contract +| CONTRACT contract_content { $2 } +| CONST IDENT EQ expr SCOL contract_content { merge_contracts (mk_contract_var $2 true None $4 (get_loc())) $6 } -| CONST IDENT COL typeconst EQ expr SCOL contract +| CONST IDENT COL typeconst EQ expr SCOL contract_content { merge_contracts (mk_contract_var $2 true (Some(mktyp $4)) $6 (get_loc())) $8 } -| VAR IDENT COL typeconst EQ expr SCOL contract +| VAR IDENT COL typeconst EQ expr SCOL contract_content { merge_contracts (mk_contract_var $2 false (Some(mktyp $4)) $6 (get_loc())) $8 } -| ASSUME qexpr SCOL contract +| ASSUME qexpr SCOL contract_content { merge_contracts (mk_contract_assume $2) $4 } -| GUARANTEES qexpr SCOL contract +| GUARANTEES qexpr SCOL contract_content { merge_contracts (mk_contract_guarantees $2) $4 } -| MODE IDENT LPAR mode_content RPAR SCOL contract +| MODE IDENT LPAR mode_content RPAR SCOL contract_content { merge_contracts ( let r, e = $4 in mk_contract_mode $2 r e (get_loc())) $7 } -| IMPORT IDENT LPAR tuple_expr RPAR RETURNS LPAR tuple_expr RPAR SCOL contract +| IMPORT IDENT LPAR expr RPAR RETURNS LPAR expr RPAR SCOL contract_content { merge_contracts (mk_contract_import $2 $4 $8 (get_loc())) $11 } mode_content: diff --git a/src/printers.ml b/src/printers.ml index a0f7881ebae7b5b2f3e03081d658bf7c00b07c77..51ceca36373f98add0db8ed188de2976a54f96a7 100644 --- a/src/printers.ml +++ b/src/printers.ml @@ -323,15 +323,51 @@ let pp_spec fmt spec = fprintf_list ~eol:"@, " ~sep:"@, " (fun fmt import -> fprintf fmt "import %s (%a) returns (%a);" import.import_nodeid - (fprintf_list ~sep:"@ " pp_expr) import.inputs - (fprintf_list ~sep:"@ " pp_expr) import.outputs + pp_expr import.inputs + pp_expr import.outputs ) fmt spec.imports -let pp_spec_as_comment fmt spec = - fprintf fmt "@[<hov 2>(*@@ "; - pp_spec fmt spec; - fprintf fmt "@]*)@ " + + let node_as_contract nd = + match nd.node_spec with + | None | Some (NodeSpec _) -> raise (Invalid_argument "Not a contract") + | Some (Contract c) -> ( + assert (c.locals = []); + assert (c.stmts = []); + { c with + locals = nd.node_locals; + stmts = nd.node_stmts; + } + ) + +let pp_contract fmt nd = + let c = node_as_contract nd in + let pp_l = fprintf_list ~sep:"," pp_var_name in + fprintf fmt "@[<hov 2>(*@@ contract %s(%a) returns (%a);@ " + nd.node_id + pp_l nd.node_inputs + pp_l nd.node_outputs; + fprintf fmt "let@ "; + pp_spec fmt c; + fprintf fmt "tel@ @]*)@ " +let pp_spec_as_comment fmt (inl, outl, spec) = + match spec with + | Contract c -> (* should have been processed by now *) + fprintf fmt "@[<hov 2>(*@@ "; + pp_spec fmt c; + fprintf fmt "@]*)@ " + + | NodeSpec name -> (* Pushing stmts in contract. We update the + original information with the computed one in + nd. *) + let pp_l = fprintf_list ~sep:"," pp_var_name in + fprintf fmt "@[<hov 2>(*@@ contract import %s(%a) returns (%a)@]*)@ " + name + pp_l inl + pp_l outl + + let pp_node fmt nd = fprintf fmt "@[<v 0>"; (* Prototype *) @@ -342,7 +378,7 @@ let pp_node fmt nd = pp_node_args nd.node_outputs; (* Contracts *) fprintf fmt "%a%t" - (fun fmt s -> match s with Some s -> pp_spec_as_comment fmt s | _ -> ()) nd.node_spec + (fun fmt s -> match s with Some s -> pp_spec_as_comment fmt (nd.node_inputs, nd.node_outputs, s) | _ -> ()) nd.node_spec (fun fmt -> match nd.node_spec with None -> () | Some _ -> fprintf fmt "@ "); (* Locals *) fprintf fmt "%a" (fun fmt locals -> @@ -375,6 +411,15 @@ let pp_node fmt nd = (*fprintf fmt "@ /* Scheduling: %a */ @ " (fprintf_list ~sep:", " pp_print_string) (Scheduling.schedule_node nd)*) +let pp_node fmt nd = + match nd.node_spec, nd.node_iscontract with + | None, false + | Some (NodeSpec _), false + -> pp_node fmt nd + | Some (Contract _), false -> pp_node fmt nd (* may happen early in the compil process *) + | Some (Contract _), true -> pp_contract fmt nd + | _ -> assert false + let pp_imported_node fmt ind = fprintf fmt "@[<v 0>"; (* Prototype *) @@ -385,7 +430,7 @@ let pp_imported_node fmt ind = pp_node_args ind.nodei_outputs; (* Contracts *) fprintf fmt "%a%t" - (fun fmt s -> match s with Some s -> pp_spec_as_comment fmt s | _ -> ()) ind.nodei_spec + (fun fmt s -> match s with Some s -> pp_spec_as_comment fmt (ind.nodei_inputs, ind.nodei_outputs, s) | _ -> ()) ind.nodei_spec (fun fmt -> match ind.nodei_spec with None -> () | Some _ -> fprintf fmt "@ "); fprintf fmt "@]@ " diff --git a/src/scheduling.ml b/src/scheduling.ml index 8721b72b5f4795f6776c9c3440ea0ea21461f787..983d38bd6c5a58440e0c5605c3406cd5234014fc 100644 --- a/src/scheduling.ml +++ b/src/scheduling.ml @@ -113,13 +113,15 @@ let filter_original n vl = let vdecl = get_node_var v n in if vdecl.var_orig then v :: res else res) vl [] +let eq_equiv eq_equiv_hash = + fun v1 v2 -> + try + Hashtbl.find eq_equiv_hash v1 = Hashtbl.find eq_equiv_hash v2 + with Not_found -> false + let schedule_node n = (* let node_vars = get_node_vars n in *) - let eq_equiv = ExprDep.node_eq_equiv n in - let eq_equiv v1 v2 = - try - Hashtbl.find eq_equiv v1 = Hashtbl.find eq_equiv v2 - with Not_found -> false in + let eq_equiv = eq_equiv (ExprDep.node_eq_equiv n) in let n', g = global_dependency n in @@ -135,6 +137,9 @@ let schedule_node n = let fanin = Liveness.compute_fanin n gg in { node = n'; schedule = sort; unused_vars = unused; fanin_table = fanin; dep_graph = gg; } +(* let schedule_eqs eqs = + * let eq_equiv = eq_equiv (ExprDep.eqs_eq_equiv eqs) in + * assert false (\* TODO: continue to implement scheduling of eqs for spec *\) *) let compute_node_reuse_table report = let disjoint = Disjunction.clock_disjoint_map (get_node_vars report.node) in diff --git a/src/tools/stateflow/semantics/cPS_lustre_generator.ml b/src/tools/stateflow/semantics/cPS_lustre_generator.ml index 1ca7be39c746e1d55a4a192d2e896dbb0d70a085..6d88a593f3e109481f7e68255850b7e0a95cfb26 100644 --- a/src/tools/stateflow/semantics/cPS_lustre_generator.ml +++ b/src/tools/stateflow/semantics/cPS_lustre_generator.ml @@ -345,7 +345,8 @@ struct node_dec_stateless = false; node_stateless = None; node_spec = None; - node_annot = []} + node_annot = []; + node_iscontract = false} ) in [node] @@ -400,7 +401,8 @@ Il faut faire les choses suivantes: node_dec_stateless = false; node_stateless = None; node_spec = None; - node_annot = []} + node_annot = []; + node_iscontract = false;} ) in node_principal diff --git a/src/typing.ml b/src/typing.ml index dfee23e23ddf188bf1ac610807b3cfae139d7070..be1ec772940223c1ed5ef1e6ea27059072071a9f 100644 --- a/src/typing.ml +++ b/src/typing.ml @@ -669,16 +669,16 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t let check_vd_env vd_env = ignore (List.fold_left add_vdecl [] vd_env) - let type_spec env spec = - let vd_env = spec.consts @ spec.locals in + let type_contract env c = + let vd_env = c.consts @ c.locals in check_vd_env vd_env; let env = type_var_decl_list ((* this argument seems useless to me, cf TODO at top of the file*) vd_env) env vd_env in (* typing stmts *) - let eqs = List.map (fun s -> match s with Eq eq -> eq | _ -> assert false) spec.stmts in + let eqs = List.map (fun s -> match s with Eq eq -> eq | _ -> assert false) c.stmts in let undefined_vars_init = List.fold_left (fun uvs v -> ISet.add v.var_id uvs) - ISet.empty spec.locals + ISet.empty c.locals in let _ = List.fold_left @@ -692,9 +692,9 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t in List.iter type_pred_ee ( - spec.assume - @ spec.guarantees - @ List.flatten (List.map (fun m -> m.ensure @ m.require) spec.modes) + c.assume + @ c.guarantees + @ List.flatten (List.map (fun m -> m.ensure @ m.require) c.modes) ); (*TODO enrich env locally with locals and consts @@ -703,13 +703,19 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t For the moment, returning the provided env *) env - + + let rec type_spec env spec loc = + match spec with + | Contract c -> type_contract env c + | NodeSpec id -> env + (** [type_node env nd loc] types node [nd] in environment env. The location is used for error reports. *) - let type_node env nd loc = + and type_node env nd loc = let is_main = nd.node_id = !Options.main_node in - let vd_env_ol = nd.node_outputs@nd.node_locals in - let vd_env = nd.node_inputs@vd_env_ol in + (* In contracts, outputs are considered as input values *) + let vd_env_ol = if nd.node_iscontract then nd.node_locals else nd.node_outputs@nd.node_locals in + let vd_env = nd.node_inputs@nd.node_outputs@nd.node_locals in check_vd_env vd_env; let init_env = env in let delta_env = type_var_decl_list vd_env init_env nd.node_inputs in @@ -720,18 +726,22 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t List.fold_left (fun uvs v -> ISet.add v.var_id uvs) ISet.empty vd_env_ol in + Format.eprintf "Undef1: %a@ " pp_iset undefined_vars_init; let undefined_vars = let eqs, auts = get_node_eqs nd in (* TODO XXX: il faut typer les handlers de l'automate *) List.fold_left (type_eq (new_env, vd_env) is_main) undefined_vars_init eqs in + Format.eprintf "Undef2: %a@ " pp_iset undefined_vars; (* Typing asserts *) List.iter (fun assert_ -> let assert_expr = assert_.assert_expr in type_subtyping_arg (new_env, vd_env) is_main false assert_expr (* Type_predef. *)type_bool ) nd.node_asserts; (* Typing spec/contracts *) - (match nd.node_spec with None -> () | Some spec -> ignore (type_spec new_env spec)); + (match nd.node_spec with + | None -> () + | Some spec -> ignore (type_spec new_env spec loc)); (* Typing annots *) List.iter (fun annot -> List.iter (fun (_, eexpr) -> ignore (type_eexpr (new_env, vd_env) eexpr)) annot.annots @@ -739,7 +749,10 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t (* check that table is empty *) let local_consts = List.fold_left (fun res vdecl -> if vdecl.var_dec_const then ISet.add vdecl.var_id res else res) ISet.empty nd.node_locals in + Format.eprintf "Localconsts: %a@ " pp_iset local_consts; let undefined_vars = ISet.diff undefined_vars local_consts in + Format.eprintf "Undef3: %a@ " pp_iset undefined_vars; + if (not (ISet.is_empty undefined_vars)) then raise (Error (loc, Undefined_var undefined_vars)); let ty_ins = type_of_vlist nd.node_inputs in @@ -757,7 +770,9 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t let delta_env = type_var_decl_list vd_env delta_env nd.nodei_outputs in let new_env = Env.overwrite env delta_env in (* Typing spec *) - (match nd.nodei_spec with None -> () | Some spec -> ignore (type_spec new_env spec)); + (match nd.nodei_spec with + | None -> () + | Some spec -> ignore (type_spec new_env spec loc)); let ty_ins = type_of_vlist nd.nodei_inputs in let ty_outs = type_of_vlist nd.nodei_outputs in let ty_node = new_ty (Tarrow (ty_ins,ty_outs)) in @@ -865,14 +880,27 @@ module Make (T: Types.S) (Expr_type_hub: EXPR_TYPE_HUB with type type_expr = T.t let check_env_compat header declared computed = uneval_prog_generics header; Env.iter declared (fun k decl_type_k -> - let loc = (get_imported_symbol header k).top_decl_loc in - let computed_t = - instantiate (ref []) (ref []) - (try Env.lookup_value computed k - with Not_found -> raise (Error (loc, Declared_but_undefined k))) in - (*Types.print_ty Format.std_formatter decl_type_k; - Types.print_ty Format.std_formatter computed_t;*) - try_unify ~sub:true ~semi:true decl_type_k computed_t loc + let top = get_imported_symbol header k in + let loc = top.top_decl_loc in + try + let computed_t = Env.lookup_value computed k in + let computed_t = instantiate (ref []) (ref []) computed_t in + (*Types.print_ty Format.std_formatter decl_type_k; + Types.print_ty Format.std_formatter computed_t;*) + try_unify ~sub:true ~semi:true decl_type_k computed_t loc + with Not_found -> + begin + (* If top is a contract we do not require the lustre + file to provide the same definition. *) + match top.top_decl_desc with + | Node nd -> ( + match nd.node_spec with + | Some (Contract _) -> () + | _ -> raise (Error (loc, Declared_but_undefined k)) + ) + | _ -> + raise (Error (loc, Declared_but_undefined k)) + end ) let check_typedef_top decl = diff --git a/src/utils/utils.ml b/src/utils/utils.ml index a2b7a2d8bfe2025ae049ff7af6c9e4e494e97229..89d1be33652004832e86eebf09c1bb0ce9955223 100644 --- a/src/utils/utils.ml +++ b/src/utils/utils.ml @@ -427,6 +427,9 @@ let get_date () = (* in *) Format.flush_str_formatter () -(* Local Variables: *) -(* compile-command:"make -C .." *) -(* End: *) + + + (* Local Variables: *) + (* compile-command:"make -C .." *) + (* End: *) +