diff --git a/src/plugins/e-acsl/doc/Changelog b/src/plugins/e-acsl/doc/Changelog index 3fbd174366f6ea7a3dbd1be7cdd868800ee3e4af..78f90c6978a4fa2d30a866460bde9fd493777145 100644 --- a/src/plugins/e-acsl/doc/Changelog +++ b/src/plugins/e-acsl/doc/Changelog @@ -25,6 +25,8 @@ Plugin E-ACSL <next-release> ############################ +-* E-ACSL [2022-03-01] Fix normalization of global annotations that + may lead to crashes (frama-c/e-acsl#195). - E-ACSL [2022-01-28] Add Linux's pthread concurrency support. -* E-ACSL [2021-12-03] Fix crash when creating an axiomatic with an existing name in E-ACSL's RTL (frama-c/e-acsl#161). diff --git a/src/plugins/e-acsl/src/analyses/bound_variables.ml b/src/plugins/e-acsl/src/analyses/bound_variables.ml index e7eaa96b0cd410bfe1c2ce7def431d9d1a9068c3..3afb018482d3cbf0e297d62feebafeb48de66b3e 100644 --- a/src/plugins/e-acsl/src/analyses/bound_variables.ml +++ b/src/plugins/e-acsl/src/analyses/bound_variables.ml @@ -696,34 +696,26 @@ end (Result.Error (Error.make_not_yet "unguarded \\exists quantification")) | _ -> () - let do_user_predicates () = - let gannot a = - match a with - | Dfun_or_pred ({l_body = LBpred p},loc) -> - (match Logic_normalizer.get_pred p with - | PoT_pred p -> process_quantif ~loc p - | PoT_term _ -> ()) - | _ -> () - in - Annotations.iter_global (fun _ a -> gannot a) - let preprocessor = object inherit E_acsl_visitor.visitor + method !vannotation annot = + match annot with + | Dfun_or_pred _ -> Cil.DoChildren + | _ -> Cil.SkipChildren + method !vpredicate p = let loc = p.pred_loc in - match Logic_normalizer.get_pred p with - | PoT_pred p -> process_quantif ~loc p; - Cil.DoChildren - | PoT_term _ -> Cil.DoChildren + let p = Logic_normalizer.get_pred p in + process_quantif ~loc p; + Cil.DoChildren end let compute ast = Visitor.visitFramacFileSameGlobals (preprocessor :> Visitor.frama_c_inplace) - ast; - do_user_predicates () + ast let compute_annot annot = ignore diff --git a/src/plugins/e-acsl/src/analyses/e_acsl_visitor.ml b/src/plugins/e-acsl/src/analyses/e_acsl_visitor.ml index fea87f8c78f1b68f947686546ce61809497e5731..fab6c7ffcec22d641aa65aa69b9a2ed591936801 100644 --- a/src/plugins/e-acsl/src/analyses/e_acsl_visitor.ml +++ b/src/plugins/e-acsl/src/analyses/e_acsl_visitor.ml @@ -31,6 +31,7 @@ let case_globals ?(var_fun_decl = fun _ -> default ()) ?(var_init = fun _ -> default ()) ?(var_def = fun _ _ -> default ()) + ?(glob_annot = fun _ -> default ()) ~fun_def = function (* library functions and built-ins *) @@ -65,6 +66,10 @@ let case_globals | GFun(fundec, _) -> fun_def fundec + (* global annotation *) + | GAnnot (ga, _) -> + glob_annot ga + (* other globals: nothing to do *) | GType _ | GCompTag _ @@ -74,7 +79,6 @@ let case_globals | GAsm _ | GPragma _ | GText _ - | GAnnot _ (* do never read annotation from sources *) -> default () @@ -106,6 +110,9 @@ class visitor method var_def : varinfo -> init -> global list Cil.visitAction = fun _ _ -> self#default () + method glob_annot: global_annotation -> global list Cil.visitAction = + fun _ -> Cil.DoChildren (* do visit ACSL annotations by default *) + method fun_def ({svar = vi}) = let kf = try Globals.Functions.get vi with Not_found -> assert false in if Functions.check kf then Cil.DoChildren else Cil.SkipChildren @@ -120,5 +127,6 @@ class visitor ~var_fun_decl:self#var_fun_decl ~var_init:self#var_init ~var_def:self#var_def + ~glob_annot:self#glob_annot ~fun_def:self#fun_def end diff --git a/src/plugins/e-acsl/src/analyses/e_acsl_visitor.mli b/src/plugins/e-acsl/src/analyses/e_acsl_visitor.mli index b1f50d9faaf88a49f034d16ae6dd42bf40180aa6..21ca0fea5d1ed32bcbd3adf7d10867cd4539f41c 100644 --- a/src/plugins/e-acsl/src/analyses/e_acsl_visitor.mli +++ b/src/plugins/e-acsl/src/analyses/e_acsl_visitor.mli @@ -31,6 +31,7 @@ val case_globals : ?var_fun_decl:(varinfo -> 'a) -> ?var_init:(varinfo -> 'a) -> ?var_def:(varinfo -> init -> 'a) -> + ?glob_annot:(global_annotation -> 'a) -> fun_def:(fundec -> 'a) -> global -> 'a (** Function to descend into the root of the ast according to the various cases @@ -47,6 +48,7 @@ val case_globals : value - [var_def] is the case for variable definitions with an initialization value + - [glob_annot] is the case for global annotations - [fun_def] is the case for function definition. *) (** Visitor for managing the root of the AST, on the globals level, with the @@ -64,5 +66,6 @@ class visitor : method var_fun_decl: varinfo -> global list Cil.visitAction method var_init: varinfo -> global list Cil.visitAction method var_def: varinfo -> init -> global list Cil.visitAction + method glob_annot: global_annotation -> global list Cil.visitAction method fun_def: fundec -> global list Cil.visitAction end diff --git a/src/plugins/e-acsl/src/analyses/logic_normalizer.ml b/src/plugins/e-acsl/src/analyses/logic_normalizer.ml index bc2b125769d3f2132e603764a329775e06911caf..eb5b5fb88651fe092b6ac3b77bff2892bedab363 100644 --- a/src/plugins/e-acsl/src/analyses/logic_normalizer.ml +++ b/src/plugins/e-acsl/src/analyses/logic_normalizer.ml @@ -21,7 +21,6 @@ (**************************************************************************) open Cil_types -open Analyses_types module Id_predicate = Datatype.Make_with_collections @@ -40,8 +39,8 @@ module Id_predicate = (* Memoization module which retrieves the preprocessed form of predicates *) module Memo: sig - val memo_pred: (predicate -> pred_or_term option) -> predicate -> unit - val get_pred: predicate -> pred_or_term + val memo_pred: (predicate -> predicate option) -> predicate -> unit + val get_pred: predicate -> predicate val memo_term : (term -> term option) -> term -> unit val get_term : term -> term val clear: unit -> unit @@ -52,7 +51,7 @@ end = struct let get_pred p = try Id_predicate.Hashtbl.find tbl_pred p - with Not_found -> PoT_pred p + with Not_found -> p let memo_pred process p = try ignore (Id_predicate.Hashtbl.find tbl_pred p) with @@ -97,7 +96,7 @@ let preprocess_pred ~loc p = | Pvalid _ -> Logic_const.pvalid ~loc (llabel, t) | _ -> assert false in - Some (PoT_pred (Logic_const.pand ~loc (init, p_copy))) + Some (Logic_const.pand ~loc (init, p_copy)) | _ -> None end | _ -> None @@ -126,8 +125,6 @@ let preprocess_term ~loc t = let preprocessor = object inherit E_acsl_visitor.visitor - (* Only logic functions and logic predicates are handled. - E-acsl simply ignores all the other global annotations *) method !vannotation annot = match annot with | Dfun_or_pred _ -> Cil.DoChildren diff --git a/src/plugins/e-acsl/src/analyses/logic_normalizer.mli b/src/plugins/e-acsl/src/analyses/logic_normalizer.mli index 7eafabeb5287e0dcae03d0a935438077ad5117d7..bee473b8b87d60499c8f81445a3e7d93251632ac 100644 --- a/src/plugins/e-acsl/src/analyses/logic_normalizer.mli +++ b/src/plugins/e-acsl/src/analyses/logic_normalizer.mli @@ -42,7 +42,7 @@ val preprocess_annot : code_annotation -> unit val preprocess_predicate : predicate -> unit (** Preprocess a predicate and its children and store the results *) -val get_pred : predicate -> pred_or_term +val get_pred : predicate -> predicate (** Retrieve the preprocessed form of a predicate *) val get_term : term -> term (** Retrieve the preprocessed form of a term *) diff --git a/src/plugins/e-acsl/src/analyses/typing.ml b/src/plugins/e-acsl/src/analyses/typing.ml index f23e7f2d050b547d41689b5c49840c6bf9bb8fc3..5a714336460373d6ec0a4d5771ca763329427c31 100644 --- a/src/plugins/e-acsl/src/analyses/typing.ml +++ b/src/plugins/e-acsl/src/analyses/typing.ml @@ -812,107 +812,105 @@ and type_bound_variables ~loc ~lenv (t1, lv, t2) = (t1, lv, t2) and type_predicate ~lenv p = - match Logic_normalizer.get_pred p with - | PoT_term t -> type_term ~use_gmp_opt:true ~lenv t - | PoT_pred p -> - Cil.CurrentLoc.set p.pred_loc; - (* this pattern matching also follows the formal rules of the JFLA's paper *) - let op = - match p.pred_content with - | Pfalse | Ptrue -> c_int - | Papp(li, _, args) -> - begin - match li.l_body with - | LBpred p -> - let typed_args = - type_args - ~use_gmp_opt:true - ~lenv - li.l_profile - args - li.l_var_info.lv_name - in - ignore (type_predicate ~lenv:typed_args p); - List.iter Interval.Env.remove li.l_profile - | LBnone -> () - | LBreads _ -> () - | LBinductive _ -> () - | LBterm _ -> - Options.fatal "unexpected logic definition" - Printer.pp_predicate p - end; - c_int - | Pdangling _ -> Error.not_yet "\\dangling" - | Prel(_, t1, t2) -> - let i1 = Interval.infer t1 in - let i2 = Interval.infer 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 ~lenv t1); - ignore (type_term ~use_gmp_opt:true ~ctx ~lenv t2); - (match ctx with - | Nan -> c_int - | Real | Rational | Gmpz | C_float _ | C_integer _ -> ctx) - | Pand(p1, p2) - | Por(p1, p2) - | Pxor(p1, p2) - | Pimplies(p1, p2) - | Piff(p1, p2) -> - ignore (type_predicate ~lenv p1); - ignore (type_predicate ~lenv p2); - c_int - | Pnot p -> - ignore (type_predicate ~lenv p); - c_int - | Pif(t, p1, p2) -> - let ctx = mk_ctx ~use_gmp_opt:false c_int in - ignore (type_term ~use_gmp_opt:false ~ctx ~lenv t); - ignore (type_predicate ~lenv p1); - ignore (type_predicate ~lenv p2); - c_int - | Plet(li, p) -> - let li_t = Misc.term_of_li li in - type_letin li li_t; - ignore (type_term ~use_gmp_opt:true ~lenv li_t); - (type_predicate ~lenv p).ty - | Pforall _ - | Pexists _ -> - begin - let guards, goal = - Error.retrieve_preprocessing - "preprocessing of quantified predicate" - Bound_variables.get_preprocessed_quantifier - p - Printer.pp_predicate + 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 *) + let op = + match p.pred_content with + | Pfalse | Ptrue -> c_int + | Papp(li, _, args) -> + begin + match li.l_body with + | LBpred p -> + let typed_args = + type_args + ~use_gmp_opt:true + ~lenv + li.l_profile + args + li.l_var_info.lv_name in - let guards = - List.map - (fun (t1, x, t2) -> - type_bound_variables ~loc:p.pred_loc ~lenv (t1, x, t2)) - guards - in Bound_variables.replace p guards goal; - (type_predicate ~lenv goal).ty - end - | Pseparated tlist -> - List.iter - (fun t -> - ignore - (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t)) - tlist; - c_int - | Pinitialized(_, t) - | Pfreeable(_, t) - | Pallocable(_, t) - | Pvalid(_, t) - | Pvalid_read(_, t) - | Pobject_pointer(_,t) - | Pvalid_function t -> - ignore (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t); - c_int - | Pat(p, _) -> (type_predicate ~lenv p).ty - | Pfresh _ -> Error.not_yet "\\fresh" - in - coerce ~arith_operand:false ~ctx:c_int ~op c_int + ignore (type_predicate ~lenv:typed_args p); + List.iter Interval.Env.remove li.l_profile + | LBnone -> () + | LBreads _ -> () + | LBinductive _ -> () + | LBterm _ -> + Options.fatal "unexpected logic definition" + Printer.pp_predicate p + end; + c_int + | Pdangling _ -> Error.not_yet "\\dangling" + | Prel(_, t1, t2) -> + let i1 = Interval.infer t1 in + let i2 = Interval.infer 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 ~lenv t1); + ignore (type_term ~use_gmp_opt:true ~ctx ~lenv t2); + (match ctx with + | Nan -> c_int + | Real | Rational | Gmpz | C_float _ | C_integer _ -> ctx) + | Pand(p1, p2) + | Por(p1, p2) + | Pxor(p1, p2) + | Pimplies(p1, p2) + | Piff(p1, p2) -> + ignore (type_predicate ~lenv p1); + ignore (type_predicate ~lenv p2); + c_int + | Pnot p -> + ignore (type_predicate ~lenv p); + c_int + | Pif(t, p1, p2) -> + let ctx = mk_ctx ~use_gmp_opt:false c_int in + ignore (type_term ~use_gmp_opt:false ~ctx ~lenv t); + ignore (type_predicate ~lenv p1); + ignore (type_predicate ~lenv p2); + c_int + | Plet(li, p) -> + let li_t = Misc.term_of_li li in + type_letin li li_t; + ignore (type_term ~use_gmp_opt:true ~lenv li_t); + (type_predicate ~lenv p).ty + | Pforall _ + | Pexists _ -> + begin + let guards, goal = + Error.retrieve_preprocessing + "preprocessing of quantified predicate" + Bound_variables.get_preprocessed_quantifier + p + Printer.pp_predicate + in + let guards = + List.map + (fun (t1, x, t2) -> + type_bound_variables ~loc:p.pred_loc ~lenv (t1, x, t2)) + guards + in Bound_variables.replace p guards goal; + (type_predicate ~lenv goal).ty + end + | Pseparated tlist -> + List.iter + (fun t -> + ignore + (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t)) + tlist; + c_int + | Pinitialized(_, t) + | Pfreeable(_, t) + | Pallocable(_, t) + | Pvalid(_, t) + | Pvalid_read(_, t) + | Pobject_pointer(_,t) + | Pvalid_function t -> + ignore (type_term ~use_gmp_opt:false ~ctx:Nan ~lenv t); + c_int + | Pat(p, _) -> (type_predicate ~lenv p).ty + | Pfresh _ -> Error.not_yet "\\fresh" + in + coerce ~arith_operand:false ~ctx:c_int ~op c_int let type_term ~use_gmp_opt ?ctx ~lenv t = Options.feedback ~dkey ~level:4 "typing term '%a' in ctx '%a'." @@ -980,28 +978,31 @@ let get_cast_of_predicate ~lenv p = let clear = Memo.clear -let typer_visitor lenv = object +let typing_visitor lenv = object inherit E_acsl_visitor.visitor + (* global logic functions and predicates are evaluated are callsites *) + method !glob_annot _ = Cil.SkipChildren + method !vpredicate p = (* 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 type_named_predicate ~lenv p - with Error.Not_yet _ | Error.Typing_error _ -> () - in + translation time *) + ignore + (try type_named_predicate ~lenv p + with Error.Not_yet _ | Error.Typing_error _ -> ()); Cil.SkipChildren end let type_program ast = Visitor.visitFramacFileSameGlobals - (typer_visitor [] :> Visitor.frama_c_inplace) + (typing_visitor [] :> Visitor.frama_c_inplace) ast let type_code_annot lenv annot = ignore (Visitor.visitFramacCodeAnnotation - (typer_visitor lenv :> Visitor.frama_c_inplace) + (typing_visitor lenv :> Visitor.frama_c_inplace) annot) let preprocess_predicate lenv p = @@ -1009,7 +1010,7 @@ let preprocess_predicate lenv p = Bound_variables.preprocess_predicate p; ignore (Visitor.visitFramacPredicate - (typer_visitor lenv :> Visitor.frama_c_inplace) + (typing_visitor lenv :> Visitor.frama_c_inplace) p) let preprocess_rte ~lenv rte = diff --git a/src/plugins/e-acsl/src/code_generator/translate_predicates.ml b/src/plugins/e-acsl/src/code_generator/translate_predicates.ml index d369220466f63974ff0d2ec2f2cac11508847858..333bd7115dd1da25c44cad16fe731c2901c9d226 100644 --- a/src/plugins/e-acsl/src/code_generator/translate_predicates.ml +++ b/src/plugins/e-acsl/src/code_generator/translate_predicates.ml @@ -310,36 +310,34 @@ let rec predicate_content_to_exp ~adata ?name kf env p = | Pfresh _ -> Env.not_yet env "\\fresh" and to_exp ~adata ?name kf ?rte env p = - match Logic_normalizer.get_pred p with - | PoT_term t -> Translate_terms.to_exp ~adata kf env t - | PoT_pred p -> - let rte = match rte with None -> Env.generate_rte env | Some b -> b in - Extlib.flatten - (Env.with_params_and_result - ~rte:false - ~f:(fun env -> - let e, adata, env = - predicate_content_to_exp ~adata ?name kf env p - in - let env = if rte then !translate_rte_exp_ref kf env e else env in - let cast = - Typing.get_cast_of_predicate - ~lenv:(Env.Local_vars.get env) - p - in - Extlib.nest - adata - (Typed_number.add_cast - ~loc:p.pred_loc - ?name - env - kf - cast - Typed_number.C_number - None - e) - ) - env) + let p = Logic_normalizer.get_pred p in + let rte = match rte with None -> Env.generate_rte env | Some b -> b in + Extlib.flatten + (Env.with_params_and_result + ~rte:false + ~f:(fun env -> + let e, adata, env = + predicate_content_to_exp ~adata ?name kf env p + in + let env = if rte then !translate_rte_exp_ref kf env e else env in + let cast = + Typing.get_cast_of_predicate + ~lenv:(Env.Local_vars.get env) + p + in + Extlib.nest + adata + (Typed_number.add_cast + ~loc:p.pred_loc + ?name + env + kf + cast + Typed_number.C_number + None + e) + ) + env) let generalized_untyped_to_exp ~adata ?name kf ?rte env p = (* If [rte] is true, it means we're translating the root predicate of an