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.
195 lines
5.4 KiB
195 lines
5.4 KiB
(*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* 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 Raw = struct
|
|
type typ_ = Typ.t
|
|
|
|
let compare_typ_ _ _ = 0
|
|
|
|
(* ignore types while comparing bases. we can't trust the types from all of our frontends to be
|
|
consistent, and the variable names should already be enough to distinguish the bases. *)
|
|
type base = Var.t * typ_ [@@deriving compare]
|
|
|
|
let equal_base = [%compare.equal: base]
|
|
|
|
type access = ArrayAccess of typ_ * t list | FieldAccess of Fieldname.t
|
|
|
|
and t = base * access list [@@deriving compare]
|
|
|
|
let equal_access = [%compare.equal: access]
|
|
|
|
let may_pp_typ fmt typ =
|
|
if Config.debug_level_analysis >= 3 then F.fprintf fmt ":%a" (Typ.pp Pp.text) typ
|
|
|
|
|
|
let pp_base fmt (pvar, typ) =
|
|
Var.pp fmt pvar ;
|
|
may_pp_typ fmt typ
|
|
|
|
|
|
let rec pp_access fmt = function
|
|
| FieldAccess field_name ->
|
|
F.pp_print_string fmt (Fieldname.get_field_name field_name)
|
|
| ArrayAccess (typ, []) ->
|
|
F.pp_print_string fmt "[_]" ;
|
|
may_pp_typ fmt typ
|
|
| ArrayAccess (typ, index_aps) ->
|
|
F.fprintf fmt "[%a]" (PrettyPrintable.pp_collection ~pp_item:pp) index_aps ;
|
|
may_pp_typ fmt typ
|
|
|
|
|
|
and pp_access_list fmt accesses =
|
|
let pp_sep fmt () = F.pp_print_char fmt '.' in
|
|
F.pp_print_list ~pp_sep pp_access fmt accesses
|
|
|
|
|
|
and pp fmt = function
|
|
| base, [] ->
|
|
pp_base fmt base
|
|
| base, accesses ->
|
|
F.fprintf fmt "%a.%a" pp_base base pp_access_list accesses
|
|
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
let lookup_field_type_annot tenv base_typ field_name =
|
|
let lookup = Tenv.lookup tenv in
|
|
Struct.get_field_type_and_annotation ~lookup field_name base_typ
|
|
|
|
|
|
(* Get the type of an access, or None if the type cannot be determined *)
|
|
let get_access_type tenv base_typ = function
|
|
| FieldAccess field_name ->
|
|
Option.map (lookup_field_type_annot tenv base_typ field_name) ~f:fst
|
|
| ArrayAccess (array_typ, _) ->
|
|
Some array_typ
|
|
|
|
|
|
(* Extract the last access of the given access path together with its base type.
|
|
* Here the base type is defined to be the declaring class of the last accessed field,
|
|
* or the type of base if the access list is empty.
|
|
* For example:
|
|
* - for x.f.g, the base type of the last access is typ(f);
|
|
* - for x.f[][], the base type of the last access is typ(x);
|
|
* - for x, the base type of the last access is type(x) *)
|
|
let last_access_info ((_, base_typ), accesses) tenv =
|
|
let rec last_access_info_impl tenv base_typ = function
|
|
| [] ->
|
|
(Some base_typ, None)
|
|
| [last_access] ->
|
|
(Some base_typ, Some last_access)
|
|
| curr_access :: rest -> (
|
|
match get_access_type tenv base_typ curr_access with
|
|
| Some access_typ ->
|
|
last_access_info_impl tenv access_typ rest
|
|
| None ->
|
|
(None, None) )
|
|
in
|
|
last_access_info_impl tenv base_typ accesses
|
|
|
|
|
|
let get_typ ap tenv =
|
|
match last_access_info ap tenv with
|
|
| (Some _ as typ), None ->
|
|
typ
|
|
| Some base_typ, Some access ->
|
|
get_access_type tenv base_typ access
|
|
| _ ->
|
|
None
|
|
|
|
|
|
let base_of_pvar pvar typ = (Var.of_pvar pvar, typ)
|
|
|
|
let base_of_id id typ = (Var.of_id id, typ)
|
|
|
|
let of_pvar pvar typ = (base_of_pvar pvar typ, [])
|
|
|
|
let of_id id typ = (base_of_id id typ, [])
|
|
|
|
let of_var var typ =
|
|
match var with Var.LogicalVar id -> of_id id typ | Var.ProgramVar pvar -> of_pvar pvar typ
|
|
|
|
|
|
let append (base, old_accesses) new_accesses = (base, old_accesses @ new_accesses)
|
|
|
|
let rec chop_prefix_path ~prefix:path1 path2 =
|
|
if phys_equal path1 path2 then Some []
|
|
else
|
|
match (path1, path2) with
|
|
| [], remaining ->
|
|
Some remaining
|
|
| _, [] ->
|
|
None
|
|
| access1 :: prefix, access2 :: rest when equal_access access1 access2 ->
|
|
chop_prefix_path ~prefix rest
|
|
| _ ->
|
|
None
|
|
|
|
|
|
let chop_prefix ~prefix:((base1, path1) as ap1) ((base2, path2) as ap2) =
|
|
if phys_equal ap1 ap2 then Some []
|
|
else if equal_base base1 base2 then chop_prefix_path ~prefix:path1 path2
|
|
else None
|
|
|
|
|
|
let replace_prefix ~prefix ~replace_with access_path =
|
|
match chop_prefix ~prefix access_path with
|
|
| Some remaining_accesses ->
|
|
Some (append replace_with remaining_accesses)
|
|
| None ->
|
|
None
|
|
end
|
|
|
|
module Abs = struct
|
|
type raw = Raw.t
|
|
|
|
type t = Abstracted of Raw.t | Exact of Raw.t [@@deriving compare]
|
|
|
|
let equal = [%compare.equal: t]
|
|
|
|
let extract = function Exact ap | Abstracted ap -> ap
|
|
|
|
let with_base base = function
|
|
| Exact (_, accesses) ->
|
|
Exact (base, accesses)
|
|
| Abstracted (_, accesses) ->
|
|
Abstracted (base, accesses)
|
|
|
|
|
|
let to_footprint formal_index access_path =
|
|
let _, base_typ = fst (extract access_path) in
|
|
with_base (Var.of_formal_index formal_index, base_typ) access_path
|
|
|
|
|
|
let get_footprint_index_base base =
|
|
match base with
|
|
| Var.LogicalVar id, _ when Ident.is_footprint id ->
|
|
Some (Ident.get_stamp id)
|
|
| _ ->
|
|
None
|
|
|
|
|
|
let is_exact = function Exact _ -> true | Abstracted _ -> false
|
|
|
|
let pp fmt = function
|
|
| Exact access_path ->
|
|
Raw.pp fmt access_path
|
|
| Abstracted access_path ->
|
|
F.fprintf fmt "%a*" Raw.pp access_path
|
|
end
|
|
|
|
include Raw
|
|
|
|
module BaseMap = PrettyPrintable.MakePPMap (struct
|
|
type t = base [@@deriving compare]
|
|
|
|
let pp = pp_base
|
|
end)
|