From d5f3afc80e2a0b130e5ecd0ef8bc9244ff8aa2be Mon Sep 17 00:00:00 2001
From: Fonenantsoa Maurica <maurica.fonenantsoa@gmail.com>
Date: Thu, 15 Nov 2018 13:17:07 +0100
Subject: [PATCH] Fixes Issue 69: unsound translation when bounds for
 quantified variables are bigger than their types

---
 src/plugins/e-acsl/VERSION                    |  2 +-
 src/plugins/e-acsl/doc/Changelog              |  1 +
 src/plugins/e-acsl/interval.ml                |  1 +
 src/plugins/e-acsl/interval.mli               |  1 +
 src/plugins/e-acsl/loops.ml                   | 88 +++++++++++++++++--
 src/plugins/e-acsl/loops.mli                  |  3 +
 src/plugins/e-acsl/misc.ml                    |  4 +
 src/plugins/e-acsl/misc.mli                   |  3 +
 src/plugins/e-acsl/tests/bts/issue69.c        | 12 +++
 .../e-acsl/tests/bts/oracle/gen_issue69.c     | 69 +++++++++++++++
 .../tests/bts/oracle/issue69.res.oracle       |  3 +
 src/plugins/e-acsl/translate.ml               |  1 +
 12 files changed, 181 insertions(+), 7 deletions(-)
 create mode 100644 src/plugins/e-acsl/tests/bts/issue69.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle/issue69.res.oracle

diff --git a/src/plugins/e-acsl/VERSION b/src/plugins/e-acsl/VERSION
index 93cc5dffbe7..06088970bd5 100644
--- a/src/plugins/e-acsl/VERSION
+++ b/src/plugins/e-acsl/VERSION
@@ -1 +1 @@
-18.0+dev
+18.0-beta
diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog
index 83371e18524..9897d0a71f4 100644
--- a/src/plugins/e-acsl/doc/Changelog
+++ b/src/plugins/e-acsl/doc/Changelog
@@ -23,6 +23,7 @@
 Plugin E-ACSL 18.0 (Argon)
 ##########################
 
+-* E-ACSL       [2018/11/13] Fix typing bug for quantified variables.
 -* runtime      [2018/11/13] Fix bug #!2405 about memory initialization
                 in presence of GCC constructors.
 -* E-ACSL       [2018/10/23] Fix bug #2406 about monitoring of variables
diff --git a/src/plugins/e-acsl/interval.ml b/src/plugins/e-acsl/interval.ml
index 0f68e4bc8d0..005ea7a7c26 100644
--- a/src/plugins/e-acsl/interval.ml
+++ b/src/plugins/e-acsl/interval.ml
@@ -61,6 +61,7 @@ module Env = struct
   let clear () = Logic_var.Hashtbl.clear tbl
   let add = Logic_var.Hashtbl.add tbl
   let remove = Logic_var.Hashtbl.remove tbl
+  let replace = Logic_var.Hashtbl.replace tbl
   let find = Logic_var.Hashtbl.find tbl
 end
 
diff --git a/src/plugins/e-acsl/interval.mli b/src/plugins/e-acsl/interval.mli
index 3d5fd8b3be2..958f24409dd 100644
--- a/src/plugins/e-acsl/interval.mli
+++ b/src/plugins/e-acsl/interval.mli
@@ -66,6 +66,7 @@ module Env: sig
   val clear: unit -> unit
   val add: Cil_types.logic_var -> Ival.t -> unit
   val remove: Cil_types.logic_var -> unit
+  val replace: Cil_types.logic_var -> Ival.t -> unit
 end
 
 (* ************************************************************************** *)
diff --git a/src/plugins/e-acsl/loops.ml b/src/plugins/e-acsl/loops.ml
index 2d0551e2996..ea01ce00c63 100644
--- a/src/plugins/e-acsl/loops.ml
+++ b/src/plugins/e-acsl/loops.ml
@@ -31,6 +31,10 @@ let translate_named_predicate_ref
   : (kernel_function -> Env.t -> predicate -> Env.t) ref
   = Extlib.mk_fun "translate_named_predicate_ref"
 
+let named_predicate_ref
+  : (kernel_function -> Env.t -> predicate -> exp * Env.t) ref
+  = Extlib.mk_fun "named_predicate_ref"
+
 let term_to_exp_ref
   : (kernel_function -> Env.t -> term -> exp * Env.t) ref
   = Extlib.mk_fun "term_to_exp_ref"
@@ -98,12 +102,74 @@ let preserve_invariant prj env kf stmt = match stmt.skind with
 (**************************** Nested loops ********************************)
 (**************************************************************************)
 
+(* It could happen that the bounds provided for a quantifier [lv] are bigger
+  than its type. [bounds_for_small_type] handles such cases
+  and provides smaller bounds whenever possible.
+  Let B be the inferred interval and R the range of [lv.typ]
+  - Case 1: B \subseteq R
+    Example: [\forall unsigned char c; 4 <= c <= 100 ==> 0 <= c <= 255]
+    Return: B
+  - Case 2: B \not\subseteq R and the bounds of B are inferred exactly
+    Example: [\forall unsigned char c; 4 <= c <= 300 ==> 0 <= c <= 255]
+    Return: B \intersect R
+  - Case 3: B \not\subseteq R and the bounds of B are NOT inferred exactly
+    Example: [\let m = n > 0 ? 4 : 341; \forall char u; 1 < u < m ==> u > 0]
+    Return: R with a guard guaranteeing that [lv] does not overflow *)
+let bounds_for_small_type ~loc (t1, lv, t2) =
+  match lv.lv_type with
+  | Linteger ->
+    t1, t2, None
+  | Ctype ty ->
+    let i1 = Interval.infer t1 in
+    let i2 = Interval.infer t2 in
+    let i =
+      (* Ival.join would NOT be correct here:
+         Eg: (Ival.join [-3..-3] [300..300]) gives {-3, 300}
+             but NOT [-3..300] *)
+      Ival.inject_range (Ival.min_int i1) (Ival.max_int i2)
+    in
+    let ity = Interval.interv_of_typ ty in
+    if Ival.is_included i ity then
+      (* case 1 *)
+      t1, t2, None
+    else if Ival.is_singleton_int i1 && Ival.is_singleton_int i2 then begin
+      (* case 2 *)
+      let i = Ival.meet i ity in
+      (* now we potentially have a better interval for [lv]
+         ==> update the binding *)
+      Interval.Env.replace lv i;
+      (* the smaller bounds *)
+      let min, max = Misc.finite_min_and_max i in
+      let t1 = Logic_const.tint ~loc min in
+      let t2 = Logic_const.tint ~loc max in
+      let ctx = Typing.integer_ty_of_typ ty in
+      (* we are assured that we will not have a GMP,
+        once again because we intersected with [ity] *)
+      Typing.type_term ~use_gmp_opt:false ~ctx t1;
+      Typing.type_term ~use_gmp_opt:false ~ctx t2;
+      t1, t2, None
+    end else
+      (* case 3 *)
+      let min, max = Misc.finite_min_and_max ity in
+      let guard_lower = Logic_const.tint ~loc min in
+      let guard_upper = Logic_const.tint ~loc max in
+      let lv_term = Logic_const.tvar ~loc lv in
+      let guard_lower = Logic_const.prel ~loc (Rle, guard_lower, lv_term) in
+      let guard_upper = Logic_const.prel ~loc (Rle, lv_term, guard_upper) in
+      let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in
+      t1, t2, Some guard
+  | Ltype _ | Lvar _ | Lreal | Larrow _ ->
+    Options.abort "quantification over non-integer type is not part of E-ACSL"
+
 let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars =
   let term_to_exp = !term_to_exp_ref in
   match lscope_vars with
   | [] ->
     mk_innermost_block env
   | Lscope.Lvs_quantif(t1, rel1, logic_x, rel2, t2) :: lscope_vars' ->
+    let t1, t2, guard_for_small_type_opt =
+      bounds_for_small_type ~loc (t1, logic_x, t2)
+    in
     let ctx =
       let ty1 = Typing.get_integer_ty t1 in
       let ty2 = Typing.get_integer_ty t2 in
@@ -198,8 +264,6 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars =
     let guard = block_to_stmt guard_blk in
     (* increment the loop counter [x++];
        previous typing ensures that [x++] fits type [ty] *)
-    (* TODO: should check that it does not overflow in the case of the type
-       of the loop variable is __declared__ too small. *)
     let tlv_one = t_plus_one ~ty:ctx_one tlv in
     let incr, env = term_to_exp kf (Env.push env) tlv_one in
     let next_blk, env = Env.pop_and_get
@@ -208,20 +272,32 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars =
       ~global_clear:false
       Env.Middle
     in
-    (* remove logic binding now that the block is constructed *)
-    let env = Env.Logic_binding.remove env logic_x in
     (* generate the whole loop *)
-    let start = block_to_stmt init_blk in
     let next = block_to_stmt next_blk in
+    let stmts, env = match guard_for_small_type_opt with
+      | None ->
+        guard :: body @ [ next ], env
+      | Some p ->
+        let e, env = !named_predicate_ref kf (Env.push env) p in
+        let stmt, env =
+          Misc.mk_e_acsl_guard ~reverse:true Misc.RTE kf e p, env
+        in
+        let b, env = Env.pop_and_get env stmt ~global_clear:false Env.After in
+        let guard_for_small_type = Cil.mkStmt ~valid_sid:true (Block b) in
+        guard_for_small_type :: guard :: body @ [ next ], env
+    in
+    let start = block_to_stmt init_blk in
     let stmt = mkStmt
       ~valid_sid:true
       (Loop(
         [],
-        mkBlock (guard :: body @ [ next ]),
+        mkBlock stmts,
         loc,
         None,
         Some break_stmt))
     in
+    (* remove logic binding before returning *)
+    let env = Env.Logic_binding.remove env logic_x in
     [ start ;  stmt ], env
   | Lscope.Lvs_let(lv, t) :: lscope_vars' ->
     let ty = Typing.get_typ t in
diff --git a/src/plugins/e-acsl/loops.mli b/src/plugins/e-acsl/loops.mli
index 3a5dfa38146..27a0596d6be 100644
--- a/src/plugins/e-acsl/loops.mli
+++ b/src/plugins/e-acsl/loops.mli
@@ -62,6 +62,9 @@ val mk_nested_loops:
 val translate_named_predicate_ref:
   (kernel_function -> Env.t -> predicate -> Env.t) ref
 
+val named_predicate_ref:
+  (kernel_function -> Env.t -> predicate -> exp * Env.t) ref
+
 val term_to_exp_ref:
   (kernel_function -> Env.t -> term -> exp * Env.t) ref
 
diff --git a/src/plugins/e-acsl/misc.ml b/src/plugins/e-acsl/misc.ml
index 55ef595b98f..ee27e0f4521 100644
--- a/src/plugins/e-acsl/misc.ml
+++ b/src/plugins/e-acsl/misc.ml
@@ -311,6 +311,10 @@ let mk_ptr_sizeof typ loc =
   | TPtr (t', _) -> Cil.new_exp ~loc (SizeOf t')
   | _ -> assert false
 
+let finite_min_and_max i = match Ival.min_and_max i with
+  | Some min, Some max -> min, max
+  | None, _ | _, None -> assert false
+
 (*
 Local Variables:
 compile-command: "make"
diff --git a/src/plugins/e-acsl/misc.mli b/src/plugins/e-acsl/misc.mli
index c78ce824241..8f12597aa04 100644
--- a/src/plugins/e-acsl/misc.mli
+++ b/src/plugins/e-acsl/misc.mli
@@ -124,6 +124,9 @@ val mk_ptr_sizeof: typ -> location -> exp
 (* [mk_ptr_sizeof ptr_typ loc] takes the pointer typ [ptr_typ] that points
    to a [typ] typ and returns [sizeof(typ)]. *)
 
+val finite_min_and_max: Ival.t -> Integer.t * Integer.t
+(* [finite_min_and_max i] takes the finite ival [i] and returns its bounds *)
+
 (*
 Local Variables:
 compile-command: "make"
diff --git a/src/plugins/e-acsl/tests/bts/issue69.c b/src/plugins/e-acsl/tests/bts/issue69.c
new file mode 100644
index 00000000000..c571c92c749
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/issue69.c
@@ -0,0 +1,12 @@
+/* run.config
+   COMMENT: typing bug of Issue 69
+*/
+
+int main(void) {
+  /*@ assert \forall unsigned char c; 4 <= c <= 300 ==> 0 <= c <= 255; */ ;
+
+  int n = 5;
+  /*@ assert
+        \let m = n > 0 ? 4 : 341;
+        \forall char u; 1 < u < m ==> u > 0; */ ;
+}
diff --git a/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c b/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c
new file mode 100644
index 00000000000..a25febaa04e
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c
@@ -0,0 +1,69 @@
+/* Generated by Frama-C */
+#include "stdio.h"
+#include "stdlib.h"
+int main(void)
+{
+  int __retres;
+  __e_acsl_memory_init((int *)0,(char ***)0,(size_t)8);
+  /*@ assert ∀ unsigned char c; 4 ≤ c ≤ 300 ⇒ 0 ≤ c ≤ 255; */
+  {
+    int __gen_e_acsl_forall;
+    int __gen_e_acsl_c;
+    __gen_e_acsl_forall = 1;
+    __gen_e_acsl_c = (unsigned char)4;
+    while (1) {
+      if (__gen_e_acsl_c <= 255) ; else break;
+      {
+        int __gen_e_acsl_and;
+        if (0 <= __gen_e_acsl_c) __gen_e_acsl_and = __gen_e_acsl_c <= 255;
+        else __gen_e_acsl_and = 0;
+        if (__gen_e_acsl_and) ;
+        else {
+          __gen_e_acsl_forall = 0;
+          goto e_acsl_end_loop1;
+        }
+      }
+      __gen_e_acsl_c ++;
+    }
+    e_acsl_end_loop1: ;
+    __e_acsl_assert(__gen_e_acsl_forall,(char *)"Assertion",(char *)"main",
+                    (char *)"\\forall unsigned char c; 4 <= c <= 300 ==> 0 <= c <= 255",
+                    6);
+  }
+  int n = 5;
+  /*@ assert \let m = n > 0? 4: 341; ∀ char u; 1 < u < m ⇒ u > 0; */
+  {
+    int __gen_e_acsl_m;
+    int __gen_e_acsl_if;
+    int __gen_e_acsl_forall_2;
+    int __gen_e_acsl_u;
+    if (n > 0) __gen_e_acsl_if = 4; else __gen_e_acsl_if = 341;
+    __gen_e_acsl_m = __gen_e_acsl_if;
+    __gen_e_acsl_forall_2 = 1;
+    __gen_e_acsl_u = (char)1 + 1;
+    while (1) {
+      {
+        int __gen_e_acsl_and_2;
+        if (-128 <= __gen_e_acsl_u) __gen_e_acsl_and_2 = __gen_e_acsl_u <= 127;
+        else __gen_e_acsl_and_2 = 0;
+        __e_acsl_assert(__gen_e_acsl_and_2,(char *)"RTE",(char *)"main",
+                        (char *)"-128 <= u <= 127",11);
+      }
+      if (__gen_e_acsl_u < __gen_e_acsl_m) ; else break;
+      if (__gen_e_acsl_u > 0) ;
+      else {
+        __gen_e_acsl_forall_2 = 0;
+        goto e_acsl_end_loop2;
+      }
+      __gen_e_acsl_u ++;
+    }
+    e_acsl_end_loop2: ;
+    __e_acsl_assert(__gen_e_acsl_forall_2,(char *)"Assertion",(char *)"main",
+                    (char *)"\\let m = n > 0? 4: 341;\n\\forall char u; 1 < u < m ==> u > 0",
+                    10);
+  }
+  __retres = 0;
+  return __retres;
+}
+
+
diff --git a/src/plugins/e-acsl/tests/bts/oracle/issue69.res.oracle b/src/plugins/e-acsl/tests/bts/oracle/issue69.res.oracle
new file mode 100644
index 00000000000..a3f1846d9e3
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle/issue69.res.oracle
@@ -0,0 +1,3 @@
+[e-acsl] beginning translation.
+[e-acsl] translation done in project "e-acsl".
+[eva:alarm] tests/bts/issue69.c:10: Warning: assertion got status unknown.
diff --git a/src/plugins/e-acsl/translate.ml b/src/plugins/e-acsl/translate.ml
index 5d44cafba2c..089c2b98ab3 100644
--- a/src/plugins/e-acsl/translate.ml
+++ b/src/plugins/e-acsl/translate.ml
@@ -840,6 +840,7 @@ let named_predicate_to_exp ?name kf env p =
 let () =
   Loops.term_to_exp_ref := term_to_exp;
   Loops.translate_named_predicate_ref := translate_named_predicate;
+  Loops.named_predicate_ref := named_predicate_to_exp;
   Quantif.predicate_to_exp_ref := named_predicate_to_exp;
   At_with_lscope.term_to_exp_ref := term_to_exp;
   At_with_lscope.predicate_to_exp_ref := named_predicate_to_exp;
-- 
GitLab