From 76f4aa1b99dfd76d463221e0fc5647149025a55d Mon Sep 17 00:00:00 2001
From: Allan Blanchard <allan.blanchard@cea.fr>
Date: Mon, 9 Sep 2019 11:58:26 +0200
Subject: [PATCH] [AST Printing] Ensures that ghost annotations are correctly
 PP

---
 src/kernel_services/ast_printing/printer.ml   |  65 ++++----
 tests/pretty_printing/annotations.i           | 113 +++++++++++++
 .../oracle/annotations.res.oracle             | 155 ++++++++++++++++++
 .../oracle/ghost_multiline_annot.0.res.oracle |  14 +-
 .../oracle/ghost_multiline_annot.6.res.oracle |   7 +-
 .../oracle/ghost_multiline_annot.7.res.oracle |  14 +-
 .../oracle/ghost_multiline_annot.8.res.oracle |  14 +-
 7 files changed, 328 insertions(+), 54 deletions(-)
 create mode 100644 tests/pretty_printing/annotations.i
 create mode 100644 tests/pretty_printing/oracle/annotations.res.oracle

diff --git a/src/kernel_services/ast_printing/printer.ml b/src/kernel_services/ast_printing/printer.ml
index 294c40cd742..e4dc9bdfc87 100644
--- a/src/kernel_services/ast_printing/printer.ml
+++ b/src/kernel_services/ast_printing/printer.ml
@@ -167,12 +167,10 @@ class printer_with_annot () = object (self)
     super#global fmt glob
 
   method private begin_annotation fmt =
-    let pre = if is_ghost then Some ("@@/": Pretty_utils.sformat) else None in
-    self#pp_open_annotation ~block:false ?pre fmt
+    self#pp_open_annotation ~block:false fmt
 
   method private end_annotation fmt =
-    let suf = if is_ghost then Some ("@@/": Pretty_utils.sformat) else None in
-    self#pp_close_annotation ~block:false ?suf fmt
+    self#pp_close_annotation ~block:false fmt
 
   method private loop_annotations fmt annots =
     if annots <> [] then
@@ -191,6 +189,20 @@ class printer_with_annot () = object (self)
       fmt
       annots
 
+  method private in_ghost_if_needed fmt ghost_flag do_it =
+    let display_ghost = ghost_flag && not is_ghost in
+    if display_ghost then begin
+      is_ghost <- true ;
+      Format.fprintf fmt "@[%t %a@ "
+        (fun fmt -> self#pp_open_annotation fmt)
+        self#pp_acsl_keyword "ghost"
+    end ;
+    do_it () ;
+    if display_ghost then begin
+      is_ghost <- false;
+      Format.fprintf fmt "%t@]" (fun fmt -> self#pp_close_annotation fmt)
+    end
+
   method! annotated_stmt next fmt s =
     (* To debug location setting:
        (let loc = fst (Cil_datatype.Stmt.loc s.skind) in
@@ -215,35 +227,22 @@ class printer_with_annot () = object (self)
           Cil_datatype.Code_annotation.compare
           (Annotations.code_annot s)
       in
-      let pGhost fmt s =
-        let was_ghost = is_ghost in
-        if not was_ghost && s.ghost then begin
-          Format.fprintf fmt "%t %a "
-            (fun fmt -> self#pp_open_annotation ~pre:"@[/*@@" fmt)
-            self#pp_acsl_keyword "ghost";
-          is_ghost <- true
-        end;
-        self#stmtkind s.sattr next fmt s.skind;
-        if not was_ghost && s.ghost then begin
-          self#pp_close_annotation ~suf:"@,*/@]" fmt;
-          is_ghost <- false;
-        end
-      in
-      (match all_annot with
-       | [] -> pGhost fmt s
-       | [ a ] when Cil.is_skip s.skind && not s.ghost ->
-         Format.fprintf fmt "@[<hv>@[%t@ %a@;<1 1>%t@]@ %a@]"
-           (fun fmt -> self#pp_open_annotation ~block:false fmt)
-           self#code_annotation a
-           (fun fmt -> self#pp_close_annotation ~block:false fmt)
-           (self#stmtkind s.sattr next) s.skind;
-       | _ ->
-         let loop_annot, stmt_annot =
-           List.partition Logic_utils.is_loop_annot all_annot
-         in
-         self#annotations fmt stmt_annot;
-         self#loop_annotations fmt loop_annot;
-         pGhost fmt s)
+      self#in_ghost_if_needed fmt s.ghost
+        (fun () -> match all_annot with
+           | [] ->  self#stmtkind s.sattr next fmt s.skind;
+           | [ a ] when Cil.is_skip s.skind && not s.ghost ->
+             Format.fprintf fmt "@[<hv>@[%t@ %a@;<1 1>%t@]@ %a@]"
+               self#begin_annotation
+               self#code_annotation a
+               self#end_annotation
+               (self#stmtkind s.sattr next) s.skind;
+           | _ ->
+             let loop_annot, stmt_annot =
+               List.partition Logic_utils.is_loop_annot all_annot
+             in
+             self#annotations fmt stmt_annot;
+             self#loop_annotations fmt loop_annot;
+             self#stmtkind s.sattr next fmt s.skind) ;
     end else
       self#stmtkind s.sattr next fmt s.skind;
     Format.pp_close_box fmt ()
diff --git a/tests/pretty_printing/annotations.i b/tests/pretty_printing/annotations.i
new file mode 100644
index 00000000000..1d80a03ba03
--- /dev/null
+++ b/tests/pretty_printing/annotations.i
@@ -0,0 +1,113 @@
+/*@ axiomatic A {
+    predicate P(integer x) reads \nothing ;
+  }
+*/
+
+//@ ghost int global_decl ;
+//@ ghost int global_def = 42 ;
+
+/*@
+  requires P(x) && x > 0 ;
+  ensures P(x) ;
+*/
+void function_no_ghost(int x) {
+  int y = 0;
+
+  /*@ loop invariant 0 <= y <= x ;
+      loop assigns y ;
+      loop variant x - y ; */
+  while (y < x) {
+    /*@ assert y < x ; */
+    y++;
+    /*@ assert y <= x ; */
+  }
+
+  //@ assert y == x ;
+
+  /*@ requires y == x ;
+      assigns y ;
+      ensures y != x ;
+  */ {
+    y -- ;
+    y *= 2 ;
+  }
+
+  //@ requires y >= 0 ;
+  y /= y ;
+}
+
+/*@
+  requires P(x) && x > 0 ;
+  ensures P(x) ;
+*/
+void function_with_ghost(int x) {
+  //@ ghost int y = 0;
+
+  /*@ ghost
+    /@ loop invariant 0 <= y <= x ;
+       loop assigns y ;
+       loop variant x - y ; @/
+    while (y < x) {
+      /@ assert y < x ; @/
+      y++;
+      /@ assert y <= x ; @/
+    }
+  */
+
+  //@ assert y == x ;
+
+  /*@ ghost
+    /@ requires y == x ;
+       assigns y ;
+       ensures y != x ;
+    @/ {
+      y -- ;
+      y *= 2 ;
+    }
+  */
+
+  /*@ ghost
+    //@ requires y >= 0 ;
+    y /= y ;
+  */
+}
+
+/* @ ghost TODO: reactivate this test when ghost-functions are supported
+  /@
+    requires P(x) && x > 0 ;
+    ensures P(x) ;
+  @/
+  void ghost_function(int x) {
+    int y = 0;
+
+    /@ loop invariant 0 <= y <= x ;
+       loop assigns y ;
+       loop variant x - y ; @/
+    while (y < x) {
+      /@ assert y < x ; @/
+      y++;
+      /@ assert y <= x ; @/
+    }
+    
+    /@ assert y == x ; @/
+
+    /@ requires y == x ;
+       assigns y ;
+       ensures y != x ;
+    @/ {
+      y -- ;
+      y *= 2 ;
+    }
+
+    //@ requires y >= 0 ;
+    y /= y ;
+}
+*/
+
+/* @ ghost TODO: reactivate this test when ghost-functions are supported
+  void function_declaration(int variable) ;
+*/
+
+void reference_function(void){
+  // @ ghost function_declaration(42) ;
+}
diff --git a/tests/pretty_printing/oracle/annotations.res.oracle b/tests/pretty_printing/oracle/annotations.res.oracle
new file mode 100644
index 00000000000..e0259ac8679
--- /dev/null
+++ b/tests/pretty_printing/oracle/annotations.res.oracle
@@ -0,0 +1,155 @@
+[kernel] Parsing tests/pretty_printing/annotations.i (no preprocessing)
+/* Generated by Frama-C */
+/*@ axiomatic A {
+      predicate P(ℤ x) 
+        reads \nothing;
+      
+      }
+ */
+/*@ ghost int global_decl; */
+/*@ ghost int global_def = 42; */
+/*@ requires P(x) ∧ x > 0;
+    ensures P(\old(x)); */
+void function_no_ghost(int x)
+{
+  int y = 0;
+  /*@ loop invariant 0 ≤ y ≤ x;
+      loop assigns y;
+      loop variant x - y; */
+  while (y < x) {
+    /*@ assert y < x; */ ;
+    y ++;
+    /*@ assert y ≤ x; */ ;
+  }
+  /*@ assert y ≡ x; */ ;
+  /*@ requires y ≡ x;
+      ensures y ≢ x;
+      assigns y; */
+  {
+    y --;
+    y *= 2;
+  }
+  /*@ requires y ≥ 0; */
+  y /= y;
+  return;
+}
+
+/*@ requires P(x) ∧ x > 0;
+    ensures P(\old(x)); */
+void function_with_ghost(int x)
+{
+  /*@ ghost int y = 0; */
+  /*@ ghost
+    /@ loop invariant 0 ≤ y ≤ x;
+       loop assigns y;
+       loop variant x - y; @/
+    while (y < x) {
+      /@ assert y < x; @/
+      ;
+      y ++;
+      /@ assert y ≤ x; @/
+      ;
+    }
+  */
+  /*@ assert y ≡ x; */ ;
+  /*@ ghost
+    /@ requires y ≡ x;
+       ensures y ≢ x;
+       assigns y; @/
+    {
+      y --;
+      y *= 2;
+    }
+  */
+  /*@ ghost /@ requires y ≥ 0; @/
+    y /= y; */
+  return;
+}
+
+void reference_function(void)
+{
+  return;
+}
+
+
+[kernel] Parsing tests/pretty_printing/result/annotations.c (with preprocessing)
+[kernel] Parsing tests/pretty_printing/annotations.i (no preprocessing)
+[kernel] tests/pretty_printing/annotations.i:13: Warning: 
+  def'n of func function_no_ghost at tests/pretty_printing/annotations.i:13 (sum 9297192) conflicts with the one at tests/pretty_printing/result/annotations.c:12 (sum 14988159); keeping the one at tests/pretty_printing/result/annotations.c:12.
+[kernel] tests/pretty_printing/annotations.i:43: Warning: 
+  def'n of func function_with_ghost at tests/pretty_printing/annotations.i:43 (sum 9297192) conflicts with the one at tests/pretty_printing/result/annotations.c:38 (sum 14988159); keeping the one at tests/pretty_printing/result/annotations.c:38.
+[kernel] tests/pretty_printing/annotations.i:111: Warning: 
+  dropping duplicate def'n of func reference_function at tests/pretty_printing/annotations.i:111 in favor of that at tests/pretty_printing/result/annotations.c:68
+/* Generated by Frama-C */
+/*@ axiomatic A {
+      predicate P(ℤ x) 
+        reads \nothing;
+      
+      }
+ */
+/*@ ghost int global_decl; */
+/*@ ghost int global_def = 42; */
+/*@ requires P(x) ∧ x > 0;
+    ensures P(\old(x)); */
+void function_no_ghost(int x)
+{
+  int y = 0;
+  /*@ loop invariant 0 ≤ y ≤ x;
+      loop assigns y;
+      loop variant x - y; */
+  while (y < x) {
+    /*@ assert y < x; */ ;
+    y ++;
+    /*@ assert y ≤ x; */ ;
+  }
+  /*@ assert y ≡ x; */ ;
+  /*@ requires y ≡ x;
+      ensures y ≢ x;
+      assigns y; */
+  {
+    y --;
+    y *= 2;
+  }
+  /*@ requires y ≥ 0; */
+  y /= y;
+  return;
+}
+
+/*@ requires P(x) ∧ x > 0;
+    ensures P(\old(x)); */
+void function_with_ghost(int x)
+{
+  /*@ ghost int y = 0; */
+  /*@ ghost
+    /@ loop invariant 0 ≤ y ≤ x;
+       loop assigns y;
+       loop variant x - y; @/
+    while (y < x) {
+      /@ assert y < x; @/
+      ;
+      y ++;
+      /@ assert y ≤ x; @/
+      ;
+    }
+  */
+  /*@ assert y ≡ x; */ ;
+  /*@ ghost
+    /@ requires y ≡ x;
+       ensures y ≢ x;
+       assigns y; @/
+    {
+      y --;
+      y *= 2;
+    }
+  */
+  /*@ ghost /@ requires y ≥ 0; @/
+    y /= y; */
+  return;
+}
+
+void reference_function(void)
+{
+  return;
+}
+
+
diff --git a/tests/syntax/oracle/ghost_multiline_annot.0.res.oracle b/tests/syntax/oracle/ghost_multiline_annot.0.res.oracle
index a1b72856b21..c8071303034 100644
--- a/tests/syntax/oracle/ghost_multiline_annot.0.res.oracle
+++ b/tests/syntax/oracle/ghost_multiline_annot.0.res.oracle
@@ -3,12 +3,14 @@
 int main(int c)
 {
   int __retres;
-  /*@ requires c ≥ 0; */
-  /*@ ghost int x = c; */
-  /*@ loop invariant x ≥ 0;
-      loop assigns x;
-      loop variant x; */
-  /*@ ghost while (x > 0) x --; */
+  /*@ ghost /@ requires c ≥ 0; @/
+    int x = c; */
+  /*@ ghost
+    /@ loop invariant x ≥ 0;
+       loop assigns x;
+       loop variant x; @/
+    while (x > 0) x --;
+  */
   __retres = 0;
   return __retres;
 }
diff --git a/tests/syntax/oracle/ghost_multiline_annot.6.res.oracle b/tests/syntax/oracle/ghost_multiline_annot.6.res.oracle
index 2a924d8ae0e..a7859b320dd 100644
--- a/tests/syntax/oracle/ghost_multiline_annot.6.res.oracle
+++ b/tests/syntax/oracle/ghost_multiline_annot.6.res.oracle
@@ -4,9 +4,10 @@ int main(void)
 {
   int __retres;
   /*@ ghost int x = 10; */
-  /*@ loop invariant x ≥ 0;
-      loop variant x; */
-  /*@ ghost while (x > 0) x --; */
+  /*@ ghost /@ loop invariant x ≥ 0;
+               loop variant x; @/
+            while (x > 0) x --;
+  */
   __retres = 0;
   return __retres;
 }
diff --git a/tests/syntax/oracle/ghost_multiline_annot.7.res.oracle b/tests/syntax/oracle/ghost_multiline_annot.7.res.oracle
index 2019d87badc..6ee6fd7133f 100644
--- a/tests/syntax/oracle/ghost_multiline_annot.7.res.oracle
+++ b/tests/syntax/oracle/ghost_multiline_annot.7.res.oracle
@@ -3,12 +3,14 @@
 int main(int c)
 {
   int __retres;
-  /*@ requires c ≥ 0; */
-  /*@ ghost int x = c; */
-  /*@ loop invariant x ≥ 0;
-      loop invariant x ≡ x;
-      loop variant x; */
-  /*@ ghost while (x > 0) x --; */
+  /*@ ghost /@ requires c ≥ 0; @/
+    int x = c; */
+  /*@ ghost
+    /@ loop invariant x ≥ 0;
+       loop invariant x ≡ x;
+       loop variant x; @/
+    while (x > 0) x --;
+  */
   __retres = 0;
   return __retres;
 }
diff --git a/tests/syntax/oracle/ghost_multiline_annot.8.res.oracle b/tests/syntax/oracle/ghost_multiline_annot.8.res.oracle
index 2019d87badc..6ee6fd7133f 100644
--- a/tests/syntax/oracle/ghost_multiline_annot.8.res.oracle
+++ b/tests/syntax/oracle/ghost_multiline_annot.8.res.oracle
@@ -3,12 +3,14 @@
 int main(int c)
 {
   int __retres;
-  /*@ requires c ≥ 0; */
-  /*@ ghost int x = c; */
-  /*@ loop invariant x ≥ 0;
-      loop invariant x ≡ x;
-      loop variant x; */
-  /*@ ghost while (x > 0) x --; */
+  /*@ ghost /@ requires c ≥ 0; @/
+    int x = c; */
+  /*@ ghost
+    /@ loop invariant x ≥ 0;
+       loop invariant x ≡ x;
+       loop variant x; @/
+    while (x > 0) x --;
+  */
   __retres = 0;
   return __retres;
 }
-- 
GitLab