Skip to content
Snippets Groups Projects
calculus.ml 28.2 KiB
Newer Older
(**************************************************************************)
(*                                                                        *)
(*  This file is part of WP plug-in of Frama-C.                           *)
(*                                                                        *)
(*  Copyright (C) 2007-2020                                               *)
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
(*    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 computation using the CFG *)

open Cil_types
open Cil_datatype

module Cfg (W : Mcfg.S) = struct

  let dkey = Wp_parameters.register_category "calculus" (* Debugging key *)
  let debug fmt = Wp_parameters.debug ~dkey fmt

  (** Before storing something at a program point, we have to process the label
   * at that point. *)
  let do_labels wenv e obj =
    let do_lab s o l = W.label wenv s l o in
    let obj = do_lab None obj Clabels.here in
    let stmt = Cil2cfg.get_edge_stmt e in
    let labels = Cil2cfg.get_edge_labels e in
    List.fold_left (do_lab stmt) obj labels

  let add_hyp wenv obj h =
    debug "add hyp %a@." WpPropId.pp_pred_info h;
    W.add_hyp wenv h obj

  let add_goal wenv obj g =
    debug "add goal %a@." WpPropId.pp_pred_info g;
    W.add_goal wenv g obj
  (*[LC] Adding scopes for loop invariant preservation: WHY ???? *)
  (*[LC] Nevertheless, if required, this form should be used (BTS #1462)

    let open_scope wenv formals blocks =
    List.fold_right
      (fun b obj -> W.scope wenv b.blocals Mcfg.SC_Block_out obj)
      blocks
      (W.scope wenv formals Mcfg.SC_Function_out W.empty)


    match WpPropId.is_loop_preservation (fst g) with
      | None -> W.add_goal wenv g obj
      | Some stmt ->
        debug "add scope for loop preservation %a@." WpPropId.pp_pred_info g ;
        let blocks = Kernel_function.find_all_enclosing_blocks stmt in
        let kf = Kernel_function.find_englobing_kf stmt in
        let formals = Kernel_function.get_formals kf in
        W.merge wenv (W.add_goal wenv g (open_scope wenv formals blocks)) obj
  *)

  let add_assigns_goal wenv obj g_assigns = match g_assigns with
    | WpPropId.AssignsAny _ | WpPropId.NoAssignsInfo -> obj
    | WpPropId.AssignsLocations a ->
        debug "add assign goal (@[%a@])@."
          WpPropId.pretty (WpPropId.assigns_info_id a);
        W.add_assigns wenv  a obj

  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
        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
        Some a.WpPropId.a_label, obj
    | WpPropId.NoAssignsInfo -> None, obj

  (** detect if the computation of the result at [edge] is possible,
   * or if it will loop. If [strategy] are provide,
   * cut are done on edges with cut properties,
   * and if not, cut are done on loop node back edge if any.
   * TODO: maybe this should be done while building the strategy ?
   * *)
  exception Stop of Cil2cfg.edge
  let test_edge_loop_ok cfg strategy edge =
    debug "[test_edge_loop_ok] (%s strategy) for %a"
      (match strategy with None -> "without" | Some _ -> "with")
      Cil2cfg.pp_edge edge;
    let rec collect_edge_preds set e =
      let cut =
        match strategy with None ->  Cil2cfg.is_back_edge e
                          | Some strategy ->
                              let e_annots = WpStrategy.get_annots strategy e in
                              (WpStrategy.get_cut e_annots <> [])
      in
      if cut then () (* normal loop cut *)
      else if Cil2cfg.Eset.mem e set
      then (* e is already in set : loop without cut ! *)
        raise (Stop e)
      else (* add e to set and continue with its preds *)
        let set = Cil2cfg.Eset.add e set in
        let preds = Cil2cfg.pred_e cfg (Cil2cfg.edge_src e) in
        List.iter (collect_edge_preds set) preds
    in
    try
      let _ = collect_edge_preds Cil2cfg.Eset.empty edge in
      debug "[test_edge_loop_ok] ok.";
      true
    with Stop e ->
      begin
        debug "[test_edge_loop_ok] loop without cut detected at %a"
          Cil2cfg.pp_edge e;
        false
      end

  (** to store the results of computations :
   * we store a result for each edge, and also a list of proof obligations.
   *
   * Be careful that there are two modes of computation :
   * the first one ([Pass1]) is used to prove the establishment of properties
   * while the second (after [change_mode_if_needed]) prove the preservation.
   * See {!R.set} for more details.
   * *)
  module R : sig
    type t
    val empty : Cil2cfg.t -> t
    val is_pass1 : t -> bool
    val change_mode_if_needed : t -> unit
    val find : t -> Cil2cfg.edge -> W.t_prop
    val set :  WpStrategy.strategy -> W.t_env ->
      t -> Cil2cfg.edge -> W.t_prop -> W.t_prop
    val add_oblig : t -> Clabels.c_label -> W.t_prop -> unit
    val add_memo : t -> Cil2cfg.edge -> W.t_prop -> unit
  end =
  struct
    type t_mode = Pass1 | Pass2

    module HE = Cil2cfg.HE (struct type t = W.t_prop option end)

    module LabObligs : sig
      type t
      val empty : t
      val is_empty : t -> bool
      val get_of_label : t -> Clabels.c_label -> W.t_prop list
      val get_of_edge : t -> Cil2cfg.edge -> W.t_prop list
      val add_to_label : t -> Clabels.c_label -> W.t_prop -> t
      val add_to_edge : t -> Cil2cfg.edge -> W.t_prop -> t
    end = struct

      type key = Olab of Clabels.c_label | Oedge of Cil2cfg.edge

      let cmp_key k1 k2 = match k1, k2 with
        | Olab l1, Olab l2 when Clabels.equal l1 l2 -> true
        | Oedge e1, Oedge e2 when Cil2cfg.same_edge e1 e2 -> true
        | _ -> false

      (* TODOopt: could have a sorted list... *)
      type t = (key * W.t_prop list) list

      let empty = []

      let is_empty obligs = (obligs = [])

      let add obligs k obj =
        let rec aux l_obligs = match l_obligs with
          | [] -> (k, [obj])::[]
          | (k', obligs)::tl when cmp_key k k' ->
              (k, obj::obligs)::tl
          | o::tl -> o::(aux tl)
        in aux obligs

      let add_to_label obligs label obj = add obligs (Olab label) obj

      let add_to_edge obligs e obj = add obligs (Oedge e) obj

      let get obligs k =
        let rec aux l_obligs = match l_obligs with
          | [] -> []
          | (k', obligs)::_ when cmp_key k k' -> obligs
          | _::tl -> aux tl
        in aux obligs

      let get_of_label obligs label = get obligs (Olab label)

      let get_of_edge obligs e = get obligs (Oedge e)

    end

    type t = {
      mutable mode : t_mode ;
      cfg: Cil2cfg.t;
      tbl : HE.t ;
      mutable memo : LabObligs.t;
      mutable obligs : LabObligs.t;
    }

    let empty cfg =
      debug "start computing (pass 1)@.";
      { mode = Pass1; cfg = cfg; tbl = HE.create 97 ;
        obligs = LabObligs.empty ; memo = LabObligs.empty ;}

    let is_pass1 res = (res.mode = Pass1)

    let add_oblig res label obj =
      debug "add proof obligation at label %a =@.  @[<hov2>  %a@]@."
        Clabels.pretty label W.pretty obj;
      res.obligs <- LabObligs.add_to_label (res.obligs) label obj

    let add_memo res e obj =
      debug "Memo goal for Pass2 at %a=@.  @[<hov2>  %a@]@."
        Cil2cfg.pp_edge e W.pretty obj;
      res.memo <- LabObligs.add_to_edge (res.memo) e obj

    let find res e =
      let obj = HE.find res.tbl e in
      match obj with None ->
        Wp_parameters.warning "find edge annot twice (%a) ?"
          Cil2cfg.pp_edge e;
        raise Not_found
                   | Some obj ->
                       if (res.mode = Pass2)
                       && (List.length
                             (Cil2cfg.pred_e res.cfg (Cil2cfg.edge_src e)) < 2) then
                         begin
                           (* it should be used once only : can free it *)
                           HE.replace res.tbl e None;
                           debug "clear edge %a@." Cil2cfg.pp_edge e
                         end;
                       obj

    (** If needed, clear wp table to compute Pass2.
     * If nothing has been stored in res.memo, there is nothing to do. *)
    let change_mode_if_needed res =
      if LabObligs.is_empty res.memo then ()
      else
        begin
          debug "change to Pass2 (clear wp table)@.";
          begin try
              let e_start = Cil2cfg.start_edge res.cfg in
              let start_goal = find res e_start in
              add_memo res e_start start_goal
            with Not_found -> ()
          end;
          HE.clear res.tbl;
          (* move memo obligs of Pass1 to obligs for Pass2 *)
          res.obligs <- res.memo;
          res.memo <- LabObligs.empty;
          res.mode <- Pass2
        end

    let collect_oblig wenv res e obj =
      let labels = Cil2cfg.get_edge_labels e in
      let add obj obligs =
        List.fold_left (fun obj o -> W.merge wenv o obj) obj obligs
      in
      let obj =
        try
          debug "get proof obligation at edge %a@." Cil2cfg.pp_edge e;
          let obligs = LabObligs.get_of_edge res.obligs e in
          add obj obligs
        with Not_found -> obj
      in
      let add_lab_oblig obj label =
        try
          debug "get proof obligation at label %a@." Clabels.pretty label;
          let obligs = LabObligs.get_of_label res.obligs label in
          add obj obligs
        with Not_found -> obj
      in
      let obj = List.fold_left add_lab_oblig obj labels in
      obj


    (** We have found some assigns hypothesis in the strategy :
     * it means that we skip the corresponding bloc, ie. we directly compute
     * the result before the block : (forall assigns. P),
     * and continue with empty. *)
    let use_assigns wenv res obj h_assigns =
      let lab, obj = add_assigns_hyp wenv obj h_assigns in
      match lab with
      | None -> obj
      | Some label -> add_oblig res label obj; W.empty

    (** store the result p for the computation of the edge e.
     *
     * - In Compute mode :
        if we have some hyps H about this edge, store H => p
        if we have some goal G about this edge, store G /\ p
        if we have annotation B to be used as both H and G, store B /\ B=>P
        We also have to add H and G from HI (invariants computed in Pass1 mode)
        So finally, we build : [ H => [ BG /\ (BH => (G /\ P)) ] ]
    *)
    let set strategy wenv res e obj =
      try
        match (HE.find res.tbl e) with
        | None -> raise Not_found
        | Some obj -> obj
      (* cannot warn here because it can happen with CUT properties.
       * We could check that obj is the same thing than the founded result *)
      (* Wp_parameters.fatal "strange loop at %a ?" Cil2cfg.pp_edge e *)
      with Not_found ->
        begin
          let e_annot = WpStrategy.get_annots strategy e in
          let h_prop = WpStrategy.get_hyp_only e_annot in
          let g_prop = WpStrategy.get_goal_only e_annot in
          let bh_prop, bg_prop = WpStrategy.get_both_hyp_goals e_annot in
          let h_assigns = WpStrategy.get_asgn_hyp e_annot in
          let g_assigns = WpStrategy.get_asgn_goal e_annot in
          (* get_cut is ignored : see get_wp_edge *)
          let obj = collect_oblig wenv res e obj in
          let is_loop_head =
            match Cil2cfg.node_type (Cil2cfg.edge_src e) with
            | Cil2cfg.Vloop (Some _, _) -> true
            | _ -> false
          in
          let compute ~goal obj =
            let local_add_goal obj g =
              if goal then add_goal wenv obj g else obj
            in
            let obj = List.fold_left (local_add_goal) obj g_prop in
            let obj = List.fold_left (add_hyp wenv) obj bh_prop in
            let obj =
              if goal then add_assigns_goal wenv obj g_assigns else obj
            in
            let obj = List.fold_left (local_add_goal) obj bg_prop in
            let obj = List.fold_left (add_hyp wenv) obj h_prop in
            obj
          in
          let obj = match res.mode with
            | Pass1 -> compute ~goal:true obj
            | Pass2 -> compute ~goal:false obj
          in
          let obj = do_labels wenv e obj in
          let obj =
            if is_loop_head then obj (* assigns used in [wp_loop] *)
            else use_assigns wenv res obj h_assigns
          in
          debug "[set_wp_edge] %a@." Cil2cfg.pp_edge e;
          debug " = @[<hov2>  %a@]@." W.pretty obj;
          Format.print_flush ();
          HE.replace res.tbl e (Some obj);
          find res e (* this should give back obj, but also do more things *)
        end

  end (* module R *)


  let use_loop_assigns strategy wenv e obj =
    let e_annot = WpStrategy.get_annots strategy e in
    let h_assigns = WpStrategy.get_asgn_hyp e_annot in
    let label, obj = add_assigns_hyp wenv obj h_assigns in
    match label with Some _ -> obj
                   | None -> assert false (* we should have assigns hyp for loops !*)

  (** Compute the result for edge [e] which goes to the loop node [nloop].
   * So [e] can be either a back_edge or a loop entry edge.
   * Be very careful not to make an infinite loop by calling [get_loop_head]...
   * *)
  let wp_loop ((_, cfg, strategy, _, wenv)) nloop e get_loop_head =
    let loop_with_quantif () =
      if Cil2cfg.is_back_edge e then
        (* Be careful not to use get_only_succ here (infinite loop) *)
        (debug "[wp_loop] cut at back edge";
         W.empty)
      else (* edge going into the loop from outside : quantify *)
        begin
          debug "[wp_loop] quantify";
          let obj = get_loop_head nloop in
          let head = match Cil2cfg.succ_e cfg nloop with
            | [h] -> h
            | _ -> assert false (* already detected in [get_loop_head] *)
          in use_loop_assigns strategy wenv head obj
        end
    in
    (*
    if WpStrategy.new_loop_computation strategy
       && R.is_pass1 res
       && loop_with_cut cfg strategy nloop
    then
      loop_with_cut_pass1 ()
    else (* old mode or no inv or pass2 *)
    *)
    match Cil2cfg.node_type nloop with
    | Cil2cfg.Vloop (Some true, _) -> (* natural loop (has back edges) *)
        loop_with_quantif ()
    | _ -> (* TODO : print info about the loop *)
        Wp_error.unsupported
          "non-natural loop without invariant property."

  type callenv = {
    pre_annots : WpStrategy.t_annots ;
    post_annots : WpStrategy.t_annots ;
    exit_annots : WpStrategy.t_annots ;
  }

  let callenv cfg strategy v =
    let eb = match Cil2cfg.pred_e cfg v with e::_ -> e | _ -> assert false in
    let en, ee = Cil2cfg.get_call_out_edges cfg v in
    {
      pre_annots = WpStrategy.get_annots strategy eb ;
      post_annots = WpStrategy.get_annots strategy en ;
      exit_annots = WpStrategy.get_annots strategy ee ;
    }

  let wp_call_any wenv cenv ~p_post ~p_exit =
    let obj = W.merge wenv p_post p_exit in
    let call_asgn = WpStrategy.get_call_asgn cenv.post_annots None in
    let lab, obj = add_assigns_hyp wenv obj call_asgn in
    match lab with
    | Some _ -> obj
    | None -> assert false

  let add_call_post wenv annots kf obj =
    List.fold_left (add_goal wenv) obj (WpStrategy.get_call_post annots kf)

  let wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit =
    let call_asgn = WpStrategy.get_call_asgn cenv.post_annots (Some kf) in
    let assigns = match call_asgn with
      | WpPropId.AssignsLocations (_, asgn_body) ->
          asgn_body.WpPropId.a_assigns
      | WpPropId.AssignsAny _ -> WritesAny
      | WpPropId.NoAssignsInfo -> assert false (* see above *)
    in
    let p_post = add_call_post wenv cenv.post_annots kf p_post in
    let p_exit = add_call_post wenv cenv.exit_annots kf p_exit in
    let pre_hyp, pre_goals = WpStrategy.get_call_pre cenv.pre_annots kf in
    let obj = W.call wenv stmt lval kf args
        ~pre:(pre_hyp)
        ~post:((WpStrategy.get_call_hyp cenv.post_annots kf))
        ~pexit:((WpStrategy.get_call_hyp cenv.exit_annots kf))
        ~assigns ~p_post ~p_exit in
    if precond
    then W.call_goal_precond wenv stmt kf args ~pre:(pre_goals) obj
    else obj

  let wp_calls ((_, cfg, strategy, _, wenv)) res v stmt
      lval call args p_post p_exit
    =
    debug "[wp_calls] %a@." Cil2cfg.pp_call_type call;
    let cenv = callenv cfg strategy v in
    match call with
    | Cil2cfg.Static kf ->
        let precond =
          WpStrategy.is_default_behavior strategy && R.is_pass1 res
        in
        wp_call_kf wenv cenv stmt lval kf args precond ~p_post ~p_exit
    | Cil2cfg.Dynamic fct ->
        let bhv = WpStrategy.behavior_name_of_strategy strategy in
        match Dyncall.get ?bhv stmt with
        | None -> wp_call_any wenv cenv ~p_post ~p_exit
        | Some (prop,calls) ->
            let precond = R.is_pass1 res in
            let do_call kf =
              let wp = wp_call_kf wenv cenv stmt lval
                  kf args precond ~p_post ~p_exit in
              kf , wp in
            let pid = WpPropId.mk_property prop in
            W.call_dynamic wenv stmt pid fct (List.map do_call calls)

  let wp_stmt wenv s obj = match s.skind with
    | Return (r, _) -> W.return wenv s r obj
    | Instr i ->
        begin match i with
          | Local_init (vi, AssignInit i, _) -> W.init wenv vi (Some i) obj
          | Local_init (_, ConsInit _, _) -> assert false
          | (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
          | (Call _) -> assert false
          | Skip _ | Code_annot _ -> obj
        end
    | Break _ | Continue _ | Goto _ -> obj
    | Loop _-> (* this is not a real loop (exit before looping)
                  just ignore it ! *) obj
    | If _ -> assert false
    | Switch _-> assert false
    | Block _-> assert false
    | UnspecifiedSequence _-> assert false
    | TryExcept _ | TryFinally _ | Throw _ | TryCatch _ -> assert false

  let wp_scope wenv vars scope obj =
    debug "[wp_scope] %s : %a@."
      (match scope with
       | Mcfg.SC_Global -> "global"
       | Mcfg.SC_Block_in -> "block in"
       | Mcfg.SC_Block_out -> "block out"
Loïc Correnson's avatar
Loïc Correnson committed
       | Mcfg.SC_Frame_in -> "frame in"
       | Mcfg.SC_Frame_out -> "frame out" )
      (Pretty_utils.pp_list  ~sep:", " Printer.pp_varinfo) vars;
    match scope with
    | Mcfg.(SC_Block_in | SC_Block_out) when vars = [] -> obj
    | _ -> W.scope wenv vars scope obj

  (** @return the WP stored for edge [e]. Compute it if it is not already
   * there and store it. Also handle the Acut annotations. *)
  let rec get_wp_edge ((_kf, cfg, strategy, res, wenv) as env) e =
    Db.yield ();
    let v = Cil2cfg.edge_dst e in
    debug "[get_wp_edge] get wp before %a@." Cil2cfg.pp_node v;
    try
      let res = R.find res e in
      debug "[get_wp_edge] %a already computed@." Cil2cfg.pp_node v;
      res
    with Not_found ->
      (* Notice that other hyp and goal are handled in R.set as usual *)
      let cutp =
        if R.is_pass1 res
        then WpStrategy.get_cut (WpStrategy.get_annots strategy e)
        else []
      in
      match cutp with
      | [] ->
          let wp = compute_wp_edge env e in
          R.set strategy wenv res e wp
      | cutp ->
          debug "[get_wp_edge] cut at node %a@." Cil2cfg.pp_node v;
          let add_cut_goal (g,p) acc =
            if g then add_goal wenv acc p else acc
          in
          let edge_annot = List.fold_right add_cut_goal cutp W.empty in
          (* put cut goal properties as goals in e if any, else true *)
          let edge_annot = R.set strategy wenv res e edge_annot in
          let wp = compute_wp_edge env e in
          let add_cut_hyp (_,p) acc = add_hyp wenv acc p in
          let oblig = List.fold_right add_cut_hyp cutp wp in
          (* TODO : we could add hyp to the oblig if we have some in strategy *)
          let oblig = W.loop_step oblig in
          if test_edge_loop_ok cfg None e
          then R.add_memo res e oblig
          else R.add_oblig res Clabels.pre (W.close wenv oblig);
          edge_annot

  and get_only_succ env cfg v = match Cil2cfg.succ_e cfg v with
    | [e'] -> get_wp_edge env e'
    | ls ->
        Wp_parameters.debug "CFG node %a has %d successors instead of 1@."
          Cil2cfg.pp_node v (List.length ls);
        Wp_error.unsupported "strange loop(s)."

  and compute_wp_edge ((kf, cfg, _annots, res, wenv) as env) e =
    let v = Cil2cfg.edge_dst e in
    debug "[compute_edge] before %a go...@." Cil2cfg.pp_node v;
    let old_loc = Cil.CurrentLoc.get () in
    let () = match Cil2cfg.node_stmt_opt v with
      | Some s -> Cil.CurrentLoc.set (Stmt.loc s)
      | None -> ()
    in
    let formals = Kernel_function.get_formals kf in
    let res = match Cil2cfg.node_type v with
      | Cil2cfg.Vstart ->
          Wp_parameters.debug "No CFG edge can lead to Vstart";
          Wp_error.unsupported "strange CFGs."
      | Cil2cfg.VfctIn ->
          let obj = get_only_succ env cfg v in
          let obj = wp_scope wenv [] Mcfg.SC_Global obj in
          obj
      | Cil2cfg.VblkIn (Cil2cfg.Bfct, b) ->
          let obj = get_only_succ env cfg v in
          let obj = wp_scope wenv b.blocals Mcfg.SC_Block_in obj in
Loïc Correnson's avatar
Loïc Correnson committed
          let obj = wp_scope wenv formals Mcfg.SC_Frame_in obj in
          obj
      | Cil2cfg.VblkOut (Cil2cfg.Bfct, b) ->
          let obj = get_only_succ env cfg v in
          let obj = wp_scope wenv b.blocals Mcfg.SC_Block_out obj in
          obj
      | Cil2cfg.VblkOut _ | Cil2cfg.VblkIn _ ->
          get_only_succ env cfg v
      | Cil2cfg.Vstmt s ->
          let obj = get_only_succ env cfg v in
          wp_stmt wenv s obj
      | Cil2cfg.Vcall (stmt, lval, fct, args) ->
          let en, ee = Cil2cfg.get_call_out_edges cfg v in
          let objn = get_wp_edge env en in
          let obje = get_wp_edge env ee in
          wp_calls env res v stmt lval fct args objn obje
      | Cil2cfg.Vtest (true, s, c) ->
          let et, ef = Cil2cfg.get_test_edges cfg v in
          let t_obj = get_wp_edge env et in
          let f_obj = get_wp_edge env ef in
          W.test wenv s c t_obj f_obj
      | Cil2cfg.Vtest (false, _, _) ->
          get_only_succ env cfg v
      | Cil2cfg.Vswitch (s, e) ->
          let cases, def_edge = Cil2cfg.get_switch_edges cfg v in
          let cases_obj = List.map (fun (c,e) -> c, get_wp_edge env e) cases in
          let def_obj = get_wp_edge env def_edge in
          W.switch wenv s e cases_obj def_obj
      | Cil2cfg.Vloop _ | Cil2cfg.Vloop2 _ ->
          let get_loop_head = fun n -> get_only_succ env cfg n in
          wp_loop env v e get_loop_head
      | Cil2cfg.VfctOut
      | Cil2cfg.Vexit ->
          let obj = get_only_succ env cfg v (* exitpost / postcondition *) in
Loïc Correnson's avatar
Loïc Correnson committed
          wp_scope wenv formals Mcfg.SC_Frame_out obj
      | Cil2cfg.Vend ->
          W.empty
          (* LC : unused entry point...
                    let obj = W.empty in
                    wp_scope wenv formals Mcfg.SC_Function_after_POST obj
          *)
    in
    let Cil2cfg.{ b_closed ; b_opened } = Cil2cfg.block_scope_for_edge cfg e in
    let do_block sc res b = wp_scope wenv b.blocals sc res in
    let res = List.fold_left (do_block Mcfg.SC_Block_in) res b_opened in
    let res = List.fold_left (do_block Mcfg.SC_Block_out) res b_closed in
    debug "[compute_edge] before %a done@." Cil2cfg.pp_node v;
    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

  let get_weakest_precondition cfg ((kf, _g, strategy, res, wenv) as env) =
    debug "[wp-cfg] start Pass1";
    Cil2cfg.iter_edges (fun e -> ignore (get_wp_edge env e)) cfg ;
    debug "[wp-cfg] end of Pass1";
    R.change_mode_if_needed res;
    (* Notice that [get_wp_edge] will start Pass2 if needed,
     * 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 = match WpStrategy.strategy_kind strategy with
      | WpStrategy.SKannots -> obj
      | WpStrategy.SKfroms info ->
          let pre = info.WpStrategy.get_pre () in
          let pre = WpStrategy.get_hyp_only pre in
          W.build_prop_of_from wenv pre obj
    in
    debug "before close: %a@." W.pretty obj;
    W.close wenv obj

  let compute cfg strategy =
    debug "[wp-cfg] start computing with the strategy for %a"
      WpStrategy.pp_info_of_strategy strategy;
    if WpStrategy.strategy_has_prop_goal strategy
    || WpStrategy.strategy_has_asgn_goal strategy then
      try
        let kf = Cil2cfg.cfg_kf cfg in
        if Cil2cfg.strange_loops cfg <> [] then
          Wp_error.unsupported "non natural loop(s)" ;
        let lvars = match WpStrategy.strategy_kind strategy with
          | WpStrategy.SKfroms info -> info.WpStrategy.more_vars
          | _ -> [] in
        let wenv = W.new_env ~lvars kf in
        let res = R.empty cfg in
        let env = (kf, cfg, strategy, res, wenv) in
        List.iter
          (fun (pid,thm) -> W.add_axiom pid thm)
          (WpStrategy.global_axioms strategy) ;
        let goal = get_weakest_precondition cfg env in
        debug "[get_weakest_precondition] %a@." W.pretty goal;
        let pp_cfg_edges_annot res fmt e =
          try W.pretty fmt (R.find res e)
          with Not_found -> Format.fprintf fmt "<released>"
        in
        let annot_cfg = pp_cfg_edges_annot res in
        debug "[wp-cfg] computing done.";
        [goal] , annot_cfg
      with Wp_error.Error (_, msg) ->
        Wp_parameters.warning "@[calculus failed on strategy@ @[for %a@]@ \
                               because@ %s (abort)@]"
          WpStrategy.pp_info_of_strategy strategy
          msg;
        let annot_cfg fmt _e = Format.fprintf fmt "" in
        [], annot_cfg
    else
      begin
        debug "[wp-cfg] no goal in this strategy : ignore.";
        let annot_cfg fmt _e = Format.fprintf fmt "" in
        [], annot_cfg
      end

end