From 9744d2ad095815188b15e064c213ceb89c59834e Mon Sep 17 00:00:00 2001
From: Allan Blanchard <allan.blanchard@cea.fr>
Date: Mon, 18 Jan 2021 17:54:13 +0100
Subject: [PATCH] [wp] Fixes block_length for opaque structs

---
 src/plugins/wp/Cvalues.ml                     |  43 +++++-
 src/plugins/wp/Cvalues.mli                    |   2 +
 src/plugins/wp/Definitions.ml                 |  22 +--
 src/plugins/wp/MemTyped.ml                    |  19 ++-
 src/plugins/wp/MemVar.ml                      |  20 +--
 src/plugins/wp/tests/wp_acsl/opaque_struct.i  |  40 ++++--
 .../wp_acsl/oracle/opaque_struct.res.oracle   | 129 ++++++++++++++++--
 .../oracle_qualif/opaque_struct.res.oracle    |  49 +++++--
 8 files changed, 264 insertions(+), 60 deletions(-)

diff --git a/src/plugins/wp/Cvalues.ml b/src/plugins/wp/Cvalues.ml
index fc6c98b97e2..df578509530 100644
--- a/src/plugins/wp/Cvalues.ml
+++ b/src/plugins/wp/Cvalues.ml
@@ -70,7 +70,10 @@ and constant_term t =
   | TConst c -> logic_constant c
   | _ -> Warning.error "constant(%a)" Printer.pp_term t
 
-(* Initialization values *)
+(* -------------------------------------------------------------------------- *)
+(* --- Initialization values                                              --- *)
+(* -------------------------------------------------------------------------- *)
+
 module OPAQUE_COMP_INIT = struct
   type initialization_funs = {
     init: lfun ;
@@ -130,6 +133,44 @@ and init_comp_value value ci =
 let initialized_obj = init_value e_true
 let uninitialized_obj = init_value e_false
 
+(* -------------------------------------------------------------------------- *)
+(* --- Length of empty compinfos                                          --- *)
+(* -------------------------------------------------------------------------- *)
+
+module OPAQUE_COMP_BYTES_LENGTH = WpContext.Generator(Cil_datatype.Compinfo)
+    (struct
+      let name = "Cvalues.EmptyCompBytesLength"
+      type key = compinfo
+      type data = lfun
+      let compile c =
+        if c.cfields <> None then
+          Wp_parameters.fatal
+            "Asking for opaque struct length on non opaque struct" ;
+        let result = Lang.t_int in
+        let name = "BytesLength" in
+        let size =
+          Lang.(generated_f ~params:[] ~result "%s_of_%s" name (comp_id c))
+        in
+        (* Registration *)
+        Definitions.define_symbol {
+          d_cluster = Definitions.compinfo c ;
+          d_lfun = size ; d_types = 0 ; d_params = [] ;
+          d_definition = Logic result ;
+        } ;
+        let min_size = if Cil.acceptEmptyCompinfo () then e_zero else e_one in
+        Definitions.define_lemma {
+          l_kind = `Axiom ;
+          l_name = "Positive_" ^ name ^ "_of_" ^ Lang.comp_id c ;
+          l_types = 0 ; l_triggers = [] ; l_forall = [] ;
+          l_cluster = Definitions.compinfo c ;
+          l_lemma = Lang.F.(p_leq min_size (e_fun size []))
+        } ;
+        size
+    end)
+
+let bytes_length_of_opaque_comp c =
+  Lang.F.e_fun (OPAQUE_COMP_BYTES_LENGTH.get c) []
+
 (* -------------------------------------------------------------------------- *)
 
 (* The type contains C-integers *)
diff --git a/src/plugins/wp/Cvalues.mli b/src/plugins/wp/Cvalues.mli
index 6c113daf413..85276cb8315 100644
--- a/src/plugins/wp/Cvalues.mli
+++ b/src/plugins/wp/Cvalues.mli
@@ -102,6 +102,8 @@ val constant_term : Cil_types.term -> term
 val initialized_obj: c_object -> term
 val uninitialized_obj: c_object -> term
 
+val bytes_length_of_opaque_comp: compinfo -> term
+
 (** {2 Lifting Operations over Memory Values} *)
 
 val map_sloc : ('a -> 'b) -> 'a Sigs.sloc -> 'b Sigs.sloc
diff --git a/src/plugins/wp/Definitions.ml b/src/plugins/wp/Definitions.ml
index cf21f0f5bb1..b8bd00cd0e6 100644
--- a/src/plugins/wp/Definitions.ml
+++ b/src/plugins/wp/Definitions.ml
@@ -336,11 +336,12 @@ class virtual visitor main =
           let c = compinfo r in
           if self#do_local c then
             begin
-              let fts = Option.map (List.map
-                  (fun f ->
-                     let t = Lang.tau_of_ctype f.ftype in
-                     self#vtau t ; Cfield (f, KValue) , t
-                  ))
+              let fts = Option.map
+                  (List.map
+                     (fun f ->
+                        let t = Lang.tau_of_ctype f.ftype in
+                        self#vtau t ; Cfield (f, KValue) , t
+                     ))
                   r.cfields
               in self#on_comp r fts ;
             end
@@ -353,11 +354,12 @@ class virtual visitor main =
           let c = icompinfo r in
           if self#do_local c then
             begin
-              let fts = Option.map (List.map
-                  (fun f ->
-                     let t = Lang.init_of_ctype f.ftype in
-                     self#vtau t ; Cfield (f, KInit) , t
-                  ))
+              let fts = Option.map
+                  (List.map
+                     (fun f ->
+                        let t = Lang.init_of_ctype f.ftype in
+                        self#vtau t ; Cfield (f, KInit) , t
+                     ))
                   r.cfields
               in self#on_icomp r fts ;
             end
diff --git a/src/plugins/wp/MemTyped.ml b/src/plugins/wp/MemTyped.ml
index 7671a4f03d7..34b939f685f 100644
--- a/src/plugins/wp/MemTyped.ml
+++ b/src/plugins/wp/MemTyped.ml
@@ -220,18 +220,19 @@ and all_value_chunks () =
 let init_footprint _ _ = Heap.Set.singleton T_init
 let value_footprint obj _l = footprint obj
 
-module OPAQUE_COMP_SIZE = WpContext.Generator(Cil_datatype.Compinfo)
+(* Note that it is the length in MemTyped and not the occupied bytes *)
+module OPAQUE_COMP_LENGTH = WpContext.Generator(Cil_datatype.Compinfo)
     (struct
-      let name = "MemTyped.EmptyCompSize"
+      let name = "MemTyped.EmptyCompLength"
       type key = compinfo
       type data = lfun
       let compile c =
         if c.cfields <> None then
           Wp_parameters.fatal
-            "Asking for opaque struct size on non opaque struct" ;
+            "Asking for opaque struct length on non opaque struct" ;
         let result = Lang.t_int in
         let size =
-          Lang.generated_f ~params:[] ~result "%s" ("Size_of_" ^ Lang.comp_id c)
+          Lang.generated_f ~params:[] ~result "Length_of_%s" (Lang.comp_id c)
         in
         (* Registration *)
         Definitions.define_symbol {
@@ -241,7 +242,7 @@ module OPAQUE_COMP_SIZE = WpContext.Generator(Cil_datatype.Compinfo)
         } ;
         Definitions.define_lemma {
           l_kind = `Axiom ;
-          l_name = "Positive_Size_of_" ^ Lang.comp_id c ;
+          l_name = "Positive_Length_of_" ^ Lang.comp_id c ;
           l_types = 0 ; l_triggers = [] ; l_forall = [] ;
           l_cluster = Definitions.compinfo c ;
           l_lemma = Lang.F.(p_lt e_zero (e_fun size []))
@@ -266,7 +267,7 @@ and length_of_field f = length_of_typ f.ftype
 and length_of_comp c =
   match c.cfields with
   | None ->
-      Lang.F.e_fun (OPAQUE_COMP_SIZE.get c) []
+      Lang.F.e_fun (OPAQUE_COMP_LENGTH.get c) []
   | Some fields ->
       (* union field are considered as struct field *)
       e_sum (List.map length_of_field fields)
@@ -644,7 +645,11 @@ let allocated sigma l = F.e_get (Sigma.value sigma T_alloc) (a_base l)
 let base_addr l = a_addr (a_base l) e_zero
 let base_offset l = a_base_offset (a_base l) (a_offset l)
 let block_length sigma obj l =
-  e_fact (Ctypes.sizeof_object obj) (allocated sigma l)
+  match obj with
+  | C_comp ({ cfields = None } as c) ->
+      e_mul (Cvalues.bytes_length_of_opaque_comp c) (allocated sigma l)
+  | _ ->
+      e_fact (Ctypes.sizeof_object obj) (allocated sigma l)
 
 (* -------------------------------------------------------------------------- *)
 (* --- Cast                                                               --- *)
diff --git a/src/plugins/wp/MemVar.ml b/src/plugins/wp/MemVar.ml
index 4d4b5c1c850..ddaef66c490 100644
--- a/src/plugins/wp/MemVar.ml
+++ b/src/plugins/wp/MemVar.ml
@@ -683,14 +683,18 @@ struct
     | Loc l -> M.block_length sigma.mem obj l
     | Ref x -> noref ~op:"block-length of" x
     | Val(m,x,_) ->
-        let obj = Ctypes.object_of (vtype m x) in
-        let size =
-          if Ctypes.sizeof_defined obj
-          then Ctypes.sizeof_object obj
-          else if Wp_parameters.ExternArrays.get ()
-          then max_int
-          else Warning.error ~source:"MemVar" "Unknown array-size"
-        in F.e_int size
+        begin match Ctypes.object_of (vtype m x) with
+          | C_comp ({ cfields = None } as c) ->
+              Cvalues.bytes_length_of_opaque_comp c
+          | obj ->
+              let size =
+                if Ctypes.sizeof_defined obj
+                then Ctypes.sizeof_object obj
+                else if Wp_parameters.ExternArrays.get ()
+                then max_int
+                else Warning.error ~source:"MemVar" "Unknown array-size"
+              in F.e_int size
+        end
 
   let cast obj l = Loc(M.cast obj (mloc_of_loc l))
   let loc_of_int e a = Loc(M.loc_of_int e a)
diff --git a/src/plugins/wp/tests/wp_acsl/opaque_struct.i b/src/plugins/wp/tests/wp_acsl/opaque_struct.i
index 5be55cbc018..208ef861206 100644
--- a/src/plugins/wp/tests/wp_acsl/opaque_struct.i
+++ b/src/plugins/wp/tests/wp_acsl/opaque_struct.i
@@ -4,8 +4,9 @@ extern struct S S1;
 extern struct S S2;
 
 /*@ axiomatic test{
-  @ check lemma should_fail : S1 == S2;
-  @ check lemma should_succeed : S1 == S1;
+  @ check lemma fail: S1 == S2;
+  @ check lemma succeed_L1: S1 == S1;
+  @ check lemma succeed_L2: \block_length(&S1) >= 0;
 }*/
 
 /*@ assigns S1; */
@@ -13,8 +14,8 @@ void f(void);
 
 void assigns(void){
   f();
-  /*@ check should_fail: S1 == \at(S1,Pre);*/
-  /*@ check should_succeed: S2 == \at(S2,Pre);*/
+  //@ check fail: S1 == \at(S1,Pre);
+  //@ check succeed: S2 == \at(S2,Pre);
 }
 
 struct S* p ;
@@ -22,10 +23,17 @@ struct S* p ;
 //@ assigns *p ;
 void g(void);
 
-/*@ requires \initialized(p); */
+/*@ requires \initialized(p); 
+    requires \valid(p);
+*/
 void initialized_assigns(void){
   g();
-  //@ check should_succeed: \initialized(p);
+  //@ check succeed: \initialized(p);
+  //@ check succeed: \block_length(p) >= 0;
+  
+  // while it can be proved in Coq, this is currently
+  // too indirect for solvers.
+  // @ check succeed: \block_length(p) >= \block_length(&S1);
 }
 
 /*@ requires ! \initialized(p); */
@@ -36,6 +44,22 @@ void uninitialized_assigns(void){
      - it is still uninitialized,
      - it has been initialized.
   */
-  //@ check should_fail: ! \initialized(p);
-  //@ check should_fail:   \initialized(p);
+  //@ check fail: ! \initialized(p);
+  //@ check fail:   \initialized(p);
+}
+
+void assigned_via_pointer(void){
+  g();
+  //@ check fail: \at(*p, Here) == \at(*p, Pre);
+}
+
+//@ assigns *a ;
+void assign(struct S *a);
+
+//@ requires \separated(a, c);
+void assigns_effect(int* p, float* q, char* c, struct S *a){
+  assign(a);
+  //@ check fail: *p == \at(*p, Pre);
+  //@ check fail: *q == \at(*q, Pre);
+  //@ check succeed: *c == \at(*c, Pre);
 }
diff --git a/src/plugins/wp/tests/wp_acsl/oracle/opaque_struct.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle/opaque_struct.res.oracle
index a8efa3e97f1..28a1c218766 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle/opaque_struct.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle/opaque_struct.res.oracle
@@ -6,51 +6,139 @@
   Axiomatic 'test'
 ------------------------------------------------------------
 
-Lemma should_fail:
+Lemma fail:
 Prove: (EqS1_S S1_0 S2_0)
 
 ------------------------------------------------------------
 
-Lemma should_succeed:
+Lemma succeed_L1:
 Prove: true
 
+------------------------------------------------------------
+
+Lemma succeed_L2:
+Prove: 0<=BytesLength_of_S1_S
+
+------------------------------------------------------------
+------------------------------------------------------------
+  Function assigned_via_pointer
+------------------------------------------------------------
+
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 53):
+Assume {
+  (* Heap *)
+  Type: (region(p.base) <= 0) /\ framed(Mptr_0) /\ sconst(Mchar_0).
+}
+Prove: EqS1_S(Load_S1_S(p, havoc(Mchar_undef_0, Mchar_0, p, Length_of_S1_S),
+                havoc(Mint_undef_0, Mint_0, p, Length_of_S1_S),
+                havoc(Mint_undef_1, Mint_1, p, Length_of_S1_S),
+                havoc(Mint_undef_2, Mint_2, p, Length_of_S1_S),
+                havoc(Mint_undef_3, Mint_3, p, Length_of_S1_S),
+                havoc(Mint_undef_4, Mint_4, p, Length_of_S1_S),
+                havoc(Mint_undef_5, Mint_5, p, Length_of_S1_S),
+                havoc(Mint_undef_6, Mint_6, p, Length_of_S1_S),
+                havoc(Mint_undef_7, Mint_7, p, Length_of_S1_S),
+                havoc(Mint_undef_8, Mint_8, p, Length_of_S1_S),
+                havoc(Mf32_undef_0, Mf32_0, p, Length_of_S1_S),
+                havoc(Mf64_undef_0, Mf64_0, p, Length_of_S1_S),
+                havoc(Mptr_undef_0, Mptr_0, p, Length_of_S1_S)),
+         Load_S1_S(p, Mchar_0, Mint_0, Mint_1, Mint_2, Mint_3, Mint_4,
+           Mint_5, Mint_6, Mint_7, Mint_8, Mf32_0, Mf64_0, Mptr_0)).
+
 ------------------------------------------------------------
 ------------------------------------------------------------
   Function assigns
 ------------------------------------------------------------
 
-Goal Check 'should_fail' (file tests/wp_acsl/opaque_struct.i, line 16):
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 17):
 Prove: EqS1_S(S1_0, S1_1).
 
 ------------------------------------------------------------
 
-Goal Check 'should_succeed' (file tests/wp_acsl/opaque_struct.i, line 17):
+Goal Check 'succeed' (file tests/wp_acsl/opaque_struct.i, line 18):
 Prove: true.
 
+------------------------------------------------------------
+------------------------------------------------------------
+  Function assigns_effect
+------------------------------------------------------------
+
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 62):
+Let x = Mint_0[p].
+Let a_1 = havoc(Mint_undef_0, Mint_0, a, Length_of_S1_S)[p].
+Assume {
+  Type: is_sint32(x) /\ is_sint32(a_1).
+  (* Heap *)
+  Type: (region(a.base) <= 0) /\ (region(c.base) <= 0) /\
+      (region(p.base) <= 0).
+  (* Pre-condition *)
+  Have: separated(a, Length_of_S1_S, c, 1).
+}
+Prove: a_1 = x.
+
+------------------------------------------------------------
+
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 63):
+Assume {
+  (* Heap *)
+  Type: (region(a.base) <= 0) /\ (region(c.base) <= 0) /\
+      (region(q.base) <= 0).
+  (* Pre-condition *)
+  Have: separated(a, Length_of_S1_S, c, 1).
+}
+Prove: of_f32(havoc(Mf32_undef_0, Mf32_0, a, Length_of_S1_S)[q])
+         = of_f32(Mf32_0[q]).
+
+------------------------------------------------------------
+
+Goal Check 'succeed' (file tests/wp_acsl/opaque_struct.i, line 64):
+Let x = Mchar_0[c].
+Let a_1 = havoc(Mchar_undef_0, Mchar_0, a, Length_of_S1_S)[c].
+Assume {
+  Type: is_sint8(x) /\ is_sint8(a_1).
+  (* Heap *)
+  Type: (region(a.base) <= 0) /\ (region(c.base) <= 0) /\ sconst(Mchar_0).
+  (* Pre-condition *)
+  Have: separated(a, Length_of_S1_S, c, 1).
+}
+Prove: a_1 = x.
+
 ------------------------------------------------------------
 ------------------------------------------------------------
   Function initialized_assigns
 ------------------------------------------------------------
 
-Goal Check 'should_succeed' (file tests/wp_acsl/opaque_struct.i, line 28):
-Let a = havoc(Init_undef_0, Init_0, p, Size_of_S1_S).
+Goal Check 'succeed' (file tests/wp_acsl/opaque_struct.i, line 31):
+Let a = havoc(Init_undef_0, Init_0, p, Length_of_S1_S).
 Assume {
   (* Heap *)
-  Type: (region(p.base) <= 0) /\ cinits(Init_0).
+  Type: (region(p.base) <= 0) /\ linked(Malloc_0) /\ cinits(Init_0).
   (* Pre-condition *)
-  Have: IsInit_S1_S(p, Init_0).
+  Have: IsInit_S1_S(p, Init_0) /\ valid_rw(Malloc_0, p, Length_of_S1_S).
   (* Call Effects *)
   Have: monotonic_init(Init_0, a).
 }
 Prove: IsInit_S1_S(p, a).
 
+------------------------------------------------------------
+
+Goal Check 'succeed' (file tests/wp_acsl/opaque_struct.i, line 32):
+Let x = p.base.
+Assume {
+  (* Heap *)
+  Type: (region(x) <= 0) /\ linked(Malloc_0).
+  (* Pre-condition *)
+  Have: valid_rw(Malloc_0, p, Length_of_S1_S).
+}
+Prove: 0 <= (BytesLength_of_S1_S * Malloc_0[x]).
+
 ------------------------------------------------------------
 ------------------------------------------------------------
   Function uninitialized_assigns
 ------------------------------------------------------------
 
-Goal Check 'should_fail' (file tests/wp_acsl/opaque_struct.i, line 39):
-Let a = havoc(Init_undef_0, Init_0, p, Size_of_S1_S).
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 47):
+Let a = havoc(Init_undef_0, Init_0, p, Length_of_S1_S).
 Assume {
   (* Heap *)
   Type: (region(p.base) <= 0) /\ cinits(Init_0).
@@ -63,8 +151,8 @@ Prove: !IsInit_S1_S(p, a).
 
 ------------------------------------------------------------
 
-Goal Check 'should_fail' (file tests/wp_acsl/opaque_struct.i, line 40):
-Let a = havoc(Init_undef_0, Init_0, p, Size_of_S1_S).
+Goal Check 'fail' (file tests/wp_acsl/opaque_struct.i, line 48):
+Let a = havoc(Init_undef_0, Init_0, p, Length_of_S1_S).
 Assume {
   (* Heap *)
   Type: (region(p.base) <= 0) /\ cinits(Init_0).
@@ -76,8 +164,23 @@ Assume {
 Prove: IsInit_S1_S(p, a).
 
 ------------------------------------------------------------
-[wp] tests/wp_acsl/opaque_struct.i:23: Warning: 
+[wp] tests/wp_acsl/opaque_struct.i:24: Warning: 
   Memory model hypotheses for function 'g':
   /*@ behavior wp_typed:
         requires \separated(p, &S1, &S2, &p); */
   void g(void);
+[wp] tests/wp_acsl/opaque_struct.i:57: Warning: 
+  Memory model hypotheses for function 'assign':
+  /*@ behavior wp_typed:
+        requires \separated(a, &S1, &S2); */
+  void assign(struct S *a);
+[wp] tests/wp_acsl/opaque_struct.i:60: Warning: 
+  Memory model hypotheses for function 'assigns_effect':
+  /*@
+     behavior wp_typed:
+       requires \separated(a, &S1, &S2);
+       requires \separated(c, &S1, &S2);
+       requires \separated(p_0, &S1, &S2);
+       requires \separated(q, &S1, &S2);
+     */
+  void assigns_effect(int *p_0, float *q, char *c, struct S *a);
diff --git a/src/plugins/wp/tests/wp_acsl/oracle_qualif/opaque_struct.res.oracle b/src/plugins/wp/tests/wp_acsl/oracle_qualif/opaque_struct.res.oracle
index a0b9b493076..d8a3007152e 100644
--- a/src/plugins/wp/tests/wp_acsl/oracle_qualif/opaque_struct.res.oracle
+++ b/src/plugins/wp/tests/wp_acsl/oracle_qualif/opaque_struct.res.oracle
@@ -2,28 +2,51 @@
 [kernel] Parsing tests/wp_acsl/opaque_struct.i (no preprocessing)
 [wp] Running WP plugin...
 [wp] Warning: Missing RTE guards
-[wp] 7 goals scheduled
-[wp] [Alt-Ergo] Goal typed_check_lemma_should_fail : Unsuccess
-[wp] [Qed] Goal typed_check_lemma_should_succeed : Valid
-[wp] [Alt-Ergo] Goal typed_assigns_check_should_fail : Unsuccess
-[wp] [Qed] Goal typed_assigns_check_should_succeed : Valid
-[wp] [Alt-Ergo] Goal typed_initialized_assigns_check_should_succeed : Valid
-[wp] [Alt-Ergo] Goal typed_uninitialized_assigns_check_should_fail : Unsuccess
-[wp] [Alt-Ergo] Goal typed_uninitialized_assigns_check_should_fail_2 : Unsuccess
-[wp] Proved goals:    3 / 7
+[wp] 13 goals scheduled
+[wp] [Alt-Ergo] Goal typed_check_lemma_fail : Unsuccess
+[wp] [Qed] Goal typed_check_lemma_succeed_L1 : Valid
+[wp] [Alt-Ergo] Goal typed_check_lemma_succeed_L2 : Valid
+[wp] [Alt-Ergo] Goal typed_assigns_check_fail : Unsuccess
+[wp] [Qed] Goal typed_assigns_check_succeed : Valid
+[wp] [Alt-Ergo] Goal typed_initialized_assigns_check_succeed : Valid
+[wp] [Alt-Ergo] Goal typed_initialized_assigns_check_succeed_2 : Valid
+[wp] [Alt-Ergo] Goal typed_uninitialized_assigns_check_fail : Unsuccess
+[wp] [Alt-Ergo] Goal typed_uninitialized_assigns_check_fail_2 : Unsuccess
+[wp] [Alt-Ergo] Goal typed_assigned_via_pointer_check_fail : Unsuccess
+[wp] [Alt-Ergo] Goal typed_assigns_effect_check_fail : Unsuccess
+[wp] [Alt-Ergo] Goal typed_assigns_effect_check_fail_2 : Unsuccess
+[wp] [Alt-Ergo] Goal typed_assigns_effect_check_succeed : Valid
+[wp] Proved goals:    6 / 13
   Qed:             2 
-  Alt-Ergo:        1  (unsuccess: 4)
+  Alt-Ergo:        4  (unsuccess: 7)
 ------------------------------------------------------------
  Axiomatics                WP     Alt-Ergo  Total   Success
-  Axiomatic test            1        -        2      50.0%
+  Axiomatic test            1        1        3      66.7%
 ------------------------------------------------------------
  Functions                 WP     Alt-Ergo  Total   Success
   assigns                   1        -        2      50.0%
-  initialized_assigns       -        1        1       100%
+  initialized_assigns       -        2        2       100%
   uninitialized_assigns     -        -        2       0.0%
+  assigned_via_pointer      -        -        1       0.0%
+  assigns_effect            -        1        3      33.3%
 ------------------------------------------------------------
-[wp] tests/wp_acsl/opaque_struct.i:23: Warning: 
+[wp] tests/wp_acsl/opaque_struct.i:24: Warning: 
   Memory model hypotheses for function 'g':
   /*@ behavior wp_typed:
         requires \separated(p, &S1, &S2, &p); */
   void g(void);
+[wp] tests/wp_acsl/opaque_struct.i:57: Warning: 
+  Memory model hypotheses for function 'assign':
+  /*@ behavior wp_typed:
+        requires \separated(a, &S1, &S2); */
+  void assign(struct S *a);
+[wp] tests/wp_acsl/opaque_struct.i:60: Warning: 
+  Memory model hypotheses for function 'assigns_effect':
+  /*@
+     behavior wp_typed:
+       requires \separated(a, &S1, &S2);
+       requires \separated(c, &S1, &S2);
+       requires \separated(p_0, &S1, &S2);
+       requires \separated(q, &S1, &S2);
+     */
+  void assigns_effect(int *p_0, float *q, char *c, struct S *a);
-- 
GitLab