You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
416 lines
14 KiB
OCaml
416 lines
14 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Heptagon *)
|
|
(* *)
|
|
(* Gwenael Delaval, LIG/INRIA, UJF *)
|
|
(* Leonard Gerard, Parkas, ENS *)
|
|
(* Adrien Guatto, Parkas, ENS *)
|
|
(* Cedric Pasteur, Parkas, ENS *)
|
|
(* *)
|
|
(* Copyright 2012 ENS, INRIA, UJF *)
|
|
(* *)
|
|
(* This file is part of the Heptagon compiler. *)
|
|
(* *)
|
|
(* Heptagon is free software: you can redistribute it and/or modify it *)
|
|
(* under the terms of the GNU General Public License as published by *)
|
|
(* the Free Software Foundation, either version 3 of the License, or *)
|
|
(* (at your option) any later version. *)
|
|
(* *)
|
|
(* Heptagon is distributed in the hope that it will be useful, *)
|
|
(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)
|
|
(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)
|
|
(* GNU General Public License for more details. *)
|
|
(* *)
|
|
(* You should have received a copy of the GNU General Public License *)
|
|
(* along with Heptagon. If not, see <http://www.gnu.org/licenses/> *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(** {2 Simple initialization analysis}
|
|
The initialization analysis only deals with the first instant of flows.
|
|
Things are easy since input/outputs of a node are considered initialized.
|
|
It is allowed to have uninitialized inputs for safe nodes,
|
|
it will consider the result as uninitialized.
|
|
[last x] is initialized when either it was declared with an initial value
|
|
or when [x] is defined in the initial state of an automaton. *)
|
|
|
|
|
|
(* Requis : typage *)
|
|
|
|
open Misc
|
|
open Names
|
|
open Idents
|
|
open Heptagon
|
|
open Types
|
|
open Location
|
|
open Format
|
|
|
|
type typ =
|
|
| Iproduct of typ list
|
|
| Ileaf of init
|
|
|
|
and init = initr ref
|
|
and initr =
|
|
| Izero
|
|
| Ione of root
|
|
| Ivar of int
|
|
| Imax of init * init
|
|
| Ilink of init
|
|
|
|
(* try to keep track of the root of the uninitialized state *)
|
|
and root =
|
|
| RLast_none of ident
|
|
| RExp of exp
|
|
| ROr of root * root
|
|
|
|
(* typing errors *)
|
|
exception Unify of root
|
|
|
|
(** unalias [init] type *)
|
|
let rec irepr i =
|
|
match !i with
|
|
| Ilink(i_son) ->
|
|
let i_son = irepr i_son in
|
|
i := Ilink(i_son); (* shorten path *)
|
|
i_son
|
|
| _ -> i
|
|
|
|
let _index = ref 0
|
|
let new_var () =
|
|
let gen_index () = incr _index; !_index in
|
|
ref (Ivar(gen_index ()))
|
|
|
|
let izero = ref Izero
|
|
let ione root = ref (Ione root)
|
|
|
|
(** max between types with some basic simplifications *)
|
|
let imax i1 i2 =
|
|
let i1 = irepr i1 in
|
|
let i2 = irepr i2 in
|
|
match !i1, !i2 with
|
|
| (Izero, Izero) -> izero
|
|
| (Izero, _) -> i2
|
|
| (_, Izero) -> i1
|
|
| (Ione r1, Ione r2) -> ione (ROr(r1, r2))
|
|
| (_, Ione r) | (Ione r, _) -> ione r
|
|
| _ -> ref (Imax(i1, i2))
|
|
|
|
let product l = Iproduct(l)
|
|
let leaf i = Ileaf(i)
|
|
|
|
(** Typing Environment *)
|
|
module IEnv =
|
|
struct
|
|
type k = | Last of ident | Var of ident
|
|
type v = init
|
|
include (Map.Make (struct type t = k let compare = compare end))
|
|
|
|
let find_var x h = find (Var x) h
|
|
let find_last x h = find (Last x) h
|
|
|
|
let find_var_typ x h = leaf (find_var x h)
|
|
let find_last_typ x h = leaf (find_last x h)
|
|
|
|
let add_var x v h = add (Var x) v h
|
|
let add_last x v h = add (Last x) v h
|
|
|
|
let _add_var_dec def h vd =
|
|
let h = add_var vd.v_ident def h in
|
|
match vd.v_last with
|
|
| Heptagon.Var -> h
|
|
| Heptagon.Last None ->
|
|
add_last vd.v_ident (ione (RLast_none vd.v_ident)) h (* last is not initialized *)
|
|
| Heptagon.Last (Some _) ->
|
|
add_last vd.v_ident izero h (* last is initialized *)
|
|
|
|
let add_initd_var_dec h vd = _add_var_dec izero h vd
|
|
let add_var_dec h vd = _add_var_dec (new_var ()) h vd
|
|
end
|
|
|
|
(** return the representative of a [typ] ( the max ) *)
|
|
let rec itype = function
|
|
| Iproduct(ty_list) ->
|
|
List.fold_left (fun acc ty -> imax acc (itype ty)) izero ty_list
|
|
| Ileaf(i) -> i
|
|
|
|
(** saturate an [init] type. Every element must be initialized *)
|
|
let rec force_initialized i =
|
|
let i = irepr i in
|
|
match !i with
|
|
| Izero -> ()
|
|
| Ivar _ -> i := Ilink(izero)
|
|
| Imax(i1, i2) -> force_initialized i1; force_initialized i2
|
|
| Ilink(i) -> force_initialized i
|
|
| Ione r -> raise (Unify r)
|
|
|
|
(** build a [typ] from a [ty] *)
|
|
let rec skeleton i ty =
|
|
match ty with
|
|
| Tprod(ty_list) -> product (List.map (skeleton i) ty_list)
|
|
| _ -> leaf i
|
|
|
|
(** sub-typing *)
|
|
let rec less left_ty right_ty =
|
|
(* an inequation [a < t[a]] becomes [a = t[0]] *)
|
|
let rec occur_check index i =
|
|
match !i with
|
|
| Izero | Ione _ -> i
|
|
| Ivar id -> if id = index then izero else i
|
|
| Imax(i1, i2) -> imax (occur_check index i1) (occur_check index i2)
|
|
| Ilink(i) -> occur_check index i
|
|
in
|
|
(* sub-typing on [init] *)
|
|
let rec iless left_i right_i =
|
|
let left_i = irepr left_i in
|
|
let right_i = irepr right_i in
|
|
if left_i == right_i then ()
|
|
else match !left_i, !right_i with
|
|
| Izero, _ -> ()
|
|
| _, Ione _ -> ()
|
|
| _, Izero -> force_initialized left_i
|
|
| Imax(i1, i2), _ -> iless i1 right_i; iless i2 right_i
|
|
| _, Ivar id ->
|
|
let left_i = occur_check id left_i in
|
|
right_i := Ilink left_i
|
|
| Ivar id, Imax(i1, i2) ->
|
|
let i1 = occur_check id i1 in
|
|
let i2 = occur_check id i2 in
|
|
right_i := Ilink(imax left_i (imax i1 i2))
|
|
| Ione r, Imax _ -> raise (Unify r)
|
|
| Ilink _, _ | _, Ilink _ -> assert false
|
|
in
|
|
if left_ty == right_ty then ()
|
|
else match left_ty, right_ty with
|
|
| Iproduct(l1), Iproduct(l2) -> List.iter2 less l1 l2
|
|
| Ileaf(i1), Ileaf(i2) -> iless i1 i2
|
|
| _ -> assert false
|
|
|
|
|
|
module Printer = struct
|
|
open Format
|
|
open Pp_tools
|
|
open Global_printer
|
|
|
|
let rec print_init ff i = match !i with
|
|
| Izero -> fprintf ff "initialized"
|
|
| Ione _ -> fprintf ff "not initialized"
|
|
| Ivar(i) -> fprintf ff "ivar_%i" i
|
|
| Imax(i1, i2) -> fprintf ff "@[<4>max %a@ %a@]" print_init i1 print_init i2
|
|
| Ilink(i) -> print_init ff i
|
|
|
|
let rec print_type ff = function
|
|
| Ileaf(i) -> print_init ff i
|
|
| Iproduct(ty_list) ->
|
|
fprintf ff "@[%a@]" (print_list_r print_type "("" *"")") ty_list
|
|
|
|
let rec print_root ff = function
|
|
| RLast_none(i) ->
|
|
fprintf ff "that last %a should be initialized" print_ident i
|
|
| RExp(e) ->
|
|
fprintf ff "the expression :@\n @[%a@]" print_location e.e_loc
|
|
| ROr(r1,r2) ->
|
|
fprintf ff "@\n- %a@\n- or %a" print_root r1 print_root r2
|
|
end
|
|
|
|
module Error = struct
|
|
open Location
|
|
|
|
type error = | Eclash of root * typ * typ
|
|
|
|
exception Error of location * error
|
|
|
|
let error loc kind = raise (Error(loc, kind))
|
|
|
|
let message loc kind =
|
|
begin match kind with
|
|
| Eclash(root, left_ty, right_ty) ->
|
|
Format.eprintf
|
|
"Initialization error :@\n%a\
|
|
this expression is %a,@ but is expected to be %a,\
|
|
@ the root of the conflict is %a.@."
|
|
print_location loc
|
|
Printer.print_type left_ty
|
|
Printer.print_type right_ty
|
|
Printer.print_root root
|
|
end;
|
|
raise Errors.Error
|
|
end
|
|
|
|
let less_exp e actual_ty expected_ty =
|
|
try
|
|
less actual_ty expected_ty
|
|
with Unify r -> Error.message e.e_loc (Error.Eclash(r,actual_ty, expected_ty))
|
|
|
|
(** Main typing function *)
|
|
let rec typing h e =
|
|
match e.e_desc with
|
|
| Econst _ -> skeleton izero e.e_ty
|
|
| Evar(x) -> IEnv.find_var_typ x h
|
|
| Elast(x) -> IEnv.find_last_typ x h
|
|
| Epre(None, e1) ->
|
|
initialized_exp h e1;
|
|
skeleton (ione (RExp e)) e1.e_ty
|
|
| Epre(Some _, e) ->
|
|
initialized_exp h e;
|
|
skeleton izero e.e_ty
|
|
| Efby (e1, e2) ->
|
|
initialized_exp h e2;
|
|
skeleton (itype (typing h e1)) e.e_ty
|
|
| Eapp({ a_op = Etuple }, e_list, _) ->
|
|
product (List.map (typing h) e_list)
|
|
| Eapp(app, e_list, _) ->
|
|
let i = apply h app e_list in
|
|
skeleton i e.e_ty
|
|
| Estruct(l) ->
|
|
let i =
|
|
List.fold_left
|
|
(fun acc (_, e) -> imax acc (itype (typing h e))) izero l in
|
|
skeleton i e.e_ty
|
|
| Eiterator (_, _, _, pe_list, e_list, _) ->
|
|
List.iter (fun e -> initialized_exp h e) pe_list;
|
|
List.iter (fun e -> initialized_exp h e) e_list;
|
|
skeleton izero e.e_ty
|
|
| Ewhen (e, _, x) ->
|
|
let i = imax (IEnv.find_var x h) (itype (typing h e)) in
|
|
skeleton i e.e_ty
|
|
| Emerge (x, c_e_list) ->
|
|
let i =
|
|
List.fold_left
|
|
(fun acc (_, e) -> imax acc (itype (typing h e))) izero c_e_list in
|
|
let i = imax (IEnv.find_var x h) i in
|
|
skeleton i e.e_ty
|
|
| Esplit (c, e2) ->
|
|
let i = imax (itype (typing h c)) (itype (typing h e2)) in
|
|
skeleton i e.e_ty
|
|
|
|
(** Typing an application *)
|
|
and apply h app e_list =
|
|
match app.a_op with
|
|
| Earrow ->
|
|
let e1,e2 = assert_2 e_list in
|
|
let ty1 = typing h e1 in
|
|
let _ = typing h e2 in
|
|
itype ty1
|
|
| _ ->
|
|
if app.a_unsafe
|
|
then ( (* when unsafe force all inputs to be initialized *)
|
|
List.iter (fun e -> initialized_exp h e) e_list; izero )
|
|
else (
|
|
List.fold_left (fun acc e -> max acc (itype (typing h e))) izero e_list)
|
|
|
|
|
|
and expect h e expected_ty =
|
|
let actual_ty = typing h e in
|
|
less_exp e actual_ty expected_ty
|
|
|
|
and initialized_exp h e = expect h e (skeleton izero e.e_ty)
|
|
|
|
let rec typing_pat h = function
|
|
| Evarpat(x) -> IEnv.find_var_typ x h
|
|
| Etuplepat(pat_list) ->
|
|
product (List.map (typing_pat h) pat_list)
|
|
|
|
(** Typing equations *)
|
|
let rec typing_eqs h eq_list = List.iter (typing_eq h) eq_list
|
|
|
|
and typing_eq h eq =
|
|
match eq.eq_desc with
|
|
| Eautomaton(handlers) -> typing_automaton h handlers
|
|
| Eswitch(e, handlers) ->
|
|
initialized_exp h e;
|
|
typing_switch h handlers
|
|
| Epresent(handlers, b) ->
|
|
typing_present h handlers b
|
|
| Ereset(b, e) ->
|
|
initialized_exp h e; ignore (typing_block h b)
|
|
| Eblock b ->
|
|
ignore (typing_block h b)
|
|
| Eeq(pat, e) ->
|
|
let ty_pat = typing_pat h pat in
|
|
expect h e ty_pat
|
|
|
|
and typing_switch h handlers =
|
|
let handler { w_block = b } = ignore (typing_block h b) in
|
|
List.iter handler handlers
|
|
|
|
and typing_present h handlers b =
|
|
let handler { p_cond = e; p_block = b } =
|
|
initialized_exp h e; ignore (typing_block h b) in
|
|
List.iter handler handlers; ignore (typing_block h b)
|
|
|
|
and typing_automaton h state_handlers =
|
|
(* we make a special treatment for state variables defined in the *)
|
|
(* initial state *)
|
|
let weak { s_unless = sunless } =
|
|
match sunless with | [] -> true | _ -> false in
|
|
|
|
(* Set in the env [last x] as initialized if [x] is initialized here *)
|
|
let initialized h { s_block = { b_defnames = l } } =
|
|
let env_update x h =
|
|
try
|
|
let xl = IEnv.find_last x h in (* it's a last in the env, good. *)
|
|
IEnv.add_last x (IEnv.find_var x h) h
|
|
with Not_found -> h (* nothing to do *)
|
|
in
|
|
Env.fold (fun x _ h -> env_update x h) l h in
|
|
|
|
let handler h { s_state = _; s_block = b; s_until = suntil; s_unless = sunless } =
|
|
let escape h { e_cond = e } = initialized_exp h e in
|
|
(* typing the body *)
|
|
let h = typing_block h b in
|
|
List.iter (escape h) suntil;
|
|
List.iter (escape h) sunless
|
|
in
|
|
|
|
(* typing the body of the automaton *)
|
|
match state_handlers with
|
|
(* we do a special treatment for state variables which *)
|
|
(* are defined in the initial state if it cannot be immediately exited *)
|
|
| initial :: other_handlers when weak initial ->
|
|
handler h initial; (* first type it *)
|
|
let h = initialized h initial in
|
|
(* then type the others in the env of the first one *)
|
|
List.iter (handler h) other_handlers
|
|
| _ -> List.iter (handler h) state_handlers
|
|
|
|
and typing_block h { b_local = dec; b_equs = eq_list } =
|
|
let h_extended = build h dec in
|
|
typing_eqs h_extended eq_list;
|
|
h_extended
|
|
|
|
(* add var_decs to a typing environment *)
|
|
and build h vdecs =
|
|
List.fold_left IEnv.add_var_dec h vdecs
|
|
|
|
(* add var_decs as initialized to a typing environement *)
|
|
let build_initialized h vdecs =
|
|
List.fold_left IEnv.add_initd_var_dec h vdecs
|
|
|
|
let typing_contract h contract =
|
|
match contract with
|
|
| None -> h
|
|
| Some { c_block = b;
|
|
c_assume = e_a;
|
|
c_enforce = e_g;
|
|
c_controllables = c } ->
|
|
let h' = build h b.b_local in
|
|
typing_eqs h' b.b_equs;
|
|
(* assumption *)
|
|
expect h' e_a (skeleton izero e_a.e_ty);
|
|
(* property *)
|
|
expect h' e_g (skeleton izero e_g.e_ty);
|
|
build_initialized h c
|
|
|
|
let typing_node { n_input = i_list; n_output = o_list;
|
|
n_contract = contract; n_block = b } =
|
|
let h = build_initialized IEnv.empty i_list in
|
|
let h = build_initialized h o_list in
|
|
let h = typing_contract h contract in
|
|
ignore (typing_block h b)
|
|
|
|
let program ({ p_desc = pd } as p) =
|
|
List.iter (function Pnode n -> typing_node n | _ -> ()) pd;
|
|
p
|
|
|
|
|