From f6e6f575fd90cbee7ff7eb784e704ceb546b9be6 Mon Sep 17 00:00:00 2001
From: Virgile Prevosto <virgile.prevosto@cea.fr>
Date: Thu, 26 Sep 2024 18:09:52 +0200
Subject: [PATCH] [inline-contracts] preparing propagation of statuses

---
 src/kernel_internals/typing/asm_contracts.ml  |  8 ++-
 .../inline_stmt_contracts.ml                  | 61 ++++++++++++++-----
 tests/misc/asm_initialized.i                  |  4 +-
 tests/misc/oracle/asm_initialized.res.oracle  | 38 ++++++++++++
 4 files changed, 92 insertions(+), 19 deletions(-)

diff --git a/src/kernel_internals/typing/asm_contracts.ml b/src/kernel_internals/typing/asm_contracts.ml
index 29123aa017..7814244c44 100644
--- a/src/kernel_internals/typing/asm_contracts.ml
+++ b/src/kernel_internals/typing/asm_contracts.ml
@@ -211,10 +211,12 @@ class visit_assembly =
                Property.ip_assigns_of_behavior kf (Kstmt stmt) ~active bhv in
              let ip_from =
                Property.ip_from_of_behavior kf (Kstmt stmt) ~active bhv in
+             let ip_init =
+               Property.ip_ensures_of_behavior kf (Kstmt stmt) bhv
+             in
              List.iter
-               Property_status.(
-                 fun x -> emit emitter ~hyps:[] x True)
-               (Option.to_list ip_assigns @ ip_from)
+               Property_status.(fun x -> emit emitter ~hyps:[] x True)
+               (Option.to_list ip_assigns @ ip_from @ ip_init)
            end
          | [ { annot_content = AStmtSpec ([], spec) } ] ->
            (* Already existing contracts. Just add assigns clause for
diff --git a/src/kernel_services/ast_transformations/inline_stmt_contracts.ml b/src/kernel_services/ast_transformations/inline_stmt_contracts.ml
index 4cd772eb9d..aa0b2711ac 100644
--- a/src/kernel_services/ast_transformations/inline_stmt_contracts.ml
+++ b/src/kernel_services/ast_transformations/inline_stmt_contracts.ml
@@ -23,8 +23,10 @@
 open Cil_types
 
 let emitter =
-  Emitter.create
-    "inline_stmt_contract" [Emitter.Code_annot] ~correctness:[] ~tuning:[]
+  let open Emitter in
+  create
+    "inline_stmt_contract" [Code_annot; Property_status]
+    ~correctness:[] ~tuning:[]
 
 let category = File.register_code_transformation_category "inline_stmt_contract"
 
@@ -38,7 +40,29 @@ let new_label () =
   let res = Printf.sprintf "__fc_inline_contract_label_%d" !nb_labels in
   incr nb_labels; res
 
-let treat_behavior for_list stmt acc b =
+let propagate_status p1 p2 =
+  match Property_status.get p1 with
+  | Best (True, _) -> Property_status.emit emitter ~hyps:[p1] p2 True
+  | _ ->
+    begin
+      let open Property_status in
+      let propagate_status_p1 e p s =
+        if Property.equal p p1 && s = True
+           && not (Emitter.(Usable_emitter.equal e.emitter (get emitter)))
+        then
+          Property_status.emit emitter ~hyps:[p1] p2 True
+      in
+      Property_status.register_status_update_hook propagate_status_p1;
+      let propagate_status_assertion e p s =
+        if Property.equal p p2 && s = Property_status.True
+           && not (Emitter.(Usable_emitter.equal e.emitter (get emitter)))
+        then
+          Property_status.emit emitter ~hyps:[p2] p1 True
+      in
+      Property_status.register_status_update_hook propagate_status_assertion
+    end
+
+let treat_behavior for_list kf stmt acc b =
   let loc = Cil_datatype.Stmt.loc stmt in
   let pand p1 p2 = Logic_const.pand ~loc (p1,p2.ip_content.tp_statement) in
   let assumes = List.fold_left pand Logic_const.ptrue b.b_assumes in
@@ -47,17 +71,20 @@ let treat_behavior for_list stmt acc b =
     let p = Logic_const.toplevel_predicate p in
     Logic_const.new_code_annotation (AAssert (for_list,p))
   in
-  let add_assert p =
-    let p = Logic_const.pimplies ~loc (assumes, p.ip_content.tp_statement) in
+  let add_assert req =
+    let p = Logic_const.pimplies ~loc (assumes, req.ip_content.tp_statement) in
+    let ip_req = Property.ip_of_requires kf (Kstmt stmt) b req in
     let annot = mk_annot p in
-    Annotations.add_code_annot emitter stmt annot
+    Annotations.add_code_annot emitter stmt annot;
+    let ip_annot = Property.ip_of_code_annot_single kf stmt annot in
+    propagate_status ip_req ip_annot
   in
-  let treat_post_cond acc (k,p) =
+  let treat_post_cond acc (k,p as postcond) =
     match k with
     | Normal ->
-      if Logic_utils.is_trivially_true assumes then
-        mk_annot p.ip_content.tp_statement :: acc
-      else begin
+      if Logic_utils.is_trivially_true assumes then begin
+        (b, postcond, mk_annot p.ip_content.tp_statement) :: acc
+      end else begin
         (match List.filter (fun l -> not (Cil.is_case_label l)) stmt.labels with
          | [] -> stmt.labels <- Label (new_label(),loc,false) :: stmt.labels
          | _ -> ());
@@ -67,28 +94,34 @@ let treat_behavior for_list stmt acc b =
               (pat ~loc (assumes,StmtLabel (ref stmt)),
                p.ip_content.tp_statement))
         in
-        mk_annot p :: acc
+        (b, postcond, mk_annot p) :: acc
       end
     | _ -> acc
   in
   List.iter add_assert b.b_requires;
   List.fold_left treat_post_cond acc b.b_post_cond
 
+let add_one_postcond kf orig_stmt new_stmt (b, post, assertion) =
+  Annotations.add_code_annot emitter ~kf new_stmt assertion;
+  let post = Property.ip_of_ensures kf (Kstmt orig_stmt) b post in
+  let assertion = Property.ip_of_code_annot_single kf new_stmt assertion in
+  propagate_status post assertion
+
 class inline_stmt_contract =
   object(self)
     inherit Visitor.frama_c_inplace
     method! vstmt_aux s =
       let loc = Cil_datatype.Stmt.loc s in
+      let kf = Option.get self#current_kf in
       match Annotations.code_annot ~filter:Logic_utils.is_contract s with
       | [ { annot_content = AStmtSpec (l,spec) } ] ->
         let bhvs = spec.spec_behavior in
-        let posts = List.fold_left (treat_behavior l s) [] bhvs in
+        let posts = List.fold_left (treat_behavior l kf s) [] bhvs in
         (match posts with
          | [] -> Cil.DoChildren
          | _ ->
            let nop = Cil.mkStmtOneInstr ~valid_sid:true (Skip loc) in
-           let kf = Option.get self#current_kf in
-           List.iter (Annotations.add_code_annot emitter ~kf nop) posts;
+           List.iter (add_one_postcond kf s nop) posts;
            let b = Cil.mkBlockNonScoping [s; nop] in
            let b = Cil.transient_block b in
            let res = Cil.mkStmt ~valid_sid:true (Block b) in
diff --git a/tests/misc/asm_initialized.i b/tests/misc/asm_initialized.i
index fdf22cba97..d0be761ab1 100644
--- a/tests/misc/asm_initialized.i
+++ b/tests/misc/asm_initialized.i
@@ -1,6 +1,6 @@
 /* run.config*
-PLUGIN: @EVA_PLUGINS@
-STDOPT: #"-asm-contracts-ensure-init -inline-stmt-contracts -absolute-valid-range 0x10000000-0xf00000000 -print"
+PLUGIN: @EVA_PLUGINS@ report
+STDOPT: #"-asm-contracts-ensure-init -asm-contracts-auto-validate -inline-stmt-contracts -absolute-valid-range 0x10000000-0xf00000000 -print -report"
 */
 
 int main() {
diff --git a/tests/misc/oracle/asm_initialized.res.oracle b/tests/misc/oracle/asm_initialized.res.oracle
index 1cdc82f26b..2c96f18db6 100644
--- a/tests/misc/oracle/asm_initialized.res.oracle
+++ b/tests/misc/oracle/asm_initialized.res.oracle
@@ -46,3 +46,41 @@ int main(void)
 }
 
 
+[report] Computing properties status...
+
+--------------------------------------------------------------------------------
+--- Properties of Function 'main'
+--------------------------------------------------------------------------------
+
+[    -    ] Assertion 'inline_stmt_contract' (file asm_initialized.i, line 9)
+            tried with Eva.
+[    -    ] Assertion 'Eva,mem_access' (file asm_initialized.i, line 10)
+            tried with Eva.
+[  Alarm  ] Assertion 'Eva,initialization' (file asm_initialized.i, line 11)
+            By Eva, with pending:
+             - Unreachable return (file asm_initialized.i, line 11)
+
+--------------------------------------------------------------------------------
+--- Status Report Summary
+--------------------------------------------------------------------------------
+     2 To be validated
+     1 Alarm emitted
+     3 Total
+--------------------------------------------------------------------------------
+/* Generated by Frama-C */
+int main(void)
+{
+  int *sp;
+  int x;
+  /*@ ensures \initialized(&sp);
+      assigns sp;
+      assigns sp \from \nothing; */
+  __asm__ volatile ("mov %%rsp, %0;" : "=r" (sp));
+  /*@ assert inline_stmt_contract: \initialized(&sp); */ ;
+  /*@ assert Eva: mem_access: \valid(sp - 2); */
+  *(sp - 2) = 3;
+  /*@ assert Eva: initialization: \initialized(&x); */
+  return x;
+}
+
+
-- 
GitLab