diff --git a/src/kernel_services/ast_data/annotations.ml b/src/kernel_services/ast_data/annotations.ml index 056d57d7a3eff673eadf2da8c7bd9adb4f3f9809..e0cd2d1d41c01f04c39b98e0766cf63beba19542 100644 --- a/src/kernel_services/ast_data/annotations.ml +++ b/src/kernel_services/ast_data/annotations.ml @@ -280,7 +280,7 @@ let merge_funspec s1 s2 = (** {2 Getting annotations} *) (**************************************************************************) -module StmtContractMap = Transitioning.Stdlib.Map.Make(Datatype.String.Set) +module Behavior_set_map = Transitioning.Stdlib.Map.Make(Datatype.String.Set) let is_same_behavior_set l1 l2 = Datatype.String.Set.(equal (of_list l1) (of_list l2)) @@ -291,10 +291,10 @@ let merge_stmt_contracts contracts = | AStmtSpec(bhvs, spec) -> let bhvs = Datatype.String.Set.of_list bhvs in let fresh_spec, acc = - try StmtContractMap.find bhvs acc, acc + try Behavior_set_map.find bhvs acc, acc with Not_found -> let spec = Cil.empty_funspec () in - spec, StmtContractMap.add bhvs spec acc + spec, Behavior_set_map.add bhvs spec acc (* avoid sharing directly the spec, as merge_funspec will modify it in place*) in @@ -303,15 +303,111 @@ let merge_stmt_contracts contracts = | _ -> acc in let merged_contracts = - List.fold_left add_one StmtContractMap.empty contracts + List.fold_left add_one Behavior_set_map.empty contracts in - StmtContractMap.fold + Behavior_set_map.fold (fun bhvs spec acc -> (Logic_const.new_code_annotation (AStmtSpec (Datatype.String.Set.elements bhvs, spec))) :: acc) merged_contracts [] +let merge_stmt_contracts_emitters contracts = + let add_one acc (c,e) = + match c.annot_content with + | AStmtSpec(bhvs, spec) -> + let bhvs = Datatype.String.Set.of_list bhvs in + (match Behavior_set_map.find_opt bhvs acc with + | Some (spec', e') -> + merge_funspec spec' spec; + if Emitter.equal e e' then acc + else + Behavior_set_map.add bhvs (spec', Emitter.kernel) acc + | None -> + let spec' = Cil.empty_funspec () in + merge_funspec spec' spec; + Behavior_set_map.add bhvs (spec',e) acc + (* avoid sharing directly the spec, + as merge_funspec will modify it in place*)) + | _ -> acc + in + let merged_contracts = + List.fold_left add_one Behavior_set_map.empty contracts + in + Behavior_set_map.fold + (fun bhvs (spec,e) acc -> + (Logic_const.new_code_annotation + (AStmtSpec (Datatype.String.Set.elements bhvs, spec)),e) + :: acc) + merged_contracts [] + +let merge_loop_assigns annots = + let merge_assigns bhvs a acc = + let a' = + match Behavior_set_map.find_opt bhvs acc with + | Some a' -> merge_assigns ~keep_empty:false a' a + | None -> a + in + Behavior_set_map.add bhvs a' acc + in + let treat_code_annot acc ca = + match ca.annot_content with + | AAssigns(bhvs,a) -> + merge_assigns (Datatype.String.Set.of_list bhvs) a acc + | _ -> acc + in + let bhvs = List.fold_left treat_code_annot Behavior_set_map.empty annots in + Behavior_set_map.fold + (fun bhvs a acc -> + Logic_const.new_code_annotation + (AAssigns (Datatype.String.Set.elements bhvs, a)) + :: acc) + bhvs [] + +let merge_loop_assigns_emitters annots = + let merge_assigns bhvs (a,e) acc = + let elt = + match Behavior_set_map.find_opt bhvs acc with + | Some (a',e') -> + let a' = merge_assigns ~keep_empty:false a' a in + let e' = if Emitter.equal e e' then e else Emitter.kernel in + a', e' + | None -> a,e + in + Behavior_set_map.add bhvs elt acc + in + let treat_code_annot acc (ca,e) = + match ca.annot_content with + | AAssigns(bhvs,a) -> + merge_assigns (Datatype.String.Set.of_list bhvs) (a,e) acc + | _ -> acc + in + let bhvs = List.fold_left treat_code_annot Behavior_set_map.empty annots in + Behavior_set_map.fold + (fun bhvs (a,e) acc -> + (Logic_const.new_code_annotation + (AAssigns (Datatype.String.Set.elements bhvs, a)),e) + :: acc) + bhvs [] + +let partition_code_annot l = + let add_one_ca (contracts, assigns, others) ca = + if Logic_utils.is_contract ca then (ca::contracts,assigns,others) + else if Logic_utils.is_assigns ca then (contracts,ca::assigns,others) + else (contracts,assigns,ca::others) + in + let (contracts,assigns,others) = List.fold_left add_one_ca ([],[],[]) l in + List.rev contracts, List.rev assigns, List.rev others + +let partition_code_annot_emitter l = + let add_one_ca (contracts, assigns, others) (ca,_ as v) = + if Logic_utils.is_contract ca then (v::contracts,assigns,others) + else if Logic_utils.is_assigns ca then (contracts,v::assigns,others) + else (contracts,assigns,v::others) + in + let (contracts,assigns,others) = List.fold_left add_one_ca ([],[],[]) l in + List.rev contracts, List.rev assigns, List.rev others + let code_annot ?emitter ?filter stmt = try let tbl = Code_annots.find stmt in @@ -330,13 +426,11 @@ let code_annot ?emitter ?filter stmt = Emitter.Usable_emitter.Hashtbl.fold (fun _ l acc -> filter !l acc) tbl [] in - let l1, l2 = List.partition Logic_utils.is_contract l in - (match l1 with - | [] -> l2 - | _ -> (merge_stmt_contracts l1) @ l2) + let contracts, assigns, others = partition_code_annot l in + merge_stmt_contracts contracts @ merge_loop_assigns assigns @ others | Some e -> - (* No need to merge stmt contracts here: each emitter maintain one - statement contract per set of behavior. *) + (* No need to merge stmt contracts or loop assigns: + each emitter maintains one of each per set of behavior. *) let l = !(Emitter.Usable_emitter.Hashtbl.find tbl (Emitter.get e)) in match filter with | None -> l @@ -362,17 +456,10 @@ let code_annot_emitter ?filter stmt = Emitter.Usable_emitter.Hashtbl.fold (fun e l acc -> filter e !l acc) tbl [] in - let l1, l2 = List.partition (fun (x,_) -> Logic_utils.is_contract x) l in - match l1 with - | [] -> l2 - | _ -> - let contracts, emitters = List.split l1 in - let e = List.hd emitters in - let emit = - if List.for_all (fun x -> Emitter.equal x e) emitters then e - else Emitter.kernel - in - (List.map (fun x -> (x, emit)) contracts) @ l2 + let contracts,assigns,others = partition_code_annot_emitter l in + merge_stmt_contracts_emitters contracts @ + merge_loop_assigns_emitters assigns @ + others with Not_found -> [] @@ -1188,7 +1275,7 @@ let add_code_annot emitter ?kf stmt ca = acc) ips l in - let ca' = code_annot ~emitter ~filter stmt in + let ca' = code_annot ~filter stmt in let new_a = match ca' with | [] -> a diff --git a/tests/spec/oracle/loop_assigns_generated.res.oracle b/tests/spec/oracle/loop_assigns_generated.res.oracle new file mode 100644 index 0000000000000000000000000000000000000000..c3505d045ec9334bcd113e19a518b10fe060f5fb --- /dev/null +++ b/tests/spec/oracle/loop_assigns_generated.res.oracle @@ -0,0 +1,13 @@ +[kernel] Parsing tests/spec/loop_assigns_generated.i (no preprocessing) +/* Generated by Frama-C */ +void f(void) +{ + int k; + int j; + int i = 0; + /*@ loop assigns k, j, i; */ + while (i < 10) i ++; + return; +} + +