From 238c1e4a58aa06f9fe55594292f1b50d2f614847 Mon Sep 17 00:00:00 2001
From: Julien Signoles <julien.signoles@cea.fr>
Date: Wed, 30 Dec 2015 16:34:58 +0100
Subject: [PATCH] new type system (still unused, but compiled)

---
 src/plugins/e-acsl/Makefile.in    |   1 +
 src/plugins/e-acsl/interval.ml    |   2 +
 src/plugins/e-acsl/interval.mli   |   1 +
 src/plugins/e-acsl/new_typing.ml  | 379 ++++++++++++++++++++++++++++++
 src/plugins/e-acsl/new_typing.mli |  68 ++++++
 5 files changed, 451 insertions(+)
 create mode 100644 src/plugins/e-acsl/new_typing.ml
 create mode 100644 src/plugins/e-acsl/new_typing.mli

diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in
index 2cdb78a96a9..48159fd39ff 100644
--- a/src/plugins/e-acsl/Makefile.in
+++ b/src/plugins/e-acsl/Makefile.in
@@ -62,6 +62,7 @@ PLUGIN_CMO:= local_config \
 	env \
 	interval \
 	typing \
+	new_typing \
 	quantif \
 	translate \
 	loops \
diff --git a/src/plugins/e-acsl/interval.ml b/src/plugins/e-acsl/interval.ml
index 4b7384905d8..c5db5e55354 100644
--- a/src/plugins/e-acsl/interval.ml
+++ b/src/plugins/e-acsl/interval.ml
@@ -67,6 +67,8 @@ let interv_of_unknown_block =
     (let u = Integer.div (Bit_utils.max_bit_address ()) Integer.eight in
      make Integer.zero u)
 
+let add i n = { lower = Integer.add i.lower n; upper = Integer.add i.upper n }
+
 (* intervals as a lattice *)
 
 let meet i1 i2 =
diff --git a/src/plugins/e-acsl/interval.mli b/src/plugins/e-acsl/interval.mli
index 95176f8958b..f6a906c3c2b 100644
--- a/src/plugins/e-acsl/interval.mli
+++ b/src/plugins/e-acsl/interval.mli
@@ -36,6 +36,7 @@ end
 
 val interv_of_typ: Cil_types.typ -> t
 
+val add: t -> Integer.t -> t
 val join: t -> t -> t
 val meet: t -> t -> t
 
diff --git a/src/plugins/e-acsl/new_typing.ml b/src/plugins/e-acsl/new_typing.ml
new file mode 100644
index 00000000000..f30a8c15d30
--- /dev/null
+++ b/src/plugins/e-acsl/new_typing.ml
@@ -0,0 +1,379 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of the Frama-C's E-ACSL plug-in.                    *)
+(*                                                                        *)
+(*  Copyright (C) 2012-2015                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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 Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file license/LGPLv2.1).             *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+(* Implement Figure 4 of J. Signoles' JFLA'15 paper "Rester statique pour
+   devenir plus rapide, plus précis et plus mince". *)
+
+let dkey = Options.dkey_typing
+
+let compute_quantif_guards_ref
+    : (predicate named -> logic_var list -> predicate named ->
+       (term * relation * logic_var * relation * term) list) ref
+    = Extlib.mk_fun "compute_quantif_guards_ref"
+
+(******************************************************************************)
+(** Datatypes and memoization *)
+(******************************************************************************)
+
+type integer_ty =
+  | Gmp
+  | C_type of ikind
+
+let c_int = C_type IInt
+
+include Datatype.Make
+(struct
+  type t = integer_ty
+  let name = "E_ACSL.Typing.t"
+  let reprs = [ Gmp; c_int ]
+  include Datatype.Undefined
+
+  let compare ty1 ty2 = match ty1, ty2 with
+    | C_type i1, C_type i2 ->
+      if i1 = i2 then 0
+      else if Cil.intTypeIncluded i1 i2 then -1 else 1
+    | C_type _, Gmp -> -1
+    | Gmp, C_type _ -> 1
+    | Gmp, Gmp -> 0
+
+  let equal = Datatype.from_compare
+
+  let pretty fmt = function
+    | Gmp -> Format.fprintf fmt "gmp"
+    | C_type k -> Printer.pp_ikind fmt k
+ end)
+
+type computed_info =
+    { ty: t;  (* type required for the term *)
+      cast: t option; (* if not [None], type of the context which the term
+                         must be casted to. If [None], no cast needed. *)
+    }
+
+module Memo: sig
+  val memo: (term -> computed_info) -> term -> computed_info
+  val get: term -> computed_info
+  val clear: unit -> unit
+end = struct
+
+  module H = Hashtbl.Make(struct
+    type t = term
+    let equal (t1:term) t2 = t1 == t2
+    let hash = Cil_datatype.Term.hash
+  end)
+
+  let tbl = H.create 97
+
+  let get t =
+    try H.find tbl t
+    with Not_found ->
+      Options.fatal "type of term '%a' was never computed." Printer.pp_term t
+
+  let memo f t =
+    try H.find tbl t
+    with Not_found ->
+      let x = f t in
+      H.add tbl t x;
+      x
+
+  let clear () = H.clear tbl
+
+end
+
+(******************************************************************************)
+(** {2 Coercion rules} *)
+(******************************************************************************)
+
+(* Compute the smallest type (bigger than [int]) which can contain the whole
+   interval. It is the \theta operator of the JFLA's paper. *)
+let ty_of_interv i =
+  let open Interval in
+  let is_pos = Integer.ge i.lower Integer.zero in
+  try
+    let lkind = Cil.intKindForValue i.lower is_pos in
+    let ukind = Cil.intKindForValue i.upper is_pos in
+    let kind = if Cil.intTypeIncluded lkind ukind then ukind else lkind in
+    (* int whenever possible to prevent superfluous casts in the generated
+       code *)
+    if Cil.intTypeIncluded kind IInt then c_int else C_type kind
+  with Cil.Not_representable ->
+    Gmp
+
+let coerce ~ctx ty =
+  if compare ty ctx = 1 then begin
+    (* type larger than the expected context,
+       so we must introduce an explicit cast *)
+    { ty = ty; cast = Some ctx }
+  end else
+    (* only add an explicit cast if the context is [Gmp] and [ty] is not *)
+    if ctx = Gmp && ty <> Gmp then { ty = ty; cast = Some Gmp }
+    else { ty = ty; cast = None }
+
+(******************************************************************************)
+(** {2 Typing} *)
+(******************************************************************************)
+
+exception Not_an_integer
+
+let rec type_term env ~ctx t =
+  let infer t =
+    Cil.CurrentLoc.set t.term_loc;
+    match t.term_node with
+    | TConst (Integer _ | LChr _)
+    | TLval _
+    | TSizeOf _
+    | TSizeOfE _
+    | TSizeOfStr _
+    | TAlignOf _
+    | TAlignOfE _
+    | TBinOp (MinusPP, _, _)
+    | Tblock_length(_, _)
+    | Tnull  ->
+      let i = Interval.infer env t in
+      ty_of_interv i
+
+    | TUnOp (_, t') ->
+      let i = Interval.infer env t in
+      let i' = Interval.infer env t' in
+      let ctx = ty_of_interv (Interval.join i i') in
+      ignore (type_term env ~ctx t');
+      ctx
+
+    | TBinOp((PlusA | MinusA | Mult | Div | Mod | Shiftlt | Shiftrt), t1, t2) ->
+      let i = Interval.infer env t in
+      let i1 = Interval.infer env t1 in
+      let i2 = Interval.infer env t2 in
+      let ctx = ty_of_interv (Interval.join i (Interval.join i1 i2)) in
+      ignore (type_term env ~ctx t1);
+      ignore (type_term env ~ctx t2);
+      ctx
+    | TBinOp ((Lt | Gt | Le | Ge | Eq | Ne), t1, t2) ->
+      assert (compare ctx c_int >= 1);
+      let i1 = Interval.infer env t1 in
+      let i2 = Interval.infer env t2 in
+      let ctx = ty_of_interv (Interval.join i1 i2) in
+      ignore (type_term env ~ctx t1);
+      ignore (type_term env ~ctx t2);
+      ctx
+    | TBinOp ((LAnd | LOr), t1, t2) ->
+      assert (compare ctx c_int >= 1);
+      let i1 = Interval.infer env t1 in
+      let i2 = Interval.infer env t2 in
+      (* both operands fit in an int. *)
+      ignore (type_term env ~ctx:c_int t1);
+      ignore (type_term env ~ctx:c_int t2);
+      ty_of_interv (Interval.join i1 i2)
+
+    | TBinOp (BAnd, _, _) -> Error.not_yet "bitwise and"
+    | TBinOp (BXor, _, _) -> Error.not_yet "bitwise xor"
+    | TBinOp (BOr, _, _) -> Error.not_yet "bitwise or"
+
+    | TCastE _
+    | TCoerce _ ->
+      let i = Interval.infer env t in
+      (* nothing to do more: [i] is already more precise than what we could
+         infer from the arguments of the cast. Also, do not type the internal
+         term: possibly it is not even an integer *)
+      ty_of_interv i
+    | Tif (t1, t2, t3) ->
+      ignore (type_term env ~ctx:c_int t1);
+      let i = Interval.infer env t in
+      let i2 = Interval.infer env t2 in
+      let i3 = Interval.infer env t3 in
+      let ctx = ty_of_interv (Interval.join i (Interval.join i2 i3)) in
+      ignore (type_term env ~ctx t2);
+      ignore (type_term env ~ctx t3);
+      ctx
+    | Tat (t, _) -> (type_term env ~ctx t).ty
+    | TLogic_coerce (_, t) -> (type_term env ~ctx t).ty
+    | TCoerceE (t1, t2) ->
+      let i = Interval.infer env t in
+      let i1 = Interval.infer env t1 in
+      let i2 = Interval.infer env t2 in
+      let ty = ty_of_interv (Interval.join i (Interval.join i1 i2)) in
+      ignore (type_term env ty t1);
+      ignore (type_term env ty t2);
+      ty
+
+    | Tapp (_,_,_) -> Error.not_yet "logic function application"
+    | Tunion _ -> Error.not_yet "tset union"
+    | Tinter _ -> Error.not_yet "tset intersection"
+    | Tcomprehension (_,_,_) -> Error.not_yet "tset comprehension"
+    | Trange (_,_) -> Error.not_yet "trange"
+    | Tlet (_,_) -> Error.not_yet "let binding"
+
+    | TConst (LStr _ | LWStr _ | LReal _ | LEnum _)
+    | TBinOp (PlusPI,_,_)
+    | TBinOp (IndexPI,_,_)
+    | TBinOp (MinusPI,_,_)
+    | TAddrOf _
+    | TStartOf _
+    | Toffset _
+    | Tlambda (_,_)
+    | TDataCons (_,_)
+    | Tbase_addr (_,_)
+    | TUpdate (_,_,_)
+    | Ttypeof _
+    | Ttype _
+    | Tempty_set  -> raise Not_an_integer
+  in
+  Memo.memo (fun t -> let ty = infer t in coerce ~ctx ty) t
+
+(* TODO: maybe useful to memoize the result (only for relations)? *)
+let rec type_predicate_named env p =
+  Cil.CurrentLoc.set p.loc;
+  let ty = match p.content with
+    | Pfalse | Ptrue -> c_int
+    | Papp _ -> Error.not_yet "logic function application"
+    | Pseparated _ -> Error.not_yet "\\separated"
+    | Pdangling _ -> Error.not_yet "\\dangling"
+    | Prel(_, t1, t2) ->
+      let i1 = Interval.infer env t1 in
+      let i2 = Interval.infer env t2 in
+      let i = Interval.join i1 i2 in
+      let ctx = ty_of_interv i in
+      ignore (type_term env ~ctx t1);
+      ignore (type_term env ~ctx t2);
+      ctx
+    | Pand(p1, p2)
+    | Por(p1, p2)
+    | Pxor(p1, p2)
+    | Pimplies(p1, p2)
+    | Piff(p1, p2) ->
+      ignore (type_predicate_named env p1);
+      ignore (type_predicate_named env p2);
+      c_int
+    | Pnot p ->
+      ignore (type_predicate_named env p);
+      c_int
+    | Pif(t, p1, p2) ->
+      ignore (type_term env ~ctx:c_int t);
+      ignore (type_predicate_named env p1);
+      ignore (type_predicate_named env p2);
+      c_int
+    | Plet _ -> Error.not_yet "let _ = _ in _"
+    | Pforall(bounded_vars, { content = Pimplies(hyps, goal) })
+    | Pexists(bounded_vars, { content = Pand(hyps, goal) }) ->
+      let guards = !compute_quantif_guards_ref p bounded_vars hyps in
+      let env =
+        List.fold_left
+          (fun env (t1, r1, x, r2, t2) ->
+            let i1 = Interval.infer env t1 in
+            let i1 = match r1 with
+              | Rlt -> Interval.add i1 Integer.one
+              | Rle -> i1
+              | _ -> assert false
+            in
+            let i2 = Interval.infer env t2 in
+            (* add one to [i2], since we increment the loop counter one more
+               time before going out the loop. *)
+            let i2 = match r2 with
+              | Rlt -> i2
+              | Rle -> Interval.add i2 Integer.one
+              | _ -> assert false
+            in
+            let i = Interval.join i1 i2 in
+            let ctx = ty_of_interv i in
+            ignore (type_term env ~ctx t1);
+            ignore (type_term env ~ctx t2);
+            Interval.Env.add x i env)
+          env
+          guards
+      in
+      type_predicate_named env goal
+
+    | Pforall _ -> Error.not_yet "unguarded \\forall quantification"
+    | Pexists _ -> Error.not_yet "unguarded \\exists quantification"
+    | Pat(p, _) -> type_predicate_named env p
+    | Pfresh _ -> Error.not_yet "\\fresh"
+    | Psubtype _ -> Error.not_yet "subtyping relation" (* Jessie specific *)
+    | Pinitialized _
+    | Pfreeable _
+    | Pallocable _
+    | Pvalid _
+    | Pvalid_read _ ->
+      c_int
+  in
+  (coerce ~ctx:c_int ty).ty
+
+let type_named_predicate ?(must_clear=true) p =
+  if not (Options.Gmp_only.get ()) then begin
+    Options.feedback ~dkey ~level:3 "typing predicate %a."
+      Printer.pp_predicate_named p;
+    if must_clear then Memo.clear ();
+    ignore (type_predicate_named Interval.Env.empty p)
+  end
+
+let generic_typ get_ty t =
+  Cil.CurrentLoc.set t.term_loc;
+  if Options.Gmp_only.get () then
+    (match t.term_type with
+    | Linteger -> Mpz.t ()
+    | Ctype ty when Cil.isIntegralType ty -> Mpz.t ()
+    | Ctype ty -> ty
+    | Ltype _ as ty when Logic_const.is_boolean_type ty -> Mpz.t ()
+    | Ltype _ -> Error.not_yet "typing of user-defined logic type"
+    | Lvar _ -> Error.not_yet "type variable"
+    | Lreal -> TFloat(FLongDouble, [])
+    | Larrow _ -> Error.not_yet "functional type")
+  else
+    let info = Memo.get t in
+    match get_ty info with
+    | Gmp -> Mpz.t ()
+    | C_type ik -> TInt(ik, [])
+
+let typ_of_term = generic_typ (fun i -> i.ty)
+
+let cast_of_term t =
+  try
+    Some
+      (generic_typ
+         (fun i ->
+           match i.cast with None -> raise Exit | Some ty -> ty)
+         t)
+  with Exit ->
+    None
+
+let clear = Memo.clear
+
+(******************************************************************************)
+(** {2 Other typing-related functions} *)
+(******************************************************************************)
+
+let is_representable n k =
+  if Options.Gmp_only.get () then
+    match k with
+    | IBool | IChar | ISChar | IUChar | IInt | IUInt | IShort | IUShort
+    | ILong | IULong ->
+      true
+    | ILongLong | IULongLong ->
+      (* no GMP initializer from a long long *)
+      false
+  else
+    Integer.ge n Integer.min_int64 && Integer.le n Integer.max_int64
+
+(*
+Local Variables:
+compile-command: "make"
+End:
+*)
diff --git a/src/plugins/e-acsl/new_typing.mli b/src/plugins/e-acsl/new_typing.mli
new file mode 100644
index 00000000000..4bd4aeb94c3
--- /dev/null
+++ b/src/plugins/e-acsl/new_typing.mli
@@ -0,0 +1,68 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of the Frama-C's E-ACSL plug-in.                    *)
+(*                                                                        *)
+(*  Copyright (C) 2012-2015                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It 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 Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file license/LGPLv2.1).             *)
+(*                                                                        *)
+(**************************************************************************)
+
+open Cil_types
+
+(******************************************************************************)
+(** {2 Typing} *)
+(******************************************************************************)
+
+type integer_ty =
+  | Gmp
+  | C_type of ikind
+
+val type_named_predicate: ?must_clear:bool -> predicate named -> unit
+(** Compute the type of each term of the given predicate. *)
+
+val typ_of_term: term -> typ
+(** Get the type of the given term. {!type_named_predicate} must already have
+    been called on the englobing predicate. *)
+
+val cast_of_term: term -> typ option
+(** Get the type which the given term must be converted to after its translation
+    (if any). {!type_named_predicate} must already have been called on the
+    englobing predicate. *)
+
+val clear: unit -> unit
+(** Remove all the previously computed types. *)
+
+(******************************************************************************)
+(** {2 Other typing-related functions} *)
+(******************************************************************************)
+
+val is_representable: Integer.t -> ikind -> bool
+(** Is the given constant representable in the given kind? *)
+
+(******************************************************************************)
+(** {2 Internal stuff} *)
+(******************************************************************************)
+
+val compute_quantif_guards_ref
+    : (predicate named -> logic_var list -> predicate named ->
+       (term * relation * logic_var * relation * term) list) ref
+(** Forward reference. *)
+
+(*
+Local Variables:
+compile-command: "make"
+End:
+*)
-- 
GitLab