diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index bc2d25e3edeec9aa496db2bb54df3355916a4fc0..d5d488ff22b009b0dea86cc055aec6f620158d9c 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -63,7 +63,8 @@ SRC_ANALYSES:= \ exit_points \ lscope \ interval \ - typing + typing \ + bound_variables SRC_ANALYSES:=$(addprefix src/analyses/, $(SRC_ANALYSES)) # code generator diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index 6b448f789441d6b8d95e5a5a1acd19e4d2ded92c..b0a81804aefdf99a1ce61a91cfa906ad0c131a87 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -25,6 +25,9 @@ Plugin E-ACSL <next-release> ############################ +-* E-ACSL [2021-07-30] Fix incorrect evaluation of quantified + predicates when a strict guard overlaps with the type of the + bound variable (unlikely in practice) (frama-c/e-acsl#149). - E-ACSL [2021-07-06] Add support for the extended quantifier \sum. -* E-ACSL [2021-06-22] Fix a crash that occured when using certain combinations of nested blocks and annotations. diff --git a/src/plugins/e-acsl/headers/header_spec.txt b/src/plugins/e-acsl/headers/header_spec.txt index 7f24e72705c0e02ecd7c80e03048f3fed66aaa35..f6e7a2458dea59fa67cbdf6a689c8ea05d0ac378 100644 --- a/src/plugins/e-acsl/headers/header_spec.txt +++ b/src/plugins/e-acsl/headers/header_spec.txt @@ -67,6 +67,8 @@ share/e-acsl/observation_model/e_acsl_observation_model.c: CEA_LGPL_OR_PROPRIETA share/e-acsl/observation_model/e_acsl_observation_model.h: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/e_acsl.h: CEA_LGPL_OR_PROPRIETARY.E_ACSL share/e-acsl/e_acsl_rtl.c: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/analyses/bound_variables.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/analyses/bound_variables.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/analyses/exit_points.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/analyses/exit_points.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/analyses/interval.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.ml b/src/plugins/e-acsl/src/analyses/bound_variables.ml new file mode 100644 index 0000000000000000000000000000000000000000..c893fa97761bd74b106b4ab6ce5107d1186765a9 --- /dev/null +++ b/src/plugins/e-acsl/src/analyses/bound_variables.ml @@ -0,0 +1,730 @@ +(**************************************************************************) +(* *) +(* This file is part of the Frama-C's E-ACSL plug-in. *) +(* *) +(* Copyright (C) 2012-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +(** Module for preprocessing the quantified predicates. Predicates with + quantifiers are hard to translate, so we delegate some of the work to a + preprocessing phase. At the end of this phase, all the quantified predicates + should have an associated preprocessed form [vars * goal] where - [vars] is a + list of guarded variables in the right order - [goal] is the predicate under + the quantifications The guarded variables in the list [vars] are of type + [term * logic_var * term * predicate option], where a tuple [(t1,v,t2,p)] + indicates that v is a logic variable with the two guards t1 <= x < t2 and p + is an additional optional guard to intersect the first guard with the + provided type for the variable v *) + +open Cil_types +open Cil +open Cil_datatype + +(** [error_msg quantif msg pp x] creates an error message from the string [msg] + containing the value [x] pretty-printed by [pp] and the predicate [quantif] + pretty-printed. *) +let error_msg quantif msg pp x = + let msg1 = Format.asprintf msg pp x in + let msg2 = + Format.asprintf "@[ in quantification@ %a@]" + Printer.pp_predicate quantif + 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 + (* The function compare should never be used since we only use this + datatype for hashtables *) + let compare _ _ = assert false + 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 + val clear : unit -> 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 + + let clear () = + Cil_datatype.Logic_var.Hashtbl.clear guard_tbl; + Quantified_predicate.Hashtbl.clear tbl +end + +let get_preprocessed_quantifier = Quantifier.get +let get_guard_for_small_type = Quantifier.get_guard_for_small_type +let clear_guards = Quantifier.clear + +(** Helper module to process the constraints in the quantification and extract + guards for the quantified variables. *) +module Constraints: sig + (** Context type manipulated by the functions of the module. *) + type t + + (** [empty quantif bounded_vars] creates an empty context from a + quantification predicate and the list of bounded variables for the + predicate. *) + val empty: predicate -> logic_var list -> t + + (** [get_bounded_var ctxt t] returns the logic var corresponding to the given + term if the term is a variable ([TLval(Tvar _, TNoOffset)]) that is a + bounded variable for the quantification being processed, or [None] + otherwise. *) + val get_bounded_var: term -> t -> logic_var option + + (** [has_unguarded_bounded_vars ctxt] returns true iff some bounded variables + in [ctxt] do not have a guard extracted from the quantification + predicate. *) + val has_unguarded_bounded_vars: t -> bool + + (** [add_lower_bound ctxt t r lv] adds the lower bound [t r lv] to the list + of guards for the quantification. + @raises Typing_error if a lower bound already exists for [lv]. *) + val add_lower_bound: term -> relation -> logic_var -> t -> t + + (** [add_linked_upper_bound ctxt lv1 r lv2] adds a link between two bounded + variables with still incomplete guards. [lv1] should already have at least + a lower bound. + @raises Typing_error if [lv1] do not have an existing lower bound. *) + val add_linked_upper_bound: logic_var -> relation -> logic_var -> t -> t + + (** [add_upper_bound ctxt lv r t] adds the upper bound [lv r t] to the list of + guards for the quantification. + For each existing linked relation [lv' r' lv], also adds the upper bound + [lv' r'' t] to the list of guards, with [r''] being the strictest relation + between [r] and [r']. + @raises Typing_error if either [lv] or [lv'] does not have an existing + lower bound. *) + val add_upper_bound: logic_var -> relation -> term -> t -> t + + (** [check_validity ctxt] checks the validity of the quantification after + processing. + @raises Typing_error if some bound (lower or upper) cound not be extracted + from the quantification for each bounded variable. *) + val check_validity: t -> unit + + (** [retrieve_guards ctxt] returns the list of guards for the bounded + variables of the quantification, in the order in which they must be + generated, ie. if [j] depends on [i] then the guard for [i] will be before + the guard for [j] in the list. + [check_validity ctxt] must have been called before calling + [retrieve_guards]. *) + val retrieve_guards: t -> (term * relation * logic_var * relation * term) list + + (** [raise_error ctxt msg] raises a [Typing_error] exception with the given + message after appending the quantification from the context. *) + val raise_error: string -> t -> 'a + + (** [error_invalid_pred ?warn_rel ctxt p] raises a [Typing_error] exception + with a message indicating that the predicate [p] is not valid in the + quantification. Depending on the state of [ctxt], more information about + missing bounds is added to the message. If [warn_rel] is [true], then a + reminder that only [Rle] and [Rlt] are allowed to guard quantified + variables is added to the message. *) + val raise_error_invalid_pred: ?warn_rel:bool -> predicate -> t -> 'a +end = struct + type t = { + (** Quantification predicate being analyzed. *) + quantif: predicate; + (** Variables of the quantification that still need guards. *) + bounded_vars: Logic_var.Set.t; + (** Bounded variables list in reverse order in which they must be + generated. *) + rev_order: Logic_var.t list; + (** Table associating a bounded variable with its guard. *) + guards: (term * relation * (relation * term) option) Logic_var.Map.t; + (** Table associating a bounded variable with a relation with another + bounded variable. *) + linked_upper_bounds: (logic_var * relation) Logic_var.Map.t; + } + + let empty quantif bounded_vars = + (* Check that the bounded vars have integer type and store them in a set. *) + let bounded_vars = + List.fold_left + (fun s v -> + let v = + match v.lv_type with + | Ctype ty when isIntegralType ty -> v + | Linteger -> v + | Ltype _ as ty when Logic_const.is_boolean_type ty -> v + | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> + Error.not_yet + (error_msg + quantif + "@[non integer variable %a@]" + Printer.pp_logic_var v) + in + Logic_var.Set.add v s + ) + Logic_var.Set.empty + bounded_vars + in + { + quantif; + bounded_vars; + rev_order = []; + guards = Logic_var.Map.empty; + linked_upper_bounds = Logic_var.Map.empty; + } + + let get_bounded_var t ctxt = + let rec aux t = + match t.term_node with + | TLogic_coerce(_, t) -> aux t + | TLval(TVar lv, TNoOffset) when Logic_var.Set.mem lv ctxt.bounded_vars -> + Some lv + | _ -> None + in + aux t + + let has_unguarded_bounded_vars ctxt = + not (Logic_var.Set.is_empty ctxt.bounded_vars) + + (** Pretty printer for a guard, represented by a tuple + [(term, relation, logic_var, relation, term)]. *) + let pp_guard fmt (t1, r1, lv, r2, t2) = + Format.fprintf + fmt + "@[%a %a %a %a %a@]" + Printer.pp_term t1 + Printer.pp_relation r1 + Printer.pp_logic_var lv + Printer.pp_relation r2 + Printer.pp_term t2 + + (** Pretty printer for a lower bound, represented by the tuple + [(term, relation, logic_var)]. *) + let pp_lbound fmt (t, r, lv) = + Format.fprintf + fmt + "@[%a %a %a@]" + Printer.pp_term t + Printer.pp_relation r + Printer.pp_logic_var lv + + (** Pretty printer for a bound between two quantified variables, + represented by the tuple [(logic_var, relation, logic_var)]. *) + let pp_linked_bound fmt (lv1, r, lv2) = + Format.fprintf + fmt + "@[%a %a %a@]" + Printer.pp_logic_var lv1 + Printer.pp_relation r + Printer.pp_logic_var lv2 + + let add_lower_bound t r lv ctxt = + match Logic_var.Map.find_opt lv ctxt.guards with + | None -> + { ctxt with + guards = Logic_var.Map.add lv (t, r, None) ctxt.guards; } + | Some (t', r', None) -> + let msg = + Format.asprintf + "@[found existing lower bound %a@ when processing %a@ \ + in quantification@ %a@]" + pp_lbound (t', r', lv) + pp_lbound (t, r, lv) + Printer.pp_predicate ctxt.quantif + in + Error.untypable msg + | Some (t1', r1', Some (r2', t2')) -> + let msg = + Format.asprintf + "@[found existing guard %a@ when processing %a@ \ + in quantification@ %a@]" + pp_guard (t1', r1', lv, r2', t2') + pp_lbound (t, r, lv) + Printer.pp_predicate ctxt.quantif + in + Error.untypable msg + + let add_linked_upper_bound lv1 r lv2 ctxt = + if Logic_var.Map.mem lv1 ctxt.guards then begin + { ctxt with + linked_upper_bounds = + Logic_var.Map.add lv2 + (lv1, r) + ctxt.linked_upper_bounds; } + end else + let msg= + Format.asprintf + "@[missing lower bound for %a@ when processing the linked upper \ + bound %a@ in quantification@ %a@]" + Printer.pp_logic_var lv1 + pp_linked_bound (lv1, r, lv2) + Printer.pp_predicate ctxt.quantif + in + Error.untypable msg + + let add_upper_bound lv r t ctxt = + (* Select the strictest relation between [r1] and [r2], with both relations + being either [Rlt] or [Rle]. *) + let strictest_relation r1 r2 = + match r1, r2 with + | (Rlt | Rle), Rlt | Rlt, Rle -> Rlt + | Rle, Rle -> Rle + | _, _ -> + Options.fatal + "invalid relations %a and %a in quantification %a" + Printer.pp_relation r1 + Printer.pp_relation r2 + Printer.pp_predicate ctxt.quantif + in + (* Replace the guard for [lv] to add the upper bound [lv r t], and + recursively walk the bounds linked to [lv] to also add upper bounds. *) + let rec replace_guard lv r t ctxt = + try + let lower_bound_t, lower_bound_r, _ = + Logic_var.Map.find lv ctxt.guards + in + let ctxt = + { ctxt with + guards = + Logic_var.Map.add + lv + (lower_bound_t, lower_bound_r, Some (r, t)) + ctxt.guards; } + in + let ctxt = + { ctxt with + bounded_vars = Logic_var.Set.remove lv ctxt.bounded_vars; } + in + let need_upper_bound = + Logic_var.Map.find_opt lv ctxt.linked_upper_bounds + in + let ctxt = + match need_upper_bound with + | Some (lv', r') -> + let ctxt = + { ctxt with + linked_upper_bounds = + Logic_var.Map.remove lv ctxt.linked_upper_bounds; } + in + replace_guard lv' (strictest_relation r r') t ctxt + | None -> ctxt + in + { ctxt with rev_order = lv :: ctxt.rev_order } + with Not_found -> + Error.untypable + (error_msg ctxt.quantif + "@[missing lower bound for %a@]" + Printer.pp_logic_var lv) + in + replace_guard lv r t ctxt + + let check_validity ctxt = + if not (Logic_var.Map.is_empty ctxt.linked_upper_bounds) then + let msg = + let iter_keys f map_seq = + Seq.iter + (fun (key, _) -> f key) + map_seq + in + Format.asprintf + "@[missing upper bound%s for %a@ in quantification@ %a@]" + (if Logic_var.Map.cardinal ctxt.linked_upper_bounds = 1 then "" + else "s") + (Pretty_utils.pp_iter + ~sep:", " + iter_keys + Printer.pp_logic_var) + (Logic_var.Map.to_seq ctxt.linked_upper_bounds) + Printer.pp_predicate ctxt.quantif + in + Error.untypable msg + else if not (Logic_var.Set.is_empty ctxt.bounded_vars) then + let msg = + Format.asprintf + "@[missing lower bound%s for %a@ in quantification@ %a@]" + (if Logic_var.Set.cardinal ctxt.bounded_vars = 1 then "" else "s") + (Pretty_utils.pp_iter + ~sep:", " + Logic_var.Set.iter + Printer.pp_logic_var) + ctxt.bounded_vars + Printer.pp_predicate ctxt.quantif + in + Error.untypable msg + + let retrieve_guards ctxt = + List.fold_left + (fun acc lv -> + match Logic_var.Map.find_opt lv ctxt.guards with + | Some (t1, r1, Some (r2, t2)) -> + (t1, r1, lv, r2, t2) :: acc + + (* The error cases should not occur if the traversal of the + quantification was completed and [check_validity] was called + successfully. *) + | Some (_, _, None) -> + Options.fatal + "Missing upper bound when trying to retrieve the guard for %a" + Printer.pp_logic_var lv + | None -> Options.fatal "Missing guard for %a" Printer.pp_logic_var lv + ) + [] + ctxt.rev_order + + let raise_error msg ctxt = + let msg2 = + Format.asprintf "@[ in quantification@ %a@]" + Printer.pp_predicate ctxt.quantif + in + Error.untypable (msg ^ msg2) + + let raise_error_invalid_pred ?(warn_rel=false) p ctxt = + (* Extract the set of variables that have another variable for upper bound. + They will be considered valid for the purpose of the error message, ie. + if we have the relation [0 < i < j] with [0 < i] as an incomplete guard + and [i < j] as a linked upper bound, then the error is about [j] not + having an upper bound, not about [i]. *) + let linked_upper_bounds = + Logic_var.Map.fold + (fun _ (lv, _) acc -> Logic_var.Set.add lv acc) + ctxt.linked_upper_bounds + Logic_var.Set.empty + in + + let missing_guards, missing_upper_bounds = + Logic_var.Set.fold + (fun lv (missing_guards, missing_upper_bounds) -> + match Logic_var.Map.find_opt lv ctxt.guards with + | Some (_, _, None) + when not (Logic_var.Set.mem lv linked_upper_bounds) -> + (* A variable with an incomplete constraint and without a linked + upper bound has a missing upper bound. *) + missing_guards, lv :: missing_upper_bounds + | None -> + (* A variable without a constraint has a missing guard. *) + lv :: missing_guards, missing_upper_bounds + | _ -> + (* Otherwise the variable either has a complete constraint, or an + incomplete constraint but with a linked upper bound. *) + missing_guards, missing_upper_bounds) + ctxt.bounded_vars + ([], []) + in + + let msg = + Format.asprintf + "@[invalid guard %a@ in quantification@ %a.@ \ + %a@,%a@ \ + %t@]" + Printer.pp_predicate p Printer.pp_predicate ctxt.quantif + (Pretty_utils.pp_list + ~pre:"@[Missing guard for " + ~sep:",@ " + ~suf:".@]" + Printer.pp_logic_var) + (List.rev missing_guards) + (Pretty_utils.pp_list + ~pre:"@[Missing upper bound for " + ~sep:",@ " + ~suf:".@]" + Printer.pp_logic_var) + (List.rev missing_upper_bounds) + (fun fmt -> + if warn_rel then + Format.fprintf fmt + "@[Only %a and %a are allowed to guard quantifier variables@]" + Printer.pp_relation Rlt Printer.pp_relation Rle) + in + Error.untypable msg + +end + +(******************************************************************************) +(** Syntactical analysis *) +(******************************************************************************) + +(** [extract_constraint ctxt t1 r t2] populates the quantification context + [ctxt] with the constraint [t1 r t2], either adding a lower bound if [t2] is + a bounded variable or adding an upper bound if [t1] is a bounded variable. + @raises Typing_error if no constraint can be extracted or if the constraint + is invalid (no bounded variable in [t1] or [t2] for instance). *) +let extract_constraint ctxt t1 r t2 = + (* Return the logic var corresponding to the given term or [None] if the term + is not a variable ([TLval(TVar _, TNoOffset)]). *) + let rec _get_logic_var_opt t = + match t.term_node with + | TLogic_coerce(_, t) -> _get_logic_var_opt t + | TLval(TVar x, TNoOffset) -> Some x + | _ -> None + in + (* Process the given relation *) + let lv1 = Constraints.get_bounded_var t1 ctxt in + let lv2 = Constraints.get_bounded_var t2 ctxt in + match lv1, lv2 with + | None, Some lv2 -> (* t1 value, t2 variable: store lower bound *) + Constraints.add_lower_bound t1 r lv2 ctxt + | Some lv1, None -> (* t1 variable, t2 value: store upper bound *) + Constraints.add_upper_bound lv1 r t2 ctxt + | Some lv1, Some lv2 -> + (* t1 variable, t2 variable: store link between t1 and t2 *) + let ctxt = Constraints.add_linked_upper_bound lv1 r lv2 ctxt in + Constraints.add_lower_bound t1 r lv2 ctxt + | None, None -> (* t1 value, t2 value: error *) + let msg = + Format.asprintf + "@[invalid constraint %a %a %a, both operands are constants or \ + already bounded@]" + Printer.pp_term t1 + Printer.pp_relation r + Printer.pp_term t2 + in + Constraints.raise_error msg ctxt + +(** [visit_quantif_relations ~is_forall ctxt p] traverses the predicate [p] of + the quantification being analyzed in the context [ctxt] from left to right + as long as the predicate is [Pimplies], [Pand] or [Prel(Rlt | Rle)], and + calls [extract_constraint] on each relation [Rlt] or [Rle] it encounters + until all the guards of the quantification have been found or the predicate + is finished. If [is_forall] is [true] then the predicate being traversed is + a universal quantification, otherwise it is an existential quantification. + The function returns a tuple [ctxt, goal_opt] where [ctxt] is the + quantification context and [goal_opt] is either [None] if the goal of the + quantification has not been found yet or [Some goal] if [goal] is the + predicate corresponding to the goal of the quantification. + @raises Typing_error if the predicate contains something other than + [Pimplies], [Pand] or [Prel] with a relation that is neither [Rlt] nor + [Rle]. *) +let rec visit_quantif_relations ~is_forall ctxt p = + match p.pred_content with + | Pimplies(lhs, rhs) -> + let ctxt, _ = visit_quantif_relations ~is_forall ctxt lhs in + if is_forall then begin + if Constraints.has_unguarded_bounded_vars ctxt then + visit_quantif_relations ~is_forall ctxt rhs + else + ctxt, Some rhs + end else + let msg = + Format.asprintf + "@[invalid quantification guard, expecting '%s' to continue@ \ + bounding variables,@ found '%s'@]" + (if Kernel.Unicode.get () then Utf8_logic.conj else "&&") + (if Kernel.Unicode.get () then Utf8_logic.implies else "==>") + in + Constraints.raise_error msg ctxt + | Pand(lhs, rhs) -> + let ctxt, _ = visit_quantif_relations ~is_forall ctxt lhs in + if Constraints.has_unguarded_bounded_vars ctxt then + visit_quantif_relations ~is_forall ctxt rhs + else if not is_forall then + ctxt, Some rhs + else + let msg = + Format.asprintf + "@[invalid quantification predicate, expecting '%s' to@ \ + separate the hypotheses from the goal, found '%s'@]" + (if Kernel.Unicode.get () then Utf8_logic.implies else "==>") + (if Kernel.Unicode.get () then Utf8_logic.conj else "&&") + in + Constraints.raise_error msg ctxt + | Prel((Rlt | Rle) as r, t1, t2) -> + let ctxt = extract_constraint ctxt t1 r t2 in + ctxt, None + | Prel _ -> + Constraints.raise_error_invalid_pred ~warn_rel:true p ctxt + | _ -> + Constraints.raise_error_invalid_pred p ctxt + +(** [compute_quantif_guards ~is_forall quantif bounded_vars p] computes the + guards for the bounded variables [bounded_vars] of the quantification + [quantif] from the predicate [p]. If [is_forall] is [true] then [quantif] is + a universal quantification, otherwise [quantif] is an existential + quantification. + The function returns the list of guards for the variables and the goal + predicate of the quantification. *) +let compute_quantif_guards ~is_forall quantif bounded_vars p = + (* Traverse the predicate of the quantification to extract the guards of the + bounded variables. *) + let ctxt = Constraints.empty quantif bounded_vars in + let ctxt, goal = visit_quantif_relations ~is_forall ctxt p in + (* Check the validity of the result of the traversal. *) + Constraints.check_validity ctxt; + if Option.is_none goal then + let msg = + Format.asprintf + "@[no goal found in quantification@ %a@]" + Printer.pp_predicate quantif + in + Error.untypable msg + else + (* Traversal is valid, the guards and the goal can be retrieved and + returned. *) + let guards = Constraints.retrieve_guards ctxt in + let goal = Option.get goal in + guards, goal + +(******************************************************************************) +(** Type intersection *) +(******************************************************************************) + +(* It could happen that the bounds provided for a quantifier [lv] are bigger + than its type. [bounds_for_small_type] 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 + Example: [\forall unsigned char c; 4 <= c <= 100 ==> 0 <= c <= 255] + Return: B + - Case 2: B \not\subseteq R and the bounds of B are inferred exactly + Example: [\forall unsigned char c; 4 <= c <= 300 ==> 0 <= c <= 255] + Return: B \intersect R + - 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 () = 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 new file mode 100644 index 0000000000000000000000000000000000000000..bd32dc7b576334137289266931c9fb51f6d99257 --- /dev/null +++ b/src/plugins/e-acsl/src/analyses/bound_variables.mli @@ -0,0 +1,42 @@ +(**************************************************************************) +(* *) +(* This file is part of the Frama-C's E-ACSL plug-in. *) +(* *) +(* Copyright (C) 2012-2020 *) +(* CEA (Commissariat à l'énergie atomique et aux énergies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Cil_datatype + + +val compute_guards: location -> is_forall:bool -> predicate -> Logic_var.t list -> predicate -> unit +(** Take a predicate starting with a quantifier and store the scope of its variable *) + +val get_preprocessed_quantifier: predicate -> (term * logic_var * term) list * predicate +(** @return the preprocessed of a quantified predicate + the [(term * logic_var * term) list] is the list of all the quantified variables + along with their syntactic guards, and the [predicate] is the goal: the original + predicate with all the quantifiers removed *) + +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 clear_guards : unit -> unit +(** Clear the table of guard conditions for quantified variables *) 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} *) (* ************************************************************************** *) diff --git a/src/plugins/e-acsl/src/code_generator/env.ml b/src/plugins/e-acsl/src/code_generator/env.ml index 24775347413b7975db7d758f4c61ae1adcdf6b5a..dce4af58bf9d22d69fa087552a06223d74ab2865 100644 --- a/src/plugins/e-acsl/src/code_generator/env.ml +++ b/src/plugins/e-acsl/src/code_generator/env.ml @@ -445,7 +445,7 @@ let transfer ~from env = match from.env_stack, env.env_stack with type where = Before | Middle | After let pop_and_get ?(split=false) env stmt ~global_clear where = let split = split && stmt.labels = [] in - (* Options.feedback "pop_and_get from %a (%b)" Printer.pp_stmt stmt split;*) + (* Options.feedback "pop_and_get from %a (%b)" Printer.pp_stmt stmt split; *) let local_env, tl = top env in let clear = if global_clear then begin diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index 44a7f433c60d80379ba5de293f8730efd9dcccb6..dae5dc6c27e651ab6cffeafd718d4619878bb54d 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -831,6 +831,7 @@ let reset_all ast = Logic_functions.reset (); Literal_strings.reset (); Global_observer.reset (); + Bound_variables.clear_guards (); Typing.clear (); (* reset some kernel states: *) (* reset the CFG that has been heavily modified by the code generation step *) diff --git a/src/plugins/e-acsl/src/code_generator/loops.ml b/src/plugins/e-acsl/src/code_generator/loops.ml index c6d6e726ba7e5f183f42551b3bba4e57472811ad..eba5a6884df10a98ab4fa6cb95c70f4f48de59fc 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.ml +++ b/src/plugins/e-acsl/src/code_generator/loops.ml @@ -200,75 +200,15 @@ let handle_annotations env kf stmt = (**************************************************************************) (**************************** Nested loops ********************************) (**************************************************************************) - -(* It could happen that the bounds provided for a quantifier [lv] are bigger - than its type. [bounds_for_small_type] 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 - Example: [\forall unsigned char c; 4 <= c <= 100 ==> 0 <= c <= 255] - Return: B - - Case 2: B \not\subseteq R and the bounds of B are inferred exactly - Example: [\forall unsigned char c; 4 <= c <= 300 ==> 0 <= c <= 255] - Return: B \intersect R - - 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) = - match lv.lv_type with - | Ltype _ | Lvar _ | Lreal | Larrow _ -> - Options.abort "quantification over non-integer type is not part of E-ACSL" - - | 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.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 (Rle, lv_term, guard_upper) in - let guard = Logic_const.pand ~loc (guard_lower, guard_upper) in - t1, t2, Some guard - let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = let term_to_exp = !term_to_exp_ref in match lscope_vars with | [] -> mk_innermost_block env | Lscope.Lvs_quantif(t1, rel1, logic_x, rel2, t2) :: lscope_vars' -> - let t1, t2, guard_for_small_type_opt = - bounds_for_small_type ~loc (t1, logic_x, t2) - in + assert (rel1 == Rle && rel2 == Rlt); + Typing.type_term ~use_gmp_opt:false t1; + Typing.type_term ~use_gmp_opt:false t2; let ctx = let ty1 = Typing.get_number_ty t1 in let ty2 = Typing.get_number_ty t2 in @@ -286,41 +226,14 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = ty; res in - let t1 = match rel1 with - | Rlt -> - let t = t_plus_one t1 in - Typing.type_term ~use_gmp_opt:false ~ctx t; - t - | Rle -> - t1 - | Rgt | Rge | Req | Rneq -> - assert false - in - let t2_one, bop2 = match rel2 with - | Rlt -> - t2, Lt - | Rle -> - (* we increment the loop counter one more time (at the end of the - loop). Thus to prevent overflow, check the type of [t2+1] - instead of [t2]. *) - t_plus_one t2, Le - | Rgt | Rge | Req | Rneq -> - assert false - in - Typing.type_term ~use_gmp_opt:false ~ctx t2_one; - let ctx_one = - let ty1 = Typing.get_number_ty t1 in - let ty2 = Typing.get_number_ty t2_one in - Typing.join ty1 ty2 - in let ty = - try Typing.typ_of_number_ty ctx_one + try Typing.typ_of_number_ty ctx with Typing.Not_a_number -> assert false in (* loop counter corresponding to the quantified variable *) let var_x, x, env = Env.Logic_binding.add ~ty env kf logic_x in let lv_x = var var_x in - let env = match ctx_one with + let env = match ctx with | Typing.C_integer _ -> env | Typing.Gmpz -> Env.add_stmt env kf (Gmp.init ~loc x) | Typing.(C_float _ | Rational | Real | Nan) -> assert false @@ -337,13 +250,22 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = ~global_clear:false Env.Middle in - (* generate the guard [x bop t2] *) + (* generate the guard [x < t2] *) let block_to_stmt b = Smart_stmt.block_stmt b in let tlv = Logic_const.tvar ~loc logic_x in + (* if [t2] is of the form [t2'+1], generate the guard [x <= t2'] + to avoid the extra addition (relevant when computing with GMP) *) let guard = - (* must copy [t2] to force being typed again *) - Logic_const.term ~loc - (TBinOp(bop2, tlv, { t2 with term_node = t2.term_node } )) Linteger + match t2.term_node with + | TBinOp (PlusA, t2_minus_one, {term_node = TConst(Integer (n, _))}) when Integer.is_one n -> + Logic_const.term ~loc + (TBinOp(Le, tlv, { t2_minus_one with term_node = t2_minus_one.term_node })) + Linteger + | _ -> + (* must copy [t2] to force it being typed again *) + Logic_const.term ~loc + (TBinOp(Lt, tlv, { t2 with term_node = t2.term_node } )) + Linteger in Typing.type_term ~use_gmp_opt:false ~ctx:Typing.c_int guard; let guard_exp, env = term_to_exp kf (Env.push env) guard in @@ -361,7 +283,7 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = let guard = block_to_stmt guard_blk in (* increment the loop counter [x++]; previous typing ensures that [x++] fits type [ty] *) - let tlv_one = t_plus_one ~ty:ctx_one tlv in + let tlv_one = t_plus_one ~ty:ctx tlv in let incr, env = term_to_exp kf (Env.push env) tlv_one in let next_blk, env = Env.pop_and_get env @@ -371,6 +293,7 @@ let rec mk_nested_loops ~loc mk_innermost_block kf env lscope_vars = in (* generate the whole loop *) let next = block_to_stmt next_blk in + let guard_for_small_type_opt = Bound_variables.get_guard_for_small_type logic_x in let stmts, env = match guard_for_small_type_opt with | None -> guard :: body @ [ next ], env diff --git a/src/plugins/e-acsl/src/code_generator/quantif.ml b/src/plugins/e-acsl/src/code_generator/quantif.ml index 2e95c2c5699ccc1b585919ac6bbf0703bb3684b0..4ca10accdffd58208f41e97c7de496bc134c0127 100644 --- a/src/plugins/e-acsl/src/code_generator/quantif.ml +++ b/src/plugins/e-acsl/src/code_generator/quantif.ml @@ -22,526 +22,15 @@ open Cil_types open Cil -open Cil_datatype (** Forward reference for [Translate.predicate_to_exp]. *) let predicate_to_exp_ref : (kernel_function -> Env.t -> predicate -> exp * Env.t) ref = Extlib.mk_fun "predicate_to_exp_ref" -(** [error_msg quantif msg pp x] creates an error message from the string [msg] - containing the value [x] pretty-printed by [pp] and the predicate [quantif] - pretty-printed. *) -let error_msg quantif msg pp x = - let msg1 = Format.asprintf msg pp x in - let msg2 = - Format.asprintf "@[ in quantification@ %a@]" - Printer.pp_predicate quantif - in - msg1 ^ msg2 - -(** Helper module to process the constraints in the quantification and extract - guards for the quantified variables. *) -module Constraints: sig - (** Context type manipulated by the functions of the module. *) - type t - - (** [empty quantif bounded_vars] creates an empty context from a - quantification predicate and the list of bounded variables for the - predicate. *) - val empty: predicate -> logic_var list -> t - - (** [get_bounded_var ctxt t] returns the logic var corresponding to the given - term if the term is a variable ([TLval(Tvar _, TNoOffset)]) that is a - bounded variable for the quantification being processed, or [None] - otherwise. *) - val get_bounded_var: term -> t -> logic_var option - - (** [has_unguarded_bounded_vars ctxt] returns true iff some bounded variables - in [ctxt] do not have a guard extracted from the quantification - predicate. *) - val has_unguarded_bounded_vars: t -> bool - - (** [add_lower_bound ctxt t r lv] adds the lower bound [t r lv] to the list - of guards for the quantification. - @raises Typing_error if a lower bound already exists for [lv]. *) - val add_lower_bound: term -> relation -> logic_var -> t -> t - - (** [add_linked_upper_bound ctxt lv1 r lv2] adds a link between two bounded - variables with still incomplete guards. [lv1] should already have at least - a lower bound. - @raises Typing_error if [lv1] do not have an existing lower bound. *) - val add_linked_upper_bound: logic_var -> relation -> logic_var -> t -> t - - (** [add_upper_bound ctxt lv r t] adds the upper bound [lv r t] to the list of - guards for the quantification. - For each existing linked relation [lv' r' lv], also adds the upper bound - [lv' r'' t] to the list of guards, with [r''] being the strictest relation - between [r] and [r']. - @raises Typing_error if either [lv] or [lv'] does not have an existing - lower bound. *) - val add_upper_bound: logic_var -> relation -> term -> t -> t - - (** [check_validity ctxt] checks the validity of the quantification after - processing. - @raises Typing_error if some bound (lower or upper) cound not be extracted - from the quantification for each bounded variable. *) - val check_validity: t -> unit - - (** [retrieve_guards ctxt] returns the list of guards for the bounded - variables of the quantification, in the order in which they must be - generated, ie. if [j] depends on [i] then the guard for [i] will be before - the guard for [j] in the list. - [check_validity ctxt] must have been called before calling - [retrieve_guards]. *) - val retrieve_guards: t -> (term * relation * logic_var * relation * term) list - - (** [raise_error ctxt msg] raises a [Typing_error] exception with the given - message after appending the quantification from the context. *) - val raise_error: string -> t -> 'a - - (** [error_invalid_pred ?warn_rel ctxt p] raises a [Typing_error] exception - with a message indicating that the predicate [p] is not valid in the - quantification. Depending on the state of [ctxt], more information about - missing bounds is added to the message. If [warn_rel] is [true], then a - reminder that only [Rle] and [Rlt] are allowed to guard quantified - variables is added to the message. *) - val raise_error_invalid_pred: ?warn_rel:bool -> predicate -> t -> 'a -end = struct - type t = { - (** Quantification predicate being analyzed. *) - quantif: predicate; - (** Variables of the quantification that still need guards. *) - bounded_vars: Logic_var.Set.t; - (** Bounded variables list in reverse order in which they must be - generated. *) - rev_order: Logic_var.t list; - (** Table associating a bounded variable with its guard. *) - guards: (term * relation * (relation * term) option) Logic_var.Map.t; - (** Table associating a bounded variable with a relation with another - bounded variable. *) - linked_upper_bounds: (logic_var * relation) Logic_var.Map.t; - } - - let empty quantif bounded_vars = - (* Check that the bounded vars have integer type and store them in a set. *) - let bounded_vars = - List.fold_left - (fun s v -> - let v = - match v.lv_type with - | Ctype ty when isIntegralType ty -> v - | Linteger -> v - | Ltype _ as ty when Logic_const.is_boolean_type ty -> v - | Ctype _ | Ltype _ | Lvar _ | Lreal | Larrow _ -> - Error.not_yet - (error_msg - quantif - "@[non integer variable %a@]" - Printer.pp_logic_var v) - in - Logic_var.Set.add v s - ) - Logic_var.Set.empty - bounded_vars - in - { - quantif; - bounded_vars; - rev_order = []; - guards = Logic_var.Map.empty; - linked_upper_bounds = Logic_var.Map.empty; - } - - let get_bounded_var t ctxt = - let rec aux t = - match t.term_node with - | TLogic_coerce(_, t) -> aux t - | TLval(TVar lv, TNoOffset) when Logic_var.Set.mem lv ctxt.bounded_vars -> - Some lv - | _ -> None - in - aux t - - let has_unguarded_bounded_vars ctxt = - not (Logic_var.Set.is_empty ctxt.bounded_vars) - - (** Pretty printer for a guard, represented by a tuple - [(term, relation, logic_var, relation, term)]. *) - let pp_guard fmt (t1, r1, lv, r2, t2) = - Format.fprintf - fmt - "@[%a %a %a %a %a@]" - Printer.pp_term t1 - Printer.pp_relation r1 - Printer.pp_logic_var lv - Printer.pp_relation r2 - Printer.pp_term t2 - - (** Pretty printer for a lower bound, represented by the tuple - [(term, relation, logic_var)]. *) - let pp_lbound fmt (t, r, lv) = - Format.fprintf - fmt - "@[%a %a %a@]" - Printer.pp_term t - Printer.pp_relation r - Printer.pp_logic_var lv - - (** Pretty printer for a bound between two quantified variables, - represented by the tuple [(logic_var, relation, logic_var)]. *) - let pp_linked_bound fmt (lv1, r, lv2) = - Format.fprintf - fmt - "@[%a %a %a@]" - Printer.pp_logic_var lv1 - Printer.pp_relation r - Printer.pp_logic_var lv2 - - let add_lower_bound t r lv ctxt = - match Logic_var.Map.find_opt lv ctxt.guards with - | None -> - { ctxt with - guards = Logic_var.Map.add lv (t, r, None) ctxt.guards; } - | Some (t', r', None) -> - let msg = - Format.asprintf - "@[found existing lower bound %a@ when processing %a@ \ - in quantification@ %a@]" - pp_lbound (t', r', lv) - pp_lbound (t, r, lv) - Printer.pp_predicate ctxt.quantif - in - Error.untypable msg - | Some (t1', r1', Some (r2', t2')) -> - let msg = - Format.asprintf - "@[found existing guard %a@ when processing %a@ \ - in quantification@ %a@]" - pp_guard (t1', r1', lv, r2', t2') - pp_lbound (t, r, lv) - Printer.pp_predicate ctxt.quantif - in - Error.untypable msg - - let add_linked_upper_bound lv1 r lv2 ctxt = - if Logic_var.Map.mem lv1 ctxt.guards then begin - { ctxt with - linked_upper_bounds = - Logic_var.Map.add lv2 - (lv1, r) - ctxt.linked_upper_bounds; } - end else - let msg= - Format.asprintf - "@[missing lower bound for %a@ when processing the linked upper \ - bound %a@ in quantification@ %a@]" - Printer.pp_logic_var lv1 - pp_linked_bound (lv1, r, lv2) - Printer.pp_predicate ctxt.quantif - in - Error.untypable msg - - let add_upper_bound lv r t ctxt = - (* Select the strictest relation between [r1] and [r2], with both relations - being either [Rlt] or [Rle]. *) - let strictest_relation r1 r2 = - match r1, r2 with - | (Rlt | Rle), Rlt | Rlt, Rle -> Rlt - | Rle, Rle -> Rle - | _, _ -> - Options.fatal - "invalid relations %a and %a in quantification %a" - Printer.pp_relation r1 - Printer.pp_relation r2 - Printer.pp_predicate ctxt.quantif - in - (* Replace the guard for [lv] to add the upper bound [lv r t], and - recursively walk the bounds linked to [lv] to also add upper bounds. *) - let rec replace_guard lv r t ctxt = - try - let lower_bound_t, lower_bound_r, _ = - Logic_var.Map.find lv ctxt.guards - in - let ctxt = - { ctxt with - guards = - Logic_var.Map.add - lv - (lower_bound_t, lower_bound_r, Some (r, t)) - ctxt.guards; } - in - let ctxt = - { ctxt with - bounded_vars = Logic_var.Set.remove lv ctxt.bounded_vars; } - in - let need_upper_bound = - Logic_var.Map.find_opt lv ctxt.linked_upper_bounds - in - let ctxt = - match need_upper_bound with - | Some (lv', r') -> - let ctxt = - { ctxt with - linked_upper_bounds = - Logic_var.Map.remove lv ctxt.linked_upper_bounds; } - in - replace_guard lv' (strictest_relation r r') t ctxt - | None -> ctxt - in - { ctxt with rev_order = lv :: ctxt.rev_order } - with Not_found -> - Error.untypable - (error_msg ctxt.quantif - "@[missing lower bound for %a@]" - Printer.pp_logic_var lv) - in - replace_guard lv r t ctxt - - let check_validity ctxt = - if not (Logic_var.Map.is_empty ctxt.linked_upper_bounds) then - let msg = - let iter_keys f map_seq = - Seq.iter - (fun (key, _) -> f key) - map_seq - in - Format.asprintf - "@[missing upper bound%s for %a@ in quantification@ %a@]" - (if Logic_var.Map.cardinal ctxt.linked_upper_bounds = 1 then "" - else "s") - (Pretty_utils.pp_iter - ~sep:", " - iter_keys - Printer.pp_logic_var) - (Logic_var.Map.to_seq ctxt.linked_upper_bounds) - Printer.pp_predicate ctxt.quantif - in - Error.untypable msg - else if not (Logic_var.Set.is_empty ctxt.bounded_vars) then - let msg = - Format.asprintf - "@[missing lower bound%s for %a@ in quantification@ %a@]" - (if Logic_var.Set.cardinal ctxt.bounded_vars = 1 then "" else "s") - (Pretty_utils.pp_iter - ~sep:", " - Logic_var.Set.iter - Printer.pp_logic_var) - ctxt.bounded_vars - Printer.pp_predicate ctxt.quantif - in - Error.untypable msg - - let retrieve_guards ctxt = - List.fold_left - (fun acc lv -> - match Logic_var.Map.find_opt lv ctxt.guards with - | Some (t1, r1, Some (r2, t2)) -> - (t1, r1, lv, r2, t2) :: acc - - (* The error cases should not occur if the traversal of the - quantification was completed and [check_validity] was called - successfully. *) - | Some (_, _, None) -> - Options.fatal - "Missing upper bound when trying to retrieve the guard for %a" - Printer.pp_logic_var lv - | None -> Options.fatal "Missing guard for %a" Printer.pp_logic_var lv - ) - [] - ctxt.rev_order - - let raise_error msg ctxt = - let msg2 = - Format.asprintf "@[ in quantification@ %a@]" - Printer.pp_predicate ctxt.quantif - in - Error.untypable (msg ^ msg2) - - let raise_error_invalid_pred ?(warn_rel=false) p ctxt = - (* Extract the set of variables that have another variable for upper bound. - They will be considered valid for the purpose of the error message, ie. - if we have the relation [0 < i < j] with [0 < i] as an incomplete guard - and [i < j] as a linked upper bound, then the error is about [j] not - having an upper bound, not about [i]. *) - let linked_upper_bounds = - Logic_var.Map.fold - (fun _ (lv, _) acc -> Logic_var.Set.add lv acc) - ctxt.linked_upper_bounds - Logic_var.Set.empty - in - - let missing_guards, missing_upper_bounds = - Logic_var.Set.fold - (fun lv (missing_guards, missing_upper_bounds) -> - match Logic_var.Map.find_opt lv ctxt.guards with - | Some (_, _, None) - when not (Logic_var.Set.mem lv linked_upper_bounds) -> - (* A variable with an incomplete constraint and without a linked - upper bound has a missing upper bound. *) - missing_guards, lv :: missing_upper_bounds - | None -> - (* A variable without a constraint has a missing guard. *) - lv :: missing_guards, missing_upper_bounds - | _ -> - (* Otherwise the variable either has a complete constraint, or an - incomplete constraint but with a linked upper bound. *) - missing_guards, missing_upper_bounds) - ctxt.bounded_vars - ([], []) - in - - let msg = - Format.asprintf - "@[invalid guard %a@ in quantification@ %a.@ \ - %a@,%a@ \ - %t@]" - Printer.pp_predicate p Printer.pp_predicate ctxt.quantif - (Pretty_utils.pp_list - ~pre:"@[Missing guard for " - ~sep:",@ " - ~suf:".@]" - Printer.pp_logic_var) - (List.rev missing_guards) - (Pretty_utils.pp_list - ~pre:"@[Missing upper bound for " - ~sep:",@ " - ~suf:".@]" - Printer.pp_logic_var) - (List.rev missing_upper_bounds) - (fun fmt -> - if warn_rel then - Format.fprintf fmt - "@[Only %a and %a are allowed to guard quantifier variables@]" - Printer.pp_relation Rlt Printer.pp_relation Rle) - in - Error.untypable msg -end - -(** [extract_constraint ctxt t1 r t2] populates the quantification context - [ctxt] with the constraint [t1 r t2], either adding a lower bound if [t2] is - a bounded variable or adding an upper bound if [t1] is a bounded variable. - @raises Typing_error if no constraint can be extracted or if the constraint - is invalid (no bounded variable in [t1] or [t2] for instance). *) -let extract_constraint ctxt t1 r t2 = - (* Return the logic var corresponding to the given term or [None] if the term - is not a variable ([TLval(TVar _, TNoOffset)]). *) - let rec _get_logic_var_opt t = - match t.term_node with - | TLogic_coerce(_, t) -> _get_logic_var_opt t - | TLval(TVar x, TNoOffset) -> Some x - | _ -> None - in - (* Process the given relation *) - let lv1 = Constraints.get_bounded_var t1 ctxt in - let lv2 = Constraints.get_bounded_var t2 ctxt in - match lv1, lv2 with - | None, Some lv2 -> (* t1 value, t2 variable: store lower bound *) - Constraints.add_lower_bound t1 r lv2 ctxt - | Some lv1, None -> (* t1 variable, t2 value: store upper bound *) - Constraints.add_upper_bound lv1 r t2 ctxt - | Some lv1, Some lv2 -> - (* t1 variable, t2 variable: store link between t1 and t2 *) - let ctxt = Constraints.add_linked_upper_bound lv1 r lv2 ctxt in - Constraints.add_lower_bound t1 r lv2 ctxt - | None, None -> (* t1 value, t2 value: error *) - let msg = - Format.asprintf - "@[invalid constraint %a %a %a, both operands are constants or \ - already bounded@]" - Printer.pp_term t1 - Printer.pp_relation r - Printer.pp_term t2 - in - Constraints.raise_error msg ctxt - -(** [visit_quantif_relations ~is_forall ctxt p] traverses the predicate [p] of - the quantification being analyzed in the context [ctxt] from left to right - as long as the predicate is [Pimplies], [Pand] or [Prel(Rlt | Rle)], and - calls [extract_constraint] on each relation [Rlt] or [Rle] it encounters - until all the guards of the quantification have been found or the predicate - is finished. If [is_forall] is [true] then the predicate being traversed is - a universal quantification, otherwise it is an existential quantification. - The function returns a tuple [ctxt, goal_opt] where [ctxt] is the - quantification context and [goal_opt] is either [None] if the goal of the - quantification has not been found yet or [Some goal] if [goal] is the - predicate corresponding to the goal of the quantification. - @raises Typing_error if the predicate contains something other than - [Pimplies], [Pand] or [Prel] with a relation that is neither [Rlt] nor - [Rle]. *) -let rec visit_quantif_relations ~is_forall ctxt p = - match p.pred_content with - | Pimplies(lhs, rhs) -> - let ctxt, _ = visit_quantif_relations ~is_forall ctxt lhs in - if is_forall then begin - if Constraints.has_unguarded_bounded_vars ctxt then - visit_quantif_relations ~is_forall ctxt rhs - else - ctxt, Some rhs - end else - let msg = - Format.asprintf - "@[invalid quantification guard, expecting '%s' to continue@ \ - bounding variables,@ found '%s'@]" - (if Kernel.Unicode.get () then Utf8_logic.conj else "&&") - (if Kernel.Unicode.get () then Utf8_logic.implies else "==>") - in - Constraints.raise_error msg ctxt - | Pand(lhs, rhs) -> - let ctxt, _ = visit_quantif_relations ~is_forall ctxt lhs in - if Constraints.has_unguarded_bounded_vars ctxt then - visit_quantif_relations ~is_forall ctxt rhs - else if not is_forall then - ctxt, Some rhs - else - let msg = - Format.asprintf - "@[invalid quantification predicate, expecting '%s' to@ \ - separate the hypotheses from the goal, found '%s'@]" - (if Kernel.Unicode.get () then Utf8_logic.implies else "==>") - (if Kernel.Unicode.get () then Utf8_logic.conj else "&&") - in - Constraints.raise_error msg ctxt - | Prel((Rlt | Rle) as r, t1, t2) -> - let ctxt = extract_constraint ctxt t1 r t2 in - ctxt, None - | Prel _ -> - Constraints.raise_error_invalid_pred ~warn_rel:true p ctxt - | _ -> - Constraints.raise_error_invalid_pred p ctxt - -(** [compute_quantif_guards ~is_forall quantif bounded_vars p] computes the - guards for the bounded variables [bounded_vars] of the quantification - [quantif] from the predicate [p]. If [is_forall] is [true] then [quantif] is - a universal quantification, otherwise [quantif] is an existential - quantification. - The function returns the list of guards for the variables and the goal - predicate of the quantification. *) -let compute_quantif_guards ~is_forall quantif bounded_vars p = - (* Traverse the predicate of the quantification to extract the guards of the - bounded variables. *) - let ctxt = Constraints.empty quantif bounded_vars in - let ctxt, goal = visit_quantif_relations ~is_forall ctxt p in - (* Check the validity of the result of the traversal. *) - Constraints.check_validity ctxt; - if Option.is_none goal then - let msg = - Format.asprintf - "@[no goal found in quantification@ %a@]" - Printer.pp_predicate quantif - in - Error.untypable msg - else - (* Traversal is valid, the guards and the goal can be retrieved and - returned. *) - let guards = Constraints.retrieve_guards ctxt in - let goal = Option.get goal in - guards, goal - -let () = Typing.compute_quantif_guards_ref := compute_quantif_guards (* It could happen that the bounds provided for a quantified [lv] are empty - in the sense that [min <= lv <= max] but [min > max]. In such cases, \true + in the sense that [min <= lv < max] but [min > max + 1]. In such cases, \true (or \false depending on the quantification) should be generated instead of nested loops. [has_empty_quantif_with_false_negative] partially detects such cases: @@ -551,31 +40,28 @@ let rec has_empty_quantif_with_false_negative = function | [] -> (* case 2 *) false - | (t1, rel1, _, rel2, t2) :: guards -> + | (t1, _, t2) :: guards -> let iv1 = Interval.(extract_ival (infer t1)) in let iv2 = Interval.(extract_ival (infer t2)) in let lower_bound, _ = Ival.min_and_max iv1 in let _, upper_bound = Ival.min_and_max iv2 in - match lower_bound, upper_bound with - | Some lower_bound, Some upper_bound -> - let res = - match rel1, rel2 with - | Rle, Rle -> Integer.gt lower_bound upper_bound - | Rle, Rlt | Rlt, Rle -> Integer.ge lower_bound upper_bound - | Rlt, Rlt -> Integer.ge lower_bound (Z.sub upper_bound Z.one) - | _ -> assert false - in - res (* case 1 *) || has_empty_quantif_with_false_negative guards - | None, _ | _, None -> - has_empty_quantif_with_false_negative guards + begin + match lower_bound, upper_bound with + | Some lower_bound, Some upper_bound -> + let res = lower_bound >= upper_bound in + res (* case 1 *) || has_empty_quantif_with_false_negative guards + | None, _ | _, None -> + has_empty_quantif_with_false_negative guards + end + module Label_ids = State_builder.Counter(struct let name = "E_ACSL.Label_ids" end) -let convert kf env loc ~is_forall quantif bounded_vars p = +let convert kf env loc ~is_forall quantif = (* guarded quantification over integers (or a subtype of integer) *) - let guards, goal = compute_quantif_guards ~is_forall quantif bounded_vars p in - match has_empty_quantif_with_false_negative guards, is_forall with + let bound_vars, goal = Bound_variables.get_preprocessed_quantifier quantif in + match has_empty_quantif_with_false_negative bound_vars, is_forall with | true, true -> Cil.one ~loc, env | true, false -> @@ -590,14 +76,14 @@ let convert kf env loc ~is_forall quantif bounded_vars p = if is_forall then o, z, fun x -> x else z, o, fun e -> new_exp ~loc:e.eloc (UnOp(LNot, e, intType)) in - (* transform [guards] into [lscope_var list], + (* transform [bound_vars] into [lscope_var list], and update logic scope in the process *) let lvs_guards, env = List.fold_right - (fun (t1, rel1, lv, rel2, t2) (lvs_guards, env) -> - let lvs = Lscope.Lvs_quantif(t1, rel1, lv, rel2, t2) in + (fun (t1, lv, t2) (lvs_guards, env) -> + let lvs = Lscope.Lvs_quantif (t1, Rle, lv, Rlt, t2) in let env = Env.Logic_scope.extend env lvs in lvs :: lvs_guards, env) - guards + bound_vars ([], env) in let var_res, res, env = @@ -658,11 +144,13 @@ let convert kf env loc ~is_forall quantif bounded_vars p = let quantif_to_exp kf env p = let loc = p.pred_loc in match p.pred_content with - | Pforall(bounded_vars, ({ pred_content = Pimplies(_, _) } as p')) -> - convert kf env loc ~is_forall:true p bounded_vars p' + | Pforall(bounded_vars, ({pred_content = Pimplies(_, _) } as p')) -> + Bound_variables.compute_guards loc ~is_forall:true p bounded_vars p'; + convert kf env loc ~is_forall:true p | Pforall _ -> Error.not_yet "unguarded \\forall quantification" | Pexists(bounded_vars, ({ pred_content = Pand(_, _) } as p')) -> - convert kf env loc ~is_forall:false p bounded_vars p' + Bound_variables.compute_guards loc ~is_forall:false p bounded_vars p'; + convert kf env loc ~is_forall:false p | Pexists _ -> Error.not_yet "unguarded \\exists quantification" | _ -> assert false diff --git a/src/plugins/e-acsl/tests/arith/oracle/at_on-purely-logic-variables.res.oracle b/src/plugins/e-acsl/tests/arith/oracle/at_on-purely-logic-variables.res.oracle index d3fb6e9662ef3e3af24a8631b3a1ac5f86024967..072289b594d3d42d3ca7ccf0cfffafb5640c869c 100644 --- a/src/plugins/e-acsl/tests/arith/oracle/at_on-purely-logic-variables.res.oracle +++ b/src/plugins/e-acsl/tests/arith/oracle/at_on-purely-logic-variables.res.oracle @@ -24,7 +24,7 @@ assert \initialized(__gen_e_acsl_at_6 + (int)((int)((int)(__gen_e_acsl_u_3 - 9) * 32) + - (int)((int)(__gen_e_acsl_v_3 - -5) - 1))); + (int)(__gen_e_acsl_v_3 - -4))); [eva:alarm] tests/arith/at_on-purely-logic-variables.c:45: Warning: function __e_acsl_assert, behavior blocking: precondition got status unknown. [eva:alarm] tests/arith/at_on-purely-logic-variables.c:43: Warning: diff --git a/src/plugins/e-acsl/tests/arith/oracle/gen_at_on-purely-logic-variables.c b/src/plugins/e-acsl/tests/arith/oracle/gen_at_on-purely-logic-variables.c index fd6454b1e06eb2d6ca9f59d962381e4b9298424c..46951ce87513b423dadef689c42e265455fe0090 100644 --- a/src/plugins/e-acsl/tests/arith/oracle/gen_at_on-purely-logic-variables.c +++ b/src/plugins/e-acsl/tests/arith/oracle/gen_at_on-purely-logic-variables.c @@ -142,9 +142,9 @@ int main(void) int __gen_e_acsl_u_7; __gen_e_acsl_u_7 = 42; *(__gen_e_acsl_at_7 + ((__gen_e_acsl_u_6 - 10) * 300 + ( - ((__gen_e_acsl_v_6 - -10) - 1) * 100 + ( - (__gen_e_acsl_w_2 - 100) - 1)))) = - (((n - (long)__gen_e_acsl_u_6) + __gen_e_acsl_u_7) + __gen_e_acsl_v_6) + __gen_e_acsl_w_2 > 0L; + (__gen_e_acsl_v_6 - -9) * 100 + ( + __gen_e_acsl_w_2 - 101)))) = ((( + n - (long)__gen_e_acsl_u_6) + __gen_e_acsl_u_7) + __gen_e_acsl_v_6) + __gen_e_acsl_w_2 > 0L; } __gen_e_acsl_w_2 ++; } @@ -167,7 +167,7 @@ int main(void) else __gen_e_acsl_if_2 = 3; if (__gen_e_acsl_v_4 <= __gen_e_acsl_if_2) ; else break; } - *(__gen_e_acsl_at_6 + ((__gen_e_acsl_u_4 - 9) * 32 + ((__gen_e_acsl_v_4 - -5) - 1))) = + *(__gen_e_acsl_at_6 + ((__gen_e_acsl_u_4 - 9) * 32 + (__gen_e_acsl_v_4 - -4))) = (n + (long)__gen_e_acsl_u_4) + __gen_e_acsl_v_4 > 0L; __gen_e_acsl_v_4 ++; } @@ -189,7 +189,7 @@ int main(void) long __gen_e_acsl_if; if (__gen_e_acsl_u_2 > 0) __gen_e_acsl_if = n + (long)__gen_e_acsl_k_2; else __gen_e_acsl_if = __gen_e_acsl_u_2 + __gen_e_acsl_v_2; - *(__gen_e_acsl_at_3 + ((__gen_e_acsl_u_2 - 9) * 11 + ((__gen_e_acsl_v_2 - -5) - 1))) = + *(__gen_e_acsl_at_3 + ((__gen_e_acsl_u_2 - 9) * 11 + (__gen_e_acsl_v_2 - -4))) = __gen_e_acsl_if > 0L; } __gen_e_acsl_v_2 ++; @@ -271,15 +271,14 @@ int main(void) (long)((int)( (long)((int)( __gen_e_acsl_u - 9L)) * 11L)) + (int)( - (int)(__gen_e_acsl_v - -5L) - 1))), + __gen_e_acsl_v - -4L))), sizeof(int), (void *)__gen_e_acsl_at_3, (void *)(& __gen_e_acsl_at_3)); __e_acsl_assert(__gen_e_acsl_valid_read_3,1,"RTE","main", - "mem_access:\n \\valid_read(__gen_e_acsl_at_3 +\n (int)((int)((int)(__gen_e_acsl_u - 9) * 11) +\n (int)((int)(__gen_e_acsl_v - -5) - 1)))", + "mem_access:\n \\valid_read(__gen_e_acsl_at_3 +\n (int)((int)((int)(__gen_e_acsl_u - 9) * 11) +\n (int)(__gen_e_acsl_v - -4)))", "tests/arith/at_on-purely-logic-variables.c",34); - if (*(__gen_e_acsl_at_3 + ((__gen_e_acsl_u - 9) * 11 + (( - __gen_e_acsl_v - -5) - 1)))) + if (*(__gen_e_acsl_at_3 + ((__gen_e_acsl_u - 9) * 11 + (__gen_e_acsl_v - -4)))) ; else { __gen_e_acsl_forall = 0; @@ -333,7 +332,7 @@ int main(void) __gen_e_acsl_k_4 = -9 + 1; while (1) { if (__gen_e_acsl_k_4 < 0) ; else break; - *(__gen_e_acsl_at_5 + ((__gen_e_acsl_k_4 - -9) - 1)) = m + (long)__gen_e_acsl_k_4; + *(__gen_e_acsl_at_5 + (__gen_e_acsl_k_4 - -8)) = m + (long)__gen_e_acsl_k_4; __gen_e_acsl_k_4 ++; } } @@ -349,16 +348,15 @@ int main(void) if (__gen_e_acsl_k_3 < 0) ; else break; { int __gen_e_acsl_valid_read_5; - __gen_e_acsl_valid_read_5 = __e_acsl_valid_read((void *)(__gen_e_acsl_at_5 + ( - (int)( - __gen_e_acsl_k_3 - -9L) - 1)), + __gen_e_acsl_valid_read_5 = __e_acsl_valid_read((void *)(__gen_e_acsl_at_5 + (int)( + __gen_e_acsl_k_3 - -8L)), sizeof(long), (void *)__gen_e_acsl_at_5, (void *)(& __gen_e_acsl_at_5)); __e_acsl_assert(__gen_e_acsl_valid_read_5,1,"RTE","main", - "mem_access:\n \\valid_read(__gen_e_acsl_at_5 + (int)((int)(__gen_e_acsl_k_3 - -9) - 1))", + "mem_access: \\valid_read(__gen_e_acsl_at_5 + (int)(__gen_e_acsl_k_3 - -8))", "tests/arith/at_on-purely-logic-variables.c",41); - if (! (*(__gen_e_acsl_at_5 + ((__gen_e_acsl_k_3 - -9) - 1)) == 0L)) + if (! (*(__gen_e_acsl_at_5 + (__gen_e_acsl_k_3 - -8)) == 0L)) ; else { __gen_e_acsl_exists_3 = 1; @@ -399,23 +397,21 @@ int main(void) (long)((int)( (long)((int)( __gen_e_acsl_u_3 - 9L)) * 32L)) + (int)( - (int)(__gen_e_acsl_v_3 - -5L) - 1))), + __gen_e_acsl_v_3 - -4L))), sizeof(int), (void *)__gen_e_acsl_at_6, (void *)(& __gen_e_acsl_at_6)); __e_acsl_assert(__gen_e_acsl_valid_read_6,1,"RTE","main", - "mem_access:\n \\valid_read(__gen_e_acsl_at_6 +\n (int)((int)((int)(__gen_e_acsl_u_3 - 9) * 32) +\n (int)((int)(__gen_e_acsl_v_3 - -5) - 1)))", + "mem_access:\n \\valid_read(__gen_e_acsl_at_6 +\n (int)((int)((int)(__gen_e_acsl_u_3 - 9) * 32) +\n (int)(__gen_e_acsl_v_3 - -4)))", "tests/arith/at_on-purely-logic-variables.c",45); /*@ assert Eva: initialization: \initialized(__gen_e_acsl_at_6 + (int)((int)((int)(__gen_e_acsl_u_3 - 9) * 32) - + - (int)((int)(__gen_e_acsl_v_3 - -5) - 1))); + + (int)(__gen_e_acsl_v_3 - -4))); */ if (*(__gen_e_acsl_at_6 + ((__gen_e_acsl_u_3 - 9) * 32 + ( - (__gen_e_acsl_v_3 - -5) - 1)))) - ; + __gen_e_acsl_v_3 - -4)))) ; else { __gen_e_acsl_forall_2 = 0; goto e_acsl_end_loop6; @@ -483,20 +479,18 @@ int main(void) __gen_e_acsl_u_5 - 10L)) * 300L)) + (int)( (long)((int)( (long)((int)( - (int)( - __gen_e_acsl_v_5 - -10L) - 1)) * 100L)) + (int)( - (long)((int)( - __gen_e_acsl_w - 100L)) - 1L)))), + __gen_e_acsl_v_5 - -9L)) * 100L)) + (int)( + __gen_e_acsl_w - 101L)))), sizeof(int), (void *)__gen_e_acsl_at_7, (void *)(& __gen_e_acsl_at_7)); __e_acsl_assert(__gen_e_acsl_valid_read_7,1,"RTE","main", - "mem_access:\n \\valid_read(__gen_e_acsl_at_7 +\n (int)((int)((int)(__gen_e_acsl_u_5 - 10) * 300) +\n (int)((int)((int)((int)(__gen_e_acsl_v_5 - -10) - 1) *\n 100)\n + (int)((int)(__gen_e_acsl_w - 100) - 1))))", + "mem_access:\n \\valid_read(__gen_e_acsl_at_7 +\n (int)((int)((int)(__gen_e_acsl_u_5 - 10) * 300) +\n (int)((int)((int)(__gen_e_acsl_v_5 - -9) * 100) +\n (int)(__gen_e_acsl_w - 101))))", "tests/arith/at_on-purely-logic-variables.c", 57); if (! *(__gen_e_acsl_at_7 + ((__gen_e_acsl_u_5 - 10) * 300 + ( - ((__gen_e_acsl_v_5 - -10) - 1) * 100 + ( - (__gen_e_acsl_w - 100) - 1))))) + (__gen_e_acsl_v_5 - -9) * 100 + ( + __gen_e_acsl_w - 101))))) ; else { __gen_e_acsl_exists_7 = 1; @@ -617,8 +611,7 @@ void __gen_e_acsl_f(int *t) __e_acsl_assert(__gen_e_acsl_valid_read_3,1,"RTE","f", "mem_access: \\valid_read(t + (int)(__gen_e_acsl_n_3 - 1))", "tests/arith/at_on-purely-logic-variables.c",7); - *(__gen_e_acsl_at_2 + ((__gen_e_acsl_n_3 - 1) - 1)) = *(t + ( - __gen_e_acsl_n_3 - 1)) > 5; + *(__gen_e_acsl_at_2 + (__gen_e_acsl_n_3 - 2)) = *(t + (__gen_e_acsl_n_3 - 1)) > 5; } __gen_e_acsl_n_3 ++; } @@ -636,7 +629,7 @@ void __gen_e_acsl_f(int *t) __e_acsl_assert(__gen_e_acsl_valid_read,1,"RTE","f", "mem_access: \\valid_read(t + __gen_e_acsl_n_2)", "tests/arith/at_on-purely-logic-variables.c",7); - *(__gen_e_acsl_at + ((__gen_e_acsl_n_2 - 1) - 1)) = *(t + __gen_e_acsl_n_2) == 12; + *(__gen_e_acsl_at + (__gen_e_acsl_n_2 - 2)) = *(t + __gen_e_acsl_n_2) == 12; } __gen_e_acsl_n_2 ++; } @@ -656,26 +649,24 @@ void __gen_e_acsl_f(int *t) int __gen_e_acsl_valid_read_2; int __gen_e_acsl_and; __gen_e_acsl_valid_read_2 = __e_acsl_valid_read((void *)(__gen_e_acsl_at + (int)( - (long)((int)( - __gen_e_acsl_n - 1L)) - 1L)), + __gen_e_acsl_n - 2L)), sizeof(int), (void *)__gen_e_acsl_at, (void *)(& __gen_e_acsl_at)); __e_acsl_assert(__gen_e_acsl_valid_read_2,1,"RTE","f", - "mem_access:\n \\valid_read(__gen_e_acsl_at + (int)((int)(__gen_e_acsl_n - 1) - 1))", + "mem_access: \\valid_read(__gen_e_acsl_at + (int)(__gen_e_acsl_n - 2))", "tests/arith/at_on-purely-logic-variables.c",7); - if (*(__gen_e_acsl_at + ((__gen_e_acsl_n - 1) - 1))) { + if (*(__gen_e_acsl_at + (__gen_e_acsl_n - 2))) { int __gen_e_acsl_valid_read_4; __gen_e_acsl_valid_read_4 = __e_acsl_valid_read((void *)(__gen_e_acsl_at_2 + (int)( - (long)((int)( - __gen_e_acsl_n - 1L)) - 1L)), + __gen_e_acsl_n - 2L)), sizeof(int), (void *)__gen_e_acsl_at_2, (void *)(& __gen_e_acsl_at_2)); __e_acsl_assert(__gen_e_acsl_valid_read_4,1,"RTE","f", - "mem_access:\n \\valid_read(__gen_e_acsl_at_2 + (int)((int)(__gen_e_acsl_n - 1) - 1))", + "mem_access: \\valid_read(__gen_e_acsl_at_2 + (int)(__gen_e_acsl_n - 2))", "tests/arith/at_on-purely-logic-variables.c",7); - __gen_e_acsl_and = *(__gen_e_acsl_at_2 + ((__gen_e_acsl_n - 1) - 1)); + __gen_e_acsl_and = *(__gen_e_acsl_at_2 + (__gen_e_acsl_n - 2)); } else __gen_e_acsl_and = 0; if (__gen_e_acsl_and) ; diff --git a/src/plugins/e-acsl/tests/arith/oracle/gen_quantif.c b/src/plugins/e-acsl/tests/arith/oracle/gen_quantif.c index d94bbc392cb7536b2c48179a9c0da1371e196cd5..f84cfba6458ecf2235e9cef3f3a2156c4237bff1 100644 --- a/src/plugins/e-acsl/tests/arith/oracle/gen_quantif.c +++ b/src/plugins/e-acsl/tests/arith/oracle/gen_quantif.c @@ -314,7 +314,7 @@ int main(void) { __e_acsl_mpz_t __gen_e_acsl__2; __e_acsl_mpz_t __gen_e_acsl_add; - __gmpz_init_set_ui(__gen_e_acsl__2,1UL); + __gmpz_init_set_str(__gen_e_acsl__2,"1",10); __gmpz_init(__gen_e_acsl_add); __gmpz_add(__gen_e_acsl_add, (__e_acsl_mpz_struct const *)(__gen_e_acsl_i_4), diff --git a/src/plugins/e-acsl/tests/bts/issue-eacsl-149.c b/src/plugins/e-acsl/tests/bts/issue-eacsl-149.c new file mode 100644 index 0000000000000000000000000000000000000000..64c896816225bc4e3c40874e3ae0bb8a6a8e34f6 --- /dev/null +++ b/src/plugins/e-acsl/tests/bts/issue-eacsl-149.c @@ -0,0 +1,7 @@ +#include <limits.h> + +int main (int argc, char *argv[]) { + /*@ assert \exists unsigned int x; -1 < x < 5 && x == 0;*/ + /*@ assert !(\forall unsigned int x; -1 < x < 5 ==> x != 0);*/ + return 0; +} diff --git a/src/plugins/e-acsl/tests/bts/oracle/gen_issue-eacsl-149.c b/src/plugins/e-acsl/tests/bts/oracle/gen_issue-eacsl-149.c new file mode 100644 index 0000000000000000000000000000000000000000..f8be21500b2e3acf531f87309718cba4c249f1fb --- /dev/null +++ b/src/plugins/e-acsl/tests/bts/oracle/gen_issue-eacsl-149.c @@ -0,0 +1,54 @@ +/* Generated by Frama-C */ +#include "stddef.h" +#include "stdio.h" +int main(int argc, char **argv) +{ + int __retres; + { + int __gen_e_acsl_exists; + int __gen_e_acsl_x; + __gen_e_acsl_exists = 0; + /*@ assert + Eva: signed_overflow: (unsigned int)((int)(-1)) + 1 ≤ 2147483647; + */ + __gen_e_acsl_x = (unsigned int)(-1) + 1; + while (1) { + if (__gen_e_acsl_x < 5) ; else break; + if (! (__gen_e_acsl_x == 0)) ; + else { + __gen_e_acsl_exists = 1; + goto e_acsl_end_loop1; + } + __gen_e_acsl_x ++; + } + e_acsl_end_loop1: ; + __e_acsl_assert(__gen_e_acsl_exists,1,"Assertion","main", + "\\exists unsigned int x; -1 < x < 5 && x == 0", + "tests/bts/issue-eacsl-149.c",4); + } + /*@ assert ∃ unsigned int x; -1 < x < 5 ∧ x ≡ 0; */ ; + { + int __gen_e_acsl_forall; + int __gen_e_acsl_x_2; + __gen_e_acsl_forall = 1; + __gen_e_acsl_x_2 = (unsigned int)(-1) + 1; + while (1) { + if (__gen_e_acsl_x_2 < 5) ; else break; + if (__gen_e_acsl_x_2 != 0) ; + else { + __gen_e_acsl_forall = 0; + goto e_acsl_end_loop2; + } + __gen_e_acsl_x_2 ++; + } + e_acsl_end_loop2: ; + __e_acsl_assert(! __gen_e_acsl_forall,1,"Assertion","main", + "!(\\forall unsigned int x; -1 < x < 5 ==> x != 0)", + "tests/bts/issue-eacsl-149.c",5); + } + /*@ assert ¬(∀ unsigned int x; -1 < x < 5 ⇒ x ≢ 0); */ ; + __retres = 0; + return __retres; +} + + diff --git a/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c b/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c index c54c761be1bb180fb37e033f7f3743ffe17320a8..d703479245e0c336419a59d7edd94154720e66c1 100644 --- a/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c +++ b/src/plugins/e-acsl/tests/bts/oracle/gen_issue69.c @@ -10,7 +10,7 @@ int main(void) __gen_e_acsl_forall = 1; __gen_e_acsl_c = (unsigned char)4; while (1) { - if (__gen_e_acsl_c <= 255) ; else break; + if (__gen_e_acsl_c < 256) ; else break; { int __gen_e_acsl_and; if (0 <= __gen_e_acsl_c) __gen_e_acsl_and = __gen_e_acsl_c <= 255; @@ -42,9 +42,9 @@ int main(void) while (1) { { int __gen_e_acsl_and_2; - if (-128 <= __gen_e_acsl_u) __gen_e_acsl_and_2 = __gen_e_acsl_u <= 127; + if (-128 <= __gen_e_acsl_u) __gen_e_acsl_and_2 = __gen_e_acsl_u < 128; else __gen_e_acsl_and_2 = 0; - __e_acsl_assert(__gen_e_acsl_and_2,1,"RTE","main","-128 <= u <= 127", + __e_acsl_assert(__gen_e_acsl_and_2,1,"RTE","main","-128 <= u < 128", "tests/bts/issue69.c",11); } if (__gen_e_acsl_u < __gen_e_acsl_m) ; else break; diff --git a/src/plugins/e-acsl/tests/bts/oracle/issue-eacsl-149.res.oracle b/src/plugins/e-acsl/tests/bts/oracle/issue-eacsl-149.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..7d556feb94e7297c6c986b727ae07354af5aacd4 --- /dev/null +++ b/src/plugins/e-acsl/tests/bts/oracle/issue-eacsl-149.res.oracle @@ -0,0 +1,4 @@ +[e-acsl] beginning translation. +[e-acsl] translation done in project "e-acsl". +[eva:alarm] tests/bts/issue-eacsl-149.c:4: Warning: + signed overflow. assert (unsigned int)((int)(-1)) + 1 ≤ 2147483647; diff --git a/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-149.e-acsl.err.log b/src/plugins/e-acsl/tests/bts/oracle_dev/issue-eacsl-149.e-acsl.err.log new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391