![Timothy Bourke](/assets/img/avatar_default.png)
For a clock (ck on x) to be well-formed in an environment H, we must have H x = ck, i.e., the clock of x is the same as the clock ck of the stream being sampled. This constraint is guaranteed by construction for fully inferred clocks (by the rules for when and merge), but nothing guarantees that user declarations be well-formed. This can lead to problems. For instance, this invalid program is incorrectly accepted: node f (x : bool; a : bool :: . on b; b : bool :: . on a) returns (y:bool); let y = true; tel as is this one: node f(a: bool :: . on a; b: bool :: . on a) returns (z: bool); var w : bool; let w = a when b; z = false fby w; tel This invalid program is incorrectly accepted and leads to an internal compiler error: node f (x : bool) returns (y:bool); var a : bool :: . on b; b : bool :: . on a; let y = true; a = true; b = true; tel This patch enforces the well-formedness constraint. It gives a sensible error message when the constraint cannot be satisfied.
320 lines
13 KiB
OCaml
320 lines
13 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Heptagon *)
|
|
(* *)
|
|
(* Gwenael Delaval, LIG/INRIA, UJF *)
|
|
(* Leonard Gerard, Parkas, ENS *)
|
|
(* Adrien Guatto, Parkas, ENS *)
|
|
(* Cedric Pasteur, Parkas, ENS *)
|
|
(* Marc Pouzet, 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/> *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
(* clock checking *)
|
|
|
|
(* v_clock is expected to contain correct clocks before entering here :
|
|
either explicit with Cbase representing the node activation clock
|
|
or fresh_clock() for unannoted variables.
|
|
Idem for e_ct : if explicit, it represents a clock annotation.
|
|
Unification is done on this mutable fields.
|
|
e_base_ck is set according to node signatures.
|
|
|
|
*)
|
|
|
|
open Idents
|
|
open Names
|
|
open Minils
|
|
open Global_printer
|
|
open Mls_printer
|
|
open Signature
|
|
open Clocks
|
|
open Location
|
|
open Format
|
|
|
|
(** Error Kind *)
|
|
type error_kind =
|
|
| Etypeclash of ct * ct
|
|
| Eclockclash of Clocks.ck * Clocks.ck
|
|
| Edefclock
|
|
|
|
let error_message loc = function
|
|
| Etypeclash (actual_ct, expected_ct) ->
|
|
Format.eprintf "%aClock Clash: this expression has clock %a,@\n\
|
|
but is expected to have clock %a.@."
|
|
print_location loc
|
|
print_ct actual_ct
|
|
print_ct expected_ct;
|
|
raise Errors.Error
|
|
| Eclockclash (actual_ck, expected_ck) ->
|
|
Format.eprintf "%aClock Clash: this value has clock %a,@\n\
|
|
but is expected to have clock %a.@."
|
|
print_location loc
|
|
print_ck actual_ck
|
|
print_ck expected_ck;
|
|
raise Errors.Error
|
|
| Edefclock ->
|
|
Format.eprintf "%aArguments defining clocks should be given as names@."
|
|
print_location loc;
|
|
raise Errors.Error
|
|
|
|
|
|
let ck_of_name h x =
|
|
if is_reset x
|
|
then fresh_clock()
|
|
else
|
|
try Env.find x h
|
|
with Not_found ->
|
|
(* Format.eprintf "looking for %a/%d@." print_ident x x.num; *)
|
|
(* Env.iter (fun k ck -> Format.eprintf "%a/%d => %a@." print_ident k k.num print_ck ck) h; *)
|
|
Misc.internal_error ("clocking: unknown clock name " ^ name x)
|
|
|
|
let rec typing_extvalue h w =
|
|
let ck = match w.w_desc with
|
|
| Wconst _ -> fresh_clock()
|
|
| Wvar x -> ck_of_name h x
|
|
| Wwhen (w1, c, n) ->
|
|
let ck_n = ck_of_name h n in
|
|
expect_extvalue h ck_n w1;
|
|
Clocks.Con (ck_n, c, n)
|
|
| Wfield (w1, _) ->
|
|
typing_extvalue h w1
|
|
| Wreinit (w1, w2) ->
|
|
let t1 = typing_extvalue h w1 in
|
|
let t2 = typing_extvalue h w2 in
|
|
unify_ck t1 t2;
|
|
t1
|
|
in
|
|
(try unify_ck ck w.w_ck
|
|
with Unify ->
|
|
eprintf "Incoherent clock annotation for extvalue %a.@\n"
|
|
Mls_printer.print_extvalue w;
|
|
error_message w.w_loc (Etypeclash (Ck ck, Ck w.w_ck)));
|
|
w.w_ck <- ck;
|
|
ck
|
|
|
|
and expect_extvalue h expected_ck e =
|
|
let actual_ck = typing_extvalue h e in
|
|
try unify_ck actual_ck expected_ck
|
|
with
|
|
| Unify -> eprintf "%a : " print_extvalue e;
|
|
error_message e.w_loc (Eclockclash (actual_ck, expected_ck))
|
|
|
|
let rec typing_pat h = function
|
|
| Evarpat x -> Ck (ck_of_name h x)
|
|
| Etuplepat pat_list -> Cprod (List.map (typing_pat h) pat_list)
|
|
|
|
|
|
let typing_app h base pat op w_list = match op with
|
|
| Earray_fill | Eselect | Eselect_dyn | Eselect_trunc | Eupdate | Eequal
|
|
| Eselect_slice | Econcat | Earray | Efield_update | Eifthenelse ->
|
|
List.iter (expect_extvalue h base) w_list;
|
|
Ck base
|
|
| Efun { qual = Module "Iostream"; name = "printf" }
|
|
| Efun { qual = Module "Iostream"; name = "fprintf" } ->
|
|
List.iter (expect_extvalue h base) w_list;
|
|
Cprod []
|
|
| ( Efun f | Enode f) ->
|
|
let node = Modules.find_value f in
|
|
let pat_id_list = Mls_utils.ident_list_of_pat pat in
|
|
let rec build_env a_l v_l env = match a_l, v_l with
|
|
| [],[] -> env
|
|
| a::a_l, v::v_l -> (match a.a_name with
|
|
| None -> build_env a_l v_l env
|
|
| Some n -> build_env a_l v_l ((n,v)::env))
|
|
| _ -> Misc.internal_error ("Clocking, non matching signature in call of " ^
|
|
Names.fullname f)
|
|
in
|
|
let env_pat = build_env node.node_outputs pat_id_list [] in
|
|
let env_args = build_env node.node_inputs w_list [] in
|
|
(* implement with Cbase as base, replace name dep by ident dep *)
|
|
let rec sigck_to_ck sck = match sck with
|
|
| Signature.Cbase -> base
|
|
| Signature.Con (sck,c,x) ->
|
|
(* find x in the envs : *)
|
|
let id = try List.assoc x env_pat
|
|
with Not_found ->
|
|
try
|
|
let w = List.assoc x env_args in
|
|
(match w.w_desc with
|
|
| Wvar id -> id
|
|
| _ -> error_message w.w_loc Edefclock)
|
|
with Not_found ->
|
|
Misc.internal_error "Clocking, non matching signature 2"
|
|
in
|
|
Clocks.Con (sigck_to_ck sck, c, id)
|
|
in
|
|
List.iter2 (fun a w -> expect_extvalue h (sigck_to_ck a.a_clock) w) node.node_inputs w_list;
|
|
Clocks.prod (List.map (fun a -> sigck_to_ck a.a_clock) node.node_outputs)
|
|
|
|
|
|
let rec stateful e = match e.e_desc with
|
|
| Efby _ -> true
|
|
| Ewhen (e,_,_) -> stateful e
|
|
| Eapp({a_unsafe = unsafe}, _, _) when unsafe -> true
|
|
| Eapp({a_op = Enode _}, _, _) -> true
|
|
| _ -> false
|
|
|
|
|
|
let typing_eq h ({ eq_lhs = pat; eq_rhs = e; eq_loc = loc } as eq) =
|
|
(* typing the expression, returns ct, ck_base *)
|
|
let rec typing e =
|
|
let ct,base = match e.e_desc with
|
|
| Eextvalue w
|
|
| Efby (_, w) ->
|
|
let ck = typing_extvalue h w in
|
|
Ck ck, ck
|
|
| Ewhen (e,c,n) ->
|
|
let ck_n = ck_of_name h n in
|
|
let _base = expect (skeleton ck_n e.e_ty) e in
|
|
let base_ck = if stateful e then ck_n else Clocks.Con (ck_n, c, n) in
|
|
skeleton (Clocks.Con (ck_n, c, n)) e.e_ty, base_ck
|
|
| Emerge (x, c_e_list) ->
|
|
let ck = ck_of_name h x in
|
|
List.iter (fun (c,e) -> expect_extvalue h (Clocks.Con (ck,c,x)) e) c_e_list;
|
|
Ck ck, ck
|
|
| Estruct l ->
|
|
let ck = fresh_clock () in
|
|
List.iter (fun (_, e) -> expect_extvalue h ck e) l;
|
|
Ck ck, ck
|
|
| Eapp({a_op = op}, args, _) -> (* hyperchronous reset *)
|
|
let base_ck = fresh_clock () in
|
|
let ct = typing_app h base_ck pat op args in
|
|
ct, base_ck
|
|
| Eiterator (it, {a_op = op}, nl, pargs, args, _) -> (* hyperchronous reset *)
|
|
let base_ck = fresh_clock() in
|
|
let ct = match it with
|
|
| Imap -> (* exactly as if clocking the node *)
|
|
typing_app h base_ck pat op (pargs@args)
|
|
| Imapi -> (* clocking the node with the extra i input on [ck_r] *)
|
|
let il (* stubs i as 0 *) =
|
|
List.map (fun _ -> mk_extvalue ~ty:Initial.tint ~linearity:Linearity.Ltop
|
|
~clock:base_ck (Wconst (Initial.mk_static_int 0))) nl
|
|
in
|
|
typing_app h base_ck pat op (pargs@args@il)
|
|
| Ifold | Imapfold ->
|
|
(* clocking node with equality constaint on last input and last output *)
|
|
let ct = typing_app h base_ck pat op (pargs@args) in
|
|
unify_ck (Clocks.last_clock ct) (Misc.last_element args).w_ck;
|
|
ct
|
|
| Ifoldi -> (* clocking the node with the extra i and last in/out constraints *)
|
|
let il (* stubs i as 0 *) =
|
|
List.map (fun _ -> mk_extvalue ~ty:Initial.tint ~linearity:Linearity.Ltop
|
|
~clock:base_ck (Wconst (Initial.mk_static_int 0))) nl
|
|
in
|
|
let rec insert_i args = match args with
|
|
| [] -> il
|
|
| [l] -> il @ [l]
|
|
| h::l -> h::(insert_i l)
|
|
in
|
|
let ct = typing_app h base_ck pat op (pargs@(insert_i args)) in
|
|
unify_ck (Clocks.last_clock ct) (Misc.last_element args).w_ck;
|
|
ct
|
|
in
|
|
ct, base_ck
|
|
in
|
|
(try unify ct e.e_ct
|
|
with Unify ->
|
|
eprintf "Inconsistent clock annotation for exp %a.@\n"
|
|
Mls_printer.print_exp e;
|
|
error_message e.e_loc (Etypeclash (ct,e.e_ct)));
|
|
e.e_ct <- ct;
|
|
ct, base
|
|
and expect expected_ct e =
|
|
let actual_ct,base = typing e in
|
|
(try unify actual_ct expected_ct
|
|
with Unify -> error_message e.e_loc (Etypeclash (actual_ct, expected_ct)));
|
|
base
|
|
in
|
|
let ct,base_ck = typing e in
|
|
let pat_ct = typing_pat h pat in
|
|
(try unify ct pat_ct
|
|
with Unify ->
|
|
eprintf "Incoherent clock between right and left side of the equation.@\n";
|
|
error_message loc (Etypeclash (ct, pat_ct)));
|
|
{ eq with eq_base_ck = base_ck }
|
|
|
|
let typing_eqs h eq_list = List.rev_map (typing_eq h) eq_list
|
|
|
|
let append_env h vds =
|
|
List.fold_left (fun h { v_ident = n; v_clock = ck } -> Env.add n ck h) h vds
|
|
|
|
|
|
let typing_contract h0 h contract =
|
|
match contract with
|
|
| None -> None, h
|
|
| Some ({ c_local = l_list;
|
|
c_eq = eq_list;
|
|
c_assume = e_a;
|
|
c_objectives = objs;
|
|
c_assume_loc = e_a_loc;
|
|
c_enforce_loc = e_g_loc;
|
|
c_controllables = c_list } as contract) ->
|
|
let h' = append_env h0 l_list in
|
|
(* assumption *)
|
|
(* property *)
|
|
let eq_list = typing_eqs h' eq_list in
|
|
expect_extvalue h' Clocks.Cbase e_a;
|
|
List.iter (fun o -> expect_extvalue h' Clocks.Cbase o.o_exp) objs;
|
|
expect_extvalue h Clocks.Cbase e_a_loc;
|
|
expect_extvalue h Clocks.Cbase e_g_loc;
|
|
let h = append_env h c_list in
|
|
Some { contract with c_eq = eq_list }, h
|
|
|
|
let rec constrain_on loc h = function
|
|
| Clocks.Cbase | Clocks.Cvar _ -> ()
|
|
| Clocks.Con (ck', c, n) ->
|
|
(try unify_ck (ck_of_name h n) ck'
|
|
with Unify -> error_message loc (Eclockclash (ck_of_name h n, ck')));
|
|
constrain_on loc h ck'
|
|
|
|
let constrain_on_over h xs =
|
|
List.iter (fun vd -> constrain_on vd.v_loc h (ck_of_name h vd.v_ident)) xs
|
|
|
|
let typing_node node =
|
|
let h0 = append_env Env.empty node.n_input in
|
|
let h0 = append_env h0 node.n_output in
|
|
let h = append_env h0 node.n_local in
|
|
constrain_on_over h node.n_input;
|
|
constrain_on_over h node.n_output;
|
|
constrain_on_over h node.n_local;
|
|
let contract, h = typing_contract h0 h node.n_contract in
|
|
(* let h = append_env h node.n_local in *)
|
|
let equs = typing_eqs h node.n_equs in
|
|
(* synchronize input and output on base : find the free vars and set them to base *)
|
|
Env.iter (fun _ ck -> unify_ck Clocks.Cbase (root_ck_of ck)) h0;
|
|
(*update clock info in variables descriptions *)
|
|
let set_clock vd = { vd with v_clock = ck_repr (Env.find vd.v_ident h) } in
|
|
let node = { node with n_input = List.map set_clock node.n_input;
|
|
n_output = List.map set_clock node.n_output;
|
|
n_local = List.rev_map set_clock node.n_local;
|
|
n_equs = equs;
|
|
n_contract = contract; }
|
|
in
|
|
let sign = Mls_utils.signature_of_node node in
|
|
Check_signature.check_signature sign;
|
|
Modules.replace_value node.n_name sign;
|
|
node
|
|
|
|
let program p =
|
|
let program_desc pd = match pd with
|
|
| Pnode nd -> Pnode (typing_node nd)
|
|
| _ -> pd
|
|
in
|
|
{ p with p_desc = List.map program_desc p.p_desc; }
|