[pulse] explicit Ok/Error summaries: bi-abduction for interprocedural analysis

Summary: [pulse] explicit Ok/Error summaries: interprocedural analysis

Reviewed By: jvillard

Differential Revision: D25871033

fbshipit-source-id: 921b7f57b
master
Loc Le 4 years ago committed by Facebook GitHub Bot
parent 7487676f9f
commit e11b1b49b3

@ -239,6 +239,8 @@ module type PPUniqRankSet = sig
val fold_map : t -> init:'accum -> f:('accum -> elt -> 'accum * elt) -> 'accum * t
val for_all : f:(elt -> bool) -> t -> bool
val is_empty : t -> bool
val is_singleton : t -> bool
@ -282,6 +284,8 @@ module MakePPUniqRankSet
let fold map ~init ~f = Map.fold (fun _key value accum -> f accum value) map init
let for_all ~f map = Map.for_all (fun _rank value -> f value) map
let is_empty = Map.is_empty
let is_singleton m = Int.equal 1 (Map.cardinal m)

@ -190,6 +190,8 @@ module type PPUniqRankSet = sig
val fold_map : t -> init:'accum -> f:('accum -> elt -> 'accum * elt) -> 'accum * t
val for_all : f:(elt -> bool) -> t -> bool
val is_empty : t -> bool
val is_singleton : t -> bool

@ -113,7 +113,7 @@ let pp f {post; pre; topl; path_condition; skipped_calls; isl_status} =
skipped_calls PulseTopl.pp_state topl
let set_isl_error_status astate = {astate with isl_status= PostStatus.ISLError}
let set_isl_status status astate = {astate with isl_status= status}
let set_path_condition path_condition astate = {astate with path_condition}
@ -148,7 +148,7 @@ module Stack = struct
if phys_equal new_post astate.post then astate else {astate with post= new_post}
let eval origin var astate =
let eval location origin var astate =
match BaseStack.find_opt var (astate.post :> base_domain).stack with
| Some addr_hist ->
(astate, addr_hist)
@ -156,6 +156,18 @@ module Stack = struct
let addr = AbstractValue.mk_fresh () in
let addr_hist = (addr, origin) in
let post_stack = BaseStack.add var addr_hist (astate.post :> base_domain).stack in
let post_heap =
if Config.pulse_isl then
BaseMemory.register_address addr (astate.post :> base_domain).heap
else (astate.post :> base_domain).heap
in
let post_attrs =
if Config.pulse_isl then
let access_trace = Trace.Immediate {location; history= []} in
BaseAddressAttributes.add_one addr (MustBeValid access_trace)
(astate.post :> base_domain).attrs
else (astate.post :> base_domain).attrs
in
let pre =
(* do not overwrite values of variables already in the pre *)
if (not (BaseStack.mem var (astate.pre :> base_domain).stack)) && is_abducible astate var
@ -166,7 +178,7 @@ module Stack = struct
PreDomain.update ~stack:foot_stack ~heap:foot_heap astate.pre
else astate.pre
in
( { post= PostDomain.update astate.post ~stack:post_stack
( { post= PostDomain.update astate.post ~stack:post_stack ~heap:post_heap ~attrs:post_attrs
; pre
; topl= astate.topl
; skipped_calls= astate.skipped_calls
@ -238,7 +250,14 @@ module AddressAttributes = struct
let invalidate address invalidation location astate =
map_post_attrs astate ~f:(BaseAddressAttributes.invalidate address invalidation location)
let astate =
map_post_attrs ~f:(BaseAddressAttributes.invalidate address invalidation location) astate
in
if Config.pulse_isl then
map_post_attrs ~f:(BaseAddressAttributes.remove_must_be_valid_attr (fst address)) astate
|> map_post_attrs ~f:(BaseAddressAttributes.remove_isl_abduced_attr (fst address))
|> map_post_attrs ~f:(BaseAddressAttributes.remove_allocation_attr (fst address))
else astate
let allocate procname address location astate =
@ -257,6 +276,14 @@ module AddressAttributes = struct
map_post_attrs astate ~f:(BaseAddressAttributes.add_one address attributes)
let remove_islabduced_attr address astate =
map_post_attrs astate ~f:(BaseAddressAttributes.remove_isl_abduced_attr address)
let remove_must_be_valid_attr address astate =
map_post_attrs astate ~f:(BaseAddressAttributes.remove_must_be_valid_attr address)
let get_closure_proc_name addr astate =
BaseAddressAttributes.get_closure_proc_name addr (astate.post :> base_domain).attrs
@ -286,6 +313,20 @@ module AddressAttributes = struct
match attr with Attribute.WrittenTo _ -> initialize value astate | _ -> astate )
let add_attrs value attrs astate =
let astate =
match Attributes.get_invalid attrs with
| Some _ ->
remove_must_be_valid_attr value astate
|> remove_allocation_attr value |> remove_islabduced_attr value
| None ->
astate
in
Attributes.fold attrs ~init:astate ~f:(fun astate attr ->
let astate = add_one value attr astate in
match attr with Attribute.WrittenTo _ -> initialize value astate | _ -> astate )
let find_opt address astate =
BaseAddressAttributes.find_opt address (astate.post :> base_domain).attrs
@ -301,7 +342,7 @@ module AddressAttributes = struct
| None ->
let is_eq_null = PathCondition.is_known_zero astate.path_condition addr in
let null_astates =
if PathCondition.is_known_neq_zero astate.path_condition addr then []
if PathCondition.is_known_not_equal_zero astate.path_condition addr then []
else
let null_attr =
Attribute.Invalid (Invalidation.ConstantDereference IntLit.zero, access_trace)
@ -470,7 +511,6 @@ let mk_initial proc_desc =
let pre =
PreDomain.update ~stack:initial_stack ~heap:initial_heap ~attrs:initial_attrs PreDomain.empty
in
let locals = Procdesc.get_locals proc_desc in
let initial_heap, initial_attrs =
if Config.pulse_isl then (initial_heap, initial_attrs)
else ((PreDomain.empty :> base_domain).heap, (PreDomain.empty :> base_domain).attrs)
@ -478,6 +518,7 @@ let mk_initial proc_desc =
let post =
PostDomain.update ~stack:initial_stack ~heap:initial_heap ~attrs:initial_attrs PostDomain.empty
in
let locals = Procdesc.get_locals proc_desc in
let post =
List.fold locals ~init:post ~f:(fun (acc : PostDomain.t) {ProcAttributes.name; typ} ->
set_uninitialized_post (`LocalDecl (Pvar.mk name proc_name, None)) typ location acc )

@ -69,7 +69,7 @@ val leq : lhs:t -> rhs:t -> bool
val pp : Format.formatter -> t -> unit
val set_isl_error_status : t -> t
val set_isl_status : PostStatus.t -> t -> t
val mk_initial : Procdesc.t -> t
@ -88,7 +88,7 @@ module Stack : sig
val find_opt : Var.t -> t -> BaseStack.value option
val eval : ValueHistory.t -> Var.t -> t -> t * (AbstractValue.t * ValueHistory.t)
val eval : Location.t -> ValueHistory.t -> Var.t -> t -> t * (AbstractValue.t * ValueHistory.t)
(** return the value of the variable in the stack or create a fresh one if needed *)
val mem : Var.t -> t -> bool
@ -131,6 +131,8 @@ module AddressAttributes : sig
val add_one : AbstractValue.t -> Attribute.t -> t -> t
(** add the attribute only to the post *)
val add_attrs : AbstractValue.t -> Attributes.t -> t -> t
val check_valid : Trace.t -> AbstractValue.t -> t -> (t, Invalidation.t * Trace.t) result
val check_initialized : Trace.t -> AbstractValue.t -> t -> (t, unit) result

@ -75,6 +75,22 @@ module Attribute = struct
let isl_abduced_rank = Variants.to_rank (ISLAbduced dummy_trace)
let isl_subset attr1 attr2 =
match (attr1, attr2) with
| Invalid (v1, _), Invalid (v2, _) ->
Invalidation.isl_equiv v1 v2
| Invalid _, WrittenTo _ ->
true
| Uninitialized, Uninitialized ->
true
| (MustBeValid _ | Allocated _ | ISLAbduced _), Invalid _ ->
false
| Invalid _, _ | _, Uninitialized ->
false
| _ ->
true
let uninitialized_rank = Variants.to_rank Uninitialized
let must_be_initialized_rank = Variants.to_rank (MustBeInitialized dummy_trace)
@ -200,6 +216,39 @@ module Attributes = struct
trace )
let isl_subset callee_attrs caller_attrs =
Set.for_all callee_attrs ~f:(fun attr1 ->
Set.for_all caller_attrs ~f:(fun attr2 -> Attribute.isl_subset attr1 attr2) )
let replace_isl_abduced attrs_callee attrs_caller =
Set.fold attrs_callee ~init:Set.empty ~f:(fun acc attr1 ->
let attr1 =
match attr1 with
| ISLAbduced _ -> (
match get_allocation attrs_caller with
| None ->
attr1
| Some (p, a) ->
Attribute.Allocated (p, a) )
| Invalid (v_callee, _) -> (
match get_invalid attrs_caller with
| None ->
attr1
| Some (v_caller, trace) -> (
match (v_callee, v_caller) with
| CFree, (CFree | CppDelete) ->
Attribute.Invalid (v_caller, trace)
| ConstantDereference i, OptionalEmpty when IntLit.iszero i ->
Attribute.Invalid (OptionalEmpty, trace)
| _ ->
attr1 ) )
| _ ->
attr1
in
Set.add acc attr1 )
include Set
end

@ -62,4 +62,12 @@ module Attributes : sig
val is_uninitialized : t -> bool
val get_must_be_initialized : t -> Trace.t option
val isl_subset : t -> t -> bool
(** check whether for each attr in the second list, there exists a corresponding attr in the first
according to {!Attributes.isl_equiv}. *)
val replace_isl_abduced : t -> t -> t
(** While applying a spec, replacing ISLAbduced by Allocated and Invalidation.Cfree by
Invalidation.delete, if applicable *)
end

@ -104,6 +104,22 @@ let remove_allocation_attr address memory =
memory
let remove_isl_abduced_attr address memory =
match get_attribute Attributes.get_isl_abduced address memory with
| Some trace ->
remove_one address (Attribute.ISLAbduced trace) memory
| None ->
memory
let remove_must_be_valid_attr address memory =
match get_attribute Attributes.get_must_be_valid address memory with
| Some trace ->
remove_one address (Attribute.MustBeValid trace) memory
| None ->
memory
let initialize address attrs =
if Graph.find_opt address attrs |> Option.exists ~f:Attributes.is_uninitialized then
remove_one address Attribute.Uninitialized attrs

@ -57,6 +57,10 @@ val pp : F.formatter -> t -> unit
val remove_allocation_attr : AbstractValue.t -> t -> t
val remove_must_be_valid_attr : AbstractValue.t -> t -> t
val remove_isl_abduced_attr : AbstractValue.t -> t -> t
val initialize : AbstractValue.t -> t -> t
val canonicalize : get_var_repr:(AbstractValue.t -> AbstractValue.t) -> t -> t

@ -24,6 +24,9 @@ type call_state =
{ astate: AbductiveDomain.t (** caller's abstract state computed so far *)
; subst: (AbstractValue.t * ValueHistory.t) AddressMap.t
(** translation from callee addresses to caller addresses and their caller histories *)
; invalid_subst: (AbstractValue.t * ValueHistory.t) AddressMap.t
(** this is a subset of subst that contains all invalid matches between callee-pre and
caller *)
; rev_subst: AbstractValue.t AddressMap.t
(** the inverse translation from [subst] from caller addresses to callee addresses *)
; visited: AddressSet.t
@ -33,15 +36,18 @@ type call_state =
visit each subgraph from each formal independently so we reset [visited] between the
visit of each formal *) }
let pp_call_state fmt {astate; subst; rev_subst; visited} =
let pp_call_state fmt {astate; subst; invalid_subst; rev_subst; visited} =
F.fprintf fmt
"@[<v>{ astate=@[<hv2>%a@];@,\
\ subst=@[<hv2>%a@];@,\
\ invalid_subst=@[<hv2>%a@];@,\
\ rev_subst=@[<hv2>%a@];@,\
\ visited=@[<hv2>%a@]@,\
\ }@]" AbductiveDomain.pp astate
(AddressMap.pp ~pp_value:(fun fmt (addr, _) -> AbstractValue.pp fmt addr))
subst
(AddressMap.pp ~pp_value:(fun fmt (addr, _) -> AbstractValue.pp fmt addr))
invalid_subst
(AddressMap.pp ~pp_value:AbstractValue.pp)
rev_subst AddressSet.pp visited
@ -58,6 +64,7 @@ type contradiction =
[foo(z,z)] where the spec for [foo(x,y)] says that [x] and [y] are disjoint. *)
| FormalActualLength of
{formals: Var.t list; actuals: ((AbstractValue.t * ValueHistory.t) * Typ.t) list}
| ISLPreconditionMismatch
| PathCondition
let pp_contradiction fmt = function
@ -71,6 +78,8 @@ let pp_contradiction fmt = function
(List.length actuals)
| PathCondition ->
F.pp_print_string fmt "path condition evaluates to false"
| ISLPreconditionMismatch ->
F.pp_print_string fmt "pre does not imply call state"
exception Contradiction of contradiction
@ -82,7 +91,9 @@ let fold_globals_of_stack call_loc stack call_state ~f =
| Var.ProgramVar pvar when Pvar.is_global pvar ->
let call_state, addr_hist_caller =
let astate, var_value =
Stack.eval [ValueHistory.VariableAccessed (pvar, call_loc)] var call_state.astate
Stack.eval call_loc
[ValueHistory.VariableAccessed (pvar, call_loc)]
var call_state.astate
in
if phys_equal astate call_state.astate then (call_state, var_value)
else ({call_state with astate}, var_value)
@ -278,17 +289,44 @@ let apply_arithmetic_constraints callee_proc_name call_location pre_post call_st
in
let call_state = conjoin_callee_arith pre_post call_state in
(* check all callee addresses that make sense for the caller, i.e. the domain of [call_state.subst] *)
AddressMap.fold
(fun addr_callee addr_hist_caller call_state ->
match
BaseAddressAttributes.find_opt addr_callee
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
with
| None ->
call_state
| Some callee_attrs ->
one_address_sat callee_attrs addr_hist_caller call_state )
call_state.subst call_state
if Config.pulse_isl then
AddressMap.fold
(fun addr_callee addr_hist_caller call_state ->
let callee_attr =
BaseAddressAttributes.find_opt addr_callee
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
in
let caller_attr =
BaseAddressAttributes.find_opt (fst addr_hist_caller)
(call_state.astate.AbductiveDomain.post :> BaseDomain.t).attrs
in
match (callee_attr, caller_attr) with
| Some callee_attrs, None ->
one_address_sat callee_attrs addr_hist_caller call_state
| Some callee_attrs, Some caller_attrs ->
if (* check implication *)
Attributes.isl_subset callee_attrs caller_attrs then
{ call_state with
invalid_subst= AddressMap.add addr_callee addr_hist_caller call_state.invalid_subst
}
else if Attributes.is_uninitialized caller_attrs then
one_address_sat callee_attrs addr_hist_caller call_state
else raise (Contradiction ISLPreconditionMismatch)
| _ ->
call_state )
call_state.subst call_state
else
AddressMap.fold
(fun addr_callee addr_hist_caller call_state ->
match
BaseAddressAttributes.find_opt addr_callee
(pre_post.AbductiveDomain.pre :> BaseDomain.t).attrs
with
| None ->
call_state
| Some callee_attrs ->
one_address_sat callee_attrs addr_hist_caller call_state )
call_state.subst call_state
let materialize_pre callee_proc_name call_location pre_post ~captured_vars_with_actuals ~formals
@ -351,7 +389,11 @@ let record_post_cell callee_proc_name call_loc ~edges_pre_opt
let attrs_post_caller =
add_call_to_attributes callee_proc_name call_loc hist_caller attrs_callee_post
in
let astate = AddressAttributes.abduce_and_add addr_caller attrs_post_caller call_state.astate in
let astate =
if Config.pulse_isl then
AddressAttributes.add_attrs addr_caller attrs_post_caller call_state.astate
else AddressAttributes.abduce_and_add addr_caller attrs_post_caller call_state.astate
in
{call_state with astate}
in
let subst, translated_post_edges =
@ -392,13 +434,25 @@ let rec record_post_for_address callee_proc_name call_loc ({AbductiveDomain.pre;
match AbductiveDomain.find_post_cell_opt addr_callee pre_post with
| None ->
call_state
| Some ((edges_post, _attrs_post) as cell_callee_post) ->
| Some ((edges_post, attrs_post) as cell_callee_post) ->
let edges_pre_opt = BaseMemory.find_opt addr_callee (pre :> BaseDomain.t).BaseDomain.heap in
let call_state_after_post =
if is_cell_read_only ~edges_pre_opt ~cell_post:cell_callee_post then call_state
else
let attrs_post =
if Config.pulse_isl then
match
AbductiveDomain.find_post_cell_opt (fst addr_hist_caller) call_state.astate
with
| None ->
attrs_post
| Some (_, attrs_caller) ->
(* if post attr is abduced, use the one in caller which is up-to-date *)
Attributes.replace_isl_abduced attrs_post attrs_caller
else attrs_post
in
record_post_cell callee_proc_name call_loc ~edges_pre_opt addr_hist_caller
~cell_callee_post call_state
~cell_callee_post:(edges_post, attrs_post) call_state
in
Memory.Edges.fold ~init:call_state_after_post edges_post
~f:(fun call_state (_access, (addr_callee_dest, _)) ->
@ -580,6 +634,45 @@ let check_all_valid callee_proc_name call_location {AbductiveDomain.pre; _} call
call_state.subst (Ok call_state.astate)
let isl_check_all_invalid invalid_addr_callers callee_proc_name call_location
{AbductiveDomain.pre; _} pre_astate astate =
match astate.AbductiveDomain.isl_status with
| AbductiveDomain.PostStatus.ISLOk ->
Ok astate
| AbductiveDomain.PostStatus.ISLError ->
AbstractValue.Map.fold
(fun addr_pre (addr_caller, hist_caller) astate_result ->
let mk_access_trace callee_access_trace =
Trace.ViaCall
{ in_call= callee_access_trace
; f= Call callee_proc_name
; location= call_location
; history= hist_caller }
in
match astate_result with
| Error _ ->
astate_result
| Ok astate -> (
match
BaseAddressAttributes.get_invalid addr_caller
(pre_astate.AbductiveDomain.post :> BaseDomain.t).attrs
with
| None ->
astate_result
| Some (invalidation, invalidation_trace) -> (
match BaseAddressAttributes.get_invalid addr_pre (pre :> BaseDomain.t).attrs with
| None ->
astate_result
| Some (_, callee_access_trace) ->
let access_trace = mk_access_trace callee_access_trace in
L.d_printfln "ERROR: caller's %a invalid!" AbstractValue.pp addr_caller ;
Error
( Diagnostic.AccessToInvalidAddress
{calling_context= []; invalidation; invalidation_trace; access_trace}
, AbductiveDomain.set_isl_status ISLError astate ) ) ) )
invalid_addr_callers (Ok astate)
(* - read all the pre, assert validity of addresses and materializes *everything* (to throw stuff
in the *current* pre as appropriate so that callers of the current procedure will also know
about the deeper reads)
@ -599,7 +692,11 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
L.d_printfln "Applying pre/post for %a(%a):@\n%a" Procname.pp callee_proc_name
(Pp.seq ~sep:"," Var.pp) formals AbductiveDomain.pp pre_post ;
let empty_call_state =
{astate; subst= AddressMap.empty; rev_subst= AddressMap.empty; visited= AddressSet.empty}
{ astate
; subst= AddressMap.empty
; invalid_subst= AddressMap.empty
; rev_subst= AddressMap.empty
; visited= AddressSet.empty }
in
(* read the precondition *)
match
@ -618,8 +715,15 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
L.d_printfln "Pre applied successfully. call_state=%a" pp_call_state call_state ;
match
let open IResult.Let_syntax in
let+ astate = check_all_valid callee_proc_name call_location pre_post call_state in
let* astate =
if Config.pulse_isl then
Ok
(AbductiveDomain.set_isl_status pre_post.AbductiveDomain.isl_status call_state.astate)
else check_all_valid callee_proc_name call_location pre_post call_state
in
(* reset [visited] *)
let invalid_subst = call_state.invalid_subst in
let pre_astate = astate in
let call_state = {call_state with astate; visited= AddressSet.empty} in
(* apply the postcondition *)
let call_state, return_caller =
@ -633,6 +737,12 @@ let apply_prepost callee_proc_name call_location ~callee_prepost:pre_post
~callee_prepost:pre_post.AbductiveDomain.topl call_state.astate
else call_state.astate
in
let+ astate =
if Config.pulse_isl then
isl_check_all_invalid invalid_subst callee_proc_name call_location pre_post pre_astate
astate
else Ok astate
in
(astate, return_caller)
with
| post ->

@ -71,6 +71,24 @@ let issue_type_of_cause = function
IssueType.vector_invalidation
let isl_equiv v1 v2 =
match (v1, v2) with
| ConstantDereference i1, ConstantDereference i2 ->
IntLit.eq i1 i2
| CFree, CFree
| CppDelete, CppDelete
| CFree, CppDelete
| CppDelete, CFree
| EndIterator, EndIterator
| GoneOutOfScope _, GoneOutOfScope _
| OptionalEmpty, OptionalEmpty
| StdVector _, StdVector _
| JavaIterator _, JavaIterator _ ->
true
| _ ->
false
let describe f cause =
match cause with
| CFree ->

@ -33,6 +33,9 @@ type t =
| JavaIterator of java_iterator_function
[@@deriving compare, equal]
val isl_equiv : t -> t -> bool
(** check equality up to some ISL equivalences *)
val pp : F.formatter -> t -> unit
val issue_type_of_cause : t -> IssueType.t

@ -53,7 +53,7 @@ let check_and_abduce_addr_access_isl access_mode location (address, history) ?(n
| Error _ ->
Error
( Diagnostic.ReadUninitializedValue {calling_context= []; trace= access_trace}
, AbductiveDomain.set_isl_error_status astate )
, AbductiveDomain.set_isl_status ISLError astate )
| Ok ok_astate ->
Ok (ok_astate :: astates) )
| Write ->
@ -140,7 +140,7 @@ module Closures = struct
(astate, closure_addr_hist)
end
let eval_var var astate = Stack.eval var astate
let eval_var location hist var astate = Stack.eval location hist var astate
let eval_access mode location addr_hist access astate =
let+ astate = check_addr_access mode location addr_hist astate in
@ -166,9 +166,12 @@ let eval mode location exp0 astate =
let rec eval mode exp astate =
match (exp : Exp.t) with
| Var id ->
Ok (eval_var (* error in case of missing history? *) [] (Var.of_id id) astate)
Ok (eval_var location (* error in case of missing history? *) [] (Var.of_id id) astate)
| Lvar pvar ->
Ok (eval_var [ValueHistory.VariableAccessed (pvar, location)] (Var.of_pvar pvar) astate)
Ok
(eval_var location
[ValueHistory.VariableAccessed (pvar, location)]
(Var.of_pvar pvar) astate)
| Lfield (exp', field, _) ->
let* astate, addr_hist = eval Read exp' astate in
eval_access mode location addr_hist (FieldAccess field) astate
@ -285,17 +288,13 @@ let eval_deref_isl location exp astate =
let+ astate = eval_deref location exp astate in
[astate]
in
List.fold ls_astate_addr_hist ~init:(Ok []) ~f:(fun acc ((astate, _) as astate_addr) ->
match acc with
| Ok acc_astates -> (
match astate.AbductiveDomain.isl_status with
| ISLOk ->
let+ astates = eval_deref_function astate_addr in
acc_astates @ astates
| ISLError ->
Ok (acc_astates @ [astate_addr]) )
| Error _ as a ->
a )
List.fold_result ls_astate_addr_hist ~init:[] ~f:(fun acc_astates ((astate, _) as astate_addr) ->
match astate.AbductiveDomain.isl_status with
| ISLOk ->
let+ astates = eval_deref_function astate_addr in
acc_astates @ astates
| ISLError ->
Ok (acc_astates @ [astate_addr]) )
let realloc_pvar pvar typ location astate =

@ -401,7 +401,7 @@ let is_known_zero phi v =
|| Formula.is_known_zero phi.formula v
let is_known_neq_zero phi v =
let is_known_not_equal_zero phi v =
CItvs.find_opt v phi.citvs |> Option.exists ~f:CItv.is_not_equal_to_zero

@ -62,8 +62,9 @@ val prune_binop : negated:bool -> Binop.t -> operand -> operand -> t -> t * new_
val is_known_zero : t -> AbstractValue.t -> bool
(** [is_known_zero phi t] returns [true] if [phi |- t = 0], [false] if we don't know for sure *)
val is_known_neq_zero : t -> AbstractValue.t -> bool
(** [is_known_neq_zero phi t] returns [true] if [phi |- t != 0], [false] if we don't know for sure *)
val is_known_not_equal_zero : t -> AbstractValue.t -> bool
(** [is_known_not_equal_zero phi t] returns [true] if [phi |- t != 0], [false] if we don't know for
sure *)
(* this only consults the concrete intervals domain for now *)

Loading…
Cancel
Save