abducted -> abduced

Reviewed By: jeremydubreil

Differential Revision: D3828585

fbshipit-source-id: b76b256
master
Sam Blackshear 9 years ago committed by Facebook Github Bot 2
parent 1e4b4df427
commit e918624076

@ -897,7 +897,7 @@ let get_name_of_objc_block_locals p => {
IList.flatten (IList.flatten vars_sigma) IList.flatten (IList.flatten vars_sigma)
}; };
let remove_abducted_retvars tenv p => let remove_abduced_retvars tenv p =>
/* compute the hpreds and pure atoms reachable from the set of seed expressions in [exps] */ /* compute the hpreds and pure atoms reachable from the set of seed expressions in [exps] */
{ {
let compute_reachable p seed_exps => { let compute_reachable p seed_exps => {
@ -967,31 +967,31 @@ let remove_abducted_retvars tenv p =>
}; };
(Sil.HpredSet.elements reach_hpreds, reach_pi) (Sil.HpredSet.elements reach_hpreds, reach_pi)
}; };
/* separate the abducted pvars from the normal ones, deallocate the abducted ones*/ /* separate the abduced pvars from the normal ones, deallocate the abduced ones*/
let (abducteds, normal_pvars) = let (abduceds, normal_pvars) =
IList.fold_left IList.fold_left
( (
fun pvars hpred => fun pvars hpred =>
switch hpred { switch hpred {
| Sil.Hpointsto (Exp.Lvar pvar) _ _ => | Sil.Hpointsto (Exp.Lvar pvar) _ _ =>
let (abducteds, normal_pvars) = pvars; let (abduceds, normal_pvars) = pvars;
if (Pvar.is_abducted pvar) { if (Pvar.is_abduced pvar) {
([pvar, ...abducteds], normal_pvars) ([pvar, ...abduceds], normal_pvars)
} else { } else {
(abducteds, [pvar, ...normal_pvars]) (abduceds, [pvar, ...normal_pvars])
} }
| _ => pvars | _ => pvars
} }
) )
([], []) ([], [])
p.Prop.sigma; p.Prop.sigma;
let (_, p') = Attribute.deallocate_stack_vars tenv p abducteds; let (_, p') = Attribute.deallocate_stack_vars tenv p abduceds;
let normal_pvar_set = let normal_pvar_set =
IList.fold_left IList.fold_left
(fun normal_pvar_set pvar => Exp.Set.add (Exp.Lvar pvar) normal_pvar_set) (fun normal_pvar_set pvar => Exp.Set.add (Exp.Lvar pvar) normal_pvar_set)
Exp.Set.empty Exp.Set.empty
normal_pvars; normal_pvars;
/* walk forward from non-abducted pvars, keep everything reachable. remove everything else */ /* walk forward from non-abduced pvars, keep everything reachable. remove everything else */
let (sigma_reach, pi_reach) = compute_reachable p' normal_pvar_set; let (sigma_reach, pi_reach) = compute_reachable p' normal_pvar_set;
Prop.normalize tenv (Prop.set p' pi::pi_reach sigma::sigma_reach) Prop.normalize tenv (Prop.set p' pi::pi_reach sigma::sigma_reach)
}; };
@ -1011,7 +1011,7 @@ let remove_locals tenv (curr_f: Procdesc.t) p => {
( (
removed, removed,
if Config.angelic_execution { if Config.angelic_execution {
remove_abducted_retvars tenv p' remove_abduced_retvars tenv p'
} else { } else {
p' p'
} }

@ -24,8 +24,8 @@ let module F = Format;
type pvar_kind = type pvar_kind =
| Local_var of Procname.t /** local variable belonging to a function */ | Local_var of Procname.t /** local variable belonging to a function */
| Callee_var of Procname.t /** local variable belonging to a callee */ | Callee_var of Procname.t /** local variable belonging to a callee */
| Abducted_retvar of Procname.t Location.t /** synthetic variable to represent return value */ | Abduced_retvar of Procname.t Location.t /** synthetic variable to represent return value */
| Abducted_ref_param of Procname.t t Location.t | Abduced_ref_param of Procname.t t Location.t
/** synthetic variable to represent param passed by reference */ /** synthetic variable to represent param passed by reference */
| Global_var /** gloval variable */ | Global_var /** gloval variable */
| Seed_var /** variable used to store the initial value of formal parameters */ | Seed_var /** variable used to store the initial value of formal parameters */
@ -40,16 +40,16 @@ let rec pvar_kind_compare k1 k2 =>
| (Callee_var n1, Callee_var n2) => Procname.compare n1 n2 | (Callee_var n1, Callee_var n2) => Procname.compare n1 n2
| (Callee_var _, _) => (-1) | (Callee_var _, _) => (-1)
| (_, Callee_var _) => 1 | (_, Callee_var _) => 1
| (Abducted_retvar p1 l1, Abducted_retvar p2 l2) => | (Abduced_retvar p1 l1, Abduced_retvar p2 l2) =>
let n = Procname.compare p1 p2; let n = Procname.compare p1 p2;
if (n != 0) { if (n != 0) {
n n
} else { } else {
Location.compare l1 l2 Location.compare l1 l2
} }
| (Abducted_retvar _, _) => (-1) | (Abduced_retvar _, _) => (-1)
| (_, Abducted_retvar _) => 1 | (_, Abduced_retvar _) => 1
| (Abducted_ref_param p1 pv1 l1, Abducted_ref_param p2 pv2 l2) => | (Abduced_ref_param p1 pv1 l1, Abduced_ref_param p2 pv2 l2) =>
let n = Procname.compare p1 p2; let n = Procname.compare p1 p2;
if (n != 0) { if (n != 0) {
n n
@ -61,8 +61,8 @@ let rec pvar_kind_compare k1 k2 =>
Location.compare l1 l2 Location.compare l1 l2
} }
} }
| (Abducted_ref_param _, _) => (-1) | (Abduced_ref_param _, _) => (-1)
| (_, Abducted_ref_param _) => 1 | (_, Abduced_ref_param _) => 1
| (Global_var, Global_var) => 0 | (Global_var, Global_var) => 0
| (Global_var, _) => (-1) | (Global_var, _) => (-1)
| (_, Global_var) => 1 | (_, Global_var) => 1
@ -94,17 +94,17 @@ let rec _pp f pv => {
} else { } else {
F.fprintf f "%a$%a|callee" Procname.pp n Mangled.pp name F.fprintf f "%a$%a|callee" Procname.pp n Mangled.pp name
} }
| Abducted_retvar n l => | Abduced_retvar n l =>
if !Config.pp_simple { if !Config.pp_simple {
F.fprintf f "%a|abductedRetvar" Mangled.pp name F.fprintf f "%a|abducedRetvar" Mangled.pp name
} else { } else {
F.fprintf f "%a$%a%a|abductedRetvar" Procname.pp n Location.pp l Mangled.pp name F.fprintf f "%a$%a%a|abducedRetvar" Procname.pp n Location.pp l Mangled.pp name
} }
| Abducted_ref_param n pv l => | Abduced_ref_param n pv l =>
if !Config.pp_simple { if !Config.pp_simple {
F.fprintf f "%a|%a|abductedRefParam" _pp pv Mangled.pp name F.fprintf f "%a|%a|abducedRefParam" _pp pv Mangled.pp name
} else { } else {
F.fprintf f "%a$%a%a|abductedRefParam" Procname.pp n Location.pp l Mangled.pp name F.fprintf f "%a$%a%a|abducedRefParam" Procname.pp n Location.pp l Mangled.pp name
} }
| Global_var => F.fprintf f "#GB$%a" Mangled.pp name | Global_var => F.fprintf f "#GB$%a" Mangled.pp name
| Seed_var => F.fprintf f "old_%a" Mangled.pp name | Seed_var => F.fprintf f "old_%a" Mangled.pp name
@ -125,22 +125,22 @@ let pp_latex f pv => {
(Mangled.to_string name) (Mangled.to_string name)
(Latex.pp_string Latex.Roman) (Latex.pp_string Latex.Roman)
"callee" "callee"
| Abducted_retvar _ => | Abduced_retvar _ =>
F.fprintf F.fprintf
f f
"%a_{%a}" "%a_{%a}"
(Latex.pp_string Latex.Roman) (Latex.pp_string Latex.Roman)
(Mangled.to_string name) (Mangled.to_string name)
(Latex.pp_string Latex.Roman) (Latex.pp_string Latex.Roman)
"abductedRetvar" "abducedRetvar"
| Abducted_ref_param _ => | Abduced_ref_param _ =>
F.fprintf F.fprintf
f f
"%a_{%a}" "%a_{%a}"
(Latex.pp_string Latex.Roman) (Latex.pp_string Latex.Roman)
(Mangled.to_string name) (Mangled.to_string name)
(Latex.pp_string Latex.Roman) (Latex.pp_string Latex.Roman)
"abductedRefParam" "abducedRefParam"
| Global_var => Latex.pp_string Latex.Boldface f (Mangled.to_string name) | Global_var => Latex.pp_string Latex.Boldface f (Mangled.to_string name)
| Seed_var => | Seed_var =>
F.fprintf F.fprintf
@ -212,10 +212,10 @@ let get_simplified_name pv => {
/** Check if the pvar is an abucted return var or param passed by ref */ /** Check if the pvar is an abucted return var or param passed by ref */
let is_abducted pv => let is_abduced pv =>
switch pv.pv_kind { switch pv.pv_kind {
| Abducted_retvar _ | Abduced_retvar _
| Abducted_ref_param _ => true | Abduced_ref_param _ => true
| _ => false | _ => false
}; };
@ -287,8 +287,8 @@ let to_callee pname pvar =>
| Local_var _ => {...pvar, pv_kind: Callee_var pname} | Local_var _ => {...pvar, pv_kind: Callee_var pname}
| Global_var => pvar | Global_var => pvar
| Callee_var _ | Callee_var _
| Abducted_retvar _ | Abduced_retvar _
| Abducted_ref_param _ | Abduced_ref_param _
| Seed_var => | Seed_var =>
L.d_str "Cannot convert pvar to callee: "; L.d_str "Cannot convert pvar to callee: ";
d pvar; d pvar;
@ -326,13 +326,13 @@ let mk_tmp name pname => {
}; };
/** create an abducted return variable for a call to [proc_name] at [loc] */ /** create an abduced return variable for a call to [proc_name] at [loc] */
let mk_abducted_ret (proc_name: Procname.t) (loc: Location.t) :t => { let mk_abduced_ret (proc_name: Procname.t) (loc: Location.t) :t => {
let name = Mangled.from_string ("$RET_" ^ Procname.to_unique_id proc_name); let name = Mangled.from_string ("$RET_" ^ Procname.to_unique_id proc_name);
{pv_name: name, pv_kind: Abducted_retvar proc_name loc} {pv_name: name, pv_kind: Abduced_retvar proc_name loc}
}; };
let mk_abducted_ref_param (proc_name: Procname.t) (pv: t) (loc: Location.t) :t => { let mk_abduced_ref_param (proc_name: Procname.t) (pv: t) (loc: Location.t) :t => {
let name = Mangled.from_string ("$REF_PARAM_" ^ Procname.to_unique_id proc_name); let name = Mangled.from_string ("$REF_PARAM_" ^ Procname.to_unique_id proc_name);
{pv_name: name, pv_kind: Abducted_ref_param proc_name pv loc} {pv_name: name, pv_kind: Abduced_ref_param proc_name pv loc}
}; };

@ -55,8 +55,8 @@ let get_ret_pvar: Procname.t => t;
let get_simplified_name: t => string; let get_simplified_name: t => string;
/** Check if the pvar is an abducted return var or param passed by ref */ /** Check if the pvar is an abduced return var or param passed by ref */
let is_abducted: t => bool; let is_abduced: t => bool;
/** Check if the pvar is a callee var */ /** Check if the pvar is a callee var */
@ -91,12 +91,12 @@ let is_frontend_tmp: t => bool;
let mk: Mangled.t => Procname.t => t; let mk: Mangled.t => Procname.t => t;
/** create an abducted variable for a parameter passed by reference */ /** create an abduced variable for a parameter passed by reference */
let mk_abducted_ref_param: Procname.t => t => Location.t => t; let mk_abduced_ref_param: Procname.t => t => Location.t => t;
/** create an abducted return variable for a call to [proc_name] at [loc] */ /** create an abduced return variable for a call to [proc_name] at [loc] */
let mk_abducted_ret: Procname.t => Location.t => t; let mk_abduced_ret: Procname.t => Location.t => t;
/** [mk_callee name proc_name] creates a program var /** [mk_callee name proc_name] creates a program var

@ -981,7 +981,7 @@ let explain_nth_function_parameter tenv use_buckets deref_str prop n pvar_off =
let find_with_exp prop exp = let find_with_exp prop exp =
let res = ref None in let res = ref None in
let found_in_pvar pv = let found_in_pvar pv =
if not (Pvar.is_abducted pv) && not (Pvar.is_this pv) then if not (Pvar.is_abduced pv) && not (Pvar.is_this pv) then
res := Some (pv, Fpvar) in res := Some (pv, Fpvar) in
let found_in_struct pv fld_lst = (* found_in_pvar has priority *) let found_in_struct pv fld_lst = (* found_in_pvar has priority *)
if !res = None then res := Some (pv, Fstruct (IList.rev fld_lst)) in if !res = None then res := Some (pv, Fstruct (IList.rev fld_lst)) in

@ -842,7 +842,7 @@ let collect_postconditions wl tenv pdesc : Paths.PathSet.t * Specs.Visitedset.t
let create_seed_vars sigma = let create_seed_vars sigma =
let hpred_add_seed sigma = function let hpred_add_seed sigma = function
| Sil.Hpointsto (Exp.Lvar pv, se, typ) when not (Pvar.is_abducted pv) -> | Sil.Hpointsto (Exp.Lvar pv, se, typ) when not (Pvar.is_abduced pv) ->
Sil.Hpointsto(Exp.Lvar (Pvar.to_seed pv), se, typ) :: sigma Sil.Hpointsto(Exp.Lvar (Pvar.to_seed pv), se, typ) :: sigma
| _ -> sigma in | _ -> sigma in
IList.fold_left hpred_add_seed [] sigma IList.fold_left hpred_add_seed [] sigma

@ -789,27 +789,28 @@ let do_error_checks tenv node_opt instr pname pdesc = match node_opt with
| None -> | None ->
() ()
let add_strexp_to_footprint tenv strexp abducted_pv typ prop = let add_strexp_to_footprint tenv strexp abduced_pv typ prop =
let abducted_lvar = Exp.Lvar abducted_pv in let abduced_lvar = Exp.Lvar abduced_pv in
let lvar_pt_fpvar = let lvar_pt_fpvar =
let sizeof_exp = Exp.Sizeof (typ, None, Subtype.subtypes) in let sizeof_exp = Exp.Sizeof (typ, None, Subtype.subtypes) in
Prop.mk_ptsto tenv abducted_lvar strexp sizeof_exp in Prop.mk_ptsto tenv abduced_lvar strexp sizeof_exp in
let sigma_fp = prop.Prop.sigma_fp in let sigma_fp = prop.Prop.sigma_fp in
Prop.normalize tenv (Prop.set prop ~sigma_fp:(lvar_pt_fpvar :: sigma_fp)) Prop.normalize tenv (Prop.set prop ~sigma_fp:(lvar_pt_fpvar :: sigma_fp))
let add_to_footprint tenv abducted_pv typ prop = let add_to_footprint tenv abduced_pv typ prop =
let fresh_fp_var = Exp.Var (Ident.create_fresh Ident.kfootprint) in let fresh_fp_var = Exp.Var (Ident.create_fresh Ident.kfootprint) in
let prop' = add_strexp_to_footprint tenv (Sil.Eexp (fresh_fp_var, Sil.Inone)) abducted_pv typ prop in let prop' =
add_strexp_to_footprint tenv (Sil.Eexp (fresh_fp_var, Sil.Inone)) abduced_pv typ prop in
prop', fresh_fp_var prop', fresh_fp_var
(* the current abduction mechanism treats struct values differently than all other types. abduction (* the current abduction mechanism treats struct values differently than all other types. abduction
on struct values adds a a struct whose fields are initialized to fresh footprint vars to the on struct values adds a a struct whose fields are initialized to fresh footprint vars to the
footprint. regular abduction just adds a fresh footprint value of the correct type to the footprint. regular abduction just adds a fresh footprint value of the correct type to the
footprint. we can get rid of this special case if we fix the abduction on struct values *) footprint. we can get rid of this special case if we fix the abduction on struct values *)
let add_struct_value_to_footprint tenv abducted_pv typ prop = let add_struct_value_to_footprint tenv abduced_pv typ prop =
let struct_strexp = let struct_strexp =
Prop.create_strexp_of_type tenv Prop.Fld_init typ None Sil.inst_none in Prop.create_strexp_of_type tenv Prop.Fld_init typ None Sil.inst_none in
let prop' = add_strexp_to_footprint tenv struct_strexp abducted_pv typ prop in let prop' = add_strexp_to_footprint tenv struct_strexp abduced_pv typ prop in
prop', struct_strexp prop', struct_strexp
let add_constraints_on_retval tenv pdesc prop ret_exp ~has_nullable_annot typ callee_pname callee_loc= let add_constraints_on_retval tenv pdesc prop ret_exp ~has_nullable_annot typ callee_pname callee_loc=
@ -817,17 +818,17 @@ let add_constraints_on_retval tenv pdesc prop ret_exp ~has_nullable_annot typ ca
else else
let is_rec_call pname = (* TODO: (t7147096) extend this to detect mutual recursion *) let is_rec_call pname = (* TODO: (t7147096) extend this to detect mutual recursion *)
Procname.equal pname (Cfg.Procdesc.get_proc_name pdesc) in Procname.equal pname (Cfg.Procdesc.get_proc_name pdesc) in
let already_has_abducted_retval p abducted_ret_pv = let already_has_abduced_retval p abduced_ret_pv =
IList.exists IList.exists
(fun hpred -> match hpred with (fun hpred -> match hpred with
| Sil.Hpointsto (Exp.Lvar pv, _, _) -> Pvar.equal pv abducted_ret_pv | Sil.Hpointsto (Exp.Lvar pv, _, _) -> Pvar.equal pv abduced_ret_pv
| _ -> false) | _ -> false)
p.Prop.sigma_fp in p.Prop.sigma_fp in
(* find an hpred [abducted] |-> A in [prop] and add [exp] = A to prop *) (* find an hpred [abduced] |-> A in [prop] and add [exp] = A to prop *)
let bind_exp_to_abducted_val exp_to_bind abducted prop = let bind_exp_to_abduced_val exp_to_bind abduced prop =
let bind_exp prop = function let bind_exp prop = function
| Sil.Hpointsto (Exp.Lvar pv, Sil.Eexp (rhs, _), _) | Sil.Hpointsto (Exp.Lvar pv, Sil.Eexp (rhs, _), _)
when Pvar.equal pv abducted -> when Pvar.equal pv abduced ->
Prop.conjoin_eq tenv exp_to_bind rhs prop Prop.conjoin_eq tenv exp_to_bind rhs prop
| _ -> prop in | _ -> prop in
IList.fold_left bind_exp prop prop.Prop.sigma in IList.fold_left bind_exp prop prop.Prop.sigma in
@ -845,17 +846,17 @@ let add_constraints_on_retval tenv pdesc prop ret_exp ~has_nullable_annot typ ca
if Config.angelic_execution && not (is_rec_call callee_pname) then if Config.angelic_execution && not (is_rec_call callee_pname) then
(* introduce a fresh program variable to allow abduction on the return value *) (* introduce a fresh program variable to allow abduction on the return value *)
let abducted_ret_pv = Pvar.mk_abducted_ret callee_pname callee_loc in let abduced_ret_pv = Pvar.mk_abduced_ret callee_pname callee_loc in
(* prevent introducing multiple abducted retvals for a single call site in a loop *) (* prevent introducing multiple abduced retvals for a single call site in a loop *)
if already_has_abducted_retval prop abducted_ret_pv then prop if already_has_abduced_retval prop abduced_ret_pv then prop
else else
let prop' = let prop' =
if !Config.footprint then if !Config.footprint then
let (prop', fresh_fp_var) = add_to_footprint tenv abducted_ret_pv typ prop in let (prop', fresh_fp_var) = add_to_footprint tenv abduced_ret_pv typ prop in
Prop.conjoin_eq tenv ~footprint: true ret_exp fresh_fp_var prop' Prop.conjoin_eq tenv ~footprint: true ret_exp fresh_fp_var prop'
else else
(* bind return id to the abducted value pointed to by the pvar we introduced *) (* bind return id to the abduced value pointed to by the pvar we introduced *)
bind_exp_to_abducted_val ret_exp abducted_ret_pv prop in bind_exp_to_abduced_val ret_exp abduced_ret_pv prop in
let prop'' = add_ret_non_null ret_exp typ prop' in let prop'' = add_ret_non_null ret_exp typ prop' in
if Config.taint_analysis then if Config.taint_analysis then
match Taint.returns_tainted callee_pname None with match Taint.returns_tainted callee_pname None with
@ -1291,27 +1292,27 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call
match actual with match actual with
| Exp.Lvar actual_pv -> | Exp.Lvar actual_pv ->
(* introduce a fresh program variable to allow abduction on the return value *) (* introduce a fresh program variable to allow abduction on the return value *)
let abducted_ref_pv = let abduced_ref_pv =
Pvar.mk_abducted_ref_param callee_pname actual_pv callee_loc in Pvar.mk_abduced_ref_param callee_pname actual_pv callee_loc in
let already_has_abducted_retval p = let already_has_abduced_retval p =
IList.exists IList.exists
(fun hpred -> match hpred with (fun hpred -> match hpred with
| Sil.Hpointsto (Exp.Lvar pv, _, _) -> Pvar.equal pv abducted_ref_pv | Sil.Hpointsto (Exp.Lvar pv, _, _) -> Pvar.equal pv abduced_ref_pv
| _ -> false) | _ -> false)
p.Prop.sigma_fp in p.Prop.sigma_fp in
(* prevent introducing multiple abducted retvals for a single call site in a loop *) (* prevent introducing multiple abduced retvals for a single call site in a loop *)
if already_has_abducted_retval prop then prop if already_has_abduced_retval prop then prop
else else
if !Config.footprint then if !Config.footprint then
let prop', abduced_strexp = match actual_typ with let prop', abduced_strexp = match actual_typ with
| Typ.Tptr ((Typ.Tstruct _) as typ, _) -> | Typ.Tptr ((Typ.Tstruct _) as typ, _) ->
(* for struct types passed by reference, do abduction on the fields of the (* for struct types passed by reference, do abduction on the fields of the
struct *) struct *)
add_struct_value_to_footprint tenv abducted_ref_pv typ prop add_struct_value_to_footprint tenv abduced_ref_pv typ prop
| Typ.Tptr (typ, _) -> | Typ.Tptr (typ, _) ->
(* for pointer types passed by reference, do abduction directly on the pointer *) (* for pointer types passed by reference, do abduction directly on the pointer *)
let (prop', fresh_fp_var) = let (prop', fresh_fp_var) =
add_to_footprint tenv abducted_ref_pv typ prop in add_to_footprint tenv abduced_ref_pv typ prop in
prop', Sil.Eexp (fresh_fp_var, Sil.Inone) prop', Sil.Eexp (fresh_fp_var, Sil.Inone)
| typ -> | typ ->
failwith failwith
@ -1327,7 +1328,7 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call
prop'.Prop.sigma in prop'.Prop.sigma in
Prop.normalize tenv (Prop.set prop' ~sigma:filtered_sigma) Prop.normalize tenv (Prop.set prop' ~sigma:filtered_sigma)
else else
(* bind actual passed by ref to the abducted value pointed to by the synthetic pvar *) (* bind actual passed by ref to the abduced value pointed to by the synthetic pvar *)
let prop' = let prop' =
let filtered_sigma = let filtered_sigma =
IList.filter IList.filter
@ -1340,7 +1341,7 @@ and add_constraints_on_actuals_by_ref tenv prop actuals_by_ref callee_pname call
IList.fold_left IList.fold_left
(fun p hpred -> (fun p hpred ->
match hpred with match hpred with
| Sil.Hpointsto (Exp.Lvar pv, rhs, texp) when Pvar.equal pv abducted_ref_pv -> | Sil.Hpointsto (Exp.Lvar pv, rhs, texp) when Pvar.equal pv abduced_ref_pv ->
let new_hpred = Sil.Hpointsto (actual, rhs, texp) in let new_hpred = Sil.Hpointsto (actual, rhs, texp) in
Prop.normalize tenv (Prop.set p ~sigma:(new_hpred :: prop'.Prop.sigma)) Prop.normalize tenv (Prop.set p ~sigma:(new_hpred :: prop'.Prop.sigma))
| _ -> p) | _ -> p)

Loading…
Cancel
Save