diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.ml b/src/plugins/e-acsl/src/analyses/bound_variables.ml index 114c9183be103c8c010293411078dfeddb5c403b..60044fdeb1fc9f7cf777c8b23fb15c7d2d686873 100644 --- a/src/plugins/e-acsl/src/analyses/bound_variables.ml +++ b/src/plugins/e-acsl/src/analyses/bound_variables.ml @@ -46,6 +46,77 @@ let error_msg quantif msg pp x = in msg1 ^ msg2 + +(** A module to work with quantifiers at preprocessing time. We create + a datatype to hash quantified predicates by simply giving the hash + of their first variable. +*) +module Quantified_predicate = + Datatype.Make_with_collections + (struct + include Cil_datatype.Predicate + let name = "E_ACSL.Quantified_predicate" + let hash (p:predicate) = match p.pred_content with + | Pforall(lvs,_) + | Pexists(lvs,_) -> + begin match lvs with + |[] -> 0 + |lv::_ -> Cil_datatype.Logic_var.hash lv + end + | _ -> assert false + let compare (p1:predicate) p2 = + if p1 == p2 then 0 else 1 + let equal (p1:predicate) p2 = p1 == p2 + let structural_descr = Structural_descr.t_abstract + let rehash = Datatype.identity + let mem_project = Datatype.never_any_project + end) + +(******************************************************************************) +(** Storing the preprocessed quantifiers *) +(******************************************************************************) + +(** In order to retrieve the preprocessed form of a quantified predicate + * we store it in a hash table. *) +module Quantifier: sig + val add: predicate -> (term * logic_var * term) list -> predicate -> unit + val get: predicate -> (term * logic_var * term) list * predicate + (** getter and setter for the additional guard that intersects with the type + of the variable *) + val get_guard_for_small_type : logic_var -> predicate option + val add_guard_for_small_type : logic_var -> predicate option -> unit +end = struct + + let tbl = Quantified_predicate.Hashtbl.create 97 + + let guard_tbl = Cil_datatype.Logic_var.Hashtbl.create 97 + + let get p = + (* Options.feedback "Get preprocessed form of predicate %a" Printer.pp_predicate p; *) + try Quantified_predicate.Hashtbl.find tbl p + with Not_found -> + Options.fatal + "The preprocessed form of predicate %a was not found." + Printer.pp_predicate p + + let add p guarded_vars goal = + (* Options.feedback "Adding preprocessed form of predicate %a" Printer.pp_predicate p; *) + Quantified_predicate.Hashtbl.add tbl p (guarded_vars, goal) + + let get_guard_for_small_type x = + try Cil_datatype.Logic_var.Hashtbl.find guard_tbl x + with Not_found -> + Options.fatal + "The typing guard for the variable %a was not found." + Printer.pp_logic_var x + + let add_guard_for_small_type lv p = + Cil_datatype.Logic_var.Hashtbl.add guard_tbl lv p +end + +let get_preprocessed_quantifier = Quantifier.get +let get_guard_for_small_type = Quantifier.get_guard_for_small_type + (** Helper module to process the constraints in the quantification and extract guards for the quantified variables. *) module Constraints: sig @@ -561,14 +632,12 @@ let compute_quantif_guards ~is_forall quantif bounded_vars p = - Case 3: B \not\subseteq R and the bounds of B are NOT inferred exactly Example: [\let m = n > 0 ? 4 : 341; \forall char u; 1 < u < m ==> u > 0] Return: R with a guard guaranteeing that [lv] does not overflow *) -let bounds_for_small_type ~loc (t1, lv, t2) = +let bounds_for_small_types ~loc (t1, lv, t2) = match lv.lv_type with | Ltype _ | Lvar _ | Lreal | Larrow _ -> - Options.abort "quantification over non-integer type is not part of E-ACSL" - + Error.not_yet "quantification over non-integer type" | Linteger -> t1, t2, None - | Ctype ty -> let iv1 = Interval.(extract_ival (infer t1)) in let iv2 = Interval.(extract_ival (infer t2)) in @@ -576,7 +645,7 @@ let bounds_for_small_type ~loc (t1, lv, t2) = Eg: (Ival.join [-3..-3] [300..300]) gives {-3, 300} but NOT [-3..300] *) let iv = Ival.inject_range (Ival.min_int iv1) (Ival.max_int iv2) in - let ity = Interval.extract_ival (Interval.interv_of_typ ty) in + let ity = Interval.extract_ival (Interval.extended_interv_of_typ ty) in if Ival.is_included iv ity then (* case 1 *) t1, t2, None @@ -603,8 +672,45 @@ let bounds_for_small_type ~loc (t1, lv, t2) = let guard_upper = Logic_const.tint ~loc max in let lv_term = Logic_const.tvar ~loc lv in let guard_lower = Logic_const.prel ~loc (Rle, guard_lower, lv_term) in - let guard_upper = Logic_const.prel ~loc (Rle, lv_term, guard_upper) in + let guard_upper = Logic_const.prel ~loc (Rlt, lv_term, guard_upper) in let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in t1, t2, Some guard let () = Typing.compute_quantif_guards_ref := compute_quantif_guards + +(** Take a guard and put it in normal form + [t1 <= x < t2] so that [t1] is the first value + taken by [x] and [t2] is the last one. + This step also adds the extra guard if the type + of the quantified variable is small *) +let normalize_guard ~loc (t1, rel1, lv, rel2, t2) = + let t_plus_one t = + let tone = Cil.lone ~loc () in + Logic_const.term ~loc (TBinOp(PlusA, t, tone)) Linteger + in + (* this function only manipulate guards that were already + processed by compute_quantif_guards, which only outputs + guards with the constructors Rlt and Rle*) + let t1 = match rel1 with + | Rlt -> + t_plus_one t1 + | Rle -> + t1 + | Rgt | Rge | Req | Rneq -> + assert false in + let t2 = match rel2 with + | Rlt -> + t2 + | Rle -> + t_plus_one t2 + | Rgt | Rge | Req | Rneq -> + assert false in + let t1, t2, guard_for_small_type = bounds_for_small_types ~loc (t1, lv, t2) in + Quantifier.add_guard_for_small_type lv guard_for_small_type; + t1, lv, t2 + +let compute_guards loc ~is_forall p bounded_vars hyps = + let guards,goal = compute_quantif_guards p ~is_forall bounded_vars hyps in + (* transform [guards] into [lscope_var list] *) + let normalized_guards = List.map (normalize_guard ~loc) guards + in Quantifier.add p normalized_guards goal diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.mli b/src/plugins/e-acsl/src/analyses/bound_variables.mli index 73faeaec2c0144846c01a155bd7ba3d31c3652ed..33c0581957e2475778865b7dd0a4cbb8920816fe 100644 --- a/src/plugins/e-acsl/src/analyses/bound_variables.mli +++ b/src/plugins/e-acsl/src/analyses/bound_variables.mli @@ -20,3 +20,14 @@ (* *) (**************************************************************************) +open Cil_types +open Cil_datatype + + +val compute_guards: location -> is_forall:bool -> predicate -> Logic_var.t list -> predicate -> unit +(** Takes a predicate starting with a quantifier and store the scope of its variable *) + +val get_preprocessed_quantifier: predicate -> (term * logic_var * term) list * predicate +(** Returns the preprocessed form of a quantifier *) + +val get_guard_for_small_type : logic_var -> predicate option diff --git a/src/plugins/e-acsl/src/analyses/interval.ml b/src/plugins/e-acsl/src/analyses/interval.ml index ec1f3a3da2641700bb4a10d49969c8da2bd7ce71..1f817c3d3457620a9c0d64458cab9a5a41d0fa4e 100644 --- a/src/plugins/e-acsl/src/analyses/interval.ml +++ b/src/plugins/e-acsl/src/analyses/interval.ml @@ -231,6 +231,17 @@ let rec interv_of_typ ty = match Cil.unrollType ty with | TNamed _ -> assert false +let rec extended_interv_of_typ ty = match interv_of_typ ty with + | Ival iv -> + let l,u = Ival.min_int iv, Ival.max_int iv in + let u = match u with + | Some u -> Some (Integer.add u Integer.one) + | None -> None + in + Ival (Ival.inject_range l u); + | Rational | Real | Nan | Float (_,_) as i + -> i + (* Compute the interval of the extended quantifier \sum, \product and \numof. [lbd_ival] is the interval of the lambda term, [k_ival] is the interval of the quantifier and [name] is the identifier of the extended quantifier (\sum, diff --git a/src/plugins/e-acsl/src/analyses/interval.mli b/src/plugins/e-acsl/src/analyses/interval.mli index d2b95afa0d1f52943af02ee78e2378fe6096b415..a06fa4145334c5b5670925a1bd116e3281573acb 100644 --- a/src/plugins/e-acsl/src/analyses/interval.mli +++ b/src/plugins/e-acsl/src/analyses/interval.mli @@ -79,6 +79,13 @@ val interv_of_typ: Cil_types.typ -> t @raise Is_a_real if the given type is a float type. @raise Not_a_number if the given type does not represent any number. *) +val extended_interv_of_typ: Cil_types.typ -> t +(** @return the interval [n..m+1] when interv_of_typ returns [n..m]. + It is in particular useful for computing bounds of quantified variables. + @raise Is_a_real if the given type is a float type. + @raise Not_a_number if the given type does not represent any number. *) + + (* ************************************************************************** *) (** {3 Environment for interval computations} *) (* ************************************************************************** *)