From 24c29ebf106eb66a9ccd063aa8083fba7f91e56d Mon Sep 17 00:00:00 2001 From: Thibaut Benjamin <thibaut.benjamin@gmail.com> Date: Fri, 21 Jan 2022 10:28:19 +0100 Subject: [PATCH] [e-acsl] linear typing for arithmetic operations --- src/plugins/e-acsl/src/analyses/analyses.ml | 2 + src/plugins/e-acsl/src/analyses/interval.ml | 425 +++++++++--------- src/plugins/e-acsl/src/analyses/interval.mli | 31 +- src/plugins/e-acsl/src/analyses/typing.ml | 138 +++--- src/plugins/e-acsl/src/analyses/typing.mli | 29 +- .../e-acsl/src/code_generator/quantif.ml | 4 +- .../src/code_generator/translate_ats.ml | 11 +- 7 files changed, 326 insertions(+), 314 deletions(-) diff --git a/src/plugins/e-acsl/src/analyses/analyses.ml b/src/plugins/e-acsl/src/analyses/analyses.ml index e2702e978ea..41266a7b2b3 100644 --- a/src/plugins/e-acsl/src/analyses/analyses.ml +++ b/src/plugins/e-acsl/src/analyses/analyses.ml @@ -29,6 +29,8 @@ let preprocess () = Logic_normalizer.preprocess ast; analyses_feedback "normalizing quantifiers"; Bound_variables.preprocess ast; + analyses_feedback "infering interval of annotations"; + Interval.infer_program ast; analyses_feedback "typing annotations"; Typing.type_program ast; analyses_feedback diff --git a/src/plugins/e-acsl/src/analyses/interval.ml b/src/plugins/e-acsl/src/analyses/interval.ml index 13c92e32857..5cad1f3925a 100644 --- a/src/plugins/e-acsl/src/analyses/interval.ml +++ b/src/plugins/e-acsl/src/analyses/interval.ml @@ -25,8 +25,8 @@ open Cil_types (* Implements Figure 3 of J. Signoles' JFLA'15 paper "Rester statique pour devenir plus rapide, plus précis et plus mince". Also implements a support for real numbers. *) - -module Error = Error.Make(struct let phase = Options.Dkey.interval end) +let dkey = Options.Dkey.interval +module Error = Error.Make(struct let phase = dkey end) (* ********************************************************************* *) (* Basic datatypes and operations *) @@ -461,7 +461,7 @@ end = struct new terms (see module {!Quantif}) which must be typed. The term corresponding to the bound variable [x] is actually used twice: once in the guard and once for encoding [x+1] when incrementing it. *) - let tbl : ival Error.or_error Misc.Id_term.Hashtbl.t = + let tbl : ival Error.result Misc.Id_term.Hashtbl.t = Misc.Id_term.Hashtbl.create 97 (* The interval of the logic function @@ -475,7 +475,7 @@ end = struct once. Since we test with physical equality, the information is not needed to determine the environment.*) - let dep_tbl : ival Error.or_error Id_term_in_profile.Hashtbl.t + let dep_tbl : ival Error.result Id_term_in_profile.Hashtbl.t = Id_term_in_profile.Hashtbl.create 97 let get_dep profile t = @@ -518,7 +518,7 @@ end = struct | _::_ -> memo_dep f t profile let clear () = - Options.feedback ~dkey ~level:4 "clearing the typing tables"; + Options.feedback ~level:4 "clearing the typing tables"; Misc.Id_term.Hashtbl.clear tbl; Id_term_in_profile.Hashtbl.clear dep_tbl @@ -632,226 +632,159 @@ let infer_sum_product oper lambda min max = match lambda, min, max with | Z.Overflow (* if the exponent of \product is too high *) -> top_ival) | _ -> Error.not_yet "extended quantifiers with non-integer parameters" -let rec infer t = +let rec infer ~logic_env t = let get_cty t = match t.term_type with Ctype ty -> ty | _ -> assert false in - match t.term_node with - | TConst (Integer (n, _)) -> singleton n - | TConst (LChr c) -> - let n = Cil.charConstToInt c in - singleton n - | TConst (LEnum enumitem) -> - let rec find_idx n = function - | [] -> assert false - | ei :: l -> if ei == enumitem then n else find_idx (n + 1) l - in - let n = Integer.of_int (find_idx 0 enumitem.eihost.eitems) in - singleton n - | TLval lv -> infer_term_lval lv - | TSizeOf ty -> infer_sizeof ty - | TSizeOfE t -> infer_sizeof (get_cty t) - | TSizeOfStr str -> singleton_of_int (String.length str + 1 (* '\0' *)) - | TAlignOf ty -> infer_alignof ty - | TAlignOfE t -> infer_alignof (get_cty t) - - | TUnOp (Neg, t) -> - let i = infer t in - lift_unop Ival.neg_int i - | TUnOp (BNot, t) -> - let i = infer t in - lift_unop Ival.bitwise_signed_not i - | TUnOp (LNot, _) - | TBinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), _, _) -> - Ival Ival.zero_or_one - - | TBinOp (PlusA, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.add_int i1 i2 - | TBinOp (MinusA, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.sub_int i1 i2 - | TBinOp (Mult, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.mul i1 i2 - | TBinOp (Div, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.div i1 i2 - | TBinOp (Mod, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.c_rem i1 i2 - | TBinOp (Shiftlt, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.shift_left i1 i2 - | TBinOp (Shiftrt, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.shift_right i1 i2 - | TBinOp (BAnd, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.bitwise_and i1 i2 - | TBinOp (BXor, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.bitwise_xor i1 i2 - | TBinOp (BOr, t1, t2) -> - let i1 = infer t1 in - let i2 = infer t2 in - lift_arith_binop Ival.bitwise_or i1 i2 - | TCastE (ty, t) -> - let src = infer t in - let dst = interv_of_typ ty in - cast ~src ~dst - | Tif (_, t2, t3) -> - let i2 = infer t2 in - let i3 = infer t3 in - join i2 i3 - | Tat (t, _) -> - infer t - | TBinOp (MinusPP, t, _) -> - (match Cil.unrollType (get_cty t) with - | TArray(_, _, _) as ta -> - begin - try - let n = Cil.bitsSizeOf ta in - (* the second argument must be in the same block than [t]. - Consequently the result of the difference belongs to - [0; \block_length(t)] *) - let nb_bytes = if n mod 8 = 0 then n / 8 else n / 8 + 1 in - ival Integer.zero (Integer.of_int nb_bytes) - with Cil.SizeOfError _ -> - Lazy.force interv_of_unknown_block - end - | TPtr _ -> - Lazy.force interv_of_unknown_block - | _ -> assert false) - | Tblock_length (_, t) - | Toffset(_, t) -> - (match Cil.unrollType (get_cty t) with - | TArray(_, _, _) as ta -> - begin - try - let n = Cil.bitsSizeOf ta in - let nb_bytes = if n mod 8 = 0 then n / 8 else n / 8 + 1 in - singleton_of_int nb_bytes - with Cil.SizeOfError _ -> - Lazy.force interv_of_unknown_block - end - | TPtr _ -> Lazy.force interv_of_unknown_block - | _ -> assert false) - | Tnull -> singleton_of_int 0 - | TLogic_coerce (_, t) -> infer t - | Tapp (li, lst, args) -> - (match li.l_body with - | LBpred _ -> - Ival Ival.zero_or_one - | LBterm t' -> - let rec fixpoint i = - let is_included, new_i = - Logic_function_env.widen ~infer t i - in - if is_included then begin - List.iter (fun lv -> Env.remove lv) li.l_profile; - new_i - end else - let i = infer t' in - List.iter (fun lv -> Env.remove lv) li.l_profile; - fixpoint i - in - fixpoint bottom - | LBnone when li.l_var_info.lv_name = "\\sum" || - li.l_var_info.lv_name = "\\product" -> - (match args with - | [ t1; t2; { term_node = Tlambda([ k ], _) } as lambda ] -> - let t1_iv = infer t1 in - let t2_iv = infer t2 in - let k_iv = join t1_iv t2_iv in - Env.add k k_iv; - let lambda_iv = infer lambda in - Env.remove k; - let t2incr = - Logic_const.term (TBinOp(PlusA, t2, Cil.lone ())) Linteger - in - (* it is correct and precise to use k_ival to compute lambda_ival, but - not during the code generation since the type used for k is the - greatest type between the type of t1 and the type of t2+1, that is - why the ival associated to k is updated *) - Env.add k (join t1_iv (infer t2incr)); - (* k is removed during code generation, it is needed for generating - the code of the lambda term *) - infer_sum_product li.l_var_info lambda_iv t1_iv t2_iv - | _ -> Error.not_yet "extended quantifiers without lambda term") - | LBnone when li.l_var_info.lv_name = "\\numof" -> - (match args with - | [ t1; t2; { term_node = Tlambda([ k ], p) } ] -> - let logic_info = Cil_const.make_logic_info "\\sum" in - logic_info.l_type <- li.l_type; - logic_info.l_tparams <- li.l_tparams; - logic_info.l_labels <- li.l_labels; - logic_info.l_profile <- li.l_profile; - logic_info.l_body <- li.l_body; - let numof_as_sum = - let conditional_term = - Logic_const.term - (Tif(p, Cil.lone (), Cil.lzero ())) Linteger - in - let lambda_term = - Logic_const.term (Tlambda([ k ], conditional_term)) Linteger - in - (Logic_const.term - (Tapp(logic_info, lst, [ t1; t2; lambda_term ])) Linteger) - in infer numof_as_sum - | _ -> - Options.fatal "unexpected input for an extended quantifier \\numof") - | LBnone - | LBreads _ -> - (match li.l_type with - | None -> assert false - | Some ret_type -> interv_of_logic_typ ret_type) - | LBinductive _ -> - Error.not_yet "logic functions inductively defined") - - | Tunion _ -> Error.not_yet "tset union" - | Tinter _ -> Error.not_yet "tset intersection" - | Tcomprehension (_,_,_) -> Error.not_yet "tset comprehension" - | Trange(Some n1, Some n2) -> - let i1 = infer n1 in - let i2 = infer n2 in - join i1 i2 - | Trange(None, _) | Trange(_, None) -> - Options.abort "unbounded ranges are not part of E-ACSl" - - | Tlet (li, t) -> - let li_t = Misc.term_of_li li in - let li_v = li.l_var_info in - let i1 = infer li_t in - Env.add li_v i1; - let i2 = infer t in - Env.remove li_v; - i2 - | TConst (LReal lr) -> - if lr.r_lower = lr.r_upper then Float(FDouble, Some lr.r_nearest) - else Rational - | Tlambda ([ _ ],lt) -> - infer lt - | Tlambda (_,_) - | TConst (LStr _ | LWStr _) - | TBinOp (PlusPI,_,_) - | TBinOp (MinusPI,_,_) - | TAddrOf _ - | TStartOf _ - | TDataCons (_,_) - | Tbase_addr (_,_) - | TUpdate (_,_,_) - | Ttypeof _ - | Ttype _ - | Tempty_set -> - Nan + let get_res = Error.map (fun x -> x) in + let compute t = + match t.term_node with + | TConst (Integer (n, _)) -> singleton n + | TConst (LChr c) -> + let n = Cil.charConstToInt c in + singleton n + | TConst (LEnum enumitem) -> + let rec find_idx n = function + | [] -> assert false + | ei :: l -> if ei == enumitem then n else find_idx (n + 1) l + in + let n = Integer.of_int (find_idx 0 enumitem.eihost.eitems) in + singleton n + | TLval lv -> infer_term_lval lv + | TSizeOf ty -> infer_sizeof ty + | TSizeOfE t -> infer_sizeof (get_cty t) + | TSizeOfStr str -> singleton_of_int (String.length str + 1 (* '\0' *)) + | TAlignOf ty -> infer_alignof ty + | TAlignOfE t -> infer_alignof (get_cty t) + + | TUnOp (Neg, t) -> + let i = infer ~logic_env t in + Error.map (lift_unop Ival.neg_int) i + | TUnOp (BNot, t) -> + let i = infer ~logic_env t in + Error.map (lift_unop Ival.bitwise_signed_not) i + | TUnOp (LNot, t) -> + ignore(infer ~logic_env t); + Ival Ival.zero_or_one + + | TBinOp ((Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr), t1, t2) -> + ignore(infer ~logic_env t1); + ignore(infer ~logic_env t2); + Ival Ival.zero_or_one + + | TBinOp (PlusA, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.add_int) i1 i2 + | TBinOp (MinusA, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.sub_int) i1 i2 + | TBinOp (Mult, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.mul) i1 i2 + | TBinOp (Div, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.div) i1 i2 + | TBinOp (Mod, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.c_rem) i1 i2 + | TBinOp (Shiftlt, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.shift_left) i1 i2 + | TBinOp (Shiftrt, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.shift_right) i1 i2 + | TBinOp (BAnd, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.bitwise_and) i1 i2 + | TBinOp (BXor, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.bitwise_xor) i1 i2 + | TBinOp (BOr, t1, t2) -> + let i1 = infer ~logic_env t1 in + let i2 = infer ~logic_env t2 in + Error.map2 (lift_arith_binop Ival.bitwise_or) i1 i2 + | TCastE (ty, t) -> + let src = infer ~logic_env t in + let dst = interv_of_typ ty in + Error.map (fun src -> cast ~src ~dst) src + | Tif (_, t2, t3) -> + let i2 = infer ~logic_env t2 in + let i3 = infer ~logic_env t3 in + Error.map2 join i2 i3 + | Tat (t, _) -> + get_res (infer ~logic_env t) + | TBinOp (MinusPP, t, _) -> + (match Cil.unrollType (get_cty t) with + | TArray(_, _, _) as ta -> + begin + try + let n = Cil.bitsSizeOf ta in + (* the second argument must be in the same block than [t]. + Consequently the result of the difference belongs to + [0; \block_length(t)] *) + let nb_bytes = if n mod 8 = 0 then n / 8 else n / 8 + 1 in + ival Integer.zero (Integer.of_int nb_bytes) + with Cil.SizeOfError _ -> + Lazy.force interv_of_unknown_block + end + | TPtr _ -> Lazy.force interv_of_unknown_block + | _ -> assert false) + | Tblock_length (_, t) + | Toffset(_, t) -> + (match Cil.unrollType (get_cty t) with + | TArray(_, _, _) as ta -> + begin + try + let n = Cil.bitsSizeOf ta in + let nb_bytes = if n mod 8 = 0 then n / 8 else n / 8 + 1 in + singleton_of_int nb_bytes + with Cil.SizeOfError _ -> + Lazy.force interv_of_unknown_block + end + | TPtr _ -> Lazy.force interv_of_unknown_block + | _ -> assert false) + | Tnull -> singleton_of_int 0 + | TLogic_coerce (_, t) -> get_res (infer ~logic_env t) + | Tapp (_,_,_) -> assert false + + | Tunion _ -> Error.not_yet "tset union" + | Tinter _ -> Error.not_yet "tset intersection" + | Tcomprehension (_,_,_) -> Error.not_yet "tset comprehension" + | Trange(Some n1, Some n2) -> + let i1 = infer ~logic_env n1 in + let i2 = infer ~logic_env n2 in + Error.map2 join i1 i2 + | Trange(None, _) | Trange(_, None) -> + Options.abort "unbounded ranges are not part of E-ACSl" + + | Tlet (_,_) -> assert false + | TConst (LReal lr) -> + if lr.r_lower = lr.r_upper then Float(FDouble, Some lr.r_nearest) + else Rational + | Tlambda ([ _ ],lt) -> + get_res (infer ~logic_env lt) + | Tlambda (_,_) + | TConst (LStr _ | LWStr _) + | TBinOp (PlusPI,_,_) + | TBinOp (IndexPI,_,_) + | TBinOp (MinusPI,_,_) + | TAddrOf _ + | TStartOf _ + | TDataCons (_,_) + | Tbase_addr (_,_) + | TUpdate (_,_,_) + | Ttypeof _ + | Ttype _ + | Tempty_set -> + Nan + in Memo.memo ~profile:(logic_env.profile) compute t and infer_term_lval (host, offset as tlv) = match offset with @@ -889,6 +822,50 @@ let infer t = include D +let typer_visitor ~logic_env = object + inherit E_acsl_visitor.visitor dkey + + (* global logic functions and predicates are evaluated are callsites *) + method !glob_annot _ = Cil.SkipChildren + + method !vterm t = + (* Do not raise a warning for e-acsl errors at preprocessing time, + those errrors are stored in the table and warnings are raised at + translation time*) + let _ = try ignore (infer ~logic_env t) + with Error.Not_yet _ | Error.Typing_error _ -> () + in + Cil.SkipChildren +end + +let infer_program ast = + let visitor = typer_visitor ~logic_env:(Logic_environment.create [] []) in + visitor#visit_file ast + +let preprocess_predicate ~logic_env p = + let visitor = typer_visitor ~logic_env in + ignore @@ visitor#visit_predicate p + +let preprocess_code_annot ~logic_env annot = + let visitor = typer_visitor ~logic_env in + ignore @@ visitor#visit_code_annot annot + +let preprocess_term ~logic_env t = + ignore (infer ~logic_env t) + +let get_p ~profile t = + let t = Logic_normalizer.get_term t in + Error.retrieve_preprocessing + "Interval inference" + (Memo.get ~profile) + t + Printer.pp_term + +let get ~logic_env = + get_p ~profile:(Logic_environment.get_profile logic_env) + +type profile = Profile.t + (* Local Variables: compile-command: "make -C ../../../../.." diff --git a/src/plugins/e-acsl/src/analyses/interval.mli b/src/plugins/e-acsl/src/analyses/interval.mli index 17faff5fc1c..98d4de40f61 100644 --- a/src/plugins/e-acsl/src/analyses/interval.mli +++ b/src/plugins/e-acsl/src/analyses/interval.mli @@ -109,12 +109,39 @@ end (** {3 Inference system} *) (* ************************************************************************** *) -val infer: Cil_types.term -> t -(** [infer t] infers the smallest possible integer interval which the values +(* val infer: Logic_environment.t -> Cil_types.term -> t *) +(* [infer t] infers the smallest possible integer interval which the values of the term can fit in. @raise Is_a_real if the term is either a float or a real. @raise Not_a_number if the term does not represent any number. *) +val get_p: profile:Profile.t -> Cil_types.term -> t +(** @return the value computed by the interval inference phase *) + +val get: logic_env:Logic_environment.t -> Cil_types.term -> t +(** @return the value computed by the interval inference phase, same as [get] + but with a full-fledged logic environment instead of a function profile *) + + +(*****************************************************************************) +(** {2 Interval processing} *) +(*****************************************************************************) + +val infer_program : Cil_types.file -> unit +(** compute and store the type of all the terms that will be translated + in a program *) + +val preprocess_predicate : + logic_env:Logic_environment.t -> Cil_types.predicate -> unit +(** compute and store the type of all the terms in a code annotation *) + +val preprocess_code_annot : + logic_env:Logic_environment.t -> Cil_types.code_annotation -> unit +(** compute and store the type of all the terms in a code annotation *) + +val preprocess_term : + logic_env:Logic_environment.t -> Cil_types.term -> unit + (* Local Variables: compile-command: "make -C ../../../../.." diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index d78ed712b88..4197e0974c2 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -332,7 +332,7 @@ let number_ty_of_typ ~post ty = | TVoid _ | TPtr _ | TArray _ | TFun _ | TComp _ | TBuiltin_va_list _ -> Nan | TNamed _ -> assert false -let ty_of_logic_ty ?term lty = +let ty_of_logic_ty ?term ?profile lty = let get_ty = function | Linteger -> Gmpz | Ctype ty -> number_ty_of_typ ~post:false ty @@ -344,9 +344,13 @@ let ty_of_logic_ty ?term lty = match term with | None -> get_ty lty | Some t -> + let profile = match profile with + | None -> assert false + | Some profile -> profile + in if Options.Gmp_only.get () && lty = Linteger then Gmpz else - let i = Interval.infer t in + let i = Interval.get_p ~profile t in ty_of_interv i (******************************************************************************) @@ -367,8 +371,8 @@ let mk_ctx ~use_gmp_opt = function (* the number_ty corresponding to [t] whenever use as an offset. In that case, it cannot be a GMP, so it must be coerced to an integral type in that case *) -let type_offset t = - let i = Interval.infer t in +let type_offset ~profile t = + let i = Interval.get_p ~profile t in match ty_of_interv i with | Gmpz -> C_integer ILongLong (* largest possible type *) | ty -> ty @@ -406,14 +410,14 @@ let rec type_term | TSizeOf _ | TSizeOfStr _ | TAlignOf _ -> - let i = Interval.infer t in + let i = Interval.get_p ~profile t in (* a constant or a left value directly under a lambda should be a gmp if the infered context for the lambda is gmp *) let ty = ty_of_interv ?ctx ~use_gmp_opt:under_lambda i in dup ty | TLval tlv -> - let i = Interval.infer t in + let i = Interval.get_p ~profile t in let ty = ty_of_interv ?ctx ~use_gmp_opt:under_lambda i in type_term_lval ~profile tlv; (* Options.feedback "Type : %a" D.pretty ty; *) @@ -423,14 +427,14 @@ let rec type_term | Tblock_length(_, t') | TSizeOfE t' | TAlignOfE t' -> - let i = Interval.infer t in + let i = Interval.get_p ~profile t in (* [t'] must be typed, but it is a pointer *) ignore (type_term ~use_gmp_opt:true ~ctx:Nan ~profile t'); let ty = ty_of_interv ?ctx i in dup ty | TBinOp (MinusPP, t1, t2) -> - let i = Interval.infer t in + let i = Interval.get_p ~profile t in (* [t1] and [t2] must be typed, but they are pointers *) ignore (type_term ~use_gmp_opt:true ~ctx:Nan ~profile t1); ignore (type_term ~use_gmp_opt:true ~ctx:Nan ~profile t2); @@ -438,8 +442,8 @@ let rec type_term dup ty | TUnOp (unop, t') -> - let i = Interval.infer t in - let i' = Interval.infer t' in + let i = Interval.get_p ~profile t in + let i' = Interval.get_p ~profile t' in let ctx_res, ctx = compute_ctx ?ctx (Interval.join i i') in ignore (type_term ~use_gmp_opt:true ~arith_operand:true ~ctx ~profile t'); (match unop with @@ -449,9 +453,9 @@ let rec type_term | TBinOp ((PlusA | MinusA | Mult | Div | Mod | Shiftlt | Shiftrt | BAnd | BOr | BXor), t1, t2) -> - let i = Interval.infer t in - let i1 = Interval.infer t1 in - let i2 = Interval.infer t2 in + let i = Interval.get_p ~profile t in + let i1 = Interval.get_p ~profile t1 in + let i2 = Interval.get_p ~profile t2 in let ctx_res, ctx = compute_ctx ?ctx (Interval.join i (Interval.join i1 i2)) in @@ -472,8 +476,8 @@ let rec type_term | TBinOp ((Lt | Gt | Le | Ge | Eq | Ne), t1, t2) -> assert (match ctx with None -> true | Some c -> D.compare c c_int >= 0); - let i1 = Interval.infer t1 in - let i2 = Interval.infer t2 in + let i1 = Interval.get_p ~profile t1 in + let i2 = Interval.get_p ~profile t2 in let ctx = mk_ctx ~use_gmp_opt:true (ty_of_interv ?ctx (Interval.join i1 i2)) in @@ -486,8 +490,8 @@ let rec type_term c_int, ty | TBinOp ((LAnd | LOr), t1, t2) -> - let i1 = Interval.infer t1 in - let i2 = Interval.infer t2 in + let i1 = Interval.get_p ~profile t1 in + let i2 = Interval.get_p ~profile t2 in let ty = ty_of_interv ?ctx (Interval.join i1 i2) in (* both operands fit in an int. *) ignore (type_term ~use_gmp_opt:true ~ctx:c_int ~profile t1); @@ -496,7 +500,7 @@ let rec type_term | TCastE(_, t') -> (* compute the smallest interval from the whole term [t] *) - let i = Interval.infer t in + let i = Interval.get_p ~profile t in (* nothing more to do: [i] is already more precise than what we could infer from the arguments of the cast. *) let ctx = ty_of_interv ?ctx i in @@ -508,9 +512,9 @@ let rec type_term mk_ctx ~use_gmp_opt:false c_int (* an int must be generated *) in ignore (type_term ~use_gmp_opt:false ~ctx:ctx1 ~profile t1); - let i = Interval.infer t in - let i2 = Interval.infer t2 in - let i3 = Interval.infer t3 in + let i = Interval.get_p ~profile t in + let i2 = Interval.get_p ~profile t2 in + let i3 = Interval.get_p ~profile t3 in let ctx = ty_of_interv ?ctx (Interval.join i (Interval.join i2 i3)) in let ctx = mk_ctx ~use_gmp_opt:true ctx in ignore (type_term ~use_gmp_opt:true ~ctx ~profile t2); @@ -535,7 +539,7 @@ let rec type_term | TBinOp ((PlusPI | MinusPI), t1, t2) -> (* both [t1] and [t2] must be typed. *) ignore (type_term ~use_gmp_opt:true ~ctx:Nan ~profile t1); - let ctx = type_offset t2 in + let ctx = type_offset ~profile t2 in ignore (type_term ~use_gmp_opt:false ~ctx ~profile t2); dup Nan @@ -570,8 +574,7 @@ let rec type_term args li.l_var_info.lv_name in - ignore (type_predicate ~profile:typed_args p); - List.iter Interval.Env.remove li.l_profile) + ignore (type_predicate ~profile:typed_args p)) pending_typing; dup c_int | LBterm t_body -> @@ -596,9 +599,7 @@ let rec type_term the type of the parameters in the current function calls *) ignore (type_term ~use_gmp_opt ~profile:typed_args t_body) in - let clear_env () = List.iter Interval.Env.remove li.l_profile in Stack.push - clear_env pending_typing; Stack.push type_args_and_body @@ -611,9 +612,9 @@ let rec type_term let anonymous = Logic_const.term (TBinOp(PlusA, t2, Cil.lone ())) Linteger in - let ty_bound = Interval.infer anonymous in + let ty_bound = Interval.get anonymous in let ty_bound = - ty_of_interv (Interval.join ty_bound (Interval.infer t1)) + ty_of_interv (Interval.join ty_bound (Interval.get t1)) in ignore (type_term @@ -621,7 +622,9 @@ let rec type_term ignore (type_term ~use_gmp_opt:true ~arith_operand:true ~ctx:ty_bound ~profile t2); - let ty = ty_of_interv (Interval.infer t) ~use_gmp_opt:true ?ctx in + let ty = + ty_of_interv (Interval.get_p ~profile t) ~use_gmp_opt:true ?ctx + in (* Options.feedback "type of extended quantifier: %a" D.pretty ty; *) ignore (type_term ~use_gmp_opt:true ?ctx ~profile lambda); dup ty @@ -647,7 +650,7 @@ let rec type_term | Trange(Some n1, Some n2) -> ignore (type_term ~use_gmp_opt ~profile n1); ignore (type_term ~use_gmp_opt ~profile n2); - let i = Interval.infer t in + let i = Interval.get_p ~profile t in let ty = ty_of_interv ?ctx i in dup ty @@ -694,28 +697,10 @@ and type_term_offset ~profile t = match t with | TField(_, toff) | TModel(_, toff) -> type_term_offset ~profile toff | TIndex(t, toff) -> - let ctx = type_offset t in + let ctx = type_offset ~profile t in ignore (type_term ~use_gmp_opt:false ~ctx ~profile t); type_term_offset ~profile toff -and type_args params ~use_gmp_opt ~profile args fname = - try - List.fold_right2 - (fun lv t (typed_args : Function_params_ty.t) -> - let ty_arg = (type_term ~use_gmp_opt ~lenv t).ty in - begin - try - let typ_arg = typ_of_number_ty ty_arg in - Interval.Env.add lv (Interval.interv_of_typ typ_arg) - with Not_a_number -> () - end; - ty_arg :: typed_args) - params - args - [] - with Invalid_argument _ -> - Options.fatal "[Tapp] unexpected number of arguments when calling %s" fname - (* [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 @@ -732,8 +717,8 @@ and type_args params ~use_gmp_opt ~profile args fname = 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 *) and type_bound_variables ~loc ~profile (t1, lv, t2) = - let i1 = Interval.infer t1 in - let i2 = Interval.infer t2 in + let i1 = Interval.get t1 in + let i2 = Interval.get t2 in let i = Interval.(widen (join i1 i2)) in let ctx = match lv.lv_type with | Linteger -> mk_ctx ~use_gmp_opt:true (ty_of_interv ~ctx:Gmpz i) @@ -790,18 +775,16 @@ and type_bound_variables ~loc ~profile (t1, lv, t2) = ignore (type_term ~use_gmp_opt:false ~ctx ~profile 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 + let _ = match ctx with | C_integer _ -> i (* [ -\infty; +\infty ] *) | Gmpz -> Interval.Ival (Ival.inject_range None None) | C_float _ | Rational | Real | Nan -> Options.fatal "unexpected quantification over %a" D.pretty ctx in - Interval.Env.add lv i; (t1, lv, t2) - <<<<<<< variant A -and type_predicate ~lenv p = +and type_predicate ~profile p = let p = Logic_normalizer.get_pred p in Cil.CurrentLoc.set p.pred_loc; (* this pattern matching also follows the formal rules of the JFLA's paper *) @@ -821,7 +804,6 @@ and type_predicate ~lenv p = li.l_var_info.lv_name in ignore (type_predicate ~profile:typed_args p); - List.iter Interval.Env.remove li.l_profile | LBnone -> () | LBreads _ -> () | LBinductive _ -> () @@ -832,8 +814,8 @@ and type_predicate ~lenv p = c_int | Pdangling _ -> Error.not_yet "\\dangling" | Prel(_, t1, t2) -> - let i1 = Interval.infer t1 in - let i2 = Interval.infer t2 in + let i1 = Interval.get_p ~profile t1 in + let i2 = Interval.get_p ~profile t2 in let i = Interval.join i1 i2 in let ctx = mk_ctx ~use_gmp_opt:true (ty_of_interv ~ctx:c_int i) in ignore (type_term ~use_gmp_opt:true ~ctx ~profile t1); @@ -882,9 +864,7 @@ and type_predicate ~lenv p = end | Pseparated tlist -> List.iter - (fun t -> - ignore - (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t)) + (fun t -> ignore (type_term ~use_gmp_opt:false ~ctx:Nan ~profile t)) tlist; c_int | Pinitialized(_, t) @@ -894,7 +874,7 @@ and type_predicate ~lenv p = | Pvalid_read(_, t) | Pobject_pointer(_,t) | Pvalid_function t -> - ignore (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t); + ignore (type_term ~use_gmp_opt:false ~ctx:Nan ~profile t); c_int | Pat(p, _) -> (type_predicate ~profile p).ty | Pfresh _ -> Error.not_yet "\\fresh" @@ -927,11 +907,17 @@ let unsafe_set t ?ctx ~logic_env ty = (** {2 Getters} *) (******************************************************************************) -let get_number_ty ~profile t = +let get_number_ty ~logic_env t = + let profile = Interval.Logic_environment.get_profile logic_env in (Error.retrieve_preprocessing "typing" (Memo.get ~profile) t Printer.pp_term).ty -let get_integer_op ~profile t = + +let get_integer_op ~logic_env t = + let profile = Interval.Logic_environment.get_profile logic_env in (Error.retrieve_preprocessing "typing" (Memo.get ~profile) t Printer.pp_term).op -let get_integer_op_of_predicate ~profile p = (type_predicate ~profile p).op + +let get_integer_op_of_predicate ~logic_env p = + let profile = Interval.Logic_environment.get_profile logic_env in + (type_predicate ~profile p).op (* {!typ_of_integer}, but handle the not-integer cases. *) let extract_typ t ty = @@ -945,23 +931,27 @@ let extract_typ t ty = | Lvar _ -> Error.not_yet "unsupported logic type: type variable" | Larrow _ -> Error.not_yet "unsupported logic type: type arrow" -let get_typ ~profile t = +let get_typ ~logic_env t = + let profile = Interval.Logic_environment.get_profile logic_env in let info = Error.retrieve_preprocessing "typing" (Memo.get ~profile) t Printer.pp_term in extract_typ t info.ty -let get_op ~profile t = +let get_op ~logic_env t = + let profile = Interval.Logic_environment.get_profile logic_env in let info = Error.retrieve_preprocessing "typing" (Memo.get ~profile) t Printer.pp_term in extract_typ t info.op -let get_cast ~profile t = +let get_cast ~logic_env t = + let profile = Interval.Logic_environment.get_profile logic_env in let info = Error.retrieve_preprocessing "typing" (Memo.get ~profile) t Printer.pp_term in try Option.map typ_of_number_ty info.cast with Not_a_number -> None -let get_cast_of_predicate ~profile p = +let get_cast_of_predicate ~logic_env p = + let profile = Interval.Logic_environment.get_profile logic_env in let info = type_predicate ~profile p in try Option.map typ_of_number_ty info.cast with Not_a_number -> assert false @@ -992,15 +982,19 @@ let type_code_annot lenv annot = let visitor = typing_visitor lenv in ignore @@ visitor#visit_code_annot annot -let preprocess_predicate profile p = +let preprocess_predicate ~logic_env p = Logic_normalizer.preprocess_predicate p; Bound_variables.preprocess_predicate p; - let visitor = typing_visitor lenv in + Interval.preprocess_predicate ~logic_env p; + let profile = Interval.Logic_environment.get_profile logic_env in + let visitor = typing_visitor profile in ignore @@ visitor#visit_predicate p -let preprocess_rte ~profile rte = +let preprocess_rte ~logic_env rte = Logic_normalizer.preprocess_annot rte; Bound_variables.preprocess_annot rte; + ignore (Interval.preprocess_code_annot ~logic_env rte); + let profile = Interval.Logic_environment.get_profile logic_env in type_code_annot profile rte (* diff --git a/src/plugins/e-acsl/src/analyses/typing.mli b/src/plugins/e-acsl/src/analyses/typing.mli index c5fd4406cfb..8e01a914147 100644 --- a/src/plugins/e-acsl/src/analyses/typing.mli +++ b/src/plugins/e-acsl/src/analyses/typing.mli @@ -119,29 +119,30 @@ val clear: unit -> unit {!type_named_predicate} has been previously computed for the given term or predicate. *) -val get_number_ty: profile:Interval.Profile.t -> term -> number_ty +val get_number_ty: logic_env:Interval.Logic_environment.t -> term -> number_ty (** @return the infered type for the given term. *) -val get_integer_op: profile:Interval.Profile.t -> term -> number_ty +val get_integer_op: logic_env:Interval.Logic_environment.t -> term -> number_ty (** @return the infered type for the top operation of the given term. It is meaningless to call this function over a non-arithmetical/logical operator. *) val get_integer_op_of_predicate: - profile:Interval.Profile.t -> predicate -> number_ty + logic_env:Interval.Logic_environment.t -> predicate -> number_ty (** @return the infered type for the top operation of the given predicate. *) -val get_typ: profile:Interval.Profile.t -> term -> typ +val get_typ: logic_env:Interval.Logic_environment.t -> term -> typ (** Get the type which the given term must be generated to. *) -val get_op: profile:Interval.Profile.t -> term -> typ +val get_op: logic_env:Interval.Logic_environment.t -> term -> typ (** Get the type which the operation on top of the given term must be generated to. *) -val get_cast: profile:Interval.Profile.t -> term -> typ option +val get_cast: logic_env:Interval.Logic_environment.t -> term -> typ option (** Get the type which the given term must be converted to (if any). *) -val get_cast_of_predicate: profile:Interval.Profile.t -> predicate -> typ option +val get_cast_of_predicate: + logic_env:Interval.Logic_environment.t -> predicate -> typ option (** Like {!get_cast}, but for predicates. *) val unsafe_set: @@ -160,14 +161,24 @@ val unsafe_set: val typ_of_lty: logic_type -> typ (** @return the C type that correponds to the given logic type. *) +(*****************************************************************************) +(** {2 Typing processing} *) +(*****************************************************************************) + val type_program : file -> unit (** compute and store the type of all the terms that will be translated in a program *) -val preprocess_predicate : Interval.Profile.t -> predicate -> unit +val preprocess_predicate : + logic_env:Interval.Logic_environment.t -> + predicate -> + unit (** compute and store the types of all the terms in a given predicate *) -val preprocess_rte : profile:Interval.Profile.t -> code_annotation -> unit +val preprocess_rte : + logic_env:Interval.Logic_environment.t -> + code_annotation -> + unit (** compute and store the type of all the terms in a code annotation *) (* diff --git a/src/plugins/e-acsl/src/code_generator/quantif.ml b/src/plugins/e-acsl/src/code_generator/quantif.ml index a73720ef31b..02d65f25a48 100644 --- a/src/plugins/e-acsl/src/code_generator/quantif.ml +++ b/src/plugins/e-acsl/src/code_generator/quantif.ml @@ -48,8 +48,8 @@ let rec has_empty_quantif_with_false_negative = function (* case 2 *) false | (t1, _, t2) :: guards -> - let iv1 = Interval.(extract_ival (infer t1)) in - let iv2 = Interval.(extract_ival (infer t2)) in + let iv1 = Interval.(extract_ival (get t1)) in + let iv2 = Interval.(extract_ival (get t2)) in let lower_bound, _ = Ival.min_and_max iv1 in let _, upper_bound = Ival.min_and_max iv2 in begin diff --git a/src/plugins/e-acsl/src/code_generator/translate_ats.ml b/src/plugins/e-acsl/src/code_generator/translate_ats.ml index 3564526a8f5..cbeed7f335d 100644 --- a/src/plugins/e-acsl/src/code_generator/translate_ats.ml +++ b/src/plugins/e-acsl/src/code_generator/translate_ats.ml @@ -92,7 +92,7 @@ end where [t_size = tmax - tmin + (-1|0|1)] depending on whether the inequalities are strict or large and [t_shifted = lv - tmin + (-1|0)] (so that we start indexing at 0) *) -let rec sizes_and_shifts_from_quantifs ~loc kf lscope sizes_and_shifts = +let rec sizes_and_shifts_from_quantifs ~loc ~logic_env kf lscope sizes_and_shifts = match lscope with | [] -> sizes_and_shifts @@ -117,7 +117,7 @@ let rec sizes_and_shifts_from_quantifs ~loc kf lscope sizes_and_shifts = | _ -> Options.fatal "Unexpected comparison operator" in - let iv = Interval.(extract_ival (infer t_size)) in + let iv = Interval.(extract_ival (get ~logic_env t_size)) in (* The EXACT amount of memory that is needed can be known at runtime. This is because the tightest bounds for the variables can be known at runtime. Example: In the following predicate @@ -156,12 +156,12 @@ let rec sizes_and_shifts_from_quantifs ~loc kf lscope sizes_and_shifts = in (* Returning *) let sizes_and_shifts = (t_size, t_shifted) :: sizes_and_shifts in - sizes_and_shifts_from_quantifs ~loc kf lscope' sizes_and_shifts + sizes_and_shifts_from_quantifs ~loc ~logic_env kf lscope' sizes_and_shifts | (Lvs_let(_, t) | Lvs_global(_, t)) :: _ when Misc.term_has_lv_from_vi t -> Error.not_yet "\\at with logic variable linked to C variable" | Lvs_let _ :: lscope' -> - sizes_and_shifts_from_quantifs ~loc kf lscope' sizes_and_shifts + sizes_and_shifts_from_quantifs ~loc ~logic_env kf lscope' sizes_and_shifts | Lvs_formal _ :: _ -> Error.not_yet "\\at using formal variable of a logic function" | Lvs_global _ :: _ -> @@ -307,8 +307,9 @@ let pretranslate_to_exp_with_lscope ~loc ~lscope kf env pot = let term_to_exp = !term_to_exp_ref in let lscope_vars = Lscope.get_all lscope in let lscope_vars = List.rev lscope_vars in + let logic_env = Env.Logic_env.get env in let sizes_and_shifts = - sizes_and_shifts_from_quantifs ~loc kf lscope_vars [] + sizes_and_shifts_from_quantifs ~loc ~logic_env kf lscope_vars [] in let logic_env = Env.Logic_env.get env in (* Creating the pointer *) -- GitLab