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

(*
* 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)