[SIL][HIL] distinguish auxiliary instructions as `Metadata`

Summary:
Bundle all non-semantic-bearing instructions into a `Metadata _`
instruction in SIL.

- On a documentation level this makes clearer the distinction between
instructions that encode the semantics of the program and those that are
just hints for the various backend analysis.

- This makes it easier to add more of these auxiliary instructions in
the future. For example, the next diff introduces a new `Skip` auxiliary
instruction to replace the hacky `ExitScope([], Location.dummy)`.

- It also makes it easier to surface all current and future such
auxiliary instructions to HIL as the datatype for these syntactic hints
can be shared between SIL and HIL. This diff brings `Nullify` and
`Abstract` to HIL for free.

Reviewed By: ngorogiannis

Differential Revision: D14827674

fbshipit-source-id: f68fe2110
master
Jules Villard 6 years ago committed by Facebook Github Bot
parent 70b7beac1c
commit b665e1c575

@ -22,7 +22,7 @@ type t =
| Assign of HilExp.AccessExpression.t * HilExp.t * Location.t | Assign of HilExp.AccessExpression.t * HilExp.t * Location.t
| Assume of HilExp.t * [`Then | `Else] * Sil.if_kind * Location.t | Assume of HilExp.t * [`Then | `Else] * Sil.if_kind * Location.t
| Call of AccessPath.base * call * HilExp.t list * CallFlags.t * Location.t | Call of AccessPath.base * call * HilExp.t list * CallFlags.t * Location.t
| ExitScope of Var.t list * Location.t | Metadata of Sil.instr_metadata
[@@deriving compare] [@@deriving compare]
let pp fmt = function let pp fmt = function
@ -35,11 +35,11 @@ let pp fmt = function
let pp_ret fmt = F.fprintf fmt "%a := " AccessPath.pp_base in let pp_ret fmt = F.fprintf fmt "%a := " AccessPath.pp_base in
let pp_actuals fmt = PrettyPrintable.pp_collection ~pp_item:HilExp.pp fmt in let pp_actuals fmt = PrettyPrintable.pp_collection ~pp_item:HilExp.pp fmt in
F.fprintf fmt "%a%a(%a) [%a]" pp_ret ret pp_call call pp_actuals actuals Location.pp loc F.fprintf fmt "%a%a(%a) [%a]" pp_ret ret pp_call call pp_actuals actuals Location.pp loc
| ExitScope (vars, loc) -> | Metadata instr_metadata ->
F.fprintf fmt "exit scope(%a) [%a]" (Pp.seq ~sep:"; " Var.pp) vars Location.pp loc Sil.pp_instr_metadata Pp.text fmt instr_metadata
type translation = Instr of t | Bind of Var.t * HilExp.AccessExpression.t | Ignore type translation = Instr of t | Bind of Var.t * HilExp.AccessExpression.t
(** convert an SIL instruction into an HIL instruction. The [f_resolve_id] function should map an (** convert an SIL instruction into an HIL instruction. The [f_resolve_id] function should map an
SSA temporary variable to the access path it represents. Evaluating the HIL instruction should SSA temporary variable to the access path it represents. Evaluating the HIL instruction should
@ -121,8 +121,5 @@ let of_sil ~include_array_indexes ~f_resolve_id (instr : Sil.instr) =
let hil_exp = exp_of_sil exp (Typ.mk (Tint IBool)) in let hil_exp = exp_of_sil exp (Typ.mk (Tint IBool)) in
let branch = if true_branch then `Then else `Else in let branch = if true_branch then `Then else `Else in
Instr (Assume (hil_exp, branch, if_kind, loc)) Instr (Assume (hil_exp, branch, if_kind, loc))
| ExitScope (vars, loc) -> | Metadata metadata ->
Instr (ExitScope (vars, loc)) Instr (Metadata metadata)
| Abstract _ | Nullify _ ->
(* these don't seem useful for most analyses. can translate them later if we want to *)
Ignore

@ -20,7 +20,7 @@ type t =
(** Assumed expression, true_branch boolean, source of the assume (conditional, ternary, etc.) *) (** Assumed expression, true_branch boolean, source of the assume (conditional, ternary, etc.) *)
| Call of AccessPath.base * call * HilExp.t list * CallFlags.t * Location.t | Call of AccessPath.base * call * HilExp.t list * CallFlags.t * Location.t
(** Var to hold the return, call expression, formals *) (** Var to hold the return, call expression, formals *)
| ExitScope of Var.t list * Location.t | Metadata of Sil.instr_metadata (** see {!Sil.instr_metadata} *)
[@@deriving compare] [@@deriving compare]
val pp : F.formatter -> t -> unit val pp : F.formatter -> t -> unit
@ -29,7 +29,6 @@ val pp : F.formatter -> t -> unit
type translation = type translation =
| Instr of t (** HIL instruction to execute *) | Instr of t (** HIL instruction to execute *)
| Bind of Var.t * HilExp.AccessExpression.t (** add binding to identifier map *) | Bind of Var.t * HilExp.AccessExpression.t (** add binding to identifier map *)
| Ignore (** no-op *)
val of_sil : val of_sil :
include_array_indexes:bool include_array_indexes:bool

@ -187,7 +187,7 @@ module Node = struct
(** Get the source location of the last instruction in the node *) (** Get the source location of the last instruction in the node *)
let get_last_loc n = let get_last_loc n =
n |> get_instrs |> Instrs.last |> Option.value_map ~f:Sil.instr_get_loc ~default:n.loc n |> get_instrs |> Instrs.last |> Option.value_map ~f:Sil.location_of_instr ~default:n.loc
let find_in_node_or_preds = let find_in_node_or_preds =
@ -360,26 +360,17 @@ module Node = struct
(** simple key for a node: just look at the instructions *) (** simple key for a node: just look at the instructions *)
let simple_key node = let simple_key node =
let add_instr instr = let add_instr instr =
if Sil.instr_is_auxiliary instr then None match instr with
else | Sil.Load _ ->
let instr_key = Some 1
match instr with | Sil.Store _ ->
| Sil.Load _ -> Some 2
1 | Sil.Prune _ ->
| Sil.Store _ -> Some 3
2 | Sil.Call _ ->
| Sil.Prune _ -> Some 4
3 | Sil.Metadata _ ->
| Sil.Call _ -> None
4
| Sil.Nullify _ ->
5
| Sil.Abstract _ ->
6
| Sil.ExitScope _ ->
7
in
Some instr_key
in in
get_instrs node get_instrs node
|> IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:add_instr |> IContainer.rev_filter_map_to_list ~fold:Instrs.fold ~f:add_instr

@ -26,6 +26,13 @@ type if_kind =
| Ik_switch | Ik_switch
[@@deriving compare] [@@deriving compare]
type instr_metadata =
| Abstract of Location.t
(** a good place to apply abstraction, mostly used in the biabduction analysis *)
| ExitScope of Var.t list * Location.t (** remove temporaries and dead program variables *)
| Nullify of Pvar.t * Location.t (** nullify stack variable *)
[@@deriving compare]
(** An instruction. *) (** An instruction. *)
type instr = type instr =
(* Note for frontend writers: (* Note for frontend writers:
@ -33,34 +40,41 @@ type instr =
`Load` instruction may be eliminated by copy-propagation. *) `Load` instruction may be eliminated by copy-propagation. *)
| Load of Ident.t * Exp.t * Typ.t * Location.t | Load of Ident.t * Exp.t * Typ.t * Location.t
(** Load a value from the heap into an identifier. (** Load a value from the heap into an identifier.
[x = *lexp:typ] where [x = *lexp:typ] where
[lexp] is an expression denoting a heap address
[typ] is the root type of [lexp]. *) - [lexp] is an expression denoting a heap address
- [typ] is the root type of [lexp]. *)
| Store of Exp.t * Typ.t * Exp.t * Location.t | Store of Exp.t * Typ.t * Exp.t * Location.t
(** Store the value of an expression into the heap. (** Store the value of an expression into the heap.
[*lexp1:typ = exp2] where [*lexp1:typ = exp2] where
[lexp1] is an expression denoting a heap address
[typ] is the root type of [lexp1] - [lexp1] is an expression denoting a heap address
[exp2] is the expression whose value is store. *)
- [typ] is the root type of [lexp1]
- [exp2] is the expression whose value is store. *)
| Prune of Exp.t * Location.t * bool * if_kind | Prune of Exp.t * Location.t * bool * if_kind
(** prune the state based on [exp=1], the boolean indicates whether true branch *) (** prune the state based on [exp=1], the boolean indicates whether true branch *)
| Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t | Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t
(** [Call ((ret_id, ret_typ), e_fun, arg_ts, loc, call_flags)] represents an instruction (** [Call ((ret_id, ret_typ), e_fun, arg_ts, loc, call_flags)] represents an instruction
[ret_id = e_fun(arg_ts);] *) [ret_id = e_fun(arg_ts);] *)
| Nullify of Pvar.t * Location.t (** nullify stack variable *) | Metadata of instr_metadata
| Abstract of Location.t (** apply abstraction *) (** hints about the program that are not strictly needed to understand its semantics, for
| ExitScope of Var.t list * Location.t (** remove temporaries and dead program variables *) instance information about its original syntactic structure *)
[@@deriving compare] [@@deriving compare]
let equal_instr = [%compare.equal: instr] let equal_instr = [%compare.equal: instr]
let skip_instr = ExitScope ([], Location.dummy) let skip_instr = Metadata (ExitScope ([], Location.dummy))
(** Check if an instruction is auxiliary, or if it comes from source instructions. *) (** Check if an instruction is auxiliary, or if it comes from source instructions. *)
let instr_is_auxiliary = function let instr_is_auxiliary = function
| Load _ | Store _ | Prune _ | Call _ -> | Load _ | Store _ | Prune _ | Call _ ->
false false
| Nullify _ | Abstract _ | ExitScope _ -> | Metadata _ ->
true true
@ -325,20 +339,30 @@ let d_offset_list (offl : offset list) = L.d_pp_with_pe pp_offset_list offl
let pp_exp_typ pe f (e, t) = F.fprintf f "%a:%a" (pp_exp_printenv pe) e (Typ.pp pe) t let pp_exp_typ pe f (e, t) = F.fprintf f "%a:%a" (pp_exp_printenv pe) e (Typ.pp pe) t
let location_of_instr_metadata = function
| Abstract loc | ExitScope (_, loc) | Nullify (_, loc) ->
loc
(** Get the location of the instruction *) (** Get the location of the instruction *)
let instr_get_loc = function let location_of_instr = function
| Load (_, _, _, loc) | Load (_, _, _, loc) | Store (_, _, _, loc) | Prune (_, loc, _, _) | Call (_, _, _, loc, _) ->
| Store (_, _, _, loc)
| Prune (_, loc, _, _)
| Call (_, _, _, loc, _)
| Nullify (_, loc)
| Abstract loc
| ExitScope (_, loc) ->
loc loc
| Metadata metadata ->
location_of_instr_metadata metadata
let exps_of_instr_metadata = function
| Abstract _ ->
[]
| ExitScope (vars, _) ->
List.map ~f:Var.to_exp vars
| Nullify (pvar, _) ->
[Exp.Lvar pvar]
(** get the expressions occurring in the instruction *) (** get the expressions occurring in the instruction *)
let instr_get_exps = function let exps_of_instr = function
| Load (id, e, _, _) -> | Load (id, e, _, _) ->
[Exp.Var id; e] [Exp.Var id; e]
| Store (e1, _, e2, _) -> | Store (e1, _, e2, _) ->
@ -347,12 +371,8 @@ let instr_get_exps = function
[cond] [cond]
| Call ((id, _), e, _, _, _) -> | Call ((id, _), e, _, _, _) ->
[e; Exp.Var id] [e; Exp.Var id]
| Nullify (pvar, _) -> | Metadata metadata ->
[Exp.Lvar pvar] exps_of_instr_metadata metadata
| Abstract _ ->
[]
| ExitScope (vars, _) ->
List.map ~f:Var.to_exp vars
(** Convert an if_kind to string *) (** Convert an if_kind to string *)
@ -373,7 +393,15 @@ let if_kind_to_string = function
"switch" "switch"
(** Pretty print an instruction. *) let pp_instr_metadata pe f = function
| Abstract loc ->
F.fprintf f "APPLY_ABSTRACTION; [%a]" Location.pp loc
| ExitScope (vars, loc) ->
F.fprintf f "EXIT_SCOPE(%a); [%a]" (Pp.seq ~sep:"," Var.pp) vars Location.pp loc
| Nullify (pvar, loc) ->
F.fprintf f "NULLIFY(%a); [%a]" (Pvar.pp pe) pvar Location.pp loc
let pp_instr ~print_types pe0 f instr = let pp_instr ~print_types pe0 f instr =
let pp_typ = if print_types then Typ.pp_full else Typ.pp in let pp_typ = if print_types then Typ.pp_full else Typ.pp in
color_wrapper pe0 f instr ~f:(fun pe f instr -> color_wrapper pe0 f instr ~f:(fun pe f instr ->
@ -392,12 +420,8 @@ let pp_instr ~print_types pe0 f instr =
F.fprintf f "%a(%a)%a [%a]" (pp_exp_printenv ~print_types pe) e F.fprintf f "%a(%a)%a [%a]" (pp_exp_printenv ~print_types pe) e
(Pp.comma_seq (pp_exp_typ pe)) (Pp.comma_seq (pp_exp_typ pe))
arg_ts CallFlags.pp cf Location.pp loc arg_ts CallFlags.pp cf Location.pp loc
| Nullify (pvar, loc) -> | Metadata metadata ->
F.fprintf f "NULLIFY(%a); [%a]" (Pvar.pp pe) pvar Location.pp loc pp_instr_metadata pe0 f metadata )
| Abstract loc ->
F.fprintf f "APPLY_ABSTRACTION; [%a]" Location.pp loc
| ExitScope (vars, loc) ->
F.fprintf f "EXIT_SCOPE(%a); [%a]" (Pp.seq ~sep:"," Var.pp) vars Location.pp loc )
let add_with_block_parameters_flag instr = let add_with_block_parameters_flag instr =
@ -1269,7 +1293,7 @@ let instr_sub_ids ~sub_id_binders f instr =
| Prune (exp, loc, true_branch, if_kind) -> | Prune (exp, loc, true_branch, if_kind) ->
let exp' = exp_sub_ids f exp in let exp' = exp_sub_ids f exp in
if phys_equal exp' exp then instr else Prune (exp', loc, true_branch, if_kind) if phys_equal exp' exp then instr else Prune (exp', loc, true_branch, if_kind)
| ExitScope (vars, loc) -> | Metadata (ExitScope (vars, loc)) ->
let sub_var var = let sub_var var =
match var with match var with
| Var.ProgramVar _ -> | Var.ProgramVar _ ->
@ -1279,8 +1303,8 @@ let instr_sub_ids ~sub_id_binders f instr =
if phys_equal ident ident' then var else Var.of_id ident' if phys_equal ident ident' then var else Var.of_id ident'
in in
let vars' = IList.map_changed ~equal:phys_equal ~f:sub_var vars in let vars' = IList.map_changed ~equal:phys_equal ~f:sub_var vars in
if phys_equal vars vars' then instr else ExitScope (vars', loc) if phys_equal vars vars' then instr else Metadata (ExitScope (vars', loc))
| Nullify _ | Abstract _ -> | Metadata (Abstract _ | Nullify _) ->
instr instr

@ -24,6 +24,13 @@ type if_kind =
| Ik_switch | Ik_switch
[@@deriving compare] [@@deriving compare]
type instr_metadata =
| Abstract of Location.t
(** a good place to apply abstraction, mostly used in the biabduction analysis *)
| ExitScope of Var.t list * Location.t (** remove temporaries and dead program variables *)
| Nullify of Pvar.t * Location.t (** nullify stack variable *)
[@@deriving compare]
(** An instruction. *) (** An instruction. *)
type instr = type instr =
(* Note for frontend writers: (* Note for frontend writers:
@ -45,9 +52,9 @@ type instr =
| Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t | Call of (Ident.t * Typ.t) * Exp.t * (Exp.t * Typ.t) list * Location.t * CallFlags.t
(** [Call ((ret_id, ret_typ), e_fun, arg_ts, loc, call_flags)] represents an instruction (** [Call ((ret_id, ret_typ), e_fun, arg_ts, loc, call_flags)] represents an instruction
[ret_id = e_fun(arg_ts);] *) [ret_id = e_fun(arg_ts);] *)
| Nullify of Pvar.t * Location.t (** nullify stack variable *) | Metadata of instr_metadata
| Abstract of Location.t (** apply abstraction *) (** hints about the program that are not strictly needed to understand its semantics, for
| ExitScope of Var.t list * Location.t (** remove temporaries and dead program variables *) instance information about its original syntactic structure *)
[@@deriving compare] [@@deriving compare]
val equal_instr : instr -> instr -> bool val equal_instr : instr -> instr -> bool
@ -275,15 +282,17 @@ val pp_offset : Pp.env -> F.formatter -> offset -> unit
val d_offset_list : offset list -> unit val d_offset_list : offset list -> unit
(** Dump a list of offsets *) (** Dump a list of offsets *)
val instr_get_loc : instr -> Location.t val location_of_instr : instr -> Location.t
(** Get the location of the instruction *) (** Get the location of the instruction *)
val instr_get_exps : instr -> Exp.t list val exps_of_instr : instr -> Exp.t list
(** get the expressions occurring in the instruction *) (** get the expressions occurring in the instruction *)
val if_kind_to_string : if_kind -> string val if_kind_to_string : if_kind -> string
(** Pretty print an if_kind *) (** Pretty print an if_kind *)
val pp_instr_metadata : Pp.env -> F.formatter -> instr_metadata -> unit
val pp_instr : print_types:bool -> Pp.env -> F.formatter -> instr -> unit val pp_instr : print_types:bool -> Pp.env -> F.formatter -> instr -> unit
(** Pretty print an instruction. *) (** Pretty print an instruction. *)

@ -113,7 +113,7 @@ let with_formals_types_proc callee_pdesc resolved_pdesc substitutions =
Some call_instr Some call_instr
| Sil.Prune (origin_exp, loc, is_true_branch, if_kind) -> | Sil.Prune (origin_exp, loc, is_true_branch, if_kind) ->
Some (Sil.Prune (convert_exp origin_exp, loc, is_true_branch, if_kind)) Some (Sil.Prune (convert_exp origin_exp, loc, is_true_branch, if_kind))
| Sil.Nullify _ | Abstract _ | ExitScope _ -> | Sil.Metadata _ ->
(* these are generated instructions that will be replaced by the preanalysis *) (* these are generated instructions that will be replaced by the preanalysis *)
None None
in in
@ -206,7 +206,7 @@ let with_block_args_instrs resolved_pdesc substitutions =
(Var.of_id id, (Exp.Var id, pvar, typ), Sil.Load (id, Exp.Lvar pvar, typ, loc)) ) (Var.of_id id, (Exp.Var id, pvar, typ), Sil.Load (id, Exp.Lvar pvar, typ, loc)) )
|> List.unzip3 |> List.unzip3
in in
let remove_temps_instr = Sil.ExitScope (dead_vars, loc) in let remove_temps_instr = Sil.Metadata (ExitScope (dead_vars, loc)) in
(block_name, id_exp_typs, load_instrs, remove_temps_instr) (block_name, id_exp_typs, load_instrs, remove_temps_instr)
in in
let convert_generic_call return_ids exp origin_args loc call_flags = let convert_generic_call return_ids exp origin_args loc call_flags =
@ -261,7 +261,7 @@ let with_block_args_instrs resolved_pdesc substitutions =
convert_generic_call return_ids origin_call_exp origin_args loc call_flags convert_generic_call return_ids origin_call_exp origin_args loc call_flags
| Sil.Prune (origin_exp, loc, is_true_branch, if_kind) -> | Sil.Prune (origin_exp, loc, is_true_branch, if_kind) ->
(Sil.Prune (convert_exp origin_exp, loc, is_true_branch, if_kind) :: instrs, id_map) (Sil.Prune (convert_exp origin_exp, loc, is_true_branch, if_kind) :: instrs, id_map)
| Sil.Nullify _ | Abstract _ | Sil.ExitScope _ -> | Sil.Metadata _ ->
(* these are generated instructions that will be replaced by the preanalysis *) (* these are generated instructions that will be replaced by the preanalysis *)
(instrs, id_map) (instrs, id_map)
in in

@ -68,17 +68,17 @@ module Make (TransferFunctions : TransferFunctions.HIL) (HilConfig : HilConfig)
HilInstr.of_sil ~include_array_indexes:HilConfig.include_array_indexes ~f_resolve_id instr HilInstr.of_sil ~include_array_indexes:HilConfig.include_array_indexes ~f_resolve_id instr
in in
match hil_translation with match hil_translation with
| Ignore ->
(None, bindings)
| Bind (id, access_path) -> | Bind (id, access_path) ->
(None, Bindings.add id access_path bindings) (None, Bindings.add id access_path bindings)
| Instr (ExitScope (vars, loc)) -> | Instr (Metadata (ExitScope (vars, loc))) ->
let bindings, vars = let bindings, vars =
List.fold vars ~init:(bindings, []) ~f:(fun (bindings, vars) var -> List.fold vars ~init:(bindings, []) ~f:(fun (bindings, vars) var ->
let bindings, vars' = Bindings.exit_scope var bindings in let bindings, vars' = Bindings.exit_scope var bindings in
(bindings, append_bindings vars vars') ) (bindings, append_bindings vars vars') )
in in
let instr = if List.is_empty vars then None else Some (HilInstr.ExitScope (vars, loc)) in let instr =
if List.is_empty vars then None else Some (HilInstr.Metadata (ExitScope (vars, loc)))
in
(instr, bindings) (instr, bindings)
| Instr instr -> | Instr instr ->
(Some instr, bindings) (Some instr, bindings)

@ -499,7 +499,7 @@ let explain_leak tenv hpred prop alloc_att_opt bucket =
| None -> | None ->
if verbose then L.d_strln "explain_leak: no current instruction" ; if verbose then L.d_strln "explain_leak: no current instruction" ;
value_str_from_pvars_vpath [] vpath value_str_from_pvars_vpath [] vpath
| Some (Sil.Nullify (pvar, _)) when check_pvar pvar -> ( | Some (Sil.Metadata (Nullify (pvar, _))) when check_pvar pvar -> (
if verbose then ( if verbose then (
L.d_str "explain_leak: current instruction is Nullify for pvar " ; L.d_str "explain_leak: current instruction is Nullify for pvar " ;
Pvar.d pvar ; Pvar.d pvar ;
@ -509,10 +509,10 @@ let explain_leak tenv hpred prop alloc_att_opt bucket =
Some (DExp.to_string de) Some (DExp.to_string de)
| _ -> | _ ->
None ) None )
| Some (Sil.Abstract _) -> | Some (Sil.Metadata (Abstract _)) ->
if verbose then L.d_strln "explain_leak: current instruction is Abstract" ; if verbose then L.d_strln "explain_leak: current instruction is Abstract" ;
let get_nullify = function let get_nullify = function
| Sil.Nullify (pvar, _) when check_pvar pvar -> | Sil.Metadata (Nullify (pvar, _)) when check_pvar pvar ->
if verbose then ( if verbose then (
L.d_str "explain_leak: found nullify before Abstract for pvar " ; L.d_str "explain_leak: found nullify before Abstract for pvar " ;
Pvar.d pvar ; Pvar.d pvar ;

@ -29,7 +29,7 @@ let add_abstraction_instructions pdesc =
in in
let do_node node = let do_node node =
let loc = Node.get_last_loc node in let loc = Node.get_last_loc node in
if node_requires_abstraction node then Node.append_instrs node [Sil.Abstract loc] if node_requires_abstraction node then Node.append_instrs node [Sil.Metadata (Abstract loc)]
in in
Procdesc.iter_nodes do_node pdesc Procdesc.iter_nodes do_node pdesc
@ -107,9 +107,9 @@ module NullifyTransferFunctions = struct
(active_defs, to_nullify) (active_defs, to_nullify)
| Sil.Store (Exp.Lvar lhs_pvar, _, _, _) -> | Sil.Store (Exp.Lvar lhs_pvar, _, _, _) ->
(VarDomain.add (Var.of_pvar lhs_pvar) active_defs, to_nullify) (VarDomain.add (Var.of_pvar lhs_pvar) active_defs, to_nullify)
| Sil.Store _ | Prune _ | ExitScope _ | Abstract _ -> | Sil.Store _ | Prune _ | Metadata (Abstract _ | ExitScope _) ->
astate astate
| Sil.Nullify _ -> | Sil.Metadata (Nullify _) ->
L.(die InternalError) L.(die InternalError)
"Should not add nullify instructions before running nullify analysis!" "Should not add nullify instructions before running nullify analysis!"
in in
@ -141,7 +141,7 @@ let add_nullify_instrs pdesc tenv liveness_inv_map =
let is_local pvar = not (Pvar.is_return pvar || Pvar.is_global pvar) in let is_local pvar = not (Pvar.is_return pvar || Pvar.is_global pvar) in
let prepend_node_nullify_instructions loc pvars instrs = let prepend_node_nullify_instructions loc pvars instrs =
List.fold pvars ~init:instrs ~f:(fun instrs pvar -> List.fold pvars ~init:instrs ~f:(fun instrs pvar ->
if is_local pvar then Sil.Nullify (pvar, loc) :: instrs else instrs ) if is_local pvar then Sil.Metadata (Nullify (pvar, loc)) :: instrs else instrs )
in in
let node_deadvars_instruction loc vars = let node_deadvars_instruction loc vars =
let local_vars = let local_vars =
@ -151,7 +151,7 @@ let add_nullify_instrs pdesc tenv liveness_inv_map =
| Var.LogicalVar _ -> | Var.LogicalVar _ ->
true ) true )
in in
if List.is_empty local_vars then None else Some (Sil.ExitScope (local_vars, loc)) if List.is_empty local_vars then None else Some (Sil.Metadata (ExitScope (local_vars, loc)))
in in
Container.iter nullify_proc_cfg ~fold:ProcCfg.Exceptional.fold_nodes ~f:(fun node -> Container.iter nullify_proc_cfg ~fold:ProcCfg.Exceptional.fold_nodes ~f:(fun node ->
match NullifyAnalysis.extract_post (ProcCfg.Exceptional.Node.id node) nullify_inv_map with match NullifyAnalysis.extract_post (ProcCfg.Exceptional.Node.id node) nullify_inv_map with

@ -92,13 +92,13 @@ let get_node () = !gs.last_node
let get_loc_exn () = let get_loc_exn () =
match !gs.last_instr with match !gs.last_instr with
| Some instr -> | Some instr ->
Sil.instr_get_loc instr Sil.location_of_instr instr
| None -> | None ->
get_node_exn () |> Procdesc.Node.get_loc get_node_exn () |> Procdesc.Node.get_loc
let get_loc () = let get_loc () =
match !gs.last_instr with Some instr -> Some (Sil.instr_get_loc instr) | None -> None match !gs.last_instr with Some instr -> Some (Sil.location_of_instr instr) | None -> None
(** normalize the list of instructions by renaming let-bound ids *) (** normalize the list of instructions by renaming let-bound ids *)

@ -1480,7 +1480,7 @@ let rec sym_exec exe_env tenv current_pdesc instr_ (prop_ : Prop.normal Prop.t)
; proc_name= callee_pname ; proc_name= callee_pname
; loc ; loc
; exe_env } ) ; exe_env } )
| Sil.Nullify (pvar, _) -> ( | Sil.Metadata (Nullify (pvar, _)) -> (
let eprop = Prop.expose prop_ in let eprop = Prop.expose prop_ in
match match
List.partition_tf List.partition_tf
@ -1500,13 +1500,13 @@ let rec sym_exec exe_env tenv current_pdesc instr_ (prop_ : Prop.normal Prop.t)
L.internal_error "Pvar %a appears on the LHS of >1 heap predicate!@." (Pvar.pp Pp.text) L.internal_error "Pvar %a appears on the LHS of >1 heap predicate!@." (Pvar.pp Pp.text)
pvar ; pvar ;
assert false ) assert false )
| Sil.Abstract _ -> | Sil.Metadata (Abstract _) ->
if Prover.check_inconsistency tenv prop_ then ret_old_path [] if Prover.check_inconsistency tenv prop_ then ret_old_path []
else else
ret_old_path ret_old_path
[ Abs.remove_redundant_array_elements current_pname tenv [ Abs.remove_redundant_array_elements current_pname tenv
(Abs.abstract current_pname tenv prop_) ] (Abs.abstract current_pname tenv prop_) ]
| Sil.ExitScope (dead_vars, _) -> | Sil.Metadata (ExitScope (dead_vars, _)) ->
let dead_ids = List.filter_map dead_vars ~f:Var.get_ident in let dead_ids = List.filter_map dead_vars ~f:Var.get_ident in
ret_old_path [Prop.exist_quantify tenv dead_ids prop_] ret_old_path [Prop.exist_quantify tenv dead_ids prop_]
@ -1940,7 +1940,7 @@ and sym_exec_wrapper exe_env handle_exn tenv summary proc_cfg instr
| _ -> | _ ->
() ) ; () ) ;
let node_has_abstraction node = let node_has_abstraction node =
let instr_is_abstraction = function Sil.Abstract _ -> true | _ -> false in let instr_is_abstraction = function Sil.Metadata (Abstract _) -> true | _ -> false in
Instrs.exists ~f:instr_is_abstraction (ProcCfg.Exceptional.instrs node) Instrs.exists ~f:instr_is_abstraction (ProcCfg.Exceptional.instrs node)
in in
let curr_node = State.get_node_exn () in let curr_node = State.get_node_exn () in

@ -373,7 +373,7 @@ let do_after_node node = Printer.node_finish_session node
(** Return the list of normal ids occurring in the instructions *) (** Return the list of normal ids occurring in the instructions *)
let instrs_get_normal_vars instrs = let instrs_get_normal_vars instrs =
let do_instr res instr = let do_instr res instr =
Sil.instr_get_exps instr Sil.exps_of_instr instr
|> List.fold_left ~init:res ~f:(fun res e -> |> List.fold_left ~init:res ~f:(fun res e ->
Exp.free_vars e Exp.free_vars e
|> Sequence.filter ~f:Ident.is_normal |> Sequence.filter ~f:Ident.is_normal
@ -559,7 +559,7 @@ let compute_visited vset =
let lines = let lines =
node_loc.Location.line node_loc.Location.line
:: IContainer.rev_map_to_list ~fold:Instrs.fold :: IContainer.rev_map_to_list ~fold:Instrs.fold
~f:(fun instr -> (Sil.instr_get_loc instr).Location.line) ~f:(fun instr -> (Sil.location_of_instr instr).Location.line)
(ProcCfg.Exceptional.instrs n) (ProcCfg.Exceptional.instrs n)
in in
List.remove_consecutive_duplicates ~equal:Int.equal (List.sort ~compare:Int.compare lines) List.remove_consecutive_duplicates ~equal:Int.equal (List.sort ~compare:Int.compare lines)

@ -270,9 +270,9 @@ module TransferFunctions = struct
let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in let mem = Dom.Mem.add_stack_loc (Loc.of_id id) mem in
L.d_printfln_escaped "/!\\ Call to non-const function %a" Exp.pp fun_exp ; L.d_printfln_escaped "/!\\ Call to non-const function %a" Exp.pp fun_exp ;
Dom.Mem.add_unknown id ~location mem Dom.Mem.add_unknown id ~location mem
| ExitScope (dead_vars, _) -> | Metadata (ExitScope (dead_vars, _)) ->
Dom.Mem.remove_temps (List.filter_map dead_vars ~f:Var.get_ident) mem Dom.Mem.remove_temps (List.filter_map dead_vars ~f:Var.get_ident) mem
| Abstract _ | Nullify _ -> | Metadata (Abstract _ | Nullify _) ->
mem mem

@ -120,7 +120,7 @@ let add_unreachable_code (cfg : CFG.t) (node : CFG.Node.t) instr rem_instrs (che
&& ExitStatement.is_end_of_block_or_procedure cfg node rem_instrs -> && ExitStatement.is_end_of_block_or_procedure cfg node rem_instrs ->
checks checks
| _ -> | _ ->
let location = Sil.instr_get_loc instr in let location = Sil.location_of_instr instr in
let unreachable_statement = UnreachableStatement.{location} in let unreachable_statement = UnreachableStatement.{location} in
{checks with unreachable_statements= unreachable_statement :: checks.unreachable_statements} {checks with unreachable_statements= unreachable_statement :: checks.unreachable_statements}

@ -372,7 +372,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
Domain.remove ret_id_typ astate' ) Domain.remove ret_id_typ astate' )
| Assume (assume_exp, _, _, loc) -> | Assume (assume_exp, _, _, loc) ->
Domain.exp_add_reads assume_exp loc summary astate Domain.exp_add_reads assume_exp loc summary astate
| ExitScope _ -> | Metadata _ ->
astate astate

@ -205,7 +205,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
add_actuals_globals astate pdesc loc actuals add_actuals_globals astate pdesc loc actuals
|> (* make sure it's not Bottom: we made a function call so this needs initialization *) |> (* make sure it's not Bottom: we made a function call so this needs initialization *)
at_least_nonbottom at_least_nonbottom
| ExitScope _ | Abstract _ | Nullify _ -> | Metadata _ ->
astate astate

@ -46,7 +46,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate_acc astate_acc
in in
List.fold ~f:add_actual_by_ref ~init:astate actuals List.fold ~f:add_actual_by_ref ~init:astate actuals
| Sil.Store _ | Load _ | Prune _ | Nullify _ | Abstract _ | ExitScope _ -> | Sil.Store _ | Load _ | Prune _ | Metadata _ ->
astate astate

@ -602,13 +602,13 @@ module InstrBasicCost = struct
CostDomain.zero_record CostDomain.zero_record
| Sil.Load _ | Sil.Store _ | Sil.Call _ | Sil.Prune _ -> | Sil.Load _ | Sil.Store _ | Sil.Call _ | Sil.Prune _ ->
CostDomain.unit_cost_atomic_operation CostDomain.unit_cost_atomic_operation
| Sil.ExitScope _ -> ( | Sil.Metadata (ExitScope _) -> (
match InstrCFG.Node.kind instr_node with match InstrCFG.Node.kind instr_node with
| Procdesc.Node.Start_node -> | Procdesc.Node.Start_node ->
CostDomain.unit_cost_atomic_operation CostDomain.unit_cost_atomic_operation
| _ -> | _ ->
CostDomain.zero_record ) CostDomain.zero_record )
| _ -> | Sil.Metadata (Abstract _ | Nullify _) ->
CostDomain.zero_record CostDomain.zero_record

@ -35,7 +35,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Sil.Store (Lvar lhs_pvar, _, Exp.Const (Const.Cfun pn), _) -> | Sil.Store (Lvar lhs_pvar, _, Exp.Const (Const.Cfun pn), _) ->
(* strong update *) (* strong update *)
Domain.add (Pvar.to_string lhs_pvar) (ProcnameSet.singleton pn) astate Domain.add (Pvar.to_string lhs_pvar) (ProcnameSet.singleton pn) astate
| Sil.Abstract _ | Call _ | Load _ | Nullify _ | Prune _ | ExitScope _ | Store _ -> | Sil.Load _ | Store _ | Call _ | Prune _ | Metadata _ ->
astate astate

@ -157,7 +157,7 @@ module TransferFunctions (LConfig : LivenessConfig) (CFG : ProcCfg.S) = struct
Domain.remove (Var.of_id ret_id) astate Domain.remove (Var.of_id ret_id) astate
|> exp_add_live call_exp |> exp_add_live call_exp
|> add_live_actuals actuals_to_read call_exp |> add_live_actuals actuals_to_read call_exp
| Sil.ExitScope _ | Abstract _ | Nullify _ -> | Sil.Metadata _ ->
astate astate
@ -185,7 +185,7 @@ module CapturedByRefTransferFunctions (CFG : ProcCfg.S) = struct
type extras = ProcData.no_extras type extras = ProcData.no_extras
let exec_instr astate _ _ instr = let exec_instr astate _ _ instr =
List.fold (Sil.instr_get_exps instr) List.fold (Sil.exps_of_instr instr)
~f:(fun acc exp -> ~f:(fun acc exp ->
Exp.fold_captured exp Exp.fold_captured exp
~f:(fun acc exp -> ~f:(fun acc exp ->

@ -299,7 +299,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
{astate with maybe_uninit_vars} {astate with maybe_uninit_vars}
| Assume (expr, _, _, loc) -> | Assume (expr, _, _, loc) ->
check_hil_expr expr ~loc ; astate check_hil_expr expr ~loc ; astate
| ExitScope _ -> | Metadata _ ->
astate astate

@ -497,7 +497,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
do_assignment lhs_access_expr rhs_exp loc proc_data astate do_assignment lhs_access_expr rhs_exp loc proc_data astate
| Assume (assume_exp, _, _, loc) -> | Assume (assume_exp, _, _, loc) ->
do_assume assume_exp loc proc_data astate do_assume assume_exp loc proc_data astate
| ExitScope _ -> | Metadata _ ->
astate astate

@ -88,7 +88,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
|> Option.value_map ~default:astate ~f:(Domain.integrate_summary tenv astate callee loc) |> Option.value_map ~default:astate ~f:(Domain.integrate_summary tenv astate callee loc)
in in
match instr with match instr with
| Assign _ | Assume _ | Call (_, Indirect _, _, _, _) | ExitScope _ -> | Assign _ | Assume _ | Call (_, Indirect _, _, _, _) | Metadata _ ->
astate astate
| Call (_, Direct callee, actuals, _, _) when should_skip_analysis tenv callee actuals -> | Call (_, Direct callee, actuals, _, _) when should_skip_analysis tenv callee actuals ->
astate astate

@ -72,7 +72,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| Call (_, Indirect _, _, _, _) -> | Call (_, Indirect _, _, _, _) ->
(* This should never happen in Java. Fail if it does. *) (* This should never happen in Java. Fail if it does. *)
L.(die InternalError) "Unexpected indirect call %a" HilInstr.pp instr L.(die InternalError) "Unexpected indirect call %a" HilInstr.pp instr
| ExitScope _ -> | Metadata _ ->
astate astate

@ -112,7 +112,7 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| None -> | None ->
astate astate
else astate else astate
| ExitScope _ -> | Metadata _ ->
astate astate

@ -416,14 +416,14 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
ignore (typecheck_expr_simple typestate1 exp1 (Typ.mk Tvoid) TypeOrigin.Undef loc1) ignore (typecheck_expr_simple typestate1 exp1 (Typ.mk Tvoid) TypeOrigin.Undef loc1)
in in
match instr with match instr with
| Sil.ExitScope (vars, _) -> | Sil.Metadata (ExitScope (vars, _)) ->
List.fold_right vars ~init:typestate ~f:(fun var astate -> List.fold_right vars ~init:typestate ~f:(fun var astate ->
match var with match var with
| Var.LogicalVar id -> | Var.LogicalVar id ->
TypeState.remove_id id astate TypeState.remove_id id astate
| Var.ProgramVar _ -> | Var.ProgramVar _ ->
astate ) astate )
| Sil.Abstract _ | Sil.Nullify _ -> | Sil.Metadata (Abstract _ | Nullify _) ->
typestate typestate
| Sil.Load (id, e, typ, loc) -> | Sil.Load (id, e, typ, loc) ->
typecheck_expr_for_errors typestate e loc ; typecheck_expr_for_errors typestate e loc ;

@ -154,9 +154,11 @@ module PulseTransferFunctions = struct
dispatch_call summary ret call actuals flags loc astate |> check_error summary dispatch_call summary ret call actuals flags loc astate |> check_error summary
in in
[post] [post]
| ExitScope (vars, _) -> | Metadata (ExitScope (vars, _)) ->
let post = PulseOperations.remove_vars vars astate in let post = PulseOperations.remove_vars vars astate in
[post] [post]
| Metadata (Abstract _ | Nullify _) ->
[astate]
let pp_session_name _node fmt = F.pp_print_string fmt "Pulse" let pp_session_name _node fmt = F.pp_print_string fmt "Pulse"

@ -17,12 +17,12 @@ let tests =
Cfg.create_proc_desc cfg Cfg.create_proc_desc cfg
(ProcAttributes.default (SourceFile.invalid __FILE__) Typ.Procname.empty_block) (ProcAttributes.default (SourceFile.invalid __FILE__) Typ.Procname.empty_block)
in in
let dummy_instr1 = Sil.ExitScope ([], Location.dummy) in let dummy_instr1 = Sil.skip_instr in
let dummy_instr2 = Sil.Abstract Location.dummy in let dummy_instr2 = Sil.Metadata (Abstract Location.dummy) in
let dummy_instr3 = let dummy_instr3 =
Sil.ExitScope ([Var.of_id (Ident.create_fresh Ident.knormal)], Location.dummy) Sil.Metadata (ExitScope ([Var.of_id (Ident.create_fresh Ident.knormal)], Location.dummy))
in in
let dummy_instr4 = Sil.ExitScope ([], Location.dummy) in let dummy_instr4 = Sil.skip_instr in
let instrs1 = [dummy_instr1; dummy_instr2] in let instrs1 = [dummy_instr1; dummy_instr2] in
let instrs2 = [dummy_instr3] in let instrs2 = [dummy_instr3] in
let instrs3 = [dummy_instr4] in let instrs3 = [dummy_instr4] in

@ -1,5 +1,5 @@
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 60 here,accessed `*(ptr)` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_branch_bad, 6, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 60 here,accessed `*(ptr)` here]
codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 70 here,accessed `ptr` here] codetoanalyze/cpp/pulse/basics.cpp, multiple_invalidations_loop_bad, 3, USE_AFTER_DELETE, no_bucket, ERROR, [invalidated by call to `delete ptr` at line 68 here,accessed `ptr` here]
codetoanalyze/cpp/pulse/closures.cpp, delete_lambda_then_call_bad, 3, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `std::function<_fn_>::function(&(lambda),&(0$?%__sil_tmpSIL_materialize_temp__n$8))`,invalidated by destructor call `std::function<_fn_>::~function(lambda)` at line 102 here,accessed `lambda` here] codetoanalyze/cpp/pulse/closures.cpp, delete_lambda_then_call_bad, 3, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `std::function<_fn_>::function(&(lambda),&(0$?%__sil_tmpSIL_materialize_temp__n$8))`,invalidated by destructor call `std::function<_fn_>::~function(lambda)` at line 102 here,accessed `lambda` here]
codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S::S(&(s),&(0$?%__sil_tmpSIL_materialize_temp__n$12))`,`&(s)` captured as `s`,invalidated by destructor call `S::~S(s)` at line 30 here,accessed `&(f)` here] codetoanalyze/cpp/pulse/closures.cpp, implicit_ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S::S(&(s),&(0$?%__sil_tmpSIL_materialize_temp__n$12))`,`&(s)` captured as `s`,invalidated by destructor call `S::~S(s)` at line 30 here,accessed `&(f)` here]
codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S::S(&(s))`,`&(s)` captured as `s`,invalidated by destructor call `S::~S(s)` at line 21 here,accessed `&(f)` here] codetoanalyze/cpp/pulse/closures.cpp, ref_capture_destroy_invoke_bad, 6, USE_AFTER_DESTRUCTOR, no_bucket, ERROR, [returned from call to `S::S(&(s))`,`&(s)` captured as `s`,invalidated by destructor call `S::~S(s)` at line 21 here,accessed `&(f)` here]

Loading…
Cancel
Save