Pulselib.PulseFormulamodule Atom = PulseFormulaAtommodule SatUnsat = PulseSatUnsatmodule Var = PulseFormulaVarmodule LinArith : sig ... endLinear Arithmetic
module Term : sig ... endmodule Formula : sig ... endtype t = {conditions : int Atom.Map.t;collection of conditions that have been assumed (via PRUNE CFG nodes) along the path. Note that these conditions are *not* normalized w.r.t. phi: phi already contains them so normalization w.r.t. phi would make them trivially true most of the time.
phi : Formula.t;the arithmetic constraints of the current symbolic state; true in both the pre and post since abstract values Var.t have immutable semantics
}include Ppx_compare_lib.Comparable.S with type t := tval compare : t Base__Ppx_compare_lib.compareinclude Ppx_compare_lib.Equal.S with type t := tval equal : t Base__Ppx_compare_lib.equalval yojson_of_t : t -> Ppx_yojson_conv_lib.Yojson.Safe.tval compare_path_stamp : path_stamp -> path_stamp -> intval equal_path_stamp : path_stamp -> path_stamp -> boolval extract_path_stamp : t -> path_stampval is_empty_path_stamp : path_stamp -> boolval pp_path_stamp : F.formatter -> path_stamp -> unitval extract_path_cond : t -> int Atom.Map.tval extract_term_cond : t -> Atom.Set.tval extract_term_cond2 : t -> Term.Set.tval map_is_empty : int Atom.Map.t -> boolval set_is_empty : Atom.Set.t -> boolval formula_is_empty : t -> boolBuild formulas from SIL and tries to decide if they are (mostly un-)satisfiable.
val pp : F.formatter -> t -> unitonly used for unit tests
val compare_function_symbol : function_symbol -> function_symbol -> intval equal_function_symbol : function_symbol -> function_symbol -> booltype operand = | AbstractValueOperand of Var.t| ConstOperand of IR.Const.t| FunctionApplicationOperand of {f : function_symbol;actuals : Var.t list;}val pp_operand : F.formatter -> operand -> unitsome operations will return a set of new facts discovered that are relevant to communicate to the memory domain
val pp_new_eq : F.formatter -> new_eq -> unittype new_eqs = new_eq IStdlib.RevList.tval ttrue : tand_equal_vars v1 v2 phi is and_equal (AbstractValueOperand v1) (AbstractValueOperand v2) phi
val get_dynamic_type : Var.t -> t -> dynamic_type_data optionval and_dynamic_type :
Var.t ->
IR.Typ.t ->
?source_file:IBase.SourceFile.t ->
t ->
(t * new_eqs) SatUnsat.tval add_dynamic_type_unsafe :
Var.t ->
IR.Typ.t ->
?source_file:IBase.SourceFile.t ->
IBase.Location.t ->
t ->
tval and_equal_binop :
Var.t ->
IR.Binop.t ->
operand ->
operand ->
t ->
(t * new_eqs) SatUnsat.tval and_is_int : Var.t -> t -> (t * new_eqs) SatUnsat.tval prune_binop :
?depth:int ->
negated:bool ->
IR.Binop.t ->
?need_atom:bool ->
operand ->
operand ->
t ->
(t * new_eqs) SatUnsat.tSome types or errors detected by Pulse require that the state be *manifest*, which corresponds to the fact that the error can happen in *any reasonable* calling context (see below). If not, the error is flagged as *latent* and not reported until it becomes manifest.
A state is *manifest* when its path condition (here meaning the conjunction of conditions encountered in if statements, loop conditions, etc., i.e. anything in a IR.Sil.instr.Prune node) is either a) empty or b) comprised only of facts of the form p>0 or p≠0 where p is known to be allocated. The latter condition captures the idea that addresses being valid pointers in memory should not deter us from reporting any error that we find on that program path as it is somewhat the happy/expected case (also, the fact p is allocated already implies p≠0). The unhappy/unexpected case here would be to report errors that require a pointer to be invalid or null in the precondition; we do not want to report such errors until we see that there exists a calling context in which the pointer is indeed invalid or null! But, to reiterate, we do want to report errors that only have valid pointers in their precondition. Similarly, ignore conditions of the form x≠y where x and y are already different according to the heap (because they are separate memory allocations).
In addition, only conditions in callees of the current procedure may cause a path to become latent, i.e. direct tests in the current function do not make an issue latent. The rationale for this is that, if the current function branches on a particular condition, it must have some reason to believe both paths are possible in its calling contexts, hence should not exhibit a bug under these conditions. This is different from conditions arising from tests in callees, which may be written more defensively than the current function. (NOTE: this doesn't apply to Erlang)
Some equalities might be represented implicitly in the precondition, see the documentation of PulseArithmetic.is_manifest.
get the canonical representative for the variable according to the equality relation
val implies_conditions_up_to :
subst:Var.t Var.Map.t ->
t ->
implies:t ->
(unit, [> `Contradiction of SatUnsat.unsat_info | `NotImplied of Atom.t ])
IStdlib.IStd.resultnote: each variable mentioned in the formula is visited at least once, possibly more
val absval_of_int : t -> IR.IntLit.t -> t * Var.tGet or create an abstract value (Var.t is AbstractValue.t) associated with a constant IR.IntLit.t. The idea is that clients will record in the abstract state that the returned t is equal to the given integer. If the same integer is queried later on then this module will return the same abstract variable.