diff --git a/Changelog b/Changelog index 3093bcd3766b85f6b85958dd6bfc49fd33f42bc0..d6cb74c629b2bcd3289153888bdb04ce073246b7 100644 --- a/Changelog +++ b/Changelog @@ -17,6 +17,8 @@ Open Source Release <next-release> ################################## +o! Kernel [2021-04-09] Change the type of the measure from string to + logic_info in the variant AST node. -* Kernel [2021-03-26] Raise an error if trying to merge a tentative definition with a proper definition during linking o Ptests [2021-03-24] Add new EXIT directive diff --git a/src/kernel_services/ast_data/cil_types.mli b/src/kernel_services/ast_data/cil_types.mli index eb920a51c908ceb0db8dc38638bc32db2d169db3..0c6f467509621c2a67019ac8153939e94e340d95 100644 --- a/src/kernel_services/ast_data/cil_types.mli +++ b/src/kernel_services/ast_data/cil_types.mli @@ -1620,7 +1620,7 @@ and predicate = { } (** variant of a loop or a recursive function. *) -and variant = term * string option +and variant = term * logic_info option (** allocates and frees. @since Oxygen-20120901 *) diff --git a/src/kernel_services/ast_data/property.ml b/src/kernel_services/ast_data/property.ml index 506bb221ee18751fb718f5c3806dc5f243890f31..6d6d06b8b3a2c7a741a6e75492d64b5a936afa1b 100644 --- a/src/kernel_services/ast_data/property.ml +++ b/src/kernel_services/ast_data/property.ml @@ -1121,7 +1121,7 @@ struct else b.b_name ^ "_" let variant_suffix = function - | (_,Some s) -> s + | (_,Some s) -> s.l_var_info.lv_name | _ -> "" let string_of_termination_kind = function diff --git a/src/kernel_services/ast_printing/cil_printer.ml b/src/kernel_services/ast_printing/cil_printer.ml index 10e359d4b91f4b15b4365918dc2ad4b1a54a86c9..a98a8021d4d29a6ad15946a930dd330743cd8802 100644 --- a/src/kernel_services/ast_printing/cil_printer.ml +++ b/src/kernel_services/ast_printing/cil_printer.ml @@ -2862,13 +2862,12 @@ class cil_printer () = object (self) method private decrement kw fmt (t, rel) = match rel with | None -> fprintf fmt "@[<2>%a@ %a;@]" self#pp_acsl_keyword kw self#term t - | Some str -> - (*TODO: replace this string with an interpreted variable*) - fprintf fmt "@[<2>%a@ %a@ %a@ %s;@]" + | Some li -> + fprintf fmt "@[<2>%a@ %a@ %a@ %a;@]" self#pp_acsl_keyword kw self#term t self#pp_acsl_keyword "for" - str + self#logic_info li method decreases fmt v = self#decrement "decreases" fmt v method variant fmt v = self#decrement "loop variant" fmt v diff --git a/src/kernel_services/ast_printing/cil_types_debug.ml b/src/kernel_services/ast_printing/cil_types_debug.ml index 60f93f7bb1b8c480cf391f4ef18f14c4ae95bd1f..d180c0eb9b16f72345d92c089f43e960643c6aa8 100644 --- a/src/kernel_services/ast_printing/cil_types_debug.ml +++ b/src/kernel_services/ast_printing/cil_types_debug.ml @@ -1037,7 +1037,7 @@ and pp_global_annotation fmt = function and pp_custom_tree fmt _custom_tree = Format.fprintf fmt "CustomDummy" -and pp_variant fmt = pp_pair pp_term (pp_option pp_string) fmt +and pp_variant fmt = pp_pair pp_term (pp_option pp_logic_info) fmt let pp_kinstr fmt = function | Kstmt(stmt) -> Format.fprintf fmt "Kstmt(%a)" pp_stmt stmt diff --git a/src/kernel_services/ast_queries/logic_typing.ml b/src/kernel_services/ast_queries/logic_typing.ml index 177bb48546c3d0fb3e8253674bd6c053cca8e62b..a1b0106a43512a0cb8a4b521ae017c404d3fa455 100644 --- a/src/kernel_services/ast_queries/logic_typing.ml +++ b/src/kernel_services/ast_queries/logic_typing.ml @@ -3541,7 +3541,11 @@ struct let type_variant env = function | (t, None) -> (type_int_term (base_ctxt env) env t, None) - | (t, r) -> (term env t, r) + | (t, Some r) -> + let loc = t.lexpr_loc in + let t = term env t in + let li, _, _, _ = type_logic_app env loc r [] [t; t] in + (t, Some li) let id_predicate env pred = Logic_const.new_predicate (predicate env pred) diff --git a/src/plugins/e-acsl/Makefile.in b/src/plugins/e-acsl/Makefile.in index cdf795b172ca4a20b047f8b8e7ffaf7abed40ceb..83c4a7647cb171e1138b9eadb3212f9056058ab9 100644 --- a/src/plugins/e-acsl/Makefile.in +++ b/src/plugins/e-acsl/Makefile.in @@ -83,11 +83,12 @@ SRC_CODE_GENERATOR:= \ label \ env \ rational \ + typed_number \ + logic_functions \ loops \ quantif \ at_with_lscope \ memory_translate \ - logic_functions \ logic_array \ translate \ contract \ diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index 8fed599b80e26fcb87b3f3cf7fc0e4475e1f9975..f3a19a88325ff3e46a7405c06ec4c4e4cf0cc63b 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -25,6 +25,7 @@ Plugin E-ACSL <next-release> ############################ +- E-ACSL [2021-04-09] Add support for loop variant. - E-ACSL [2021-04-07] Add support for multiple binders in guarded quantifications (frama-c/e-acsl#127). -* runtime [2021-04-08] Fix backtrace output on failed assertion diff --git a/src/plugins/e-acsl/doc/refman/intro_modern.tex b/src/plugins/e-acsl/doc/refman/intro_modern.tex index a8514fc0dd945aa19088ab45a021b7878c01b97f..4e85af395ced1a11e8513c64f3c13fb55cca4d32 100644 --- a/src/plugins/e-acsl/doc/refman/intro_modern.tex +++ b/src/plugins/e-acsl/doc/refman/intro_modern.tex @@ -61,7 +61,6 @@ currently implemented into the \framac's \eacsl plug-in. annotations & behavior-specific annotations \\ & loop assigns \\ - & loop variants \\ & global annotations \\ \hline diff --git a/src/plugins/e-acsl/doc/refman/loops.tex b/src/plugins/e-acsl/doc/refman/loops.tex index 1de09ce3a638ab6f99d83e5be39edbb0b1cf955e..f9e3599b0b739122e5c63a997eaf979734a34b0f 100644 --- a/src/plugins/e-acsl/doc/refman/loops.tex +++ b/src/plugins/e-acsl/doc/refman/loops.tex @@ -11,7 +11,7 @@ \ loop-annot ::= loop-clause* ; { loop-behavior* } ; - { loop-variant? } + loop-variant? \ loop-clause ::= loop-invariant ; | { loop-assigns } @@ -23,6 +23,6 @@ { loop-behavior } ::= { "for" id ("," id)* ":" } ; { loop-clause* } ; \hspace{-35mm} annotation for behavior $id$ \ - { loop-variant } ::= { "loop" "variant" term ";" } ; - | { "loop" "variant" term "for" id ";" } ; \hspace{-35mm} variant for relation $id$ + loop-variant ::= "loop" "variant" term ";" ; + | "loop" "variant" term "for" id ";" ; \hspace{-35mm} variant for relation $id$ \end{syntax} diff --git a/src/plugins/e-acsl/headers/header_spec.txt b/src/plugins/e-acsl/headers/header_spec.txt index b617a2a25a6db53ae67acc05e0627d8041b58505..82e0dc5a87c8525b82c160f0d198bed05dbead73 100644 --- a/src/plugins/e-acsl/headers/header_spec.txt +++ b/src/plugins/e-acsl/headers/header_spec.txt @@ -122,6 +122,8 @@ src/code_generator/translate.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/code_generator/translate.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/code_generator/translate_annots.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/code_generator/translate_annots.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/code_generator/typed_number.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL +src/code_generator/typed_number.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/dependencies/dep_eva.mli: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/dependencies/dep_eva.enabled.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL src/dependencies/dep_eva.disabled.ml: CEA_LGPL_OR_PROPRIETARY.E_ACSL diff --git a/src/plugins/e-acsl/src/code_generator/env.ml b/src/plugins/e-acsl/src/code_generator/env.ml index d10a15f1587e2974fbaa5c5311a6c1ad515a20a9..24775347413b7975db7d758f4c61ae1adcdf6b5a 100644 --- a/src/plugins/e-acsl/src/code_generator/env.ml +++ b/src/plugins/e-acsl/src/code_generator/env.ml @@ -54,6 +54,11 @@ type local_env = { rte: bool } +type loop_env = { + variant: (term * logic_info option) option; + invariants: toplevel_predicate list; +} + type t = { lscope: Lscope.t; lscope_reset: bool; @@ -67,8 +72,8 @@ type t = { (* Stack of contracts for active functions and statements *) var_mapping: Varinfo.t Stack.t Logic_var.Map.t; (* records of C bindings for logic vars *) - loop_invariants: toplevel_predicate list list; - (* list of loop invariants for each currently visited loops *) + loop_envs: loop_env list; + (* list of loop environment for each currently visited loops *) cpt: int; (* counter used when generating variables *) } @@ -88,6 +93,9 @@ let empty_local_env = mp_tbl = empty_mp_tbl; rte = true } +let empty_loop_env = + { variant = None; + invariants = [] } let empty = { lscope = Lscope.empty; lscope_reset = true; @@ -97,7 +105,7 @@ let empty = env_stack = []; contract_stack = []; var_mapping = Logic_var.Map.empty; - loop_invariants = []; + loop_envs = []; cpt = 0 } let top env = match env.env_stack with @@ -113,15 +121,36 @@ let has_no_new_stmt env = (* ************************************************************************** *) let push_loop env = - { env with loop_invariants = [] :: env.loop_invariants } + { env with loop_envs = empty_loop_env :: env.loop_envs } -let add_loop_invariant env inv = match env.loop_invariants with +let top_loop_env env = + match env.loop_envs with | [] -> assert false - | invs :: tl -> { env with loop_invariants = (inv :: invs) :: tl } + | loop_env :: tl -> loop_env, tl + +let set_loop_variant ?measure env t = + let loop_env, tl = top_loop_env env in + let loop_env = { loop_env with variant = Some (t, measure) } in + { env with loop_envs = loop_env :: tl } -let pop_loop env = match env.loop_invariants with +let add_loop_invariant env inv = + match env.loop_envs with | [] -> assert false - | invs :: tl -> invs, { env with loop_invariants = tl } + | loop_env :: tl -> + let loop_env = { loop_env with invariants = inv :: loop_env.invariants} in + { env with loop_envs = loop_env :: tl } + +let top_loop_variant env = + let loop_env, _ = top_loop_env env in + loop_env.variant + +let top_loop_invariants env = + let loop_env, _ = top_loop_env env in + loop_env.invariants + +let pop_loop env = + let _, tl = top_loop_env env in + { env with loop_envs = tl } (* ************************************************************************** *) (** {2 RTEs} *) diff --git a/src/plugins/e-acsl/src/code_generator/env.mli b/src/plugins/e-acsl/src/code_generator/env.mli index 8c8152b97183aeaa0be6d330b6fc8ca4b1f4684f..3c521f8635eaedc7fbb6c2e47538343b07a76789 100644 --- a/src/plugins/e-acsl/src/code_generator/env.mli +++ b/src/plugins/e-acsl/src/code_generator/env.mli @@ -153,12 +153,15 @@ val annotation_kind: t -> Smart_stmt.annotation_kind val set_annotation_kind: t -> Smart_stmt.annotation_kind -> t (* ************************************************************************** *) -(** {2 Loop invariants} *) +(** {2 Loop annotations} *) (* ************************************************************************** *) val push_loop: t -> t +val set_loop_variant: ?measure:logic_info -> t -> term -> t val add_loop_invariant: t -> toplevel_predicate -> t -val pop_loop: t -> toplevel_predicate list * t +val top_loop_variant: t -> (term * logic_info option) option +val top_loop_invariants: t -> toplevel_predicate list +val pop_loop: t -> t (* ************************************************************************** *) (** {2 RTEs} *) diff --git a/src/plugins/e-acsl/src/code_generator/gmp.ml b/src/plugins/e-acsl/src/code_generator/gmp.ml index cfc504740298f8517998be505a7512b21b9e50a4..ce2757719cbb4693ca98c49c0160925e35b0fd99 100644 --- a/src/plugins/e-acsl/src/code_generator/gmp.ml +++ b/src/plugins/e-acsl/src/code_generator/gmp.ml @@ -35,6 +35,23 @@ let apply_on_var ~loc funname e = in Smart_stmt.rtl_call ~loc ~prefix funname [ e ] +let name_of_mpz_arith_bop = function + | PlusA -> "__gmpz_add" + | MinusA -> "__gmpz_sub" + | Mult -> "__gmpz_mul" + | Div -> "__gmpz_tdiv_q" + | Mod -> "__gmpz_tdiv_r" + | BAnd -> "__gmpz_and" + | BOr -> "__gmpz_ior" + | BXor -> "__gmpz_xor" + | Shiftlt -> "__gmpz_mul_2exp" + | Shiftrt -> "__gmpz_tdiv_q_2exp" + | Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr | PlusPI | IndexPI | MinusPI + | MinusPP as bop -> + Options.fatal + "Operation '%a' either not arithmetic or not supported on GMP integers" + Printer.pp_binop bop + let init ~loc e = apply_on_var "init" ~loc e let clear ~loc e = apply_on_var "clear" ~loc e diff --git a/src/plugins/e-acsl/src/code_generator/gmp.mli b/src/plugins/e-acsl/src/code_generator/gmp.mli index db4811dbeca2f1b6925cdc933215ea28c7a5cded..a4d86ff66e8969776b4061d4ba26e9990621bf08 100644 --- a/src/plugins/e-acsl/src/code_generator/gmp.mli +++ b/src/plugins/e-acsl/src/code_generator/gmp.mli @@ -24,6 +24,10 @@ open Cil_types +val name_of_mpz_arith_bop: binop -> string +(** [name_of_mpz_arith_bop bop] returns the name of the GMP function on integer + corresponding to the [bop] arithmetic operation. *) + val init: loc:location -> exp -> stmt (** build stmt [mpz_init(v)] or [mpq_init(v)] depending on typ of [v] *) diff --git a/src/plugins/e-acsl/src/code_generator/injector.ml b/src/plugins/e-acsl/src/code_generator/injector.ml index f095fa81d2cf4144cf04e60d98175a71b82b5bda..d9d96e9c8d7b05790452beef63497ffde4f3a958 100644 --- a/src/plugins/e-acsl/src/code_generator/injector.ml +++ b/src/plugins/e-acsl/src/code_generator/injector.ml @@ -224,8 +224,8 @@ let add_new_block_in_stmt env kf stmt = end else env in - (* handle loop invariants *) - let new_stmt, env = Loops.preserve_invariant env kf stmt in + (* handle loop annotations *) + let new_stmt, env = Loops.handle_annotations env kf stmt in new_stmt, env else stmt, env diff --git a/src/plugins/e-acsl/src/code_generator/logic_functions.ml b/src/plugins/e-acsl/src/code_generator/logic_functions.ml index ebb75d0b2b7cda9a7955284269fcb74cab8f1041..860869d2fdc79820789af7e753044cdb8aad74ce 100644 --- a/src/plugins/e-acsl/src/code_generator/logic_functions.ml +++ b/src/plugins/e-acsl/src/code_generator/logic_functions.ml @@ -283,7 +283,9 @@ let add_generated_functions globals = in List.rev rev_globals -let tapp_to_exp ~loc fname env kf t li params_ty args = +(* Generate (and memoize) the function body and create the call to the + generated function. *) +let function_to_exp ~loc fname env kf t li params_ty args = let ret_ty = Typing.get_typ t in let gen tbl = let vi, kf, gen_body = generate_kf fname ~loc env ret_ty params_ty li in @@ -342,6 +344,101 @@ let tapp_to_exp ~loc fname env kf t li params_ty args = ret_ty (fun vi _ -> [ Cil.mkStmtOneInstr ~valid_sid:true (mkcall vi) ]) +let tapp_to_exp kf env ?eargs tapp = + match tapp.term_node with + | Tapp(li, [], targs) -> + let loc = tapp.term_loc in + let fname = li.l_var_info.lv_name in + (* build the varinfo (as an expression) which stores the result of the + function call. *) + let _, e, env = + if Builtins.mem li.l_var_info.lv_name then + (* E-ACSL built-in function call *) + let args, env = + match eargs with + | None -> + List.fold_right + (fun targ (l, env) -> + let e, env = !term_to_exp_ref kf env targ in + e :: l, env) + targs + ([], env) + | Some eargs -> + if List.compare_lengths targs eargs != 0 then + Options.fatal + "[Tapp] unexpected number of arguments when calling %s" + fname; + eargs, env + in + Env.new_var + ~loc + ~name:(fname ^ "_app") + env + kf + (Some tapp) + (Misc.cty (Option.get li.l_type)) + (fun vi _ -> + [ Smart_stmt.rtl_call ~loc + ~result:(Cil.var vi) + ~prefix:"" + fname + args ]) + else + (* build the arguments and compute the integer_ty of the parameters *) + let params_ty, args, env = + let eargs, env = + match eargs with + | None -> + List.fold_right + (fun targ (eargs, env) -> + let e, env = !term_to_exp_ref kf env targ in + e :: eargs, env) + targs + ([], env) + | Some eargs -> + if List.compare_lengths targs eargs != 0 then + Options.fatal + "[Tapp] unexpected number of arguments when calling %s" + fname; + eargs, env + in + try + List.fold_right2 + (fun targ earg (params_ty, args, env) -> + let param_ty = Typing.get_number_ty targ in + let e, env = + try + let ty = Typing.typ_of_number_ty param_ty in + Typed_number.add_cast + ~loc + env + kf + (Some ty) + Typed_number.C_number + (Some targ) + earg + with Typing.Not_a_number -> + earg, env + in + param_ty :: params_ty, e :: args, env) + targs eargs + ([], [], env) + with Invalid_argument _ -> + Options.fatal + "[Tapp] unexpected number of arguments when calling %s" + fname + in + let gen_fname = + Varname.get ~scope:Varname.Global (Functions.RTL.mk_gen_name fname) + in + function_to_exp ~loc gen_fname env kf tapp li params_ty args + in + e, env + | _ -> + Options.fatal + "tapp_to_exp called with '%a' instead of Tapp term" + Printer.pp_term tapp + (* Local Variables: compile-command: "make -C ../.." diff --git a/src/plugins/e-acsl/src/code_generator/logic_functions.mli b/src/plugins/e-acsl/src/code_generator/logic_functions.mli index 0f8898ffb67155c4231e4f960f4d80b6c15c6ad8..6e3e662d05d912a3052c90d734515200a95bf118 100644 --- a/src/plugins/e-acsl/src/code_generator/logic_functions.mli +++ b/src/plugins/e-acsl/src/code_generator/logic_functions.mli @@ -37,10 +37,9 @@ open Cil_types val reset: unit -> unit val tapp_to_exp: - loc:location -> - string -> Env.t -> kernel_function -> - term -> logic_info -> Typing.number_ty list -> exp list -> - varinfo * exp * Env.t + kernel_function -> Env.t -> ?eargs:exp list -> term -> exp * Env.t +(** Translate a Tapp term to an expression. If the optional argument [eargs] is + provided, then these expressions are used as arguments of the fonction. *) val add_generated_functions: global list -> global list (* @return the input list of globals in which the generated functions have been diff --git a/src/plugins/e-acsl/src/code_generator/loops.ml b/src/plugins/e-acsl/src/code_generator/loops.ml index ab0c7fa3d38c81d2a0e42f34332bdcb9897d0ed0..656f790c6b03964797ddc44cf27dd361cb7cb684 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.ml +++ b/src/plugins/e-acsl/src/code_generator/loops.ml @@ -40,17 +40,129 @@ let term_to_exp_ref = Extlib.mk_fun "term_to_exp_ref" (**************************************************************************) -(************************* Loop invariants ********************************) +(************************* Loop annotations *******************************) (**************************************************************************) -let preserve_invariant env kf stmt = match stmt.skind with +let handle_annotations env kf stmt = + match stmt.skind with | Loop(_, ({ bstmts = stmts } as blk), loc, cont, break) -> - let rec handle_invariants (stmts, env as acc) = function - | [] -> - (* empty loop body: no need to verify the invariant twice *) - acc + (* Loop variant, save the current value of the variant *) + let stmts, env, variant = + match Env.top_loop_variant env with + | Some (t, measure_opt) -> + let env = Env.set_annotation_kind env Smart_stmt.Variant in + let env = Env.push env in + Typing.type_term ~use_gmp_opt:true t; + let ty = Typing.get_typ t in + let e, env = !term_to_exp_ref kf env t in + let vi_old, e_old, env = + Env.new_var + ~loc + ~scope:Varname.Function + ~name:"old_variant" + env + kf + (Some t) + ty + (fun _ _ -> []) + in + let stmt = + Smart_stmt.assigns ~loc ~result:(Cil.var vi_old) e + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + Smart_stmt.block_stmt blk :: stmts, env, Some (t, e_old, measure_opt) + | None -> stmts, env, None + in + (* Auxiliary function to generate variant and invariant checks *) + let rec aux (stmts, env) = function + | [] -> begin + (* No statements remaining in the loop: variant check *) + let env = Env.set_annotation_kind env Smart_stmt.Variant in + match variant with + | Some (t, e_old, Some measure) -> + let env = Env.push env in + let t_old = { t with term_node = t.term_node } in + let tapp = + { term_node = Tapp(measure, [], [t_old; t]); + term_loc = loc; + term_name = []; + term_type = Linteger;} + in + Typing.type_term ~use_gmp_opt:true tapp; + let e, env = !term_to_exp_ref kf env t in + let e_tapp, env = + Logic_functions.tapp_to_exp kf env ~eargs:[e_old; e] tapp + in + let msg = + Format.asprintf + "%s(old %a, %a)" + measure.l_var_info.lv_name + Printer.pp_term t_old + Printer.pp_term t + in + let stmt = + Smart_stmt.runtime_check_with_msg + ~loc + msg + ~pred_kind:Assert + Smart_stmt.Variant + kf + e_tapp + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + let stmts = Smart_stmt.block_stmt blk :: stmts in + stmts, env + | Some (t, e_old, None) -> + let env = Env.push env in + Typing.type_term ~use_gmp_opt:true t; + let e, env = !term_to_exp_ref kf env t in + let msg1 = + Format.asprintf + "(old %a) %a 0" + Printer.pp_term t + Printer.pp_relation Rge + in + let msg2 = + Format.asprintf + "(old %a) > %a" + Printer.pp_term t + Printer.pp_term t + in + let stmt = + Smart_stmt.block_from_stmts [ + Smart_stmt.runtime_check_with_msg + ~loc + msg1 + ~pred_kind:Assert + Smart_stmt.Variant + kf + (Cil.mkBinOp ~loc Ge e_old (Cil.zero ~loc)); + Smart_stmt.runtime_check_with_msg + ~loc + msg2 + ~pred_kind:Assert + Smart_stmt.Variant + kf + (Cil.mkBinOp ~loc Gt e_old e) + ] + in + let blk, env = + Env.pop_and_get env stmt ~global_clear:false Env.Middle + in + let stmts = Smart_stmt.block_stmt blk :: stmts in + stmts, env + | None -> + stmts, env + end | [ last ] -> - let invariants, env = Env.pop_loop env in + (* Last statement of the loop: invariant check *) + (* Optimisation to only verify invariant on a non-empty body loop. *) + let invariants = Env.top_loop_invariants env in + let env = Env.set_annotation_kind env Smart_stmt.Invariant in let env = Env.push env in let env = let translate_named_predicate = !translate_predicate_ref in @@ -59,15 +171,15 @@ let preserve_invariant env kf stmt = match stmt.skind with let blk, env = Env.pop_and_get env last ~global_clear:false Env.Before in - Smart_stmt.block last blk :: stmts, env - | s :: tl -> - handle_invariants (s :: stmts, env) tl + aux (Smart_stmt.block last blk :: stmts, env) [] + | hd :: tl -> aux (hd :: stmts, env) tl in - let env = Env.set_annotation_kind env Smart_stmt.Invariant in - let stmts, env = handle_invariants ([], env) stmts in + let stmts, env = aux ([], env) stmts in + (* The loop annotations have been handled, the loop environment can be + popped *) + let env = Env.pop_loop env in let new_blk = { blk with bstmts = List.rev stmts } in - { stmt with skind = Loop([], new_blk, loc, cont, break) }, - env + { stmt with skind = Loop([], new_blk, loc, cont, break) }, env | _ -> stmt, env diff --git a/src/plugins/e-acsl/src/code_generator/loops.mli b/src/plugins/e-acsl/src/code_generator/loops.mli index c797919dabca98e48323974115c2c0dc560f9d56..20b0a1262aceec6a10d19f14324afbfbb318eaff 100644 --- a/src/plugins/e-acsl/src/code_generator/loops.mli +++ b/src/plugins/e-acsl/src/code_generator/loops.mli @@ -25,13 +25,14 @@ open Cil_types (**************************************************************************) -(************************* Loop invariants ********************************) +(************************* Loop annotations *******************************) (**************************************************************************) -val preserve_invariant: +val handle_annotations: Env.t -> Kernel_function.t -> stmt -> stmt * Env.t -(** Modify the given stmt loop to insert the code which preserves its loop - invariants. Also return the modified environment. *) +(** Modify the given stmt loop to insert the code which verifies the loop + annotations, ie. preserves its loop invariants and checks the loop variant. + Also return the modified environment. *) (**************************************************************************) (**************************** Nested loops ********************************) diff --git a/src/plugins/e-acsl/src/code_generator/smart_stmt.ml b/src/plugins/e-acsl/src/code_generator/smart_stmt.ml index 1cc37e8113c2e018354194b310ba91c89ce61a85..9799cadb813983e5a73c2fdc5844fa035b8beb34 100644 --- a/src/plugins/e-acsl/src/code_generator/smart_stmt.ml +++ b/src/plugins/e-acsl/src/code_generator/smart_stmt.ml @@ -44,6 +44,7 @@ type annotation_kind = | Precondition | Postcondition | Invariant + | Variant | RTE let kind_to_string loc k = @@ -54,6 +55,7 @@ let kind_to_string loc k = | Precondition -> "Precondition" | Postcondition -> "Postcondition" | Invariant -> "Invariant" + | Variant -> "Variant" | RTE -> "RTE") let block stmt b = match b.bstmts with diff --git a/src/plugins/e-acsl/src/code_generator/smart_stmt.mli b/src/plugins/e-acsl/src/code_generator/smart_stmt.mli index c56404a5a7120a2b752aacca94471f0c9f1ff758..4cdeb0fd23d1f0d62cb58b15806a3c321dc45c55 100644 --- a/src/plugins/e-acsl/src/code_generator/smart_stmt.mli +++ b/src/plugins/e-acsl/src/code_generator/smart_stmt.mli @@ -104,6 +104,7 @@ type annotation_kind = | Precondition | Postcondition | Invariant + | Variant | RTE val runtime_check: diff --git a/src/plugins/e-acsl/src/code_generator/translate.ml b/src/plugins/e-acsl/src/code_generator/translate.ml index b5b44048da759670b1884a7e9d2123655d6afd62..2c4ace3aea337c3113b74e6f977b64824b33b8b9 100644 --- a/src/plugins/e-acsl/src/code_generator/translate.ml +++ b/src/plugins/e-acsl/src/code_generator/translate.ml @@ -45,114 +45,10 @@ let relation_to_binop = function | Req -> Eq | Rneq -> Ne -let name_of_mpz_arith_bop = function - | PlusA -> "__gmpz_add" - | MinusA -> "__gmpz_sub" - | Mult -> "__gmpz_mul" - | Div -> "__gmpz_tdiv_q" - | Mod -> "__gmpz_tdiv_r" - | BAnd -> "__gmpz_and" - | BOr -> "__gmpz_ior" - | BXor -> "__gmpz_xor" - | Shiftlt -> "__gmpz_mul_2exp" - | Shiftrt -> "__gmpz_tdiv_q_2exp" - | Lt | Gt | Le | Ge | Eq | Ne | LAnd | LOr | PlusPI | IndexPI | MinusPI - | MinusPP -> assert false - -(* Type of a string that represents a number. - Used when a string is required to encode a constant number because it is not - representable in any C type *) -type strnum = - | Str_Z (* integers *) - | Str_R (* reals *) - | C_number (* integers and floats included *) - -(* convert [e] in a way that it is compatible with the given typing context. *) -let add_cast ~loc ?name env kf ctx strnum t_opt e = - let mk_mpz e = - let _, e, env = - Env.new_var - ~loc - ?name - env - kf - t_opt - (Gmp_types.Z.t ()) - (fun lv v -> [ Gmp.init_set ~loc (Cil.var lv) v e ]) - in - e, env - in - let e, env = match strnum with - | Str_Z -> mk_mpz e - | Str_R -> Rational.create ~loc ?name e env kf t_opt - | C_number -> e, env - in - match ctx with - | None -> - e, env - | Some ctx -> - let ty = Cil.typeOf e in - match Gmp_types.Z.is_t ty, Gmp_types.Z.is_t ctx with - | true, true -> - (* Z --> Z *) - e, env - | false, true -> - if Gmp_types.Q.is_t ty then - (* R --> Z *) - Rational.cast_to_z ~loc ?name e env - else - (* C integer --> Z *) - let e = - if not (Cil.isIntegralType ty) && strnum = C_number then - (* special case for \null that must be casted to long: it is the - only non integral value that can be seen as an integer, while the - type system infers that it is C-representable (see - tests/runtime/null.i) *) - Cil.mkCast Cil.longType e (* \null *) - else - e - in - mk_mpz e - | _, false -> - if Gmp_types.Q.is_t ctx then - if Gmp_types.Q.is_t (Cil.typeOf e) then (* R --> R *) - e, env - else (* C integer or Z --> R *) - Rational.create ~loc ?name e env kf t_opt - else if Gmp_types.Z.is_t ty || strnum = Str_Z then - (* Z --> C type or the integer is represented by a string: - anyway, it fits into a C integer: convert it *) - let fname, new_ty = - if Cil.isSignedInteger ctx then "__gmpz_get_si", Cil.longType - else "__gmpz_get_ui", Cil.ulongType - in - let _, e, env = - Env.new_var - ~loc - ?name - env - kf - None - new_ty - (fun v _ -> - [ Smart_stmt.rtl_call ~loc - ~result:(Cil.var v) - ~prefix:"" - fname - [ e ] ]) - in - e, env - else if Gmp_types.Q.is_t ty || strnum = Str_R then - (* R --> C type or the real is represented by a string *) - Rational.add_cast ~loc ?name e env kf ctx - else - (* C type --> another C type *) - Cil.mkCastT ~force:false ~oldt:ty ~newt:ctx e, env - let constant_to_exp ~loc t c = let mk_real s = let s = Rational.normalize_str s in - Cil.mkString ~loc s, Str_R + Cil.mkString ~loc s, Typed_number.Str_R in match c with | Integer(n, _repr) -> @@ -163,7 +59,7 @@ let constant_to_exp ~loc t c = | Typing.Rational -> mk_real (Integer.to_string n) | Typing.Gmpz -> (* too large integer *) - Cil.mkString ~loc (Integer.to_string n), Str_Z + Cil.mkString ~loc (Integer.to_string n), Typed_number.Str_Z | Typing.C_float fkind -> Cil.kfloat ~loc fkind (Int64.to_float (Integer.to_int64 n)), C_number | Typing.C_integer kind -> @@ -171,7 +67,7 @@ let constant_to_exp ~loc t c = match cast, kind with | Some ty, (ILongLong | IULongLong) when Gmp_types.Z.is_t ty -> (* too large integer *) - Cil.mkString ~loc (Integer.to_string n), Str_Z + Cil.mkString ~loc (Integer.to_string n), Typed_number.Str_Z | Some ty, _ when Gmp_types.Q.is_t ty -> mk_real (Integer.to_string n) | (None | Some _), _ -> @@ -181,15 +77,15 @@ let constant_to_exp ~loc t c = long] addition and so [1LL]. If we keep the initial string representation, the kind would be ignored in the generated code and so [1] would be generated. *) - Cil.kinteger64 ~loc ~kind n, C_number) - | LStr s -> Cil.new_exp ~loc (Const (CStr s)), C_number - | LWStr s -> Cil.new_exp ~loc (Const (CWStr s)), C_number - | LChr c -> Cil.new_exp ~loc (Const (CChr c)), C_number + Cil.kinteger64 ~loc ~kind n, Typed_number.C_number) + | LStr s -> Cil.new_exp ~loc (Const (CStr s)), Typed_number.C_number + | LWStr s -> Cil.new_exp ~loc (Const (CWStr s)), Typed_number.C_number + | LChr c -> Cil.new_exp ~loc (Const (CChr c)), Typed_number.C_number | LReal lr -> if lr.r_lower = lr.r_upper - then Cil.kfloat ~loc FDouble lr.r_nearest, C_number + then Cil.kfloat ~loc FDouble lr.r_nearest, Typed_number.C_number else mk_real lr.r_literal - | LEnum e -> Cil.new_exp ~loc (Const (CEnum e)), C_number + | LEnum e -> Cil.new_exp ~loc (Const (CEnum e)), Typed_number.C_number let conditional_to_exp ?(name="if") loc kf t_opt e1 (e2, env2) (e3, env3) = let env = Env.pop (Env.pop env3) in @@ -274,17 +170,18 @@ and context_insensitive_term_to_exp kf env t = c, env, strnum, "" | TLval lv -> let lv, env, name = tlval_to_lval kf env lv in - Smart_exp.lval ~loc lv, env, C_number, name - | TSizeOf ty -> Cil.sizeOf ~loc ty, env, C_number, "sizeof" + Smart_exp.lval ~loc lv, env, Typed_number.C_number, name + | TSizeOf ty -> Cil.sizeOf ~loc ty, env, Typed_number.C_number, "sizeof" | TSizeOfE t -> let e, env = term_to_exp kf env t in - Cil.sizeOf ~loc (Cil.typeOf e), env, C_number, "sizeof" + Cil.sizeOf ~loc (Cil.typeOf e), env, Typed_number.C_number, "sizeof" | TSizeOfStr s -> - Cil.new_exp ~loc (SizeOfStr s), env, C_number, "sizeofstr" - | TAlignOf ty -> Cil.new_exp ~loc (AlignOf ty), env, C_number, "alignof" + Cil.new_exp ~loc (SizeOfStr s), env, Typed_number.C_number, "sizeofstr" + | TAlignOf ty -> + Cil.new_exp ~loc (AlignOf ty), env, Typed_number.C_number, "alignof" | TAlignOfE t -> let e, env = term_to_exp kf env t in - Cil.new_exp ~loc (AlignOfE e), env, C_number, "alignof" + Cil.new_exp ~loc (AlignOfE e), env, Typed_number.C_number, "alignof" | TUnOp(Neg | BNot as op, t') -> let ty = Typing.get_typ t in let e, env = term_to_exp kf env t' in @@ -303,11 +200,11 @@ and context_insensitive_term_to_exp kf env t = (Some t) (fun _ ev -> [ Smart_stmt.rtl_call ~loc ~prefix:"" name [ ev; e ] ]) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else if Gmp_types.Q.is_t ty then Env.not_yet env "reals: Neg | BNot" else - Cil.new_exp ~loc (UnOp(op, e, ty)), env, C_number, "" + Cil.new_exp ~loc (UnOp(op, e, ty)), env, Typed_number.C_number, "" | TUnOp(LNot, t) -> let ty = Typing.get_op t in if Gmp_types.Z.is_t ty then @@ -318,18 +215,19 @@ and context_insensitive_term_to_exp kf env t = let e, env = comparison_to_exp kf ~loc ~name:"not" env Typing.gmpz Eq t zero (Some t) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else begin assert (Cil.isIntegralType ty); let e, env = term_to_exp kf env t in - Cil.new_exp ~loc (UnOp(LNot, e, Cil.intType)), env, C_number, "" + let e = Cil.new_exp ~loc (UnOp(LNot, e, Cil.intType)) in + e, env, Typed_number.C_number, "" end | TBinOp(PlusA | MinusA | Mult as bop, t1, t2) -> let ty = Typing.get_typ t in let e1, env = term_to_exp kf env t1 in let e2, env = term_to_exp kf env t2 in if Gmp_types.Z.is_t ty then - let name = name_of_mpz_arith_bop bop in + let name = Gmp.name_of_mpz_arith_bop bop in let mk_stmts _ e = [ Smart_stmt.rtl_call ~loc ~prefix:"" name @@ -338,13 +236,13 @@ and context_insensitive_term_to_exp kf env t = let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf (Some t) mk_stmts in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else if Gmp_types.Q.is_t ty then let e, env = Rational.binop ~loc bop e1 e2 env kf (Some t) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); - Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" + Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, Typed_number.C_number, "" end | TBinOp(Div | Mod as bop, t1, t2) -> let ty = Typing.get_typ t in @@ -355,7 +253,7 @@ and context_insensitive_term_to_exp kf env t = RTE should do this automatically. *) let ctx = Typing.get_number_ty t in let t = Some t in - let name = name_of_mpz_arith_bop bop in + let name = Gmp.name_of_mpz_arith_bop bop in (* [TODO] can now do better since the type system got some info about possible values of [t2] *) (* guarding divisions and modulos *) @@ -384,20 +282,20 @@ and context_insensitive_term_to_exp kf env t = in let name = Misc.name_of_binop bop in let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf t mk_stmts in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else if Gmp_types.Q.is_t ty then let e, env = Rational.binop ~loc bop e1 e2 env kf (Some t) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); (* no guard required since RTEs are generated separately *) - Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" + Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, Typed_number.C_number, "" end | TBinOp(Lt | Gt | Le | Ge | Eq | Ne as bop, t1, t2) -> (* comparison operators *) let ity = Typing.get_integer_op t in let e, env = comparison_to_exp ~loc kf env ity bop t1 t2 (Some t) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | TBinOp((Shiftlt | Shiftrt) as bop, t1, t2) -> (* left/right shift *) let ty = Typing.get_typ t in @@ -486,7 +384,7 @@ and context_insensitive_term_to_exp kf env t = (* Create the shift instruction *) let mk_shift_instr result_e = - let name = name_of_mpz_arith_bop bop in + let name = Gmp.name_of_mpz_arith_bop bop in Smart_stmt.rtl_call ~loc ~prefix:"" name @@ -543,10 +441,10 @@ and context_insensitive_term_to_exp kf env t = in let name = bop_name in let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf t mk_stmts in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); - Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" + Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, Typed_number.C_number, "" end | TBinOp(LOr, t1, t2) -> (* t1 || t2 <==> if t1 then true else t2 *) @@ -560,7 +458,7 @@ and context_insensitive_term_to_exp kf env t = ~name:"or" loc kf (Some t) e1 (Cil.one loc, env') res2 ) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | TBinOp(LAnd, t1, t2) -> (* t1 && t2 <==> if t1 then t2 else false *) let e, env = @@ -573,7 +471,7 @@ and context_insensitive_term_to_exp kf env t = ~name:"and" loc kf (Some t) e1 res2 (Cil.zero loc, env3) ) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | TBinOp((BOr | BXor | BAnd) as bop, t1, t2) -> (* other logic/arith operators *) let ty = Typing.get_typ t in @@ -581,17 +479,17 @@ and context_insensitive_term_to_exp kf env t = let e2, env = term_to_exp kf env t2 in if Gmp_types.Z.is_t ty then let mk_stmts _v e = - let name = name_of_mpz_arith_bop bop in + let name = Gmp.name_of_mpz_arith_bop bop in let instr = Smart_stmt.rtl_call ~loc ~prefix:"" name [ e; e1; e2 ] in [ instr ] in let name = Misc.name_of_binop bop in let t = Some t in let _, e, env = Env.new_var_and_mpz_init ~loc ~name env kf t mk_stmts in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else begin assert (Logic_typing.is_integral_type t.term_type); - Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" + Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, Typed_number.C_number, "" end | TBinOp(PlusPI | IndexPI | MinusPI as bop, t1, t2) -> if Misc.is_set_of_ptr_or_array t1.term_type || @@ -607,14 +505,15 @@ and context_insensitive_term_to_exp kf env t = in let e1, env = term_to_exp kf env t1 in let e2, env = term_to_exp kf env t2 in - Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, C_number, "" + Cil.new_exp ~loc (BinOp(bop, e1, e2, ty)), env, Typed_number.C_number, "" | TBinOp(MinusPP, t1, t2) -> begin match Typing.get_number_ty t with | Typing.C_integer _ -> let e1, env = term_to_exp kf env t1 in let e2, env = term_to_exp kf env t2 in let ty = Typing.get_typ t in - Cil.new_exp ~loc (BinOp(MinusPP, e1, e2, ty)), env, C_number, "" + let e = Cil.new_exp ~loc (BinOp(MinusPP, e1, e2, ty)) in + e, env, Typed_number.C_number, "" | Typing.Gmpz -> Env.not_yet env "pointer subtraction resulting in gmp" | Typing.(C_float _ | Rational | Real | Nan) -> @@ -623,73 +522,27 @@ and context_insensitive_term_to_exp kf env t = | TCastE(ty, t') -> let e, env = term_to_exp kf env t' in let e, env = - add_cast ~loc ~name:"cast" env kf (Some ty) C_number (Some t) e + Typed_number.add_cast + ~loc + ~name:"cast" + env + kf + (Some ty) + Typed_number.C_number + (Some t) + e in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | TLogic_coerce _ -> assert false (* handle in [term_to_exp] *) | TAddrOf lv -> let lv, env, _ = tlval_to_lval kf env lv in - Cil.mkAddrOf ~loc lv, env, C_number, "addrof" + Cil.mkAddrOf ~loc lv, env, Typed_number.C_number, "addrof" | TStartOf lv -> let lv, env, _ = tlval_to_lval kf env lv in - Cil.mkAddrOrStartOf ~loc lv, env, C_number, "startof" - | Tapp(li, [], targs) -> - let fname = li.l_var_info.lv_name in - (* build the varinfo (as an expression) which stores the result of the - function call. *) - let _, e, env = - if Builtins.mem li.l_var_info.lv_name then - (* E-ACSL built-in function call *) - let args, env = - try - List.fold_right - (fun targ (l, env) -> - let e, env = term_to_exp kf env targ in - e :: l, env) - targs - ([], env) - with Invalid_argument _ -> - Options.fatal - "[Tapp] unexpected number of arguments when calling %s" - fname - in - Env.new_var - ~loc - ~name:(fname ^ "_app") - env - kf - (Some t) - (Misc.cty (Option.get li.l_type)) - (fun vi _ -> - [ Smart_stmt.rtl_call ~loc - ~result:(Cil.var vi) - ~prefix:"" - fname - args ]) - else - (* build the arguments and compute the integer_ty of the parameters *) - let params_ty, args, env = - List.fold_right - (fun targ (params_ty, args, env) -> - let e, env = term_to_exp kf env targ in - let param_ty = Typing.get_number_ty targ in - let e, env = - try - let ty = Typing.typ_of_number_ty param_ty in - add_cast loc env kf (Some ty) C_number (Some targ) e - with Typing.Not_a_number -> - e, env - in - param_ty :: params_ty, e :: args, env) - targs - ([], [], env) - in - let gen_fname = - Varname.get ~scope:Varname.Global (Functions.RTL.mk_gen_name fname) - in - Logic_functions.tapp_to_exp ~loc gen_fname env kf t li params_ty args - in - e, env, C_number, "app" + Cil.mkAddrOrStartOf ~loc lv, env, Typed_number.C_number, "startof" + | Tapp(_, [], _) -> + let e, env = Logic_functions.tapp_to_exp kf env t in + e, env, Typed_number.C_number, "app" | Tapp(_, _ :: _, _) -> Env.not_yet env "logic functions with labels" | Tlambda _ -> Env.not_yet env "functional" @@ -704,16 +557,16 @@ and context_insensitive_term_to_exp kf env t = conditional_to_exp loc kf (Some t) e1 res2 res3 ) in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | Tat(t, BuiltinLabel Here) -> let e, env = term_to_exp kf env t in - e, env, C_number, "" + e, env, Typed_number.C_number, "" | Tat(t', label) -> let lscope = Env.Logic_scope.get env in let pot = Lscope.PoT_term t' in if Lscope.is_used lscope pot then let e, env = At_with_lscope.to_exp ~loc kf env pot label in - e, env, C_number, "" + e, env, Typed_number.C_number, "" else let e, env = term_to_exp kf (Env.push env) t' in let e, env, sty = at_to_exp_no_lscope env kf (Some t) label e in @@ -721,22 +574,23 @@ and context_insensitive_term_to_exp kf env t = | Tbase_addr(BuiltinLabel Here, t) -> let name = "base_addr" in let e, env = Memory_translate.call ~loc kf name Cil.voidPtrType env t in - e, env, C_number, name + e, env, Typed_number.C_number, name | Tbase_addr _ -> Env.not_yet env "labeled \\base_addr" | Toffset(BuiltinLabel Here, t) -> let size_t = Cil.theMachine.Cil.typeOfSizeOf in let name = "offset" in let e, env = Memory_translate.call ~loc kf name size_t env t in - e, env, C_number, name + e, env, Typed_number.C_number, name | Toffset _ -> Env.not_yet env "labeled \\offset" | Tblock_length(BuiltinLabel Here, t) -> let size_t = Cil.theMachine.Cil.typeOfSizeOf in let name = "block_length" in let e, env = Memory_translate.call ~loc kf name size_t env t in - e, env, C_number, name + e, env, Typed_number.C_number, name | Tblock_length _ -> Env.not_yet env "labeled \\block_length" | Tnull -> - Cil.mkCast (TPtr(TVoid [], [])) (Cil.zero ~loc), env, C_number, "null" + let e = Cil.mkCast (TPtr(TVoid [], [])) (Cil.zero ~loc) in + e, env, Typed_number.C_number, "null" | TUpdate _ -> Env.not_yet env "functional update" | Ttypeof _ -> Env.not_yet env "typeof" | Ttype _ -> Env.not_yet env "C type" @@ -751,7 +605,7 @@ and context_insensitive_term_to_exp kf env t = let env = env_of_li li kf env loc in let e, env = term_to_exp kf env t in Interval.Env.remove li.l_var_info; - e, env, C_number, "" + e, env, Typed_number.C_number, "" (* Convert an ACSL term into a corresponding C expression (if any) in the given environment. Also extend this environment in order to include the generating @@ -767,7 +621,7 @@ and term_to_exp kf env t = let env = if generate_rte then translate_rte kf env e else env in let cast = Typing.get_cast t in let name = if name = "" then None else Some name in - add_cast + Typed_number.add_cast ~loc:t.term_loc ?name env @@ -878,7 +732,7 @@ and at_to_exp_no_lscope env kf t_opt label e = end in ignore (Visitor.visitFramacStmt o stmt); - res, !env_ref, C_number + res, !env_ref, Typed_number.C_number and env_of_li li kf env loc = let t = Misc.term_of_li li in @@ -994,7 +848,7 @@ and predicate_content_to_exp ?name kf env p = (* convert [t'] to [e] in a separated local env *) let e, env = predicate_to_exp kf (Env.push env) p' in let e, env, sty = at_to_exp_no_lscope env kf None label e in - assert (sty = C_number); + assert (sty = Typed_number.C_number); e, env end | Pvalid_read(BuiltinLabel Here as llabel, t) as pc @@ -1087,13 +941,13 @@ and predicate_to_exp ?name kf ?rte env p = let e, env = predicate_content_to_exp ?name kf env p in let env = if rte then translate_rte kf env e else env in let cast = Typing.get_cast_of_predicate p in - add_cast + Typed_number.add_cast ~loc:p.pred_loc ?name env kf cast - C_number + Typed_number.C_number None e ) @@ -1205,10 +1059,15 @@ let untyped_predicate_to_exp p = let env = Env.push Env.empty in let env = Env.rte env false in let e, env = - try generalized_untyped_predicate_to_exp ~must_clear_typing:false (Kernel_function.dummy ()) env p + try generalized_untyped_predicate_to_exp + ~must_clear_typing:false + (Kernel_function.dummy ()) + env + p with Rtl.Symbols.Unregistered _ -> raise (No_simple_predicate_translation p) in - if not (Env.has_no_new_stmt env) then raise (No_simple_predicate_translation p); + if not (Env.has_no_new_stmt env) + then raise (No_simple_predicate_translation p); e (* This function is used by plug-in [Cfp]. *) diff --git a/src/plugins/e-acsl/src/code_generator/translate_annots.ml b/src/plugins/e-acsl/src/code_generator/translate_annots.ml index 2046b2b4bed3954cd134ae8cf36ec5864bb8c7c3..e4f26ec3919ed0ecbc5de7c3e0c9186077de6bb1 100644 --- a/src/plugins/e-acsl/src/code_generator/translate_annots.ml +++ b/src/plugins/e-acsl/src/code_generator/translate_annots.ml @@ -57,7 +57,7 @@ let pre_funspec kf kinstr env funspec = unsupported (fun spec -> let ppt = Property.ip_decreases_of_spec kf kinstr spec in - if must_translate_opt ppt then Env.not_yet env "variant clause") + if must_translate_opt ppt then Env.not_yet env "decreases clause") funspec; (* TODO: spec.spec_terminates is not part of the E-ACSL subset *) unsupported @@ -67,8 +67,9 @@ let pre_funspec kf kinstr env funspec = funspec; env in - let env = convert_unsupported_clauses env in let loc = Kernel_function.get_location kf in + Cil.CurrentLoc.set loc; + let env = convert_unsupported_clauses env in let contract = Contract.create ~loc funspec in Env.with_rte env true ~f:(fun env -> Contract.translate_preconditions kf kinstr env contract) @@ -110,9 +111,9 @@ let pre_code_annotation kf stmt env annot = else env else env - | AVariant _ -> + | AVariant (t, measure) -> if must_translate (Property.ip_of_code_annot_single kf stmt annot) - then Env.not_yet env "variant" + then Env.set_loop_variant env ?measure t else env | AAssigns _ -> (* TODO: it is not a precondition --> should not be handled here, diff --git a/src/plugins/e-acsl/src/code_generator/typed_number.ml b/src/plugins/e-acsl/src/code_generator/typed_number.ml new file mode 100644 index 0000000000000000000000000000000000000000..dfa8a4497a868038fade0ac52e77a056075c37cc --- /dev/null +++ b/src/plugins/e-acsl/src/code_generator/typed_number.ml @@ -0,0 +1,109 @@ +(**************************************************************************) +(* *) +(* 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). *) +(* *) +(**************************************************************************) + +(** Manipulate the type of numbers. *) + +type strnum = + | Str_Z (* integers *) + | Str_R (* reals *) + | C_number (* integers and floats included *) + +let add_cast ~loc ?name env kf ctx strnum t_opt e = + let mk_mpz e = + let _, e, env = + Env.new_var + ~loc + ?name + env + kf + t_opt + (Gmp_types.Z.t ()) + (fun lv v -> [ Gmp.init_set ~loc (Cil.var lv) v e ]) + in + e, env + in + let e, env = match strnum with + | Str_Z -> mk_mpz e + | Str_R -> Rational.create ~loc ?name e env kf t_opt + | C_number -> e, env + in + match ctx with + | None -> + e, env + | Some ctx -> + let ty = Cil.typeOf e in + match Gmp_types.Z.is_t ty, Gmp_types.Z.is_t ctx with + | true, true -> + (* Z --> Z *) + e, env + | false, true -> + if Gmp_types.Q.is_t ty then + (* R --> Z *) + Rational.cast_to_z ~loc ?name e env + else + (* C integer --> Z *) + let e = + if not (Cil.isIntegralType ty) && strnum = C_number then + (* special case for \null that must be casted to long: it is the + only non integral value that can be seen as an integer, while the + type system infers that it is C-representable (see + tests/runtime/null.i) *) + Cil.mkCast Cil.longType e (* \null *) + else + e + in + mk_mpz e + | _, false -> + if Gmp_types.Q.is_t ctx then + if Gmp_types.Q.is_t (Cil.typeOf e) then (* R --> R *) + e, env + else (* C integer or Z --> R *) + Rational.create ~loc ?name e env kf t_opt + else if Gmp_types.Z.is_t ty || strnum = Str_Z then + (* Z --> C type or the integer is represented by a string: + anyway, it fits into a C integer: convert it *) + let fname, new_ty = + if Cil.isSignedInteger ctx then "__gmpz_get_si", Cil.longType + else "__gmpz_get_ui", Cil.ulongType + in + let _, e, env = + Env.new_var + ~loc + ?name + env + kf + None + new_ty + (fun v _ -> + [ Smart_stmt.rtl_call ~loc + ~result:(Cil.var v) + ~prefix:"" + fname + [ e ] ]) + in + e, env + else if Gmp_types.Q.is_t ty || strnum = Str_R then + (* R --> C type or the real is represented by a string *) + Rational.add_cast ~loc ?name e env kf ctx + else + (* C type --> another C type *) + Cil.mkCastT ~force:false ~oldt:ty ~newt:ctx e, env diff --git a/src/plugins/e-acsl/src/code_generator/typed_number.mli b/src/plugins/e-acsl/src/code_generator/typed_number.mli new file mode 100644 index 0000000000000000000000000000000000000000..bf75cc1cfe617ed4ff73ce2f39f1632e5fa58362 --- /dev/null +++ b/src/plugins/e-acsl/src/code_generator/typed_number.mli @@ -0,0 +1,49 @@ +(**************************************************************************) +(* *) +(* 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). *) +(* *) +(**************************************************************************) + +(** Manipulate the type of numbers. *) + +open Cil_types + +(** Type of a string that represents a number. + Used when a string is required to encode a constant number because it is not + representable in any C type *) +type strnum = + | Str_Z (* integers *) + | Str_R (* reals *) + | C_number (* integers and floats included *) + +(** [add_cast ~loc ?name env kf ctx sty t_opt e] convert number expression [e] + in a way that it is compatible with the given typing context [ctx]. + [sty] indicates if the expression is a string representing a number (integer + or real) or directly a C number type. + [t_opt] is the term that is represented by the expression [e]. *) +val add_cast: + loc:location -> + ?name:string -> + Env.t -> + kernel_function -> + typ option -> + strnum -> + term option -> + exp -> + exp * Env.t diff --git a/src/plugins/e-acsl/tests/constructs/decrease.i b/src/plugins/e-acsl/tests/constructs/decrease.i new file mode 100644 index 0000000000000000000000000000000000000000..b5dc3a50ca7515eb3f4fa32bcff259c351083bce --- /dev/null +++ b/src/plugins/e-acsl/tests/constructs/decrease.i @@ -0,0 +1,87 @@ +/* run.config_ci, run.config_dev + STDOPT: +"-eva-ignore-recursive-calls -eva-slevel 7" + */ + +// Test that the last iteration of the variant can be negative +int f(int x) { + /*@ loop variant x; */ + while (x >= 0) { + x -= 2; + } + return x; +} + +// Test variant with general measure +/*@ predicate lexico(integer x, integer y) = + x > y && 0 <= x; */ +int g(int x) { + /*@ loop variant x for lexico; */ + while (x >= 0) { + x -= 2; + } + return x; +} + +// Test classic variant +/*@ requires n <= 12; */ +int fact(int n) { + int result = n; + /*@ loop invariant n >= 1; + loop variant n; */ + while (n > 1) { + result *= (n - 1); + --n; + } + return result; +} + +// Test decreases on recursive function +/*@ decreases n; */ +int fib(int n) { + if (n == 1) return 1; + if (n <= 0) return 0; + return fib(n - 1) + fib(n - 2); +} + +// Test decreases on mutually recursive functions +int odd(int); +/*@ requires n >= 0; + decreases n; */ +int even(int n) { + if (n == 0) return 1; + return odd(n - 1); +} +/*@ requires n >= 0; + decreases n; */ +int odd(int n) { + if (n == 0) return 0; + return even(n - 1); +} + +int main() { + int f10 = f(10); + //@ assert f10 == -2; + int f7 = f(7); + //@ assert f7 == -1; + int g10 = g(10); + //@ assert g10 == -2; + int g7 = g(7); + //@ assert g7 == -1; + + int fact7 = fact(7); + //@ assert fact7 == 5040; + + int fib7 = fib(7); + //@ assert fib7 == 13; + + int even7 = even(7); + //@ assert even7 == 0; + int even10 = even(10); + //@ assert even10 == 1; + int odd7 = odd(7); + //@ assert odd7 == 1; + int odd10 = odd(10); + //@ assert odd10 == 0; + + return 0; +} diff --git a/src/plugins/e-acsl/tests/constructs/oracle_ci/decrease.res.oracle b/src/plugins/e-acsl/tests/constructs/oracle_ci/decrease.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..74f4f63423966d0396e43ebbb9a02c8887c3ddcf --- /dev/null +++ b/src/plugins/e-acsl/tests/constructs/oracle_ci/decrease.res.oracle @@ -0,0 +1,73 @@ +[e-acsl] beginning translation. +[e-acsl] tests/constructs/decrease.i:50: Warning: + E-ACSL construct `decreases clause' is not yet supported. + Ignoring annotation. +[e-acsl] tests/constructs/decrease.i:47: Warning: + E-ACSL construct `decreases clause' is not yet supported. + Ignoring annotation. +[e-acsl] tests/constructs/decrease.i:40: Warning: + E-ACSL construct `decreases clause' is not yet supported. + Ignoring annotation. +[e-acsl] translation done in project "e-acsl". +[eva] tests/constructs/decrease.i:43: Warning: + recursive call during value analysis + of __gen_e_acsl_fib (__gen_e_acsl_fib <- fib :: tests/constructs/decrease.i:40 <- + __gen_e_acsl_fib :: tests/constructs/decrease.i:74 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva] tests/constructs/decrease.i:43: Warning: + recursive call during value analysis + of __gen_e_acsl_fib (__gen_e_acsl_fib <- fib :: tests/constructs/decrease.i:40 <- + __gen_e_acsl_fib :: tests/constructs/decrease.i:74 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva:alarm] tests/constructs/decrease.i:43: Warning: + signed overflow. + assert -2147483648 ≤ tmp + tmp_0; + (tmp from fib(n - 1), tmp_0 from fib(n - 2)) +[eva:alarm] tests/constructs/decrease.i:43: Warning: + signed overflow. + assert tmp + tmp_0 ≤ 2147483647; + (tmp from fib(n - 1), tmp_0 from fib(n - 2)) +[eva:alarm] tests/constructs/decrease.i:75: Warning: + function __e_acsl_assert, behavior blocking: precondition got status unknown. +[eva] tests/constructs/decrease.i:58: Warning: + recursive call during value analysis + of __gen_e_acsl_even (__gen_e_acsl_even <- odd :: tests/constructs/decrease.i:47 <- + __gen_e_acsl_odd :: tests/constructs/decrease.i:52 <- + even :: tests/constructs/decrease.i:50 <- + __gen_e_acsl_even :: tests/constructs/decrease.i:77 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva:alarm] tests/constructs/decrease.i:78: Warning: + function __e_acsl_assert, behavior blocking: precondition got status unknown. +[eva] tests/constructs/decrease.i:58: Warning: + recursive call during value analysis + of __gen_e_acsl_even (__gen_e_acsl_even <- odd :: tests/constructs/decrease.i:47 <- + __gen_e_acsl_odd :: tests/constructs/decrease.i:52 <- + even :: tests/constructs/decrease.i:50 <- + __gen_e_acsl_even :: tests/constructs/decrease.i:79 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva:alarm] tests/constructs/decrease.i:80: Warning: + function __e_acsl_assert, behavior blocking: precondition got status unknown. +[eva] tests/constructs/decrease.i:52: Warning: + recursive call during value analysis + of __gen_e_acsl_odd (__gen_e_acsl_odd <- even :: tests/constructs/decrease.i:50 <- + __gen_e_acsl_even :: tests/constructs/decrease.i:58 <- + odd :: tests/constructs/decrease.i:47 <- + __gen_e_acsl_odd :: tests/constructs/decrease.i:81 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva:alarm] tests/constructs/decrease.i:82: Warning: + function __e_acsl_assert, behavior blocking: precondition got status unknown. +[eva] tests/constructs/decrease.i:52: Warning: + recursive call during value analysis + of __gen_e_acsl_odd (__gen_e_acsl_odd <- even :: tests/constructs/decrease.i:50 <- + __gen_e_acsl_even :: tests/constructs/decrease.i:58 <- + odd :: tests/constructs/decrease.i:47 <- + __gen_e_acsl_odd :: tests/constructs/decrease.i:83 <- + main). + Assuming the call has no effect. The analysis will be unsound. +[eva:alarm] tests/constructs/decrease.i:84: Warning: + function __e_acsl_assert, behavior blocking: precondition got status unknown. diff --git a/src/plugins/e-acsl/tests/constructs/oracle_ci/gen_decrease.c b/src/plugins/e-acsl/tests/constructs/oracle_ci/gen_decrease.c new file mode 100644 index 0000000000000000000000000000000000000000..41a66ca7b5efb2a1f491aca7a9eefbe396862a59 --- /dev/null +++ b/src/plugins/e-acsl/tests/constructs/oracle_ci/gen_decrease.c @@ -0,0 +1,226 @@ +/* Generated by Frama-C */ +#include "stddef.h" +#include "stdio.h" +extern int __e_acsl_sound_verdict; + +int f(int x) +{ + int __gen_e_acsl_old_variant; + /*@ loop variant x; */ + while (1) { + __gen_e_acsl_old_variant = x; + if (! (x >= 0)) break; + x -= 2; + __e_acsl_assert(__gen_e_acsl_old_variant >= 0,1,"Variant","f", + "(old x) \342\211\245 0","tests/constructs/decrease.i",8); + __e_acsl_assert(__gen_e_acsl_old_variant > x,1,"Variant","f", + "(old x) > x","tests/constructs/decrease.i",8); + } + return x; +} + +/*@ predicate lexico(ℤ x, ℤ y) = x > y ∧ 0 ≤ x; + +*/ +int __gen_e_acsl_lexico(int x, int y); + +int g(int x) +{ + int __gen_e_acsl_old_variant; + /*@ loop variant x for lexico; */ + while (1) { + __gen_e_acsl_old_variant = x; + if (! (x >= 0)) break; + x -= 2; + { + int __gen_e_acsl_lexico_2; + __gen_e_acsl_lexico_2 = __gen_e_acsl_lexico(__gen_e_acsl_old_variant,x); + __e_acsl_assert(__gen_e_acsl_lexico_2,1,"Variant","g", + "lexico(old x, x)","tests/constructs/decrease.i",19); + } + } + return x; +} + +/*@ requires n ≤ 12; */ +int __gen_e_acsl_fact(int n); + +int fact(int n) +{ + int __gen_e_acsl_old_variant; + int result = n; + __e_acsl_assert(n >= 1,1,"Invariant","fact","n >= 1", + "tests/constructs/decrease.i",29); + /*@ loop invariant n ≥ 1; + loop variant n; */ + while (1) { + __gen_e_acsl_old_variant = n; + if (! (n > 1)) break; + result *= n - 1; + n --; + __e_acsl_assert(n >= 1,1,"Invariant","fact","n >= 1", + "tests/constructs/decrease.i",29); + __e_acsl_assert(__gen_e_acsl_old_variant >= 0,1,"Variant","fact", + "(old n) \342\211\245 0","tests/constructs/decrease.i", + 31); + __e_acsl_assert(__gen_e_acsl_old_variant > n,1,"Variant","fact", + "(old n) > n","tests/constructs/decrease.i",31); + } + return result; +} + +/*@ decreases n; */ +int __gen_e_acsl_fib(int n); + +int fib(int n) +{ + int __retres; + int tmp; + int tmp_0; + if (n == 1) { + __retres = 1; + goto return_label; + } + if (n <= 0) { + __retres = 0; + goto return_label; + } + tmp = __gen_e_acsl_fib(n - 1); + tmp_0 = __gen_e_acsl_fib(n - 2); + /*@ assert Eva: signed_overflow: -2147483648 ≤ tmp + tmp_0; */ + /*@ assert Eva: signed_overflow: tmp + tmp_0 ≤ 2147483647; */ + __retres = tmp + tmp_0; + return_label: return __retres; +} + +/*@ requires n ≥ 0; + decreases n; */ +int __gen_e_acsl_odd(int n); + +int odd(int n); + +/*@ requires n ≥ 0; + decreases n; */ +int __gen_e_acsl_even(int n); + +int even(int n) +{ + int __retres; + int tmp; + if (n == 0) { + __retres = 1; + goto return_label; + } + tmp = __gen_e_acsl_odd(n - 1); + __retres = tmp; + return_label: return __retres; +} + +int odd(int n) +{ + int __retres; + int tmp; + if (n == 0) { + __retres = 0; + goto return_label; + } + tmp = __gen_e_acsl_even(n - 1); + __retres = tmp; + return_label: return __retres; +} + +int main(void) +{ + int __retres; + int f10 = f(10); + __e_acsl_assert(f10 == -2,1,"Assertion","main","f10 == -2", + "tests/constructs/decrease.i",63); + /*@ assert f10 ≡ -2; */ ; + int f7 = f(7); + __e_acsl_assert(f7 == -1,1,"Assertion","main","f7 == -1", + "tests/constructs/decrease.i",65); + /*@ assert f7 ≡ -1; */ ; + int g10 = g(10); + __e_acsl_assert(g10 == -2,1,"Assertion","main","g10 == -2", + "tests/constructs/decrease.i",67); + /*@ assert g10 ≡ -2; */ ; + int g7 = g(7); + __e_acsl_assert(g7 == -1,1,"Assertion","main","g7 == -1", + "tests/constructs/decrease.i",69); + /*@ assert g7 ≡ -1; */ ; + int fact7 = __gen_e_acsl_fact(7); + __e_acsl_assert(fact7 == 5040,1,"Assertion","main","fact7 == 5040", + "tests/constructs/decrease.i",72); + /*@ assert fact7 ≡ 5040; */ ; + int fib7 = __gen_e_acsl_fib(7); + __e_acsl_assert(fib7 == 13,1,"Assertion","main","fib7 == 13", + "tests/constructs/decrease.i",75); + /*@ assert fib7 ≡ 13; */ ; + int even7 = __gen_e_acsl_even(7); + __e_acsl_assert(even7 == 0,1,"Assertion","main","even7 == 0", + "tests/constructs/decrease.i",78); + /*@ assert even7 ≡ 0; */ ; + int even10 = __gen_e_acsl_even(10); + __e_acsl_assert(even10 == 1,1,"Assertion","main","even10 == 1", + "tests/constructs/decrease.i",80); + /*@ assert even10 ≡ 1; */ ; + int odd7 = __gen_e_acsl_odd(7); + __e_acsl_assert(odd7 == 1,1,"Assertion","main","odd7 == 1", + "tests/constructs/decrease.i",82); + /*@ assert odd7 ≡ 1; */ ; + int odd10 = __gen_e_acsl_odd(10); + __e_acsl_assert(odd10 == 0,1,"Assertion","main","odd10 == 0", + "tests/constructs/decrease.i",84); + /*@ assert odd10 ≡ 0; */ ; + __retres = 0; + return __retres; +} + +/*@ requires n ≥ 0; + decreases n; */ +int __gen_e_acsl_even(int n) +{ + int __retres; + __e_acsl_assert(n >= 0,1,"Precondition","even","n >= 0", + "tests/constructs/decrease.i",48); + __retres = even(n); + return __retres; +} + +/*@ requires n ≥ 0; + decreases n; */ +int __gen_e_acsl_odd(int n) +{ + int __retres; + __e_acsl_assert(n >= 0,1,"Precondition","odd","n >= 0", + "tests/constructs/decrease.i",54); + __retres = odd(n); + return __retres; +} + +/*@ decreases n; */ +int __gen_e_acsl_fib(int n) +{ + int __retres; + __retres = fib(n); + return __retres; +} + +/*@ requires n ≤ 12; */ +int __gen_e_acsl_fact(int n) +{ + int __retres; + __e_acsl_assert(n <= 12,1,"Precondition","fact","n <= 12", + "tests/constructs/decrease.i",26); + __retres = fact(n); + return __retres; +} + +int __gen_e_acsl_lexico(int x, int y) +{ + int __gen_e_acsl_and; + if (x > y) __gen_e_acsl_and = 0 <= x; else __gen_e_acsl_and = 0; + return __gen_e_acsl_and; +} + + diff --git a/src/plugins/e-acsl/tests/constructs/oracle_dev/decrease.e-acsl.err.log b/src/plugins/e-acsl/tests/constructs/oracle_dev/decrease.e-acsl.err.log new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle b/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle index 2a20efd543f570f710b1f32d7177806c057ed931..6420addb008c45e48b24143a2062ccc305fbe9b7 100644 --- a/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle +++ b/src/plugins/e-acsl/tests/special/oracle_ci/e-acsl-valid.res.oracle @@ -4,8 +4,6 @@ [e-acsl] tests/special/e-acsl-valid.c:28: Warning: E-ACSL construct `assigns clause in behavior' is not yet supported. Ignoring annotation. -[e-acsl] tests/special/e-acsl-valid.c:26: Warning: - E-ACSL construct `variant' is not yet supported. Ignoring annotation. [e-acsl] tests/special/e-acsl-valid.c:24: Warning: E-ACSL construct `assigns clause in behavior' is not yet supported. Ignoring annotation. diff --git a/src/plugins/e-acsl/tests/special/oracle_ci/gen_e-acsl-valid.c b/src/plugins/e-acsl/tests/special/oracle_ci/gen_e-acsl-valid.c index 5653d657ff06a0a63975ad0803668d46a9f06e70..8fd9ca39e0bf76b673897322fa3d71b82bcf9f84 100644 --- a/src/plugins/e-acsl/tests/special/oracle_ci/gen_e-acsl-valid.c +++ b/src/plugins/e-acsl/tests/special/oracle_ci/gen_e-acsl-valid.c @@ -26,6 +26,7 @@ void __gen_e_acsl_f(int *x, int *y); void f(int *x, int *y) { + long __gen_e_acsl_old_variant; { int __gen_e_acsl_valid_read; __e_acsl_store_block((void *)(& y),(size_t)8); @@ -51,10 +52,18 @@ void f(int *x, int *y) int i = 0; /*@ loop invariant 0 ≤ i ≤ 1; loop variant 2 - i; */ - while (i < 1) { + while (1) { + __gen_e_acsl_old_variant = 2L - i; + if (! (i < 1)) break; /*@ assert 1 ≡ 1; */ ; /*@ assert \valid(y); */ ; i ++; + __e_acsl_assert(__gen_e_acsl_old_variant >= 0L,1,"Variant","f", + "(old 2 - i) \342\211\245 0", + "tests/special/e-acsl-valid.c",31); + __e_acsl_assert(__gen_e_acsl_old_variant > 2L - i,1,"Variant","f", + "(old 2 - i) > 2 - i","tests/special/e-acsl-valid.c", + 31); } } __e_acsl_delete_block((void *)(& y));