From e3dfc90e6910caa9dd639f668abdb83a5daea21e Mon Sep 17 00:00:00 2001
From: Julien Signoles <julien.signoles@cea.fr>
Date: Mon, 27 Oct 2014 10:00:38 +0100
Subject: [PATCH] [translation] add a missing cast when an integer is used in a
 floating point/real context (was revealed by a Value's change, and now
 reveals a bug with == over reals)

---
 src/plugins/e-acsl/TODO                       |  3 +
 src/plugins/e-acsl/doc/Changelog              |  3 +
 .../oracle/bts1307.1.res.oracle               |  5 --
 .../e-acsl-runtime/oracle/bts1307.res.oracle  | 17 +----
 .../tests/e-acsl-runtime/oracle/gen_bts1307.c |  4 +-
 .../e-acsl-runtime/oracle/gen_bts13072.c      |  3 +-
 src/plugins/e-acsl/translate.ml               | 74 +++++++++++++------
 7 files changed, 66 insertions(+), 43 deletions(-)

diff --git a/src/plugins/e-acsl/TODO b/src/plugins/e-acsl/TODO
index b7e35cba3ac..8a372299c04 100644
--- a/src/plugins/e-acsl/TODO
+++ b/src/plugins/e-acsl/TODO
@@ -81,6 +81,9 @@
 - interprétation des tableaux logiques
 [Dillon] Windows
 - \valid(p) is not true if p is a const pointer (depend on -const-readonly)
+- utiliser == avec des real en arguments : c'est convertit en une égalité sur 
+des floating points, qui ne fait notoirement pas ce qu'on veut 
+(voir tests/e-acsl-runtime/bts1307.i).
 
 [to be check]: no call to full_init or initialize for the assigned result of a 
 function call
diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog
index 41b61fc71f4..776b0996f4f 100644
--- a/src/plugins/e-acsl/doc/Changelog
+++ b/src/plugins/e-acsl/doc/Changelog
@@ -15,6 +15,9 @@
 #   E-ACSL: the Whole E-ACSL plug-in
 ###############################################################################
 
+-* E-ACSL       [2014/10/27] Add a missing cast when translating an integral
+	        type used in a floating point/real context in an annotation.
+
 ########################
 Plugin E-ACSL 0.4.1 Neon
 ########################
diff --git a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.1.res.oracle b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.1.res.oracle
index e97a4bc14d2..0a07b7eceec 100644
--- a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.1.res.oracle
+++ b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.1.res.oracle
@@ -50,11 +50,6 @@ FRAMAC_SHARE/e-acsl/e_acsl_gmp.h:166:[value] Function __gmpz_tdiv_q: preconditio
 FRAMAC_SHARE/e-acsl/e_acsl_gmp.h:198:[value] Function __gmpz_get_ui: precondition got status valid.
 [value] using specification for function __valid_read
 [value] user error: type long double not implemented. Using double instead
-tests/e-acsl-runtime/bts1307.i:13:[kernel] warning: non-finite long double value ([--..--]):
-                  assert \is_finite((long double)(__e_acsl_4**__e_acsl_at_3));
-tests/e-acsl-runtime/bts1307.i:13:[kernel] warning: non-finite long double value ([--..--]):
-                  assert
-                  \is_finite((long double)(5-(long double)((long double)(__e_acsl_4**__e_acsl_at_3)*0.4)));
 [value] using specification for function __gmpz_clear
 FRAMAC_SHARE/e-acsl/e_acsl_gmp.h:117:[value] Function __gmpz_clear: precondition got status valid.
 tests/e-acsl-runtime/bts1307.i:13:[value] Function __e_acsl_foo, behavior OverEstimate_Motoring: postcondition got status valid.
diff --git a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.res.oracle b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.res.oracle
index 19a39a5b1a3..8ced7d9de92 100644
--- a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.res.oracle
+++ b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/bts1307.res.oracle
@@ -36,20 +36,7 @@ tests/e-acsl-runtime/bts1307.i:9:[value] Function foo: precondition got status v
 tests/e-acsl-runtime/bts1307.i:13:[value] Function foo, behavior OverEstimate_Motoring: postcondition got status valid.
 [value] using specification for function __valid_read
 [value] user error: type long double not implemented. Using double instead
-tests/e-acsl-runtime/bts1307.i:13:[kernel] warning: non-finite float value ([-1.79769313486e+308 .. 1.79769313486e+308]):
-                  assert
-                  \is_finite((float)(*__e_acsl_at_2+(int)(5-(long double)((float)((int)
-                                                                                   (5/80)**__e_acsl_at_3)*0.4))));
-tests/e-acsl-runtime/bts1307.i:13:[value] Function __e_acsl_foo, behavior OverEstimate_Motoring: postcondition got status valid.
-tests/e-acsl-runtime/bts1307.i:19:[value] Function __e_acsl_bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:20:[value] Function __e_acsl_bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:21:[value] Function __e_acsl_bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:19:[value] Function bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:20:[value] Function bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:21:[value] Function bar: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:25:[value] Function bar, behavior UnderEstimate_Motoring: postcondition got status unknown.
-FRAMAC_SHARE/e-acsl/e_acsl.h:34:[value] Function e_acsl_assert: precondition got status valid.
-tests/e-acsl-runtime/bts1307.i:25:[value] Function __e_acsl_bar, behavior UnderEstimate_Motoring: postcondition got status unknown.
-[value] using specification for function __e_acsl_memory_clean
+FRAMAC_SHARE/e-acsl/e_acsl.h:34:[value] Function e_acsl_assert: precondition got status invalid.
+tests/e-acsl-runtime/bts1307.i:13:[value] Function __e_acsl_foo, behavior OverEstimate_Motoring: no state left in which to evaluate postcondition, status not computed.
 [value] done for function main
 [value] ====== VALUES COMPUTED ======
diff --git a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts1307.c b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts1307.c
index d1e3c38cc94..49ba96ec713 100644
--- a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts1307.c
+++ b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts1307.c
@@ -149,7 +149,9 @@ void __e_acsl_foo(float *Mtmax_in, float *Mwmax, float *Mtmax_out)
     __e_acsl_valid_read_3 = __valid_read((void *)__e_acsl_at,sizeof(float));
     e_acsl_assert(__e_acsl_valid_read_3,(char *)"RTE",(char *)"foo",
                   (char *)"mem_access: \\valid_read(__e_acsl_at)",13);
-    e_acsl_assert(*__e_acsl_at == *__e_acsl_at_2 + (5 - ((5 / 80) * *__e_acsl_at_3) * 0.4),
+    e_acsl_assert(*__e_acsl_at == *__e_acsl_at_2 + (long double)((long double)5 - 
+                                                                 ((long double)(
+                                                                  5 / 80) * *__e_acsl_at_3) * 0.4),
                   (char *)"Postcondition",(char *)"foo",
                   (char *)"*\\old(Mtmax_out) == *\\old(Mtmax_in)+(5-((5/80)**\\old(Mwmax))*0.4)",
                   13);
diff --git a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts13072.c b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts13072.c
index c8a41f86fa9..5a1adaa3409 100644
--- a/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts13072.c
+++ b/src/plugins/e-acsl/tests/e-acsl-runtime/oracle/gen_bts13072.c
@@ -223,7 +223,8 @@ void __e_acsl_foo(float *Mtmax_in, float *Mwmax, float *Mtmax_out)
     __e_acsl_valid_read_3 = __valid_read((void *)__e_acsl_at,sizeof(float));
     e_acsl_assert(__e_acsl_valid_read_3,(char *)"RTE",(char *)"foo",
                   (char *)"mem_access: \\valid_read(__e_acsl_at)",13);
-    e_acsl_assert(*__e_acsl_at == *__e_acsl_at_2 + (5 - (__e_acsl_4 * *__e_acsl_at_3) * 0.4),
+    e_acsl_assert(*__e_acsl_at == *__e_acsl_at_2 + ((long double)5 - 
+                                                    ((long double)__e_acsl_4 * *__e_acsl_at_3) * 0.4),
                   (char *)"Postcondition",(char *)"foo",
                   (char *)"*\\old(Mtmax_out) == *\\old(Mtmax_in)+(5-((5/80)**\\old(Mwmax))*0.4)",
                   13);
diff --git a/src/plugins/e-acsl/translate.ml b/src/plugins/e-acsl/translate.ml
index 82429669ad2..d5e954eb5c1 100644
--- a/src/plugins/e-acsl/translate.ml
+++ b/src/plugins/e-acsl/translate.ml
@@ -131,6 +131,26 @@ let conditional_to_exp ?(name="if") loc ctx e1 (e2, env2) (e3, env3) =
     in
     e, env
 
+(* If [e] is inserted in a context of type [float] or equivalent, then an
+   explicit cast must be introduced (an explicit coercion is required in C, but
+   it is implicit in the logic world).
+   [lty] is the type of the logic context, while [lty_t] is the logic type of
+   the term which [e] comes from. *)
+let cast_integer_to_float lty lty_t e =
+  if Cil.isIntegralType (Cil.typeOf e) then
+    let ty, correct = match lty, lty_t with
+      | Ctype ty, _ -> ty, true
+      | Lreal, Linteger -> Cil.longDoubleType, false
+      | Lreal, _ -> Cil.longDoubleType, true
+      | (Ltype _ | Lvar _ | Linteger | Larrow _), _ -> assert false
+    in
+    if not correct  then
+      Options.warning
+        "casting an integer to a long double without verification";
+    Cil.mkCast ~force:false ~e ~newt:ty
+  else
+    e
+
 let rec thost_to_host kf env = function
   | TVar { lv_origin = Some v } -> 
     Var (Cil.get_varinfo (Env.get_behavior env) v), env, v.vname
@@ -235,7 +255,13 @@ and context_insensitive_term_to_exp kf env t =
       in
       e, env, false, ""
     else
-      Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, false, ""
+      if Logic_typing.is_integral_type t.term_type then
+        Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, false, ""
+      else
+        (* floating point context: casting the arguments potentially required *)
+        let e1 = cast_integer_to_float t.term_type t1.term_type e1 in
+        let e2 = cast_integer_to_float t.term_type t2.term_type e2 in
+        Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)),  env, false, ""
   | TBinOp(Div | Mod as bop, t1, t2) ->
     let ty = Typing.typ_of_term_operation t in
     let ctx = Some ty in
@@ -276,7 +302,13 @@ and context_insensitive_term_to_exp kf env t =
       e, env, false, ""
     else
       (* no guard required since RTEs are generated separately *)
-      Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, false, ""
+      if Logic_typing.is_integral_type t.term_type then
+        Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, false, ""
+      else
+        (* floating point context: casting the arguments potentially required *)
+        let e1 = cast_integer_to_float t.term_type t1.term_type e1 in
+        let e2 = cast_integer_to_float t.term_type t2.term_type e2 in
+        Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)),  env, false, ""
   | TBinOp(Lt | Gt | Le | Ge | Eq | Ne as bop, t1, t2) ->
     (* comparison operators *)
     let e, env = comparison_to_exp ~loc kf env bop t1 t2 (Some t) in
@@ -638,26 +670,26 @@ and named_predicate_to_exp ?name kf ?rte env p =
 
 and translate_rte_annots: 
     'a. (Format.formatter -> 'a -> unit) -> 'a ->
-    kernel_function -> Env.t -> code_annotation list -> Env.t =
+  kernel_function -> Env.t -> code_annotation list -> Env.t =
   fun pp elt kf env l ->
-  let old_valid = !is_visiting_valid in
-  let old_kind = Env.annotation_kind env in
-  let env = Env.set_annotation_kind env Misc.RTE in
-  let env =
-    List.fold_left
-      (fun env a -> match a.annot_content with
-      | AAssert(_, p) -> 
-	handle_error
-	  (fun env -> 
-	    Options.feedback ~dkey ~level:4 "prevent RTE from %a" pp elt;
-	    translate_named_predicate kf (Env.rte env false) p)
-	  env
-      | _ -> assert false)
-      env
-      l
-  in
-  is_visiting_valid := old_valid;
-  Env.set_annotation_kind env old_kind
+    let old_valid = !is_visiting_valid in
+    let old_kind = Env.annotation_kind env in
+    let env = Env.set_annotation_kind env Misc.RTE in
+    let env =
+      List.fold_left
+        (fun env a -> match a.annot_content with
+        | AAssert(_, p) -> 
+	  handle_error
+	    (fun env -> 
+	      Options.feedback ~dkey ~level:4 "prevent RTE from %a" pp elt;
+	      translate_named_predicate kf (Env.rte env false) p)
+	    env
+        | _ -> assert false)
+        env
+        l
+    in
+    is_visiting_valid := old_valid;
+    Env.set_annotation_kind env old_kind
 
 and translate_rte kf env e =
   let stmt = Cil.mkStmtOneInstr ~valid_sid:true (Skip e.eloc) in
-- 
GitLab