diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.ml b/src/plugins/e-acsl/src/analyses/bound_variables.ml index 88e38a22e7215954cce28f7f9847f6fc73eb96f1..b1c754cfc2e00144cf62369d8095d9d261a7871c 100644 --- a/src/plugins/e-acsl/src/analyses/bound_variables.ml +++ b/src/plugins/e-acsl/src/analyses/bound_variables.ml @@ -79,13 +79,14 @@ module Quantified_predicate = (******************************************************************************) (* Memoization module to store the preprocessed form of a quantified predicate *) -module Preprocessed_quantifier: sig +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 + val add_guard_for_small_type : logic_var -> predicate -> unit + val replace : predicate -> (term * logic_var * term) list -> predicate -> unit val clear : unit -> unit end = struct @@ -94,7 +95,6 @@ end = struct 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 @@ -102,28 +102,31 @@ end = struct 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 + try Some (Cil_datatype.Logic_var.Hashtbl.find guard_tbl x) + with Not_found -> None let add_guard_for_small_type lv p = Cil_datatype.Logic_var.Hashtbl.add guard_tbl lv p + let replace p guarded_vars goal = + Quantified_predicate.Hashtbl.replace tbl p (guarded_vars, goal) + let clear () = Cil_datatype.Logic_var.Hashtbl.clear guard_tbl; Quantified_predicate.Hashtbl.clear tbl end +(** Getters and setters *) let get_preprocessed_quantifier = Quantifier.get let get_guard_for_small_type = Quantifier.get_guard_for_small_type +let add_guard_for_small_type = Quantifier.add_guard_for_small_type +let replace = Quantifier.replace let clear_guards = Quantifier.clear + (** Helper module to process the constraints in the quantification and extract guards for the quantified variables. *) module Constraints: sig @@ -628,13 +631,9 @@ let compute_quantif_guards ~is_forall quantif bounded_vars p = guards, goal -let () = Typing.compute_quantif_guards_ref := compute_quantif_guards - -(** Take a guard and put it in normal form +(** Takes 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 *) + taken by [x] and [t2] is the last one. *) let normalize_guard ~loc (t1, rel1, lv, rel2, t2) = let t_plus_one t = let tone = Cil.lone ~loc () in @@ -659,12 +658,100 @@ let normalize_guard ~loc (t1, rel1, lv, rel2, 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 + +module Preprocessor : sig + val compute : file -> unit + val compute_annot : code_annotation -> unit + val compute_predicate : predicate -> unit +end += struct + + let process_quantif ~loc p = + Cil.CurrentLoc.set loc; + match p.pred_content with + | Pforall(bound_vars, ({ pred_content = Pimplies(_, _) } as goal)) -> + compute_guards loc ~is_forall:true p bound_vars goal + | Pexists(bound_vars, ({ pred_content = Pand(_, _) } as goal)) -> + compute_guards loc ~is_forall:false p bound_vars goal + | Pforall _ -> Error.not_yet "unguarded \\forall quantification" + | Pexists _ -> Error.not_yet "unguarded \\exists quantification" + | _ -> () + + let preprocessor = object + inherit Visitor.frama_c_inplace + + (* Only logic functions and logic predicates are handled. + E-acsl simply ignores all the other global annotations *) + method !vannotation annot = + match annot with + | Dfun_or_pred _ -> Cil.DoChildren + | _ -> Cil.SkipChildren + + (* Ignore the annotations attached to statements from the RTL *) + method !vglob_aux = + function + (* library functions and built-ins *) + | GVarDecl(vi, _) | GVar(vi, _, _) + | GFunDecl(_, vi, _) | GFun({ svar = vi }, _) when Builtins.mem vi.vname -> + Cil.SkipChildren + + | GVarDecl(vi, _) | GVar(vi, _, _) | GFun({ svar = vi }, _) + when Misc.is_fc_or_compiler_builtin vi -> + Cil.SkipChildren + | g when Rtl.Symbols.mem_global g -> + Cil.SkipChildren + + (* generated function declaration: nothing to do *) + | GFunDecl(_, vi, _) when Misc.is_fc_stdlib_generated vi -> + Cil.SkipChildren + + | GFun({svar = vi}, _) -> + let kf = try Globals.Functions.get vi with Not_found -> assert false in + if Functions.check kf then Cil.DoChildren else Cil.SkipChildren + + | GAnnot _ -> Cil.DoChildren + + (* other globals: nothing to do *) + | GFunDecl _ + | GVarDecl _ + | GVar _ + | GType _ + | GCompTag _ + | GCompTagDecl _ + | GEnumTag _ + | GEnumTagDecl _ + | GAsm _ + | GPragma _ + | GText _ + -> Cil.SkipChildren + + method !vpredicate p = + let loc = p.pred_loc in + match Preprocess_predicates.get_preprocessed_form p with + | PoT_pred p -> Error.generic_handle (process_quantif ~loc) () p; + Cil.DoChildren + | PoT_term _ -> Cil.DoChildren + + end + + let compute ast = + Visitor.visitFramacFileSameGlobals preprocessor ast + + let compute_annot annot = + ignore (Visitor.visitFramacCodeAnnotation preprocessor annot) + + let compute_predicate p = + ignore (Visitor.visitFramacPredicate preprocessor p) +end + +let preprocess = Preprocessor.compute +let preprocess_annot = Preprocessor.compute_annot +let preprocess_predicate = Preprocessor.compute_predicate diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.mli b/src/plugins/e-acsl/src/analyses/bound_variables.mli index bd32dc7b576334137289266931c9fb51f6d99257..51041a1f1915f157c24b903a486582b1b06fa502 100644 --- a/src/plugins/e-acsl/src/analyses/bound_variables.mli +++ b/src/plugins/e-acsl/src/analyses/bound_variables.mli @@ -33,10 +33,27 @@ val get_preprocessed_quantifier: predicate -> (term * logic_var * term) list * p along with their syntactic guards, and the [predicate] is the goal: the original predicate with all the quantifiers removed *) +val add_guard_for_small_type : logic_var -> predicate -> unit +(** Adds an optional additional guard condition that comes from the typing *) + val get_guard_for_small_type : logic_var -> predicate option (** @return the optional additional guard for a quantified logic variables. It may happen that the syntactic guard of the variable can be refined with the type of the variable, this additional predicate translates this refinement *) +val replace : predicate -> (term * logic_var * term) list -> predicate -> unit +(** Replace the computed guards. This is because the typing sometimes simplifies the + computed bounds, so we allow for storing new bounds *) + val clear_guards : unit -> unit (** Clear the table of guard conditions for quantified variables *) + +val preprocess : file -> unit +(** Preprocess all the quantified predicates in the ast and store the result + in an hashtable *) + +val preprocess_annot : code_annotation -> unit +(** Preprocess the quantified in a given code annotation *) + +val preprocess_predicate : predicate -> unit +(** Preprocess the quantified predicate in a given predicate *) diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index 0f1094d579d4bad8f02c6bb3feca1c84d8ec3678..7e6a9b3c90ce19623dac955393040fbebc70b64a 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -595,8 +595,10 @@ and type_term_offset = function let ctx = type_offset t in ignore (type_term ~use_gmp_opt:false ~ctx t); type_term_offset toff -(* It could happen that the bounds provided for a quantifier [lv] are bigger - than its type. [bounds_for_small_type] handles such cases +(* [type_bound_variables] infers an interval associated with each of + the provided bounds of a quantified variable, and provides a term + accordingly. It could happen that the bounds provided for a quantifier + [lv] are bigger than its type. [type_bound_variables] handles such cases and provides smaller bounds whenever possible. Let B be the inferred interval and R the range of [lv.typ] - Case 1: B \subseteq R @@ -608,49 +610,79 @@ and type_term_offset = function - 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_types ~loc (t1, lv, t2) = - match lv.lv_type with - | Ltype _ | Lvar _ | Lreal | Larrow _ -> - 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 - (* Ival.join is NOT correct here: - 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.extended_interv_of_typ ty) in - if Ival.is_included iv ity then - (* case 1 *) - t1, t2, None - else if Ival.is_singleton_int iv1 && Ival.is_singleton_int iv2 then begin - (* case 2 *) - let i = Ival.meet iv ity in - (* now we potentially have a better interval for [lv] - ==> update the binding *) - Interval.Env.replace lv (Interval.Ival i); - (* the smaller bounds *) - let min, max = Misc.finite_min_and_max i in - let t1 = Logic_const.tint ~loc min in - let t2 = Logic_const.tint ~loc max in - let ctx = Typing.number_ty_of_typ ~post:false ty in - (* we are assured that we will not have a GMP, - once again because we intersected with [ity] *) - Typing.type_term ~use_gmp_opt:false ~ctx t1; - Typing.type_term ~use_gmp_opt:false ~ctx t2; - t1, t2, None - end else - (* case 3 *) - let min, max = Misc.finite_min_and_max ity in - let guard_lower = Logic_const.tint ~loc min in - 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 (Rlt, lv_term, guard_upper) in - let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in - t1, t2, Some guard +let type_bound_variables ~loc ~dep ~lenv (t1, lv, t2) = + let i1 = Interval.(extract_ival (infer t1)) in + let i2 = Interval.(extract_ival (infer t2)) in + (* Ival.join is NOT correct here: + Eg: (Ival.join [-3..-3] [300..300]) gives {-3, 300} + but NOT [-3..300] *) + let i = Ival.inject_range (Ival.min_int i1) (Ival.max_int i2) in + (* let i = Interval.join i1 i2 in *) + let ctx = match lv.lv_type with + | Linteger -> mk_ctx ~use_gmp_opt:true (ty_of_interv ~ctx:Gmpz (Interval.Ival i)) + | Ctype ty -> + (match Cil.unrollType ty with + | TInt(ik, _) -> mk_ctx ~use_gmp_opt:true (C_integer ik) + | ty -> + Options.fatal "unexpected type %a for quantified variable %a" + Printer.pp_typ ty + Printer.pp_logic_var lv) + | lty -> + Options.fatal "unexpected type %a for quantified variable %a" + Printer.pp_logic_type lty + Printer.pp_logic_var lv + in + let t1, t2, i = + match lv.lv_type with + | Ltype _ | Lvar _ | Lreal | Larrow _ -> + Error.not_yet "quantification over non-integer type" + | Linteger -> t1, t2, i + | Ctype ty -> + (* let iv1 = Interval.(extract_ival (infer t1)) in + * let iv2 = Interval.(extract_ival (infer t2)) in + * (\* Ival.join is NOT correct here: + * 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.extended_interv_of_typ ty) in + if Ival.is_included i ity then + (* case 1 *) + t1, t2, i + else if Ival.is_singleton_int i1 && Ival.is_singleton_int i2 then begin + (* case 2 *) + let i = Ival.meet i ity in + (* We can now update the bounds in the preprocessed form + that come from the meet of the two intervals *) + let min, max = Misc.finite_min_and_max i in + let t1 = Logic_const.tint ~loc min in + let t2 = Logic_const.tint ~loc max in + t1, t2, i + end else + (* case 3 *) + let min, max = Misc.finite_min_and_max ity in + let guard_lower = Logic_const.tint ~loc min in + 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 (Rlt, lv_term, guard_upper) in + let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in + Bound_variables.add_guard_for_small_type lv guard; + t1, t2, i + in + (* forcing when typing bounds prevents to generate an extra useless + GMP variable when --e-acsl-gmp-only *) + ignore (type_term ~use_gmp_opt:false ~ctx ~dep ~lenv t1); + ignore (type_term ~use_gmp_opt:false ~ctx ~dep ~lenv t2); + (* if we must generate GMP code, degrade the interval in order to + guarantee that [x] will be a GMP when typing the goal *) + let i = match ctx with + | C_integer _ -> i + | Gmpz -> Ival.inject_range None None (* [ -\infty; +\infty ] *) + | C_float _ | Rational | Real | Nan -> + Options.fatal "unexpected quantification over %a" D.pretty ctx + in + Interval.Env.add lv (Interval.Ival i); + (t1, lv, t2) let rec type_predicate p = Cil.CurrentLoc.set p.pred_loc; @@ -709,66 +741,18 @@ let rec type_predicate p = ignore (type_term ~use_gmp_opt:true li_t); (type_predicate p).ty - | Pforall(bounded_vars, ({ pred_content = Pimplies(_, _) } as p')) - | Pexists(bounded_vars, ({ pred_content = Pand(_, _) } as p')) -> - let is_forall = - match p.pred_content with - | Pforall _ -> true - | Pexists _ -> false - | _ -> assert false - in + + | Pforall(_, { pred_content = Pimplies(_, _) }) + | Pexists(_, { pred_content = Pand(_, _) }) -> let guards, goal = - !compute_quantif_guards_ref ~is_forall p bounded_vars p' - in - let iv_plus_one iv = - Interval.Ival (Ival.add_singleton_int Integer.one iv) + Bound_variables.get_preprocessed_quantifier p in - List.iter - (fun (t1, r1, x, r2, t2) -> - let i1 = Interval.infer t1 in - let i1 = match r1, i1 with - | Rlt, Interval.Ival iv -> iv_plus_one iv - | Rle, _ -> i1 - | _ -> assert false - in - let i2 = Interval.infer t2 in - (* add one to [i2], since we increment the loop counter one more - time before going outside the loop. *) - let i2 = match r2, i2 with - | Rlt, _ -> i2 - | Rle, Interval.Ival iv -> iv_plus_one iv - | _ -> assert false - in - let i = Interval.join i1 i2 in - let ctx = match x.lv_type with - | Linteger -> mk_ctx ~use_gmp_opt:true (ty_of_interv ~ctx:Gmpz i) - | Ctype ty -> - (match Cil.unrollType ty with - | TInt(ik, _) -> mk_ctx ~use_gmp_opt:true (C_integer ik) - | ty -> - Options.fatal "unexpected type %a for quantified variable %a" - Printer.pp_typ ty - Printer.pp_logic_var x) - | lty -> - Options.fatal "unexpected type %a for quantified variable %a" - Printer.pp_logic_type lty - Printer.pp_logic_var x - in - (* forcing when typing bounds prevents to generate an extra useless - GMP variable when --e-acsl-gmp-only *) - ignore (type_term ~use_gmp_opt:false ~ctx t1); - ignore (type_term ~use_gmp_opt:false ~ctx t2); - (* if we must generate GMP code, degrade the interval in order to - guarantee that [x] will be a GMP when typing the goal *) - let i = match ctx with - | C_integer _ -> i - | Gmpz -> Interval.top_ival (* [ -\infty; +\infty ] *) - | C_float _ | Rational | Real | Nan -> - Options.fatal "unexpected quantification over %a" D.pretty ctx - in - Interval.Env.add x i) - guards; - (type_predicate goal).ty + let guards = + List.map + (fun (t1, x, t2) -> + type_bound_variables ~loc:p.pred_loc ~dep ~lenv (t1, x, t2)) + guards + in Bound_variables.replace p guards goal; | Pseparated tlist -> List.iter diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index 275d229ff01904b1f55aecb28d20e529aed981cb..e865ccb14bbe3b6f649337e495a9098158a3149a 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -864,6 +864,7 @@ let inject () = Project.pretty (Project.current ()); Gmp_types.init (); let ast = Ast.get () in + Bound_variables.preprocess ast; Preprocess_typing.type_program ast; inject_in_file ast; reset_all ast;