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.
447 lines
16 KiB
447 lines
16 KiB
9 years ago
|
(*
|
||
6 years ago
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
9 years ago
|
*
|
||
7 years ago
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
9 years ago
|
*)
|
||
|
|
||
8 years ago
|
open! IStd
|
||
9 years ago
|
module F = Format
|
||
|
|
||
8 years ago
|
module type S = sig
|
||
8 years ago
|
module TraceDomain : AbstractDomain.WithBottom
|
||
8 years ago
|
|
||
7 years ago
|
module AccessMap : PrettyPrintable.PPMap with type key = AccessPath.access
|
||
|
|
||
8 years ago
|
module BaseMap = AccessPath.BaseMap
|
||
|
|
||
6 years ago
|
type node = TraceDomain.t * tree
|
||
5 years ago
|
|
||
|
and tree = Subtree of node AccessMap.t | Star
|
||
8 years ago
|
|
||
6 years ago
|
include AbstractDomain.WithBottom with type t = node BaseMap.t
|
||
8 years ago
|
|
||
|
val empty_node : node
|
||
|
|
||
6 years ago
|
val make_node : TraceDomain.t -> node AccessMap.t -> node
|
||
8 years ago
|
|
||
6 years ago
|
val make_access_node : TraceDomain.t -> AccessPath.access -> TraceDomain.t -> node
|
||
9 years ago
|
|
||
6 years ago
|
val make_normal_leaf : TraceDomain.t -> node
|
||
8 years ago
|
|
||
6 years ago
|
val make_starred_leaf : TraceDomain.t -> node
|
||
8 years ago
|
|
||
7 years ago
|
val get_node : AccessPath.Abs.t -> t -> node option
|
||
8 years ago
|
|
||
6 years ago
|
val get_trace : AccessPath.Abs.t -> t -> TraceDomain.t option
|
||
8 years ago
|
|
||
7 years ago
|
val add_node : AccessPath.Abs.t -> node -> t -> t
|
||
8 years ago
|
|
||
6 years ago
|
val add_trace : AccessPath.Abs.t -> TraceDomain.t -> t -> t
|
||
8 years ago
|
|
||
8 years ago
|
val node_join : node -> node -> node
|
||
|
|
||
7 years ago
|
val fold : ('a -> AccessPath.Abs.t -> node -> 'a) -> t -> 'a -> 'a
|
||
8 years ago
|
|
||
6 years ago
|
val trace_fold : ('a -> AccessPath.Abs.t -> TraceDomain.t -> 'a) -> t -> 'a -> 'a
|
||
8 years ago
|
|
||
7 years ago
|
val exists : (AccessPath.Abs.t -> node -> bool) -> t -> bool
|
||
|
|
||
|
val iter : (AccessPath.Abs.t -> node -> unit) -> t -> unit
|
||
|
|
||
7 years ago
|
val depth : t -> int
|
||
|
|
||
8 years ago
|
val pp_node : F.formatter -> node -> unit
|
||
|
end
|
||
|
|
||
7 years ago
|
module type Config = sig
|
||
|
val max_depth : int
|
||
7 years ago
|
|
||
|
(* note : doesn't apply to bases, since we can't represent a starred base *)
|
||
|
|
||
|
val max_width : int
|
||
7 years ago
|
end
|
||
|
|
||
|
module DefaultConfig = struct
|
||
|
(* arbitrarily chosen large value *)
|
||
|
let max_depth = Int.max_value / 2
|
||
7 years ago
|
|
||
|
let max_width = Int.max_value / 2
|
||
7 years ago
|
end
|
||
|
|
||
|
module Make (TraceDomain : AbstractDomain.WithBottom) (Config : Config) = struct
|
||
8 years ago
|
module TraceDomain = TraceDomain
|
||
7 years ago
|
|
||
|
module AccessMap = PrettyPrintable.MakePPMap (struct
|
||
|
type t = AccessPath.access
|
||
|
|
||
|
let compare a1 a2 =
|
||
|
match (a1, a2) with
|
||
7 years ago
|
| AccessPath.ArrayAccess (t1, _), AccessPath.ArrayAccess (t2, _) ->
|
||
|
(* ignore indexes *)
|
||
7 years ago
|
Typ.compare t1 t2
|
||
7 years ago
|
| _ ->
|
||
|
AccessPath.compare_access a1 a2
|
||
|
|
||
7 years ago
|
|
||
|
let pp = AccessPath.pp_access
|
||
|
end)
|
||
|
|
||
8 years ago
|
module BaseMap = AccessPath.BaseMap
|
||
9 years ago
|
|
||
6 years ago
|
type node = TraceDomain.t * tree
|
||
7 years ago
|
|
||
|
and tree = Subtree of node AccessMap.t | Star
|
||
9 years ago
|
|
||
|
type t = node BaseMap.t
|
||
8 years ago
|
|
||
6 years ago
|
let bottom = BaseMap.empty
|
||
9 years ago
|
|
||
6 years ago
|
let is_bottom = BaseMap.is_empty
|
||
7 years ago
|
|
||
8 years ago
|
let make_node trace subtree = (trace, Subtree subtree)
|
||
9 years ago
|
|
||
6 years ago
|
let empty_node = make_node TraceDomain.bottom AccessMap.empty
|
||
9 years ago
|
|
||
7 years ago
|
let is_empty_tree = function Star -> false | Subtree node_map -> AccessMap.is_empty node_map
|
||
|
|
||
8 years ago
|
let make_normal_leaf trace = make_node trace AccessMap.empty
|
||
9 years ago
|
|
||
8 years ago
|
let make_starred_leaf trace = (trace, Star)
|
||
9 years ago
|
|
||
6 years ago
|
let empty_normal_leaf = make_normal_leaf TraceDomain.bottom
|
||
7 years ago
|
|
||
6 years ago
|
let empty_starred_leaf = make_starred_leaf TraceDomain.bottom
|
||
7 years ago
|
|
||
7 years ago
|
(* no need to make it tail-recursive, trees shouldn't be big enough to blow up the call stack *)
|
||
|
let rec node_depth (_, tree) = 1 + tree_depth tree
|
||
|
|
||
|
and tree_depth = function
|
||
7 years ago
|
| Star ->
|
||
|
0
|
||
|
| Subtree node_map ->
|
||
|
AccessMap.fold (fun _ node acc -> max_depth node acc) node_map 0
|
||
|
|
||
7 years ago
|
|
||
|
and max_depth node max_depth_acc = Int.max (node_depth node) max_depth_acc
|
||
|
|
||
|
let depth access_tree = BaseMap.fold (fun _ node acc -> max_depth node acc) access_tree 0
|
||
|
|
||
9 years ago
|
let make_access_node base_trace access trace =
|
||
|
make_node base_trace (AccessMap.singleton access (make_normal_leaf trace))
|
||
|
|
||
7 years ago
|
|
||
8 years ago
|
(** find all of the traces in the subtree and join them with [orig_trace] *)
|
||
6 years ago
|
let rec join_all_traces ?(join_traces = TraceDomain.join) orig_trace = function
|
||
7 years ago
|
| Subtree subtree ->
|
||
|
let join_all_traces_ orig_trace tree =
|
||
8 years ago
|
let node_join_traces _ (trace, node) trace_acc =
|
||
7 years ago
|
join_all_traces (join_traces trace_acc trace) node
|
||
8 years ago
|
in
|
||
|
AccessMap.fold node_join_traces tree orig_trace
|
||
|
in
|
||
8 years ago
|
join_all_traces_ orig_trace subtree
|
||
7 years ago
|
| Star ->
|
||
|
orig_trace
|
||
|
|
||
8 years ago
|
|
||
8 years ago
|
let get_node ap tree =
|
||
|
let rec accesses_get_node access_list trace tree =
|
||
8 years ago
|
match (access_list, tree) with
|
||
7 years ago
|
| _, Star ->
|
||
|
(trace, Star)
|
||
|
| [], (Subtree _ as tree) ->
|
||
|
(trace, tree)
|
||
|
| access :: accesses, Subtree subtree ->
|
||
|
let access_trace, access_subtree = AccessMap.find access subtree in
|
||
8 years ago
|
accesses_get_node accesses access_trace access_subtree
|
||
|
in
|
||
8 years ago
|
let get_node_ base accesses tree =
|
||
9 years ago
|
let base_trace, base_tree = BaseMap.find base tree in
|
||
8 years ago
|
accesses_get_node accesses base_trace base_tree
|
||
|
in
|
||
7 years ago
|
let base, accesses = AccessPath.Abs.extract ap in
|
||
8 years ago
|
match get_node_ base accesses tree with
|
||
7 years ago
|
| trace, subtree ->
|
||
|
if AccessPath.Abs.is_exact ap then Some (trace, subtree)
|
||
9 years ago
|
else
|
||
|
(* input query was [ap]*, and [trace] is the trace associated with [ap]. get the traces
|
||
|
associated with the children of [ap] in [tree] and join them with [trace] *)
|
||
8 years ago
|
Some (join_all_traces trace subtree, subtree)
|
||
7 years ago
|
| exception Caml.Not_found ->
|
||
7 years ago
|
None
|
||
|
|
||
8 years ago
|
|
||
|
let get_trace ap tree = Option.map ~f:fst (get_node ap tree)
|
||
|
|
||
7 years ago
|
let rec access_tree_lteq ((lhs_trace, lhs_tree) as lhs) ((rhs_trace, rhs_tree) as rhs) =
|
||
8 years ago
|
if phys_equal lhs rhs then true
|
||
7 years ago
|
else
|
||
5 years ago
|
TraceDomain.leq ~lhs:lhs_trace ~rhs:rhs_trace
|
||
8 years ago
|
&&
|
||
|
match (lhs_tree, rhs_tree) with
|
||
7 years ago
|
| Subtree lhs_subtree, Subtree rhs_subtree ->
|
||
|
AccessMap.for_all
|
||
9 years ago
|
(fun k lhs_v ->
|
||
8 years ago
|
try
|
||
|
let rhs_v = AccessMap.find k rhs_subtree in
|
||
|
access_tree_lteq lhs_v rhs_v
|
||
7 years ago
|
with Caml.Not_found -> false )
|
||
9 years ago
|
lhs_subtree
|
||
7 years ago
|
| _, Star ->
|
||
|
true
|
||
|
| Star, Subtree _ ->
|
||
|
false
|
||
|
|
||
8 years ago
|
|
||
5 years ago
|
let leq ~lhs ~rhs =
|
||
8 years ago
|
if phys_equal lhs rhs then true
|
||
9 years ago
|
else
|
||
|
BaseMap.for_all
|
||
|
(fun k lhs_v ->
|
||
8 years ago
|
try
|
||
|
let rhs_v = BaseMap.find k rhs in
|
||
|
access_tree_lteq lhs_v rhs_v
|
||
7 years ago
|
with Caml.Not_found -> false )
|
||
9 years ago
|
lhs
|
||
9 years ago
|
|
||
7 years ago
|
|
||
|
let node_join_ f_node_merge f_trace_merge ((trace1, tree1) as node1) ((trace2, tree2) as node2) =
|
||
8 years ago
|
if phys_equal node1 node2 then node1
|
||
9 years ago
|
else
|
||
|
let trace' = f_trace_merge trace1 trace2 in
|
||
|
(* note: this is much-uglified by address equality optimization checks. skip to the else cases
|
||
|
for the actual semantics *)
|
||
8 years ago
|
match (tree1, tree2) with
|
||
7 years ago
|
| Subtree subtree1, Subtree subtree2 ->
|
||
|
let tree' = AccessMap.merge (fun _ v1 v2 -> f_node_merge v1 v2) subtree1 subtree2 in
|
||
7 years ago
|
if AccessMap.cardinal tree' > Config.max_width then
|
||
|
(* too big; create a star insted *)
|
||
|
let trace'' = join_all_traces trace' (Subtree tree') in
|
||
|
(trace'', Star)
|
||
|
else if phys_equal trace' trace1 && phys_equal tree' subtree1 then node1
|
||
8 years ago
|
else if phys_equal trace' trace2 && phys_equal tree' subtree2 then node2
|
||
|
else (trace', Subtree tree')
|
||
7 years ago
|
| Star, t ->
|
||
|
(* vacuum up all the traces associated with the subtree t and join them with trace' *)
|
||
9 years ago
|
let trace'' = join_all_traces trace' t in
|
||
8 years ago
|
if phys_equal trace'' trace1 then node1 else (trace'', Star)
|
||
7 years ago
|
| t, Star ->
|
||
|
(* same as above, but kind-of duplicated to allow address equality optimization *)
|
||
9 years ago
|
let trace'' = join_all_traces trace' t in
|
||
8 years ago
|
if phys_equal trace'' trace2 then node2 else (trace'', Star)
|
||
9 years ago
|
|
||
7 years ago
|
|
||
8 years ago
|
let rec node_join node1 node2 = node_join_ node_merge TraceDomain.join node1 node2
|
||
8 years ago
|
|
||
|
and node_merge node1_opt node2_opt =
|
||
8 years ago
|
match (node1_opt, node2_opt) with
|
||
7 years ago
|
| Some node1, Some node2 ->
|
||
|
let joined_node = node_join node1 node2 in
|
||
8 years ago
|
if phys_equal joined_node node1 then node1_opt
|
||
|
else if phys_equal joined_node node2 then node2_opt
|
||
8 years ago
|
else Some joined_node
|
||
7 years ago
|
| None, node_opt | node_opt, None ->
|
||
|
node_opt
|
||
|
|
||
8 years ago
|
|
||
7 years ago
|
(* truncate [node] to a tree of depth <= [depth]. [depth] must be positive *)
|
||
7 years ago
|
let node_depth_truncate ((_, tree) as node) depth =
|
||
|
let rec node_depth_truncate_ depth ((trace, tree) as node) =
|
||
7 years ago
|
match tree with
|
||
7 years ago
|
| Star ->
|
||
|
node
|
||
|
| Subtree subtree ->
|
||
|
if Int.( <= ) depth 0 then
|
||
7 years ago
|
let trace' = join_all_traces trace tree in
|
||
|
make_starred_leaf trace'
|
||
|
else
|
||
|
let subtree' = AccessMap.map (node_depth_truncate_ (depth - 1)) subtree in
|
||
|
(trace, Subtree subtree')
|
||
|
in
|
||
|
if tree_depth tree > depth then node_depth_truncate_ depth node
|
||
|
else (* already short enough; don't bother truncating *)
|
||
|
node
|
||
|
|
||
7 years ago
|
|
||
8 years ago
|
(* helper for [add_access]. [last_trace] is the trace associated with [tree] in the parent. *)
|
||
|
let access_tree_add_trace ~node_to_add ~seen_array_access ~is_exact accesses node =
|
||
7 years ago
|
let rec access_tree_add_trace_ ~seen_array_access accesses node depth =
|
||
8 years ago
|
match (accesses, node) with
|
||
|
| [], (trace, tree) -> (
|
||
|
match (is_exact, seen_array_access) with
|
||
7 years ago
|
| true, false ->
|
||
|
(* adding x.f, do strong update on both subtree and its traces *)
|
||
7 years ago
|
node_depth_truncate node_to_add (Config.max_depth - depth)
|
||
7 years ago
|
| true, true ->
|
||
|
(* adding x[_], do weak update on subtree and on its immediate trace. note : [node]
|
||
7 years ago
|
already satisfies the depth invariant because it's already in the tree; no need to
|
||
|
truncate it. *)
|
||
|
let truncated_node = node_depth_truncate node_to_add (Config.max_depth - depth) in
|
||
|
node_join truncated_node node
|
||
7 years ago
|
| _ ->
|
||
|
(* adding x.f* or x[_]*, join with traces of subtree and replace it with * *)
|
||
8 years ago
|
let node_trace, node_tree = node_to_add in
|
||
|
let trace' = join_all_traces (TraceDomain.join trace node_trace) tree in
|
||
|
make_starred_leaf (join_all_traces trace' node_tree) )
|
||
7 years ago
|
| _, (_, Star) ->
|
||
|
node_join node_to_add node
|
||
|
| access :: accesses, (trace, Subtree subtree) ->
|
||
|
let depth' = depth + 1 in
|
||
7 years ago
|
let access_node' =
|
||
|
if depth' >= Config.max_depth then
|
||
|
access_tree_add_trace_ ~seen_array_access accesses empty_starred_leaf depth'
|
||
|
else
|
||
|
let access_node =
|
||
7 years ago
|
try AccessMap.find access subtree with Caml.Not_found -> empty_normal_leaf
|
||
7 years ago
|
in
|
||
|
(* once we encounter a subtree rooted in an array access, we have to do weak updates in
|
||
5 years ago
|
the entire subtree. the reason: if I do x[i].f.g = <interesting trace>, then
|
||
|
x[j].f.g = <empty trace>, I don't want to overwrite <interesting trace>. instead, I
|
||
|
should get <interesting trace> |_| <empty trace> *)
|
||
7 years ago
|
let seen_array_access =
|
||
|
seen_array_access
|
||
|
||
|
||
|
match access with
|
||
7 years ago
|
| AccessPath.ArrayAccess _ ->
|
||
|
true
|
||
|
| AccessPath.FieldAccess _ ->
|
||
|
false
|
||
7 years ago
|
in
|
||
|
access_tree_add_trace_ ~seen_array_access accesses access_node depth'
|
||
8 years ago
|
in
|
||
7 years ago
|
let access_map = AccessMap.add access access_node' subtree in
|
||
|
if AccessMap.cardinal access_map > Config.max_width then
|
||
|
(join_all_traces trace (Subtree access_map), Star)
|
||
|
else (trace, Subtree (AccessMap.add access access_node' subtree))
|
||
8 years ago
|
in
|
||
7 years ago
|
access_tree_add_trace_ ~seen_array_access accesses node 1
|
||
8 years ago
|
|
||
7 years ago
|
|
||
8 years ago
|
let add_node ap node_to_add tree =
|
||
7 years ago
|
let base, accesses = AccessPath.Abs.extract ap in
|
||
|
let is_exact = AccessPath.Abs.is_exact ap in
|
||
8 years ago
|
let base_node =
|
||
6 years ago
|
try BaseMap.find base tree
|
||
|
with Caml.Not_found ->
|
||
7 years ago
|
(* note: we interpret max_depth <= 0 as max_depth = 1 *)
|
||
|
if Config.max_depth > 1 then empty_normal_leaf else empty_starred_leaf
|
||
8 years ago
|
in
|
||
8 years ago
|
let base_node' =
|
||
8 years ago
|
access_tree_add_trace ~node_to_add ~seen_array_access:false ~is_exact accesses base_node
|
||
|
in
|
||
8 years ago
|
BaseMap.add base base_node' tree
|
||
|
|
||
7 years ago
|
|
||
8 years ago
|
let add_trace ap trace tree = add_node ap (make_normal_leaf trace) tree
|
||
8 years ago
|
|
||
9 years ago
|
let join tree1 tree2 =
|
||
8 years ago
|
if phys_equal tree1 tree2 then tree1
|
||
8 years ago
|
else BaseMap.merge (fun _ n1 n2 -> node_merge n1 n2) tree1 tree2
|
||
9 years ago
|
|
||
7 years ago
|
|
||
8 years ago
|
let rec access_map_fold_ f base accesses m acc =
|
||
|
AccessMap.fold (fun access node acc -> node_fold_ f base (accesses @ [access]) node acc) m acc
|
||
8 years ago
|
|
||
7 years ago
|
|
||
|
and node_fold_ f base accesses ((_, tree) as node) acc =
|
||
8 years ago
|
let cur_ap_raw = (base, accesses) in
|
||
8 years ago
|
match tree with
|
||
7 years ago
|
| Subtree access_map ->
|
||
|
let acc' = f acc (AccessPath.Abs.Exact cur_ap_raw) node in
|
||
8 years ago
|
access_map_fold_ f base accesses access_map acc'
|
||
7 years ago
|
| Star ->
|
||
|
f acc (AccessPath.Abs.Abstracted cur_ap_raw) node
|
||
|
|
||
8 years ago
|
|
||
6 years ago
|
let node_fold (f : 'a -> AccessPath.Abs.t -> node -> 'a) base node acc =
|
||
7 years ago
|
node_fold_ f base [] node acc
|
||
8 years ago
|
|
||
7 years ago
|
|
||
6 years ago
|
let fold (f : 'a -> AccessPath.Abs.t -> node -> 'a) tree acc_ =
|
||
8 years ago
|
BaseMap.fold (fun base node acc -> node_fold f base node acc) tree acc_
|
||
|
|
||
7 years ago
|
|
||
6 years ago
|
let trace_fold (f : 'a -> AccessPath.Abs.t -> TraceDomain.t -> 'a) =
|
||
8 years ago
|
let f_ acc ap (trace, _) = f acc ap trace in
|
||
8 years ago
|
fold f_
|
||
|
|
||
7 years ago
|
|
||
7 years ago
|
exception Found
|
||
|
|
||
6 years ago
|
let exists (f : AccessPath.Abs.t -> node -> bool) tree =
|
||
7 years ago
|
try
|
||
|
fold (fun _ access_path node -> if f access_path node then raise Found else false) tree false
|
||
|
with Found -> true
|
||
|
|
||
|
|
||
6 years ago
|
let iter (f : AccessPath.Abs.t -> node -> unit) tree =
|
||
7 years ago
|
fold (fun () access_path node -> f access_path node) tree ()
|
||
|
|
||
|
|
||
7 years ago
|
(* try for a bit to reach a fixed point before widening aggressively *)
|
||
|
let joins_before_widen = 3
|
||
8 years ago
|
|
||
6 years ago
|
let max_iter = 10
|
||
|
|
||
8 years ago
|
let widen ~prev ~next ~num_iters =
|
||
6 years ago
|
if phys_equal prev next || num_iters >= max_iter then prev
|
||
7 years ago
|
else if Int.( <= ) num_iters joins_before_widen then join prev next
|
||
8 years ago
|
else
|
||
8 years ago
|
let trace_widen prev next = TraceDomain.widen ~prev ~next ~num_iters in
|
||
7 years ago
|
(* turn [node] into a starred node by vacuuming up its sub-traces *)
|
||
7 years ago
|
let node_add_stars ((trace, tree) as node) =
|
||
7 years ago
|
match tree with
|
||
7 years ago
|
| Subtree _ ->
|
||
|
let trace' = join_all_traces ~join_traces:trace_widen trace tree in
|
||
7 years ago
|
make_starred_leaf trace'
|
||
7 years ago
|
| Star ->
|
||
|
node
|
||
7 years ago
|
in
|
||
8 years ago
|
let rec node_widen prev_node_opt next_node_opt =
|
||
8 years ago
|
match (prev_node_opt, next_node_opt) with
|
||
7 years ago
|
| Some prev_node, Some next_node ->
|
||
|
let widened_node = node_join_ node_widen trace_widen prev_node next_node in
|
||
8 years ago
|
if phys_equal widened_node prev_node then prev_node_opt
|
||
|
else if phys_equal widened_node next_node then next_node_opt
|
||
8 years ago
|
else Some widened_node
|
||
7 years ago
|
| None, Some next_node ->
|
||
|
let widened_node = node_add_stars next_node in
|
||
8 years ago
|
if phys_equal widened_node next_node then next_node_opt else Some widened_node
|
||
7 years ago
|
| Some _, None | None, None ->
|
||
|
prev_node_opt
|
||
8 years ago
|
in
|
||
8 years ago
|
BaseMap.merge (fun _ prev_node next_node -> node_widen prev_node next_node) prev next
|
||
|
|
||
7 years ago
|
|
||
8 years ago
|
let rec pp_node fmt (trace, subtree) =
|
||
7 years ago
|
let pp_subtree fmt tree =
|
||
|
match tree with
|
||
7 years ago
|
| Subtree access_map ->
|
||
|
AccessMap.pp ~pp_value:pp_node fmt access_map
|
||
|
| Star ->
|
||
7 years ago
|
F.pp_print_char fmt '*'
|
||
8 years ago
|
in
|
||
6 years ago
|
if not (TraceDomain.is_bottom trace) then
|
||
7 years ago
|
if not (is_empty_tree subtree) then
|
||
|
F.fprintf fmt "(%a, %a)" TraceDomain.pp trace pp_subtree subtree
|
||
7 years ago
|
else TraceDomain.pp fmt trace
|
||
|
else pp_subtree fmt subtree
|
||
8 years ago
|
|
||
7 years ago
|
|
||
8 years ago
|
let pp fmt base_tree = BaseMap.pp ~pp_value:pp_node fmt base_tree
|
||
9 years ago
|
end
|
||
7 years ago
|
|
||
7 years ago
|
module PathSet (Config : Config) = struct
|
||
|
include Make (AbstractDomain.BooleanOr) (Config)
|
||
7 years ago
|
|
||
7 years ago
|
let mem access_path tree =
|
||
|
match get_node access_path tree with None -> false | Some (is_mem, _) -> is_mem
|
||
|
|
||
7 years ago
|
|
||
7 years ago
|
(* print as a set of paths rather than a map of paths to bools *)
|
||
|
let pp fmt tree =
|
||
|
let collect_path acc access_path (is_mem, _) = if is_mem then access_path :: acc else acc in
|
||
|
fold collect_path tree [] |> PrettyPrintable.pp_collection ~pp_item:AccessPath.Abs.pp fmt
|
||
|
end
|