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.

1287 lines
56 KiB

(*
* Copyright (c) 2009 - 2013 Monoidics ltd.
* Copyright (c) 2013 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! Utils
(** Implementation of Abstraction Functions *)
module L = Logging
module F = Format
(** {2 Abstraction} *)
type rule =
{ r_vars: Ident.t list;
r_root: Match.hpred_pat;
r_sigma: Match.hpred_pat list; (* sigma should be in a specific order *)
r_new_sigma: Sil.hpred list;
r_new_pi: Prop.normal Prop.t -> Prop.normal Prop.t -> Sil.subst -> Sil.atom list;
r_condition: Prop.normal Prop.t -> Sil.subst -> bool }
let sigma_rewrite p r : Prop.normal Prop.t option =
match (Match.prop_match_with_impl p r.r_condition r.r_vars r.r_root r.r_sigma) with
| None -> None
| Some(sub, p_leftover) ->
if not (r.r_condition p_leftover sub) then None
else
let res_pi = r.r_new_pi p p_leftover sub in
let res_sigma = Prop.sigma_sub sub r.r_new_sigma in
let p_with_res_pi = IList.fold_left Prop.prop_atom_and p_leftover res_pi in
let p_new = Prop.prop_sigma_star p_with_res_pi res_sigma in
Some (Prop.normalize p_new)
let sigma_fav_list sigma =
Sil.fav_to_list (Prop.sigma_fav sigma)
let sigma_fav_in_pvars =
Sil.fav_imperative_to_functional Prop.sigma_fav_in_pvars_add
let sigma_fav_in_pvars_list sigma =
Sil.fav_to_list (sigma_fav_in_pvars sigma)
(******************** Start of SLL abstraction rules *****************)
let create_fresh_primeds_ls para =
let id_base = Ident.create_fresh Ident.kprimed in
let id_next = Ident.create_fresh Ident.kprimed in
let id_end = Ident.create_fresh Ident.kprimed in
let ids_shared =
let svars = para.Sil.svars in
let f _ = Ident.create_fresh Ident.kprimed in
IList.map f svars in
let ids_tuple = (id_base, id_next, id_end, ids_shared) in
let exp_base = Sil.Var id_base in
let exp_next = Sil.Var id_next in
let exp_end = Sil.Var id_end in
let exps_shared = IList.map (fun id -> Sil.Var id) ids_shared in
let exps_tuple = (exp_base, exp_next, exp_end, exps_shared) in
(ids_tuple, exps_tuple)
let create_condition_ls ids_private id_base p_leftover (inst: Sil.subst) =
let (insts_of_private_ids, insts_of_public_ids, inst_of_base) =
let f id' = IList.exists (fun id'' -> Ident.equal id' id'') ids_private in
let (inst_private, inst_public) = Sil.sub_domain_partition f inst in
let insts_of_public_ids = Sil.sub_range inst_public in
let inst_of_base = try Sil.sub_find (Ident.equal id_base) inst_public with Not_found -> assert false in
let insts_of_private_ids = Sil.sub_range inst_private in
(insts_of_private_ids, insts_of_public_ids, inst_of_base) in
let fav_insts_of_public_ids = IList.flatten (IList.map Sil.exp_fav_list insts_of_public_ids) in
let fav_insts_of_private_ids = IList.flatten (IList.map Sil.exp_fav_list insts_of_private_ids) in
let (fav_p_leftover, _) =
let sigma = Prop.get_sigma p_leftover in
(sigma_fav_list sigma, sigma_fav_in_pvars_list sigma) in
let fpv_inst_of_base = Sil.exp_fpv inst_of_base in
let fpv_insts_of_private_ids = IList.flatten (IList.map Sil.exp_fpv insts_of_private_ids) in
(*
let fav_inst_of_base = Sil.exp_fav_list inst_of_base in
L.out "@[.... application of condition ....@\n@.";
L.out "@[<4> private ids : %a@\n@." pp_exp_list insts_of_private_ids;
L.out "@[<4> public ids : %a@\n@." pp_exp_list insts_of_public_ids;
*)
(* (not (IList.intersect compare fav_inst_of_base fav_in_pvars)) && *)
(fpv_inst_of_base = []) &&
(fpv_insts_of_private_ids = []) &&
(not (IList.exists Ident.is_normal fav_insts_of_private_ids)) &&
(not (IList.intersect Ident.compare fav_insts_of_private_ids fav_p_leftover)) &&
(not (IList.intersect Ident.compare fav_insts_of_private_ids fav_insts_of_public_ids))
let mk_rule_ptspts_ls impl_ok1 impl_ok2 (para: Sil.hpara) =
let (ids_tuple, exps_tuple) = create_fresh_primeds_ls para in
let (id_base, id_next, id_end, ids_shared) = ids_tuple in
let (exp_base, exp_next, exp_end, exps_shared) = exps_tuple in
let (ids_exist_fst, para_fst) = Sil.hpara_instantiate para exp_base exp_next exps_shared in
let (para_fst_start, para_fst_rest) =
let mark_impl_flag hpred = { Match.hpred = hpred; Match.flag = impl_ok1 } in
match para_fst with
| [] -> L.out "@.@.ERROR (Empty Para): %a @.@." (Sil.pp_hpara pe_text) para; assert false
| hpred :: hpreds ->
let hpat = mark_impl_flag hpred in
let hpats = IList.map mark_impl_flag hpreds in
(hpat, hpats) in
let (ids_exist_snd, para_snd) =
let mark_impl_flag hpred = { Match.hpred = hpred; Match.flag = impl_ok2 } in
let (ids, para_body) = Sil.hpara_instantiate para exp_next exp_end exps_shared in
let para_body_hpats = IList.map mark_impl_flag para_body in
(ids, para_body_hpats) in
let lseg_res = Prop.mk_lseg Sil.Lseg_NE para exp_base exp_end exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = id_next :: (ids_exist_fst @ ids_exist_snd) in
create_condition_ls ids_private id_base in
{ r_vars = id_base :: id_next :: id_end :: ids_shared @ ids_exist_fst @ ids_exist_snd;
r_root = para_fst_start;
r_sigma = para_fst_rest @ para_snd;
r_new_sigma = [lseg_res];
r_new_pi = gen_pi_res;
r_condition = condition }
let mk_rule_ptsls_ls k2 impl_ok1 impl_ok2 para =
let (ids_tuple, exps_tuple) = create_fresh_primeds_ls para in
let (id_base, id_next, id_end, ids_shared) = ids_tuple in
let (exp_base, exp_next, exp_end, exps_shared) = exps_tuple in
let (ids_exist, para_inst) = Sil.hpara_instantiate para exp_base exp_next exps_shared in
let (para_inst_start, para_inst_rest) =
match para_inst with
| [] -> L.out "@.@.ERROR (Empty Para): %a @.@." (Sil.pp_hpara pe_text) para; assert false
| hpred :: hpreds ->
let allow_impl hpred = { Match.hpred = hpred; Match.flag = impl_ok1 } in
(allow_impl hpred, IList.map allow_impl hpreds) in
let lseg_pat = { Match.hpred = Prop.mk_lseg k2 para exp_next exp_end exps_shared; Match.flag = impl_ok2 } in
let lseg_res = Prop.mk_lseg Sil.Lseg_NE para exp_base exp_end exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = id_next :: ids_exist in
create_condition_ls ids_private id_base in
{ r_vars = id_base :: id_next :: id_end :: ids_shared @ ids_exist;
r_root = para_inst_start;
r_sigma = para_inst_rest @ [lseg_pat];
r_new_pi = gen_pi_res;
r_new_sigma = [lseg_res];
r_condition = condition }
let mk_rule_lspts_ls k1 impl_ok1 impl_ok2 para =
let (ids_tuple, exps_tuple) = create_fresh_primeds_ls para in
let (id_base, id_next, id_end, ids_shared) = ids_tuple in
let (exp_base, exp_next, exp_end, exps_shared) = exps_tuple in
let lseg_pat = { Match.hpred = Prop.mk_lseg k1 para exp_base exp_next exps_shared; Match.flag = impl_ok1 } in
let (ids_exist, para_inst_pat) =
let (ids, para_body) = Sil.hpara_instantiate para exp_next exp_end exps_shared in
let allow_impl hpred = { Match.hpred = hpred; Match.flag = impl_ok2 } in
let para_body_pat = IList.map allow_impl para_body in
(ids, para_body_pat) in
let lseg_res = Prop.mk_lseg Sil.Lseg_NE para exp_base exp_end exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = id_next :: ids_exist in
create_condition_ls ids_private id_base in
{ r_vars = id_base :: id_next :: id_end :: ids_shared @ ids_exist;
r_root = lseg_pat;
r_sigma = para_inst_pat;
r_new_sigma = [lseg_res];
r_new_pi = gen_pi_res;
r_condition = condition }
let lseg_kind_add k1 k2 = match k1, k2 with
| Sil.Lseg_NE, Sil.Lseg_NE | Sil.Lseg_NE, Sil.Lseg_PE | Sil.Lseg_PE, Sil.Lseg_NE -> Sil.Lseg_NE
| Sil.Lseg_PE, Sil.Lseg_PE -> Sil.Lseg_PE
let mk_rule_lsls_ls k1 k2 impl_ok1 impl_ok2 para =
let (ids_tuple, exps_tuple) = create_fresh_primeds_ls para in
let (id_base, id_next, id_end, ids_shared) = ids_tuple in
let (exp_base, exp_next, exp_end, exps_shared) = exps_tuple in
let lseg_fst_pat =
{ Match.hpred = Prop.mk_lseg k1 para exp_base exp_next exps_shared; Match.flag = impl_ok1 } in
let lseg_snd_pat =
{ Match.hpred = Prop.mk_lseg k2 para exp_next exp_end exps_shared; Match.flag = impl_ok2 } in
let k_res = lseg_kind_add k1 k2 in
let lseg_res = Prop.mk_lseg k_res para exp_base exp_end exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = []
(*
let inst_base, inst_next, inst_end =
let find x = sub_find (equal x) inst in
try
(find id_base, find id_next, find id_end)
with Not_found -> assert false in
let spooky_case _ =
(lseg_kind_equal Sil.Lseg_PE k_res)
&& (check_allocatedness p_leftover inst_end)
&& ((check_disequal p_start inst_base inst_next)
|| (check_disequal p_start inst_next inst_end)) in
let obvious_case _ =
check_disequal p_start inst_base inst_end &&
not (check_disequal p_leftover inst_base inst_end) in
if not (spooky_case () || obvious_case ()) then []
else [Aneq(inst_base, inst_end)]
*)
in
let condition =
let ids_private = [id_next] in
create_condition_ls ids_private id_base in
{ r_vars = id_base :: id_next :: id_end :: ids_shared ;
r_root = lseg_fst_pat;
r_sigma = [lseg_snd_pat];
r_new_sigma = [lseg_res];
r_new_pi = gen_pi_res;
r_condition = condition }
let mk_rules_for_sll (para : Sil.hpara) : rule list =
if not !Config.nelseg then
begin
let pts_pts = mk_rule_ptspts_ls true true para in
let pts_pels = mk_rule_ptsls_ls Sil.Lseg_PE true false para in
let pels_pts = mk_rule_lspts_ls Sil.Lseg_PE false true para in
let pels_nels = mk_rule_lsls_ls Sil.Lseg_PE Sil.Lseg_NE false false para in
let nels_pels = mk_rule_lsls_ls Sil.Lseg_NE Sil.Lseg_PE false false para in
let pels_pels = mk_rule_lsls_ls Sil.Lseg_PE Sil.Lseg_PE false false para in
[pts_pts; pts_pels; pels_pts; pels_nels; nels_pels; pels_pels]
end
else
begin
let pts_pts = mk_rule_ptspts_ls true true para in
let pts_nels = mk_rule_ptsls_ls Sil.Lseg_NE true false para in
let nels_pts = mk_rule_lspts_ls Sil.Lseg_NE false true para in
let nels_nels = mk_rule_lsls_ls Sil.Lseg_NE Sil.Lseg_NE false false para in
[pts_pts; pts_nels; nels_pts; nels_nels]
end
(****************** End of SLL abstraction rules ******************)
(****************** Start of DLL abstraction rules ******************)
let create_condition_dll = create_condition_ls
let mk_rule_ptspts_dll impl_ok1 impl_ok2 para =
let id_iF = Ident.create_fresh Ident.kprimed in
let id_iF' = Ident.create_fresh Ident.kprimed in
let id_oB = Ident.create_fresh Ident.kprimed in
let id_oF = Ident.create_fresh Ident.kprimed in
let ids_shared =
let svars = para.Sil.svars_dll in
let f _ = Ident.create_fresh Ident.kprimed in
IList.map f svars in
let exp_iF = Sil.Var id_iF in
let exp_iF' = Sil.Var id_iF' in
let exp_oB = Sil.Var id_oB in
let exp_oF = Sil.Var id_oF in
let exps_shared = IList.map (fun id -> Sil.Var id) ids_shared in
let (ids_exist_fst, para_fst) = Sil.hpara_dll_instantiate para exp_iF exp_oB exp_iF' exps_shared in
let (para_fst_start, para_fst_rest) =
let mark_impl_flag hpred = { Match.hpred = hpred; Match.flag = impl_ok1 } in
match para_fst with
| [] -> L.out "@.@.ERROR (Empty DLL para): %a@.@." (Sil.pp_hpara_dll pe_text) para; assert false
| hpred :: hpreds ->
let hpat = mark_impl_flag hpred in
let hpats = IList.map mark_impl_flag hpreds in
(hpat, hpats) in
let (ids_exist_snd, para_snd) =
let mark_impl_flag hpred = { Match.hpred = hpred; Match.flag = impl_ok2 } in
let (ids, para_body) = Sil.hpara_dll_instantiate para exp_iF' exp_iF exp_oF exps_shared in
let para_body_hpats = IList.map mark_impl_flag para_body in
(ids, para_body_hpats) in
let dllseg_res = Prop.mk_dllseg Sil.Lseg_NE para exp_iF exp_oB exp_oF exp_iF' exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
(* for the case of ptspts since iF'=iB therefore iF' cannot be private*)
let ids_private = ids_exist_fst @ ids_exist_snd in
create_condition_dll ids_private id_iF in
(*
L.out "r_root/para_fst_start=%a @.@." pp_hpat para_fst_start;
L.out "para_fst_rest=%a @.@." pp_hpat_list para_fst_rest;
L.out "para_snd=%a @.@." pp_hpat_list para_snd;
L.out "dllseg_res=%a @.@." pp_hpred dllseg_res;
*)
{ r_vars = id_iF :: id_oB :: id_iF':: id_oF :: ids_shared @ ids_exist_fst @ ids_exist_snd;
r_root = para_fst_start;
r_sigma = para_fst_rest @ para_snd;
r_new_sigma = [dllseg_res];
r_new_pi = gen_pi_res;
r_condition = condition }
let mk_rule_ptsdll_dll k2 impl_ok1 impl_ok2 para =
let id_iF = Ident.create_fresh Ident.kprimed in
let id_iF' = Ident.create_fresh Ident.kprimed in
let id_oB = Ident.create_fresh Ident.kprimed in
let id_oF = Ident.create_fresh Ident.kprimed in
let id_iB = Ident.create_fresh Ident.kprimed in
let ids_shared =
let svars = para.Sil.svars_dll in
let f _ = Ident.create_fresh Ident.kprimed in
IList.map f svars in
let exp_iF = Sil.Var id_iF in
let exp_iF' = Sil.Var id_iF' in
let exp_oB = Sil.Var id_oB in
let exp_oF = Sil.Var id_oF in
let exp_iB = Sil.Var id_iB in
let exps_shared = IList.map (fun id -> Sil.Var id) ids_shared in
let (ids_exist, para_inst) = Sil.hpara_dll_instantiate para exp_iF exp_oB exp_iF' exps_shared in
let (para_inst_start, para_inst_rest) =
match para_inst with
| [] -> assert false
| hpred :: hpreds ->
let allow_impl hpred = { Match.hpred = hpred; Match.flag = impl_ok1 } in
(allow_impl hpred, IList.map allow_impl hpreds) in
let dllseg_pat = { Match.hpred = Prop.mk_dllseg k2 para exp_iF' exp_iF exp_oF exp_iB exps_shared; Match.flag = impl_ok2 } in
let dllseg_res = Prop.mk_dllseg Sil.Lseg_NE para exp_iF exp_oB exp_oF exp_iB exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = id_iF':: ids_exist in
create_condition_dll ids_private id_iF in
{ r_vars = id_iF :: id_oB :: id_iF':: id_oF:: id_iB:: ids_shared @ ids_exist;
r_root = para_inst_start;
r_sigma = para_inst_rest @ [dllseg_pat];
r_new_pi = gen_pi_res;
r_new_sigma = [dllseg_res];
r_condition = condition }
let mk_rule_dllpts_dll k1 impl_ok1 impl_ok2 para =
let id_iF = Ident.create_fresh Ident.kprimed in
let id_iF' = Ident.create_fresh Ident.kprimed in
let id_oB = Ident.create_fresh Ident.kprimed in
let id_oB' = Ident.create_fresh Ident.kprimed in
let id_oF = Ident.create_fresh Ident.kprimed in
let ids_shared =
let svars = para.Sil.svars_dll in
let f _ = Ident.create_fresh Ident.kprimed in
IList.map f svars in
let exp_iF = Sil.Var id_iF in
let exp_iF' = Sil.Var id_iF' in
let exp_oB = Sil.Var id_oB in
let exp_oB' = Sil.Var id_oB' in
let exp_oF = Sil.Var id_oF in
let exps_shared = IList.map (fun id -> Sil.Var id) ids_shared in
let (ids_exist, para_inst) = Sil.hpara_dll_instantiate para exp_iF' exp_oB' exp_oF exps_shared in
let para_inst_pat =
let allow_impl hpred = { Match.hpred = hpred; Match.flag = impl_ok2 } in
IList.map allow_impl para_inst in
let dllseg_pat = { Match.hpred = Prop.mk_dllseg k1 para exp_iF exp_oB exp_iF' exp_oB' exps_shared; Match.flag = impl_ok1 } in
let dllseg_res = Prop.mk_dllseg Sil.Lseg_NE para exp_iF exp_oB exp_oF exp_iF' exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = id_oB':: ids_exist in
create_condition_dll ids_private id_iF in
{ r_vars = id_iF :: id_oB :: id_iF':: id_oB':: id_oF:: ids_shared @ ids_exist;
r_root = dllseg_pat;
r_sigma = para_inst_pat;
r_new_pi = gen_pi_res;
r_new_sigma = [dllseg_res];
r_condition = condition }
let mk_rule_dlldll_dll k1 k2 impl_ok1 impl_ok2 para =
let id_iF = Ident.create_fresh Ident.kprimed in
let id_iF' = Ident.create_fresh Ident.kprimed in
let id_oB = Ident.create_fresh Ident.kprimed in
let id_oB' = Ident.create_fresh Ident.kprimed in
let id_oF = Ident.create_fresh Ident.kprimed in
let id_iB = Ident.create_fresh Ident.kprimed in
let ids_shared =
let svars = para.Sil.svars_dll in
let f _ = Ident.create_fresh Ident.kprimed in
IList.map f svars in
let exp_iF = Sil.Var id_iF in
let exp_iF' = Sil.Var id_iF' in
let exp_oB = Sil.Var id_oB in
let exp_oB' = Sil.Var id_oB' in
let exp_oF = Sil.Var id_oF in
let exp_iB = Sil.Var id_iB in
let exps_shared = IList.map (fun id -> Sil.Var id) ids_shared in
let lseg_fst_pat = { Match.hpred = Prop.mk_dllseg k1 para exp_iF exp_oB exp_iF' exp_oB' exps_shared; Match.flag = impl_ok1 } in
let lseg_snd_pat = { Match.hpred = Prop.mk_dllseg k2 para exp_iF' exp_oB' exp_oF exp_iB exps_shared; Match.flag = impl_ok2 } in
let k_res = lseg_kind_add k1 k2 in
let lseg_res = Prop.mk_dllseg k_res para exp_iF exp_oB exp_oF exp_iB exps_shared in
let gen_pi_res _ _ (_: Sil.subst) = [] in
let condition =
let ids_private = [id_iF'; id_oB'] in
create_condition_dll ids_private id_iF in
{ r_vars = id_iF :: id_iF' :: id_oB:: id_oB' :: id_oF:: id_iB:: ids_shared ;
r_root = lseg_fst_pat;
r_sigma = [lseg_snd_pat];
r_new_sigma = [lseg_res];
r_new_pi = gen_pi_res;
r_condition = condition }
let mk_rules_for_dll (para : Sil.hpara_dll) : rule list =
if not !Config.nelseg then
begin
let pts_pts = mk_rule_ptspts_dll true true para in
let pts_pedll = mk_rule_ptsdll_dll Sil.Lseg_PE true false para in
let pedll_pts = mk_rule_dllpts_dll Sil.Lseg_PE false true para in
let pedll_nedll = mk_rule_dlldll_dll Sil.Lseg_PE Sil.Lseg_NE false false para in
let nedll_pedll = mk_rule_dlldll_dll Sil.Lseg_NE Sil.Lseg_PE false false para in
let pedll_pedll = mk_rule_dlldll_dll Sil.Lseg_PE Sil.Lseg_PE false false para in
[pts_pts; pts_pedll; pedll_pts; pedll_nedll; nedll_pedll; pedll_pedll]
end
else
begin
let ptspts_dll = mk_rule_ptspts_dll true true para in
let ptsdll_dll = mk_rule_ptsdll_dll Sil.Lseg_NE true false para in
let dllpts_dll = mk_rule_dllpts_dll Sil.Lseg_NE false true para in
let dlldll_dll = mk_rule_dlldll_dll Sil.Lseg_NE Sil.Lseg_NE false false para in
[ptspts_dll; ptsdll_dll; dllpts_dll; dlldll_dll]
end
(****************** End of DLL abstraction rules ******************)
(****************** Start of Predicate Discovery ******************)
let typ_get_recursive_flds tenv typ_exp =
let filter typ (_, t, _) =
match t with
| Sil.Tvar _ | Sil.Tint _ | Sil.Tfloat _ | Sil.Tvoid | Sil.Tfun _ -> false
| Sil.Tptr (Sil.Tvar tname', _) ->
let typ' = match Tenv.lookup tenv tname' with
| None ->
L.err "@.typ_get_recursive: Undefined type %s@." (Typename.to_string tname');
t
| Some st -> Sil.Tstruct st in
Sil.typ_equal typ' typ
| Sil.Tptr _ | Sil.Tstruct _ | Sil.Tarray _ ->
false
in
match typ_exp with
| Sil.Sizeof (typ, _) ->
(match Tenv.expand_type tenv typ with
| Sil.Tint _ | Sil.Tvoid | Sil.Tfun _ | Sil.Tptr _ | Sil.Tfloat _ -> []
| Sil.Tstruct { Sil.instance_fields } ->
IList.map (fun (x, _, _) -> x) (IList.filter (filter typ) instance_fields)
| Sil.Tarray _ -> []
| Sil.Tvar _ -> assert false)
| Sil.Var _ -> [] (* type of |-> not known yet *)
| Sil.Const _ -> []
| _ ->
L.err "@.typ_get_recursive: unexpected type expr: %a@." (Sil.pp_exp pe_text) typ_exp;
assert false
let discover_para_roots p root1 next1 root2 next2 : Sil.hpara option =
let eq_arg1 = Sil.exp_equal root1 next1 in
let eq_arg2 = Sil.exp_equal root2 next2 in
let precondition_check = (not eq_arg1 && not eq_arg2) in
if not precondition_check then None
else
let corres = [(next1, next2)] in
let todos = [(root1, root2)] in
let sigma = Prop.get_sigma p in
match Match.find_partial_iso (Prover.check_equal p) corres todos sigma with
| None -> None
| Some (new_corres, new_sigma1, _, _) ->
let hpara, _ = Match.hpara_create new_corres new_sigma1 root1 next1 in
Some hpara
let discover_para_dll_roots p root1 blink1 flink1 root2 blink2 flink2 : Sil.hpara_dll option =
let eq_arg1 = Sil.exp_equal root1 blink1 in
let eq_arg1' = Sil.exp_equal root1 flink1 in
let eq_arg2 = Sil.exp_equal root2 blink2 in
let eq_arg2' = Sil.exp_equal root2 flink2 in
let precondition_check = not (eq_arg1 || eq_arg1' || eq_arg2 || eq_arg2') in
if not precondition_check then None
else
let corres = [(blink1, blink2); (flink1, flink2)] in
let todos = [(root1, root2)] in
let sigma = Prop.get_sigma p in
match Match.find_partial_iso (Prover.check_equal p) corres todos sigma with
| None -> None
| Some (new_corres, new_sigma1, _, _) ->
let hpara_dll, _ = Match.hpara_dll_create new_corres new_sigma1 root1 blink1 flink1 in
Some hpara_dll
let discover_para_candidates tenv p =
let edges = ref [] in
let add_edge edg = edges := edg :: !edges in
let get_edges_strexp rec_flds root se =
let is_rec_fld fld = IList.exists (Sil.fld_equal fld) rec_flds in
match se with
| Sil.Eexp _ | Sil.Earray _ -> ()
| Sil.Estruct (fsel, _) ->
let fsel' = IList.filter (fun (fld, _) -> is_rec_fld fld) fsel in
let process (_, nextse) =
match nextse with
| Sil.Eexp (next, _) -> add_edge (root, next)
| _ -> assert false in
IList.iter process fsel' in
let rec get_edges_sigma = function
| [] -> ()
| Sil.Hlseg _ :: sigma_rest | Sil.Hdllseg _ :: sigma_rest ->
get_edges_sigma sigma_rest
| Sil.Hpointsto (root, se, te) :: sigma_rest ->
let rec_flds = typ_get_recursive_flds tenv te in
get_edges_strexp rec_flds root se;
get_edges_sigma sigma_rest in
let rec find_all_consecutive_edges found edges_seen = function
| [] -> IList.rev found
| (e1, e2) :: edges_notseen ->
let edges_others = (IList.rev edges_seen) @ edges_notseen in
let edges_matched = IList.filter (fun (e1', _) -> Sil.exp_equal e2 e1') edges_others in
let new_found =
let f found_acc (_, e3) = (e1, e2, e3) :: found_acc in
IList.fold_left f found edges_matched in
let new_edges_seen = (e1, e2) :: edges_seen in
find_all_consecutive_edges new_found new_edges_seen edges_notseen in
let sigma = Prop.get_sigma p in
get_edges_sigma sigma;
find_all_consecutive_edges [] [] !edges
let discover_para_dll_candidates tenv p =
let edges = ref [] in
let add_edge edg = (edges := edg :: !edges) in
let get_edges_strexp rec_flds root se =
let is_rec_fld fld = IList.exists (Sil.fld_equal fld) rec_flds in
match se with
| Sil.Eexp _ | Sil.Earray _ -> ()
| Sil.Estruct (fsel, _) ->
let fsel' = IList.filter (fun (fld, _) -> is_rec_fld fld) fsel in
let convert_to_exp acc (_, se) =
match se with
| Sil.Eexp (e, _) -> e:: acc
| _ -> assert false in
let links = IList.rev (IList.fold_left convert_to_exp [] fsel') in
let rec iter_pairs = function
| [] -> ()
| x:: l -> (IList.iter (fun y -> add_edge (root, x, y)) l; iter_pairs l) in
iter_pairs links in
let rec get_edges_sigma = function
| [] -> ()
| Sil.Hlseg _ :: sigma_rest | Sil.Hdllseg _ :: sigma_rest ->
get_edges_sigma sigma_rest
| Sil.Hpointsto (root, se, te) :: sigma_rest ->
let rec_flds = typ_get_recursive_flds tenv te in
get_edges_strexp rec_flds root se;
get_edges_sigma sigma_rest in
let rec find_all_consecutive_edges found edges_seen = function
| [] -> IList.rev found
| (iF, blink, flink) :: edges_notseen ->
let edges_others = (IList.rev edges_seen) @ edges_notseen in
let edges_matched = IList.filter (fun (e1', _, _) -> Sil.exp_equal flink e1') edges_others in
let new_found =
let f found_acc (_, _, flink2) = (iF, blink, flink, flink2) :: found_acc in
IList.fold_left f found edges_matched in
let new_edges_seen = (iF, blink, flink) :: edges_seen in
find_all_consecutive_edges new_found new_edges_seen edges_notseen in
let sigma = Prop.get_sigma p in
get_edges_sigma sigma;
find_all_consecutive_edges [] [] !edges
let discover_para tenv p =
let candidates = discover_para_candidates tenv p in
let already_defined para paras =
IList.exists (fun para' -> Match.hpara_iso para para') paras in
let f paras (root, next, out) =
match (discover_para_roots p root next next out) with
| None -> paras
| Some para -> if already_defined para paras then paras else para :: paras in
IList.fold_left f [] candidates
let discover_para_dll tenv p =
(*
L.out "@[.... Called discover_dll para ...@.";
L.out "@[<4> PROP : %a@\n@." pp_prop p;
*)
let candidates = discover_para_dll_candidates tenv p in
let already_defined para paras =
IList.exists (fun para' -> Match.hpara_dll_iso para para') paras in
let f paras (iF, oB, iF', oF) =
match (discover_para_dll_roots p iF oB iF' iF' iF oF) with
| None -> paras
| Some para -> if already_defined para paras then paras else para :: paras in
IList.fold_left f [] candidates
(****************** End of Predicate Discovery ******************)
(****************** Start of the ADT abs_rules ******************)
(** Type of parameter for abstraction rules *)
type para_ty =
| SLL of Sil.hpara
| DLL of Sil.hpara_dll
(** Rule set: a list of rules of a given type *)
type rule_set = para_ty * rule list
type rules = rule_set list
module Global =
struct
let current_rules : rules ref =
ref []
end
let get_current_rules () =
!Global.current_rules
let set_current_rules rules =
Global.current_rules := rules
let reset_current_rules () =
Global.current_rules := []
let eqs_sub subst eqs =
IList.map (fun (e1, e2) -> (Sil.exp_sub subst e1, Sil.exp_sub subst e2)) eqs
let eqs_solve ids_in eqs_in =
let rec solve (sub: Sil.subst) (eqs: (Sil.exp * Sil.exp) list) : Sil.subst option =
let do_default id e eqs_rest =
if not (IList.exists (fun id' -> Ident.equal id id') ids_in) then None
else
let sub' = match Sil.extend_sub sub id e with
| None -> L.out "@.@.ERROR : Buggy Implementation.@.@."; assert false
| Some sub' -> sub' in
let eqs_rest' = eqs_sub sub' eqs_rest in
solve sub' eqs_rest' in
match eqs with
| [] -> Some sub
| (e1, e2) :: eqs_rest when Sil.exp_equal e1 e2 ->
solve sub eqs_rest
| (Sil.Var id1, (Sil.Const _ as e2)) :: eqs_rest ->
do_default id1 e2 eqs_rest
| ((Sil.Const _ as e1), (Sil.Var _ as e2)) :: eqs_rest ->
solve sub ((e2, e1):: eqs_rest)
| ((Sil.Var id1 as e1), (Sil.Var id2 as e2)) :: eqs_rest ->
let n = Ident.compare id1 id2 in
begin
if n = 0 then solve sub eqs_rest
else if n > 0 then solve sub ((e2, e1):: eqs_rest)
else do_default id1 e2 eqs_rest
end
| _ :: _ -> None in
let compute_ids sub =
let sub_list = Sil.sub_to_list sub in
let sub_dom = IList.map fst sub_list in
let filter id =
not (IList.exists (fun id' -> Ident.equal id id') sub_dom) in
IList.filter filter ids_in in
match solve Sil.sub_empty eqs_in with
| None -> None
| Some sub -> Some (compute_ids sub, sub)
let sigma_special_cases_eqs sigma =
let rec f ids_acc eqs_acc sigma_acc = function
| [] ->
[(IList.rev ids_acc, IList.rev eqs_acc, IList.rev sigma_acc)]
| Sil.Hpointsto _ as hpred :: sigma_rest ->
f ids_acc eqs_acc (hpred:: sigma_acc) sigma_rest
| Sil.Hlseg(_, para, e1, e2, es) as hpred :: sigma_rest ->
let empty_case =
f ids_acc ((e1, e2):: eqs_acc) sigma_acc sigma_rest in
let pointsto_case =
let (eids, para_inst) = Sil.hpara_instantiate para e1 e2 es in
f (eids@ids_acc) eqs_acc sigma_acc (para_inst@sigma_rest) in
let general_case =
f ids_acc eqs_acc (hpred:: sigma_acc) sigma_rest in
empty_case @ pointsto_case @ general_case
| Sil.Hdllseg(_, para, e1, e2, e3, e4, es) as hpred :: sigma_rest ->
let empty_case =
f ids_acc ((e1, e3):: (e2, e4):: eqs_acc) sigma_acc sigma_rest in
let pointsto_case =
let (eids, para_inst) = Sil.hpara_dll_instantiate para e1 e2 e3 es in
f (eids@ids_acc) eqs_acc sigma_acc (para_inst@sigma_rest) in
let general_case =
f ids_acc eqs_acc (hpred:: sigma_acc) sigma_rest in
empty_case @ pointsto_case @ general_case in
f [] [] [] sigma
let sigma_special_cases ids sigma : (Ident.t list * Sil.hpred list) list =
let special_cases_eqs = sigma_special_cases_eqs sigma in
let special_cases_rev =
let f acc (eids_cur, eqs_cur, sigma_cur) =
let ids_all = ids @ eids_cur in
match (eqs_solve ids_all eqs_cur) with
| None -> acc
| Some (ids_res, sub) ->
(ids_res, IList.map (Sil.hpred_sub sub) sigma_cur) :: acc in
IList.fold_left f [] special_cases_eqs in
IList.rev special_cases_rev
let hpara_special_cases hpara : Sil.hpara list =
let update_para (evars', body') = { hpara with Sil.evars = evars'; Sil.body = body'} in
let special_cases = sigma_special_cases hpara.Sil.evars hpara.Sil.body in
IList.map update_para special_cases
let hpara_special_cases_dll hpara : Sil.hpara_dll list =
let update_para (evars', body') = { hpara with Sil.evars_dll = evars'; Sil.body_dll = body'} in
let special_cases = sigma_special_cases hpara.Sil.evars_dll hpara.Sil.body_dll in
IList.map update_para special_cases
let abs_rules_apply_rsets (rsets: rule_set list) (p_in: Prop.normal Prop.t) : Prop.normal Prop.t =
let apply_rule (changed, p) r =
match (sigma_rewrite p r) with
| None -> (changed, p)
| Some p' ->
(*
L.out "@[.... abstraction (rewritten in abs_rules) ....@.";
L.out "@[<4> PROP:%a@\n@." pp_prop p';
*)
(true, p') in
let rec apply_rule_set p rset =
let (_, rules) = rset in
let (changed, p') = IList.fold_left apply_rule (false, p) rules in
if changed then apply_rule_set p' rset else p' in
IList.fold_left apply_rule_set p_in rsets
let abs_rules_apply_lists tenv (p_in: Prop.normal Prop.t) : Prop.normal Prop.t =
let new_rsets = ref [] in
let old_rsets = get_current_rules () in
let rec discover_then_abstract p =
let (closed_paras_sll, closed_paras_dll) =
let paras_sll = discover_para tenv p in
let paras_dll = discover_para_dll tenv p in
let closed_paras_sll = IList.flatten (IList.map hpara_special_cases paras_sll) in
let closed_paras_dll = IList.flatten (IList.map hpara_special_cases_dll paras_dll) in
begin
(*
if IList.length closed_paras_sll >= 1 then
begin
L.out "@.... discovered predicates ....@.";
L.out "@[<4> pred : %a@\n@." pp_hpara_list closed_paras_sll;
end
if IList.length closed_paras_dll >= 1 then
begin
L.out "@.... discovered predicates ....@.";
L.out "@[<4> pred : %a@\n@." pp_hpara_dll_list closed_paras_dll;
end
*)
(closed_paras_sll, closed_paras_dll)
end in
let (todo_paras_sll, todo_paras_dll) =
let eq_sll para rset = match rset with
| (SLL para', _) -> Match.hpara_iso para para'
| _ -> false in
let eq_dll para rset = match rset with
| (DLL para', _) -> Match.hpara_dll_iso para para'
| _ -> false in
let filter_sll para =
not (IList.exists (eq_sll para) old_rsets) &&
not (IList.exists (eq_sll para) !new_rsets) in
let filter_dll para =
not (IList.exists (eq_dll para) old_rsets) &&
not (IList.exists (eq_dll para) !new_rsets) in
let todo_paras_sll = IList.filter filter_sll closed_paras_sll in
let todo_paras_dll = IList.filter filter_dll closed_paras_dll in
(todo_paras_sll, todo_paras_dll) in
let f_recurse () =
let todo_rsets_sll =
IList.map (fun para -> (SLL para, mk_rules_for_sll para)) todo_paras_sll in
let todo_rsets_dll =
IList.map (fun para -> (DLL para, mk_rules_for_dll para)) todo_paras_dll in
new_rsets := !new_rsets @ todo_rsets_sll @ todo_rsets_dll;
let p' = abs_rules_apply_rsets todo_rsets_sll p in
let p'' = abs_rules_apply_rsets todo_rsets_dll p' in
discover_then_abstract p'' in
match todo_paras_sll, todo_paras_dll with
| [], [] -> p
| _ -> f_recurse () in
let p1 = abs_rules_apply_rsets old_rsets p_in in
let p2 = discover_then_abstract p1 in
let new_rules = old_rsets @ !new_rsets in
set_current_rules new_rules;
p2
let abs_rules_apply tenv (p_in: Prop.normal Prop.t) : Prop.normal Prop.t =
abs_rules_apply_lists tenv p_in
(****************** End of the ADT abs_rules ******************)
(****************** Start of Main Abstraction Functions ******************)
let abstract_pure_part p ~(from_abstract_footprint: bool) =
let do_pure pure =
let pi_filtered =
let sigma = Prop.get_sigma p in
let fav_sigma = Prop.sigma_fav sigma in
let fav_nonpure = Prop.prop_fav_nonpure p in (** vars in current and footprint sigma *)
let filter atom =
let fav' = Sil.atom_fav atom in
Sil.fav_for_all fav' (fun id ->
if Ident.is_primed id then Sil.fav_mem fav_sigma id
else if Ident.is_footprint id then Sil.fav_mem fav_nonpure id
else true) in
IList.filter filter pure in
let new_pure =
IList.fold_left
(fun pi a ->
match a with
| Sil.Aneq (Sil.Var _, _) -> a:: pi
(* we only use Lt and Le because Gt and Ge are inserted in terms of Lt and Le. *)
| Sil.Aeq (Sil.Const (Sil.Cint i), Sil.BinOp (Sil.Lt, _, _))
| Sil.Aeq (Sil.BinOp (Sil.Lt, _, _), Sil.Const (Sil.Cint i))
| Sil.Aeq (Sil.Const (Sil.Cint i), Sil.BinOp (Sil.Le, _, _))
| Sil.Aeq (Sil.BinOp (Sil.Le, _, _), Sil.Const (Sil.Cint i)) when Sil.Int.isone i ->
a :: pi
| Sil.Aeq (Sil.Var name, e) when not (Ident.is_primed name) ->
(match e with
| Sil.Var _
| Sil.Const _ -> a :: pi
| _ -> pi)
| _ -> pi)
[] pi_filtered in
IList.rev new_pure in
let new_pure = do_pure (Prop.get_pure p) in
let eprop' = Prop.replace_pi new_pure (Prop.replace_sub Sil.sub_empty p) in
let eprop'' =
if !Config.footprint && not from_abstract_footprint then
let new_pi_footprint = do_pure (Prop.get_pi_footprint p) in
Prop.replace_pi_footprint new_pi_footprint eprop'
else eprop' in
Prop.normalize eprop''
(* Collect symbolic garbage from pi and sigma *)
let abstract_gc p =
let pi = Prop.get_pi p in
let p_without_pi = Prop.normalize (Prop.replace_pi [] p) in
let fav_p_without_pi = Prop.prop_fav p_without_pi in
(* let weak_filter atom =
let fav_atom = atom_fav atom in
IList.intersect compare fav_p_without_pi fav_atom in *)
let strong_filter = function
| Sil.Aeq(e1, e2) | Sil.Aneq(e1, e2) ->
let fav_e1 = Sil.exp_fav e1 in
let fav_e2 = Sil.exp_fav e2 in
let intersect_e1 _ = IList.intersect Ident.compare (Sil.fav_to_list fav_e1) (Sil.fav_to_list fav_p_without_pi) in
let intersect_e2 _ = IList.intersect Ident.compare (Sil.fav_to_list fav_e2) (Sil.fav_to_list fav_p_without_pi) in
let no_fav_e1 = Sil.fav_is_empty fav_e1 in
let no_fav_e2 = Sil.fav_is_empty fav_e2 in
(no_fav_e1 || intersect_e1 ()) && (no_fav_e2 || intersect_e2 ()) in
let new_pi = IList.filter strong_filter pi in
let prop = Prop.normalize (Prop.replace_pi new_pi p) in
match Prop.prop_iter_create prop with
| None -> prop
| Some iter -> Prop.prop_iter_to_prop (Prop.prop_iter_gc_fields iter)
module IdMap = Map.Make (Ident) (** maps from identifiers *)
module HpredSet =
Set.Make(struct
type t = Sil.hpred
let compare = Sil.hpred_compare
end)
let hpred_entries hpred = match hpred with
| Sil.Hpointsto (e, _, _) -> [e]
| Sil.Hlseg (_, _, e, _, _) -> [e]
| Sil.Hdllseg (_, _, e1, _, _, e2, _) -> [e1; e2]
(** find the id's in sigma reachable from the given roots *)
let sigma_reachable root_fav sigma =
let fav_to_set fav = Ident.idlist_to_idset (Sil.fav_to_list fav) in
let reach_set = ref (fav_to_set root_fav) in
let edges = ref [] in
let do_hpred hpred =
let hp_fav_set = fav_to_set (Sil.hpred_fav hpred) in
let add_entry e = edges := (e, hp_fav_set) :: !edges in
IList.iter add_entry (hpred_entries hpred) in
IList.iter do_hpred sigma;
let edge_fires (e, _) = match e with
| Sil.Var id ->
if (Ident.is_primed id || Ident.is_footprint id) then Ident.IdentSet.mem id !reach_set
else true
| _ -> true in
let rec apply_once edges_to_revisit edges_todo modified = match edges_todo with
| [] -> (edges_to_revisit, modified)
| edge:: edges_todo' ->
if edge_fires edge then
begin
reach_set := Ident.IdentSet.union (snd edge) !reach_set;
apply_once edges_to_revisit edges_todo' true
end
else apply_once (edge :: edges_to_revisit) edges_todo' modified in
let rec find_fixpoint edges_todo =
let edges_to_revisit, modified = apply_once [] edges_todo false in
if modified then find_fixpoint edges_to_revisit in
find_fixpoint !edges;
(* L.d_str "reachable: ";
Ident.IdentSet.iter (fun id -> Sil.d_exp (Sil.Var id); L.d_str " ") !reach_set;
L.d_ln (); *)
!reach_set
let get_cycle root prop =
let sigma = Prop.get_sigma prop in
let get_points_to e =
match e with
| Sil.Eexp(e', _) ->
(try
Some(IList.find (fun hpred -> match hpred with
| Sil.Hpointsto(e'', _, _) -> Sil.exp_equal e'' e'
| _ -> false) sigma)
with _ -> None)
| _ -> None in
let print_cycle cyc =
(L.d_str "Cycle= ";
IList.iter (fun ((e, t), f, e') ->
match e, e' with
| Sil.Eexp (e, _), Sil.Eexp (e', _) ->
L.d_str ("("^(Sil.exp_to_string e)^": "^(Sil.typ_to_string t)^", "^(Ident.fieldname_to_string f)^", "^(Sil.exp_to_string e')^")")
| _ -> ()) cyc;
L.d_strln "") in
(* perform a dfs of a graph stopping when e_root is reached. *)
(* Returns a pair (path, bool) where path is a list of edges ((e1,type_e1),f,e2) *)
(* describing the path to e_root and bool is true if e_root is reached. *)
let rec dfs e_root et_src path el visited =
match el with
| [] -> path, false
| (f, e):: el' ->
if Sil.strexp_equal e e_root then
(et_src, f, e):: path, true
else if IList.mem Sil.strexp_equal e visited then
path, false
else (
let visited' = (fst et_src):: visited in
let res = (match get_points_to e with
| None -> path, false
| Some (Sil.Hpointsto(_, Sil.Estruct(fl, _), Sil.Sizeof(te, _))) ->
dfs e_root (e, te) ((et_src, f, e):: path) fl visited'
| _ -> path, false (* check for lists *)) in
if snd res then res
else dfs e_root et_src path el' visited') in
L.d_strln "Looking for cycle with root expression: "; Sil.d_hpred root; L.d_strln "";
match root with
| Sil.Hpointsto(e_root, Sil.Estruct(fl, _), Sil.Sizeof(te, _)) ->
let se_root = Sil.Eexp(e_root, Sil.Inone) in
(* start dfs with empty path and expr pointing to root *)
let (pot_cycle, res) = dfs se_root (se_root, te) [] fl [] in
if res then (
print_cycle pot_cycle;
pot_cycle
) else (
L.d_strln "NO cycle found from root";
[])
| _ -> L.d_strln "Root exp is not an allocated object. No cycle found"; []
(* Check whether the hidden counter field of a struct representing an *)
(* objective-c object is positive, and whether the leak is part of the *)
(* specified buckets. In the positive case, it returns the bucket *)
let should_raise_objc_leak hpred =
match hpred with
| Sil.Hpointsto(_, Sil.Estruct((fn, Sil.Eexp( (Sil.Const (Sil.Cint i)), _)):: _, _),
Sil.Sizeof (typ, _))
when Ident.fieldname_is_hidden fn && Sil.Int.gt i Sil.Int.zero (* counter > 0 *) ->
Mleak_buckets.should_raise_objc_leak typ
| _ -> None
let get_retain_cycle_dotty _prop cycle =
match _prop with
| None -> None
| Some (Some _prop) ->
Dotty.dotty_prop_to_str _prop cycle
| _ -> None
let get_var_retain_cycle _prop =
let sigma = Prop.get_sigma _prop in
let is_pvar v h =
match h with
| Sil.Hpointsto (Sil.Lvar _, v', _) when Sil.strexp_equal v v' -> true
| _ -> false in
let is_hpred_block v h =
match h, v with
| Sil.Hpointsto (e, _, Sil.Sizeof(typ, _)), Sil.Eexp (e', _)
when Sil.exp_equal e e' && Sil.is_block_type typ -> true
| _, _ -> false in
let find v =
try
let hp = IList.find (is_pvar v) sigma in
Some (Sil.hpred_get_lhs hp)
with Not_found -> None in
let find_block v =
if (IList.exists (is_hpred_block v) sigma) then
Some (Sil.Lvar Sil.block_pvar)
else None in
let sexp e = Sil.Eexp (e, Sil.Inone) in
let find_or_block ((e, t), f, e') =
match find e with
| Some pvar -> [((sexp pvar, t), f, e')]
| _ -> (match find_block e with
| Some blk -> [((sexp blk, t), f, e')]
| _ -> [((sexp (Sil.Sizeof(t, Sil.Subtype.exact)), t), f, e')]) in
(* returns the pvars of the first cycle we find in sigma. *)
(* This is an heuristic that works if there is one cycle. *)
(* In case there are more than one cycle we may return not necessarily*)
(* the one we are looking for. *)
let rec do_sigma sigma_todo =
match sigma_todo with
| [] -> []
| hp:: sigma' ->
let cycle = get_cycle hp _prop in
L.d_strln "Filtering pvar in cycle ";
let cycle' = IList.flatten (IList.map find_or_block cycle) in
if cycle' = [] then do_sigma sigma'
else cycle' in
do_sigma sigma
let remove_opt _prop =
match _prop with
| Some (Some p) -> p
| _ -> Prop.prop_emp
(* Checks if cycle has fields (derived from a property or directly defined as ivar) *)
(* with attributes weak/unsafe_unretained/assing *)
let cycle_has_weak_or_unretained_or_assign_field cycle =
(* returns items annotation for field fn in struct t *)
let get_item_annotation t fn =
match t with
| Sil.Tstruct { Sil.instance_fields; static_fields } ->
let ia = ref [] in
IList.iter (fun (fn', _, ia') ->
if Ident.fieldname_equal fn fn' then ia := ia')
(instance_fields @ static_fields);
!ia
| _ -> [] in
let rec has_weak_or_unretained_or_assign params =
match params with
| [] -> false
| att:: _ when Config.unsafe_unret = att || Config.weak = att || Config.assign = att -> true
| _:: params' -> has_weak_or_unretained_or_assign params' in
let do_annotation (a, _) =
((a.Sil.class_name = Config.property_attributes) ||
(a.Sil.class_name = Config.ivar_attributes)) && has_weak_or_unretained_or_assign a.Sil.parameters in
let rec do_cycle c =
match c with
| [] -> false
| ((_, t), fn, _):: c' ->
let ia = get_item_annotation t fn in
if (IList.exists do_annotation ia) then true
else do_cycle c' in
do_cycle cycle
let check_observer_is_unsubscribed_deallocation prop e =
let pvar_opt = match Prop.get_resource_attribute prop e with
| Some (Sil.Aresource ({Sil.ra_vpath = Some (Sil.Dpvar pvar) })) -> Some pvar
| _ -> None in
let loc = State.get_loc () in
match Prop.get_observer_attribute prop e with
| Some Sil.Aobserver ->
(match pvar_opt with
| Some pvar when !Config.nsnotification_center_checker_backend ->
L.d_strln (" ERROR: Object " ^ (Pvar.to_string pvar) ^
" is being deallocated while still registered in a notification center");
let desc = Localise.desc_registered_observer_being_deallocated pvar loc in
raise (Exceptions.Registered_observer_being_deallocated (desc, __POS__))
| _ -> ())
| _ -> ()
let check_junk ?original_prop pname tenv prop =
let fav_sub_sigmafp = Sil.fav_new () in
Sil.sub_fav_add fav_sub_sigmafp (Prop.get_sub prop);
Prop.sigma_fav_add fav_sub_sigmafp (Prop.get_sigma_footprint prop);
let leaks_reported = ref [] in
let remove_junk_once fp_part fav_root sigma =
let id_considered_reachable = (* reachability function *)
let reach_set = sigma_reachable fav_root sigma in
fun id -> Ident.IdentSet.mem id reach_set in
let should_remove_hpred entries =
let predicate = function
| Sil.Var id ->
(Ident.is_primed id || Ident.is_footprint id)
&& not (Sil.fav_mem fav_root id) && not (id_considered_reachable id)
| _ -> false in
IList.for_all predicate entries in
let hpred_in_cycle hpred = (* check if the predicate belongs to a cycle in the heap *)
let id_in_cycle id =
let set1 = sigma_reachable (Sil.fav_from_list [id]) sigma in
let set2 = Ident.IdentSet.remove id set1 in
let fav2 = Sil.fav_from_list (Ident.IdentSet.elements set2) in
let set3 = sigma_reachable fav2 sigma in
Ident.IdentSet.mem id set3 in
let entries = hpred_entries hpred in
let predicate = function
| Sil.Var id -> id_in_cycle id
| _ -> false in
let hpred_is_loop = match hpred with (* true if hpred has a self loop, ie one field points to id *)
| Sil.Hpointsto (Sil.Var id, se, _) ->
let fav = Sil.fav_new () in
Sil.strexp_fav_add fav se;
Sil.fav_mem fav id
| _ -> false in
hpred_is_loop || IList.exists predicate entries in
let rec remove_junk_recursive sigma_done sigma_todo =
match sigma_todo with
| [] -> IList.rev sigma_done
| hpred :: sigma_todo' ->
let entries = hpred_entries hpred in
if should_remove_hpred entries then
begin
let part = if fp_part then "footprint" else "normal" in
L.d_strln (".... Prop with garbage in " ^ part ^ " part ....");
L.d_increase_indent 1;
L.d_strln "PROP:";
Prop.d_prop prop; L.d_ln ();
L.d_strln "PREDICATE:";
Prop.d_sigma [hpred];
L.d_ln ();
let alloc_attribute =
(* find the alloc attribute of one of the roots of hpred, if it exists *)
let res = ref None in
let do_entry e =
check_observer_is_unsubscribed_deallocation prop e;
match Prop.get_resource_attribute prop e with
| Some (Sil.Aresource ({ Sil.ra_kind = Sil.Racquire }) as a) ->
L.d_str "ATTRIBUTE: "; Sil.d_exp (Sil.Const (Sil.Cattribute a)); L.d_ln ();
res := Some a
| _ ->
(match Prop.get_undef_attribute prop e with
| Some (Sil.Aundef _ as a) ->
res := Some a
| _ -> ()) in
IList.iter do_entry entries;
!res in
L.d_decrease_indent 1;
let is_undefined = Option.map_default Sil.attr_is_undef false alloc_attribute in
let resource = match Errdesc.hpred_is_open_resource prop hpred with
| Some res -> res
| None -> Sil.Rmemory Sil.Mmalloc in
let ml_bucket_opt =
match resource with
| Sil.Rmemory Sil.Mobjc -> should_raise_objc_leak hpred
| Sil.Rmemory Sil.Mnew | Sil.Rmemory Sil.Mnew_array
when !Config.curr_language = Config.C_CPP ->
Mleak_buckets.should_raise_cpp_leak ()
| _ -> None in
let exn_retain_cycle cycle =
let cycle_dotty = get_retain_cycle_dotty original_prop cycle in
let desc = Errdesc.explain_retain_cycle
(remove_opt original_prop) cycle (State.get_loc ()) cycle_dotty in
Exceptions.Retain_cycle
(remove_opt original_prop, hpred, desc, __POS__) in
let exn_leak = Exceptions.Leak
(fp_part, prop, hpred,
Errdesc.explain_leak tenv hpred prop alloc_attribute ml_bucket_opt,
!Absarray.array_abstraction_performed,
resource,
__POS__) in
let ignore_resource, exn =
(match alloc_attribute, resource with
| Some _, Sil.Rmemory Sil.Mobjc when (hpred_in_cycle hpred) ->
(* When there is a cycle in objc we ignore it *)
(* only if it's empty or it has weak or unsafe_unretained fields. *)
(* Otherwise we report a retain cycle. *)
let cycle = get_var_retain_cycle (remove_opt original_prop) in
let ignore_cycle =
(IList.length cycle = 0) ||
(cycle_has_weak_or_unretained_or_assign_field cycle) in
ignore_cycle, exn_retain_cycle cycle
| Some _, Sil.Rmemory Sil.Mobjc
| Some _, Sil.Rmemory Sil.Mnew
| Some _, Sil.Rmemory Sil.Mnew_array when !Config.curr_language = Config.C_CPP ->
ml_bucket_opt = None, exn_leak
| Some _, Sil.Rmemory _ -> !Config.curr_language = Config.Java, exn_leak
| Some _, Sil.Rignore -> true, exn_leak
| Some _, Sil.Rfile -> false, exn_leak
| Some _, Sil.Rlock -> false, exn_leak
| _ when hpred_in_cycle hpred && Sil.has_objc_ref_counter hpred ->
(* When its a cycle and the object has a ref counter then *)
(* we have a retain cycle. Objc object may not have the *)
(* Sil.Mobjc qualifier when added in footprint doing abduction *)
let cycle = get_var_retain_cycle (remove_opt original_prop) in
IList.length cycle = 0, exn_retain_cycle cycle
| _ -> !Config.curr_language = Config.Java, exn_leak) in
let already_reported () =
let attr_opt_equal ao1 ao2 = match ao1, ao2 with
| None, None -> true
| Some a1, Some a2 -> Sil.attribute_equal a1 a2
| Some _, None
| None, Some _ -> false in
(alloc_attribute = None && !leaks_reported <> []) ||
(* None attribute only reported if it's the first one *)
IList.mem attr_opt_equal alloc_attribute !leaks_reported in
let ignore_leak =
!Config.allowleak || ignore_resource || is_undefined || already_reported () in
let report_and_continue =
!Config.curr_language = Config.Java || !Config.footprint in
let report_leak () =
if not report_and_continue then raise exn
else
begin
Reporting.log_error pname exn;
leaks_reported := alloc_attribute :: !leaks_reported;
end in
if not ignore_leak then report_leak ();
remove_junk_recursive sigma_done sigma_todo'
end
else
remove_junk_recursive (hpred :: sigma_done) sigma_todo' in
remove_junk_recursive [] sigma in
let rec remove_junk fp_part fav_root sigma = (* call remove_junk_once until sigma stops shrinking *)
let sigma' = remove_junk_once fp_part fav_root sigma in
if IList.length sigma' = IList.length sigma then sigma'
else remove_junk fp_part fav_root sigma' in
let sigma_new = remove_junk false fav_sub_sigmafp (Prop.get_sigma prop) in
let sigma_fp_new = remove_junk true (Sil.fav_new ()) (Prop.get_sigma_footprint prop) in
if Prop.sigma_equal (Prop.get_sigma prop) sigma_new && Prop.sigma_equal (Prop.get_sigma_footprint prop) sigma_fp_new
then prop
else Prop.normalize (Prop.replace_sigma sigma_new (Prop.replace_sigma_footprint sigma_fp_new prop))
(** Check whether the prop contains junk.
If it does, and [Config.allowleak] is true, remove the junk, otherwise raise a Leak exception. *)
let abstract_junk ?original_prop pname tenv prop =
Absarray.array_abstraction_performed := false;
check_junk ~original_prop pname tenv prop
(** Remove redundant elements in an array, and check for junk afterwards *)
let remove_redundant_array_elements pname tenv prop =
Absarray.array_abstraction_performed := false;
let prop' = Absarray.remove_redundant_elements prop in
check_junk ~original_prop: (Some(prop)) pname tenv prop'
let abstract_prop pname tenv ~(rename_primed: bool) ~(from_abstract_footprint: bool) p =
Absarray.array_abstraction_performed := false;
let pure_abs_p = abstract_pure_part ~from_abstract_footprint: true p in
let array_abs_p =
if from_abstract_footprint
then pure_abs_p
else abstract_pure_part ~from_abstract_footprint: from_abstract_footprint (Absarray.abstract_array_check pure_abs_p) in
let abs_p = abs_rules_apply tenv array_abs_p in
let abs_p = abstract_gc abs_p in (** abstraction might enable more gc *)
let abs_p = check_junk ~original_prop: (Some(p)) pname tenv abs_p in
let ren_abs_p =
if rename_primed
then Prop.prop_rename_primed_footprint_vars abs_p
else abs_p in
ren_abs_p
let get_local_stack cur_sigma init_sigma =
let filter_stack = function
| Sil.Hpointsto (Sil.Lvar _, _, _) -> true
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> false in
let get_stack_var = function
| Sil.Hpointsto (Sil.Lvar pvar, _, _) -> pvar
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> assert false in
let filter_local_stack olds = function
| Sil.Hpointsto (Sil.Lvar pvar, _, _) -> not (IList.exists (Pvar.equal pvar) olds)
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> false in
let init_stack = IList.filter filter_stack init_sigma in
let init_stack_pvars = IList.map get_stack_var init_stack in
let cur_local_stack = IList.filter (filter_local_stack init_stack_pvars) cur_sigma in
let cur_local_stack_pvars = IList.map get_stack_var cur_local_stack in
(cur_local_stack, cur_local_stack_pvars)
(** Extract the footprint, add a local stack and return it as a prop *)
let extract_footprint_for_abs (p : 'a Prop.t) : Prop.exposed Prop.t * Pvar.t list =
let sigma = Prop.get_sigma p in
let foot_pi = Prop.get_pi_footprint p in
let foot_sigma = Prop.get_sigma_footprint p in
let (local_stack, local_stack_pvars) = get_local_stack sigma foot_sigma in
let p0 = Prop.from_sigma (local_stack @ foot_sigma) in
let p1 = Prop.replace_pi foot_pi p0 in
(p1, local_stack_pvars)
let remove_local_stack sigma pvars =
let filter_non_stack = function
| Sil.Hpointsto (Sil.Lvar pvar, _, _) -> not (IList.exists (Pvar.equal pvar) pvars)
| Sil.Hpointsto _ | Sil.Hlseg _ | Sil.Hdllseg _ -> true in
IList.filter filter_non_stack sigma
(** [prop_set_fooprint p p_foot] removes a local stack from [p_foot],
and sets proposition [p_foot] as footprint of [p]. *)
let set_footprint_for_abs (p : 'a Prop.t) (p_foot : 'a Prop.t) local_stack_pvars : Prop.exposed Prop.t =
let p_foot_pure = Prop.get_pure p_foot in
let p_foot_sigma = Prop.get_sigma p_foot in
let pi = p_foot_pure in
let sigma = remove_local_stack p_foot_sigma local_stack_pvars in
Prop.replace_sigma_footprint sigma (Prop.replace_pi_footprint pi p)
(** Abstract the footprint of prop *)
let abstract_footprint pname (tenv : Tenv.t) (prop : Prop.normal Prop.t) : Prop.normal Prop.t =
let (p, added_local_vars) = extract_footprint_for_abs prop in
let p_abs =
abstract_prop
pname tenv ~rename_primed: false
~from_abstract_footprint: true (Prop.normalize p) in
let prop' = set_footprint_for_abs prop p_abs added_local_vars in
Prop.normalize prop'
let _abstract pname pay tenv p =
if pay then SymOp.pay(); (* pay one symop *)
let p' = if !Config.footprint then abstract_footprint pname tenv p else p in
abstract_prop pname tenv ~rename_primed: true ~from_abstract_footprint: false p'
let abstract pname tenv p =
_abstract pname true tenv p
let abstract_no_symop pname tenv p =
_abstract pname false tenv p
let lifted_abstract pname tenv pset =
let f p =
if Prover.check_inconsistency p then None
else Some (abstract pname tenv p) in
let abstracted_pset = Propset.map_option f pset in
abstracted_pset
(***************** End of Main Abstraction Functions *****************)