From fceda1b22dedef49dc9fbfc627c7ce437b5ca4ae Mon Sep 17 00:00:00 2001
From: Thibaut Benjamin <thibaut.benjamin@gmail.com>
Date: Tue, 9 Aug 2022 17:08:26 +0200
Subject: [PATCH] [e-acsl] improve support for functions returning structures

---
 .../e-acsl/src/code_generator/assigns.ml      | 19 +++--
 .../e-acsl/src/code_generator/assigns.mli     |  6 +-
 .../src/code_generator/logic_functions.ml     | 12 ++-
 src/plugins/e-acsl/tests/arith/functions.c    |  1 +
 .../tests/arith/oracle/functions.res.oracle   | 18 ++++-
 .../e-acsl/tests/arith/oracle/gen_functions.c | 73 ++++++++++++-------
 6 files changed, 87 insertions(+), 42 deletions(-)

diff --git a/src/plugins/e-acsl/src/code_generator/assigns.ml b/src/plugins/e-acsl/src/code_generator/assigns.ml
index eaa80b49e8f..bf8e2dddc4c 100644
--- a/src/plugins/e-acsl/src/code_generator/assigns.ml
+++ b/src/plugins/e-acsl/src/code_generator/assigns.ml
@@ -81,10 +81,15 @@ let rec get_assigns_from ~loc env lprofile lv =
 
 (* Special case when the function takes an extra argument as its result:
    For the argument [__e_acsl_mpz_t *__retres_arg], this function generates the
-   expression [( *__retres_arg )[0]] *)
-let get_gmp_integer ~loc vi =
-  Smart_exp.lval
-    ~loc
-    (Mem
-       (Smart_exp.lval  ~loc (Var vi, NoOffset)),
-     (Index (Cil.zero ~loc, NoOffset)))
+   expression [( *__retres_arg )[0]]. For a struct argument, this function just
+   generates the variable corresponding to the argument. *)
+let get_assigned_var ~loc vi ret_gmp =
+  let var =
+    if ret_gmp then
+      Smart_exp.lval
+        ~loc
+        (Mem
+           (Smart_exp.lval  ~loc (Var vi, NoOffset)),
+         (Index (Cil.zero ~loc, NoOffset)))
+    else Smart_exp.lval ~loc (Var vi, NoOffset)
+  in Logic_utils.expr_to_term var
diff --git a/src/plugins/e-acsl/src/code_generator/assigns.mli b/src/plugins/e-acsl/src/code_generator/assigns.mli
index c245cd4de58..650214584a2 100644
--- a/src/plugins/e-acsl/src/code_generator/assigns.mli
+++ b/src/plugins/e-acsl/src/code_generator/assigns.mli
@@ -31,7 +31,7 @@ val get_assigns_from :
 (* @returns the list of expressions that are allowed to be used to assign the
    the result of a logic function *)
 
-val get_gmp_integer :
-  loc:Cil_types.location -> Cil_types.varinfo -> Cil_types.exp
+val get_assigned_var :
+  loc:Cil_types.location -> Cil_types.varinfo -> bool -> Cil_types.term
 (* @returns the expression that gets assigned when the result of the function is
-   a pointer on a GMP type *)
+   passed as an additional argument *)
diff --git a/src/plugins/e-acsl/src/code_generator/logic_functions.ml b/src/plugins/e-acsl/src/code_generator/logic_functions.ml
index 331ec21a4ea..a0b88104c04 100644
--- a/src/plugins/e-acsl/src/code_generator/logic_functions.ml
+++ b/src/plugins/e-acsl/src/code_generator/logic_functions.ml
@@ -55,7 +55,14 @@ let term_to_exp_ref
 
 (* @return true iff the result of the function is provided by reference as the
    first extra argument at each call *)
-let result_as_extra_argument = Gmp_types.Z.is_t
+let result_as_extra_argument typ =
+  let is_composite = function
+    | TComp _ | TPtr _ | TArray _ -> true
+    | TInt _ | TVoid _  | TFloat _ | TFun _ | TNamed _ | TEnum _
+    | TBuiltin_va_list _ -> false
+  in
+  Gmp_types.Z.is_t typ ||
+  is_composite typ
 (* TODO: to be extended to any compound type? E.g. returning a struct is not
    good practice... *)
 
@@ -151,6 +158,7 @@ let generate_kf ~loc fname env ret_ty params_ty params_ival li =
   in
   (* build the varinfo storing the result *)
   let res_as_extra_arg = result_as_extra_argument ret_ty in
+  let ret_gmp = Gmp_types.Z.is_t ret_ty in
   let ret_vi, ret_ty, params_with_ret, params_ty_with_ret =
     let vname = "__retres" in
     if res_as_extra_arg then
@@ -209,7 +217,7 @@ let generate_kf ~loc fname env ret_ty params_ty params_ival li =
     let assigned_var =
       Logic_const.new_identified_term
         (if res_as_extra_arg then
-           Logic_utils.expr_to_term (Assigns.get_gmp_integer ~loc ret_vi)
+           Assigns.get_assigned_var ~loc ret_vi ret_gmp
          else
            Logic_const.tresult fundec.svar.vtype)
     in
diff --git a/src/plugins/e-acsl/tests/arith/functions.c b/src/plugins/e-acsl/tests/arith/functions.c
index 428a12d2cb2..f4b773c2774 100644
--- a/src/plugins/e-acsl/tests/arith/functions.c
+++ b/src/plugins/e-acsl/tests/arith/functions.c
@@ -67,6 +67,7 @@ int main(void) {
   mystruct m;
   m.k = 8;
   m.l = 9;
+  /*@ assert \let r = t1(m); r.k == 8; */;
   /*@ assert t2(t1(m)) == 17; */;
 
   k(9);
diff --git a/src/plugins/e-acsl/tests/arith/oracle/functions.res.oracle b/src/plugins/e-acsl/tests/arith/oracle/functions.res.oracle
index 722a30329dc..cc5b0778cf1 100644
--- a/src/plugins/e-acsl/tests/arith/oracle/functions.res.oracle
+++ b/src/plugins/e-acsl/tests/arith/oracle/functions.res.oracle
@@ -1,4 +1,7 @@
 [e-acsl] beginning translation.
+[e-acsl] functions.c:70: Warning: 
+  E-ACSL construct `function application' is not yet supported.
+  Ignoring annotation.
 [e-acsl] translation done in project "e-acsl".
 [eva:alarm] functions.c:48: Warning: 
   function __e_acsl_assert_register_int: precondition data->values == \null ||
@@ -46,16 +49,23 @@
   function __e_acsl_assert_register_struct: precondition data->values == \null ||
                                                          \valid(data->values) got status unknown.
 [eva:alarm] functions.c:70: Warning: 
+  function __e_acsl_assert_register_struct: precondition data->values == \null ||
+                                                         \valid(data->values) got status unknown.
+[eva:alarm] functions.c:70: Warning: assertion got status unknown.
+[eva:alarm] functions.c:71: Warning: 
+  function __e_acsl_assert_register_struct: precondition data->values == \null ||
+                                                         \valid(data->values) got status unknown.
+[eva:alarm] functions.c:71: Warning: 
   function __e_acsl_assert_register_long: precondition data->values == \null ||
                                                        \valid(data->values) got status unknown.
 [eva:alarm] functions.c:28: Warning: 
   function __e_acsl_assert_register_int: precondition data->values == \null ||
                                                       \valid(data->values) got status unknown.
-[eva:alarm] functions.c:75: Warning: 
+[eva:alarm] functions.c:76: Warning: 
   non-finite double value. assert \is_finite(__gen_e_acsl__9);
-[eva:alarm] functions.c:75: Warning: 
+[eva:alarm] functions.c:76: Warning: 
   function __e_acsl_assert_register_double: precondition data->values == \null ||
                                                          \valid(data->values) got status unknown.
-[eva:alarm] functions.c:75: Warning: 
+[eva:alarm] functions.c:76: Warning: 
   function __e_acsl_assert, behavior blocking: precondition got status unknown.
-[eva:alarm] functions.c:77: Warning: assertion got status unknown.
+[eva:alarm] functions.c:78: Warning: assertion got status unknown.
diff --git a/src/plugins/e-acsl/tests/arith/oracle/gen_functions.c b/src/plugins/e-acsl/tests/arith/oracle/gen_functions.c
index 192abded1c0..7d6406619df 100644
--- a/src/plugins/e-acsl/tests/arith/oracle/gen_functions.c
+++ b/src/plugins/e-acsl/tests/arith/oracle/gen_functions.c
@@ -325,63 +325,84 @@ int main(void)
   m.k = 8;
   m.l = 9;
   {
+    mystruct __gen_e_acsl_r;
     mystruct __gen_e_acsl_t1_2;
-    long __gen_e_acsl_t2_2;
     __e_acsl_assert_data_t __gen_e_acsl_assert_data_11 =
       {.values = (void *)0};
     __gen_e_acsl_t1_2 = __gen_e_acsl_t1(m);
-    __gen_e_acsl_t2_2 = __gen_e_acsl_t2(__gen_e_acsl_t1_2);
+    __gen_e_acsl_r = __gen_e_acsl_t1_2;
+    __e_acsl_assert_register_int(& __gen_e_acsl_assert_data_11,"r.k",0,
+                                 __gen_e_acsl_r.k);
     __e_acsl_assert_register_struct(& __gen_e_acsl_assert_data_11,"m");
     __e_acsl_assert_register_struct(& __gen_e_acsl_assert_data_11,"t1(m)");
-    __e_acsl_assert_register_long(& __gen_e_acsl_assert_data_11,"t2(t1(m))",
-                                  0,__gen_e_acsl_t2_2);
     __gen_e_acsl_assert_data_11.blocking = 1;
     __gen_e_acsl_assert_data_11.kind = "Assertion";
-    __gen_e_acsl_assert_data_11.pred_txt = "t2(t1(m)) == 17";
+    __gen_e_acsl_assert_data_11.pred_txt = "\\let r = t1(m); r.k == 8";
     __gen_e_acsl_assert_data_11.file = "functions.c";
     __gen_e_acsl_assert_data_11.fct = "main";
     __gen_e_acsl_assert_data_11.line = 70;
-    __e_acsl_assert(__gen_e_acsl_t2_2 == 17L,& __gen_e_acsl_assert_data_11);
+    __e_acsl_assert(__gen_e_acsl_r.k == 8,& __gen_e_acsl_assert_data_11);
     __e_acsl_assert_clean(& __gen_e_acsl_assert_data_11);
   }
-  /*@ assert t2(t1(m)) == 17; */ ;
-  __gen_e_acsl_k(9);
-  double d = 2.0;
+  /*@ assert \let r = t1(m); r.k == 8; */ ;
   {
-    double __gen_e_acsl_f2_2;
+    mystruct __gen_e_acsl_t1_4;
+    long __gen_e_acsl_t2_2;
     __e_acsl_assert_data_t __gen_e_acsl_assert_data_12 =
       {.values = (void *)0};
-    __gen_e_acsl_f2_2 = __gen_e_acsl_f2(d);
-    __e_acsl_assert_register_double(& __gen_e_acsl_assert_data_12,"d",d);
-    __e_acsl_assert_register_double(& __gen_e_acsl_assert_data_12,"f2(d)",
-                                    __gen_e_acsl_f2_2);
+    __gen_e_acsl_t1_4 = __gen_e_acsl_t1(m);
+    __gen_e_acsl_t2_2 = __gen_e_acsl_t2(__gen_e_acsl_t1_4);
+    __e_acsl_assert_register_struct(& __gen_e_acsl_assert_data_12,"m");
+    __e_acsl_assert_register_struct(& __gen_e_acsl_assert_data_12,"t1(m)");
+    __e_acsl_assert_register_long(& __gen_e_acsl_assert_data_12,"t2(t1(m))",
+                                  0,__gen_e_acsl_t2_2);
     __gen_e_acsl_assert_data_12.blocking = 1;
     __gen_e_acsl_assert_data_12.kind = "Assertion";
-    __gen_e_acsl_assert_data_12.pred_txt = "f2(d) > 0";
+    __gen_e_acsl_assert_data_12.pred_txt = "t2(t1(m)) == 17";
     __gen_e_acsl_assert_data_12.file = "functions.c";
     __gen_e_acsl_assert_data_12.fct = "main";
-    __gen_e_acsl_assert_data_12.line = 75;
-    __e_acsl_assert(__gen_e_acsl_f2_2 > 0.,& __gen_e_acsl_assert_data_12);
+    __gen_e_acsl_assert_data_12.line = 71;
+    __e_acsl_assert(__gen_e_acsl_t2_2 == 17L,& __gen_e_acsl_assert_data_12);
     __e_acsl_assert_clean(& __gen_e_acsl_assert_data_12);
   }
-  /*@ assert f2(d) > 0; */ ;
+  /*@ assert t2(t1(m)) == 17; */ ;
+  __gen_e_acsl_k(9);
+  double d = 2.0;
   {
-    int __gen_e_acsl_f_sum_2;
+    double __gen_e_acsl_f2_2;
     __e_acsl_assert_data_t __gen_e_acsl_assert_data_13 =
       {.values = (void *)0};
-    __gen_e_acsl_f_sum_2 = __gen_e_acsl_f_sum(100);
-    __e_acsl_assert_register_int(& __gen_e_acsl_assert_data_13,"f_sum(100)",
-                                 0,__gen_e_acsl_f_sum_2);
+    __gen_e_acsl_f2_2 = __gen_e_acsl_f2(d);
+    __e_acsl_assert_register_double(& __gen_e_acsl_assert_data_13,"d",d);
+    __e_acsl_assert_register_double(& __gen_e_acsl_assert_data_13,"f2(d)",
+                                    __gen_e_acsl_f2_2);
     __gen_e_acsl_assert_data_13.blocking = 1;
     __gen_e_acsl_assert_data_13.kind = "Assertion";
-    __gen_e_acsl_assert_data_13.pred_txt = "f_sum(100) == 100";
+    __gen_e_acsl_assert_data_13.pred_txt = "f2(d) > 0";
     __gen_e_acsl_assert_data_13.file = "functions.c";
     __gen_e_acsl_assert_data_13.fct = "main";
-    __gen_e_acsl_assert_data_13.line = 77;
-    __e_acsl_assert(__gen_e_acsl_f_sum_2 == 100,
-                    & __gen_e_acsl_assert_data_13);
+    __gen_e_acsl_assert_data_13.line = 76;
+    __e_acsl_assert(__gen_e_acsl_f2_2 > 0.,& __gen_e_acsl_assert_data_13);
     __e_acsl_assert_clean(& __gen_e_acsl_assert_data_13);
   }
+  /*@ assert f2(d) > 0; */ ;
+  {
+    int __gen_e_acsl_f_sum_2;
+    __e_acsl_assert_data_t __gen_e_acsl_assert_data_14 =
+      {.values = (void *)0};
+    __gen_e_acsl_f_sum_2 = __gen_e_acsl_f_sum(100);
+    __e_acsl_assert_register_int(& __gen_e_acsl_assert_data_14,"f_sum(100)",
+                                 0,__gen_e_acsl_f_sum_2);
+    __gen_e_acsl_assert_data_14.blocking = 1;
+    __gen_e_acsl_assert_data_14.kind = "Assertion";
+    __gen_e_acsl_assert_data_14.pred_txt = "f_sum(100) == 100";
+    __gen_e_acsl_assert_data_14.file = "functions.c";
+    __gen_e_acsl_assert_data_14.fct = "main";
+    __gen_e_acsl_assert_data_14.line = 78;
+    __e_acsl_assert(__gen_e_acsl_f_sum_2 == 100,
+                    & __gen_e_acsl_assert_data_14);
+    __e_acsl_assert_clean(& __gen_e_acsl_assert_data_14);
+  }
   /*@ assert f_sum(100) == 100; */ ;
   __retres = 0;
   __e_acsl_memory_clean();
-- 
GitLab