diff --git a/headers/header_spec.txt b/headers/header_spec.txt index 12f47d738bde8c22fc84e36987e7250514c3f6bb..d1e4b57004ff92cc5829c0968b7b6eeb2f95d182 100644 --- a/headers/header_spec.txt +++ b/headers/header_spec.txt @@ -1680,8 +1680,18 @@ src/plugins/wp/WpTac.ml: CEA_WP src/plugins/wp/WpTac.mli: CEA_WP src/plugins/wp/calculus.ml: CEA_WP src/plugins/wp/calculus.mli: CEA_WP +src/plugins/wp/cfgAnnot.ml: CEA_WP +src/plugins/wp/cfgAnnot.mli: CEA_WP +src/plugins/wp/cfgInfos.ml: CEA_WP +src/plugins/wp/cfgInfos.mli: CEA_WP +src/plugins/wp/cfgCalculus.ml: CEA_WP +src/plugins/wp/cfgCalculus.mli: CEA_WP src/plugins/wp/cfgDump.ml: CEA_WP src/plugins/wp/cfgDump.mli: CEA_WP +src/plugins/wp/cfgGenerator.ml: CEA_WP +src/plugins/wp/cfgGenerator.mli: CEA_WP +src/plugins/wp/cfgInit.ml: CEA_WP +src/plugins/wp/cfgInit.mli: CEA_WP src/plugins/wp/cfgWP.ml: CEA_WP src/plugins/wp/cfgWP.mli: CEA_WP src/plugins/wp/cil2cfg.ml: CEA_WP @@ -1827,6 +1837,8 @@ src/plugins/wp/wpAnnot.ml: CEA_WP src/plugins/wp/wpAnnot.mli: CEA_WP src/plugins/wp/wpContext.ml: CEA_WP src/plugins/wp/wpContext.mli: CEA_WP +src/plugins/wp/wpGenerator.ml: CEA_WP +src/plugins/wp/wpGenerator.mli: CEA_WP src/plugins/wp/wpPropId.ml: CEA_WP src/plugins/wp/wpPropId.mli: CEA_WP src/plugins/wp/wpReached.ml: CEA_WP diff --git a/src/kernel_services/ast_data/property.ml b/src/kernel_services/ast_data/property.ml index 0cb64d24a9898703c24e347a8161aec4a942904d..506bb221ee18751fb718f5c3806dc5f243890f31 100644 --- a/src/kernel_services/ast_data/property.ml +++ b/src/kernel_services/ast_data/property.ml @@ -370,6 +370,40 @@ let get_behavior = function | IPGlobalInvariant _ | IPOther _ -> None +let get_for_behaviors = function + | IPPredicate {ip_kind} -> + (match get_pk_behavior ip_kind with None -> [] | Some b -> [b.b_name]) + | IPBehavior {ib_bhv=b} + | IPAllocation {ial_bhv=Id_contract (_, b)} + | IPAssigns {ias_bhv=Id_contract (_, b)} + | IPFrom {if_bhv=Id_contract (_, b)} -> [b.b_name] + + | IPAllocation {ial_bhv=Id_loop ca} + | IPAssigns {ias_bhv=Id_loop ca} + | IPFrom {if_bhv=Id_loop ca} + | IPCodeAnnot { ica_ca = ca } -> + begin + match ca.annot_content with + | AAssert (bhvs,_) + | AInvariant (bhvs,_,_) + | AStmtSpec(bhvs,_) + | AAssigns(bhvs,_) + | AAllocation(bhvs,_) -> bhvs + | AVariant _ | APragma _ | AExtended _ -> [] + end + + | IPAxiomatic _ + | IPExtended _ + | IPLemma _ + | IPComplete _ + | IPDisjoint _ + | IPDecrease _ + | IPReachable _ + | IPPropertyInstance _ + | IPTypeInvariant _ + | IPGlobalInvariant _ + | IPOther _ -> [] + (* -------------------------------------------------------------------------- *) (* --- Property Status --- *) (* -------------------------------------------------------------------------- *) diff --git a/src/kernel_services/ast_data/property.mli b/src/kernel_services/ast_data/property.mli index 8c26d95abc56c6037c5797147a19a3c947abbb0e..531f38e79aab9956d437243fb4ce703c2d043383 100644 --- a/src/kernel_services/ast_data/property.mli +++ b/src/kernel_services/ast_data/property.mli @@ -520,6 +520,7 @@ val get_kinstr: identified_property -> kinstr val get_kf: identified_property -> kernel_function option val get_behavior: identified_property -> funbehavior option val get_names: identified_property -> string list +val get_for_behaviors: identified_property -> string list val location: identified_property -> location (** returns the location of the property. diff --git a/src/libraries/utils/bag.mli b/src/libraries/utils/bag.mli index 2b64c202ecd8b0d7e36e2cd9eff1ccf99b736491..d44fc6263cc4e10c300167608ba7e415b8ea3f79 100644 --- a/src/libraries/utils/bag.mli +++ b/src/libraries/utils/bag.mli @@ -36,6 +36,7 @@ val concat : 'a t -> 'a t -> 'a t val map : ('a -> 'b) -> 'a t -> 'b t val umap : ('a -> 'b t) -> 'a t -> 'b t +val umap_list : ('a -> 'b t) -> 'a list -> 'b t val iter : ('a -> unit) -> 'a t -> unit val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b diff --git a/src/plugins/wp/Factory.mli b/src/plugins/wp/Factory.mli index eef3a3a7ed5b5d193dc8c3c7b77b2065a129c10d..07df6d8730678c5a7341340e388aafc76db23630 100644 --- a/src/plugins/wp/Factory.mli +++ b/src/plugins/wp/Factory.mli @@ -50,3 +50,5 @@ val parse : Apply specifications to default setup. Default setup is [Factory.default]. Default warning is [Wp_parameters.abort]. *) + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/Generator.ml b/src/plugins/wp/Generator.ml index 31c18109ae01aec27ceb2ea4fc9c52291c090a14..4b5d51804c65821460d0d5ee8397f6c39468e130 100644 --- a/src/plugins/wp/Generator.ml +++ b/src/plugins/wp/Generator.ml @@ -21,106 +21,67 @@ (**************************************************************************) (* -------------------------------------------------------------------------- *) -(* --- WP Computer (main entry points) --- *) +(* --- Model Setup --- *) (* -------------------------------------------------------------------------- *) -class type computer = - object - method lemma : bool - method model : WpContext.model - method add_strategy : WpStrategy.strategy -> unit - method add_lemma : LogicUsage.logic_lemma -> unit - method compute : Wpo.t Bag.t +let user_setup () : Factory.setup = + begin + match Wp_parameters.Model.get () with + | ["Runtime"] -> + Wp_parameters.abort + "Model 'Runtime' is no more available.@\nIt will be reintroduced \ + in a future release." + | ["Logic"] -> + Wp_parameters.warning ~once:true + "Deprecated 'Logic' model.@\nUse 'Typed' with option '-wp-ref' \ + instead." ; + { + mheap = Factory.Typed MemTyped.Fits ; + mvar = Factory.Ref ; + cint = Cint.Natural ; + cfloat = Cfloat.Real ; + } + | ["Store"] -> + Wp_parameters.warning ~once:true + "Deprecated 'Store' model.@\nUse 'Typed' instead." ; + { + mheap = Factory.Typed MemTyped.Fits ; + mvar = Factory.Var ; + cint = Cint.Natural ; + cfloat = Cfloat.Real ; + } + | spec -> Factory.parse spec end (* -------------------------------------------------------------------------- *) -(* --- Property Entry Point --- *) -(* -------------------------------------------------------------------------- *) - -let compute_ip cc ip = - let open Property in match ip with - | IPLemma _ - | IPAxiomatic _ - -> - let rec iter cc = function - | IPLemma {il_name} -> cc#add_lemma (LogicUsage.logic_lemma il_name) - | IPAxiomatic {iax_props} -> List.iter (iter cc) iax_props - | _ -> () - in iter cc ip ; - cc#compute - - | IPBehavior {ib_kf; ib_bhv} -> - let model = cc#model in - let bhv = [ib_bhv.Cil_types.b_name] in - let assigns = WpAnnot.WithAssigns in - List.iter cc#add_strategy - (WpAnnot.get_function_strategies ~model ~assigns ~bhv ib_kf) ; - cc#compute - | IPComplete _ - | IPDisjoint _ - | IPCodeAnnot _ - | IPAllocation _ - | IPAssigns _ - | IPDecrease _ - | IPPredicate _ - -> - let model = cc#model in - let assigns = WpAnnot.WithAssigns in - List.iter cc#add_strategy - (WpAnnot.get_id_prop_strategies ~model ~assigns ip) ; - cc#compute - - | IPFrom _ - | IPReachable _ - | IPPropertyInstance _ - | IPOther _ - | IPTypeInvariant _ - | IPGlobalInvariant _ - | IPExtended _ - -> - Wp_parameters.result "Nothing to compute for '%a'" pretty ip ; - Bag.empty - -(* -------------------------------------------------------------------------- *) -(* --- Annotations Entry Point --- *) +(* --- WP Computer (main entry points) --- *) (* -------------------------------------------------------------------------- *) -let add_kf cc ?bhv ?prop kf = - let model = cc#model in - let assigns = WpAnnot.WithAssigns in - List.iter cc#add_strategy - (WpAnnot.get_function_strategies ~model ~assigns ?bhv ?prop kf) - -let add_lemmas cc = function - | None | Some[] -> - LogicUsage.iter_lemmas - (fun lem -> - let idp = WpPropId.mk_lemma_id lem in - if WpAnnot.filter_status idp then cc#add_lemma lem) - | Some ps -> - if List.mem "-@lemmas" ps then () - else LogicUsage.iter_lemmas - (fun lem -> - let idp = WpPropId.mk_lemma_id lem in - if WpAnnot.filter_status idp && WpPropId.select_by_name ps idp - then cc#add_lemma lem) - -let compute_kf cc ?kf ?bhv ?prop () = - begin - Option.iter (add_kf cc ?bhv ?prop) kf ; - cc#compute - end - -let compute_selection cc ?(fct=Wp_parameters.Fct_all) ?bhv ?prop () = - begin - add_lemmas cc prop ; - Wp_parameters.iter_fct (add_kf cc ?bhv ?prop) fct ; - cc#compute - end - -let compute_call cc stmt = - let model = cc#model in - List.iter cc#add_strategy (WpAnnot.get_call_pre_strategies ~model stmt) ; - cc#compute +let create + ?dump ?legacy + ?(setup: Factory.setup option) + ?(driver: Factory.driver option) + () : Wpo.generator = + let default f = function Some v -> v | None -> f () in + let dump = default Wp_parameters.Dump.get dump in + let legacy = default Wp_parameters.Legacy.get legacy in + let driver = default Driver.load_driver driver in + let setup = default user_setup setup in + if legacy then + let cc = + if dump + then WpGenerator.dumper () + else WpGenerator.computer setup driver in + let the_model = cc#model in + object + method model = the_model + method compute_ip = WpGenerator.compute_ip cc + method compute_call = WpGenerator.compute_call cc + method compute_main = WpGenerator.compute_selection cc + end + else + if dump + then CfgGenerator.dumper setup driver + else CfgGenerator.generator setup driver (* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/Generator.mli b/src/plugins/wp/Generator.mli index 146436a830a12047d91aac938fcdae84481772c2..aead35c64025fcf62bcfaf1a2fee33895da8d4b7 100644 --- a/src/plugins/wp/Generator.mli +++ b/src/plugins/wp/Generator.mli @@ -24,28 +24,14 @@ (* --- WP Computer (main entry points) --- *) (* -------------------------------------------------------------------------- *) -class type computer = - object - method lemma : bool - method model : WpContext.model - method add_strategy : WpStrategy.strategy -> unit - method add_lemma : LogicUsage.logic_lemma -> unit - method compute : Wpo.t Bag.t - end +(** Compute model setup from command line options. *) +val user_setup : unit -> Factory.setup -open Wp_parameters +val create : + ?dump:bool -> + ?legacy:bool -> + ?setup:Factory.setup -> + ?driver:Factory.driver -> + unit -> Wpo.generator -val compute_ip : computer -> Property.t -> Wpo.t Bag.t -val compute_call : computer -> Cil_types.stmt -> Wpo.t Bag.t - -val compute_kf : computer -> - ?kf:Kernel_function.t -> - ?bhv:string list -> - ?prop:string list -> - unit -> Wpo.t Bag.t - -val compute_selection : computer -> - ?fct:functions -> - ?bhv:string list -> - ?prop:string list -> - unit -> Wpo.t Bag.t +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/Makefile.in b/src/plugins/wp/Makefile.in index 114eb12bfb245431031c90822f57624120627395..d78ac2bd27308ea2531701755545426636c607eb 100644 --- a/src/plugins/wp/Makefile.in +++ b/src/plugins/wp/Makefile.in @@ -96,9 +96,11 @@ PLUGIN_CMO:= \ ProverTask ProverErgo ProverCoq \ filter_axioms Cache ProverWhy3 \ driver prover ProverSearch ProverScript \ - Generator Factory \ + Factory \ + cfgInit cfgAnnot cfgInfos cfgCalculus \ calculus cfgDump cfgWP \ - register VC + wpGenerator cfgGenerator \ + Generator register VC PLUGIN_CMI:= diff --git a/src/plugins/wp/StmtSemantics.ml b/src/plugins/wp/StmtSemantics.ml index b9b9e3afead51dd1acc59c9df87845c3affee4fc..b068e462498a28251561510c8acd4ff0fe6a0b6e 100644 --- a/src/plugins/wp/StmtSemantics.ml +++ b/src/plugins/wp/StmtSemantics.ml @@ -98,7 +98,7 @@ struct subst_formals = Varinfo.Map.empty} in env @* [ Clabels.init, Cfg.node (); - Clabels.at_exit, Cfg.node(); + Clabels.exit, Cfg.node(); ] (* -------------------------------------------------------------------------- *) @@ -380,7 +380,7 @@ struct @* [Clabels.init, env @: Clabels.init; Clabels.pre, pre_node; Clabels.here, pre_node; Clabels.next, post_node; Clabels.post, post_node; - Clabels.at_exit, env @: Clabels.at_exit] + Clabels.exit, env @: Clabels.exit] in (* TODO: Call inlining. *) @@ -391,7 +391,7 @@ struct @^ result (env_call @* [(Clabels.here, return_node); (Clabels.next, next_node)]) @^ exit_status (env_call @* [(Clabels.here, exit_stop); - (Clabels.next, env @: Clabels.at_exit)]) + (Clabels.next, env @: Clabels.exit)]) and call : env -> lval option -> exp -> exp list -> paths @@ -469,7 +469,7 @@ struct (fun acc post -> acc @^ post_cond Exits post_at_exit_env post) nop b.b_post_cond @^ goto post_normal_behavior (env @: Clabels.post) - @^ goto post_at_exit_behavior (env @: Clabels.at_exit) + @^ goto post_at_exit_behavior (env @: Clabels.exit) in let env = env @* [Clabels.here, env @: Clabels.pre; Clabels.next, env @: Clabels.post] in parallel behavior env spec.spec_behavior diff --git a/src/plugins/wp/VC.ml b/src/plugins/wp/VC.ml index b614862c03b25b4d5c331c9d165684b08c53ca68..32355d2fc93dbf0e666f73d1439067e66e63d405 100644 --- a/src/plugins/wp/VC.ml +++ b/src/plugins/wp/VC.ml @@ -69,24 +69,21 @@ let () = Property_status.register_property_remove_hook remove (* --- Generator Interface --- *) (* -------------------------------------------------------------------------- *) -let generator ?model () = +let generator model = let setup = match model with - | None -> Register.cmdline () - | Some s -> Factory.parse [s] in - let driver = Driver.load_driver () in - CfgWP.computer setup driver + | None -> None + | Some s -> Some (Factory.parse [s]) in + Generator.create ~dump:false ?setup () let generate_ip ?model ip = - let gen = generator ?model () in - Generator.compute_ip gen ip + (generator model)#compute_ip ip -let generate_kf ?model ?(bhv=[]) kf = - let gen = generator ?model () in - Generator.compute_kf gen ~bhv ~kf () +let generate_kf ?model ?bhv ?prop kf = + let kfs = Kernel_function.Set.singleton kf in + (generator model)#compute_main ~fct:(Fct_list kfs) ?bhv ?prop () let generate_call ?model stmt = - let gen = generator ?model () in - Generator.compute_call gen stmt + (generator model)#compute_call stmt (* -------------------------------------------------------------------------- *) (* --- Prover Interface --- *) diff --git a/src/plugins/wp/VC.mli b/src/plugins/wp/VC.mli index 3410ae50f5bdf16c0b03bc3497e9f624ed63938d..cc695838ed463262c37bb9832c09892044b7cb10 100644 --- a/src/plugins/wp/VC.mli +++ b/src/plugins/wp/VC.mli @@ -66,7 +66,8 @@ val iter_kf : (t -> unit) -> ?bhv:string list -> Kernel_function.t -> unit *) val generate_ip : ?model:string -> Property.t -> t Bag.t -val generate_kf : ?model:string -> ?bhv:string list -> Kernel_function.t -> t Bag.t +val generate_kf : ?model:string -> ?bhv:string list -> ?prop:string list -> + Kernel_function.t -> t Bag.t val generate_call : ?model:string -> Cil_types.stmt -> t Bag.t (** {2 Prover Interface} *) diff --git a/src/plugins/wp/calculus.ml b/src/plugins/wp/calculus.ml index ffc8d50d665a0fd2302cbada6f80fb395ce1d2ab..5cb2133bb68bc2fac5644b88672b91e7e740947e 100644 --- a/src/plugins/wp/calculus.ml +++ b/src/plugins/wp/calculus.ml @@ -76,12 +76,12 @@ module Cfg (W : Mcfg.S) = struct let add_assigns_hyp wenv obj h_assigns = match h_assigns with | WpPropId.AssignsLocations (h_id, a) -> let hid = Some h_id in - let obj = W.use_assigns wenv a.WpPropId.a_stmt hid a obj in + let obj = W.use_assigns wenv hid a obj in Some a.WpPropId.a_label, obj | WpPropId.AssignsAny a -> Wp_parameters.warning ~current:true ~once:true "Missing assigns clause (assigns 'everything' instead)" ; - let obj = W.use_assigns wenv a.WpPropId.a_stmt None a obj in + let obj = W.use_assigns wenv None a obj in Some a.WpPropId.a_label, obj | WpPropId.NoAssignsInfo -> None, obj @@ -476,7 +476,7 @@ module Cfg (W : Mcfg.S) = struct | (Set (lv, e, _)) -> W.assign wenv s lv e obj | (Asm _) -> let asm = WpPropId.mk_asm_assigns_desc s in - W.use_assigns wenv asm.WpPropId.a_stmt None asm obj + W.use_assigns wenv None asm obj | (Call _) -> assert false | Skip _ | Code_annot _ -> obj end @@ -617,49 +617,7 @@ module Cfg (W : Mcfg.S) = struct Cil.CurrentLoc.set old_loc; res - let compute_global_init wenv filter obj = - Globals.Vars.fold_in_file_order - (fun var initinfo obj -> - if var.vstorage = Extern then obj else - let do_init = match filter with - | `All -> true - | `InitConst -> WpStrategy.isGlobalInitConst var - in if not do_init then obj - else - let old_loc = Cil.CurrentLoc.get () in - Cil.CurrentLoc.set var.vdecl ; - let obj = W.init wenv var initinfo.init obj in - Cil.CurrentLoc.set old_loc ; obj - ) obj - - let process_global_const wenv obj = - Globals.Vars.fold_in_file_order - (fun var _initinfo obj -> - if WpStrategy.isGlobalInitConst var - then W.const wenv var obj - else obj - ) obj - - (* WP of global initializations. *) - let process_global_init wenv kf obj = - if WpStrategy.is_main_init kf then - begin - let obj = W.label wenv None Clabels.init obj in - compute_global_init wenv `All obj - end - else if W.has_init wenv then - begin - let obj = - if WpStrategy.isInitConst () - then process_global_const wenv obj else obj in - let obj = W.use_assigns wenv None None WpPropId.mk_init_assigns obj in - let obj = W.label wenv None Clabels.init obj in - compute_global_init wenv `All obj - end - else - if WpStrategy.isInitConst () - then compute_global_init wenv `InitConst obj - else obj + module Init = CfgInit.Make(W) let get_weakest_precondition cfg ((kf, _g, strategy, res, wenv) as env) = debug "[wp-cfg] start Pass1"; @@ -670,7 +628,7 @@ module Cfg (W : Mcfg.S) = struct * but if not, it will only fetch Pass1 result. *) let e_start = Cil2cfg.start_edge cfg in let obj = get_wp_edge env e_start in - let obj = process_global_init wenv kf obj in + let obj = Init.process_global_init wenv kf obj in let obj = match WpStrategy.strategy_kind strategy with | WpStrategy.SKannots -> obj | WpStrategy.SKfroms info -> diff --git a/src/plugins/wp/cfgAnnot.ml b/src/plugins/wp/cfgAnnot.ml new file mode 100644 index 0000000000000000000000000000000000000000..c837a3ede6ba819838292be5010ca9f7178ac395 --- /dev/null +++ b/src/plugins/wp/cfgAnnot.ml @@ -0,0 +1,467 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(* -------------------------------------------------------------------------- *) +(* --- Smoke Tests --- *) +(* -------------------------------------------------------------------------- *) + +let smoke kf ~id ?doomed ?unreachable () = + WpPropId.mk_smoke kf ~id ?doomed ?unreachable () , Logic_const.pfalse + +let get_unreachable kf stmt = + WpPropId.mk_smoke kf ~id:"unreachabled" ~unreachable:stmt () + +(* -------------------------------------------------------------------------- *) +(* --- Memoization --- *) +(* -------------------------------------------------------------------------- *) + +module CodeKey = +struct + type t = kernel_function * stmt + let compare (a:t) (b:t) = Stdlib.compare (snd a).sid (snd b).sid + let pretty fmt (a:t) = Format.fprintf fmt "s%d" (snd a).sid +end + +(* -------------------------------------------------------------------------- *) +(* --- Property Accessors : Behaviors --- *) +(* -------------------------------------------------------------------------- *) + +type behavior = { + bhv_assumes: WpPropId.pred_info list ; + bhv_requires: WpPropId.pred_info list ; + bhv_smokes: WpPropId.pred_info list ; + bhv_ensures: WpPropId.pred_info list ; + bhv_exits: WpPropId.pred_info list ; + bhv_post_assigns: WpPropId.assigns_full_info ; + bhv_exit_assigns: WpPropId.assigns_full_info ; +} + +let normalize_assumes h = + let module L = NormAtLabels in + let labels = L.labels_fct_pre in + L.preproc_annot labels h + +let implies ?assumes p = + match assumes with None -> p | Some h -> Logic_const.pimplies (h,p) + +let filter ~goal ip = + if goal + then Logic_utils.verify_predicate ip.ip_content.tp_kind + else Logic_utils.use_predicate ip.ip_content.tp_kind + +let normalize_pre ~goal kf bhv ?assumes ip = + if filter ~goal ip then + let module L = NormAtLabels in + let labels = L.labels_fct_pre in + let id = WpPropId.mk_pre_id kf Kglobal bhv ip in + let p = L.preproc_annot labels ip.ip_content.tp_statement in + Some (id, implies ?assumes p) + else None + +let normalize_post ~goal kf bhv tk ?assumes (itk,ip) = + if tk = itk && filter ~goal ip then + let module L = NormAtLabels in + let labels = L.labels_fct_post ~exit:(tk=Exits) in + let id = WpPropId.mk_post_id kf Kglobal bhv (tk,ip) in + let p = L.preproc_annot labels ip.ip_content.tp_statement in + Some (id , implies ?assumes p) + else None + +let normalize_froms tk froms = + let module L = NormAtLabels in + let labels = L.labels_fct_assigns ~exit:(tk=Exits) in + L.preproc_assigns labels froms + +let normalize_fct_assigns kf ~exits bhv = function + | WritesAny -> + WpPropId.empty_assigns_info, WpPropId.empty_assigns_info + | Writes froms -> + let make tk = + match WpPropId.mk_fct_assigns_id kf exits bhv tk froms with + | None -> WpPropId.empty_assigns_info + | Some id -> + let assigns = normalize_froms tk froms in + let desc = WpPropId.mk_kf_assigns_desc assigns in + WpPropId.mk_assigns_info id desc + in + make Normal, + if exits then make Exits else WpPropId.empty_assigns_info + +let get_behavior_goals kf ?(smoking=false) ?(exits=false) bhv = + let pre_cond = normalize_pre ~goal:false kf bhv in + let post_cond = normalize_post ~goal:true kf bhv in + let p_asgn, e_asgn = normalize_fct_assigns kf ~exits bhv bhv.b_assigns in + let smokes = + if smoking && bhv.b_requires <> [] then + let bname = + if Cil.is_default_behavior bhv then "default" else bhv.b_name in + let id = bname ^ "_requires" in + let doomed = Property.ip_requires_of_behavior kf Kglobal bhv in + [smoke kf ~id ~doomed ()] + else [] + in + { + bhv_assumes = List.filter_map pre_cond bhv.b_assumes; + bhv_requires = List.filter_map pre_cond bhv.b_requires; + bhv_ensures = List.filter_map (post_cond Normal) bhv.b_post_cond ; + bhv_exits = List.filter_map (post_cond Exits) bhv.b_post_cond ; + bhv_post_assigns = p_asgn ; + bhv_exit_assigns = e_asgn ; + bhv_smokes = smokes; + } + +(* -------------------------------------------------------------------------- *) +(* --- Side Behavior Requires --- *) +(* -------------------------------------------------------------------------- *) + +let get_requires ~goal kf bhv = + List.filter_map (normalize_pre ~goal kf bhv) bhv.b_requires + +let get_preconditions ~goal kf = + let module L = NormAtLabels in + let mk_pre = L.preproc_annot L.labels_fct_pre in + List.map + (fun bhv -> + let p = Ast_info.behavior_precondition ~goal bhv in + let ip = Logic_const.new_predicate p in + WpPropId.mk_pre_id kf Kglobal bhv ip, mk_pre p + ) (Annotations.behaviors kf) + +let get_complete_behaviors kf = + let spec = Annotations.funspec kf in + let module L = NormAtLabels in + List.map + (fun bs -> + WpPropId.mk_compl_bhv_id (kf,Kglobal,[],bs) , + L.preproc_annot L.labels_fct_pre @@ Ast_info.complete_behaviors spec bs + ) spec.spec_complete_behaviors + +let get_disjoint_behaviors kf = + let spec = Annotations.funspec kf in + let module L = NormAtLabels in + List.map + (fun bs -> + WpPropId.mk_disj_bhv_id (kf,Kglobal,[],bs) , + L.preproc_annot L.labels_fct_pre @@ Ast_info.disjoint_behaviors spec bs + ) spec.spec_disjoint_behaviors + +(* -------------------------------------------------------------------------- *) +(* --- Contracts --- *) +(* -------------------------------------------------------------------------- *) + +type contract = { + contract_cond : WpPropId.pred_info list ; + contract_hpre : WpPropId.pred_info list ; + contract_post : WpPropId.pred_info list ; + contract_exit : WpPropId.pred_info list ; + contract_smoke : WpPropId.pred_info list ; + contract_assigns : Cil_types.assigns ; +} + +let assigns_upper_bound behaviors = + let collect_assigns (def, assigns) bhv = + (* Default behavior prevails *) + if Cil.is_default_behavior bhv then Some bhv.b_assigns, None + else if Option.is_some def then def, None + else begin + (* Note that here, def is None *) + match assigns, bhv.b_assigns with + | None, a -> None, Some a + | Some WritesAny, _ | Some _, WritesAny -> None, Some WritesAny + | Some (Writes a), Writes b -> None, Some (Writes (a @ b)) + end + in + match List.fold_left collect_assigns (None, None) behaviors with + | Some a, _ -> a (* default behavior first *) + | _, Some a -> a (* else combined behaviors *) + | _ -> WritesAny + +(* -------------------------------------------------------------------------- *) +(* --- Call Contracts --- *) +(* -------------------------------------------------------------------------- *) + +(*TODO: put it in Status_by_call ? *) +module AllPrecondStatus = + State_builder.Hashtbl(Kernel_function.Hashtbl)(Datatype.Unit) + (struct + let name = "Wp.CfgAnnot.AllPrecondStatus" + let dependencies = [Ast.self] + let size = 32 + end) + +let setup_preconditions kf = + if not (AllPrecondStatus.mem kf) then + begin + AllPrecondStatus.add kf () ; + Statuses_by_call.setup_all_preconditions_proxies kf ; + end + +let get_precond_at kf stmt (id,p) = + let pi = WpPropId.property_of_id id in + let pi_at = Statuses_by_call.precondition_at_call kf pi stmt in + let id_at = WpPropId.mk_call_pre_id kf stmt pi pi_at in + id_at , p + +module CallContract = WpContext.StaticGenerator(Kernel_function) + (struct + type key = kernel_function + type data = contract + let name = "Wp.CfgAnnot.CallContract" + let compile kf = + let wcond : WpPropId.pred_info list ref = ref [] in + let whpre : WpPropId.pred_info list ref = ref [] in + let wpost : WpPropId.pred_info list ref = ref [] in + let wexit : WpPropId.pred_info list ref = ref [] in + let add w f x = match f x with Some y -> w := y :: !w | None -> () in + let behaviors = Annotations.behaviors kf in + setup_preconditions kf ; + List.iter + begin fun bhv -> + let assumes = normalize_assumes (Ast_info.behavior_assumes bhv) in + let mk_cond = normalize_pre ~goal:true kf bhv ~assumes in + let mk_hpre = normalize_pre ~goal:false kf bhv ~assumes in + let mk_post = normalize_post ~goal:false kf bhv ~assumes in + List.iter (add wcond @@ mk_cond) bhv.b_requires ; + List.iter (add whpre @@ mk_hpre) bhv.b_requires ; + List.iter (add wpost @@ mk_post Normal) bhv.b_post_cond ; + List.iter (add wexit @@ mk_post Exits) bhv.b_post_cond ; + end behaviors ; + { + contract_cond = List.rev !wcond ; + contract_hpre = List.rev !whpre ; + contract_post = List.rev !wpost ; + contract_exit = List.rev !wexit ; + contract_smoke = [] ; + contract_assigns = assigns_upper_bound behaviors + } + end) + +let get_call_contract ?smoking kf stmt = + let cc = CallContract.get kf in + let preconds = List.map (get_precond_at kf stmt) cc.contract_cond in + match smoking with + | None -> + { cc with contract_cond = preconds } + | Some s -> + let g = smoke kf ~id:"dead_call" ~unreachable:s () in + { cc with contract_cond = preconds ; contract_smoke = [ g ] } + +(* -------------------------------------------------------------------------- *) +(* --- Assembly Code --- *) +(* -------------------------------------------------------------------------- *) + +let is_assembly stmt = + match stmt.skind with + | Instr (Asm _) -> true + | _ -> false + +let get_stmt_assigns kf stmt = + let asgn = + Annotations.fold_code_annot + begin fun _emitter ca l -> + match ca.annot_content with + | AStmtSpec(fors,s) -> + List.fold_left + (fun l bhv -> + match bhv.b_assigns with + | WritesAny -> l + | Writes froms -> + let module L = NormAtLabels in + let labels = L.labels_stmt_assigns ~kf stmt in + match + WpPropId.mk_stmt_assigns_id kf stmt fors bhv froms + with + | None -> l + | Some id -> + let froms = L.preproc_assigns labels froms in + let desc = WpPropId.mk_stmt_assigns_desc stmt froms in + WpPropId.mk_assigns_info id desc :: l + ) l s.spec_behavior + | _ -> l + end stmt [] + in if asgn = [] then [WpPropId.empty_assigns_info] else asgn + +(* -------------------------------------------------------------------------- *) +(* --- Code Assertions --- *) +(* -------------------------------------------------------------------------- *) + +type code_assertions = { + code_admitted: WpPropId.pred_info list ; + code_verified: WpPropId.pred_info list ; +} + +let reverse_code_assertions a = { + code_admitted = List.rev a.code_admitted ; + code_verified = List.rev a.code_verified ; +} + +module CodeAssertions = WpContext.StaticGenerator(CodeKey) + (struct + type key = CodeKey.t + type data = code_assertions + let name = "Wp.CfgAnnot.CodeAssertions" + let compile (kf,stmt) = + let labels = NormAtLabels.labels_assert ~kf stmt in + let normalize_pred p = NormAtLabels.preproc_annot labels p in + reverse_code_assertions @@ + Annotations.fold_code_annot + begin fun _emitter ca l -> + match ca.annot_content with + | AStmtSpec _ when not @@ is_assembly stmt -> + let source = fst (Cil_datatype.Stmt.loc stmt) in + Wp_parameters.warning ~once:true ~source + "Statement specifications not yet supported (skipped)." ; l + | AInvariant(_,false,_) -> + let source = fst (Cil_datatype.Stmt.loc stmt) in + Wp_parameters.warning ~once:true ~source + "Generalized invariant not yet supported (skipped)." ; l + | AAssert(_,a) -> + let p = + WpPropId.mk_assert_id kf stmt ca , + normalize_pred a.tp_statement in + let admit = Logic_utils.use_predicate a.tp_kind in + let verif = Logic_utils.verify_predicate a.tp_kind in + let use flag p ps = if flag then p::ps else ps in + { + code_admitted = use admit p l.code_admitted ; + code_verified = use verif p l.code_verified ; + } + | _ -> l + end stmt { + code_admitted = []; + code_verified = []; + } + end) + +let get_code_assertions ?(smoking=false) kf stmt = + let ca = CodeAssertions.get (kf,stmt) in + if smoking then + let s = smoke kf ~id:"dead_code" ~unreachable:stmt () in + { ca with code_verified = s :: ca.code_verified } + else ca + +(* -------------------------------------------------------------------------- *) +(* --- Loop Invariants --- *) +(* -------------------------------------------------------------------------- *) + +type loop_contract = { + (* to be verified at loop entry *) + loop_established: WpPropId.pred_info list; + (* to be assumed for loop current *) + loop_invariants: WpPropId.pred_info list; + (* to be proved after loop invariants *) + loop_smoke: WpPropId.pred_info list; + (* to be verified after loop body *) + loop_preserved: WpPropId.pred_info list; + (* assigned by loop body *) + loop_assigns: WpPropId.assigns_full_info list; +} + +let reverse_loop_contract l = { + loop_established = List.rev l.loop_established ; + loop_invariants = List.rev l.loop_invariants ; + loop_preserved = List.rev l.loop_preserved ; + loop_assigns = List.rev l.loop_assigns ; + loop_smoke = List.rev l.loop_smoke ; +} + +let default_assigns stmt l = + { l with + loop_assigns = + if l.loop_assigns <> [] then l.loop_assigns + else [WpPropId.mk_loop_any_assigns_info stmt] } + +module LoopContract = WpContext.StaticGenerator(CodeKey) + (struct + type key = CodeKey.t + type data = loop_contract + let name = "Wp.CfgAnnot.LoopContract" + let compile (kf,stmt) = + let labels = NormAtLabels.labels_loop stmt in + let normalize_pred p = NormAtLabels.preproc_annot labels p in + let normalize_annot (i,p) = i, normalize_pred p in + let normalize_assigns w = NormAtLabels.preproc_assigns labels w in + default_assigns stmt @@ + reverse_loop_contract @@ + Annotations.fold_code_annot + begin fun _emitter ca l -> + match ca.annot_content with + | AInvariant(_,true,inv) -> + let p = normalize_pred inv.tp_statement in + let g_hyp = WpPropId.mk_inv_hyp_id kf stmt ca in + let g_est, g_ind = WpPropId.mk_loop_inv kf stmt ca in + let admit = Logic_utils.use_predicate inv.tp_kind in + let verif = Logic_utils.verify_predicate inv.tp_kind in + let use flag id p ps = if flag then (id,p) :: ps else ps in + { l with + loop_established = use verif g_est p l.loop_established ; + loop_invariants = use admit g_hyp p l.loop_invariants ; + loop_preserved = use verif g_ind p l.loop_preserved ; + } + | AVariant(term, None) -> + let vpos , vdec = + WpStrategy.mk_variant_properties kf stmt ca term in + { l with loop_preserved = + normalize_annot vdec :: + normalize_annot vpos :: + l.loop_preserved } + | AAssigns(_,WritesAny) -> + let asgn = WpPropId.mk_loop_any_assigns_info stmt in + { l with loop_assigns = asgn :: l.loop_assigns } + | AAssigns(_,Writes w) -> + begin match WpPropId.mk_loop_assigns_id kf stmt ca w with + | None -> l (* shall not occur *) + | Some id -> + let w = normalize_assigns w in + let a = WpPropId.mk_loop_assigns_desc stmt w in + let asgn = WpPropId.mk_assigns_info id a in + { l with loop_assigns = asgn :: l.loop_assigns } + end + | _ -> l + end stmt { + loop_established = [] ; + loop_invariants = [] ; + loop_preserved = [] ; + loop_smoke = [] ; + loop_assigns = [] ; + } + end) + +let get_loop_contract ?(smoking=false) kf stmt = + let lc = LoopContract.get (kf,stmt) in + if smoking && not (WpReached.is_dead_code stmt) then + let g = smoke kf ~id:"dead_loop" ~unreachable:stmt () in + { lc with loop_smoke = g :: lc.loop_smoke } + else lc + +(* -------------------------------------------------------------------------- *) +(* --- Clear Tablesnts --- *) +(* -------------------------------------------------------------------------- *) + +let clear () = + CallContract.clear () ; + LoopContract.clear () ; + CodeAssertions.clear () + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgAnnot.mli b/src/plugins/wp/cfgAnnot.mli new file mode 100644 index 0000000000000000000000000000000000000000..1ca50e9f0ce2ca26a0462d03491ae619278641dd --- /dev/null +++ b/src/plugins/wp/cfgAnnot.mli @@ -0,0 +1,115 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open WpPropId + +(** Normalization of Annotations. + + Labels are renamed wrt NormAtLabels and reorganized for use/prove + dispatching in [CfgCalculus]. *) + +(* -------------------------------------------------------------------------- *) +(* --- Property Accessors : Behaviors --- *) +(* -------------------------------------------------------------------------- *) + +type behavior = { + bhv_assumes: pred_info list ; + bhv_requires: pred_info list ; + bhv_smokes: pred_info list ; + bhv_ensures: pred_info list ; + bhv_exits: pred_info list ; + bhv_post_assigns: assigns_full_info ; + bhv_exit_assigns: assigns_full_info ; +} + +val get_requires : + goal:bool -> kernel_function -> funbehavior -> pred_info list + +val get_preconditions : + goal:bool -> kernel_function -> pred_info list + +val get_behavior_goals : + kernel_function -> + ?smoking:bool -> ?exits:bool -> + funbehavior -> behavior + +val get_complete_behaviors : kernel_function -> pred_info list +val get_disjoint_behaviors : kernel_function -> pred_info list + +(* -------------------------------------------------------------------------- *) +(* --- Property Accessors : Assertions --- *) +(* -------------------------------------------------------------------------- *) + +type code_assertions = { + code_admitted: pred_info list ; + code_verified: pred_info list ; +} + +val get_code_assertions : + ?smoking:bool -> kernel_function -> stmt -> code_assertions + +val get_unreachable : kernel_function -> stmt -> prop_id +val get_stmt_assigns : kernel_function -> stmt -> assigns_full_info list + +(* -------------------------------------------------------------------------- *) +(* --- Property Accessors : Loop Contracts --- *) +(* -------------------------------------------------------------------------- *) + +type loop_contract = { + (** to be verified at loop entry *) + loop_established: pred_info list; + (** to be assumed for loop current *) + loop_invariants: pred_info list; + (** to be proved after loop invariants *) + loop_smoke: pred_info list; + (** to be verified after loop body *) + loop_preserved: pred_info list; + (** assigned by loop body *) + loop_assigns: assigns_full_info list; +} + +val get_loop_contract : ?smoking:bool -> + kernel_function -> stmt -> loop_contract + +(* -------------------------------------------------------------------------- *) +(* --- Property Accessors : Call Contracts --- *) +(* -------------------------------------------------------------------------- *) + +type contract = { + contract_cond : pred_info list ; + contract_hpre : pred_info list ; + contract_post : pred_info list ; + contract_exit : pred_info list ; + contract_smoke : pred_info list ; + contract_assigns : assigns ; +} + +val get_call_contract : ?smoking:stmt -> kernel_function -> stmt -> contract + +(* -------------------------------------------------------------------------- *) +(* --- Clear Tablesnts --- *) +(* -------------------------------------------------------------------------- *) + +val clear : unit -> unit + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgCalculus.ml b/src/plugins/wp/cfgCalculus.ml new file mode 100644 index 0000000000000000000000000000000000000000..697b3eb75e3b8dd4b7f3aa1ce01b892aa447453c --- /dev/null +++ b/src/plugins/wp/cfgCalculus.ml @@ -0,0 +1,417 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Cil_datatype + +(* -------------------------------------------------------------------------- *) +(* --- Automata Helpers --- *) +(* -------------------------------------------------------------------------- *) + +module WpLog = Wp_parameters +module Kf = Kernel_function +module Cfg = Interpreted_automata +module G = Cfg.G +module V = Cfg.Vertex +module Vhash = V.Hashtbl +type vertex = Cfg.vertex +type assigns = WpPropId.assigns_full_info + +(* -------------------------------------------------------------------------- *) +(* --- Calculus Modes (passes) --- *) +(* -------------------------------------------------------------------------- *) + +type mode = { + kf: kernel_function; + bhv : funbehavior ; + infos : CfgInfos.t ; +} + +type props = [ `All | `Names of string list | `PropId of Property.t ] + +let default_requires mode kf = + if Cil.is_default_behavior mode.bhv then [] else + try + let bhv = List.find Cil.is_default_behavior (Annotations.behaviors kf) in + CfgAnnot.get_requires ~goal:false kf bhv + with Not_found -> [] + +(* -------------------------------------------------------------------------- *) +(* --- Property Selection by Mode --- *) +(* -------------------------------------------------------------------------- *) + +let is_default_bhv (m: mode) = Cil.is_default_behavior m.bhv + +let is_selected_bhv (m: mode) (bhv: funbehavior) = + m.bhv.b_name = bhv.b_name + +let is_selected_for (m: mode) ~goal (fors: string list) = + (fors=[] && (not goal || is_default_bhv m)) || + List.mem m.bhv.b_name fors + +let is_selected_ca (m: mode) ~goal (ca: code_annotation) = + match ca.annot_content with + | AAssigns(forb,_) + | AAllocation(forb,_) + | AAssert(forb,_) + | AInvariant(forb,_,_) + -> is_selected_for m ~goal forb + | AVariant _ -> is_default_bhv m + | AExtended _ | AStmtSpec _ | APragma _ -> + assert false (* n/a *) + +let is_active_mode ~mode ~goal (p: Property.t) = + let open Property in + match p with + | IPCodeAnnot { ica_ca } -> is_selected_ca mode ~goal ica_ca + | IPPredicate { ip_kind } -> + begin match ip_kind with + | PKRequires _ | PKAssumes _ -> true + | PKEnsures(bhv,_) -> is_selected_bhv mode bhv + | PKTerminates -> is_default_bhv mode + end + | IPAllocation { ial_bhv = bhv } | IPAssigns { ias_bhv = bhv } -> + begin match bhv with + | Id_loop ca -> is_selected_ca mode ~goal ca + | Id_contract(_,bhv) -> is_selected_bhv mode bhv + end + | IPDecrease { id_ca = None } -> is_default_bhv mode + | IPDecrease { id_ca = Some ca } -> is_selected_ca mode ~goal ca + | IPComplete _ | IPDisjoint _ -> is_default_bhv mode + | IPOther _ -> true + | IPFrom _ | IPGlobalInvariant _ | IPTypeInvariant _ + | IPAxiomatic _ | IPLemma _ + | IPExtended _ | IPBehavior _ + | IPReachable _ | IPPropertyInstance _ + -> assert false (* n/a *) + +let is_selected_props (props : props) ?pi pid = + WpPropId.filter_status pid && + match props with + | `All | `Names [] -> WpPropId.select_default pid + | `Names ps -> WpPropId.select_by_name ps pid + | `PropId p -> + Property.equal p @@ match pi with + | Some q -> q + | None -> WpPropId.property_of_id pid + +let rec factorize ~wdefault = function + | (_,w)::wcs when w==wdefault -> factorize ~wdefault wcs + | (e,w)::wcs -> collect ~wdefault [e] w wcs + | [] -> [] +and collect ~wdefault rs wr = function + | (e,w)::wcs when w==wr -> collect ~wdefault (e::rs) wr wcs + | wcs -> (List.rev rs,wr) :: factorize ~wdefault wcs + +(* -------------------------------------------------------------------------- *) +(* --- WP Calculus Driver from Interpreted Automata --- *) +(* -------------------------------------------------------------------------- *) + +module Make(W : Mcfg.S) = +struct + + module I = CfgInit.Make(W) + + (* --- Traversal Environment --- *) + + type env = { + mode: mode; + props: props; + body: Cfg.automaton option; + succ: Cfg.vertex -> Cfg.G.edge list; + dead: stmt -> bool ; + we: W.t_env; + wp: W.t_prop option Vhash.t; (* None is used for non-dag detection *) + mutable wk: W.t_prop; (* end point *) + } + + exception NonNaturalLoop of location + + (* --- Annotation Helpers --- *) + + let fmerge env f = function + | [] -> W.empty + | [x] -> f x + | x::xs -> + let cup = W.merge env.we in + List.fold_left (fun p y -> cup (f y) p) (f x) xs + + let is_selected ~goal { mode ; props } (pid,_) = + let pi = WpPropId.property_of_id pid in + is_active_mode ~mode ~goal pi && + ( not goal || is_selected_props props ~pi pid ) + + let is_selected_callpre { props } (pid,_) = + is_selected_props props pid + + let use_assigns env (a : assigns) w = + match a with + | NoAssignsInfo -> assert false + | AssignsAny ad -> + WpLog.warning ~current:true ~once:true + "Missing assigns clause (assigns 'everything' instead)" ; + W.use_assigns env.we None ad w + | AssignsLocations(ap,ad) -> W.use_assigns env.we (Some ap) ad w + + let prove_assigns env (a : assigns) w = + match a with + | NoAssignsInfo | AssignsAny _ -> w + | AssignsLocations ai -> + if is_selected ~goal:true env ai + then W.add_assigns env.we ai w + else w + + let use_property env (p : WpPropId.pred_info) w = + if is_selected ~goal:false env p then W.add_hyp env.we p w else w + + let prove_property env (p : WpPropId.pred_info) w = + if is_selected ~goal:true env p then W.add_goal env.we p w else w + + (* --- Decomposition of WP Rules --- *) + + let rec wp (env:env) (a:vertex) : W.t_prop = + match Vhash.find env.wp a with + | None -> raise (NonNaturalLoop (Cil.CurrentLoc.get())); + | Some pi -> pi + | exception Not_found -> + (* cut circularities *) + Vhash.add env.wp a None ; + let pi = match a.vertex_start_of with + | None -> successors env a + | Some s -> stmt env a s + in Vhash.replace env.wp a (Some pi) ; pi + + (* Compute a stmt node *) + and stmt env a (s: stmt) : W.t_prop = + let kl = Cil.CurrentLoc.get () in + try + Cil.CurrentLoc.set (Stmt.loc s) ; + let smoking = + is_default_bhv env.mode && env.dead s in + let ca = CfgAnnot.get_code_assertions ~smoking env.mode.kf s in + let pi = + W.label env.we (Some s) (Clabels.stmt s) @@ + List.fold_right (prove_property env) ca.code_verified @@ + List.fold_right (use_property env) ca.code_admitted @@ + control env a s + in + Cil.CurrentLoc.set kl ; pi + with err -> + Cil.CurrentLoc.set kl ; raise err + + (* Branching wrt control-flow *) + and control env a s : W.t_prop = + match a.vertex_control with + | If { cond ; vthen ; velse } -> + let wthen = wp env vthen in + let welse = wp env velse in + W.test env.we s cond wthen welse + | Switch { value ; cases ; default } -> + let wcases = List.map (fun (e,v) -> e,wp env v) cases in + let wdefault = wp env default in + W.switch env.we s value (factorize ~wdefault wcases) wdefault + | Loop _ -> + let m = env.mode in + let smoking = + is_default_bhv m && + WpLog.SmokeTests.get () && + WpLog.SmokeDeadloop.get () in + loop env a s (CfgAnnot.get_loop_contract ~smoking m.kf s) + | Edges -> successors env a + + (* Compute loops *) + and loop env a s (lc : CfgAnnot.loop_contract) : W.t_prop = + List.fold_right (prove_property env) lc.loop_established @@ + List.fold_right (use_assigns env) lc.loop_assigns @@ + W.label env.we None (Clabels.loop_current s) @@ + List.fold_right (use_property env) lc.loop_invariants @@ + List.fold_right (prove_property env) lc.loop_smoke @@ + let q = + List.fold_right (prove_property env) lc.loop_preserved @@ + List.fold_right (prove_assigns env) lc.loop_assigns @@ + W.empty in + ( Vhash.replace env.wp a (Some q) ; successors env a ) + + (* Merge transitions *) + and successors env (a : vertex) = transitions env (env.succ a) + and transitions env (es : G.edge list) = fmerge env (transition env) es + and transition env (_,edge,dst) : W.t_prop = + let p = wp env dst in + match edge.edge_transition with + | Skip -> p + | Return(r,s) -> W.return env.we s r p + | Enter { blocals=[] } | Leave { blocals=[] } -> p + | Enter { blocals=xs } -> W.scope env.we xs SC_Block_in p + | Leave { blocals=xs } -> W.scope env.we xs SC_Block_out p + | Instr (i,s) -> instr env s i p + | Prop _ | Guard _ -> (* soundly ignored *) p + + (* Compute a single instruction *) + and instr env s instr (w : W.t_prop) : W.t_prop = + match instr with + | Skip _ | Code_annot _ -> w + | Set(lv,e,_) -> W.assign env.we s lv e w + | Local_init(x,AssignInit i,_) -> W.init env.we x (Some i) w + | Local_init(x,ConsInit (vf, args, kind), loc) -> + Cil.treat_constructor_as_func + begin fun r fct args _loc -> + match Kf.get_called fct with + | Some kf -> call env s r kf args w + | None -> + WpLog.warning ~once:true "No function for constructor '%s'" + vf.vname ; + let any = WpPropId.mk_stmt_assigns_any_desc s in + W.use_assigns env.we None any (W.merge env.we w env.wk) + end x vf args kind loc + | Call(res,fct,args,_loc) -> + begin + match Kf.get_called fct with + | Some kf -> call env s res kf args w + | None -> + match Dyncall.get ~bhv:env.mode.bhv.b_name s with + | None -> + WpLog.warning ~once:true "Missing 'calls' for %s" + (if Cil.is_default_behavior env.mode.bhv + then "default behavior" + else env.mode.bhv.b_name) ; + let any = WpPropId.mk_stmt_assigns_any_desc s in + W.use_assigns env.we None any (W.merge env.we w env.wk) + | Some(prop,kfs) -> + let id = WpPropId.mk_property prop in + W.call_dynamic env.we s id fct @@ + List.map (fun kf -> kf, call env s res kf args w) kfs + end + | Asm _ -> + let assigns = CfgAnnot.get_stmt_assigns env.mode.kf s in + List.fold_right (use_assigns env) assigns w + + and call env s r kf es wr : W.t_prop = + let smoking = + if is_default_bhv env.mode && + WpLog.SmokeTests.get () && + WpLog.SmokeDeadcall.get () + then Some s else None in + let c = CfgAnnot.get_call_contract ?smoking kf s in + let p_post = List.fold_right (prove_property env) c.contract_smoke wr in + let p_exit = List.fold_right (prove_property env) c.contract_smoke env.wk in + let w_call = W.call env.we s r kf es + ~pre:c.contract_hpre + ~post:c.contract_post + ~pexit:c.contract_exit + ~assigns:c.contract_assigns + ~p_post ~p_exit in + if is_default_bhv env.mode then + let pre = List.filter (is_selected_callpre env) c.contract_cond in + W.call_goal_precond env.we s kf es ~pre w_call + else w_call + + let do_complete_disjoint env w = + if not (is_default_bhv env.mode) then w + else + let kf = env.mode.kf in + let complete = CfgAnnot.get_complete_behaviors kf in + let disjoint = CfgAnnot.get_disjoint_behaviors kf in + List.fold_right (prove_property env) complete @@ + List.fold_right (prove_property env) disjoint w + + let do_global_init env w = + I.process_global_init env.we env.mode.kf @@ + W.scope env.we [] SC_Global w + + let do_preconditions env ~formals (b : CfgAnnot.behavior) w = + let kf = env.mode.kf in + let init = WpStrategy.is_main_init kf in + let side_behaviors = + if init || WpLog.PrecondWeakening.get () then [] + else CfgAnnot.get_preconditions ~goal:false kf in + let requires_init = if init then b.bhv_requires else [] in + (* pre-state *) + W.label env.we None Clabels.pre @@ + (* pre-conditions *) + List.fold_right (use_property env) (default_requires env.mode kf) @@ + List.fold_right (use_property env) b.bhv_assumes @@ + List.fold_right (prove_property env) requires_init @@ + List.fold_right (use_property env) b.bhv_requires @@ + List.fold_right (prove_property env) b.bhv_smokes @@ + List.fold_right (use_property env) side_behaviors @@ + (* frame-in *) + W.scope env.we formals SC_Frame_in w + + + let do_post env ~formals (b : CfgAnnot.behavior) w = + W.label env.we None Clabels.post @@ + W.scope env.we formals SC_Frame_out @@ + List.fold_right (prove_property env) b.bhv_ensures @@ + prove_assigns env b.bhv_post_assigns w + + let do_exit env ~formals (b : CfgAnnot.behavior) w = + W.label env.we None Clabels.exit @@ + W.scope env.we formals SC_Frame_out @@ + List.fold_right (prove_property env) b.bhv_exits @@ + prove_assigns env b.bhv_exit_assigns w + + let do_funbehavior env ~formals ~exits (b:CfgAnnot.behavior) w = + match env.body with + | None -> w + | Some cfg -> + let wpost = do_post env ~formals b w in + Vhash.add env.wp cfg.return_point (Some wpost) ; + env.wk <- if exits then do_exit env ~formals b w else w ; + wp env cfg.entry_point + + (* Putting everything together *) + let compute ~mode ~props = + let kf = mode.kf in + let infos = mode.infos in + let body = CfgInfos.body infos in + let succ = match body with + | None -> (fun _ -> []) + | Some cfg -> Cfg.G.succ_e cfg.graph in + let dead = + if body <> None && + is_default_bhv mode && + WpLog.SmokeTests.get () && + WpLog.SmokeDeadcode.get () + then CfgInfos.smoking infos else (fun _ -> false) in + let env = { + mode ; props ; body ; + succ ; dead ; + we = W.new_env kf ; + wp = Vhash.create 32 ; + wk = W.empty ; + } in + let formals = Kf.get_formals kf in + let exits = not @@ Kf.Set.is_empty @@ CfgInfos.calls infos in + let smoking = WpLog.SmokeTests.get () in + let bhv = CfgAnnot.get_behavior_goals kf ~smoking ~exits mode.bhv in + begin + W.close env.we @@ + do_global_init env @@ + do_preconditions env ~formals bhv @@ + do_complete_disjoint env @@ + do_funbehavior env ~formals ~exits bhv @@ + W.empty + end + +end + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgCalculus.mli b/src/plugins/wp/cfgCalculus.mli new file mode 100644 index 0000000000000000000000000000000000000000..a43c14feae502803d2963d3de10f4f0b5e70eab9 --- /dev/null +++ b/src/plugins/wp/cfgCalculus.mli @@ -0,0 +1,43 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(* -------------------------------------------------------------------------- *) +(* --- WP Calculus Driver from Interpreted Automata --- *) +(* -------------------------------------------------------------------------- *) + +type mode = { + kf : kernel_function ; (* Selected function *) + bhv : funbehavior ; (* Selected behavior *) + infos : CfgInfos.t ; (* Associated infos *) +} + +type props = [ `All | `Names of string list | `PropId of Property.t ] + +module Make(W : Mcfg.S) : +sig + exception NonNaturalLoop of location + val compute : mode:mode -> props:props -> W.t_prop +end + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgDump.ml b/src/plugins/wp/cfgDump.ml index 84a6852f17d1c188032521f0deba065500c7d11e..798d85ac72a8951197527fa8d11c9672bf87f8b1 100644 --- a/src/plugins/wp/cfgDump.ml +++ b/src/plugins/wp/cfgDump.ml @@ -20,283 +20,249 @@ (* *) (**************************************************************************) -(* *) (**************************************************************************) let _dkey = "cfgdump" (* debugging key *) -module VC = -struct - - let fc = ref None - let out = ref Format.std_formatter - let knode = ref 0 - let node () = incr knode ; !knode - - let create kf bhv = - begin - let name = - match bhv with - | None -> Kernel_function.get_name kf - | Some bname -> Kernel_function.get_name kf ^ "_" ^ bname - in - let file = Filename.concat (Wp_parameters.get_output () :> string) name in - Wp_parameters.feedback "CFG %a -> %s@." Kernel_function.pretty kf name ; - let fout = open_out (file ^ ".dot") in - fc := Some (fout,file) ; - out := Format.formatter_of_out_channel fout ; - Format.fprintf !out "digraph %a {@\n" Kernel_function.pretty kf ; - Format.fprintf !out " rankdir = TB ;@\n" ; - Format.fprintf !out " node [ style = filled, shape = box ] ;@\n" ; - Format.fprintf !out " N000 [ color = red, shape = circle, label = \"*\" ] ;@\n" ; - end - - let flush () = - begin - Format.fprintf !out "}@." ; - out := Format.std_formatter ; - match !fc with - | None -> () - | Some (fout,file) -> - close_out fout ; - ignore (Sys.command - (Printf.sprintf "dot -Tpdf %s.dot > %s.pdf" file file)) - end - - (* -------------------------------------------------------------------------- *) - (* --- MCFG Interface --- *) - (* -------------------------------------------------------------------------- *) - - type t_prop = int (* current node *) - - let pretty fmt k = Format.fprintf fmt "N%03d" k - - let link a b = - if b =0 - then Format.fprintf !out " %a -> %a [ style=dotted ];@." pretty a pretty b - else Format.fprintf !out " %a -> %a ;@." pretty a pretty b - - let merge _env k1 k2 = - if k1=0 then k2 else - if k2=0 then k1 else - let u = node () in - Format.fprintf !out " %a [ label=\"\" , shape=circle ] ;@." pretty u ; - link u k1 ; link u k2 ; u - - let empty = 0 - - let has_init _ = false - - type t_env = Kernel_function.t - - let new_env ?lvars kf : t_env = ignore lvars ; kf - - let add_axiom _p _l = () - - let add_hyp _env (pid,pred) k = - let u = node () in - if Wp_parameters.debug_atleast 1 then - Format.fprintf !out " %a [ color=green , label=\"Assume %a\" ] ;@." pretty u Printer.pp_predicate pred - else - Format.fprintf !out " %a [ color=green , label=\"Assume %a\" ] ;@." pretty u WpPropId.pp_propid pid ; - link u k ; u - - let add_goal env (pid,pred) k = - let u = node () in - if Wp_parameters.debug_atleast 1 then - Format.fprintf !out " %a [ color=red , label=\"Prove %a\" ] ;@." pretty u Printer.pp_predicate pred - else - Format.fprintf !out " %a [ color=red , label=\"Prove %a\" ] ;@." pretty u WpPropId.pp_propid pid ; - Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; - merge env u k - - let pp_assigns fmt = function - | Cil_types.WritesAny -> Format.pp_print_string fmt " \\everything" - | Cil_types.Writes [] -> Format.pp_print_string fmt " \\nothing" - | Cil_types.Writes froms -> - List.iter - (fun (t,_) -> Format.fprintf fmt "@ %a" Printer.pp_identified_term t) - froms - - let add_assigns env (pid,_) k = - let u = node () in - Format.fprintf !out " %a [ color=red , label=\"Assigns %a\" ] ;@." pretty u WpPropId.pp_propid pid ; - Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; - merge env u k - - let use_assigns _env _stmt region d k = - let u = node () in - begin match region with - | None -> - Format.fprintf !out " %a [ color=orange , label=\"Havoc All\" ] ;@." pretty u - | Some pid -> - Format.fprintf !out " %a [ color=orange , label=\"Havoc %a:\n@[<hov 2>assigns%a;@]\" ] ;@." - pretty u WpPropId.pp_propid pid - pp_assigns d.WpPropId.a_assigns - end ; - link u k ; u - - let label _env stmt label k = - if Clabels.is_here label then k else - let u = node () in - ( match stmt with - | None -> - Format.fprintf !out " %a [ label=\"Label %a\" ] ;@." pretty u Clabels.pretty label - | Some s -> - Format.fprintf !out " %a [ label=\"Label %a (Stmt s%d)\" ] ;@." pretty u Clabels.pretty label s.Cil_types.sid - ) ; - link u k ; u - - let assign _env _stmt x e k = - let u = node () in - Format.fprintf !out " %a [ color=orange , label=\"%a := %a\" ] ;@." pretty u - Printer.pp_lval x Printer.pp_exp e ; - link u k ; u - - let return _env _stmt r k = - let u = node () in - begin - match r with - | None -> - Format.fprintf !out " %a [ color=orange , label=\"Return\" ] ;@." pretty u - | Some e -> - Format.fprintf !out " %a [ color=orange , label=\"Return %a\" ] ;@." pretty u - Printer.pp_exp e - end ; - link u k ; u - - let test _env _stmt e k1 k2 = - let u = node () in - Format.fprintf !out " %a [ color=cyan , label=\"If %a\" ] ;@." pretty u Printer.pp_exp e ; - link u k1 ; link u k2 ; u - - let switch _env _stmt e cases def = - let u = node () in - Format.fprintf !out " %a [ color=cyan , label=\"Switch %a\" ] ;@." pretty u Printer.pp_exp e ; - List.iter (fun (_,k) -> link u k) cases ; - link u def ; u - - let const _ x k = - let u = node () in - Format.fprintf !out " %a [ color=orange, label=\"const %a\" ] ;@." - pretty u Printer.pp_lval (Cil.var x) ; - link u k ; u - - let init _ x init k = - let u = node () in - let pp_init fmt = function - | None -> Format.pp_print_string fmt "<default>" - | Some init -> Printer.pp_init fmt init +let fc = ref None +let out = ref Format.std_formatter +let knode = ref 0 +let node () = incr knode ; !knode + +let fopen kf bhv = + begin + let name = + match bhv with + | None -> Kernel_function.get_name kf + | Some bname -> Kernel_function.get_name kf ^ "_" ^ bname in - Format.fprintf !out " %a [ color=orange, label=\"init %a := %a\" ] ;@." - pretty u Printer.pp_lval (Cil.var x) pp_init init ; - link u k ; u - - let tag s k = - let u = node () in - Format.fprintf !out " %a [ color=cyan , label=\"Tag %s\" ] ;@." pretty u s ; - link u k ; u - - let loop_entry w = tag "BeforeLoop" w - let loop_step w = tag "InLoop" w - - let call_dynamic _env _stmt _pid fct calls = - let u = node () in - Format.fprintf !out " %a [ color=red , label \"CallPtr %a\" ];@." pretty u - Printer.pp_exp fct ; - List.iter (fun (_,k) -> link u k) calls ; u - - let call_goal_precond env _stmt kf _es ~pre k = - let u = node () in - Format.fprintf !out " %a [ color=red , label=\"Prove PreCond %a%t\" ] ;@." - pretty u Kernel_function.pretty kf - begin fun fmt -> - if Wp_parameters.debug_atleast 1 then - List.iter - (fun (_,p) -> Format.fprintf fmt "\n@[<hov 2>Requires %a ;@]" - Printer.pp_predicate p) pre - end ; - ignore pre ; - Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; - merge env u k - - let call env stmt _r kf _es ~pre ~post ~pexit ~assigns ~p_post ~p_exit = - let u_post = List.fold_right (add_hyp env) post p_post in - let u_exit = List.fold_right (add_hyp env) pexit p_exit in + let file = Filename.concat (Wp_parameters.get_output () :> string) name in + Wp_parameters.feedback "CFG %a -> %s@." Kernel_function.pretty kf name ; + let fout = open_out (file ^ ".dot") in + fc := Some (fout,file) ; + out := Format.formatter_of_out_channel fout ; + Format.fprintf !out "digraph %a {@\n" Kernel_function.pretty kf ; + Format.fprintf !out " rankdir = TB ;@\n" ; + Format.fprintf !out " node [ style = filled, shape = box ] ;@\n" ; + Format.fprintf !out " N000 [ color = red, shape = circle, label = \"*\" ] ;@\n" ; + end + +let flush () = + begin + Format.fprintf !out "}@." ; + out := Format.std_formatter ; + match !fc with + | None -> () + | Some (fout,file) -> + close_out fout ; + ignore (Sys.command + (Printf.sprintf "dot -Tpdf %s.dot > %s.pdf" file file)) + end + +(* -------------------------------------------------------------------------- *) +(* --- MCFG Interface --- *) +(* -------------------------------------------------------------------------- *) + +type t_prop = int (* current node *) + +let pretty fmt k = Format.fprintf fmt "N%03d" k + +let link a b = + if b =0 + then Format.fprintf !out " %a -> %a [ style=dotted ];@." pretty a pretty b + else Format.fprintf !out " %a -> %a ;@." pretty a pretty b + +let merge _env k1 k2 = + if k1=0 then k2 else + if k2=0 then k1 else let u = node () in - link u u_post ; link u u_exit ; - Format.fprintf !out - " %a [ color=orange , label=\"Call %a @[<hov 2>(assigns%a)@]\" ] ;@." - pretty u Kernel_function.pretty kf pp_assigns assigns ; - ignore stmt ; - List.fold_right (add_hyp env) pre u - - let pp_scope sc fmt xs = - let title = match sc with - | Mcfg.SC_Global -> "Global" - | Mcfg.SC_Frame_in -> "F-in" - | Mcfg.SC_Frame_out -> "F-out" - | Mcfg.SC_Block_in -> "B-in" - | Mcfg.SC_Block_out -> "B-out" - in begin - Format.fprintf fmt "%s {" title ; - List.iter (fun x -> Format.fprintf fmt " %a" Printer.pp_varinfo x) xs ; - Format.fprintf fmt " }" ; - end - - let scope _kfenv xs scope k = - let u = node () in - Format.fprintf !out " %a [ color=lightblue , label=\"%a\" ] ;@." pretty u - (pp_scope scope) xs ; - link u k ; u + Format.fprintf !out " %a [ label=\"\" , shape=circle ] ;@." pretty u ; + link u k1 ; link u k2 ; u - let close kfenv k = +let empty = 0 + +let has_init _ = false + +type t_env = Kernel_function.t + +let new_env ?lvars kf : t_env = ignore lvars ; kf + +let add_axiom _p _l = () + +let add_hyp _env (pid,pred) k = + let u = node () in + if Wp_parameters.debug_atleast 1 then + Format.fprintf !out " %a [ color=green , label=\"Assume %a\" ] ;@." pretty u Printer.pp_predicate pred + else + Format.fprintf !out " %a [ color=green , label=\"Assume %a\" ] ;@." pretty u WpPropId.pp_propid pid ; + link u k ; u + +let add_goal env (pid,pred) k = + let u = node () in + if Wp_parameters.debug_atleast 1 then + Format.fprintf !out " %a [ color=red , label=\"Prove %a\" ] ;@." pretty u Printer.pp_predicate pred + else + Format.fprintf !out " %a [ color=red , label=\"Prove %a\" ] ;@." pretty u WpPropId.pp_propid pid ; + Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; + merge env u k + +let pp_assigns fmt = function + | Cil_types.WritesAny -> Format.pp_print_string fmt " \\everything" + | Cil_types.Writes [] -> Format.pp_print_string fmt " \\nothing" + | Cil_types.Writes froms -> + List.iter + (fun (t,_) -> Format.fprintf fmt "@ %a" Printer.pp_identified_term t) + froms + +let add_assigns env (pid,_) k = + let u = node () in + Format.fprintf !out " %a [ color=red , label=\"Assigns %a\" ] ;@." pretty u WpPropId.pp_propid pid ; + Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; + merge env u k + +let use_assigns _env region d k = + let u = node () in + begin match region with + | None -> + Format.fprintf !out " %a [ color=orange , label=\"Havoc All\" ] ;@." pretty u + | Some pid -> + Format.fprintf !out " %a [ color=orange , label=\"Havoc %a:\n@[<hov 2>assigns%a;@]\" ] ;@." + pretty u WpPropId.pp_propid pid + pp_assigns d.WpPropId.a_assigns + end ; + link u k ; u + +let label _env stmt label k = + if Clabels.is_here label then k else let u = node () in - Format.fprintf !out " %a [ color=cyan , label=\"Function %a\" ] ;@." pretty u - Kernel_function.pretty kfenv ; + ( match stmt with + | None -> + Format.fprintf !out " %a [ label=\"Label %a\" ] ;@." pretty u Clabels.pretty label + | Some s -> + Format.fprintf !out " %a [ label=\"Label %a (Stmt s%d)\" ] ;@." pretty u Clabels.pretty label s.Cil_types.sid + ) ; link u k ; u - let build_prop_of_from _env _ps _k = 0 - -end - -module WP = Calculus.Cfg(VC) - -(* ------------------------------------------------------------------------ *) -(* --- Proof Obligation Generation --- *) -(* ------------------------------------------------------------------------ *) - -class computer () = - let driver = Driver.load_driver () in - let model = Factory.(instance default driver) in - object - val mutable wptasks = [] - - method model = model - method lemma = true - method add_lemma (_ : LogicUsage.logic_lemma) = () - - method add_strategy strategy = - wptasks <- strategy :: wptasks - - method compute : Wpo.t Bag.t = - begin - - (* Generates Wpos and accumulate exported goals *) +let assign _env _stmt x e k = + let u = node () in + Format.fprintf !out " %a [ color=orange , label=\"%a := %a\" ] ;@." pretty u + Printer.pp_lval x Printer.pp_exp e ; + link u k ; u + +let return _env _stmt r k = + let u = node () in + begin + match r with + | None -> + Format.fprintf !out " %a [ color=orange , label=\"Return\" ] ;@." pretty u + | Some e -> + Format.fprintf !out " %a [ color=orange , label=\"Return %a\" ] ;@." pretty u + Printer.pp_exp e + end ; + link u k ; u + +let test _env _stmt e k1 k2 = + let u = node () in + Format.fprintf !out " %a [ color=cyan , label=\"If %a\" ] ;@." + pretty u Printer.pp_exp e ; + link u k1 ; link u k2 ; u + +let switch _env _stmt e cases def = + let u = node () in + Format.fprintf !out " %a [ color=cyan , label=\"Switch %a\" ] ;@." + pretty u Printer.pp_exp e ; + List.iter (fun (es,k) -> + let c = node () in + Format.fprintf !out " %a [ color=orange , label=\"Case %a\" ] ;@." + pretty c (Pretty_utils.pp_list ~sep:"," Printer.pp_exp) es ; + link u c ; + link c k ; + ) cases ; + let d = node () in + Format.fprintf !out " %a [ color=orange , label=\"Default\" ] ;@." pretty d ; + link u d ; + link d def ; u + +let const _ x k = + let u = node () in + Format.fprintf !out " %a [ color=orange, label=\"const %a\" ] ;@." + pretty u Printer.pp_lval (Cil.var x) ; + link u k ; u + +let init _ x init k = + let u = node () in + let pp_init fmt = function + | None -> Format.pp_print_string fmt "<default>" + | Some init -> Printer.pp_init fmt init + in + Format.fprintf !out " %a [ color=orange, label=\"init %a := %a\" ] ;@." + pretty u Printer.pp_lval (Cil.var x) pp_init init ; + link u k ; u + +let tag s k = + let u = node () in + Format.fprintf !out " %a [ color=cyan , label=\"Tag %s\" ] ;@." pretty u s ; + link u k ; u + +let loop_entry w = tag "BeforeLoop" w +let loop_step w = tag "InLoop" w + +let call_dynamic _env _stmt _pid fct calls = + let u = node () in + Format.fprintf !out " %a [ color=red , label=\"CallPtr %a\" ];@." pretty u + Printer.pp_exp fct ; + List.iter (fun (_,k) -> link u k) calls ; u + +let call_goal_precond env _stmt kf _es ~pre k = + let u = node () in + Format.fprintf !out " %a [ color=red , label=\"Prove PreCond %a%t\" ] ;@." + pretty u Kernel_function.pretty kf + begin fun fmt -> + if Wp_parameters.debug_atleast 1 then List.iter - (fun strategy -> - let cfg = WpStrategy.cfg_of_strategy strategy in - let kf = Cil2cfg.cfg_kf cfg in - let bhv = WpStrategy.behavior_name_of_strategy strategy in - VC.create kf bhv ; - try ignore (WP.compute cfg strategy) ; VC.flush () - with err -> VC.flush () ; raise err - ) wptasks ; - wptasks <- [] ; - Bag.empty - - end (* method compute *) - - end (* class computer *) - -let create () = (new computer () :> Generator.computer) + (fun (_,p) -> Format.fprintf fmt "\n@[<hov 2>Requires %a ;@]" + Printer.pp_predicate p) pre + end ; + ignore pre ; + Format.fprintf !out " %a -> %a [ style=dotted ] ;@." pretty u pretty k ; + merge env u k + +let call env stmt _r kf _es ~pre ~post ~pexit ~assigns ~p_post ~p_exit = + let u_post = List.fold_right (add_hyp env) post p_post in + let u_exit = List.fold_right (add_hyp env) pexit p_exit in + let u = node () in + link u u_post ; link u u_exit ; + Format.fprintf !out + " %a [ color=orange , label=\"Call %a @[<hov 2>(assigns%a)@]\" ] ;@." + pretty u Kernel_function.pretty kf pp_assigns assigns ; + ignore stmt ; + List.fold_right (add_hyp env) pre u + +let pp_scope sc fmt xs = + let title = match sc with + | Mcfg.SC_Global -> "Global" + | Mcfg.SC_Frame_in -> "F-in" + | Mcfg.SC_Frame_out -> "F-out" + | Mcfg.SC_Block_in -> "B-in" + | Mcfg.SC_Block_out -> "B-out" + in begin + Format.fprintf fmt "%s {" title ; + List.iter (fun x -> Format.fprintf fmt " %a" Printer.pp_varinfo x) xs ; + Format.fprintf fmt " }" ; + end + +let scope _kfenv xs scope k = + let u = node () in + Format.fprintf !out " %a [ color=lightblue , label=\"%a\" ] ;@." pretty u + (pp_scope scope) xs ; + link u k ; u + +let close kfenv k = + let u = node () in + Format.fprintf !out " %a [ color=cyan , label=\"Function %a\" ] ;@." pretty u + Kernel_function.pretty kfenv ; + link u k ; u + +let build_prop_of_from _env _ps _k = 0 + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgDump.mli b/src/plugins/wp/cfgDump.mli index 8a56875a65392d30461eaf1f32eef4fc8ef5aa7d..48722048bdd848f86a31476656f5b8b9b42299f2 100644 --- a/src/plugins/wp/cfgDump.mli +++ b/src/plugins/wp/cfgDump.mli @@ -20,6 +20,13 @@ (* *) (**************************************************************************) -(* Produce a CfgProof.computer that dumps a graph of generated PO *) +(* Dump calls to Mcfg into DOT graphs *) -val create : unit -> Generator.computer +open Cil_types + +include Mcfg.S + +val fopen : kernel_function -> string option -> unit +val flush : unit -> unit + +(**************************************************************************) diff --git a/src/plugins/wp/cfgGenerator.ml b/src/plugins/wp/cfgGenerator.ml new file mode 100644 index 0000000000000000000000000000000000000000..45e5b9c4b347cce18bf5e4e5264fbefcc05872ff --- /dev/null +++ b/src/plugins/wp/cfgGenerator.ml @@ -0,0 +1,330 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types +open Wp_parameters + +(* -------------------------------------------------------------------------- *) +(* --- Task Manager --- *) +(* -------------------------------------------------------------------------- *) + +module KFmap = Kernel_function.Map + +type pool = { + mutable modes: CfgCalculus.mode list KFmap.t ; + mutable props: CfgCalculus.props ; + mutable lemmas: LogicUsage.logic_lemma list ; +} + +let empty () = { + lemmas = []; + modes = KFmap.empty; + props = `All; +} + +let get_kf_infos model kf ?bhv ?prop () = + let missing = WpRTE.missing_guards model kf in + if missing && Wp_parameters.RTE.get () then + WpRTE.generate model kf ; + let smoking = + Wp_parameters.SmokeTests.get () && + Wp_parameters.SmokeDeadcode.get () in + let infos = CfgInfos.get kf ~smoking ?bhv ?prop () in + (*TODO: print warning first *) + if missing then + Wp_parameters.warning ~current:false ~once:true "Missing RTE guards" ; + infos + +(* -------------------------------------------------------------------------- *) +(* --- Behavior Selection --- *) +(* -------------------------------------------------------------------------- *) + +let empty_default_behavior : funbehavior = { + b_name = Cil.default_behavior_name ; + b_requires = [] ; + b_assumes = [] ; + b_post_cond = [] ; + b_assigns = WritesAny ; + b_allocation = FreeAllocAny ; + b_extended = [] ; +} + +let default kf = + match Annotations.behaviors kf with + | [] -> [empty_default_behavior] + | bhvs -> List.filter Cil.is_default_behavior bhvs + +let select kf bnames = + match Annotations.behaviors kf with + | [] -> if bnames = [] then [empty_default_behavior] else [] + | bhvs -> if bnames = [] then bhvs else + List.filter + (fun b -> List.mem b.b_name bnames) + bhvs + +(* -------------------------------------------------------------------------- *) +(* --- Elementary Tasks --- *) +(* -------------------------------------------------------------------------- *) + +let add_lemma_task pool ?(prop=[]) (l : LogicUsage.logic_lemma) = + if Logic_utils.verify_predicate l.lem_predicate.tp_kind && + (prop=[] || WpPropId.select_by_name prop (WpPropId.mk_lemma_id l)) + then pool.lemmas <- l :: pool.lemmas + +let add_fun_task model pool ~kf ?infos ?bhvs ?target () = + let infos = match infos with + | Some infos -> infos + | None -> get_kf_infos model kf () in + let bhvs = match bhvs with + | Some bhvs -> bhvs + | None -> + let bhvs = Annotations.behaviors kf in + if List.exists (Cil.is_default_behavior) bhvs then bhvs + else empty_default_behavior :: bhvs in + let add_mode kf m = + let ms = try KFmap.find kf pool.modes with Not_found -> [] in + pool.modes <- KFmap.add kf (m :: ms) pool.modes in + begin + List.iter (fun bhv -> add_mode kf CfgCalculus.{ infos ; kf ; bhv }) bhvs ; + Option.iter (fun ip -> pool.props <- `PropId ip) target ; + end + +let notyet prop = + Wp_parameters.warning ~once:true + "Not yet implemented wp for '%a'" Property.pretty prop + +(* -------------------------------------------------------------------------- *) +(* --- Property Tasks --- *) +(* -------------------------------------------------------------------------- *) + +let rec strategy_ip model pool target = + let open Property in + match target with + | IPLemma { il_name } -> + add_lemma_task pool (LogicUsage.logic_lemma il_name) + | IPAxiomatic { iax_props } -> + List.iter (strategy_ip model pool) iax_props + | IPBehavior { ib_kf = kf ; ib_bhv = bhv } -> + add_fun_task model pool ~kf ~bhvs:[bhv] () + | IPPredicate { ip_kf = kf ; ip_kind ; ip_kinstr = ki } -> + begin match ip_kind with + | PKAssumes _ -> () + | PKRequires bhv -> + begin + match ki with + | Kglobal -> (*TODO*) notyet target + | Kstmt _ -> add_fun_task model pool ~kf ~bhvs:[bhv] ~target () + end + | PKEnsures(bhv,_) -> + add_fun_task model pool ~kf ~bhvs:[bhv] ~target () + | PKTerminates -> + add_fun_task model pool ~kf ~bhvs:(default kf) ~target () + end + | IPDecrease { id_kf = kf } -> + add_fun_task model pool ~kf ~bhvs:(default kf) ~target () + | IPAssigns { ias_kf=kf ; ias_bhv=Id_loop ca } + | IPAllocation { ial_kf=kf ; ial_bhv=Id_loop ca } -> + let bhvs = match ca.annot_content with + | AAssigns(bhvs,_) | AAllocation(bhvs,_) -> bhvs + | _ -> [] in + add_fun_task model pool ~kf ~bhvs:(select kf bhvs) ~target () + | IPAssigns { ias_kf=kf ; ias_bhv=Id_contract(_,bhv) } + | IPAllocation { ial_kf=kf ; ial_bhv=Id_contract(_,bhv) } + -> add_fun_task model pool ~kf ~bhvs:[bhv] ~target () + | IPCodeAnnot { ica_kf = kf ; ica_ca = ca } -> + begin match ca.annot_content with + | AExtended _ | APragma _ -> () + | AStmtSpec(fors,_) -> + (*TODO*) notyet target ; + add_fun_task model pool ~kf ~bhvs:(select kf fors) () + | AVariant _ -> + add_fun_task model pool ~kf ~target () + | AAssert(fors, _) + | AInvariant(fors, _, _) + | AAssigns(fors, _) + | AAllocation(fors, _) -> + add_fun_task model pool ~kf ~bhvs:(select kf fors) ~target () + end + | IPComplete _ -> (*TODO*) notyet target + | IPDisjoint _ -> (*TODO*) notyet target + | IPFrom _ | IPReachable _ | IPTypeInvariant _ | IPGlobalInvariant _ + | IPPropertyInstance _ -> notyet target (* ? *) + | IPExtended _ | IPOther _ -> () + +(* -------------------------------------------------------------------------- *) +(* --- Function Strategy Tasks --- *) +(* -------------------------------------------------------------------------- *) + +let strategy_main model pool ?(fct=Fct_all) ?(bhv=[]) ?(prop=[]) () = + begin + if fct = Fct_all && bhv = [] then + LogicUsage.iter_lemmas (add_lemma_task pool ~prop) ; + Wp_parameters.iter_fct + (fun kf -> + let infos = get_kf_infos model kf ~bhv ~prop () in + if CfgInfos.annots infos then + if bhv=[] + then add_fun_task model pool ~infos ~kf () + else add_fun_task model pool ~infos ~kf ~bhvs:(select kf bhv) () + ) fct ; + pool.props <- (if prop=[] then `All else `Names prop); + end + +(* -------------------------------------------------------------------------- *) +(* --- Compute All Tasks --- *) +(* -------------------------------------------------------------------------- *) + +module Make(VCG : CfgWP.VCgen) = +struct + + module WP = CfgCalculus.Make(VCG) + + let compute model pool = + begin + let collection = ref Bag.empty in + Lang.F.release () ; + if pool.lemmas <> [] then + WpContext.on_context (model,WpContext.Global) + begin fun () -> + List.iter + (fun (l : LogicUsage.logic_lemma) -> + if Logic_utils.verify_predicate l.lem_predicate.tp_kind then + let wpo = VCG.compile_lemma l in + collection := Bag.add wpo !collection + ) pool.lemmas ; + end () ; + KFmap.iter + (fun kf modes -> + List.iter + begin fun (mode: CfgCalculus.mode) -> + WpContext.on_context (model,WpContext.Kf mode.kf) + begin fun () -> + LogicUsage.iter_lemmas + (fun (l : LogicUsage.logic_lemma) -> + if Logic_utils.use_predicate l.lem_predicate.tp_kind + then VCG.register_lemma l) ; + let bhv = + if Cil.is_default_behavior mode.bhv then None + else Some mode.bhv.b_name in + let index = Wpo.Function(mode.kf,bhv) in + let props = pool.props in + try + let wp = WP.compute ~mode ~props in + let wcs = VCG.compile_wp index wp in + collection := Bag.concat !collection wcs + with WP.NonNaturalLoop loc -> + Wp_parameters.error + ~source:(fst loc) + "Non natural loop detected.@\n\ + WP for function '%a' aborted." + Kernel_function.pretty kf + end () + end + (List.rev modes) + ) pool.modes ; + CfgAnnot.clear () ; + CfgInfos.clear () ; + !collection + end + + let compute_ip model ip = + let pool = empty () in + strategy_ip model pool ip ; + compute model pool + + let compute_call _model _stmt = + Wp_parameters.warning + ~once:true "Not yet implemented call preconds" ; + Bag.empty + + let compute_main model ?fct ?bhv ?prop () = + let pool = empty () in + strategy_main model pool ?fct ?bhv ?prop () ; + compute model pool + +end + +(* -------------------------------------------------------------------------- *) +(* --- New WP Computer (main entry points) --- *) +(* -------------------------------------------------------------------------- *) + +let generators = WpContext.MINDEX.create 1 + +let generator setup driver = + let model = Factory.instance setup driver in + try WpContext.MINDEX.find generators model + with Not_found -> + let module VCG = (val CfgWP.vcgen setup driver) in + let module CC = Make(VCG) in + let generator : Wpo.generator = + object + method model = model + method compute_ip = CC.compute_ip model + method compute_call = CC.compute_call model + method compute_main = CC.compute_main model + end in + WpContext.MINDEX.add generators model generator ; + generator + +(* -------------------------------------------------------------------------- *) +(* --- Dumper --- *) +(* -------------------------------------------------------------------------- *) + +let dump pool = + let module WP = CfgCalculus.Make(CfgDump) in + let props = pool.props in + KFmap.iter + (fun _ modes -> + List.iter + begin fun (mode : CfgCalculus.mode) -> + let bhv = + if Cil.is_default_behavior mode.bhv + then None else Some mode.bhv.b_name in + try + CfgDump.fopen mode.kf bhv ; + ignore (WP.compute ~mode ~props) ; + CfgDump.flush () ; + with err -> + CfgDump.flush () ; + raise err + end + (List.rev modes) + ) pool.modes + +let dumper setup driver = + let model = Factory.instance setup driver in + let generator : Wpo.generator = + object + method model = model + method compute_ip ip = + let pool = empty () in + strategy_ip model pool ip ; + dump pool ; Bag.empty + method compute_call _ = Bag.empty + method compute_main ?fct ?bhv ?prop () = + let pool = empty () in + strategy_main model pool ?fct ?bhv ?prop () ; + dump pool ; Bag.empty + end + in generator + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgGenerator.mli b/src/plugins/wp/cfgGenerator.mli new file mode 100644 index 0000000000000000000000000000000000000000..ec7f9f9480c0ddfbb449c9559737489179e3b849 --- /dev/null +++ b/src/plugins/wp/cfgGenerator.mli @@ -0,0 +1,30 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- New WP Computer (main entry points) --- *) +(* -------------------------------------------------------------------------- *) + +val dumper : Factory.setup -> Factory.driver -> Wpo.generator +val generator : Factory.setup -> Factory.driver -> Wpo.generator + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgInfos.ml b/src/plugins/wp/cfgInfos.ml new file mode 100644 index 0000000000000000000000000000000000000000..a6e1ad306a00670f622a394c263f237a70d1a515 --- /dev/null +++ b/src/plugins/wp/cfgInfos.ml @@ -0,0 +1,318 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +module Cfg = Interpreted_automata +module Fset = Kernel_function.Set +module Shash = Cil_datatype.Stmt.Hashtbl + +(* -------------------------------------------------------------------------- *) +(* --- Compute Kernel-Function & CFG Infos for WP --- *) +(* -------------------------------------------------------------------------- *) + +module CheckPath = Graph.Path.Check(Cfg.G) + +type t = { + body : Cfg.automaton option ; + checkpath : CheckPath.path_checker option ; + reachability : WpReached.reachability option ; + mutable annots : bool; (* has goals to prove *) + mutable doomed : WpPropId.prop_id Bag.t; + mutable calls : Kernel_function.Set.t; +} + +(* -------------------------------------------------------------------------- *) +(* --- Getters --- *) +(* -------------------------------------------------------------------------- *) + +let body infos = infos.body +let calls infos = infos.calls +let annots infos = infos.annots +let doomed infos = infos.doomed + +let wpreached s = function + | None -> false + | Some reachability -> WpReached.smoking reachability s +let smoking infos s = wpreached s infos.reachability + +let unreachable infos v = + match infos.body, infos.checkpath with + | Some cfg , Some checkpath -> + not @@ CheckPath.check_path checkpath cfg.entry_point v + | _ -> true + +(* -------------------------------------------------------------------------- *) +(* --- Selected Properties --- *) +(* -------------------------------------------------------------------------- *) + +let selected ~bhv ~prop pid = + (prop = [] || WpPropId.select_by_name prop pid) && + (bhv = [] || WpPropId.select_for_behaviors bhv pid) + +let selected_default ~bhv = + bhv=[] || List.mem Cil.default_behavior_name bhv + +let selected_name ~prop name = + prop=[] || WpPropId.are_selected_names prop [name] + +let selected_assigns ~prop = function + | Cil_types.WritesAny -> false + | Writes _ when prop = [] -> true + | Writes l -> + let collect_names l (t, _) = + WpPropId.ident_names t.Cil_types.it_content.term_name @ l + in + let names = List.fold_left collect_names ["@assigns"] l in + WpPropId.are_selected_names prop names + +let selected_allocates ~prop = function + | Cil_types.FreeAllocAny -> false + | _ -> (selected_name ~prop "@allocates" || selected_name ~prop "@frees") + +let selected_precond ~prop ip = + prop = [] || + let tk_name = "@requires" in + let tp_names = WpPropId.user_pred_names ip.Cil_types.ip_content in + WpPropId.are_selected_names prop (tk_name :: tp_names) + +let selected_postcond ~prop (tk,ip) = + prop = [] || + let tk_name = "@" ^ WpPropId.string_of_termination_kind tk in + let tp_names = WpPropId.user_pred_names ip.Cil_types.ip_content in + WpPropId.are_selected_names prop (tk_name :: tp_names) + +let selected_requires ~prop (b : Cil_types.funbehavior) = + List.exists (selected_precond ~prop) b.b_requires + +let selected_call ~bhv ~prop kf = + bhv = [] && List.exists (selected_requires ~prop) (Annotations.behaviors kf) + +let selected_clause ~prop name getter kf = + getter kf <> [] && selected_name ~prop name + +let selected_disjoint_complete kf ~bhv ~prop = + selected_default ~bhv && + ( selected_clause ~prop "@complete_behaviors" Annotations.complete kf || + selected_clause ~prop "@disjoint_behaviors" Annotations.disjoint kf ) + +let selected_bhv ~smoking ~bhv ~prop (b : Cil_types.funbehavior) = + (bhv = [] || List.mem b.b_name bhv) && + begin + (selected_assigns ~prop b.b_assigns) || + (selected_allocates ~prop b.b_allocation) || + (smoking && b.b_requires <> []) || + (List.exists (selected_postcond ~prop) b.b_post_cond) + end + +let selected_main_bhv ~bhv ~prop (b : Cil_types.funbehavior) = + (bhv = [] || List.mem b.b_name bhv) && (selected_requires ~prop b) + +(* -------------------------------------------------------------------------- *) +(* --- Calls --- *) +(* -------------------------------------------------------------------------- *) + +let collect_calls ~bhv kf stmt = + let open Cil_types in + match stmt.skind with + | Instr(Call(_,fct,_,_)) -> + begin + match Kernel_function.get_called fct with + | Some kf -> Fset.singleton kf + | None -> + let bhvs = + if bhv = [] + then List.map (fun b -> b.b_name) (Annotations.behaviors kf) + else bhv in + List.fold_left + (fun fs bhv -> match Dyncall.get ~bhv stmt with + | None -> fs + | Some(_,kfs) -> List.fold_right Fset.add kfs fs + ) Fset.empty bhvs + end + | Instr(Local_init(x,ConsInit(vf, args, kind), loc)) -> + Cil.treat_constructor_as_func + (fun _r fct _args _loc -> + match Kernel_function.get_called fct with + | Some kf -> Fset.singleton kf + | None -> Fset.empty) + x vf args kind loc + | _ -> Fset.empty + +(* -------------------------------------------------------------------------- *) +(* --- Memoization Key --- *) +(* -------------------------------------------------------------------------- *) + +module Key = +struct + type t = { + kf: Kernel_function.t ; + smoking: bool ; + bhv : string list ; + prop : string list ; + } + + let compare a b = + let cmp = Kernel_function.compare a.kf b.kf in + if cmp <> 0 then cmp else + let cmp = Stdlib.compare a.smoking b.smoking in + if cmp <> 0 then cmp else + let cmp = Stdlib.compare a.bhv b.bhv in + if cmp <> 0 then cmp else + Stdlib.compare a.prop b.prop + + let pp_filter kind fmt xs = + match xs with + | [] -> () + | x::xs -> + Format.fprintf fmt "~%s:%s" kind x ; + List.iter (Format.fprintf fmt ",%s") xs + + let pretty fmt k = + begin + Kernel_function.pretty fmt k.kf ; + pp_filter "smoking" fmt (if k.smoking then ["true"] else []) ; + pp_filter "bhv" fmt k.bhv ; + pp_filter "prop" fmt k.prop ; + end + +end + +(* -------------------------------------------------------------------------- *) +(* --- Main Collection Pass --- *) +(* -------------------------------------------------------------------------- *) + +let dead_posts ~bhv ~prop tk (bhvs : CfgAnnot.behavior list) = + let post ~bhv ~prop tk (b: CfgAnnot.behavior) = + let assigns, ps = match tk with + | Cil_types.Exits -> b.bhv_exit_assigns, b.bhv_exits + | _ -> b.bhv_post_assigns, b.bhv_ensures in + let ps = List.filter (selected ~prop ~bhv) @@ List.map fst ps in + match assigns with + | WpPropId.AssignsLocations(id, _) -> Bag.list (id :: ps) + | _ -> Bag.list ps + in Bag.umap_list (post ~bhv ~prop tk) bhvs + +let loop_contract_pids kf stmt = + match stmt.Cil_types.skind with + | Loop _ -> + let invs = CfgAnnot.get_loop_contract kf stmt in + let add_assigns assigns l = + match assigns with + | WpPropId.NoAssignsInfo | AssignsAny _ -> l + | AssignsLocations (pid, _) -> pid :: l + in + List.fold_right (fun (pid,_) l -> pid :: l) invs.loop_established @@ + List.fold_right (fun (pid,_) l -> pid :: l) invs.loop_preserved @@ + List.fold_right add_assigns invs.loop_assigns [] + | _ -> [] + +let compile Key.{ kf ; smoking ; bhv ; prop } = + let body, checkpath, reachability = + if Kernel_function.has_definition kf then + let cfg = Cfg.get_automaton kf in + Some cfg, + Some (CheckPath.create cfg.graph), + if smoking then Some (WpReached.reachability kf) else None + else None, None, None + in + let infos = { + body ; checkpath ; reachability ; + annots = false ; + doomed = Bag.empty ; + calls = Fset.empty ; + } in + let behaviors = Annotations.behaviors kf in + (* Inits *) + if WpStrategy.is_main_init kf then + infos.annots <- List.exists (selected_main_bhv ~bhv ~prop) behaviors ; + (* Function Body *) + Option.iter + begin fun (cfg : Cfg.automaton) -> + (* Spec Iteration *) + if selected_disjoint_complete kf ~bhv ~prop || + (List.exists (selected_bhv ~smoking ~bhv ~prop) behaviors) + then infos.annots <- true ; + (* Stmt Iteration *) + Shash.iter + (fun stmt (src,_) -> + let fs = collect_calls ~bhv kf stmt in + let dead = unreachable infos src in + let ca = CfgAnnot.get_code_assertions kf stmt in + let ca_pids = List.map fst ca.code_verified in + let loop_pids = loop_contract_pids kf stmt in + if dead then + begin + if wpreached stmt reachability then + (let p = CfgAnnot.get_unreachable kf stmt in + infos.doomed <- Bag.append infos.doomed p) ; + infos.doomed <- Bag.concat infos.doomed (Bag.list ca_pids) ; + infos.doomed <- Bag.concat infos.doomed (Bag.list loop_pids) ; + end + else + begin + if not infos.annots && + ( List.exists (selected ~bhv ~prop) ca_pids || + List.exists (selected ~bhv ~prop) loop_pids || + Fset.exists (selected_call ~bhv ~prop) fs ) + then infos.annots <- true ; + infos.calls <- Fset.union fs infos.calls ; + end + ) cfg.stmt_table ; + (* Dead Post Conditions *) + let dead_exit = Fset.is_empty infos.calls in + let dead_post = unreachable infos cfg.return_point in + let bhvs = + if dead_exit || dead_post then + let exits = not dead_exit in + List.map (CfgAnnot.get_behavior_goals kf ~exits) behaviors + else [] in + if dead_exit then + infos.doomed <- + Bag.concat infos.doomed (dead_posts ~bhv ~prop Exits bhvs) ; + if dead_post then + infos.doomed <- + Bag.concat infos.doomed (dead_posts ~bhv ~prop Normal bhvs) ; + end body ; + (* Doomed *) + Bag.iter + (fun p -> if WpPropId.filter_status p then WpAnnot.set_unreachable p) + infos.doomed ; + (* Collected Infos *) + infos + +(* -------------------------------------------------------------------------- *) +(* --- Memoization Data --- *) +(* -------------------------------------------------------------------------- *) + +module Generator = WpContext.StaticGenerator(Key) + (struct + type key = Key.t + type data = t + let name = "Wp.CfgInfos.Generator" + let compile = compile + end) + +let get kf ?(smoking=false) ?(bhv=[]) ?(prop=[]) () = + Generator.get { kf ; smoking ; bhv ; prop } + +let clear () = Generator.clear () + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgInfos.mli b/src/plugins/wp/cfgInfos.mli new file mode 100644 index 0000000000000000000000000000000000000000..472a321933a16e7853b7fd20727ceb0136b27d0f --- /dev/null +++ b/src/plugins/wp/cfgInfos.mli @@ -0,0 +1,42 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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). *) +(* *) +(**************************************************************************) + +(* Compute Kernel-Function & CFG Infos for WP *) + +type t + +module Cfg = Interpreted_automata + +(** Memoized *) +val get : Kernel_function.t -> + ?smoking:bool -> ?bhv:string list -> ?prop:string list -> + unit -> t +val clear : unit -> unit + +val body : t -> Cfg.automaton option +val annots : t -> bool +val doomed : t -> WpPropId.prop_id Bag.t +val calls : t -> Kernel_function.Set.t +val smoking : t -> Cil_types.stmt -> bool +val unreachable : t -> Cfg.vertex -> bool + +(**************************************************************************) diff --git a/src/plugins/wp/cfgInit.ml b/src/plugins/wp/cfgInit.ml new file mode 100644 index 0000000000000000000000000000000000000000..32168850ea40fec1f7095a53a1e4594d5b9d1881 --- /dev/null +++ b/src/plugins/wp/cfgInit.ml @@ -0,0 +1,72 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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). *) +(* *) +(**************************************************************************) + +(* Compute Init WP *) + +module Make(W : Mcfg.S) = +struct + + let compute_global_init wenv filter obj = + Globals.Vars.fold_in_file_order + (fun var initinfo obj -> + if var.vstorage = Extern then obj else + let do_init = match filter with + | `All -> true + | `InitConst -> WpStrategy.isGlobalInitConst var + in if not do_init then obj + else + let old_loc = Cil.CurrentLoc.get () in + Cil.CurrentLoc.set var.vdecl ; + let obj = W.init wenv var initinfo.init obj in + Cil.CurrentLoc.set old_loc ; obj + ) obj + + let process_global_const wenv obj = + Globals.Vars.fold_in_file_order + (fun var _initinfo obj -> + if WpStrategy.isGlobalInitConst var + then W.const wenv var obj + else obj + ) obj + + (* WP of global initializations. *) + let process_global_init wenv kf obj = + if WpStrategy.is_main_init kf then + begin + let obj = W.label wenv None Clabels.init obj in + compute_global_init wenv `All obj + end + else if W.has_init wenv then + begin + let obj = + if WpStrategy.isInitConst () + then process_global_const wenv obj else obj in + let obj = W.use_assigns wenv None WpPropId.mk_init_assigns obj in + let obj = W.label wenv None Clabels.init obj in + compute_global_init wenv `All obj + end + else + if WpStrategy.isInitConst () + then compute_global_init wenv `InitConst obj + else obj + +end diff --git a/src/plugins/wp/cfgInit.mli b/src/plugins/wp/cfgInit.mli new file mode 100644 index 0000000000000000000000000000000000000000..83d3cf130575ff59e361d37b3347dada1bcd92dc --- /dev/null +++ b/src/plugins/wp/cfgInit.mli @@ -0,0 +1,32 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* alternatives) *) +(* *) +(* you can redistribute it and/or modify it under the terms of the GNU *) +(* Lesser General Public License as published by the Free Software *) +(* Foundation, version 2.1. *) +(* *) +(* It is distributed in the hope that it will be useful, *) +(* but WITHOUT ANY WARRANTY; without even the implied warranty of *) +(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) +(* GNU Lesser General Public License for more details. *) +(* *) +(* See the GNU Lesser General Public License version 2.1 *) +(* for more details (enclosed in the file licenses/LGPLv2.1). *) +(* *) +(**************************************************************************) + +open Cil_types + +(* Compute Init WP *) + +module Make(W : Mcfg.S) : +sig + + val process_global_init : W.t_env -> kernel_function -> W.t_prop -> W.t_prop + +end diff --git a/src/plugins/wp/cfgWP.ml b/src/plugins/wp/cfgWP.ml index e8ed36e6a7e14b00cb30e55b2a6256150df7e35b..baa2652d9193dc016225f83dc1b0ee1533b09509 100644 --- a/src/plugins/wp/cfgWP.ml +++ b/src/plugins/wp/cfgWP.ml @@ -35,7 +35,15 @@ open Lang.F open Sigs open Wpo -module VC( C : Sigs.Compiler ) = +module type VCgen = +sig + include Mcfg.S + val register_lemma : logic_lemma -> unit + val compile_lemma : logic_lemma -> Wpo.t + val compile_wp : Wpo.index -> t_prop -> Wpo.t Bag.t +end + +module VC( C : Sigs.Compiler ) : VCgen = struct open C @@ -598,8 +606,9 @@ struct let sequence = { pre=s0 ; post=s1 } in sequence , assigned - let use_assigns wenv stmt hpid ainfo wp = in_wenv wenv wp + let use_assigns wenv hpid ainfo wp = in_wenv wenv wp begin fun env wp -> + let stmt = ainfo.a_stmt in match ainfo.a_assigns with | WritesAny -> @@ -1344,8 +1353,9 @@ struct else group.verifs <- Bag.concat group.verifs (make_vcqs target tags vc) - let compile collection index (wp : t_prop) = + let compile_wp index (wp : t_prop) = let groups = ref PMAP.empty in + let collection = ref Bag.empty in Gmap.iter_sorted (fun target -> Splitter.iter (group_vc groups target)) wp.vcs ; @@ -1383,121 +1393,56 @@ struct end pid group - end !groups + end !groups ; + !collection + + let register_lemma l = ignore (L.lemma l) - let lemma = L.lemma + let compile_lemma l = + begin + let id = WpPropId.mk_lemma_id l in + let def = L.lemma l in + let model = WpContext.get_model () in + let vca = { + Wpo.VC_Lemma.depends = l.lem_depends ; + Wpo.VC_Lemma.lemma = def ; + Wpo.VC_Lemma.sequent = None ; + } in + let index = match LogicUsage.section_of_lemma l.lem_name with + | LogicUsage.Toplevel _ -> Wpo.Axiomatic None + | LogicUsage.Axiomatic a -> Wpo.Axiomatic (Some a.ax_name) in + let mid = WpContext.MODEL.id model in + let sid = WpPropId.get_propid id in + let leg = WpPropId.get_legacy id in + let wpo = { + Wpo.po_model = model ; + Wpo.po_gid = Printf.sprintf "%s_%s" mid sid ; + Wpo.po_leg = Printf.sprintf "%s_%s" mid leg ; + Wpo.po_sid = sid ; + Wpo.po_name = Printf.sprintf "Lemma '%s'" l.lem_name ; + Wpo.po_idx = index ; + Wpo.po_pid = id ; + Wpo.po_formula = Wpo.GoalLemma vca ; + } in + Wpo.add wpo ; wpo + end end (* -------------------------------------------------------------------------- *) -(* --- WPO Computer --- *) +(* --- VCgen Cache --- *) (* -------------------------------------------------------------------------- *) -module KFmap = Kernel_function.Map - -module Computer(M : Sigs.Compiler) = -struct - - module VCG = VC(M) - module WP = Calculus.Cfg(VCG) - - let compile_lemma l = ignore (VCG.lemma l) - - let prove_lemma collection l = - if Logic_utils.verify_predicate l.lem_predicate.tp_kind then - begin - let id = WpPropId.mk_lemma_id l in - let def = VCG.lemma l in - let model = WpContext.get_model () in - let vca = { - Wpo.VC_Lemma.depends = l.lem_depends ; - Wpo.VC_Lemma.lemma = def ; - Wpo.VC_Lemma.sequent = None ; - } in - let index = match LogicUsage.section_of_lemma l.lem_name with - | LogicUsage.Toplevel _ -> Wpo.Axiomatic None - | LogicUsage.Axiomatic a -> Wpo.Axiomatic (Some a.ax_name) in - let mid = WpContext.MODEL.id model in - let sid = WpPropId.get_propid id in - let leg = WpPropId.get_legacy id in - let wpo = { - Wpo.po_model = model ; - Wpo.po_gid = Printf.sprintf "%s_%s" mid sid ; - Wpo.po_leg = Printf.sprintf "%s_%s" mid leg ; - Wpo.po_sid = sid ; - Wpo.po_name = Printf.sprintf "Lemma '%s'" l.lem_name ; - Wpo.po_idx = index ; - Wpo.po_pid = id ; - Wpo.po_formula = Wpo.GoalLemma vca ; - } in - Wpo.add wpo ; - collection := Bag.append !collection wpo ; - end - - let prove_strategy collection model kf strategy = - let cfg = WpStrategy.cfg_of_strategy strategy in - let bhv = WpStrategy.get_bhv strategy in - let index = Wpo.Function( kf , bhv ) in - if WpRTE.missing_guards model kf then - Wp_parameters.warning ~current:false ~once:true - "Missing RTE guards" ; - try - let (results,_) = WP.compute cfg strategy in - List.iter (VCG.compile collection index) results - with Warning.Error(source,reason) -> - Wp_parameters.failure - ~current:false "From %s: %s" source reason - - class wp (model:WpContext.model) = - object - val mutable lemmas : LogicUsage.logic_lemma Bag.t = Bag.empty - val mutable annots : WpStrategy.strategy Bag.t KFmap.t = KFmap.empty - method lemma = true - method model = model - - method add_lemma lemma = lemmas <- Bag.append lemmas lemma - - method add_strategy strategy = - let kf = WpStrategy.get_kf strategy in - let sf = try KFmap.find kf annots with Not_found -> Bag.empty in - annots <- KFmap.add kf (Bag.append sf strategy) annots - - method compute : Wpo.t Bag.t = - begin - let collection = ref Bag.empty in - Lang.F.release () ; - WpContext.on_context (model,WpContext.Global) - begin fun () -> - LogicUsage.iter_lemmas compile_lemma ; - Bag.iter (prove_lemma collection) lemmas ; - end () ; - KFmap.iter - (fun kf strategies -> - WpContext.on_context (model,WpContext.Kf kf) - begin fun () -> - LogicUsage.iter_lemmas compile_lemma ; - Bag.iter (prove_strategy collection model kf) strategies ; - end () - ) annots ; - lemmas <- Bag.empty ; - annots <- KFmap.empty ; - Lang.F.release () ; - !collection - end - end - -end - -(* Cache because computer functors can not be instantiated twice *) -module COMPUTERS = Hashtbl.Make(WpContext.MODEL) -let computers = COMPUTERS.create 1 +(* Cache by Model Context *) +let vcgenerators = WpContext.MINDEX.create 1 -let computer setup driver = +let vcgen setup driver : (module VCgen) = let model = Factory.instance setup driver in - try COMPUTERS.find computers model + try WpContext.MINDEX.find vcgenerators model with Not_found -> let module M = (val Factory.(compiler setup.mheap setup.mvar)) in - let module VC = Computer(M) in - let generator = (new VC.wp model :> Generator.computer) in - COMPUTERS.add computers model generator ; - generator + let vcgen = (module VC(M) : VCgen) in + WpContext.MINDEX.add vcgenerators model vcgen ; + vcgen + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cfgWP.mli b/src/plugins/wp/cfgWP.mli index ee6bb78f29572ab4dffdaa8ed1841550a14bb2ca..198aa8d6b9eabf7542ede9f170fa7ee9aa766b30 100644 --- a/src/plugins/wp/cfgWP.mli +++ b/src/plugins/wp/cfgWP.mli @@ -20,14 +20,20 @@ (* *) (**************************************************************************) +open LogicUsage + (* -------------------------------------------------------------------------- *) -(* --- WP Calculus --- *) +(* --- VC Generator --- *) (* -------------------------------------------------------------------------- *) -module VC( M : Sigs.Compiler ) : Mcfg.S -module Computer( M : Sigs.Compiler ) : +module type VCgen = sig - class wp : WpContext.model -> Generator.computer + include Mcfg.S + val register_lemma : logic_lemma -> unit + val compile_lemma : logic_lemma -> Wpo.t + val compile_wp : Wpo.index -> t_prop -> Wpo.t Bag.t end -val computer : Factory.setup -> Factory.driver -> Generator.computer +val vcgen : Factory.setup -> Factory.driver -> (module VCgen) + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/cil2cfg.ml b/src/plugins/wp/cil2cfg.ml index 15d56049af783c14a410b414253f39e95494b2a6..861d41295e4efda49df284417e93b66f98ec434d 100644 --- a/src/plugins/wp/cil2cfg.ml +++ b/src/plugins/wp/cil2cfg.ml @@ -1388,3 +1388,66 @@ module KfCfg = let get kf = KfCfg.memo create kf (* ------------------------------------------------------------------------ *) +(** {2 CFG dump} *) + +module Dump = +struct + type dot = { + name: string; + chan: out_channel; + fmt : Stdlib__format.formatter; + } + + let create kf = + let name = Kernel_function.get_name kf in + let name = + Filename.concat (Wp_parameters.get_output () :> string) ("Cil2CFG_" ^ name) + in + let chan = open_out (name ^ ".dot") in + let fmt = Format.formatter_of_out_channel chan in + let out = { name ; chan ; fmt } in + Format.fprintf fmt "digraph %a {@\n" Kernel_function.pretty kf ; + Format.fprintf fmt " rankdir = TB ;@\n" ; + Format.fprintf fmt " node [ style = filled, shape = box ] ;@\n" ; + out + + let finalize out = + Format.fprintf out.fmt "}@." ; + close_out out.chan ; + let name = out.name in + ignore (Sys.command (Printf.sprintf "dot -Tpdf %s.dot > %s.pdf" name name)) + + let pp_id fmt (id, sid) = + Format.fprintf fmt "N%03d%03d" sid id + + let pp_content fmt = function + | None -> + Format.fprintf fmt "label=\"No statement\"" + | Some s -> + Format.fprintf fmt "label=\"wp:sid%d@\n%a\"" + s.sid Cil_printer.pp_stmt s + + let pp_node fmt n = + Format.fprintf fmt " %a [ %a ];@\n" + pp_id (node_id n) pp_content (node_stmt_opt n) + + let pp_edge fmt e = + Format.fprintf fmt + " %a -> %a [ label=\"%a\"];@\n" + pp_id (node_id (edge_src e)) + pp_id (node_id (edge_dst e)) + EL.pretty (edge_type e) + + let process_kf kf = + let cfg = (get kf).graph in + let dot = create kf in + CFG.iter_vertex (Format.fprintf dot.fmt "%a" pp_node) cfg ; + CFG.iter_edges_e (Format.fprintf dot.fmt "%a" pp_edge) cfg ; + finalize dot + + let process () = + Globals.Functions.iter process_kf + +end + +(* ------------------------------------------------------------------------ *) diff --git a/src/plugins/wp/cil2cfg.mli b/src/plugins/wp/cil2cfg.mli index 087824dfa25a93f43b3502aa5551afb0f98ecf5d..97ef821dd4005d62a566c673811065aa68da8edd 100644 --- a/src/plugins/wp/cil2cfg.mli +++ b/src/plugins/wp/cil2cfg.mli @@ -177,3 +177,8 @@ sig end module HE (I : sig type t end) : HEsig with type ti = I.t + +module Dump : +sig + val process: unit -> unit +end diff --git a/src/plugins/wp/clabels.ml b/src/plugins/wp/clabels.ml index afd2335229e896e83d3b70776636e070834c63d7..8563c5b3fb77262a7c8a6bd6a53a1fc8cc7ac96a 100644 --- a/src/plugins/wp/clabels.ml +++ b/src/plugins/wp/clabels.ml @@ -40,11 +40,10 @@ let here = "wp:here" let next = "wp:next" let pre = "wp:pre" let post = "wp:post" -let old = "wp:old" +let exit = "wp:exit" let break = "wp:break" let continue = "wp:continue" let default = "wp:default" -let at_exit = "wp:exit" let loopcurrent = "wp:loopcurrent" let loopentry = "wp:loopentry" @@ -59,6 +58,7 @@ let mem l lbl = List.mem l lbl let case n = "wp:case" ^ Int64.to_string n let stmt s = "wp:sid" ^ string_of_int s.sid +let stmt_post s = "wp:sid:post:" ^ string_of_int s.sid let loop_entry s = stmt s (* same point *) let loop_current s = "wp:head" ^ string_of_int s.sid @@ -69,14 +69,14 @@ let of_logic = function | BuiltinLabel Pre -> pre | BuiltinLabel Post -> post | FormalLabel name -> name - | BuiltinLabel Old -> old + | BuiltinLabel Old -> "wp:old" | BuiltinLabel LoopCurrent -> loopcurrent | BuiltinLabel LoopEntry -> loopentry | StmtLabel s -> stmt !s let is_post = function | BuiltinLabel Post -> true - | FormalLabel a -> a = post + | FormalLabel a -> a = post || a = exit | _ -> false let name = function FormalLabel a -> a | _ -> "" diff --git a/src/plugins/wp/clabels.mli b/src/plugins/wp/clabels.mli index 90e9318ddee025a91e738563c85067b2d3ef6629..dc3ec8d2a5f1f9a01cc9e212a1b4e323da46f987 100644 --- a/src/plugins/wp/clabels.mli +++ b/src/plugins/wp/clabels.mli @@ -44,18 +44,18 @@ val here : c_label val next : c_label val init : c_label val post : c_label +val exit : c_label val break : c_label val continue : c_label val default : c_label -val at_exit : c_label val loopentry : c_label val loopcurrent : c_label -val old : c_label val formal : string -> c_label val case : int64 -> c_label val stmt : Cil_types.stmt -> c_label +val stmt_post : Cil_types.stmt -> c_label val loop_entry : Cil_types.stmt -> c_label val loop_current : Cil_types.stmt -> c_label diff --git a/src/plugins/wp/doc/manual/wp_plugin.tex b/src/plugins/wp/doc/manual/wp_plugin.tex index c44452f15e11463a5d8e28fa88ae47ce095a7f49..933cd6aa14b0a1b4ccbfb24322588a51c9227e50 100644 --- a/src/plugins/wp/doc/manual/wp_plugin.tex +++ b/src/plugins/wp/doc/manual/wp_plugin.tex @@ -808,6 +808,11 @@ interface of the programmatic API. for which the current status is already 'invalid' (default is: \texttt{no}). \item [\tt -wp-(no)-status-maybe] includes in the goal selection those properties with an undetermined status (default is: \texttt{yes}). +\item [\tt -wp-(no)-legacy] use the legacy WP generator, if set to \texttt{no}, + WP uses a recently developed generator (default is: \texttt{no}). +\item [\tt -wp-dump] does not prove selected properties, but dumps the control + flow graph computed by WP, including code annotations, both DOT files and PDF + files into the directory specified by \texttt{-wp-out}. \end{description} \textbf{Remark:} options \texttt{-wp-status-xxx} are not taken into account diff --git a/src/plugins/wp/mcfg.ml b/src/plugins/wp/mcfg.ml index 0562e0f4b4ca37b7a1ede60bf0a830e2111bc689..568b894770929317b5078add8bfb06d9979f5055 100644 --- a/src/plugins/wp/mcfg.ml +++ b/src/plugins/wp/mcfg.ml @@ -73,8 +73,8 @@ module type S = sig (** [use_assigns env hid kind assgn goal] performs the havoc on the goal. * [hid] should be [None] iff [assgn] is [WritesAny], * and tied to the corresponding identified_property otherwise.*) - val use_assigns : t_env -> stmt option -> WpPropId.prop_id option -> - WpPropId.assigns_desc -> t_prop -> t_prop + val use_assigns : t_env -> + WpPropId.prop_id option -> WpPropId.assigns_desc -> t_prop -> t_prop val label : t_env -> stmt option -> Clabels.c_label -> t_prop -> t_prop val init : t_env -> varinfo -> init option -> t_prop -> t_prop diff --git a/src/plugins/wp/normAtLabels.ml b/src/plugins/wp/normAtLabels.ml index d6d49358da443ad7fa8ef64c6b7b5a41c4c3e1c7..f72ca1ee71a532afb23ce34bb39d34deae742d5b 100644 --- a/src/plugins/wp/normAtLabels.ml +++ b/src/plugins/wp/normAtLabels.ml @@ -121,6 +121,8 @@ let labels_fct ?kf ?at l = | BuiltinLabel Pre -> Clabels.pre | StmtLabel at -> Clabels.stmt !at | BuiltinLabel LoopEntry -> Clabels.loop_entry (enclosing_loop ?kf ?at l) + (* Labels fct is not used to label invariant establishment/preservation, + thus, contrary to what is done in [labels_loop], Current is not Here. *) | BuiltinLabel LoopCurrent -> Clabels.loop_current (enclosing_loop ?kf ?at l) | _ -> raise (LabelError l) @@ -133,16 +135,16 @@ let labels_fct_pre = function | BuiltinLabel (Pre|Here) -> Clabels.pre | l -> raise (LabelError l) -let labels_fct_post = function +let labels_fct_post ~exit = function | BuiltinLabel Init -> Clabels.init | BuiltinLabel (Pre | Old) -> Clabels.pre - | BuiltinLabel (Post | Here) -> Clabels.post + | BuiltinLabel (Post | Here) -> if exit then Clabels.exit else Clabels.post | l -> raise (LabelError l) -let labels_fct_assigns = function +let labels_fct_assigns ~exit = function | BuiltinLabel Init -> Clabels.init | BuiltinLabel (Here | Pre | Old) -> Clabels.pre - | BuiltinLabel Post -> Clabels.post + | BuiltinLabel Post -> if exit then Clabels.exit else Clabels.post | l -> raise (LabelError l) (* -------------------------------------------------------------------------- *) @@ -153,12 +155,24 @@ let labels_stmt_pre ~kf s = function | BuiltinLabel Here -> Clabels.stmt s | l -> labels_fct ~kf ~at:s l -let labels_stmt_post ~kf s l_post = function +let labels_stmt_post ~kf s = function + | BuiltinLabel Old -> Clabels.stmt s + | BuiltinLabel (Here | Post) -> Clabels.stmt_post s + | l -> labels_fct ~kf ~at:s l + +let labels_stmt_assigns ~kf s = function + | BuiltinLabel (Here | Old) -> Clabels.stmt s + | BuiltinLabel Post -> Clabels.stmt_post s + | l -> labels_fct ~kf ~at:s l + +(* LEGACY *) +let labels_stmt_post_l ~kf s l_post = function | BuiltinLabel Old -> Clabels.stmt s | BuiltinLabel (Here | Post) as l -> option l l_post | l -> labels_fct ~kf ~at:s l -let labels_stmt_assigns ~kf s l_post = function +(* LEGACY *) +let labels_stmt_assigns_l ~kf s l_post = function | BuiltinLabel (Here | Old) -> Clabels.stmt s | BuiltinLabel Post as l -> option l l_post | l -> labels_fct ~kf ~at:s l @@ -167,16 +181,12 @@ let labels_stmt_assigns ~kf s l_post = function (* --- User Assertions in Functions Code --- *) (* -------------------------------------------------------------------------- *) -let labels_assert_before ~kf s = function +let labels_assert ~kf s = function | BuiltinLabel Here -> Clabels.stmt s | l -> labels_fct ~kf ~at:s l -let labels_assert_after ~kf s l_post = function - | BuiltinLabel Old -> Clabels.stmt s - | BuiltinLabel Here as l -> option l l_post - | l -> labels_fct ~kf ~at:s l - let labels_loop s = function + (* In invariant preservation/establishment, LoopCurrent is Here. *) | BuiltinLabel (Here | LoopCurrent) -> Clabels.here | BuiltinLabel LoopEntry -> Clabels.loop_entry s | FormalLabel wplabel -> Clabels.formal wplabel diff --git a/src/plugins/wp/normAtLabels.mli b/src/plugins/wp/normAtLabels.mli index 800b10b7e4d0a4d64cb450943b6335aa183ffd8a..b6b41f04bc428215cb8ae40066755a78bf94f997 100644 --- a/src/plugins/wp/normAtLabels.mli +++ b/src/plugins/wp/normAtLabels.mli @@ -30,14 +30,15 @@ type label_mapping val labels_empty : label_mapping val labels_fct_pre : label_mapping -val labels_fct_post : label_mapping -val labels_fct_assigns : label_mapping -val labels_assert_before : kf:kernel_function -> stmt -> label_mapping -val labels_assert_after : kf:kernel_function -> stmt -> c_label option -> label_mapping +val labels_fct_post : exit:bool -> label_mapping +val labels_fct_assigns : exit:bool -> label_mapping +val labels_assert : kf:kernel_function -> stmt -> label_mapping val labels_loop : stmt -> label_mapping val labels_stmt_pre : kf:kernel_function -> stmt -> label_mapping -val labels_stmt_post : kf:kernel_function -> stmt -> c_label option -> label_mapping -val labels_stmt_assigns : kf:kernel_function -> stmt -> c_label option -> label_mapping +val labels_stmt_post : kf:kernel_function -> stmt -> label_mapping +val labels_stmt_assigns : kf:kernel_function -> stmt -> label_mapping +val labels_stmt_post_l : kf:kernel_function -> stmt -> c_label option -> label_mapping +val labels_stmt_assigns_l : kf:kernel_function -> stmt -> c_label option -> label_mapping val labels_predicate : (logic_label * logic_label) list -> label_mapping val labels_axiom : label_mapping diff --git a/src/plugins/wp/register.ml b/src/plugins/wp/register.ml index d1528afaf904910ce84500ad8c1ef3e0491e9595..9bff55614bf71b2281a5e671c08bc725efe3aed9 100644 --- a/src/plugins/wp/register.ml +++ b/src/plugins/wp/register.ml @@ -20,53 +20,10 @@ (* *) (**************************************************************************) -open Factory - let dkey_main = Wp_parameters.register_category "main" let dkey_raised = Wp_parameters.register_category "raised" let wkey_smoke = Wp_parameters.register_warn_category "smoke" -(* --------- Command Line ------------------- *) - -let cmdline () : setup = - begin - match Wp_parameters.Model.get () with - | ["Runtime"] -> - Wp_parameters.abort - "Model 'Runtime' is no more available.@\nIt will be reintroduced \ - in a future release." - | ["Logic"] -> - Wp_parameters.warning ~once:true - "Deprecated 'Logic' model.@\nUse 'Typed' with option '-wp-ref' \ - instead." ; - { - mheap = Factory.Typed MemTyped.Fits ; - mvar = Factory.Ref ; - cint = Cint.Natural ; - cfloat = Cfloat.Real ; - } - | ["Store"] -> - Wp_parameters.warning ~once:true - "Deprecated 'Store' model.@\nUse 'Typed' instead." ; - { - mheap = Factory.Typed MemTyped.Fits ; - mvar = Factory.Var ; - cint = Cint.Natural ; - cfloat = Cfloat.Real ; - } - | spec -> Factory.parse spec - end - -let set_model (s:setup) = - Wp_parameters.Model.set [Factory.ident s] - -(* --------- WP Computer -------------------- *) - -let computer () = - if Wp_parameters.Model.get () = ["Dump"] - then CfgDump.create () - else CfgWP.computer (cmdline ()) (Driver.load_driver ()) - (* ------------------------------------------------------------------------ *) (* --- Memory Model Hypotheses --- *) (* ------------------------------------------------------------------------ *) @@ -751,11 +708,12 @@ let cmdline_run () = if fct <> Wp_parameters.Fct_none then begin Wp_parameters.feedback ~ontty:`Feedback "Running WP plugin..."; - let computer = computer () in + let generator = Generator.create () in + let model = generator#model in Ast.compute (); Dyncall.compute (); if Wp_parameters.RTE.get () then - WpRTE.generate_all computer#model ; + WpRTE.generate_all model ; if Wp_parameters.has_dkey dkey_logicusage then begin LogicUsage.compute (); @@ -771,14 +729,14 @@ let cmdline_run () = (** TODO entry point *) if Wp_parameters.has_dkey dkey_builtins then begin - WpContext.on_context (computer#model,WpContext.Global) + WpContext.on_context (model,WpContext.Global) LogicBuiltins.dump (); end ; - WpTarget.compute computer#model ; - wp_compute_memory_context computer#model ; + WpTarget.compute model ; + wp_compute_memory_context model ; if Wp_parameters.CheckMemoryContext.get () then - wp_insert_memory_context computer#model ; - let goals = Generator.compute_selection computer ~fct ~bhv ~prop () in + wp_insert_memory_context model ; + let goals = generator#compute_main ~fct ~bhv ~prop () in do_wp_proofs goals ; begin if fct <> Wp_parameters.Fct_all then @@ -786,7 +744,7 @@ let cmdline_run () = else do_wp_print () ; end ; - do_wp_report computer#model ; + do_wp_report model ; end end @@ -815,10 +773,7 @@ let pp_wp_parameters fmt = if Wp_parameters.RTE.get () then Format.pp_print_string fmt " -wp-rte" ; let spec = Wp_parameters.Model.get () in if spec <> [] && spec <> ["Typed"] then - ( let descr = - if spec = ["Dump"] then "Dump" - else Factory.descr (Factory.parse spec) - in + ( let descr = Factory.descr (Factory.parse spec) in Format.fprintf fmt " -wp-model '%s'" descr ) ; if not (Wp_parameters.Let.get ()) then Format.pp_print_string fmt " -wp-no-let" ; diff --git a/src/plugins/wp/tests/wp/exit_post_scope.i b/src/plugins/wp/tests/wp/exit_post_scope.i new file mode 100644 index 0000000000000000000000000000000000000000..4cfdafaf8c5c6de925d1e2abcaab3b51fe57efa6 --- /dev/null +++ b/src/plugins/wp/tests/wp/exit_post_scope.i @@ -0,0 +1,9 @@ +void bar(void); + +/*@ + ensures \valid(&f); + exits \valid(&f); +*/ +void foo(int f){ + bar(); +} diff --git a/src/plugins/wp/tests/wp/oracle/exit_post_scope.res.oracle b/src/plugins/wp/tests/wp/oracle/exit_post_scope.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..84a6cd87562135b7069d0526bc0a799c8b19db4c --- /dev/null +++ b/src/plugins/wp/tests/wp/oracle/exit_post_scope.res.oracle @@ -0,0 +1,19 @@ +# frama-c -wp [...] +[kernel] Parsing tests/wp/exit_post_scope.i (no preprocessing) +[wp] Running WP plugin... +[kernel:annot:missing-spec] tests/wp/exit_post_scope.i:7: Warning: + Neither code nor specification for function bar, generating default assigns from the prototype +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function foo +------------------------------------------------------------ + +Goal Post-condition (file tests/wp/exit_post_scope.i, line 4) in 'foo': +Prove: false. + +------------------------------------------------------------ + +Goal Exit-condition (file tests/wp/exit_post_scope.i, line 5) in 'foo': +Prove: false. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp/oracle_qualif/exit_post_scope.res.oracle b/src/plugins/wp/tests/wp/oracle_qualif/exit_post_scope.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..cd113ca71f9103c32d6e29d58c63237284a54411 --- /dev/null +++ b/src/plugins/wp/tests/wp/oracle_qualif/exit_post_scope.res.oracle @@ -0,0 +1,15 @@ +# frama-c -wp [...] +[kernel] Parsing tests/wp/exit_post_scope.i (no preprocessing) +[wp] Running WP plugin... +[kernel:annot:missing-spec] tests/wp/exit_post_scope.i:7: Warning: + Neither code nor specification for function bar, generating default assigns from the prototype +[wp] Warning: Missing RTE guards +[wp] 2 goals scheduled +[wp] [Alt-Ergo] Goal typed_foo_ensures : Unsuccess +[wp] [Alt-Ergo] Goal typed_foo_exits : Unsuccess +[wp] Proved goals: 0 / 2 + Alt-Ergo: 0 (unsuccess: 2) +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + foo - - 2 0.0% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_plugin/cfg.c b/src/plugins/wp/tests/wp_plugin/cfg.c new file mode 100644 index 0000000000000000000000000000000000000000..0c0a510591c31a3e1074d13742043bd4614be76a --- /dev/null +++ b/src/plugins/wp/tests/wp_plugin/cfg.c @@ -0,0 +1,65 @@ +//@ assigns \nothing; +void foo(void); + +/*@ ensures BUG_WP: \false; */ +void f1(void) +{ + if (0 == 1) + goto return_label; + { + int t1 = 1; + foo(); + goto return_label; + } + return_label: return; +} + +/*@ ensures BUG_WP: \false; */ +void f1_simpler(void) +{ + if (0 == 1) + goto return_label; + { + int t1 = 1; + goto return_label; + } + return_label: return; +} + +/*@ ensures BUG_WP: \false; */ +void f1_variant(void) +{ + if (0 == 1) L: ; + else { + int t1 = 1; + foo(); + goto return_label; + } + return_label: return; +} + +/*@ ensures FAILS_AS_EXPECTED: \false; */ +void f1_variant_invert(void) +{ + if (! (0 == 1)) { + int t1 = 1; + foo(); + goto return_label; + } + else L: ; + return_label: return; +} + +/*@ ensures BUG_WP: \false; */ +void f2(void) +{ + if (0 == 1) + goto L; + if (0 == 0) { + int t1 = 1; + foo(); + goto return_label; + } + else L: ; + return_label: return; +} diff --git a/src/plugins/wp/tests/wp_plugin/oracle/cfg.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle/cfg.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..f78e97da034cdcae042c07a28d2fdb21a6fead50 --- /dev/null +++ b/src/plugins/wp/tests/wp_plugin/oracle/cfg.res.oracle @@ -0,0 +1,44 @@ +# frama-c -wp [...] +[kernel] Parsing tests/wp_plugin/cfg.c (with preprocessing) +[wp] Running WP plugin... +[wp] Warning: Missing RTE guards +------------------------------------------------------------ + Function f1 +------------------------------------------------------------ + +Goal Post-condition 'BUG_WP' in 'f1': +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f1_simpler +------------------------------------------------------------ + +Goal Post-condition 'BUG_WP' in 'f1_simpler': +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f1_variant +------------------------------------------------------------ + +Goal Post-condition 'BUG_WP' in 'f1_variant': +Prove: true. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f1_variant_invert +------------------------------------------------------------ + +Goal Post-condition 'FAILS_AS_EXPECTED' in 'f1_variant_invert': +Prove: false. + +------------------------------------------------------------ +------------------------------------------------------------ + Function f2 +------------------------------------------------------------ + +Goal Post-condition 'BUG_WP' in 'f2': +Prove: true. + +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_plugin/oracle_qualif/cfg.res.oracle b/src/plugins/wp/tests/wp_plugin/oracle_qualif/cfg.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..65a2d69e16bdb23fa6a41746dccb152924467752 --- /dev/null +++ b/src/plugins/wp/tests/wp_plugin/oracle_qualif/cfg.res.oracle @@ -0,0 +1,21 @@ +# frama-c -wp [...] +[kernel] Parsing tests/wp_plugin/cfg.c (with preprocessing) +[wp] Running WP plugin... +[wp] Warning: Missing RTE guards +[wp] 5 goals scheduled +[wp] [Qed] Goal typed_f1_ensures_BUG_WP : Valid +[wp] [Qed] Goal typed_f1_simpler_ensures_BUG_WP : Valid +[wp] [Qed] Goal typed_f1_variant_ensures_BUG_WP : Valid +[wp] [Alt-Ergo] Goal typed_f1_variant_invert_ensures_FAILS_AS_EXPECTED : Unsuccess +[wp] [Qed] Goal typed_f2_ensures_BUG_WP : Valid +[wp] Proved goals: 4 / 5 + Qed: 4 + Alt-Ergo: 0 (unsuccess: 1) +------------------------------------------------------------ + Functions WP Alt-Ergo Total Success + f1 1 - 1 100% + f1_simpler 1 - 1 100% + f1_variant 1 - 1 100% + f1_variant_invert - - 1 0.0% + f2 1 - 1 100% +------------------------------------------------------------ diff --git a/src/plugins/wp/tests/wp_plugin/oracle_qualif/stmt.log b/src/plugins/wp/tests/wp_plugin/oracle_qualif/stmt.log index 54fa701c499fd123bd6160f72a022ee8b5fd21de..fc376ede27e35a2ecf8fc9819774ab3bedbdae75 100644 --- a/src/plugins/wp/tests/wp_plugin/oracle_qualif/stmt.log +++ b/src/plugins/wp/tests/wp_plugin/oracle_qualif/stmt.log @@ -1,4 +1,4 @@ -# frama-c -wp -wp-model 'Dump' [...] +# frama-c -wp [...] [kernel] Parsing tests/wp_plugin/stmt.c (with preprocessing) [wp] Running WP plugin... [wp] [CFG] Goal f_exits : Valid (Unreachable) diff --git a/src/plugins/wp/tests/wp_plugin/stmt.c b/src/plugins/wp/tests/wp_plugin/stmt.c index df45bff806e6f85034d9af3b89d04427f64516dd..bb82e910c1cc73a606e725ae72c332328f1dd9a4 100644 --- a/src/plugins/wp/tests/wp_plugin/stmt.c +++ b/src/plugins/wp/tests/wp_plugin/stmt.c @@ -4,7 +4,7 @@ /* run.config_qualif OPT: -load-module report -then -report - EXECNOW: LOG stmt.log LOG f.dot LOG f_default_for_stmt_2.dot LOG g.dot LOG g_default_for_stmt_11.dot @frama-c@ -no-autoload-plugins -load-module wp -wp-precond-weakening -wp -wp-warn-key pedantic-assigns=inactive -wp-model Dump -wp-out tests/wp_plugin/result_qualif -wp-msg-key shell @PTEST_FILE@ 1> tests/wp_plugin/result_qualif/stmt.log + EXECNOW: LOG stmt.log LOG f.dot LOG f_default_for_stmt_2.dot LOG g.dot LOG g_default_for_stmt_11.dot @frama-c@ -no-autoload-plugins -load-module wp -wp-precond-weakening -wp -wp-warn-key pedantic-assigns=inactive -wp-dump -wp-out tests/wp_plugin/result_qualif -wp-msg-key shell @PTEST_FILE@ 1> tests/wp_plugin/result_qualif/stmt.log */ /*@ ensures a > 0 ==> \result == a + b; diff --git a/src/plugins/wp/wpAnnot.ml b/src/plugins/wp/wpAnnot.ml index 3d520317fc0d9c15445a98c8712dc60e0a12eb07..fef919f1502924d6c0a679065c50cccc5ada6697 100644 --- a/src/plugins/wp/wpAnnot.ml +++ b/src/plugins/wp/wpAnnot.ml @@ -30,41 +30,6 @@ open Cil_types open Cil_datatype open Logic_utils -(* -------------------------------------------------------------------------- *) -(* --- Selection of relevant assigns and postconditions --- *) -(* -------------------------------------------------------------------------- *) - -(* Properties for kf-conditions of termination-kind 'tkind' *) -let get_called_postconds (tkind:termination_kind) kf = - let bhvs = Annotations.behaviors kf in - List.fold_left - (fun properties bhv -> - List.fold_left - (fun properties postcond -> - if tkind = fst postcond then - let pid_spec = Property.ip_of_ensures kf Kglobal bhv postcond in - pid_spec :: properties - else properties) - properties bhv.b_post_cond) - [] - bhvs - -let get_called_post_conditions = get_called_postconds Cil_types.Normal -let get_called_exit_conditions = get_called_postconds Cil_types.Exits - -(** Properties for assigns of kf *) -let get_called_assigns kf = - let bhvs = Annotations.behaviors kf in - List.fold_left - (fun properties bhv -> - if Cil.is_default_behavior bhv then - match Property.ip_assigns_of_behavior kf Kglobal [] bhv with - | None -> properties - | Some ip -> ip :: properties - else properties) - [] - bhvs - (* -------------------------------------------------------------------------- *) (* --- Status of Unreachable Annotations --- *) (* -------------------------------------------------------------------------- *) @@ -111,74 +76,13 @@ let set_unreachable pid = in List.iter emit pids -(*----------------------------------------------------------------------------*) -(* Proofs *) -(*----------------------------------------------------------------------------*) - -type proof = { - target : Property.t ; - proved : proofpart array ; - mutable invalid : bool ; - mutable dependencies : Property.Set.t ; -} and proofpart = - | Noproof - | Complete - | Parts of Bitvector.t - -let target p = p.target -let dependencies p = - Property.Set.elements (Property.Set.remove p.target p.dependencies) - -let create_proof ip = - let n = WpPropId.subproofs ip in - { - target = WpPropId.property_of_id ip ; - proved = Array.make n Noproof ; - dependencies = Property.Set.empty ; - invalid = false ; - } - -let add_proof pf ip hs = - begin - if not (Property.equal (WpPropId.property_of_id ip) pf.target) - then Wp_parameters.fatal "Partial proof inconsistency" ; - List.iter - (fun iph -> - if not (WpPropId.is_requires iph) then - pf.dependencies <- Property.Set.add iph pf.dependencies - ) hs ; - let k = WpPropId.subproof_idx ip in - match WpPropId.parts_of_id ip with - | None -> pf.proved.(k) <- Complete - | Some(p,n) -> - match pf.proved.(k) with - | Complete -> () - | Noproof -> - let bv = Bitvector.create n in - Bitvector.set_range bv 0 (p-1) ; - Bitvector.set_range bv (p+1) (n-1) ; - pf.proved.(k) <- Parts bv - | Parts bv -> - Bitvector.clear bv p ; - if Bitvector.is_empty bv - then pf.proved.(k) <- Complete - end - -let add_invalid_proof pf = pf.invalid <- true - -let is_composed pf = - Array.length pf.proved > 1 - -let is_proved pf = - Array.for_all (function Complete -> true | _ -> false) pf.proved - -let is_invalid pf = - pf.invalid && not (is_proved pf) - (* -------------------------------------------------------------------------- *) -(* --- PID for Functions --- *) +(* --- Preconditions at Callsites --- *) (* -------------------------------------------------------------------------- *) +let call_preconditions = + Statuses_by_call.all_call_preconditions_at ~warn_missing:true + let mk_call_pre_id called_kf bhv s_call called_pre = (* TODOclean : quite dirty here ! *) let id = WpPropId.mk_pre_id called_kf Kglobal bhv called_pre in @@ -187,13 +91,6 @@ let mk_call_pre_id called_kf bhv s_call called_pre = Statuses_by_call.precondition_at_call called_kf called_pre s_call in WpPropId.mk_call_pre_id called_kf s_call called_pre called_pre_p -(* -------------------------------------------------------------------------- *) -(* --- Preconditions --- *) -(* -------------------------------------------------------------------------- *) - -let call_preconditions = - Statuses_by_call.all_call_preconditions_at ~warn_missing:true - (* Preconditions at call-point as WpPropId.t *) let preconditions_at_call s = function | Cil2cfg.Static kf -> @@ -202,8 +99,9 @@ let preconditions_at_call s = function List.map aux preconds | Cil2cfg.Dynamic _ -> [] -let get_called_preconditions_at kf stmt = - List.map snd (call_preconditions kf stmt) +(* ########################################################################## *) +(* ### WARNING: DEPRECATED API BELOW THIS LINE ### *) +(* ########################################################################## *) (*----------------------------------------------------------------------------*) (* Strategy and annotations *) @@ -316,23 +214,9 @@ let filter_speconly config pid = | _ -> false else true -let filter_status pid = - Wp_parameters.StatusAll.get () || - begin - let module C = Property_status.Consolidation in - match C.get (WpPropId.property_of_id pid) with - | C.Never_tried -> true - | C.Considered_valid | C.Inconsistent _ -> false - | C.Valid _ | C.Valid_under_hyp _ - | C.Invalid_but_dead _ | C.Valid_but_dead _ | C.Unknown_but_dead _ -> - Wp_parameters.StatusTrue.get () - | C.Unknown _ -> Wp_parameters.StatusMaybe.get () - | C.Invalid _ | C.Invalid_under_hyp _ -> Wp_parameters.StatusFalse.get () - end - let filter_configstatus config pid = (match config.asked_prop with IdProp _ -> true | _ -> false) || - (filter_status pid) + (WpPropId.filter_status pid) let filter_asked config pid = match config.asked_prop with @@ -422,7 +306,7 @@ let add_stmt_assigns_goal config s active acc b l_post = match b.b_assigns with | Some id -> if goal_to_select config id then let kf = config.kf in - let labels = NormAtLabels.labels_stmt_assigns ~kf s l_post in + let labels = NormAtLabels.labels_stmt_assigns_l ~kf s l_post in let assigns = NormAtLabels.preproc_assigns labels assigns in let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in WpStrategy.add_assigns acc WpStrategy.Agoal id a_desc @@ -431,12 +315,13 @@ let add_stmt_assigns_goal config s active acc b l_post = match b.b_assigns with let add_fct_assigns_goal config acc tkind b = match b.b_assigns with | WritesAny -> acc | Writes assigns -> - let id = WpPropId.mk_fct_assigns_id config.kf b tkind assigns in + let has_exit = Cil2cfg.has_exit (Cil2cfg.get config.kf) in + let id = WpPropId.mk_fct_assigns_id config.kf has_exit b tkind assigns in match id with | None -> acc | Some id -> if goal_to_select config id then - let labels = NormAtLabels.labels_fct_assigns in + let labels = NormAtLabels.labels_fct_assigns ~exit:false in let assigns' = NormAtLabels.preproc_assigns labels assigns in let a_desc = WpPropId.mk_kf_assigns_desc assigns' in WpStrategy.add_assigns acc WpStrategy.Agoal id a_desc @@ -772,7 +657,7 @@ let add_called_post called_kf termination_kind acc = let add_call_checks config s kf posts exits = if cur_fct_default_bhv config && Wp_parameters.SmokeTests.get () - && Wp_parameters.SmokeDeadcode.get () + && Wp_parameters.SmokeDeadcall.get () then WpStrategy.add_prop_dead_call kf s posts exits else @@ -1310,7 +1195,7 @@ let process_unreached_annots cfg reachability = let kf = Cil2cfg.cfg_kf cfg in let spec = Annotations.funspec kf in let add_id acc id = - if filter_status id then id::acc + if WpPropId.filter_status id then id::acc else (* non-selected property : nothing to do *) acc in let do_post b tk acc (termk, _ as p) = @@ -1489,3 +1374,34 @@ let get_function_strategies ~model ?(assigns=WithAssigns) ?(bhv=[]) ?(prop=[]) kf = let prop = match prop with [] -> AllProps | _ -> NamedProp prop in get_strategies assigns kf model bhv None prop + +let get_property_strategies ~model ip = + let open Property in + match ip with + | IPBehavior {ib_kf; ib_bhv} -> + let bhv = [ib_bhv.Cil_types.b_name] in + let assigns = WithAssigns in + get_function_strategies ~model ~assigns ~bhv ib_kf + | IPComplete _ + | IPDisjoint _ + | IPCodeAnnot _ + | IPAllocation _ + | IPAssigns _ + | IPDecrease _ + | IPPredicate _ + -> + let assigns = WithAssigns in + get_id_prop_strategies ~model ~assigns ip + + | IPAxiomatic _ + | IPLemma _ + | IPFrom _ + | IPReachable _ + | IPPropertyInstance _ + | IPOther _ + | IPTypeInvariant _ + | IPGlobalInvariant _ + | IPExtended _ + -> + Wp_parameters.result "Nothing to compute for '%a'" pretty ip ; + [] diff --git a/src/plugins/wp/wpAnnot.mli b/src/plugins/wp/wpAnnot.mli index 0fcc0b671b7adee8836ef30666226ca8b2e524a4..a0a268cbc61cd59047db6ca65190568e294b6cca 100644 --- a/src/plugins/wp/wpAnnot.mli +++ b/src/plugins/wp/wpAnnot.mli @@ -20,58 +20,24 @@ (* *) (**************************************************************************) +open Cil_types + (** Every access to annotations have to go through here, * so this is the place where we decide what the computation * is allowed to use. *) -open Cil_types - -(*----------------------------------------------------------------------------*) - -(** A proof accumulator for a set of related prop_id *) -type proof - -val create_proof : WpPropId.prop_id -> proof -(** to be used only once for one of the related prop_id *) - -val add_proof : proof -> WpPropId.prop_id -> Property.t list -> unit -(** accumulate in the proof the partial proof for this prop_id *) - -val add_invalid_proof : proof -> unit -(** add an invalid proof result ; can not revert a complete proof *) - -val is_composed : proof -> bool -(** whether a proof needs several lemma to be complete *) - -val is_proved : proof -> bool -(** whether all partial proofs have been accumulated or not *) - -val is_invalid : proof -> bool -(** whether an invalid proof result has been registered or not *) - -val target : proof -> Property.t -val dependencies : proof -> Property.t list - -val filter_status : WpPropId.prop_id -> bool +(* ########################################################################## *) +(* ### WARNING: DEPRECATED API ### *) +(* ########################################################################## *) (*----------------------------------------------------------------------------*) val unreachable_proved : int ref val unreachable_failed : int ref - -val get_called_preconditions_at : kernel_function -> stmt -> Property.t list -val get_called_post_conditions : kernel_function -> Property.t list -val get_called_exit_conditions : kernel_function -> Property.t list -val get_called_assigns : kernel_function -> Property.t list - -(*----------------------------------------------------------------------------*) +val set_unreachable : WpPropId.prop_id -> unit type asked_assigns = NoAssigns | OnlyAssigns | WithAssigns -val get_id_prop_strategies : - model:WpContext.model -> - ?assigns:asked_assigns -> Property.t -> WpStrategy.strategy list - val get_call_pre_strategies : model:WpContext.model -> stmt -> WpStrategy.strategy list @@ -83,4 +49,7 @@ val get_function_strategies : ?prop:string list -> Kernel_function.t -> WpStrategy.strategy list +val get_property_strategies : + model:WpContext.model -> Property.t -> WpStrategy.strategy list + (*----------------------------------------------------------------------------*) diff --git a/src/plugins/wp/wpContext.ml b/src/plugins/wp/wpContext.ml index 0205b6529d5f33c4af4c20ec1d77c1558b7bac73..62b15a0def8506932e938f1fd2bebd995e8ef5e3 100644 --- a/src/plugins/wp/wpContext.ml +++ b/src/plugins/wp/wpContext.ml @@ -56,6 +56,8 @@ struct } end +module MINDEX = Hashtbl.Make(MODEL) + module MODELS = struct diff --git a/src/plugins/wp/wpContext.mli b/src/plugins/wp/wpContext.mli index cf3576f78470dd4eb1c73f4719c993450864506e..5b3203bc0b2c5522369e8c207c925d25c2ff60a6 100644 --- a/src/plugins/wp/wpContext.mli +++ b/src/plugins/wp/wpContext.mli @@ -78,6 +78,8 @@ sig val compare : t -> t -> int end +module MINDEX : Hashtbl.S with type key = model + val is_defined : unit -> bool val on_context : context -> ('a -> 'b) -> 'a -> 'b val get_model : unit -> model diff --git a/src/plugins/wp/wpGenerator.ml b/src/plugins/wp/wpGenerator.ml new file mode 100644 index 0000000000000000000000000000000000000000..8d09a45d599a2d4b85c42bca094bdfb0b296dd27 --- /dev/null +++ b/src/plugins/wp/wpGenerator.ml @@ -0,0 +1,233 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- WP Computer (main entry points) --- *) +(* -------------------------------------------------------------------------- *) + +class type computer = + object + method model : WpContext.model + method add_strategy : WpStrategy.strategy -> unit + method add_lemma : LogicUsage.logic_lemma -> unit + method compute : Wpo.t Bag.t + end + +(* -------------------------------------------------------------------------- *) +(* --- Property Entry Point --- *) +(* -------------------------------------------------------------------------- *) + +let compute_ip cc ip = + let open Property in match ip with + | IPLemma _ + | IPAxiomatic _ + -> + let rec iter cc = function + | IPLemma {il_name} -> cc#add_lemma (LogicUsage.logic_lemma il_name) + | IPAxiomatic {iax_props} -> List.iter (iter cc) iax_props + | _ -> () + in iter cc ip ; + cc#compute + | _ -> + List.iter cc#add_strategy + (WpAnnot.get_property_strategies ~model:cc#model ip) ; + cc#compute + +(* -------------------------------------------------------------------------- *) +(* --- Annotations Entry Point --- *) +(* -------------------------------------------------------------------------- *) + +let add_kf cc ?bhv ?prop kf = + let model = cc#model in + let assigns = WpAnnot.WithAssigns in + List.iter cc#add_strategy + (WpAnnot.get_function_strategies ~model ~assigns ?bhv ?prop kf) + +let add_lemmas cc = function + | None | Some[] -> + LogicUsage.iter_lemmas + (fun lem -> + let idp = WpPropId.mk_lemma_id lem in + if WpPropId.filter_status idp then cc#add_lemma lem) + | Some ps -> + if List.mem "-@lemmas" ps then () + else LogicUsage.iter_lemmas + (fun lem -> + let idp = WpPropId.mk_lemma_id lem in + if WpPropId.filter_status idp && WpPropId.select_by_name ps idp + then cc#add_lemma lem) + +let compute_kf cc ?kf ?bhv ?prop () = + begin + Option.iter (add_kf cc ?bhv ?prop) kf ; + cc#compute + end + +let compute_selection cc ?(fct=Wp_parameters.Fct_all) ?bhv ?prop () = + begin + add_lemmas cc prop ; + Wp_parameters.iter_fct (add_kf cc ?bhv ?prop) fct ; + cc#compute + end + +let compute_call cc stmt = + let model = cc#model in + List.iter cc#add_strategy (WpAnnot.get_call_pre_strategies ~model stmt) ; + cc#compute + +(* -------------------------------------------------------------------------- *) +(* --- WPO Computer --- *) +(* -------------------------------------------------------------------------- *) + +module KFmap = Kernel_function.Map + +module Computer(VCG : CfgWP.VCgen) = +struct + + open LogicUsage + module WP = Calculus.Cfg(VCG) + + let prove_strategy collection model kf strategy = + let cfg = WpStrategy.cfg_of_strategy strategy in + let bhv = WpStrategy.get_bhv strategy in + let index = Wpo.Function( kf , bhv ) in + if WpRTE.missing_guards model kf then + Wp_parameters.warning ~current:false ~once:true + "Missing RTE guards" ; + try + let (results,_) = WP.compute cfg strategy in + List.iter + (fun wp -> + let wcs = VCG.compile_wp index wp in + collection := Bag.concat !collection wcs + ) results + with Warning.Error(source,reason) -> + Wp_parameters.failure + ~current:false "From %s: %s" source reason + + class wp (model:WpContext.model) = + object + val mutable lemmas : LogicUsage.logic_lemma Bag.t = Bag.empty + val mutable annots : WpStrategy.strategy Bag.t KFmap.t = KFmap.empty + + method model = model + + method add_lemma lemma = + if Logic_utils.verify_predicate lemma.lem_predicate.tp_kind then + lemmas <- Bag.append lemmas lemma + + method add_strategy strategy = + let kf = WpStrategy.get_kf strategy in + let sf = try KFmap.find kf annots with Not_found -> Bag.empty in + annots <- KFmap.add kf (Bag.append sf strategy) annots + + method compute : Wpo.t Bag.t = + begin + let collection = ref Bag.empty in + Lang.F.release () ; + WpContext.on_context (model,WpContext.Global) + begin fun () -> + Bag.iter + (fun (l : LogicUsage.logic_lemma) -> + if Logic_utils.verify_predicate l.lem_predicate.tp_kind then + let vc = VCG.compile_lemma l in + collection := Bag.append !collection vc + ) lemmas ; + end () ; + KFmap.iter + (fun kf strategies -> + WpContext.on_context (model,WpContext.Kf kf) + begin fun () -> + LogicUsage.iter_lemmas + (fun (l : LogicUsage.logic_lemma) -> + if Logic_utils.use_predicate l.lem_predicate.tp_kind + then VCG.register_lemma l) ; + LogicUsage.iter_lemmas VCG.register_lemma ; + Bag.iter (prove_strategy collection model kf) strategies ; + end () + ) annots ; + lemmas <- Bag.empty ; + annots <- KFmap.empty ; + Lang.F.release () ; + !collection + end + end + +end + +(* -------------------------------------------------------------------------- *) +(* --- Dump Computer --- *) +(* -------------------------------------------------------------------------- *) + +module DUMPER = Calculus.Cfg(CfgDump) + +let dumper () = + let driver = Driver.load_driver () in + let model = Factory.(instance default driver) in + object + val mutable wptasks = [] + + method model = model + + method add_lemma (_ : LogicUsage.logic_lemma) = () + + method add_strategy strategy = + wptasks <- strategy :: wptasks + + method compute : Wpo.t Bag.t = + begin + (* Generates Wpos and accumulate exported goals *) + List.iter + (fun strategy -> + let cfg = WpStrategy.cfg_of_strategy strategy in + let kf = Cil2cfg.cfg_kf cfg in + let bhv = WpStrategy.behavior_name_of_strategy strategy in + CfgDump.fopen kf bhv ; + try + ignore (DUMPER.compute cfg strategy) ; + CfgDump.flush () + with err -> + CfgDump.flush () ; + raise err + ) wptasks ; + wptasks <- [] ; + Bag.empty + end + + end + +(* -------------------------------------------------------------------------- *) +(* --- Generators --- *) +(* -------------------------------------------------------------------------- *) + +let generators = WpContext.MINDEX.create 1 + +let computer setup driver = + let model = Factory.instance setup driver in + try WpContext.MINDEX.find generators model + with Not_found -> + let module VCG = (val CfgWP.vcgen setup driver) in + let module CC = Computer(VCG) in + let computer = (new CC.wp model :> computer) in + WpContext.MINDEX.add generators model computer ; computer + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpGenerator.mli b/src/plugins/wp/wpGenerator.mli new file mode 100644 index 0000000000000000000000000000000000000000..a2e0cec5c9cfec7b86469cca946fa0b15f2ab38d --- /dev/null +++ b/src/plugins/wp/wpGenerator.mli @@ -0,0 +1,53 @@ +(**************************************************************************) +(* *) +(* This file is part of WP plug-in of Frama-C. *) +(* *) +(* Copyright (C) 2007-2021 *) +(* CEA (Commissariat a l'energie atomique et aux energies *) +(* 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). *) +(* *) +(**************************************************************************) + +(* -------------------------------------------------------------------------- *) +(* --- WP Computer (main entry points) --- *) +(* -------------------------------------------------------------------------- *) + +class type computer = + object + method model : WpContext.model + method add_strategy : WpStrategy.strategy -> unit + method add_lemma : LogicUsage.logic_lemma -> unit + method compute : Wpo.t Bag.t + end + +open Wp_parameters + +val compute_ip : computer -> Property.t -> Wpo.t Bag.t +val compute_call : computer -> Cil_types.stmt -> Wpo.t Bag.t + +val compute_kf : computer -> + ?kf:Kernel_function.t -> + ?bhv:string list -> + ?prop:string list -> + unit -> Wpo.t Bag.t + +val compute_selection : computer -> + ?fct:functions -> + ?bhv:string list -> + ?prop:string list -> + unit -> Wpo.t Bag.t + +val dumper : unit -> computer +val computer : Factory.setup -> Factory.driver -> computer diff --git a/src/plugins/wp/wpPropId.ml b/src/plugins/wp/wpPropId.ml index 05ca307d17159ba9d542aeccebf2033da84ccdea..c3312d5f84313af0f14bb82cdfc8351ddd311322 100644 --- a/src/plugins/wp/wpPropId.ml +++ b/src/plugins/wp/wpPropId.ml @@ -115,6 +115,11 @@ let mk_assert_id kf s ca = mk_prop PKProp (mk_annot_id kf s ca) let mk_loop_inv_id kf s ~established ca = let kind = if established then PKEstablished else PKPreserved in mk_prop kind (mk_annot_id kf s ca) + +let mk_loop_inv kf s ca = + mk_loop_inv_id kf s ~established:true ca, + mk_loop_inv_id kf s ~established:false ca + let mk_inv_hyp_id kf s ca = mk_prop PKPropLoop (mk_annot_id kf s ca) let mk_var_decr_id kf s ca = mk_prop PKVarDecr (mk_annot_id kf s ca) let mk_var_pos_id kf s ca = mk_prop PKVarPos (mk_annot_id kf s ca) @@ -128,16 +133,16 @@ let mk_bhv_from_id kf ki a bhv from = let id = Property.ip_of_from kf ki (Property.Id_contract (a,bhv)) from in mk_prop PKProp (Option.get id) -let get_kind_for_tk kf tkind = match tkind with +let get_kind_for_tk tkind has_exit = match tkind with | Normal -> - if Cil2cfg.has_exit (Cil2cfg.get kf) then PKAFctOut else PKProp + if has_exit then PKAFctOut else PKProp | Exits -> PKAFctExit | _ -> assert false -let mk_fct_from_id kf bhv tkind from = +let mk_fct_from_id kf kf_has_exit bhv tkind from = let contract_info = Property.Id_contract(Datatype.String.Set.empty,bhv) in let id = Property.ip_of_from kf Kglobal contract_info from in - let kind = get_kind_for_tk kf tkind in + let kind = get_kind_for_tk tkind kf_has_exit in mk_prop kind (Option.get id) let mk_disj_bhv_id (kf,ki,active,disj) = @@ -160,15 +165,18 @@ let mk_loop_assigns_id kf s ca a = let p = Property.ip_of_assigns kf (Kstmt s) ca (Writes a) in Option.map (mk_prop PKPropLoop) p -let mk_fct_assigns_id kf b tkind a = +let mk_fct_assigns_id kf kf_has_exit b tkind a = let b = Property.Id_contract(Datatype.String.Set.empty,b) in - let kind = get_kind_for_tk kf tkind in + let kind = get_kind_for_tk tkind kf_has_exit in let p = Property.ip_of_assigns kf Kglobal b (Writes a) in Option.map (mk_prop kind) p let mk_pre_id kf ki b p = mk_prop PKProp (Property.ip_of_requires kf ki b p) +let mk_post_id kf ki b p = + mk_prop PKProp (Property.ip_of_ensures kf ki b p) + let mk_stmt_post_id kf s b p = mk_prop PKProp (Property.ip_of_ensures kf (Kstmt s) b p) @@ -468,13 +476,13 @@ let ident_names names = List.filter (function "" -> true | _ as n -> '\"' <> (String.get n 0) ) names -let pred_names p = +let user_pred_names p = let p_names = ident_names p.tp_statement.pred_name in if p.tp_kind = Check then "@check"::p_names else p_names let code_annot_names ca = match ca.annot_content with - | AAssert (_, pred) -> "@assert" :: pred_names pred - | AInvariant (_,_,pred) -> "@invariant":: pred_names pred + | AAssert (_, pred) -> "@assert" :: user_pred_names pred + | AInvariant (_,_,pred) -> "@invariant":: user_pred_names pred | AVariant (term, _) -> "@variant"::(ident_names term.term_name) | AExtended(_,_,{ext_name}) -> [Printf.sprintf "@%s" ext_name] | _ -> [] (* TODO : add some more names ? *) @@ -485,7 +493,7 @@ let user_prop_names p = let open Property in match p with | IPPredicate {ip_kind; ip_pred} -> Format.asprintf "@@%a" Property.pretty_predicate_kind ip_kind :: - pred_names ip_pred.ip_content + user_pred_names ip_pred.ip_content | IPExtended {ie_ext={ext_name}} -> [ Printf.sprintf "@%s" ext_name ] | IPCodeAnnot {ica_ca} -> code_annot_names ica_ca | IPComplete {ic_bhvs} -> @@ -503,7 +511,7 @@ let user_prop_names p = | IPDecrease {id_ca=Some ca} -> "@decreases"::code_annot_names ca | IPDecrease _ -> [ "@decreases" ] | IPLemma {il_name = a; il_pred = l} -> - let names = "@lemma"::a::pred_names l in + let names = "@lemma"::a::user_pred_names l in begin match LogicUsage.section_of_lemma a with | LogicUsage.Toplevel _ -> names @@ -537,7 +545,7 @@ let user_bhv_names p = in Option.fold ~none:fors ~some:(fun b -> b.b_name :: fors) (get_behavior p) let string_of_termination_kind = function - Normal -> "post" + Normal -> "ensures" | Exits -> "exits" | Breaks -> "breaks" | Continues -> "continues" @@ -808,11 +816,13 @@ let are_selected_names asked names = | Some false -> false | _ -> true - let select_by_name asked_names pid = let names = user_prop_pid pid in are_selected_names asked_names names +let select_for_behaviors bhvs pid = + let fors = Property.get_for_behaviors @@ property_of_id pid in + List.exists (fun b -> List.mem b fors) bhvs let select_call_pre s_call asked_pre pid = match pid.p_kind with @@ -859,6 +869,13 @@ let mk_stmt_assigns_desc s assigns = { a_assigns = Writes assigns ; } +let mk_stmt_assigns_any_desc s = { + a_label = Clabels.stmt s ; + a_stmt = Some s ; + a_kind = StmtAssigns ; + a_assigns = WritesAny ; +} + let mk_init_assigns = { a_label = Clabels.init ; a_stmt = None ; @@ -1100,3 +1117,87 @@ let get_induction p = | PKEstablished|PKVarDecr|PKVarPos|PKPreserved -> (match get_stmt (property_of_id p) with | None -> None | Some (_, s) -> Some s) + +(* -------------------------------------------------------------------------- *) +(* --- Filter according to status --- *) +(* -------------------------------------------------------------------------- *) + +let filter_status pid = + Wp_parameters.StatusAll.get () || + begin + let module C = Property_status.Consolidation in + match C.get (property_of_id pid) with + | C.Never_tried -> true + | C.Considered_valid | C.Inconsistent _ -> false + | C.Valid _ | C.Valid_under_hyp _ + | C.Invalid_but_dead _ | C.Valid_but_dead _ | C.Unknown_but_dead _ -> + Wp_parameters.StatusTrue.get () + | C.Unknown _ -> Wp_parameters.StatusMaybe.get () + | C.Invalid _ | C.Invalid_under_hyp _ -> Wp_parameters.StatusFalse.get () + end + +(*----------------------------------------------------------------------------*) +(* Proofs Management *) +(*----------------------------------------------------------------------------*) + +type proof = { + target : Property.t ; + proved : proofpart array ; + mutable invalid : bool ; + mutable dependencies : Property.Set.t ; +} and proofpart = + | Noproof + | Complete + | Parts of Bitvector.t + +let target p = p.target +let dependencies p = + Property.Set.elements (Property.Set.remove p.target p.dependencies) + +let create_proof ip = + let n = subproofs ip in + { + target = property_of_id ip ; + proved = Array.make n Noproof ; + dependencies = Property.Set.empty ; + invalid = false ; + } + +let add_proof pf ip hs = + begin + if not (Property.equal (property_of_id ip) pf.target) + then Wp_parameters.fatal "Partial proof inconsistency" ; + List.iter + (fun iph -> + if not (is_requires iph) then + pf.dependencies <- Property.Set.add iph pf.dependencies + ) hs ; + let k = subproof_idx ip in + match parts_of_id ip with + | None -> pf.proved.(k) <- Complete + | Some(p,n) -> + match pf.proved.(k) with + | Complete -> () + | Noproof -> + let bv = Bitvector.create n in + Bitvector.set_range bv 0 (p-1) ; + Bitvector.set_range bv (p+1) (n-1) ; + pf.proved.(k) <- Parts bv + | Parts bv -> + Bitvector.clear bv p ; + if Bitvector.is_empty bv + then pf.proved.(k) <- Complete + end + +let add_invalid_proof pf = pf.invalid <- true + +let is_composed pf = + Array.length pf.proved > 1 + +let is_proved pf = + Array.for_all (function Complete -> true | _ -> false) pf.proved + +let is_invalid pf = + pf.invalid && not (is_proved pf) + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpPropId.mli b/src/plugins/wp/wpPropId.mli index 0d380c5661766e425df4c5c93ecf73634613ca65..20ba4ab98d8f7742b304325fe97835e4e2273322 100644 --- a/src/plugins/wp/wpPropId.mli +++ b/src/plugins/wp/wpPropId.mli @@ -62,16 +62,18 @@ val is_smoke_test : prop_id -> bool (** test if the prop_id does not have a [no_wp:] in its name(s). *) val select_default : prop_id -> bool -(** test if the prop_id has to be selected for the asked name. - Also returns a debug message to explain then answer. Includes - a test for [no_wp:]. *) +(** test if the prop_id has to be selected for the asked names. *) val select_by_name : string list -> prop_id -> bool -(** test if the prop_id has to be selected when we want to select the call - * precondition the the [stmt] call (None means all the call preconditions). - * Also returns a debug message to explain then answer. *) +(** test if the prop_id has to be selected for the asked behavior names. *) +val select_for_behaviors : string list -> prop_id -> bool + +(** test if the prop_id has to be selected when we want to select the call. *) val select_call_pre : stmt -> Property.t option -> prop_id -> bool +(** From a list of names (of an identified terms), returns usable names. *) +val ident_names : string list -> string list + (*----------------------------------------------------------------------------*) val prop_id_keys : prop_id -> string list * string list (* required , hints *) @@ -80,6 +82,7 @@ val get_propid : prop_id -> string (** Unique identifier of [prop_id] *) val get_legacy : prop_id -> string (** Unique legacy identifier of [prop_id] *) val pp_propid : Format.formatter -> prop_id -> unit (** Print unique id of [prop_id] *) +val user_pred_names: toplevel_predicate -> string list val user_bhv_names: Property.identified_property -> string list val user_prop_names: Property.identified_property -> string list val are_selected_names: string list -> string list -> bool @@ -129,6 +132,9 @@ val mk_assert_id : kernel_function -> stmt -> code_annotation -> prop_id val mk_loop_inv_id : kernel_function -> stmt -> established:bool -> code_annotation -> prop_id +(** Invariant establishment and preservation, in this order *) +val mk_loop_inv : kernel_function -> stmt -> code_annotation -> prop_id * prop_id + (** Invariant used as hypothesis *) val mk_inv_hyp_id : kernel_function -> stmt -> code_annotation -> prop_id @@ -148,8 +154,10 @@ val mk_bhv_from_id : kernel_function -> kinstr -> string list -> funbehavior -> from -> prop_id -(** \from property of function behavior assigns. Must not be [FromAny]. *) -val mk_fct_from_id : kernel_function -> funbehavior -> +(** \from property of function behavior assigns. Must not be [FromAny]. + The boolean indicate whether the function has exit node or not. +*) +val mk_fct_from_id : kernel_function -> bool -> funbehavior -> termination_kind -> from -> prop_id (** disjoint behaviors property. @@ -176,13 +184,18 @@ val mk_stmt_assigns_id : val mk_loop_assigns_id : kernel_function -> stmt -> code_annotation -> from list -> prop_id option -(** function assigns *) -val mk_fct_assigns_id : kernel_function -> funbehavior -> +(** function assigns + The boolean indicate whether the function has exit node or not. +*) +val mk_fct_assigns_id : kernel_function -> bool -> funbehavior -> termination_kind -> from list -> prop_id option val mk_pre_id : kernel_function -> kinstr -> funbehavior -> identified_predicate -> prop_id +val mk_post_id : kernel_function -> kinstr -> funbehavior -> + termination_kind * identified_predicate -> prop_id + val mk_stmt_post_id : kernel_function -> stmt -> funbehavior -> termination_kind * identified_predicate -> prop_id @@ -231,6 +244,7 @@ val merge_assign_info : val mk_loop_assigns_desc : stmt -> from list -> assigns_desc val mk_stmt_assigns_desc : stmt -> from list -> assigns_desc +val mk_stmt_assigns_any_desc : stmt -> assigns_desc val mk_asm_assigns_desc : stmt -> assigns_desc @@ -277,3 +291,33 @@ val subproof_idx : prop_id -> int val get_induction : prop_id -> stmt option (*----------------------------------------------------------------------------*) + +(*----------------------------------------------------------------------------*) + +(** A proof accumulator for a set of related prop_id *) +type proof + +val create_proof : prop_id -> proof +(** to be used only once for one of the related prop_id *) + +val add_proof : proof -> prop_id -> Property.t list -> unit +(** accumulate in the proof the partial proof for this prop_id *) + +val add_invalid_proof : proof -> unit +(** add an invalid proof result ; can not revert a complete proof *) + +val is_composed : proof -> bool +(** whether a proof needs several lemma to be complete *) + +val is_proved : proof -> bool +(** whether all partial proofs have been accumulated or not *) + +val is_invalid : proof -> bool +(** whether an invalid proof result has been registered or not *) + +val target : proof -> Property.t +val dependencies : proof -> Property.t list + +val filter_status : prop_id -> bool + +(*----------------------------------------------------------------------------*) diff --git a/src/plugins/wp/wpStrategy.ml b/src/plugins/wp/wpStrategy.ml index a3e11ddd1d1e2f26fdc086dc4defd558932575a7..1385c9083189e6c15547c5e1bf5ca864367c5012 100644 --- a/src/plugins/wp/wpStrategy.ml +++ b/src/plugins/wp/wpStrategy.ml @@ -196,7 +196,7 @@ let add_prop_fct_pre acc kind kf bhv ~assumes pre = let add_prop_fct_post acc kind kf bhv tkind post = if verify_predicate post.ip_content.tp_kind then begin let id = WpPropId.mk_fct_post_id kf bhv (tkind, post) in - let labels = NormAtLabels.labels_fct_post in + let labels = NormAtLabels.labels_fct_post ~exit:false in let p = Logic_const.pred_of_id_pred post in let p = normalize id labels p in add_prop acc kind id p @@ -234,7 +234,7 @@ let add_prop_stmt_spec_pre acc kind kf s spec = let add_prop_stmt_post acc kind kf s bhv tkind l_post ~assumes post = if verify_predicate post.ip_content.tp_kind then begin let id = WpPropId.mk_stmt_post_id kf s bhv (tkind, post) in - let labels = NormAtLabels.labels_stmt_post ~kf s l_post in + let labels = NormAtLabels.labels_stmt_post_l ~kf s l_post in let p = Logic_const.pred_of_id_pred post in let p = normalize id labels ?assumes p in add_prop acc kind id p @@ -261,14 +261,14 @@ let add_prop_call_pre acc kind id ~assumes pre = let add_prop_call_post acc kind called_kf bhv tkind ~assumes post = let id = WpPropId.mk_fct_post_id called_kf bhv (tkind, post) in - let labels = NormAtLabels.labels_fct_post in + let labels = NormAtLabels.labels_fct_post ~exit:false in let p = Logic_const.pred_of_id_pred post in let p = normalize id labels ~assumes p in add_prop acc kind id p let add_prop_assert acc kind kf s ca p = let id = WpPropId.mk_assert_id kf s ca in - let labels = NormAtLabels.labels_assert_before ~kf s in + let labels = NormAtLabels.labels_assert ~kf s in let p = normalize id labels p in add_prop acc kind id p @@ -410,7 +410,7 @@ let add_stmt_spec_assigns_hyp acc kf s l_post spec = | None -> add_assigns_any acc Ahyp (WpPropId.mk_stmt_any_assigns_info s) | Some id -> - let labels = NormAtLabels.labels_stmt_assigns ~kf s l_post in + let labels = NormAtLabels.labels_stmt_assigns_l ~kf s l_post in let assigns = NormAtLabels.preproc_assigns labels assigns in let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in add_assigns acc Ahyp id a_desc @@ -439,7 +439,7 @@ let add_call_assigns_hyp acc kf_caller s ~called_kf l_post spec_opt = add_assigns_any acc (AcallHyp called_kf) asgn | Some pid -> let kf = kf_caller in - let labels = NormAtLabels.labels_stmt_assigns ~kf s l_post in + let labels = NormAtLabels.labels_stmt_assigns_l ~kf s l_post in let assigns = NormAtLabels.preproc_assigns labels assigns in let a_desc = WpPropId.mk_stmt_assigns_desc s assigns in add_assigns acc (AcallHyp called_kf) pid a_desc @@ -471,13 +471,14 @@ let add_fct_bhv_assigns_hyp acc kf tkind b = match b.b_assigns with let id = WpPropId.mk_kf_any_assigns_info () in add_assigns_any acc Ahyp id | Writes assigns -> - let id = WpPropId.mk_fct_assigns_id kf b tkind assigns in + let has_exit = Cil2cfg.has_exit (Cil2cfg.get kf) in + let id = WpPropId.mk_fct_assigns_id kf has_exit b tkind assigns in match id with | None -> let id = WpPropId.mk_kf_any_assigns_info () in add_assigns_any acc Ahyp id | Some id -> - let labels = NormAtLabels.labels_fct_assigns in + let labels = NormAtLabels.labels_fct_assigns ~exit:false in let assigns' = NormAtLabels.preproc_assigns labels assigns in let a_desc = WpPropId.mk_kf_assigns_desc assigns' in add_assigns acc Ahyp id a_desc diff --git a/src/plugins/wp/wp_parameters.ml b/src/plugins/wp/wp_parameters.ml index 96bdd3930df12e2b045c09cd426ed8c8ce5f40c3..38c898654f4ac51b3d37cb85bbe5d1e2c80b11e4 100644 --- a/src/plugins/wp/wp_parameters.ml +++ b/src/plugins/wp/wp_parameters.ml @@ -44,7 +44,7 @@ let has_dkey (k:category) = is_debug_key_enabled k (* --- WP Generation --- *) (* ------------------------------------------------------------------------ *) -let wp_generation = add_group "Goal Selection" +let wp_generation = add_group "Goal Generator" let () = Parameter_customize.set_group wp_generation let () = Parameter_customize.do_not_save () @@ -55,6 +55,21 @@ module WP = end) let () = on_reset WP.clear +let () = Parameter_customize.set_group wp_generation +module Legacy = + True(struct + let option_name = "-wp-legacy" + let help = "Use legacy generator engine." + end) + +let () = Parameter_customize.set_group wp_generation +module Dump = + Action(struct + let option_name = "-wp-dump" + let help = "Dump WP calculus graph." + end) +let () = on_reset Dump.clear + let () = Parameter_customize.set_group wp_generation let () = Parameter_customize.do_not_save () module Functions = diff --git a/src/plugins/wp/wp_parameters.mli b/src/plugins/wp/wp_parameters.mli index a7ce251dd60d391185176f047d52420b314c94ba..2bb0a1fea14799a4fc2f5ed9cb40d536343baa8c 100644 --- a/src/plugins/wp/wp_parameters.mli +++ b/src/plugins/wp/wp_parameters.mli @@ -39,6 +39,8 @@ val iter_kf : (Kernel_function.t -> unit) -> unit (** {2 Goal Selection} *) module WP : Parameter_sig.Bool +module Dump : Parameter_sig.Bool +module Legacy : Parameter_sig.Bool module Behaviors : Parameter_sig.String_list module Properties : Parameter_sig.String_list module StatusAll : Parameter_sig.Bool diff --git a/src/plugins/wp/wpo.ml b/src/plugins/wp/wpo.ml index 1490b3613ebf5b7eac3dc7cb2efd8cbbd6e3ae0c..0d0bf47c4f8059caf2d74312150bcc67f866f3ae 100644 --- a/src/plugins/wp/wpo.ml +++ b/src/plugins/wp/wpo.ml @@ -578,7 +578,7 @@ type system = { mutable wpo_ip : WPOset.t Pmap.t ; (* ip -> WPOs *) mutable age : int WPOmap.t ; (* wpo -> age *) mutable results : Results.t WPOmap.t ; (* results collector *) - proofs : WpAnnot.proof Hproof.t ; (* proof collector *) + proofs : WpPropId.proof Hproof.t ; (* proof collector *) } let create_system () = @@ -707,11 +707,11 @@ let get_proof g = try let proof = Hproof.find system.proofs (proof g target) in if is_smoke_test g then - if WpAnnot.is_proved proof then `Failed else - if WpAnnot.is_invalid proof then `Passed else + if WpPropId.is_proved proof then `Failed else + if WpPropId.is_invalid proof then `Passed else `Unknown else - if WpAnnot.is_proved proof then `Passed else `Unknown + if WpPropId.is_proved proof then `Passed else `Unknown with Not_found -> `Unknown in status , target @@ -719,7 +719,7 @@ let find_proof system g = let pi = proof g (WpPropId.property_of_id g.po_pid) in try Hproof.find system.proofs pi with Not_found -> - let proof = WpAnnot.create_proof g.po_pid in + let proof = WpPropId.create_proof g.po_pid in Hproof.add system.proofs pi proof ; proof let clear_results g = @@ -746,18 +746,18 @@ let set_result g p r = let smoke = is_smoke_test g in let proof = find_proof system g in let emitter = WpContext.get_emitter g.po_model in - let target = WpAnnot.target proof in - let unproved = not (WpAnnot.is_proved proof) in + let target = WpPropId.target proof in + let unproved = not (WpPropId.is_proved proof) in if VCS.is_valid r then - WpAnnot.add_proof proof g.po_pid (get_depend g) + WpPropId.add_proof proof g.po_pid (get_depend g) else if smoke then - WpAnnot.add_invalid_proof proof ; - let proved = WpAnnot.is_proved proof in + WpPropId.add_invalid_proof proof ; + let proved = WpPropId.is_proved proof in let status = if smoke then if proved then Property_status.False_if_reachable (* All goals SAT *) - else if WpAnnot.is_invalid proof + else if WpPropId.is_invalid proof then Property_status.True (* Some goal is UNSAT *) else Property_status.Dont_know (* Not finished yet *) else @@ -765,7 +765,7 @@ let set_result g p r = then Property_status.True else Property_status.Dont_know in - let hyps = if smoke then [] else WpAnnot.dependencies proof in + let hyps = if smoke then [] else WpPropId.dependencies proof in Property_status.emit emitter ~hyps target status ; if smoke && unproved && proved then WpReached.set_doomed emitter g.po_pid ; end @@ -988,3 +988,21 @@ let get_files w = ) results [] in descr_files @ result_files + +(* -------------------------------------------------------------------------- *) +(* --- Generators --- *) +(* -------------------------------------------------------------------------- *) + +class type generator = + object + method model : WpContext.model + method compute_ip : Property.t -> t Bag.t + method compute_call : stmt -> t Bag.t + method compute_main : + ?fct:Wp_parameters.functions -> + ?bhv:string list -> + ?prop:string list -> + unit -> t Bag.t + end + +(* -------------------------------------------------------------------------- *) diff --git a/src/plugins/wp/wpo.mli b/src/plugins/wp/wpo.mli index f2c11d5a7917e4ebdc2ded2d1b3deaaec7e582cd..98e14b7efc254befdda2e7a1c6eecfb4942c2f8f 100644 --- a/src/plugins/wp/wpo.mli +++ b/src/plugins/wp/wpo.mli @@ -210,3 +210,29 @@ val pp_goal_flow : Format.formatter -> t -> unit (** Dynamically exported. *) val prover_of_name : string -> prover option + +(* -------------------------------------------------------------------------- *) +(* --- Generators --- *) +(* -------------------------------------------------------------------------- *) + +(** VC Generator interface. *) + +class type generator = + object + method model : WpContext.model + (** Generate VCs for the given Property. *) + method compute_ip : Property.t -> t Bag.t + (** Generate VCs for call preconditions at the given statement. *) + method compute_call : stmt -> t Bag.t + (** Generate VCs for all functions + matching provided behaviors and property names. + For `~bhv` and `~prop` optional arguments, + default and empty list means {i all} properties. *) + method compute_main : + ?fct:Wp_parameters.functions -> + ?bhv:string list -> + ?prop:string list -> + unit -> t Bag.t + end + +(* -------------------------------------------------------------------------- *)