Newer
Older
(** Time-stamp: <modified the 03/03/2009 (at 18:00) by Erwan Jahier> *)
(* exported *)
type t =
| Equal
| Ko of string (* a msg explaining why the unification failed *)
Erwan Jahier
committed
let teff2str = LicDump.string_of_type_eff4msg
let (is_overloadable : Eff.type_ -> bool) = function
| Int_type_eff -> true
| Real_type_eff -> true
| _ -> false
(* [contains t1 t2] is true iff t2 appears in t1 *)
let rec (contains : Eff.type_ -> Eff.type_ -> bool) =
fun t1 t2 ->
if t1 = t2 then true else
match t1 with
| Any
| Overload
| Bool_type_eff
| Int_type_eff
| Real_type_eff
| Enum_type_eff(_,_)
| External_type_eff _ -> false
| Abstract_type_eff (_, teff) -> false
| Array_type_eff(teff,i) -> contains teff t2
| Struct_type_eff(l, fl) ->
List.exists (fun (_,(teff,_)) -> contains teff t2) fl
(* exported *)
let (f : Eff.type_ list -> Eff.type_ list -> t) = fun l1 l2 ->
let rec (unify_type_eff : Eff.type_ -> Eff.type_ -> t) =
fun t1 t2 ->
if t1 = t2 then Equal else
match (t1,t2) with
| Array_type_eff(teff_ext1,i1), Array_type_eff(teff_ext2,i2) ->
if i1 <> i2 then Ko "\n*** incompatible array size" else
unify_type_eff teff_ext1 teff_ext2
| Struct_type_eff(l1, fl1), Struct_type_eff(l2, fl2) ->
if l1 <> l2 then Ko "\n*** incompatible structure" else
let fl1 = List.map (fun (_,(te,_)) -> te) fl1
and fl2 = List.map (fun (_,(te,_)) -> te) fl2 in
List.fold_left2 unify_do_acc Equal fl1 fl2
| Overload, Any
| Any, Overload -> Unif Overload
| t, Any
| Any, t ->
if contains t Any || contains t Overload then
Ko(("\n*** " ^ teff2str t1) ^ " and " ^ (teff2str t2) ^
" are not unifiable (there is a cycle)")
else
Unif t
| t, Overload
| Overload, t ->
if contains t Any || contains t Overload then
Ko("\n*** " ^ (teff2str t1) ^ " and " ^ (teff2str t2) ^
" are not unifiable (there is a cycle)")
else if is_overloadable t then
Unif t
else
Ko("\n*** " ^ (teff2str t) ^ " should be an integer or a real")
| _ ->
Ko("\n*** " ^ (teff2str t1) ^ " and " ^ (teff2str t2) ^
" are not unifiable")
and (unify_do_acc: t -> Eff.type_ -> Eff.type_ -> t) =
fun acc te1 te2 ->
match acc, unify_type_eff te1 te2 with
| Equal, Equal -> Equal
| Ko msg, _
| _, Ko msg -> Ko msg
| Unif t, Equal
| Equal, Unif t -> Unif t
| Unif t1, Unif t2 -> if t1 = t2 then acc else
Ko("\n*** " ^ (teff2str t1) ^ " and " ^ (teff2str t2) ^
" are not unifiable")
in
assert (List.length l1 = List.length l2);
List.fold_left2 unify_do_acc Equal l1 l2
(************************************************************************************)
(* Some unit tests *)
let i = Int_type_eff
let r = Real_type_eff
let b = Bool_type_eff
let e = External_type_eff (Ident.long_of_string "Toto::t")
let o = Overload
let a = Any
let array t c = Array_type_eff(t,c)
let struc t = Struct_type_eff ((Ident.long_of_string "T::t"),
[(Ident.of_string "x"),(t,None)])
let unify_failed = function Ko(_) -> true | _ -> false
let to_string = function
| Ko(msg) -> msg
| Equal -> "types are equals\n"
| Unif t -> "types are unifiable via " ^ (teff2str t) ^ "\n"
let proposition1 t1 t2 =
(* two lists of type are unifiable iff there exists a substitution
that makes them equal. Hence, if [f] and [subst_type] are
correct, the following function should always return true *)
match f t1 t2 with
| Unif t -> List.map (subst_type t) t1 = List.map (subst_type t) t2
| _ -> true
(* Since i don't know how to prove proposition1, I generate some random tests *)
let rec gen_random_type_eff () =
match Random.int 8 with
| 0 -> b
| 1 -> i
| 2 -> r
| 3 -> e
| 4 -> array (gen_random_type_eff ()) 42
| 5 -> struc (gen_random_type_eff ())
| 6 -> Any
| _ -> Overload
let gen_unifiable_typeff_of_size size =
let rec aux tl1 tl2 =
let ntl1 = (gen_random_type_eff ())::tl1
and ntl2 = (gen_random_type_eff ())::tl2 in
if unify_failed (f ntl1 ntl2) then
aux tl1 tl2
else
if List.length ntl1 = size then (ntl1,ntl2) else aux ntl1 ntl2
in
aux [] []
Erwan Jahier
committed
let (type_eff_list_to_string : Eff.type_ list -> string) =
fun tel ->
let str_l = List.map LicDump.string_of_type_eff4msg tel in
String.concat "*" str_l
let unit_test () =
assert( f [i;r;b] [i;r;b] = Equal );
assert( f [i;r;b] [i;r;a] = Unif b );
assert( f [i;r;a] [i;r;a] = Equal );
assert( f [i;a;b] [i;b;a] = Unif b );
assert( f [a] [o] = Unif o );
assert( unify_failed(f [a] [ array o 3]) );
assert( unify_failed(f [a] [ array a 3]) );
assert( unify_failed(f [o] [ array o 3]) );
assert( unify_failed(f [array i 4] [array o 3]));
assert( unify_failed(f [a;a] [r;b]));
Random.self_init ();
for i = 1 to 1000 do
let (tl1, tl2) = gen_unifiable_typeff_of_size (1+ Random.int 10) in
Verbose.print_string ~level:3 (
" ==> try UnifyType.proposition1 with lists " ^
Erwan Jahier
committed
(type_eff_list_to_string tl1) ^ " and " ^
(type_eff_list_to_string tl2) ^ "\n");
assert (proposition1 tl1 tl2)
done
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
(*****************************************)
(* Check that the interface and the implementation types profile
declarations are compatible.
The idea is to consider abstract types as alpha types. The problem
is that my unification algo works for one type variable only.
However, it is a very simple [*] case of unification with several
alpha var, that does not require the general unification
algorithm. By considering variables one by one, I can use [UnifyType.f].
[*] it is simpler than the general case because we can have alpha
var in the provide part.
*)
let (profile_is_compatible: node_key -> Lxm.t -> Eff.type_ list * Eff.type_ list ->
Eff.type_ list * Eff.type_ list -> Eff.type_ option) =
fun nk lxm (ins1, ins2) (outs1, outs2) ->
let to_any t = match t with (* consider abstract types as alpha types *)
| Abstract_type_eff(name, _) -> Any
| t -> t
in
let sot = LicDump.string_of_type_eff4msg in
let msg_prefix = ("provided node for " ^ (Ident.string_of_long (fst nk)) ^
" is not compatible with its implementation: ")
in
let apply_subst s t = try List.assoc t s with Not_found -> t in
let unif_types (subst,topt) t_prov t_body =
if t_body = Any || t_body = Overload then
(* Migth occur when a model is instanciated with a polymorphic operator,
such as Lustre::eq. In such a case, we obtain an Any or an Overload
from the implementation part ; the solution then is to replace that Any
type by the type of the provided part.
*)
(
assert (t_prov <> Any);
assert (t_prov <> Overload);
assert (topt = None || topt = Some t_prov); (* at most one type var per op *)
(subst, Some t_prov)
)
else
let t_prov = apply_subst subst t_prov and t_body = apply_subst subst t_body in
let nt_prov = to_any t_prov in
match f [nt_prov] [t_body] with
| Equal -> subst, topt
| Unif t ->
(match t_prov with
| Abstract_type_eff(_, tbody) ->
if tbody <> t && t_prov <> t_body then
let msg = msg_prefix ^ "\n*** "^(sot tbody)^" <> "^(sot t) in
raise(Errors.Compile_error (lxm, msg))
| _ -> ()
);
(t_prov,t)::subst, topt
| Ko(msg) -> raise(Errors.Compile_error (lxm, msg_prefix ^ msg))
in
let acc = [], None in
let _ =
if List.length ins1 <> List.length ins2 then
raise(Errors.Compile_error (lxm, msg_prefix ^ " wrong number of inputs"));
if List.length outs1 <> List.length outs2 then
raise(Errors.Compile_error (lxm, msg_prefix ^ " wrong number of outputs"))
in
let acc = List.fold_left2 unif_types acc ins1 ins2 in
let acc = List.fold_left2 unif_types acc outs1 outs2 in
snd acc