(* * Copyright (c) 2018-present, Facebook, Inc. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. *) open! IStd module F = Format module L = Logging module Reverse = struct module Dead = AbstractDomain.BooleanAnd module VarSet = AbstractDomain.FiniteSet (Var) module DeadAndVarSet = AbstractDomain.Pair (Dead) (VarSet) module M = AbstractDomain.Map (Var) (DeadAndVarSet) (* invariant: sets are not empty *) type t = M.t let empty = M.empty let add k v rm = M.update k (function | None -> Some (false, VarSet.singleton v) | Some (_, s) -> Some (false, VarSet.add v s)) rm let mem = M.mem let deadify k rm = M.update k (function (None | Some (true, _)) as vo -> vo | Some (false, s) -> Some (true, s)) rm let remove k v rm = match M.find_opt k rm with | None -> (rm, false) | Some (dead, s) -> let s = VarSet.remove v s in if VarSet.is_empty s then (M.remove k rm, dead) else (M.add k (dead, s) rm, false) let pp = M.pp let join = M.join let widen = M.widen end type t = { resolve: IdAccessPathMapDomain.t ; reverse: Reverse.t (** there is a [x -> y] mapping in reverse for each variable [y] appearing in [resolve x] *) } let empty = {resolve= IdAccessPathMapDomain.empty; reverse= Reverse.empty} let add id ap {resolve; reverse} = if (Config.debug_exceptions || Config.write_html) && Reverse.mem id reverse then (if Config.debug_exceptions then L.(die InternalError) else L.d_printfln ?color:None) "Variable %a appearing on both sides of bindings" Var.pp id ; let resolve = IdAccessPathMapDomain.add id ap resolve in let reverse = HilExp.AccessExpression.fold_vars ap ~init:reverse ~f:(fun acc var_in_ap -> Reverse.add var_in_ap id acc ) in {resolve; reverse} let exit_scope id bindings = let {resolve; reverse} = bindings in match IdAccessPathMapDomain.find_opt id resolve with | None -> if Reverse.mem id reverse then let reverse = Reverse.deadify id reverse in ({resolve; reverse}, []) else (bindings, [id]) | Some ap -> let resolve = IdAccessPathMapDomain.remove id resolve in let reverse, vars = HilExp.AccessExpression.fold_vars ap ~init:(reverse, []) ~f:(fun (reverse, vars) var_in_ap -> let reverse, dead = Reverse.remove var_in_ap id reverse in let vars = if dead then var_in_ap :: vars else vars in (reverse, vars) ) in ({resolve; reverse}, vars) let resolve {resolve} id = IdAccessPathMapDomain.find_opt id resolve let fold {resolve} ~init ~f = IdAccessPathMapDomain.fold f resolve init let pp f {resolve; reverse} = F.fprintf f "{@[ resolve=@[%a@];@;reverse=@[%a@]@]}" IdAccessPathMapDomain.pp resolve Reverse.pp reverse let ( <= ) ~lhs ~rhs = IdAccessPathMapDomain.( <= ) ~lhs:lhs.resolve ~rhs:rhs.resolve let join bindings1 bindings2 = if phys_equal bindings1 bindings2 then bindings1 else let {resolve= resolve1; reverse= reverse1} = bindings1 in let {resolve= resolve2; reverse= reverse2} = bindings2 in {resolve= IdAccessPathMapDomain.join resolve1 resolve2; reverse= Reverse.join reverse1 reverse2} let widen ~prev ~next ~num_iters = if phys_equal prev next then prev else let {resolve= resolve1; reverse= reverse1} = prev in let {resolve= resolve2; reverse= reverse2} = next in { resolve= IdAccessPathMapDomain.widen ~prev:resolve1 ~next:resolve2 ~num_iters ; reverse= Reverse.widen ~prev:reverse1 ~next:reverse2 ~num_iters }