summaryrefslogblamecommitdiff
path: root/abstract/abs_domain.ml
blob: 64eefc785d9f02106a556ecfaeb9185432784e4e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11



               
         
 




                      
















                                                                                
                                                           











                                                            
                                                    







                                                              
 

                                                                               


                     
 










                                                                                    
                              







                          
                                                            



                              




                                                        



                                                  
                                                                               



                                  
































































































































                                                                                                                
















                                                                                   


































































































































                                                                                     
                                   





















                                                             

   
open Ast
open Formula
open Num_domain
open Typing
open Util

type disjunct_policy =
  | DAlways
  | DNever
  | DSometimes

module type ENVIRONMENT_DOMAIN = sig
    type t
    type itv

    (* construction *)
    val init              : t
    val bottom            : t
    val vbottom           : t -> t      (* bottom value with same environment *)
    val is_bot            : t -> bool

    (* variable management *)
    val addvar            : t -> id -> Typing.typ -> t
    val rmvar             : t -> id -> t
    val vars              : t -> (id * Typing.typ) list

    val forgetvar         : t -> id -> t
    val var_itv           : t -> id -> itv
    val set_disjunct      : t -> id -> disjunct_policy -> t

    (* set_theoretic operations *)
    val join              : t -> t -> t   (* union *)
    val meet              : t -> t -> t   (* intersection *)
    val widen             : t -> t -> t

    val subset            : t -> t -> bool
    val eq                : t -> t -> bool

    (* abstract operations *)
    val apply_f           : t -> bool_expr -> t
    val apply_cl          : t -> conslist -> t
    val assign            : t -> (id * id) list -> t

    (* pretty-printing *)
    val print_vars  : Format.formatter -> t -> id list -> unit
    val print_all   : Format.formatter -> t -> unit
    val print_itv   : Format.formatter -> itv -> unit

end


module D_ignore_enum (ND : NUMERICAL_ENVIRONMENT_DOMAIN) : ENVIRONMENT_DOMAIN =
struct
    type t = ND.t
    type itv = ND.itv


    let init = ND.init
    let bottom = ND.bottom
    let vbottom = ND.vbottom
    let is_bot = ND.is_bot

    let addvar x id ty = ND.addvar x id (ty = TReal)
    let rmvar = ND.rmvar
    let vars x = List.map (fun (id, r) -> id, if r then TReal else TInt) (ND.vars x)

    let forgetvar = ND.forgetvar
    let var_itv = ND.var_itv
    let set_disjunct x _ _ = x

    let join = ND.join
    let meet = ND.meet
    let widen = ND.widen

    let subset = ND.subset
    let eq = ND.eq

    let rec apply_cl x (econs, cons, rest) = match rest with
        | CLTrue ->
          ND.apply_ncl x cons
        | CLFalse -> vbottom x
        | CLAnd(a, b) ->
          let y = apply_cl x (econs, cons, a) in
          apply_cl y (econs, cons, b)
        | CLOr((eca, ca, ra), (ecb, cb, rb)) ->
          let y = apply_cl x (econs@eca, cons@ca, ra) in
          let z = apply_cl x (econs@ecb, cons@cb, rb) in
          join y z

    let apply_f x f = apply_cl x (conslist_of_f f)

    let assign x v = ND.assign x (List.map (fun (id, id2) -> id, NIdent id2) v)

    let print_vars = ND.print_vars
    let print_all = ND.print_all
    let print_itv = ND.print_itv
end

module D (ND : NUMERICAL_ENVIRONMENT_DOMAIN) : ENVIRONMENT_DOMAIN = struct

    module Valuation = struct
      type t = string VarMap.t
      let compare = VarMap.compare Pervasives.compare

      let subset a b =
          VarMap.for_all
            (fun id v ->
                try v = VarMap.find id b
                with Not_found -> true)
            a

      let print fmt x =
          let b = List.map (fun (x, y) -> y, x) (VarMap.bindings x) in
          let s = List.sort Pervasives.compare b in
          let rec bl = function
            | [] -> []
            | (v, id)::q ->
              begin match bl q with
              | (vv, ids)::q when vv = v -> (v, id::ids)::q
              | r -> (v, [id])::r
              end
          in
          let sbl = bl s in
          Format.fprintf fmt "@[<v 2>{ ";
          List.iteri
            (fun j (v, ids) ->
              if j > 0 then Format.fprintf fmt "@ ";
              Format.fprintf fmt "@[<hov 4>";
              List.iteri
                (fun i id ->
                  if i > 0 then Format.fprintf fmt ",@ ";
                  Format.fprintf fmt "%a" Formula_printer.print_id id)
                ids;
              Format.fprintf fmt " = %s@]" v)
            sbl;
          Format.fprintf fmt " }@]"

    end
    module VMap = Mapext.Make(Valuation)

    type t = {
        nvars     : (id * bool) list;
        evars     : (id * string list) list;
        nd_init   : ND.t;
        nd_bottom : ND.t;
        value     : ND.t VMap.t;
        disj_v    : id list;
        nodisj_v  : id list;
    }
    type itv =
      | IN of ND.itv VMap.t
      | IE of SSet.t


    let init = {
        nvars = []; evars = []; disj_v = []; nodisj_v = [];
        nd_init = ND.init;
        nd_bottom = ND.bottom;
        value = VMap.singleton VarMap.empty ND.init;
      }
    let bottom = {
        nvars = []; evars = []; disj_v = []; nodisj_v = [];
        nd_init = ND.init;
        nd_bottom = ND.bottom;
        value = VMap.empty;
      }
    let vbottom x = { x with value = VMap.empty }
    
    let is_bot x = VMap.is_empty x.value || VMap.for_all (fun _ v -> ND.is_bot v) x.value

    let strict value =
        VMap.filter (fun _ v -> not (ND.is_bot v)) value

    let add_case x (enum, num) =
        let value = x.value in
        let enum = VarMap.filter (fun k v -> not (List.mem k x.nodisj_v)) enum in
        let v = 
          if VMap.exists (fun e0 n0 -> Valuation.subset enum e0 && ND.subset num n0) value || ND.is_bot num then
            value
          else
            try VMap.add enum (ND.join num (VMap.find enum value)) value
            with Not_found -> VMap.add enum num value
        in { x with value = v }

    let addvar x id ty = match ty with
        | TInt | TReal ->
          let is_real = (ty = TReal) in
          { x with
            nvars     = (id, is_real)::(x.nvars);
            nd_init   = ND.addvar x.nd_init id is_real;
            nd_bottom = ND.addvar x.nd_bottom id is_real;
            value     = VMap.map (fun v -> ND.addvar v id is_real) x.value
          }
        | TEnum options ->
          { x with
            evars     = (id, options)::x.evars
          }

    let vars x =
        List.map (fun (id, r) -> id, if r then TReal else TInt) x.nvars @
        List.map (fun (id, o) -> id, TEnum o) x.evars

    let forgetvar x id =
        if List.mem_assoc id x.evars then
          VMap.fold
              (fun enum num value ->
                let enum' = VarMap.remove id enum in
                add_case value (enum', num))
              x.value
              { x with value = VMap.empty }
        else
          { x with value = strict @@ VMap.map (fun v -> ND.forgetvar v id) x.value }

    let rmvar x id =
        if List.mem_assoc id x.evars then
          let y = forgetvar x id in
          { y with
            evars = List.remove_assoc id x.evars }
        else
          { x with
            nvars = List.remove_assoc id x.nvars;
            value = strict @@ VMap.map (fun v -> ND.rmvar v id) x.value }

    let var_itv x id =
        if List.mem_assoc id x.nvars
          then
            let x = List.fold_left
              (fun x (v, _) ->
                if v.[0] = 'N' || (String.length v > 3 && String.sub v 0 3 = "pre")
                  then forgetvar x v else x)
              x x.evars in
            IN (VMap.map (fun v -> ND.var_itv v id) x.value)
          else
            let all = List.fold_left (fun a b -> SSet.add b a) SSet.empty
                (List.assoc id x.evars) in
            IE
              (VMap.fold
                (fun enum _ s ->
                    try SSet.add (VarMap.find id enum) s
                    with Not_found -> all)
                x.value
                SSet.empty)

    let set_disjunct x id p =
        { x with
          disj_v = (if p = DAlways then id::x.disj_v
                      else List.filter ((<>) id) x.disj_v);
          nodisj_v = (if p = DNever then id::x.nodisj_v
                      else List.filter ((<>) id) x.nodisj_v);
        }

    let join x y =
        VMap.fold
          (fun enum num value -> add_case value (enum, num))
          x.value
          y
    let meet x y = not_implemented "meet for enum+num domain"
    let widen x y =
        { x with
          value = VMap.merge
            (fun _ a b -> match a, b with
              | None, Some a -> Some a
              | Some a, None -> Some a
              | Some a, Some b -> Some (ND.widen a b)
              | _ -> assert false)
            x.value y.value }

    (* Some cases of subset/equality not detected *)
    let subset a b =
        VMap.for_all
          (fun enum num ->
              VMap.exists
                (fun enum_b num_b ->
                  Valuation.subset enum enum_b && ND.subset num num_b)
                b.value)
          a.value
    let eq a b = subset a b && subset b a

    (* Constraints ! *)
    let apply_ec x (op, a, b) =
        VMap.fold
            (fun enum num value ->
              match a, b with
              | EItem x, EItem y ->
                  if (x = y && op = E_EQ) || (x <> y && op = E_NE)
                    then add_case value (enum, num)
                    else value
              | EItem u, EIdent i | EIdent i, EItem u ->
                  begin try let y = VarMap.find i enum in
                    if (u = y && op = E_EQ) || (u <> y && op = E_NE)
                      then add_case value (enum, num)
                      else value
                  with Not_found ->
                    if op = E_EQ
                      then add_case value (VarMap.add i u enum, num)
                      else (* MAKE A DISJUNCTION *)
                        List.fold_left
                          (fun value item ->
                              add_case value (VarMap.add i item enum, num))
                          value
                          (List.filter ((<>) u) (List.assoc i x.evars))
                  end
              | EIdent i, EIdent j ->
                  begin
                    try let x = VarMap.find i enum in
                      try let y = VarMap.find j enum in
                        if (x = y && op = E_EQ) || (x <> y && op = E_NE)
                          then add_case value (enum, num)
                          else value
                      with Not_found (* J not found *) ->
                        add_case value (VarMap.add j x enum, num)
                    with Not_found (* I not found *) ->
                      try let y = VarMap.find j enum in
                        add_case value (VarMap.add i y enum, num)
                      with Not_found (* J not found *) ->
                        if op = E_EQ
                        then (* MAKE A DISJUNCTION ! *)
                            List.fold_left
                              (fun value item ->
                                let enum = VarMap.add i item enum in
                                let enum = VarMap.add j item enum in
                                add_case value (enum, num))
                              value
                              (List.assoc i x.evars)
                        else
                          add_case value (enum, num)
                  end)
            x.value
            { x with value = VMap.empty }

    let apply_ecl x cons =
        let f x = List.fold_left apply_ec x cons in
        fix eq f x
            

    let apply_ncl x econs =
        { x with value = strict @@ VMap.map (fun x -> ND.apply_ncl x econs) x.value }

    let rec apply_cl x (econs, ncons, rest) = match rest with
        | CLTrue ->
          let y = apply_ecl x econs in
          let z = apply_ncl y ncons in
          z
        | CLFalse -> vbottom x
        | CLAnd(a, b) ->
          let y = apply_cl x (econs, ncons, a) in
          apply_cl y (econs, ncons, b)
        | CLOr((eca, ca, ra), (ecb, cb, rb)) ->
          let y = apply_cl x (econs@eca, ncons@ca, ra) in
          let z = apply_cl x (econs@ecb, ncons@cb, rb) in
          join y z

    let apply_f x f = apply_cl x (conslist_of_f f)

    let assign x e =
      let ee, ne = List.partition (fun (id, id2) -> List.mem_assoc id x.evars) e in
      let ne_e = List.map (fun (id, id2) -> id, NIdent id2) ne in

      VMap.fold
          (fun enum0 num value ->
              let enum = List.fold_left
                  (fun enum (id, id2) ->
                      try let y = VarMap.find id2 enum0 in
                        VarMap.add id y enum
                      with Not_found ->
                        VarMap.remove id enum)
                  enum0 ee
              in
              add_case value (enum, ND.assign num ne_e))
          x.value
          { x with value = VMap.empty }

    let print_all fmt x =
        Format.fprintf fmt "@[<v>";
        VMap.iter
          (fun enum num ->
            Format.fprintf fmt "@[<hv 2>{ %a ∧@ %a }@]@ "
              Valuation.print enum ND.print_all num)
          x.value;
        Format.fprintf fmt "@]"

    let print_vars fmt x idl = print_all fmt x (* TODO *)

    let print_itv fmt = function
        | IN x ->
          Format.fprintf fmt "@[<v 0>";
          VMap.iter
            (fun enum i ->
              Format.fprintf fmt "%a when %a@ "
                ND.print_itv i Valuation.print enum)
            x;
          Format.fprintf fmt "@]"
        | IE x ->
          Format.fprintf fmt "@[<hov 2>{ ";
          SSet.iter (fun x -> Format.fprintf fmt "%s@ " x) x;
          Format.fprintf fmt "}@]"

end