source/domains/abstractSetDomain.ml (156 lines of code) (raw):

(* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open AbstractDomainCore module type ELEMENT = sig type t [@@deriving show] val name : string val compare : t -> t -> int end module type SET = sig type t type element val empty : t val is_empty : t -> bool val singleton : element -> t val add : element -> t -> t val remove : element -> t -> t val mem : element -> t -> bool val union : t -> t -> t val inter : t -> t -> t val subset : t -> t -> bool val diff : t -> t -> t val map : (element -> element) -> t -> t val filter : (element -> bool) -> t -> t val fold : (element -> 'a -> 'a) -> t -> 'a -> 'a val exists : (element -> bool) -> t -> bool val of_list : element list -> t val elements : t -> element list val show_element : element -> string val element_name : string end module type S = sig include AbstractDomainCore.S type element type _ AbstractDomainCore.part += Element : element AbstractDomainCore.part val add : element -> t -> t val remove : element -> t -> t val contains : element -> t -> bool val singleton : element -> t val elements : t -> element list val of_list : element list -> t end module MakeWithSet (Set : SET) = struct module rec Base : (BASE with type t := Set.t) = MakeBase (Domain) and Domain : (S with type t = Set.t and type element = Set.element) = struct include Set type element = Set.element type _ part += Self : t part | Element : Set.element part let bottom = Set.empty let is_bottom = Set.is_empty let contains = Set.mem let join left right = if left == right then left else Set.union left right let meet left right = if left == right then left else Set.inter left right let widen ~iteration:_ ~prev ~next = join prev next let less_or_equal ~left ~right = if left == right || is_bottom left then true else if is_bottom right then false else Set.subset left right let subtract to_remove ~from = if to_remove == from || is_bottom from then bottom else if is_bottom to_remove then from else Set.diff from to_remove let show set = Set.elements set |> ListLabels.map ~f:Set.show_element |> String.concat ", " |> Format.sprintf "[%s]" let pp formatter set = Format.fprintf formatter "%s" (show set) let transform : type a f. a part -> ([ `Transform ], a, f, _) operation -> f:f -> t -> t = fun part op ~f set -> match part, op with | Element, Map -> Set.map f set | Element, Add -> Set.add f set | Element, Filter -> Set.filter f set | _ -> Base.transform part op ~f set let reduce : type a f b. a part -> using:([ `Reduce ], a, f, b) operation -> f:f -> init:b -> t -> b = fun part ~using:op ~f ~init set -> match part, op with | Element, Acc -> Set.fold f set init | Element, Exists -> init || Set.exists f set | _ -> Base.reduce part ~using:op ~f ~init set let partition : type a f b. a part -> ([ `Partition ], a, f, b) operation -> f:f -> t -> (b, t) Core_kernel.Map.Poly.t = fun part op ~f set -> let update element = function | None -> Set.singleton element | Some set -> Set.add element set in match part, op with | Element, By -> let f element result = Core_kernel.Map.Poly.update result (f element) ~f:(update element) in Set.fold f set Core_kernel.Map.Poly.empty | Element, ByFilter -> let f element result = match f element with | None -> result | Some key -> Core_kernel.Map.Poly.update result key ~f:(update element) in Set.fold f set Core_kernel.Map.Poly.empty | _ -> Base.partition part op ~f set let introspect (type a) (op : a introspect) : a = match op with | GetParts f -> f#report Self; f#report Element | Structure -> [Format.sprintf "Set(%s)" Set.element_name] | Name part -> ( match part with | Element -> Format.sprintf "Set(%s).Element" Set.element_name | Self -> Format.sprintf "Set(%s).Self" Set.element_name | _ -> Base.introspect op) let create parts = let create_part so_far (Part (part, value)) = match part with | Element -> Set.add value so_far | _ -> Base.create part value so_far in ListLabels.fold_left parts ~f:create_part ~init:bottom let fold = Base.fold let apply = Base.apply end let _ = Base.fold (* unused module warning work-around *) include Domain end module Make (Element : ELEMENT) = struct module Set = struct include Set.Make (Element) type element = Element.t let show_element = Element.show let element_name = Element.name end include MakeWithSet (Set) end