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