From b7fe57005bee6ed79ede4b2ac0080c7644dfd8bb Mon Sep 17 00:00:00 2001
From: Virgile Prevosto <virgile.prevosto@m4x.org>
Date: Mon, 25 Feb 2019 15:10:51 +0100
Subject: [PATCH] [ACSL] Make `Logic_interp` aware of local variables scopes

Note: at some point, it might be useful to make `term` and `pred` take a
`kinstr` as argument to have a better scope for typechecking.
---
 src/kernel_services/analysis/logic_interp.ml  | 52 +++++++++++++++----
 .../ast_data/kernel_function.ml               |  6 +++
 .../ast_data/kernel_function.mli              |  8 +++
 3 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/src/kernel_services/analysis/logic_interp.ml b/src/kernel_services/analysis/logic_interp.ml
index 8a82bb7009c..e8af8bc2a58 100644
--- a/src/kernel_services/analysis/logic_interp.ml
+++ b/src/kernel_services/analysis/logic_interp.ml
@@ -27,14 +27,43 @@ open Cil_datatype
 exception Error of Cil_types.location * string
 exception Unbound of string
 
-let find_var kf x =
+let find_var kf kinstr ?label ~var =
   let vi =
-    try Globals.Vars.find_from_astinfo x (VLocal kf)
+    try
+      let vi = Globals.Vars.find_from_astinfo var (VLocal kf) in
+      (match kinstr with
+        | Kglobal -> vi (* don't refine search: the Kglobal here
+                           does not indicate the function contract, but merely
+                           the fact that we do not have any information about
+                           the targeted program point. Hence, no scope check
+                           can be performed or we might reject many legitimate
+                           terms and predicates.
+                        *)
+        | Kstmt stmt ->
+          let scope =
+            match label with
+            | None | Some "Here" | Some "Post" | Some "Old" -> stmt
+            | Some "Pre" -> raise Not_found (* no local variable in scope. *)
+            | Some "Init" -> raise Not_found (* no local variable in scope. *)
+            | Some "LoopEntry" | Some "LoopCurrent" ->
+              if not (Kernel_function.stmt_in_loop kf stmt) then
+                Kernel.fatal
+                  "Use of LoopEntry or LoopCurrent outside of a loop";
+              Kernel_function.find_enclosing_loop kf stmt
+            | Some l ->
+              (try let s = Kernel_function.find_label kf l in !s
+               with Not_found ->
+                 Kernel.fatal
+                  "Use of label %s that does not exist in function %a"
+                  l Kernel_function.pretty kf)
+          in
+         if Kernel_function.var_is_in_scope scope vi then vi
+         else raise Not_found)
     with Not_found ->
       try
-        Globals.Vars.find_from_astinfo x (VFormal kf)
+        Globals.Vars.find_from_astinfo var (VFormal kf)
       with Not_found ->
-        Globals.Vars.find_from_astinfo x VGlobal
+        Globals.Vars.find_from_astinfo var VGlobal
   in
   cvar_to_lvar vi
 
@@ -46,18 +75,21 @@ let find_var kf x =
 module DefaultLT (X:
 sig
   val kf: Kernel_function.t
-  val in_loop: bool (* Only useful for code annotations *)
+  val kinstr: Cil_types.kinstr
 end) =
   Logic_typing.Make
     (struct
       let anonCompFieldName = Cabs2cil.anonCompFieldName
       let conditionalConversion = Cabs2cil.logicConditionalConversion
 
-      let is_loop () = X.in_loop
+      let is_loop () =
+        match X.kinstr with
+          | Kglobal -> false
+          | Kstmt s -> Kernel_function.stmt_in_loop X.kf s
 
       let find_macro _ = raise Not_found
 
-      let find_var x = find_var X.kf x
+      let find_var ?label ~var = find_var X.kf X.kinstr ?label ~var
 
       let find_enum_tag x =
         try
@@ -118,7 +150,7 @@ let code_annot kf stmt s =
   sync_typedefs ();
   let module LT = DefaultLT(struct
     let kf = kf
-    let in_loop = Kernel_function.stmt_in_loop kf stmt
+    let kinstr = Kstmt stmt
   end) in
   let loc = Stmt.loc stmt in
   let pa =
@@ -141,7 +173,7 @@ let term kf ?(loc=Location.unknown) ?(env=default_term_env ()) s =
   sync_typedefs ();
   let module LT = DefaultLT(struct
     let kf = kf
-    let in_loop = false (* unused *)
+    let kinstr = Kglobal
   end) in
   let pa_expr = Extlib.opt_map snd (Logic_lexer.lexpr (fst loc, s)) in
   let parse pa_expr = LT.term env pa_expr in
@@ -156,7 +188,7 @@ let predicate kf ?(loc=Location.unknown) ?(env=default_term_env ()) s =
   sync_typedefs ();
   let module LT = DefaultLT(struct
     let kf = kf
-    let in_loop = false (* unused *)
+    let kinstr = Kglobal
   end) in
   let pa_expr = Extlib.opt_map snd (Logic_lexer.lexpr (fst loc, s)) in
   let parse pa_expr = LT.predicate env pa_expr in
diff --git a/src/kernel_services/ast_data/kernel_function.ml b/src/kernel_services/ast_data/kernel_function.ml
index 1d7d7d982b0..8e876e4d23b 100644
--- a/src/kernel_services/ast_data/kernel_function.ml
+++ b/src/kernel_services/ast_data/kernel_function.ml
@@ -425,6 +425,12 @@ let find_syntactic_callsites kf =
   try CallSites.find table kf
   with Not_found -> []
 
+let var_is_in_scope stmt vi =
+  let blocks = find_all_enclosing_blocks stmt in
+  List.exists
+    (fun b -> List.exists (Cil_datatype.Varinfo.equal vi) b.blocals)
+    blocks
+
 (* ************************************************************************* *)
 (** {2 Checkers} *)
 (* ************************************************************************* *)
diff --git a/src/kernel_services/ast_data/kernel_function.mli b/src/kernel_services/ast_data/kernel_function.mli
index d6700783c49..18c1dac1287 100644
--- a/src/kernel_services/ast_data/kernel_function.mli
+++ b/src/kernel_services/ast_data/kernel_function.mli
@@ -127,6 +127,14 @@ val find_syntactic_callsites : t -> (t * stmt) list
       [stmt].
       @since Carbon-20110201 *)
 
+val var_is_in_scope: stmt -> varinfo -> bool
+  (** [var_is_in_scope kf stmt vi] returns [true] iff the local variable [vi]
+      is syntactically visible from statement [stmt] in function [kf]. Note
+      that on the contrary to {!Globals.Syntactic_search.find_in_scope}, the
+      variable is searched according to its [vid], not its [vorig_name].
+
+      @since Frama-C+dev *)
+
 (* ************************************************************************* *)
 (** {2 Checkers} *)
 (* ************************************************************************* *)
-- 
GitLab