From 1f8a287f21d5884e78edc81fe59bfa0714d7abe2 Mon Sep 17 00:00:00 2001
From: Virgile Prevosto <virgile.prevosto@m4x.org>
Date: Fri, 23 Feb 2024 10:23:58 +0100
Subject: [PATCH] [ir2cabs] emulate and/or normalization done by FC's own ACSL
 parser

See frama-c/frama-c#1364
---
 convert_acsl.ml                          | 27 ++++++++++++++++++++----
 tests/basic/oracle/cxx_c_link.res.oracle |  4 ----
 tests/specs/cxx_c_acsl.c                 | 13 ++++++++++++
 tests/specs/cxx_c_acsl.cpp               |  6 ++++++
 tests/specs/oracle/cxx_c_acsl.res.oracle | 17 +++++++++++++++
 5 files changed, 59 insertions(+), 8 deletions(-)
 create mode 100644 tests/specs/cxx_c_acsl.c
 create mode 100644 tests/specs/cxx_c_acsl.cpp
 create mode 100644 tests/specs/oracle/cxx_c_acsl.res.oracle

diff --git a/convert_acsl.ml b/convert_acsl.ml
index 0e9ed0a6..c844ea36 100644
--- a/convert_acsl.ml
+++ b/convert_acsl.ml
@@ -232,6 +232,25 @@ let convert_logic_reference env typ e =
       PLunop(Ustar, full_e)
     | _ -> e
 
+(* ACSL parser normalizes conjunctions and disjunctions. We do the same here
+   in order to ensure syntactical equality of ACSL predicates
+   when parsed either directly or through framaCIRGen in an extern "C". *)
+let rec pland ce1 ce2 =
+  match ce2.lexpr_node with
+  | PLand(ce3,ce4) ->
+    let lexpr_loc = fst ce1.lexpr_loc, snd ce3.lexpr_loc in
+    let lexpr_node = pland ce1 ce3 in
+    PLand ({ lexpr_loc; lexpr_node}, ce4)
+  | _ -> PLand (ce1, ce2)
+
+let rec plor ce1 ce2 =
+  match ce2.lexpr_node with
+  | PLor(ce3, ce4) ->
+    let lexpr_loc = fst ce1.lexpr_loc, snd ce3.lexpr_loc in
+    let lexpr_node = plor ce1 ce3 in
+    PLor ({ lexpr_loc; lexpr_node }, ce4)
+  | _ -> PLor (ce1, ce2)
+
 let rec convert_logic_expr env e =
   let env = Convert_env.set_loc env e.loc in
   let lexpr_loc = Convert_env.get_loc env in
@@ -283,11 +302,11 @@ and convert_logic_expr_node env = function
   | TBinOp(BOLogicalAnd,e1,e2) ->
     let env, ce1 = convert_logic_expr env e1 in
     let env, ce2 = convert_logic_expr env e2 in
-    env, PLand(ce1,ce2)
+    env, pland ce1 ce2
   | TBinOp(BOLogicalOr,e1,e2) ->
     let env, ce1 = convert_logic_expr env e1 in
     let env, ce2 = convert_logic_expr env e2 in
-    env, PLor(ce1,ce2)
+    env, plor ce1 ce2
   | TBinOp _ -> Convert_env.fatal env "Unknown binary operator in logic"
   | TCastE(typ,e) ->
     let env, ctyp = convert_logic_type env typ in
@@ -487,11 +506,11 @@ and convert_pred env = function
   | Pand(p1,p2) ->
     let env, p1 = convert_pred_named env p1 in
     let env, p2 = convert_pred_named env p2 in
-    env, PLand(p1,p2)
+    env, pland p1 p2
   | Por(p1,p2) ->
     let env, p1 = convert_pred_named env p1 in
     let env, p2 = convert_pred_named env p2 in
-    env, PLor(p1,p2)
+    env, plor p1 p2
   | Pxor(p1,p2) ->
     let env, p1 = convert_pred_named env p1 in
     let env, p2 = convert_pred_named env p2 in
diff --git a/tests/basic/oracle/cxx_c_link.res.oracle b/tests/basic/oracle/cxx_c_link.res.oracle
index 247cba88..82252579 100644
--- a/tests/basic/oracle/cxx_c_link.res.oracle
+++ b/tests/basic/oracle/cxx_c_link.res.oracle
@@ -1,10 +1,6 @@
 [kernel] Parsing cxx_c_link.cpp (external front-end)
 Now output intermediate result
 [kernel] Parsing cxx_c_link.c (with preprocessing)
-[kernel] FRAMAC_SHARE/libc/stdlib.h:322: Warning: 
-  found two contracts. Merging them
-[kernel] FRAMAC_SHARE/libc/stdlib.h:330: Warning: 
-  found two contracts. Merging them
 /* Generated by Frama-C */
 #include "__fc_alloc_axiomatic.h"
 #include "stdlib.h"
diff --git a/tests/specs/cxx_c_acsl.c b/tests/specs/cxx_c_acsl.c
new file mode 100644
index 00000000..30d915ed
--- /dev/null
+++ b/tests/specs/cxx_c_acsl.c
@@ -0,0 +1,13 @@
+/* run.config
+DONTRUN: main test is the .cpp file
+*/
+
+//@ predicate test1(double x) = \is_finite(x) && 0.0 < x && x < 2.0;
+
+//@ predicate test2(double x) = \is_finite(x) && 0.0 < x < 2.0;
+
+//@ predicate test3(double x) = (\is_finite(x) && 0.0 < x) && x < 2.0;
+
+//@ predicate test4(double x) = \is_finite(x) && (0.0 < x && x < 2.0);
+
+//@ predicate test5(double x) = (\is_finite(x) && 0.0 < x) && (x < 2.0 && \is_finite(x));
diff --git a/tests/specs/cxx_c_acsl.cpp b/tests/specs/cxx_c_acsl.cpp
new file mode 100644
index 00000000..c7cec0e2
--- /dev/null
+++ b/tests/specs/cxx_c_acsl.cpp
@@ -0,0 +1,6 @@
+/* run.config
+OPT: %{dep:@PTEST_DIR@/@PTEST_NAME@.c} -print
+*/
+extern "C" {
+#include "cxx_c_acsl.c"
+}
diff --git a/tests/specs/oracle/cxx_c_acsl.res.oracle b/tests/specs/oracle/cxx_c_acsl.res.oracle
new file mode 100644
index 00000000..135f9fd8
--- /dev/null
+++ b/tests/specs/oracle/cxx_c_acsl.res.oracle
@@ -0,0 +1,17 @@
+[kernel] Parsing cxx_c_acsl.cpp (external front-end)
+Now output intermediate result
+[kernel] Parsing cxx_c_acsl.c (with preprocessing)
+/* Generated by Frama-C */
+/*@ predicate test1(double x) = \is_finite(x) ∧ 0.0 < x < 2.0;
+ */
+/*@ predicate test2(double x) = \is_finite(x) ∧ 0.0 < x < 2.0;
+ */
+/*@ predicate test3(double x) = \is_finite(x) ∧ 0.0 < x < 2.0;
+ */
+/*@ predicate test4(double x) = \is_finite(x) ∧ 0.0 < x < 2.0;
+ */
+/*@
+predicate test5(double x) =
+  \is_finite(x) ∧ 0.0 < x < 2.0 ∧ \is_finite(x);
+ */
+
-- 
GitLab