From ef99c7f4d46130d96d1e2b9386d9ebfe98714ef6 Mon Sep 17 00:00:00 2001
From: Basile Desloges <basile.desloges@cea.fr>
Date: Wed, 10 Feb 2021 11:39:50 +0100
Subject: [PATCH] [eacsl] Fix generated `__e_acsl_globals_clean()`

The `__eacsl_globals_clean()` global function is only created if there
are globals variables. A `return;` statement is added at the end of the
function to satisfy Frama-C invariants.
---
 .../src/code_generator/global_observer.ml     | 32 +++++++-------
 .../src/code_generator/global_observer.mli    |  4 +-
 .../e-acsl/src/code_generator/injector.ml     | 32 ++++++++------
 .../e-acsl/tests/bts/issue-eacsl-145.c        | 12 ++++++
 .../tests/bts/oracle_ci/gen_issue-eacsl-145.c | 42 +++++++++++++++++++
 .../bts/oracle_ci/issue-eacsl-145.res.oracle  | 39 +++++++++++++++++
 .../oracle_dev/issue-eacsl-145.e-acsl.err.log |  0
 7 files changed, 132 insertions(+), 29 deletions(-)
 create mode 100644 src/plugins/e-acsl/tests/bts/issue-eacsl-145.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-145.c
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-145.res.oracle
 create mode 100644 src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-145.e-acsl.err.log

diff --git a/src/plugins/e-acsl/src/code_generator/global_observer.ml b/src/plugins/e-acsl/src/code_generator/global_observer.ml
index 352152f2c2a..6ee21213ec0 100644
--- a/src/plugins/e-acsl/src/code_generator/global_observer.ml
+++ b/src/plugins/e-acsl/src/code_generator/global_observer.ml
@@ -187,20 +187,24 @@ let mk_init_function () =
   vi, fundec
 
 let mk_clean_function () =
-  (* Create and register [__e_acsl_globals_clean] function with definition
-     for de-allocation of global variables *)
-  let vi, fundec, _kf = mk_function function_clean_name in
-  (* Generate delete statements and add them to the function body *)
-  let stmts =
-    Varinfo.Hashtbl.fold_sorted
-      (fun vi _l acc ->
-         if Misc.is_fc_or_compiler_builtin vi then acc
-         else Smart_stmt.delete_stmt vi :: acc)
-      tbl
-      []
-  in
-  fundec.sbody.bstmts <- stmts;
-  vi, fundec
+  if Varinfo.Hashtbl.length tbl = 0 then
+    None
+  else
+    (* Create and register [__e_acsl_globals_clean] function with definition
+       for de-allocation of global variables *)
+    let vi, fundec, _kf = mk_function function_clean_name in
+    (* Generate delete statements and add them to the function body *)
+    let return = Cil.mkStmt ~valid_sid:true (Return (None, Location.unknown)) in
+    let stmts =
+      Varinfo.Hashtbl.fold_sorted
+        (fun vi _l acc ->
+           if Misc.is_fc_or_compiler_builtin vi then acc
+           else Smart_stmt.delete_stmt vi :: acc)
+        tbl
+        [return]
+    in
+    fundec.sbody.bstmts <- stmts;
+    Some (vi, fundec)
 
 (*
 Local Variables:
diff --git a/src/plugins/e-acsl/src/code_generator/global_observer.mli b/src/plugins/e-acsl/src/code_generator/global_observer.mli
index e37f39301d3..de355a0efc9 100644
--- a/src/plugins/e-acsl/src/code_generator/global_observer.mli
+++ b/src/plugins/e-acsl/src/code_generator/global_observer.mli
@@ -44,9 +44,9 @@ val mk_init_function: unit -> varinfo * fundec
 (** Generate a new C function containing the observers for global variable
     declarations and initializations. *)
 
-val mk_clean_function: unit -> varinfo * fundec
+val mk_clean_function: unit -> (varinfo * fundec) option
 (** Generate a new C function containing the observers for global variable
-    de-allocations. *)
+    de-allocations if there are global variables. *)
 
 (*
 Local Variables:
diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml
index acae30bc013..f095fa81d2c 100644
--- a/src/plugins/e-acsl/src/code_generator/injector.ml
+++ b/src/plugins/e-acsl/src/code_generator/injector.ml
@@ -710,8 +710,12 @@ let inject_global_handler file main =
     let vi_init, fundec_init = Global_observer.mk_init_function () in
     let cil_fct_init = GFun(fundec_init, Location.unknown) in
     (* Create [__e_acsl_globals_delete] function *)
-    let vi_clean, fundec_clean = Global_observer.mk_clean_function () in
-    let cil_fct_clean = GFun(fundec_clean, Location.unknown) in
+    let vi_and_fundec_clean_opt = Global_observer.mk_clean_function () in
+    let cil_fct_clean_opt =
+      Option.map
+        (fun (_, fundec_clean) -> GFun(fundec_clean, Location.unknown))
+        vi_and_fundec_clean_opt
+    in
     match main with
     | Some main ->
       let mk_fct_call vi =
@@ -730,14 +734,14 @@ let inject_global_handler file main =
       (* Create [__e_acsl_globals_init();] call *)
       let stmt_init = mk_fct_call vi_init in
       (* Create [__e_acsl_globals_delete();] call *)
-      let stmt_clean =
-        match fundec_clean.sbody.bstmts with
-        | [] -> None
-        | _ -> Some (mk_fct_call vi_clean)
+      let stmt_clean_opt =
+        Option.map
+          (fun (vi_clean, _) -> mk_fct_call vi_clean)
+          vi_and_fundec_clean_opt
       in
       (* Surround the content of main with the calls to
          [__e_acsl_globals_init();] and [__e_acsl_globals_delete();] *)
-      surround_function_with main main_fundec stmt_init stmt_clean;
+      surround_function_with main main_fundec stmt_init stmt_clean_opt;
       (* Retrieve all globals except main *)
       let main_vi = Globals.Functions.get_vi main in
       let new_globals =
@@ -758,9 +762,10 @@ let inject_global_handler file main =
         let globals_to_add = [ GFun(main_fundec, Location.unknown) ] in
         (* Prepend [__e_acsl_globals_clean] if not empty *)
         let globals_to_add =
-          match fundec_clean.sbody.bstmts with
-          | [] -> globals_to_add
-          | _ -> cil_fct_clean :: globals_to_add
+          Option.fold
+            ~none:globals_to_add
+            ~some:(fun cil_fct_clean -> cil_fct_clean :: globals_to_add)
+            cil_fct_clean_opt
         in
         (* Prepend [__e_acsl_globals_init] *)
         let globals_to_add = cil_fct_init :: globals_to_add in
@@ -782,9 +787,10 @@ let inject_global_handler file main =
         Global_observer.function_init_name
         Global_observer.function_clean_name;
       let globals_func =
-        match fundec_clean.sbody.bstmts with
-        | [] -> [ cil_fct_init ]
-        | _ -> [ cil_fct_init; cil_fct_clean ]
+        Option.fold
+          ~none:[ cil_fct_init ]
+          ~some:(fun cil_fct_clean -> [ cil_fct_init; cil_fct_clean ])
+          cil_fct_clean_opt
       in
       file.globals <- file.globals @ globals_func
 
diff --git a/src/plugins/e-acsl/tests/bts/issue-eacsl-145.c b/src/plugins/e-acsl/tests/bts/issue-eacsl-145.c
new file mode 100644
index 00000000000..dde2d2aed6d
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/issue-eacsl-145.c
@@ -0,0 +1,12 @@
+/* run.config_ci
+  COMMENT: frama-c/e-acsl#145, test for validity of globals_init and
+  globals_clean.
+  STDOPT: +"-verbose 1 -eva-print"
+*/
+int G = 0;
+
+int main(void) {
+  /*@ assert \valid(&G); */
+  int a = G;
+  return 0;
+}
diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-145.c b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-145.c
new file mode 100644
index 00000000000..7382aae8323
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle_ci/gen_issue-eacsl-145.c
@@ -0,0 +1,42 @@
+/* Generated by Frama-C */
+#include "stddef.h"
+#include "stdio.h"
+int G = 0;
+void __e_acsl_globals_init(void)
+{
+  static char __e_acsl_already_run = 0;
+  if (! __e_acsl_already_run) {
+    __e_acsl_already_run = 1;
+    __e_acsl_store_block((void *)(& G),(size_t)4);
+    __e_acsl_full_init((void *)(& G));
+  }
+  return;
+}
+
+void __e_acsl_globals_clean(void)
+{
+  __e_acsl_delete_block((void *)(& G));
+  return;
+}
+
+int main(void)
+{
+  int __retres;
+  __e_acsl_memory_init((int *)0,(char ***)0,(size_t)8);
+  __e_acsl_globals_init();
+  {
+    int __gen_e_acsl_valid;
+    __gen_e_acsl_valid = __e_acsl_valid((void *)(& G),sizeof(int),
+                                        (void *)(& G),(void *)0);
+    __e_acsl_assert(__gen_e_acsl_valid,"Assertion","main","\\valid(&G)",
+                    "tests/bts/issue-eacsl-145.c",9);
+  }
+  /*@ assert \valid(&G); */ ;
+  int a = G;
+  __retres = 0;
+  __e_acsl_globals_clean();
+  __e_acsl_memory_clean();
+  return __retres;
+}
+
+
diff --git a/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-145.res.oracle b/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-145.res.oracle
new file mode 100644
index 00000000000..73cc9ba6ba1
--- /dev/null
+++ b/src/plugins/e-acsl/tests/bts/oracle_ci/issue-eacsl-145.res.oracle
@@ -0,0 +1,39 @@
+[kernel] Parsing tests/bts/issue-eacsl-145.c (with preprocessing)
+[e-acsl] beginning translation.
+[kernel] Parsing FRAMAC_SHARE/e-acsl/e_acsl.h (with preprocessing)
+[e-acsl] translation done in project "e-acsl".
+[eva] Analyzing a complete application starting at main
+[eva] Computing initial state
+[eva] Initial state computed
+[eva:initial-state] Values of globals at initialization
+  G ∈ {0}
+  __e_acsl_heap_allocation_size ∈ [--..--]
+  __e_acsl_heap_allocated_blocks ∈ [--..--]
+  __fc_heap_status ∈ [--..--]
+  __e_acsl_math_HUGE_VAL ∈ [-1.79769313486e+308 .. 1.79769313486e+308]
+  __e_acsl_math_HUGE_VALF ∈ [-3.40282346639e+38 .. 3.40282346639e+38]
+  __e_acsl_math_INFINITY ∈ [-1.79769313486e+308 .. 1.79769313486e+308]
+  __e_acsl_init ∈ [--..--]
+[eva] using specification for function __e_acsl_memory_init
+[eva] using specification for function __e_acsl_store_block
+[eva] using specification for function __e_acsl_full_init
+[eva] using specification for function __e_acsl_valid
+[eva] using specification for function __e_acsl_assert
+[eva] using specification for function __e_acsl_delete_block
+[eva] using specification for function __e_acsl_memory_clean
+[eva] done for function main
+[eva] ====== VALUES COMPUTED ======
+[eva:summary] ====== ANALYSIS SUMMARY ======
+  ----------------------------------------------------------------------------
+  5 functions analyzed (out of 5): 100% coverage.
+  In these functions, 27 statements reached (out of 27): 100% coverage.
+  ----------------------------------------------------------------------------
+  No errors or warnings raised during the analysis.
+  ----------------------------------------------------------------------------
+  0 alarms generated by the analysis.
+  ----------------------------------------------------------------------------
+  Evaluation of the logical properties reached by the analysis:
+    Assertions        1 valid     0 unknown     0 invalid      1 total
+    Preconditions     1 valid     0 unknown     0 invalid      1 total
+  100% of the logical properties reached have been proven.
+  ----------------------------------------------------------------------------
diff --git a/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-145.e-acsl.err.log b/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-145.e-acsl.err.log
new file mode 100644
index 00000000000..e69de29bb2d
-- 
GitLab