You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2036 lines
69 KiB

(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
(** The Smallfoot Intermediate Language *)
open! IStd
module Hashtbl = Caml.Hashtbl
module L = Logging
module F = Format
(** {2 Programs and Types} *)
(** Kind of prune instruction *)
type if_kind =
| Ik_bexp
(* boolean expressions, and exp ? exp : exp *)
| Ik_dowhile
| Ik_for
| Ik_if
| Ik_land_lor
(* obtained from translation of && or || *)
| Ik_while
| Ik_switch
[@@deriving compare]
(** An instruction. *)
type instr =
(** Load a value from the heap into an identifier.
[x = *lexp:typ] where
[lexp] is an expression denoting a heap address
[typ] is the root type of [lexp]. *)
(* Note for frontend writers:
[x] must be used in a subsequent instruction, otherwise the entire
`Load` instruction may be eliminated by copy-propagation. *)
| Load of Ident.t * Exp.t * Typ.t * Location.t
(** Store the value of an expression into the heap.
[*lexp1:typ = exp2] where
[lexp1] is an expression denoting a heap address
[typ] is the root type of [lexp1]
[exp2] is the expression whose value is store. *)
| Store of Exp.t * Typ.t * Exp.t * Location.t
(** prune the state based on [exp=1], the boolean indicates whether true branch *)
| Prune of Exp.t * Location.t * bool * if_kind
(** [Call (ret_id, e_fun, arg_ts, loc, call_flags)] represents an instruction
[ret_id = e_fun(arg_ts);]. The return value is ignored when [ret_id = None]. *)
| Call of (Ident.t * Typ.t) option * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t
(** nullify stack variable *)
| Nullify of Pvar.t * Location.t
| Abstract of Location.t (** apply abstraction *)
| Remove_temps of Ident.t list * Location.t (** remove temporaries *)
| Declare_locals of (Pvar.t * Typ.t) list * Location.t (** declare local variables *)
[@@deriving compare]
let equal_instr = [%compare.equal : instr]
let skip_instr = Remove_temps ([], Location.dummy)
(** Check if an instruction is auxiliary, or if it comes from source instructions. *)
let instr_is_auxiliary = function
| Load _ | Store _ | Prune _ | Call _
-> false
| Nullify _ | Abstract _ | Remove_temps _ | Declare_locals _
-> true
(** offset for an lvalue *)
type offset = Off_fld of Typ.Fieldname.t * Typ.t | Off_index of Exp.t
(** {2 Components of Propositions} *)
(** an atom is a pure atomic formula *)
type atom =
| Aeq of Exp.t * Exp.t (** equality *)
| Aneq of Exp.t * Exp.t (** disequality *)
| Apred of PredSymb.t * (** predicate symbol applied to exps *) Exp.t list
| Anpred of PredSymb.t * (** negated predicate symbol applied to exps *) Exp.t list
[@@deriving compare]
let equal_atom = [%compare.equal : atom]
let atom_has_local_addr a =
match a with
| Aeq (e0, e1) | Aneq (e0, e1)
-> Exp.has_local_addr e0 || Exp.has_local_addr e1
| Apred _ | Anpred _
-> false
(** kind of lseg or dllseg predicates *)
type lseg_kind =
| Lseg_NE (** nonempty (possibly circular) listseg *)
| Lseg_PE (** possibly empty (possibly circular) listseg *)
[@@deriving compare]
let equal_lseg_kind = [%compare.equal : lseg_kind]
(** The boolean is true when the pointer was dereferenced without testing for zero. *)
type zero_flag = bool option [@@deriving compare]
(** True when the value was obtained by doing case analysis on null in a procedure call. *)
type null_case_flag = bool [@@deriving compare]
(** instrumentation of heap values *)
type inst =
| Iabstraction
| Iactual_precondition
| Ialloc
| Iformal of zero_flag * null_case_flag
| Iinitial
| Ilookup
| Inone
| Inullify
| Irearrange of zero_flag * null_case_flag * int * PredSymb.path_pos
| Itaint
| Iupdate of zero_flag * null_case_flag * int * PredSymb.path_pos
| Ireturn_from_call of int
[@@deriving compare]
let equal_inst = [%compare.equal : inst]
(** structured expressions represent a value of structured type, such as an array or a struct. *)
type 'inst strexp0 =
| Eexp of Exp.t * 'inst (** Base case: expression with instrumentation *)
| Estruct of (Typ.Fieldname.t * 'inst strexp0) list * 'inst (** C structure *)
(** Array of given length
There are two conditions imposed / used in the array case.
First, if some index and value pair appears inside an array
in a strexp, then the index is less than the length of the array.
For instance, x |->[10 | e1: v1] implies that e1 <= 9.
Second, if two indices appear in an array, they should be different.
For instance, x |->[10 | e1: v1, e2: v2] implies that e1 != e2. *)
| Earray of Exp.t * (Exp.t * 'inst strexp0) list * 'inst
[@@deriving compare]
type strexp = inst strexp0
let compare_strexp ?(inst= false) se1 se2 =
compare_strexp0 (match inst with true -> compare_inst | false -> fun _ _ -> 0) se1 se2
let equal_strexp ?(inst= false) se1 se2 = Int.equal (compare_strexp ~inst se1 se2) 0
(** an atomic heap predicate *)
type 'inst hpred0 =
| Hpointsto of Exp.t * 'inst strexp0 * Exp.t
(** represents [exp|->strexp:typexp] where [typexp]
is an expression representing a type, e.h. [sizeof(t)]. *)
| Hlseg of lseg_kind * 'inst hpara0 * Exp.t * Exp.t * Exp.t list
(** higher - order predicate for singly - linked lists.
Should ensure that exp1!= exp2 implies that exp1 is allocated.
This assumption is used in the rearrangement. The last [exp list] parameter
is used to denote the shared links by all the nodes in the list. *)
| Hdllseg of lseg_kind * 'inst hpara_dll0 * Exp.t * Exp.t * Exp.t * Exp.t * Exp.t list
(** higher-order predicate for doubly-linked lists.
Parameter for the higher-order singly-linked list predicate.
Means "lambda (root,next,svars). Exists evars. body".
Assume that root, next, svars, evars are disjoint sets of
primed identifiers, and include all the free primed identifiers in body.
body should not contain any non - primed identifiers or program
variables (i.e. pvars). *)
[@@deriving compare]
and 'inst hpara0 =
{root: Ident.t; next: Ident.t; svars: Ident.t list; evars: Ident.t list; body: 'inst hpred0 list}
[@@deriving compare]
(** parameter for the higher-order doubly-linked list predicates.
Assume that all the free identifiers in body_dll should belong to
cell, blink, flink, svars_dll, evars_dll. *)
and 'inst hpara_dll0 =
{ cell: Ident.t (** address cell *)
; blink: Ident.t (** backward link *)
; flink: Ident.t (** forward link *)
; svars_dll: Ident.t list
; evars_dll: Ident.t list
; body_dll: 'inst hpred0 list }
[@@deriving compare]
type hpred = inst hpred0
(** Comparsion between heap predicates. Reverse natural order, and order first by anchor exp. *)
let compare_hpred ?(inst= false) hpred1 hpred2 =
compare_hpred0 (match inst with true -> compare_inst | false -> fun _ _ -> 0) hpred1 hpred2
let equal_hpred ?(inst= false) hpred1 hpred2 = Int.equal (compare_hpred ~inst hpred1 hpred2) 0
type hpara = inst hpara0
let compare_hpara = compare_hpara0 (fun _ _ -> 0)
let equal_hpara = [%compare.equal : hpara]
type hpara_dll = inst hpara_dll0
let compare_hpara_dll = compare_hpara_dll0 (fun _ _ -> 0)
let equal_hpara_dll = [%compare.equal : hpara_dll]
(** Return the lhs expression of a hpred *)
let hpred_get_lhs h =
match h
with Hpointsto (e, _, _) | Hlseg (_, _, e, _, _) | Hdllseg (_, _, e, _, _, _, _) -> e
(** {2 Comparision and Inspection Functions} *)
let has_objc_ref_counter tenv hpred =
match hpred with
| Hpointsto (_, _, Sizeof {typ= {desc= Tstruct name}}) -> (
match Tenv.lookup tenv name with
| Some {fields}
-> List.exists ~f:Typ.Struct.is_objc_ref_counter_field fields
| _
-> false )
| _
-> false
(** Returns the zero value of a type, for int, float and ptr types, None othwewise *)
let zero_value_of_numerical_type_option typ =
match typ.Typ.desc with
| Typ.Tint _
-> Some (Exp.Const (Cint IntLit.zero))
| Typ.Tfloat _
-> Some (Exp.Const (Cfloat 0.0))
| Typ.Tptr _
-> Some (Exp.Const (Cint IntLit.null))
| _
-> None
(** Returns the zero value of a type, for int, float and ptr types, fail otherwise *)
let zero_value_of_numerical_type typ = Option.value_exn (zero_value_of_numerical_type_option typ)
(** Make a static local name in objc *)
let mk_static_local_name pname vname = pname ^ "_" ^ vname
(** Check if a pvar is a local static in objc *)
let is_static_local_name pname pvar =
(* local static name is of the form procname_varname *)
let var_name = Mangled.to_string (Pvar.get_name pvar) in
match Str.split_delim (Str.regexp_string pname) var_name with [_; _] -> true | _ -> false
(** {2 Sets of expressions} *)
let elist_to_eset es = List.fold ~f:(fun set e -> Exp.Set.add e set) ~init:Exp.Set.empty es
(** {2 Sets of heap predicates} *)
module HpredSet = Caml.Set.Make (struct
type t = hpred
let compare = compare_hpred ~inst:false
end)
(** {2 Pretty Printing} *)
(** Begin change color if using diff printing, return updated printenv and change status *)
let color_pre_wrapper pe f x =
if Config.print_using_diff && pe.Pp.kind <> Pp.TEXT then
let color = pe.Pp.cmap_norm (Obj.repr x) in
if color <> pe.Pp.color then (
( if Pp.equal_print_kind pe.Pp.kind Pp.HTML then Io_infer.Html.pp_start_color
else Latex.pp_color )
f color ;
if Pp.equal_color color Pp.Red then
(Pp.{(** All subexpressiona red *)
pe with cmap_norm= colormap_red; color= Red}, true)
else (Pp.{pe with color}, true) )
else (pe, false)
else (pe, false)
(** Close color annotation if changed *)
let color_post_wrapper changed pe f =
if changed then
if Pp.equal_print_kind pe.Pp.kind Pp.HTML then Io_infer.Html.pp_end_color f ()
else Latex.pp_color f pe.Pp.color
(** Print a sequence with difference mode if enabled. *)
let pp_seq_diff pp pe0 f =
if not Config.print_using_diff then Pp.comma_seq pp f
else
let rec doit = function
| []
-> ()
| [x]
-> let _, changed = color_pre_wrapper pe0 f x in
F.fprintf f "%a" pp x ; color_post_wrapper changed pe0 f
| x :: l
-> let _, changed = color_pre_wrapper pe0 f x in
F.fprintf f "%a" pp x ; color_post_wrapper changed pe0 f ; F.fprintf f ", " ; doit l
in
doit
(** Pretty print an expression. *)
let pp_exp_printenv pe0 f e0 =
let pe, changed = color_pre_wrapper pe0 f e0 in
let e =
match pe.Pp.obj_sub with
| Some sub
-> Obj.obj (sub (Obj.repr e0) (* apply object substitution to expression *))
| None
-> e0
in
if not (Exp.equal e0 e) then
match e with Exp.Lvar pvar -> Pvar.pp_value pe f pvar | _ -> assert false
else Exp.pp_printenv pe Typ.pp f e ;
color_post_wrapper changed pe0 f
(** dump an expression. *)
let d_exp (e: Exp.t) = L.add_print_action (L.PTexp, Obj.repr e)
(** Pretty print a list of expressions. *)
let pp_exp_list pe f expl = Pp.seq (pp_exp_printenv pe) f expl
(** dump a list of expressions. *)
let d_exp_list (el: Exp.t list) = L.add_print_action (L.PTexp_list, Obj.repr el)
let pp_texp pe f = function
| Exp.Sizeof {typ; nbytes; dynamic_length; subtype}
-> let pp_len f l = Option.iter ~f:(F.fprintf f "[%a]" (pp_exp_printenv pe)) l in
let pp_size f size = Option.iter ~f:(Int.pp f) size in
F.fprintf f "%a%a%a%a" (Typ.pp pe) typ pp_size nbytes pp_len dynamic_length Subtype.pp
subtype
| e
-> pp_exp_printenv pe f e
(** Pretty print a type with all the details. *)
let pp_texp_full pe f = function
| Exp.Sizeof {typ; nbytes; dynamic_length; subtype}
-> let pp_len f l = Option.iter ~f:(F.fprintf f "[%a]" (pp_exp_printenv pe)) l in
let pp_size f size = Option.iter ~f:(Int.pp f) size in
F.fprintf f "%a%a%a%a" (Typ.pp_full pe) typ pp_size nbytes pp_len dynamic_length Subtype.pp
subtype
| e
-> Exp.pp_printenv pe Typ.pp_full f e
(** Dump a type expression with all the details. *)
let d_texp_full (te: Exp.t) = L.add_print_action (L.PTtexp_full, Obj.repr te)
(** Pretty print an offset *)
let pp_offset pe f = function
| Off_fld (fld, _)
-> F.fprintf f "%a" Typ.Fieldname.pp fld
| Off_index exp
-> F.fprintf f "%a" (pp_exp_printenv pe) exp
(** Convert an offset to a string *)
let offset_to_string e = F.asprintf "%a" (pp_offset Pp.text) e
(** dump an offset. *)
let d_offset (off: offset) = L.add_print_action (L.PToff, Obj.repr off)
(** Pretty print a list of offsets *)
let rec pp_offset_list pe f = function
| []
-> ()
| [off1; off2]
-> F.fprintf f "%a.%a" (pp_offset pe) off1 (pp_offset pe) off2
| off :: off_list
-> F.fprintf f "%a.%a" (pp_offset pe) off (pp_offset_list pe) off_list
(** Dump a list of offsets *)
let d_offset_list (offl: offset list) = L.add_print_action (L.PToff_list, Obj.repr offl)
let pp_exp_typ pe f (e, t) = F.fprintf f "%a:%a" (pp_exp_printenv pe) e (Typ.pp pe) t
(** Get the location of the instruction *)
let instr_get_loc = function
| Load (_, _, _, loc)
| Store (_, _, _, loc)
| Prune (_, loc, _, _)
| Call (_, _, _, loc, _)
| Nullify (_, loc)
| Abstract loc
| Remove_temps (_, loc)
| Declare_locals (_, loc)
-> loc
(** get the expressions occurring in the instruction *)
let instr_get_exps = function
| Load (id, e, _, _)
-> [Exp.Var id; e]
| Store (e1, _, e2, _)
-> [e1; e2]
| Prune (cond, _, _, _)
-> [cond]
| Call (ret_id, e, _, _, _)
-> e :: Option.value_map ~f:(fun (id, _) -> [Exp.Var id]) ~default:[] ret_id
| Nullify (pvar, _)
-> [Exp.Lvar pvar]
| Abstract _
-> []
| Remove_temps (temps, _)
-> List.map ~f:(fun id -> Exp.Var id) temps
| Declare_locals _
-> []
(** Pretty print an instruction. *)
let pp_instr pe0 f instr =
let pe, changed = color_pre_wrapper pe0 f instr in
( match instr with
| Load (id, e, t, loc)
-> F.fprintf f "%a=*%a:%a %a" (Ident.pp pe) id (pp_exp_printenv pe) e (Typ.pp pe) t Location.pp
loc
| Store (e1, t, e2, loc)
-> F.fprintf f "*%a:%a=%a %a" (pp_exp_printenv pe) e1 (Typ.pp pe) t (pp_exp_printenv pe) e2
Location.pp loc
| Prune (cond, loc, true_branch, _)
-> F.fprintf f "PRUNE(%a, %b); %a" (pp_exp_printenv pe) cond true_branch Location.pp loc
| Call (ret_id, e, arg_ts, loc, cf)
-> (match ret_id with None -> () | Some (id, _) -> F.fprintf f "%a=" (Ident.pp pe) id) ;
F.fprintf f "%a(%a)%a %a" (pp_exp_printenv pe) e
(Pp.comma_seq (pp_exp_typ pe))
arg_ts CallFlags.pp cf Location.pp loc
| Nullify (pvar, loc)
-> F.fprintf f "NULLIFY(%a); %a" (Pvar.pp pe) pvar Location.pp loc
| Abstract loc
-> F.fprintf f "APPLY_ABSTRACTION; %a" Location.pp loc
| Remove_temps (temps, loc)
-> F.fprintf f "REMOVE_TEMPS(%a); %a" (Ident.pp_list pe) temps Location.pp loc
| Declare_locals (ptl, loc)
-> let pp_typ fmt (pvar, _) = F.fprintf fmt "%a" (Pvar.pp pe) pvar in
F.fprintf f "DECLARE_LOCALS(%a); %a" (Pp.comma_seq pp_typ) ptl Location.pp loc ) ;
color_post_wrapper changed pe0 f
(** Check if a pvar is a local pointing to a block in objc *)
let is_block_pvar pvar = Typ.has_block_prefix (Mangled.to_string (Pvar.get_name pvar))
(* A block pvar used to explain retain cycles *)
let block_pvar = Pvar.mk (Mangled.from_string "block") (Typ.Procname.from_string_c_fun "")
(** Dump an instruction. *)
let d_instr (i: instr) = L.add_print_action (L.PTinstr, Obj.repr i)
let rec pp_instr_list pe f = function
| []
-> F.fprintf f ""
| i :: is
-> F.fprintf f "%a;@\n%a" (pp_instr pe) i (pp_instr_list pe) is
(** Dump a list of instructions. *)
let d_instr_list (il: instr list) = L.add_print_action (L.PTinstr_list, Obj.repr il)
let pp_atom pe0 f a =
let pe, changed = color_pre_wrapper pe0 f a in
( match a with
| Aeq (BinOp (op, e1, e2), Const Cint i) when IntLit.isone i -> (
match pe.Pp.kind with
| TEXT | HTML
-> F.fprintf f "%a" (pp_exp_printenv pe) (Exp.BinOp (op, e1, e2))
| LATEX
-> F.fprintf f "%a" (pp_exp_printenv pe) (Exp.BinOp (op, e1, e2)) )
| Aeq (e1, e2) -> (
match pe.Pp.kind with
| TEXT | HTML
-> F.fprintf f "%a = %a" (pp_exp_printenv pe) e1 (pp_exp_printenv pe) e2
| LATEX
-> F.fprintf f "%a{=}%a" (pp_exp_printenv pe) e1 (pp_exp_printenv pe) e2 )
| Aneq (e1, e2) -> (
match pe.Pp.kind with
| TEXT | HTML
-> F.fprintf f "%a != %a" (pp_exp_printenv pe) e1 (pp_exp_printenv pe) e2
| LATEX
-> F.fprintf f "%a{\\neq}%a" (pp_exp_printenv pe) e1 (pp_exp_printenv pe) e2 )
| Apred (a, es)
-> F.fprintf f "%s(%a)" (PredSymb.to_string pe a) (Pp.comma_seq (pp_exp_printenv pe)) es
| Anpred (a, es)
-> F.fprintf f "!%s(%a)" (PredSymb.to_string pe a) (Pp.comma_seq (pp_exp_printenv pe)) es ) ;
color_post_wrapper changed pe0 f
(** dump an atom *)
let d_atom (a: atom) = L.add_print_action (L.PTatom, Obj.repr a)
let pp_lseg_kind f = function Lseg_NE -> F.fprintf f "ne" | Lseg_PE -> F.fprintf f ""
(** Print a *-separated sequence. *)
let rec pp_star_seq pp f = function
| []
-> ()
| [x]
-> F.fprintf f "%a" pp x
| x :: l
-> F.fprintf f "%a * %a" pp x (pp_star_seq pp) l
(********* START OF MODULE Predicates **********)
(** Module Predicates records the occurrences of predicates as parameters
of (doubly -)linked lists and Epara. Provides unique numbering
for predicates and an iterator. *)
module Predicates : sig
(** predicate environment *)
type env
(** create an empty predicate environment *)
val empty_env : unit -> env
(** return true if the environment is empty *)
val is_empty : env -> bool
(** return the id of the hpara *)
val get_hpara_id : env -> hpara -> int
(** return the id of the hpara_dll *)
val get_hpara_dll_id : env -> hpara_dll -> int
(** [iter env f f_dll] iterates [f] and [f_dll] on all the hpara and hpara_dll,
passing the unique id to the functions. The iterator can only be used once. *)
val iter : env -> (int -> hpara -> unit) -> (int -> hpara_dll -> unit) -> unit
(** Process one hpred, updating the predicate environment *)
val process_hpred : env -> hpred -> unit
end = struct
(** hash tables for hpara *)
module HparaHash = Hashtbl.Make (struct
type t = hpara
let equal = equal_hpara
let hash = Hashtbl.hash
end)
(** hash tables for hpara_dll *)
module HparaDllHash = Hashtbl.Make (struct
type t = hpara_dll
let equal = equal_hpara_dll
let hash = Hashtbl.hash
end)
(** Map each visited hpara to a unique number and a boolean denoting whether it has been emitted,
also keep a list of hparas still to be emitted. Same for hpara_dll. *)
type env =
{ mutable num: int
; hash: (int * bool) HparaHash.t
; mutable todo: hpara list
; hash_dll: (int * bool) HparaDllHash.t
; mutable todo_dll: hpara_dll list }
(** return true if the environment is empty *)
let is_empty env = Int.equal env.num 0
(** return the id of the hpara *)
let get_hpara_id env hpara = fst (HparaHash.find env.hash hpara)
(** return the id of the hpara_dll *)
let get_hpara_dll_id env hpara_dll = fst (HparaDllHash.find env.hash_dll hpara_dll)
(** Process one hpara, updating the map from hparas to numbers, and the todo list *)
let process_hpara env hpara =
if not (HparaHash.mem env.hash hpara) then (
HparaHash.add env.hash hpara (env.num, false) ;
env.num <- env.num + 1 ;
env.todo <- env.todo @ [hpara] )
(** Process one hpara_dll, updating the map from hparas to numbers, and the todo list *)
let process_hpara_dll env hpara_dll =
if not (HparaDllHash.mem env.hash_dll hpara_dll) then (
HparaDllHash.add env.hash_dll hpara_dll (env.num, false) ;
env.num <- env.num + 1 ;
env.todo_dll <- env.todo_dll @ [hpara_dll] )
(** Process a sexp, updating env *)
let rec process_sexp env = function
| Eexp _
-> ()
| Earray (_, esel, _)
-> List.iter ~f:(fun (_, se) -> process_sexp env se) esel
| Estruct (fsel, _)
-> List.iter ~f:(fun (_, se) -> process_sexp env se) fsel
(** Process one hpred, updating env *)
let rec process_hpred env = function
| Hpointsto (_, se, _)
-> process_sexp env se
| Hlseg (_, hpara, _, _, _)
-> List.iter ~f:(process_hpred env) hpara.body ;
process_hpara env hpara
| Hdllseg (_, hpara_dll, _, _, _, _, _)
-> List.iter ~f:(process_hpred env) hpara_dll.body_dll ;
process_hpara_dll env hpara_dll
(** create an empty predicate environment *)
let empty_env () =
{num= 0; hash= HparaHash.create 3; todo= []; hash_dll= HparaDllHash.create 3; todo_dll= []}
(** iterator for predicates which are marked as todo in env,
unless they have been visited already.
This can in turn extend the todo list for the nested predicates,
which are then visited as well.
Can be applied only once, as it destroys the todo list *)
let iter (env: env) f f_dll =
while env.todo <> [] || env.todo_dll <> [] do
match env.todo with
| hpara :: todo'
-> env.todo <- todo' ;
let n, emitted = HparaHash.find env.hash hpara in
if not emitted then f n hpara
| [] ->
match env.todo_dll with
| hpara_dll :: todo_dll'
-> env.todo_dll <- todo_dll' ;
let n, emitted = HparaDllHash.find env.hash_dll hpara_dll in
if not emitted then f_dll n hpara_dll
| []
-> ()
done
end
(********* END OF MODULE Predicates **********)
let pp_texp_simple pe =
match pe.Pp.opt with SIM_DEFAULT -> pp_texp pe | SIM_WITH_TYP -> pp_texp_full pe
let inst_abstraction = Iabstraction
let inst_actual_precondition = Iactual_precondition
let inst_alloc = Ialloc
(** for formal parameters *)
let inst_formal = Iformal (None, false)
(** for initial values *)
let inst_initial = Iinitial
let inst_lookup = Ilookup
let inst_none = Inone
let inst_nullify = Inullify
let inst_rearrange b loc pos = Irearrange (Some b, false, loc.Location.line, pos)
let inst_taint = Itaint
let inst_update loc pos = Iupdate (None, false, loc.Location.line, pos)
(** update the location of the instrumentation *)
let inst_new_loc loc inst =
match inst with
| Iabstraction
-> inst
| Iactual_precondition
-> inst
| Ialloc
-> inst
| Iformal _
-> inst
| Iinitial
-> inst
| Ilookup
-> inst
| Inone
-> inst
| Inullify
-> inst
| Irearrange (zf, ncf, _, pos)
-> Irearrange (zf, ncf, loc.Location.line, pos)
| Itaint
-> inst
| Iupdate (zf, ncf, _, pos)
-> Iupdate (zf, ncf, loc.Location.line, pos)
| Ireturn_from_call _
-> Ireturn_from_call loc.Location.line
(** return a string representing the inst *)
let inst_to_string inst =
let zero_flag_to_string = function Some true -> "(z)" | _ -> "" in
let null_case_flag_to_string ncf = if ncf then "(ncf)" else "" in
match inst with
| Iabstraction
-> "abstraction"
| Iactual_precondition
-> "actual_precondition"
| Ialloc
-> "alloc"
| Iformal (zf, ncf)
-> "formal" ^ zero_flag_to_string zf ^ null_case_flag_to_string ncf
| Iinitial
-> "initial"
| Ilookup
-> "lookup"
| Inone
-> "none"
| Inullify
-> "nullify"
| Irearrange (zf, ncf, n, _)
-> "rearrange:" ^ zero_flag_to_string zf ^ null_case_flag_to_string ncf ^ string_of_int n
| Itaint
-> "taint"
| Iupdate (zf, ncf, n, _)
-> "update:" ^ zero_flag_to_string zf ^ null_case_flag_to_string ncf ^ string_of_int n
| Ireturn_from_call n
-> "return_from_call: " ^ string_of_int n
exception JoinFail
(** join of instrumentations, can raise JoinFail *)
let inst_partial_join inst1 inst2 =
let fail () =
L.d_strln ("inst_partial_join failed on " ^ inst_to_string inst1 ^ " " ^ inst_to_string inst2) ;
raise JoinFail
in
if equal_inst inst1 inst2 then inst1
else
match (inst1, inst2) with
| _, Inone | Inone, _
-> inst_none
| _, Ialloc | Ialloc, _
-> fail ()
| _, Iinitial | Iinitial, _
-> fail ()
| _, Iupdate _ | Iupdate _, _
-> fail ()
| _
-> inst_none
(** meet of instrumentations *)
let inst_partial_meet inst1 inst2 = if equal_inst inst1 inst2 then inst1 else inst_none
(** Return the zero flag of the inst *)
let inst_zero_flag = function
| Iabstraction
-> None
| Iactual_precondition
-> None
| Ialloc
-> None
| Iformal (zf, _)
-> zf
| Iinitial
-> None
| Ilookup
-> None
| Inone
-> None
| Inullify
-> None
| Irearrange (zf, _, _, _)
-> zf
| Itaint
-> None
| Iupdate (zf, _, _, _)
-> zf
| Ireturn_from_call _
-> None
(** Set the null case flag of the inst. *)
let inst_set_null_case_flag = function
| Iformal (zf, false)
-> Iformal (zf, true)
| Irearrange (zf, false, n, pos)
-> Irearrange (zf, true, n, pos)
| Iupdate (zf, false, n, pos)
-> Iupdate (zf, true, n, pos)
| inst
-> inst
(** Get the null case flag of the inst. *)
let inst_get_null_case_flag = function Iupdate (_, ncf, _, _) -> Some ncf | _ -> None
(** Update [inst_old] to [inst_new] preserving the zero flag *)
let update_inst inst_old inst_new =
let combine_zero_flags z1 z2 =
match (z1, z2) with
| Some b1, Some b2
-> Some (b1 || b2)
| Some b, None
-> Some b
| None, Some b
-> Some b
| None, None
-> None
in
match inst_new with
| Iabstraction
-> inst_new
| Iactual_precondition
-> inst_new
| Ialloc
-> inst_new
| Iformal (zf, ncf)
-> let zf' = combine_zero_flags (inst_zero_flag inst_old) zf in
Iformal (zf', ncf)
| Iinitial
-> inst_new
| Ilookup
-> inst_new
| Inone
-> inst_new
| Inullify
-> inst_new
| Irearrange (zf, ncf, n, pos)
-> let zf' = combine_zero_flags (inst_zero_flag inst_old) zf in
Irearrange (zf', ncf, n, pos)
| Itaint
-> inst_new
| Iupdate (zf, ncf, n, pos)
-> let zf' = combine_zero_flags (inst_zero_flag inst_old) zf in
Iupdate (zf', ncf, n, pos)
| Ireturn_from_call _
-> inst_new
(** describe an instrumentation with a string *)
let pp_inst pe f inst =
let str = inst_to_string inst in
if Pp.equal_print_kind pe.Pp.kind Pp.HTML then
F.fprintf f " %a%s%a" Io_infer.Html.pp_start_color Pp.Orange str Io_infer.Html.pp_end_color ()
else F.fprintf f "%s%s%s" (Binop.str pe Lt) str (Binop.str pe Gt)
let pp_inst_if_trace pe f inst = if Config.trace_error then pp_inst pe f inst
(** pretty print a strexp with an optional predicate env *)
let rec pp_sexp_env pe0 envo f se =
let pe, changed = color_pre_wrapper pe0 f se in
( match se with
| Eexp (e, inst)
-> F.fprintf f "%a%a" (pp_exp_printenv pe) e (pp_inst_if_trace pe) inst
| Estruct (fel, inst) -> (
match pe.Pp.kind with
| TEXT | HTML
-> let pp_diff f (n, se) = F.fprintf f "%a:%a" Typ.Fieldname.pp n (pp_sexp_env pe envo) se in
F.fprintf f "{%a}%a" (pp_seq_diff pp_diff pe) fel (pp_inst_if_trace pe) inst
| LATEX
-> let pp_diff f (n, se) =
F.fprintf f "%a:%a" (Typ.Fieldname.pp_latex Latex.Boldface) n (pp_sexp_env pe envo) se
in
F.fprintf f "\\{%a\\}%a" (pp_seq_diff pp_diff pe) fel (pp_inst_if_trace pe) inst )
| Earray (len, nel, inst)
-> let pp_diff f (i, se) =
F.fprintf f "%a:%a" (pp_exp_printenv pe) i (pp_sexp_env pe envo) se
in
F.fprintf f "[%a|%a]%a" (pp_exp_printenv pe) len (pp_seq_diff pp_diff pe) nel
(pp_inst_if_trace pe) inst ) ;
color_post_wrapper changed pe0 f
(** Pretty print an hpred with an optional predicate env *)
let rec pp_hpred_env pe0 envo f hpred =
let pe, changed = color_pre_wrapper pe0 f hpred in
( match hpred with
| Hpointsto (e, se, te)
-> (
let pe' =
match (e, se) with
| Lvar pvar, Eexp (Var _, _) when not (Pvar.is_global pvar)
-> Pp.{pe with obj_sub= None (* dont use obj sub on the var defining it *)}
| _
-> pe
in
match pe'.Pp.kind with
| TEXT | HTML
-> F.fprintf f "%a|->%a:%a" (pp_exp_printenv pe') e (pp_sexp_env pe' envo) se
(pp_texp_simple pe') te
| LATEX
-> F.fprintf f "%a\\mapsto %a" (pp_exp_printenv pe') e (pp_sexp_env pe' envo) se )
| Hlseg (k, hpara, e1, e2, elist) -> (
match pe.Pp.kind with
| TEXT | HTML
-> F.fprintf f "lseg%a(%a,%a,[%a],%a)" pp_lseg_kind k (pp_exp_printenv pe) e1
(pp_exp_printenv pe) e2
(Pp.comma_seq (pp_exp_printenv pe))
elist (pp_hpara_env pe envo) hpara
| LATEX
-> F.fprintf f "\\textsf{lseg}_{%a}(%a,%a,[%a],%a)" pp_lseg_kind k (pp_exp_printenv pe) e1
(pp_exp_printenv pe) e2
(Pp.comma_seq (pp_exp_printenv pe))
elist (pp_hpara_env pe envo) hpara )
| Hdllseg (k, hpara_dll, iF, oB, oF, iB, elist) ->
match pe.Pp.kind with
| TEXT | HTML
-> F.fprintf f "dllseg%a(%a,%a,%a,%a,[%a],%a)" pp_lseg_kind k (pp_exp_printenv pe) iF
(pp_exp_printenv pe) oB (pp_exp_printenv pe) oF (pp_exp_printenv pe) iB
(Pp.comma_seq (pp_exp_printenv pe))
elist (pp_hpara_dll_env pe envo) hpara_dll
| LATEX
-> F.fprintf f "\\textsf{dllseg}_{%a}(%a,%a,%a,%a,[%a],%a)" pp_lseg_kind k
(pp_exp_printenv pe) iF (pp_exp_printenv pe) oB (pp_exp_printenv pe) oF
(pp_exp_printenv pe) iB
(Pp.comma_seq (pp_exp_printenv pe))
elist (pp_hpara_dll_env pe envo) hpara_dll ) ;
color_post_wrapper changed pe0 f
and pp_hpara_env pe envo f hpara =
match envo with
| None
-> let r, n, svars, evars, b = (hpara.root, hpara.next, hpara.svars, hpara.evars, hpara.body) in
F.fprintf f "lam [%a,%a,%a]. exists [%a]. %a" (Ident.pp pe) r (Ident.pp pe) n
(Pp.seq (Ident.pp pe))
svars
(Pp.seq (Ident.pp pe))
evars
(pp_star_seq (pp_hpred_env pe envo))
b
| Some env
-> F.fprintf f "P%d" (Predicates.get_hpara_id env hpara)
and pp_hpara_dll_env pe envo f hpara_dll =
match envo with
| None
-> let iF, oB, oF, svars, evars, b =
( hpara_dll.cell
, hpara_dll.blink
, hpara_dll.flink
, hpara_dll.svars_dll
, hpara_dll.evars_dll
, hpara_dll.body_dll )
in
F.fprintf f "lam [%a,%a,%a,%a]. exists [%a]. %a" (Ident.pp pe) iF (Ident.pp pe) oB
(Ident.pp pe) oF
(Pp.seq (Ident.pp pe))
svars
(Pp.seq (Ident.pp pe))
evars
(pp_star_seq (pp_hpred_env pe envo))
b
| Some env
-> F.fprintf f "P%d" (Predicates.get_hpara_dll_id env hpara_dll)
(** pretty print a strexp *)
let pp_sexp pe f = pp_sexp_env pe None f
(** pretty print a hpara *)
let pp_hpara pe f = pp_hpara_env pe None f
(** pretty print a hpara_dll *)
let pp_hpara_dll pe f = pp_hpara_dll_env pe None f
(** pretty print a hpred *)
let pp_hpred pe f = pp_hpred_env pe None f
(** dump a strexp. *)
let d_sexp (se: strexp) = L.add_print_action (L.PTsexp, Obj.repr se)
(** Pretty print a list of expressions. *)
let pp_sexp_list pe f sel =
F.fprintf f "%a" (Pp.seq (fun f se -> F.fprintf f "%a" (pp_sexp pe) se)) sel
(** dump a list of expressions. *)
let d_sexp_list (sel: strexp list) = L.add_print_action (L.PTsexp_list, Obj.repr sel)
let rec pp_hpara_list pe f = function
| []
-> ()
| [para]
-> F.fprintf f "PRED: %a" (pp_hpara pe) para
| para :: paras
-> F.fprintf f "PRED: %a@\n@\n%a" (pp_hpara pe) para (pp_hpara_list pe) paras
let rec pp_hpara_dll_list pe f = function
| []
-> ()
| [para]
-> F.fprintf f "PRED: %a" (pp_hpara_dll pe) para
| para :: paras
-> F.fprintf f "PRED: %a@\n@\n%a" (pp_hpara_dll pe) para (pp_hpara_dll_list pe) paras
(** dump a hpred. *)
let d_hpred (hpred: hpred) = L.add_print_action (L.PThpred, Obj.repr hpred)
(** {2 Functions for traversing SIL data types} *)
let rec strexp_expmap (f: Exp.t * inst option -> Exp.t * inst option) =
let fe e = fst (f (e, None)) in
let fei (e, inst) =
match f (e, Some inst) with e', None -> (e', inst) | e', Some inst' -> (e', inst')
in
function
| Eexp (e, inst)
-> let e', inst' = fei (e, inst) in
Eexp (e', inst')
| Estruct (fld_se_list, inst)
-> let f_fld_se (fld, se) = (fld, strexp_expmap f se) in
Estruct (List.map ~f:f_fld_se fld_se_list, inst)
| Earray (len, idx_se_list, inst)
-> let len' = fe len in
let f_idx_se (idx, se) =
let idx' = fe idx in
(idx', strexp_expmap f se)
in
Earray (len', List.map ~f:f_idx_se idx_se_list, inst)
let hpred_expmap (f: Exp.t * inst option -> Exp.t * inst option) =
let fe e = fst (f (e, None)) in
function
| Hpointsto (e, se, te)
-> let e' = fe e in
let se' = strexp_expmap f se in
let te' = fe te in
Hpointsto (e', se', te')
| Hlseg (k, hpara, root, next, shared)
-> let root' = fe root in
let next' = fe next in
let shared' = List.map ~f:fe shared in
Hlseg (k, hpara, root', next', shared')
| Hdllseg (k, hpara, iF, oB, oF, iB, shared)
-> let iF' = fe iF in
let oB' = fe oB in
let oF' = fe oF in
let iB' = fe iB in
let shared' = List.map ~f:fe shared in
Hdllseg (k, hpara, iF', oB', oF', iB', shared')
let rec strexp_instmap (f: inst -> inst) strexp =
match strexp with
| Eexp (e, inst)
-> Eexp (e, f inst)
| Estruct (fld_se_list, inst)
-> let f_fld_se (fld, se) = (fld, strexp_instmap f se) in
Estruct (List.map ~f:f_fld_se fld_se_list, f inst)
| Earray (len, idx_se_list, inst)
-> let f_idx_se (idx, se) = (idx, strexp_instmap f se) in
Earray (len, List.map ~f:f_idx_se idx_se_list, f inst)
let rec hpara_instmap (f: inst -> inst) hpara =
{hpara with body= List.map ~f:(hpred_instmap f) hpara.body}
and hpara_dll_instmap (f: inst -> inst) hpara_dll =
{hpara_dll with body_dll= List.map ~f:(hpred_instmap f) hpara_dll.body_dll}
and hpred_instmap (fn: inst -> inst) (hpred: hpred) : hpred =
match hpred with
| Hpointsto (e, se, te)
-> let se' = strexp_instmap fn se in
Hpointsto (e, se', te)
| Hlseg (k, hpara, e, f, el)
-> Hlseg (k, hpara_instmap fn hpara, e, f, el)
| Hdllseg (k, hpar_dll, e, f, g, h, el)
-> Hdllseg (k, hpara_dll_instmap fn hpar_dll, e, f, g, h, el)
let hpred_list_expmap (f: Exp.t * inst option -> Exp.t * inst option) (hlist: hpred list) =
List.map ~f:(hpred_expmap f) hlist
let atom_expmap (f: Exp.t -> Exp.t) = function
| Aeq (e1, e2)
-> Aeq (f e1, f e2)
| Aneq (e1, e2)
-> Aneq (f e1, f e2)
| Apred (a, es)
-> Apred (a, List.map ~f es)
| Anpred (a, es)
-> Anpred (a, List.map ~f es)
let atom_list_expmap (f: Exp.t -> Exp.t) (alist: atom list) = List.map ~f:(atom_expmap f) alist
(** {2 Function for computing lexps in sigma} *)
let hpred_get_lexp acc = function
| Hpointsto (e, _, _)
-> e :: acc
| Hlseg (_, _, e, _, _)
-> e :: acc
| Hdllseg (_, _, e1, _, _, e2, _)
-> e1 :: e2 :: acc
let hpred_list_get_lexps (filter: Exp.t -> bool) (hlist: hpred list) : Exp.t list =
let lexps = List.fold ~f:hpred_get_lexp ~init:[] hlist in
List.filter ~f:filter lexps
(** {2 Functions for computing program variables} *)
let rec exp_fpv e =
match (e : Exp.t) with
| Var _
-> []
| Exn e
-> exp_fpv e
| Closure {captured_vars}
-> List.map ~f:(fun (_, pvar, _) -> pvar) captured_vars
| Const _
-> []
| Cast (_, e) | UnOp (_, e, _)
-> exp_fpv e
| BinOp (_, e1, e2)
-> exp_fpv e1 @ exp_fpv e2
| Lvar name
-> [name]
| Lfield (e, _, _)
-> exp_fpv e
| Lindex (e1, e2)
-> exp_fpv e1 @ exp_fpv e2
(* TODO: Sizeof length expressions may contain variables, do not ignore them. *)
| Sizeof _
-> []
let exp_list_fpv el = List.concat_map ~f:exp_fpv el
let atom_fpv = function
| Aeq (e1, e2)
-> exp_fpv e1 @ exp_fpv e2
| Aneq (e1, e2)
-> exp_fpv e1 @ exp_fpv e2
| Apred (_, es) | Anpred (_, es)
-> List.fold ~f:(fun fpv e -> List.rev_append (exp_fpv e) fpv) ~init:[] es
let rec strexp_fpv = function
| Eexp (e, _)
-> exp_fpv e
| Estruct (fld_se_list, _)
-> let f (_, se) = strexp_fpv se in
List.concat_map ~f fld_se_list
| Earray (len, idx_se_list, _)
-> let fpv_in_len = exp_fpv len in
let f (idx, se) = exp_fpv idx @ strexp_fpv se in
fpv_in_len @ List.concat_map ~f idx_se_list
let rec hpred_fpv = function
| Hpointsto (base, se, te)
-> exp_fpv base @ strexp_fpv se @ exp_fpv te
| Hlseg (_, para, e1, e2, elist)
-> let fpvars_in_elist = exp_list_fpv elist in
hpara_fpv para @ exp_fpv (* This set has to be empty. *) e1 @ exp_fpv e2 @ fpvars_in_elist
| Hdllseg (_, para, e1, e2, e3, e4, elist)
-> let fpvars_in_elist = exp_list_fpv elist in
hpara_dll_fpv para (* This set has to be empty. *)
@ exp_fpv e1 @ exp_fpv e2 @ exp_fpv e3 @ exp_fpv e4 @ fpvars_in_elist
(** hpara should not contain any program variables.
This is because it might cause problems when we do interprocedural
analysis. In interprocedural analysis, we should consider the issue
of scopes of program variables. *)
and hpara_fpv para =
let fpvars_in_body = List.concat_map ~f:hpred_fpv para.body in
match fpvars_in_body with [] -> [] | _ -> assert false
(** hpara_dll should not contain any program variables.
This is because it might cause problems when we do interprocedural
analysis. In interprocedural analysis, we should consider the issue
of scopes of program variables. *)
and hpara_dll_fpv para =
let fpvars_in_body = List.concat_map ~f:hpred_fpv para.body_dll in
match fpvars_in_body with [] -> [] | _ -> assert false
(** {2 Functions for computing free non-program variables} *)
(** Type of free variables. These include primed, normal and footprint variables.
We keep a count of how many types the variables appear. *)
type fav = Ident.t list ref
let fav_new () = ref []
(** Emptyness check. *)
let fav_is_empty fav = match !fav with [] -> true | _ -> false
(** Check whether a predicate holds for all elements. *)
let fav_for_all fav predicate = List.for_all ~f:predicate !fav
(** Check whether a predicate holds for some elements. *)
let fav_exists fav predicate = List.exists ~f:predicate !fav
(** flag to indicate whether fav's are stored in duplicate form.
Only to be used with fav_to_list *)
let fav_duplicates = ref false
(** extend [fav] with a [id] *)
let ( ++ ) fav id =
if !fav_duplicates || not (List.exists ~f:(Ident.equal id) !fav) then fav := id :: !fav
(** extend [fav] with ident list [idl] *)
let ( +++ ) fav idl = List.iter ~f:(fun id -> fav ++ id) idl
(** add identity lists to fav *)
let ident_list_fav_add idl fav = fav +++ idl
(** Convert a list to a fav. *)
let fav_from_list l =
let fav = fav_new () in
let _ = List.iter ~f:(fun id -> fav ++ id) l in
fav
let rec remove_duplicates_from_sorted special_equal = function
| []
-> []
| [x]
-> [x]
| x :: y :: l
-> if special_equal x y then remove_duplicates_from_sorted special_equal (y :: l)
else x :: remove_duplicates_from_sorted special_equal (y :: l)
(** Convert a [fav] to a list of identifiers while preserving the order
that the identifiers were added to [fav]. *)
let fav_to_list fav = List.rev !fav
(** Pretty print a fav. *)
let pp_fav pe f fav = Pp.seq (Ident.pp pe) f (fav_to_list fav)
(** Copy a [fav]. *)
let fav_copy fav = ref (List.map ~f:(fun x -> x) !fav)
(** Turn a xxx_fav_add function into a xxx_fav function *)
let fav_imperative_to_functional f x =
let fav = fav_new () in
let _ = f fav x in
fav
(** [fav_filter_ident fav f] only keeps [id] if [f id] is true. *)
let fav_filter_ident fav filter = fav := List.filter ~f:filter !fav
(** Like [fav_filter_ident] but return a copy. *)
let fav_copy_filter_ident fav filter = ref (List.filter ~f:filter !fav)
(** checks whether every element in l1 appears l2 **)
let rec ident_sorted_list_subset l1 l2 =
match (l1, l2) with
| [], _
-> true
| _ :: _, []
-> false
| id1 :: l1, id2 :: l2
-> let n = Ident.compare id1 id2 in
if Int.equal n 0 then ident_sorted_list_subset l1 (id2 :: l2)
else if n > 0 then ident_sorted_list_subset (id1 :: l1) l2
else false
(** [fav_subset_ident fav1 fav2] returns true if every ident in [fav1]
is in [fav2].*)
let fav_subset_ident fav1 fav2 = ident_sorted_list_subset (fav_to_list fav1) (fav_to_list fav2)
let fav_mem fav id = List.exists ~f:(Ident.equal id) !fav
let rec exp_fav_add fav e =
match (e : Exp.t) with
| Var id
-> fav ++ id
| Exn e
-> exp_fav_add fav e
| Closure {captured_vars}
-> List.iter ~f:(fun (e, _, _) -> exp_fav_add fav e) captured_vars
| Const (Cint _ | Cfun _ | Cstr _ | Cfloat _ | Cclass _)
-> ()
| Cast (_, e) | UnOp (_, e, _)
-> exp_fav_add fav e
| BinOp (_, e1, e2)
-> exp_fav_add fav e1 ; exp_fav_add fav e2
| Lvar _
-> ()
| Lfield (* do nothing since we only count non-program variables *)
(e, _, _)
-> exp_fav_add fav e
| Lindex (e1, e2)
-> exp_fav_add fav e1 ; exp_fav_add fav e2
(* TODO: Sizeof length expressions may contain variables, do not ignore them. *)
| Sizeof _
-> ()
let exp_fav = fav_imperative_to_functional exp_fav_add
let exp_fav_list e = fav_to_list (exp_fav e)
let ident_in_exp id e =
let fav = fav_new () in
exp_fav_add fav e ; fav_mem fav id
let atom_fav_add fav = function
| Aeq (e1, e2) | Aneq (e1, e2)
-> exp_fav_add fav e1 ; exp_fav_add fav e2
| Apred (_, es) | Anpred (_, es)
-> List.iter ~f:(fun e -> exp_fav_add fav e) es
let atom_fav = fav_imperative_to_functional atom_fav_add
(** Atoms do not contain binders *)
let atom_av_add = atom_fav_add
let rec strexp_fav_add fav = function
| Eexp (e, _)
-> exp_fav_add fav e
| Estruct (fld_se_list, _)
-> List.iter ~f:(fun (_, se) -> strexp_fav_add fav se) fld_se_list
| Earray (len, idx_se_list, _)
-> exp_fav_add fav len ;
List.iter ~f:(fun (e, se) -> exp_fav_add fav e ; strexp_fav_add fav se) idx_se_list
let hpred_fav_add fav = function
| Hpointsto (base, sexp, te)
-> exp_fav_add fav base ; strexp_fav_add fav sexp ; exp_fav_add fav te
| Hlseg (_, _, e1, e2, elist)
-> exp_fav_add fav e1 ;
exp_fav_add fav e2 ;
List.iter ~f:(exp_fav_add fav) elist
| Hdllseg (_, _, e1, e2, e3, e4, elist)
-> exp_fav_add fav e1 ;
exp_fav_add fav e2 ;
exp_fav_add fav e3 ;
exp_fav_add fav e4 ;
List.iter ~f:(exp_fav_add fav) elist
let hpred_fav = fav_imperative_to_functional hpred_fav_add
(** This function should be used before adding a new
index to Earray. The [exp] is the newly created
index. This function "cleans" [exp] according to whether it is
the footprint or current part of the prop.
The function faults in the re - execution mode, as an internal check of the tool. *)
let array_clean_new_index footprint_part new_idx =
if footprint_part && not !Config.footprint then assert false ;
let fav = exp_fav new_idx in
if footprint_part && fav_exists fav (fun id -> not (Ident.is_footprint id)) then (
L.d_warning
( "Array index " ^ Exp.to_string new_idx
^ " has non-footprint vars: replaced by fresh footprint var" ) ;
L.d_ln () ;
let id = Ident.create_fresh Ident.kfootprint in
Exp.Var id )
else new_idx
(** {2 Functions for computing all free or bound non-program variables} *)
let exp_av_add = exp_fav_add (** Expressions do not bind variables *)
(** Structured expressions do not bind variables *)
let strexp_av_add = strexp_fav_add
let rec hpara_av_add fav para =
List.iter ~f:(hpred_av_add fav) para.body ;
fav ++ para.root ;
fav ++ para.next ;
fav +++ para.svars ;
fav +++ para.evars
and hpara_dll_av_add fav para =
List.iter ~f:(hpred_av_add fav) para.body_dll ;
fav ++ para.cell ;
fav ++ para.blink ;
fav ++ para.flink ;
fav +++ para.svars_dll ;
fav +++ para.evars_dll
and hpred_av_add fav = function
| Hpointsto (base, se, te)
-> exp_av_add fav base ; strexp_av_add fav se ; exp_av_add fav te
| Hlseg (_, para, e1, e2, elist)
-> hpara_av_add fav para ;
exp_av_add fav e1 ;
exp_av_add fav e2 ;
List.iter ~f:(exp_av_add fav) elist
| Hdllseg (_, para, e1, e2, e3, e4, elist)
-> hpara_dll_av_add fav para ;
exp_av_add fav e1 ;
exp_av_add fav e2 ;
exp_av_add fav e3 ;
exp_av_add fav e4 ;
List.iter ~f:(exp_av_add fav) elist
let hpara_shallow_av_add fav para =
List.iter ~f:(hpred_fav_add fav) para.body ;
fav ++ para.root ;
fav ++ para.next ;
fav +++ para.svars ;
fav +++ para.evars
let hpara_dll_shallow_av_add fav para =
List.iter ~f:(hpred_fav_add fav) para.body_dll ;
fav ++ para.cell ;
fav ++ para.blink ;
fav ++ para.flink ;
fav +++ para.svars_dll ;
fav +++ para.evars_dll
(** Variables in hpara, excluding bound vars in the body *)
let hpara_shallow_av = fav_imperative_to_functional hpara_shallow_av_add
(** Variables in hpara_dll, excluding bound vars in the body *)
let hpara_dll_shallow_av = fav_imperative_to_functional hpara_dll_shallow_av_add
(** {2 Functions for Substitution} *)
let rec reverse_with_base base = function [] -> base | x :: l -> reverse_with_base (x :: base) l
let sorted_list_merge compare l1_in l2_in =
let rec merge acc l1 l2 =
match (l1, l2) with
| [], l2
-> reverse_with_base l2 acc
| l1, []
-> reverse_with_base l1 acc
| x1 :: l1', x2 :: l2'
-> if compare x1 x2 <= 0 then merge (x1 :: acc) l1' l2 else merge (x2 :: acc) l1 l2'
in
merge [] l1_in l2_in
let rec sorted_list_check_consecutives f = function
| [] | [_]
-> false
| x1 :: (x2 :: _ as l)
-> if f x1 x2 then true else sorted_list_check_consecutives f l
(** substitution *)
type ident_exp = Ident.t * Exp.t [@@deriving compare]
let equal_ident_exp = [%compare.equal : ident_exp]
type exp_subst = ident_exp list [@@deriving compare]
type subst = [`Exp of exp_subst | `Typ of Typ.type_subst_t] [@@deriving compare]
type subst_fun = [`Exp of Ident.t -> Exp.t | `Typ of (Typ.t -> Typ.t) * (Typ.Name.t -> Typ.Name.t)]
(** Equality for substitutions. *)
let equal_exp_subst = [%compare.equal : exp_subst]
let sub_check_duplicated_ids sub =
let f (id1, _) (id2, _) = Ident.equal id1 id2 in
sorted_list_check_consecutives f sub
(** Create a substitution from a list of pairs.
For all (id1, e1), (id2, e2) in the input list,
if id1 = id2, then e1 = e2. *)
let exp_subst_of_list sub =
let sub' = List.sort ~cmp:compare_ident_exp sub in
let sub'' = remove_duplicates_from_sorted equal_ident_exp sub' in
if sub_check_duplicated_ids sub'' then assert false ;
sub'
let subst_of_list sub = `Exp (exp_subst_of_list sub)
(** like exp_subst_of_list, but allow duplicate ids and only keep the first occurrence *)
let exp_subst_of_list_duplicates sub =
let sub' = List.sort ~cmp:compare_ident_exp sub in
let rec remove_duplicate_ids = function
| (id1, e1) :: (id2, e2) :: l
-> if Ident.equal id1 id2 then remove_duplicate_ids ((id1, e1) :: l)
else (id1, e1) :: remove_duplicate_ids ((id2, e2) :: l)
| l
-> l
in
remove_duplicate_ids sub'
(** Convert a subst to a list of pairs. *)
let sub_to_list sub = sub
(** The empty substitution. *)
let exp_sub_empty = exp_subst_of_list []
let sub_empty = `Exp exp_sub_empty
let is_sub_empty = function
| `Exp []
-> true
| `Exp _
-> false
| `Typ sub
-> Typ.is_type_subst_empty sub
(** Join two substitutions into one.
For all id in dom(sub1) cap dom(sub2), sub1(id) = sub2(id). *)
let sub_join sub1 sub2 =
let sub = sorted_list_merge compare_ident_exp sub1 sub2 in
let sub' = remove_duplicates_from_sorted equal_ident_exp sub in
if sub_check_duplicated_ids sub' then assert false ;
sub
(** Compute the common id-exp part of two inputs [subst1] and [subst2].
The first component of the output is this common part.
The second and third components are the remainder of [subst1]
and [subst2], respectively. *)
let sub_symmetric_difference sub1_in sub2_in =
let rec diff sub_common sub1_only sub2_only sub1 sub2 =
match (sub1, sub2) with
| [], _ | _, []
-> let sub1_only' = reverse_with_base sub1 sub1_only in
let sub2_only' = reverse_with_base sub2 sub2_only in
let sub_common = reverse_with_base [] sub_common in
(sub_common, sub1_only', sub2_only')
| id_e1 :: sub1', id_e2 :: sub2'
-> let n = compare_ident_exp id_e1 id_e2 in
if Int.equal n 0 then diff (id_e1 :: sub_common) sub1_only sub2_only sub1' sub2'
else if n < 0 then diff sub_common (id_e1 :: sub1_only) sub2_only sub1' sub2
else diff sub_common sub1_only (id_e2 :: sub2_only) sub1 sub2'
in
diff [] [] [] sub1_in sub2_in
(** [sub_find filter sub] returns the expression associated to the first identifier
that satisfies [filter]. Raise [Not_found] if there isn't one. *)
let sub_find filter (sub: exp_subst) = snd (List.find_exn ~f:(fun (i, _) -> filter i) sub)
(** [sub_filter filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter]. *)
let sub_filter filter (sub: exp_subst) = List.filter ~f:(fun (i, _) -> filter i) sub
(** [sub_filter_pair filter sub] restricts the domain of [sub] to the
identifiers satisfying [filter(id, sub(id))]. *)
let sub_filter_pair = List.filter
(** [sub_range_partition filter sub] partitions [sub] according to
whether range expressions satisfy [filter]. *)
let sub_range_partition filter (sub: exp_subst) = List.partition_tf ~f:(fun (_, e) -> filter e) sub
(** [sub_domain_partition filter sub] partitions [sub] according to
whether domain identifiers satisfy [filter]. *)
let sub_domain_partition filter (sub: exp_subst) =
List.partition_tf ~f:(fun (i, _) -> filter i) sub
(** Return the list of identifiers in the domain of the substitution. *)
let sub_domain sub = List.map ~f:fst sub
(** Return the list of expressions in the range of the substitution. *)
let sub_range sub = List.map ~f:snd sub
(** [sub_range_map f sub] applies [f] to the expressions in the range of [sub]. *)
let sub_range_map f sub = exp_subst_of_list (List.map ~f:(fun (i, e) -> (i, f e)) sub)
(** [sub_map f g sub] applies the renaming [f] to identifiers in the domain
of [sub] and the substitution [g] to the expressions in the range of [sub]. *)
let sub_map f g sub = exp_subst_of_list (List.map ~f:(fun (i, e) -> (f i, g e)) sub)
let mem_sub id sub = List.exists ~f:(fun (id1, _) -> Ident.equal id id1) sub
(** Extend substitution and return [None] if not possible. *)
let extend_sub sub id exp : exp_subst option =
let compare (id1, _) (id2, _) = Ident.compare id1 id2 in
if mem_sub id sub then None else Some (sorted_list_merge compare sub [(id, exp)])
(** Free auxilary variables in the domain and range of the
substitution. *)
let sub_fav_add fav (sub: exp_subst) =
List.iter ~f:(fun (id, e) -> fav ++ id ; exp_fav_add fav e) sub
(** Substitutions do not contain binders *)
let sub_av_add = sub_fav_add
let rec exp_sub_ids (f: subst_fun) exp =
let f_typ x = match f with `Exp _ -> x | `Typ (f, _) -> f x in
let f_tname x = match f with `Exp _ -> x | `Typ (_, f) -> f x in
match (exp : Exp.t) with
| Var id -> (
match f with
| `Exp f_exp -> (
match f_exp id with
| Exp.Var id' when Ident.equal id id'
-> exp
(* it will preserve physical equality when needed *) | exp'
-> exp' )
| _
-> exp )
| Lvar _
-> exp
| Exn e
-> let e' = exp_sub_ids f e in
if phys_equal e' e then exp else Exp.Exn e'
| Closure c
-> let captured_vars =
IList.map_changed
(fun (e, pvar, typ as captured) ->
let e' = exp_sub_ids f e in
let typ' = f_typ typ in
if phys_equal e' e && phys_equal typ typ' then captured else (e', pvar, typ'))
c.captured_vars
in
if phys_equal captured_vars c.captured_vars then exp else Exp.Closure {c with captured_vars}
| Const (Cint _ | Cfun _ | Cstr _ | Cfloat _ | Cclass _)
-> exp
| Cast (t, e)
-> let e' = exp_sub_ids f e in
let t' = f_typ t in
if phys_equal e' e && phys_equal t' t then exp else Exp.Cast (t', e')
| UnOp (op, e, typ_opt)
-> let e' = exp_sub_ids f e in
let typ_opt' =
match typ_opt with
| Some t
-> let t' = f_typ t in
if phys_equal t t' then typ_opt else Some t'
| None
-> typ_opt
in
if phys_equal e' e && phys_equal typ_opt typ_opt' then exp else Exp.UnOp (op, e', typ_opt')
| BinOp (op, e1, e2)
-> let e1' = exp_sub_ids f e1 in
let e2' = exp_sub_ids f e2 in
if phys_equal e1' e1 && phys_equal e2' e2 then exp else Exp.BinOp (op, e1', e2')
| Lfield (e, fld, typ)
-> let e' = exp_sub_ids f e in
let typ' = f_typ typ in
let fld' = Typ.Fieldname.class_name_replace ~f:f_tname fld in
if phys_equal e' e && phys_equal typ typ' && phys_equal fld fld' then exp
else Exp.Lfield (e', fld', typ')
| Lindex (e1, e2)
-> let e1' = exp_sub_ids f e1 in
let e2' = exp_sub_ids f e2 in
if phys_equal e1' e1 && phys_equal e2' e2 then exp else Exp.Lindex (e1', e2')
| Sizeof ({typ; dynamic_length= Some l; subtype} as sizeof_data)
-> let l' = exp_sub_ids f l in
let typ' = f_typ typ in
let subtype' = Subtype.sub_type f_tname subtype in
if phys_equal l' l && phys_equal typ typ' && phys_equal subtype subtype' then exp
else Exp.Sizeof {sizeof_data with typ= typ'; dynamic_length= Some l'; subtype= subtype'}
| Sizeof ({typ; dynamic_length= None; subtype} as sizeof_data)
-> let typ' = f_typ typ in
let subtype' = Subtype.sub_type f_tname subtype in
if phys_equal typ typ' then exp
else Exp.Sizeof {sizeof_data with typ= typ'; subtype= subtype'}
let apply_sub subst : subst_fun =
match subst with
| `Exp l
-> `Exp
(fun id ->
match List.Assoc.find l ~equal:Ident.equal id with Some x -> x | None -> Exp.Var id)
| `Typ typ_subst
-> `Typ (Typ.sub_type typ_subst, Typ.sub_tname typ_subst)
let exp_sub (subst: subst) e = exp_sub_ids (apply_sub subst) e
(** apply [f] to id's in [instr]. if [sub_id_binders] is false, [f] is only applied to bound id's *)
let instr_sub_ids ~sub_id_binders f instr =
let sub_id id =
match exp_sub_ids f (Var id) with Var id' when not (Ident.equal id id') -> id' | _ -> id
in
let sub_typ x = match f with `Exp _ -> x | `Typ (f, _) -> f x in
match instr with
| Load (id, rhs_exp, typ, loc)
-> let id' = if sub_id_binders then sub_id id else id in
let rhs_exp' = exp_sub_ids f rhs_exp in
let typ' = sub_typ typ in
if phys_equal id' id && phys_equal rhs_exp' rhs_exp && phys_equal typ typ' then instr
else Load (id', rhs_exp', typ', loc)
| Store (lhs_exp, typ, rhs_exp, loc)
-> let lhs_exp' = exp_sub_ids f lhs_exp in
let typ' = sub_typ typ in
let rhs_exp' = exp_sub_ids f rhs_exp in
if phys_equal lhs_exp' lhs_exp && phys_equal typ typ' && phys_equal rhs_exp' rhs_exp then
instr
else Store (lhs_exp', typ', rhs_exp', loc)
| Call (ret_id, fun_exp, actuals, call_flags, loc)
-> let ret_id' =
if sub_id_binders then
match ret_id with
| Some (id, typ)
-> let id' = sub_id id in
let typ' = sub_typ typ in
if Ident.equal id id' && phys_equal typ typ' then ret_id else Some (id', typ')
| None
-> None
else ret_id
in
let fun_exp' = exp_sub_ids f fun_exp in
let actuals' =
IList.map_changed
(fun (actual, typ as actual_pair) ->
let actual' = exp_sub_ids f actual in
let typ' = sub_typ typ in
if phys_equal actual' actual && phys_equal typ typ' then actual_pair
else (actual', typ'))
actuals
in
if phys_equal ret_id' ret_id && phys_equal fun_exp' fun_exp && phys_equal actuals' actuals
then instr
else Call (ret_id', fun_exp', actuals', call_flags, loc)
| Prune (exp, loc, true_branch, if_kind)
-> let exp' = exp_sub_ids f exp in
if phys_equal exp' exp then instr else Prune (exp', loc, true_branch, if_kind)
| Remove_temps (ids, loc)
-> let ids' = IList.map_changed sub_id ids in
if phys_equal ids' ids then instr else Remove_temps (ids', loc)
| Declare_locals (locals, loc)
-> let locals' =
IList.map_changed
(fun (name, typ as local_var) ->
let typ' = sub_typ typ in
if phys_equal typ typ' then local_var else (name, typ'))
locals
in
if phys_equal locals locals' then instr else Declare_locals (locals', loc)
| Nullify _ | Abstract _
-> instr
(** apply [subst] to all id's in [instr], including binder id's *)
let instr_sub (subst: subst) instr = instr_sub_ids ~sub_id_binders:true (apply_sub subst) instr
(** compare expressions from different procedures without considering loc's, ident's, and pvar's.
the [exp_map] param gives a mapping of names used in the procedure of [e1] to names used in the
procedure of [e2] *)
let rec exp_compare_structural e1 e2 exp_map =
let compare_exps_with_map e1 e2 exp_map =
try
let e1_mapping = Exp.Map.find e1 exp_map in
(Exp.compare e1_mapping e2, exp_map)
with Not_found ->
(* assume e1 and e2 equal, enforce by adding to [exp_map] *)
(0, Exp.Map.add e1 e2 exp_map)
in
match ((e1 : Exp.t), (e2 : Exp.t)) with
| Var _, Var _
-> compare_exps_with_map e1 e2 exp_map
| UnOp (o1, e1, to1), UnOp (o2, e2, to2)
-> let n = Unop.compare o1 o2 in
if n <> 0 then (n, exp_map)
else
let n, exp_map = exp_compare_structural e1 e2 exp_map in
((if n <> 0 then n else [%compare : Typ.t option] to1 to2), exp_map)
| BinOp (o1, e1, f1), BinOp (o2, e2, f2)
-> let n = Binop.compare o1 o2 in
if n <> 0 then (n, exp_map)
else
let n, exp_map = exp_compare_structural e1 e2 exp_map in
if n <> 0 then (n, exp_map) else exp_compare_structural f1 f2 exp_map
| Cast (t1, e1), Cast (t2, e2)
-> let n, exp_map = exp_compare_structural e1 e2 exp_map in
((if n <> 0 then n else Typ.compare t1 t2), exp_map)
| Lvar _, Lvar _
-> compare_exps_with_map e1 e2 exp_map
| Lfield (e1, f1, t1), Lfield (e2, f2, t2)
-> let n, exp_map = exp_compare_structural e1 e2 exp_map in
( ( if n <> 0 then n
else
let n = Typ.Fieldname.compare f1 f2 in
if n <> 0 then n else Typ.compare t1 t2 )
, exp_map )
| Lindex (e1, f1), Lindex (e2, f2)
-> let n, exp_map = exp_compare_structural e1 e2 exp_map in
if n <> 0 then (n, exp_map) else exp_compare_structural f1 f2 exp_map
| _
-> (Exp.compare e1 e2, exp_map)
let exp_typ_compare_structural (e1, t1) (e2, t2) exp_map =
let n, exp_map = exp_compare_structural e1 e2 exp_map in
((if n <> 0 then n else Typ.compare t1 t2), exp_map)
(** compare instructions from different procedures without considering loc's, ident's, and pvar's.
the [exp_map] param gives a mapping of names used in the procedure of [instr1] to identifiers
used in the procedure of [instr2] *)
let compare_structural_instr instr1 instr2 exp_map =
let id_typ_opt_compare_structural id_typ1 id_typ2 exp_map =
let id_typ_compare_structural (id1, typ1) (id2, typ2) =
let n, exp_map = exp_compare_structural (Var id1) (Var id2) exp_map in
if n <> 0 then (n, exp_map) else (Typ.compare typ1 typ2, exp_map)
in
match (id_typ1, id_typ2) with
| Some it1, Some it2
-> id_typ_compare_structural it1 it2
| None, None
-> (0, exp_map)
| None, _
-> (-1, exp_map)
| _, None
-> (1, exp_map)
in
let id_list_compare_structural ids1 ids2 exp_map =
let n = Int.compare (List.length ids1) (List.length ids2) in
if n <> 0 then (n, exp_map)
else
List.fold2_exn
~f:(fun (n, exp_map) id1 id2 ->
if n <> 0 then (n, exp_map) else exp_compare_structural (Var id1) (Var id2) exp_map)
~init:(0, exp_map) ids1 ids2
in
match (instr1, instr2) with
| Load (id1, e1, t1, _), Load (id2, e2, t2, _)
-> let n, exp_map = exp_compare_structural (Var id1) (Var id2) exp_map in
if n <> 0 then (n, exp_map)
else
let n, exp_map = exp_compare_structural e1 e2 exp_map in
((if n <> 0 then n else Typ.compare t1 t2), exp_map)
| Store (e11, t1, e21, _), Store (e12, t2, e22, _)
-> let n, exp_map = exp_compare_structural e11 e12 exp_map in
if n <> 0 then (n, exp_map)
else
let n = Typ.compare t1 t2 in
if n <> 0 then (n, exp_map) else exp_compare_structural e21 e22 exp_map
| Prune (cond1, _, true_branch1, ik1), Prune (cond2, _, true_branch2, ik2)
-> let n, exp_map = exp_compare_structural cond1 cond2 exp_map in
( ( if n <> 0 then n
else
let n = Bool.compare true_branch1 true_branch2 in
if n <> 0 then n else compare_if_kind ik1 ik2 )
, exp_map )
| Call (ret_id1, e1, arg_ts1, _, cf1), Call (ret_id2, e2, arg_ts2, _, cf2)
-> let args_compare_structural args1 args2 exp_map =
let n = Int.compare (List.length args1) (List.length args2) in
if n <> 0 then (n, exp_map)
else
List.fold2_exn
~f:(fun (n, exp_map) arg1 arg2 ->
if n <> 0 then (n, exp_map) else exp_typ_compare_structural arg1 arg2 exp_map)
~init:(0, exp_map) args1 args2
in
let n, exp_map = id_typ_opt_compare_structural ret_id1 ret_id2 exp_map in
if n <> 0 then (n, exp_map)
else
let n, exp_map = exp_compare_structural e1 e2 exp_map in
if n <> 0 then (n, exp_map)
else
let n, exp_map = args_compare_structural arg_ts1 arg_ts2 exp_map in
((if n <> 0 then n else CallFlags.compare cf1 cf2), exp_map)
| Nullify (pvar1, _), Nullify (pvar2, _)
-> exp_compare_structural (Lvar pvar1) (Lvar pvar2) exp_map
| Abstract _, Abstract _
-> (0, exp_map)
| Remove_temps (temps1, _), Remove_temps (temps2, _)
-> id_list_compare_structural temps1 temps2 exp_map
| Declare_locals (ptl1, _), Declare_locals (ptl2, _)
-> let n = Int.compare (List.length ptl1) (List.length ptl2) in
if n <> 0 then (n, exp_map)
else
List.fold2_exn
~f:(fun (n, exp_map) (pv1, t1) (pv2, t2) ->
if n <> 0 then (n, exp_map)
else
let n, exp_map = exp_compare_structural (Lvar pv1) (Lvar pv2) exp_map in
if n <> 0 then (n, exp_map) else (Typ.compare t1 t2, exp_map))
~init:(0, exp_map) ptl1 ptl2
| _
-> (compare_instr instr1 instr2, exp_map)
let atom_sub subst = atom_expmap (exp_sub subst)
let hpred_sub subst =
let f (e, inst_opt) = (exp_sub subst e, inst_opt) in
hpred_expmap f
(** {2 Functions for replacing occurrences of expressions.} *)
let rec exp_replace_exp epairs e =
(* First we check if there is an exact match *)
match List.find ~f:(fun (e1, _) -> Exp.equal e e1) epairs with
| Some (_, e2)
-> e2
| None ->
(* If e is a compound expression, we need to check for its subexpressions as well *)
match e with
| Exp.UnOp (op, e0, ty)
-> let e0' = exp_replace_exp epairs e0 in
if phys_equal e0 e0' then e else Exp.UnOp (op, e0', ty)
| Exp.BinOp (op, lhs, rhs)
-> let lhs' = exp_replace_exp epairs lhs in
let rhs' = exp_replace_exp epairs rhs in
if phys_equal lhs lhs' && phys_equal rhs rhs' then e else Exp.BinOp (op, lhs', rhs')
| Exp.Cast (ty, e0)
-> let e0' = exp_replace_exp epairs e0 in
if phys_equal e0 e0' then e else Exp.Cast (ty, e0')
| Exp.Lfield (e0, fname, ty)
-> let e0' = exp_replace_exp epairs e0 in
if phys_equal e0 e0' then e else Exp.Lfield (e0', fname, ty)
| Exp.Lindex (base, index)
-> let base' = exp_replace_exp epairs base in
let index' = exp_replace_exp epairs index in
if phys_equal base base' && phys_equal index index' then e else Exp.Lindex (base', index')
| _
-> e
let atom_replace_exp epairs atom = atom_expmap (fun e -> exp_replace_exp epairs e) atom
let rec strexp_replace_exp epairs = function
| Eexp (e, inst)
-> Eexp (exp_replace_exp epairs e, inst)
| Estruct (fsel, inst)
-> let f (fld, se) = (fld, strexp_replace_exp epairs se) in
Estruct (List.map ~f fsel, inst)
| Earray (len, isel, inst)
-> let len' = exp_replace_exp epairs len in
let f (idx, se) =
let idx' = exp_replace_exp epairs idx in
(idx', strexp_replace_exp epairs se)
in
Earray (len', List.map ~f isel, inst)
let hpred_replace_exp epairs = function
| Hpointsto (root, se, te)
-> let root_repl = exp_replace_exp epairs root in
let strexp_repl = strexp_replace_exp epairs se in
let te_repl = exp_replace_exp epairs te in
Hpointsto (root_repl, strexp_repl, te_repl)
| Hlseg (k, para, root, next, shared)
-> let root_repl = exp_replace_exp epairs root in
let next_repl = exp_replace_exp epairs next in
let shared_repl = List.map ~f:(exp_replace_exp epairs) shared in
Hlseg (k, para, root_repl, next_repl, shared_repl)
| Hdllseg (k, para, e1, e2, e3, e4, shared)
-> let e1' = exp_replace_exp epairs e1 in
let e2' = exp_replace_exp epairs e2 in
let e3' = exp_replace_exp epairs e3 in
let e4' = exp_replace_exp epairs e4 in
let shared_repl = List.map ~f:(exp_replace_exp epairs) shared in
Hdllseg (k, para, e1', e2', e3', e4', shared_repl)
(** {2 Compaction} *)
module HpredInstHash = Hashtbl.Make (struct
type t = hpred
let equal = equal_hpred ~inst:true
let hash = Hashtbl.hash
end)
type sharing_env = {exph: Exp.t Exp.Hash.t; hpredh: hpred HpredInstHash.t}
(** Create a sharing env to store canonical representations *)
let create_sharing_env () = {exph= Exp.Hash.create 3; hpredh= HpredInstHash.create 3}
(** Return a canonical representation of the exp *)
let exp_compact sh e =
try Exp.Hash.find sh.exph e
with Not_found -> Exp.Hash.add sh.exph e e ; e
let rec sexp_compact sh se =
match se with
| Eexp (e, inst)
-> Eexp (exp_compact sh e, inst)
| Estruct (fsel, inst)
-> Estruct (List.map ~f:(fun (f, se) -> (f, sexp_compact sh se)) fsel, inst)
| Earray _
-> se
(** Return a compact representation of the hpred *)
let _hpred_compact sh hpred =
match hpred with
| Hpointsto (e1, se, e2)
-> let e1' = exp_compact sh e1 in
let e2' = exp_compact sh e2 in
let se' = sexp_compact sh se in
Hpointsto (e1', se', e2')
| Hlseg _
-> hpred
| Hdllseg _
-> hpred
let hpred_compact sh hpred =
try HpredInstHash.find sh.hpredh hpred
with Not_found ->
let hpred' = _hpred_compact sh hpred in
HpredInstHash.add sh.hpredh hpred' hpred' ; hpred'
(** {2 Functions for constructing or destructing entities in this module} *)
(** Compute the offset list of an expression *)
let exp_get_offsets exp =
let rec f offlist_past e =
match (e : Exp.t) with
| Var _
| Const _
| UnOp _
| BinOp _
| Exn _
| Closure _
| Lvar _
| Sizeof {dynamic_length= None}
-> offlist_past
| Sizeof {dynamic_length= Some l}
-> f offlist_past l
| Cast (_, sub_exp)
-> f offlist_past sub_exp
| Lfield (sub_exp, fldname, typ)
-> f (Off_fld (fldname, typ) :: offlist_past) sub_exp
| Lindex (sub_exp, e)
-> f (Off_index e :: offlist_past) sub_exp
in
f [] exp
let exp_add_offsets exp offsets =
let rec f acc = function
| []
-> acc
| (Off_fld (fld, typ)) :: offs'
-> f (Exp.Lfield (acc, fld, typ)) offs'
| (Off_index e) :: offs'
-> f (Exp.Lindex (acc, e)) offs'
in
f exp offsets
(** Convert all the lseg's in sigma to nonempty lsegs. *)
let sigma_to_sigma_ne sigma : (atom list * hpred list) list =
if Config.nelseg then
let f eqs_sigma_list hpred =
match hpred with
| Hpointsto _ | Hlseg (Lseg_NE, _, _, _, _) | Hdllseg (Lseg_NE, _, _, _, _, _, _)
-> let g (eqs, sigma) = (eqs, hpred :: sigma) in
List.map ~f:g eqs_sigma_list
| Hlseg (Lseg_PE, para, e1, e2, el)
-> let g (eqs, sigma) =
[(Aeq (e1, e2) :: eqs, sigma); (eqs, Hlseg (Lseg_NE, para, e1, e2, el) :: sigma)]
in
List.concat_map ~f:g eqs_sigma_list
| Hdllseg (Lseg_PE, para_dll, e1, e2, e3, e4, el)
-> let g (eqs, sigma) =
[ (Aeq (e1, e3) :: Aeq (e2, e4) :: eqs, sigma)
; (eqs, Hdllseg (Lseg_NE, para_dll, e1, e2, e3, e4, el) :: sigma) ]
in
List.concat_map ~f:g eqs_sigma_list
in
List.fold ~f ~init:[([], [])] sigma
else [([], sigma)]
(** [hpara_instantiate para e1 e2 elist] instantiates [para] with [e1],
[e2] and [elist]. If [para = lambda (x, y, xs). exists zs. b],
then the result of the instantiation is [b\[e1 / x, e2 / y, elist / xs, _zs'/ zs\]]
for some fresh [_zs'].*)
let hpara_instantiate para e1 e2 elist =
let subst_for_svars =
let g id e = (id, e) in
try List.map2_exn ~f:g para.svars elist
with Invalid_argument _ -> assert false
in
let ids_evars =
let g _ = Ident.create_fresh Ident.kprimed in
List.map ~f:g para.evars
in
let subst_for_evars =
let g id id' = (id, Exp.Var id') in
try List.map2_exn ~f:g para.evars ids_evars
with Invalid_argument _ -> assert false
in
let subst =
`Exp
(exp_subst_of_list ((para.root, e1) :: (para.next, e2) :: subst_for_svars @ subst_for_evars))
in
(ids_evars, List.map ~f:(hpred_sub subst) para.body)
(** [hpara_dll_instantiate para cell blink flink elist] instantiates [para] with [cell],
[blink], [flink], and [elist]. If [para = lambda (x, y, z, xs). exists zs. b],
then the result of the instantiation is
[b\[cell / x, blink / y, flink / z, elist / xs, _zs'/ zs\]]
for some fresh [_zs'].*)
let hpara_dll_instantiate (para: hpara_dll) cell blink flink elist =
let subst_for_svars =
let g id e = (id, e) in
try List.map2_exn ~f:g para.svars_dll elist
with Invalid_argument _ -> assert false
in
let ids_evars =
let g _ = Ident.create_fresh Ident.kprimed in
List.map ~f:g para.evars_dll
in
let subst_for_evars =
let g id id' = (id, Exp.Var id') in
try List.map2_exn ~f:g para.evars_dll ids_evars
with Invalid_argument _ -> assert false
in
let subst =
`Exp
(exp_subst_of_list
( (para.cell, cell) :: (para.blink, blink) :: (para.flink, flink) :: subst_for_svars
@ subst_for_evars ))
in
(ids_evars, List.map ~f:(hpred_sub subst) para.body_dll)
let custom_error = Pvar.mk_global (Mangled.from_string "INFER_CUSTOM_ERROR") Pvar.TUExtern