From 4a8862f91a7adb40f8461416f1536129b5c6968b Mon Sep 17 00:00:00 2001 From: Thibault Martin <thi.martin.pro@pm.me> Date: Mon, 11 Sep 2023 15:02:04 +0200 Subject: [PATCH] Generic function for collect, fix clauses clauses combination - Hashtbl stores a bool to remember if bhv has assumes clauses - When looking for clauses to combine for default behavior, we search in that order (and stop if we found some clauses) : - unguarded behaviors (no assumes). - complete behaviors (each one of them needs to be in our table). - all behaviors in our table. It is a generic function used by {!Generator.get_clauses}. --- src/kernel_internals/typing/populate_spec.ml | 132 ++++++++++++------- 1 file changed, 86 insertions(+), 46 deletions(-) diff --git a/src/kernel_internals/typing/populate_spec.ml b/src/kernel_internals/typing/populate_spec.ml index 119668f8d36..b57588753f9 100644 --- a/src/kernel_internals/typing/populate_spec.ml +++ b/src/kernel_internals/typing/populate_spec.ml @@ -83,15 +83,70 @@ let emitter_status = let emit_status status ppt = Property_status.emit emitter_status ~hyps:[] ppt status -(* Generic completes function for {!Generator.completes}. *) -let completes_generic (type clause) completes table = +(* Generic function to fill the clause hashtbl. + [filter] returns all the clauses of behavior [b] and depend on the type of + clause. [is_empty] correspond to each [Generator.is_empty] function. + The table also remembers for each behavior if it has assumes clause or not. +*) +let collect_behaviors_generic filter is_empty spec = + let table = Hashtbl.create 17 in + let iter b = + let clauses = filter b in + if not (is_empty clauses) then + Hashtbl.add table b.b_name (b.b_assumes = [], clauses) + in + List.iter iter spec.spec_behavior; + table + +(* Filter [table] to keep behaviors without guard (assume) clause. *) +let unguarded table = + let filter _ (unguarded, clause) acc = + if unguarded then clause :: acc else acc + in + match Hashtbl.fold filter table [] with + | [] -> None + | l -> Some l + +(* Find the first group of complete behaviors in which all behaviors are in our + table. *) +let completes (type clause) completes table = let exception Ok of clause list in - let collect l b = Hashtbl.find table b :: l in + let collect l b = + let _, c = Hashtbl.find table b in + c :: l + in let collect bhvs = try let r = List.fold_left collect [] bhvs in raise (Ok r) with Not_found -> () in - try List.iter collect completes; None with Ok l -> Some l + try + List.iter collect completes; None + with Ok l -> Some l + +(* Concat all clauses from the table into a list. *) +let get_all_clauses table = + let clauses = + Hashtbl.fold + (fun _ (_,clause) clauses -> clause :: clauses) table [] + in + match clauses with + | [] -> None + | l -> Some l + +(* This function search for existing clauses inside our table. The search is + done in a specific order and stops when some clauses are found : + - unguarded behaviors (no assumes). + - complete behaviors (each one of them needs to be in our table). + - all behaviors in our table. + It is a generic function used by {!Generator.get_clauses}. +*) +let get_clauses_generic spec table = + match unguarded table with + | Some l -> Some l + | None -> + match completes spec.spec_complete_behaviors table with + | Some l -> Some l + | None -> get_all_clauses table (* Register a new mode (or replace an existing one). *) let register ?gen_exits ?status_exits ?gen_assigns ?status_assigns @@ -148,8 +203,8 @@ sig val has_default_behavior : behaviors -> bool (* Collect all clauses from function specification. *) val collect_behaviors : spec -> behaviors - (* Collect all clauses from complete behaviors. *) - val completes : string list list -> behaviors -> clause list option + (* Collect clauses given a [spec] and result of [collect_behaviors]. *) + val get_clauses : spec -> behaviors -> clause list option (* Generate a default clause in ACSL mode. *) val acsl_default : unit -> clause @@ -178,8 +233,8 @@ struct let combine_or_default mode kf spec table = if mode = ACSL then false, G.acsl_default () else - let completes_opt = G.completes spec.spec_complete_behaviors table in - match mode, completes_opt with + let clauses_opt = G.get_clauses spec table in + match mode, clauses_opt with | (Safe | Frama_C | Other _), Some completes_clauses -> true, G.combine_default completes_clauses | Safe, None -> @@ -231,7 +286,7 @@ module Exits_generator = struct type clause = t_exits - type behaviors = (string, clause) Hashtbl.t + type behaviors = (string, (bool * clause)) Hashtbl.t let name = "exits" @@ -241,15 +296,10 @@ struct Hashtbl.mem behaviors default let collect_behaviors spec = - let table = Hashtbl.create 17 in - let iter b = - let exits = List.filter (fun (k, _ ) -> Exits = k) b.b_post_cond in - if exits <> [] then Hashtbl.add table b.b_name exits - in - List.iter iter spec.spec_behavior; - table + let filter b = List.filter (fun (k, _ ) -> Exits = k) b.b_post_cond in + collect_behaviors_generic filter is_empty spec - let completes = completes_generic + let get_clauses = get_clauses_generic let acsl_default () = [ Exits, Logic_const.(new_predicate pfalse) ] @@ -326,7 +376,7 @@ module Assigns_generator = struct type clause = t_assigns - type behaviors = (string, assigns) Hashtbl.t + type behaviors = (string, (bool * clause)) Hashtbl.t let name = "assigns" @@ -336,14 +386,10 @@ struct Hashtbl.mem behaviors default let collect_behaviors spec = - let table = Hashtbl.create 17 in - let iter { b_name; b_assigns } = - if b_assigns <> WritesAny then Hashtbl.add table b_name b_assigns - in - List.iter iter spec.spec_behavior; - table + let filter b = b.b_assigns in + collect_behaviors_generic filter is_empty spec - let completes = completes_generic + let get_clauses = get_clauses_generic let acsl_default () = WritesAny @@ -370,8 +416,9 @@ struct if r <> 0 then r else compare_deps d1 d2 let combine_default clauses = - (* Note: combination is made on a set of complete behaviors in the sens of - [completes], thus here all clauses are [Writes ...] *) + (* Note: combination is made on a set of behaviors in the sens of + [get_clauses] and [collect_behaviors], thus here all clauses are + [Writes ...]. *) let collect acc = function | Writes l -> List.rev_append (List.rev l) acc | _ -> assert false @@ -452,7 +499,7 @@ module Requires_generator = struct type clause = t_requires - type behaviors = (string, clause) Hashtbl.t + type behaviors = (string, (bool * clause)) Hashtbl.t let name = "requires" @@ -462,14 +509,10 @@ struct Hashtbl.mem behaviors default let collect_behaviors spec = - let table = Hashtbl.create 17 in - let iter { b_name; b_requires } = - if b_requires <> [] then Hashtbl.add table b_name b_requires - in - List.iter iter spec.spec_behavior; - table + let filter b = b.b_requires in + collect_behaviors_generic filter is_empty spec - let completes = completes_generic + let get_clauses = get_clauses_generic let acsl_default () = [] @@ -529,7 +572,7 @@ module Allocates_generator = struct type clause = t_allocates - type behaviors = (string, allocation) Hashtbl.t + type behaviors = (string, (bool * clause)) Hashtbl.t let name = "allocates" @@ -539,14 +582,10 @@ struct Hashtbl.mem behaviors default let collect_behaviors spec = - let table = Hashtbl.create 17 in - let iter { b_name; b_allocation } = - if b_allocation <> FreeAllocAny then Hashtbl.add table b_name b_allocation - in - List.iter iter spec.spec_behavior; - table + let filter b = b.b_allocation in + collect_behaviors_generic filter is_empty spec - let completes = completes_generic + let get_clauses = get_clauses_generic let acsl_default () = FreeAlloc([],[]) @@ -560,8 +599,9 @@ struct acsl_default () let combine_default clauses = - (* Note: combination is made on a set of complete behaviors in the sens of - [completes], thus here all clauses are [FreeAlloc ...] *) + (* Note: combination is made on a set of behaviors in the sens of + [get_clauses] and [collect_behaviors], thus here all clauses are + [FreeAlloc ...]. *) let collect (facc, aacc) = function | FreeAlloc(f, a) -> List.rev_append (List.rev f) facc, List.rev_append (List.rev a) aacc @@ -640,7 +680,7 @@ struct let collect_behaviors spec = None <> spec.spec_terminates - let completes _ _ = None + let get_clauses _ _ = None let acsl_default () = Some(Logic_const.(new_predicate ptrue)) -- GitLab