From 5cfc38d29a21ec4010b3dd09eae0027e1b4194ac Mon Sep 17 00:00:00 2001
From: Thibault Martin <thi.martin.pro@pm.me>
Date: Wed, 6 Sep 2023 17:52:49 +0200
Subject: [PATCH] [Break API] many changes in populate_funspec, now takes
 clauses list

---
 src/kernel_internals/typing/populate_spec.ml  | 70 ++++++++++++-------
 src/kernel_internals/typing/populate_spec.mli | 24 +++++--
 2 files changed, 60 insertions(+), 34 deletions(-)

diff --git a/src/kernel_internals/typing/populate_spec.ml b/src/kernel_internals/typing/populate_spec.ml
index 5f356c5d512..b8f9af33cde 100644
--- a/src/kernel_internals/typing/populate_spec.ml
+++ b/src/kernel_internals/typing/populate_spec.ml
@@ -26,6 +26,15 @@ type mode =
   | ACSL | Safe | Frama_C (* Modes available for specification generation. *)
   | Skip (* Internally used to skip generation. *)
   | Other of string (* Allow user to use a custom mode, see {!register}. *)
+
+type clause = [
+  | `Exits
+  | `Assigns
+  | `Requires
+  | `Allocates
+  | `Terminates
+]
+
 (* Allow customization, each clause can be handled with a different [mode]. *)
 type config = {
   exits: mode;
@@ -703,13 +712,11 @@ let get_mode = function
 
 (* Given a [mode], returns the configuration for each clause. *)
 let build_config mode =
-  (* For now Allocates are skipped by default *)
-  let skip_mode = match mode with Other _ -> mode | _ -> Skip in
   {
     exits = mode;
     assigns = mode;
     requires = mode;
-    allocates = skip_mode;
+    allocates = mode;
     terminates = mode;
   }
 
@@ -719,16 +726,16 @@ let get_config_mode () =
 
 (* Build the default configuration, then select modes depending on the
    parameter [-generated-spec-custom]. *)
-let get_config () =
+let get_config_custom () =
   let default = get_config_mode () in
-  let collect (k,v) conf =
+  let collect (k,v) config =
     let mode = get_mode (Option.get v) in
     match k with
-    | "exits" -> {conf with exits = mode}
-    | "assigns" -> {conf with assigns = mode}
-    | "requires" -> {conf with requires = mode}
-    | "allocates" -> {conf with allocates = mode}
-    | "terminates" -> {conf with terminates = mode}
+    | "exits" -> {config with exits = mode}
+    | "assigns" -> {config with assigns = mode}
+    | "requires" -> {config with requires = mode}
+    | "allocates" -> {config with allocates = mode}
+    | "terminates" -> {config with terminates = mode}
     | s ->
       Kernel.abort
         "@['%s'@] is not a valid key for -generated-spec-custom.@, Accepted \
@@ -737,6 +744,17 @@ let get_config () =
   in
   Kernel.GeneratedSpecCustom.fold collect default
 
+let activated_config clauses default =
+  let collect config clause =
+    match clause with
+    | `Exits -> {config with exits = default.exits}
+    | `Assigns -> {config with assigns = default.assigns}
+    | `Requires -> {config with requires = default.requires}
+    | `Allocates -> {config with allocates = default.allocates}
+    | `Terminates -> {config with terminates = default.terminates}
+  in
+  List.fold_left collect (build_config Skip) clauses
+
 let do_warning ~empty (combined, clauses) kf =
   if clauses <> [] then
     let clauses = String.concat ", " clauses in
@@ -754,13 +772,15 @@ let do_warning ~empty (combined, clauses) kf =
          generating default specification%s,@, see -generated-spec-* options \
          for more info"
         clauses Kernel_function.pretty kf combined
+
 (* Perform generation of all clauses, adds them to the original specification,
    and emit property status for each of them. *)
-let do_populate kf original_spec =
+let do_populate clauses kf original_spec =
   let config =
+    activated_config clauses @@
     if is_frama_c_builtin kf then build_config Frama_C
     else if is_frama_c_stdlib kf then build_config ACSL
-    else get_config ()
+    else get_config_custom ()
   in
   let apply (combined, clauses) get_default mode =
     let g, to_warn = get_default mode kf original_spec in
@@ -829,18 +849,14 @@ let () = Ast.add_linked_state Is_populated.self
      OR
      [kf]'s specification is empty
 *)
-let populate_funspec ~force kf spec =
-  let has_body = Kernel_function.has_definition kf in
-  let is_empty_spec = Cil.is_empty_funspec spec in
-  if (force || Kernel.GenerateDefaultSpec.get ())
-  && not @@ Is_populated.mem kf
-  && (force || has_body || not @@ is_empty_spec) then begin
-    do_populate kf spec;
-    Is_populated.add kf ();
-    true
-  end
-  else false
-
-(* Annotations always force specification generation when calling for
-   populate_funspec. *)
-let () = Annotations.populate_spec_ref := populate_funspec ~force:true
+let populate_funspec ?(do_body=false) ?funspec kf clauses =
+  let funspec = match funspec with
+    | None -> Annotations.funspec kf
+    | Some funspec -> funspec
+  in
+  if not @@ Is_populated.mem kf then
+    let is_proto = not @@ Kernel_function.has_definition kf in
+    if is_proto || do_body then begin
+      do_populate clauses kf funspec;
+      Is_populated.add kf ();
+    end
diff --git a/src/kernel_internals/typing/populate_spec.mli b/src/kernel_internals/typing/populate_spec.mli
index cdaffe392f4..74bada5d9c5 100644
--- a/src/kernel_internals/typing/populate_spec.mli
+++ b/src/kernel_internals/typing/populate_spec.mli
@@ -28,6 +28,16 @@
 
 open Cil_types
 
+(** Different types of clauses which can be generated via
+    {!populate_funspec}. *)
+type clause = [
+  | `Exits
+  | `Assigns
+  | `Requires
+  | `Allocates
+  | `Terminates
+]
+
 (** Represents exits clause in the sense of
     {!Cil_types.behavior.b_post_cond}. *)
 type exits = (termination_kind * identified_predicate) list
@@ -68,11 +78,11 @@ val register :
   ?status_terminates:status ->
   string -> unit
 
-(** [populate_funspec ~force kf spec] generates missing specifications for the
-    kernel_function [kf] and its current specification [spec].
-    [force] is used in certain context to force specification generation.
-    if [force] is false :
-      + {!Kernel.GenerateDefaultSpec} can be used to turn off the generation.
-      + Generation will be skipped for prototypes with empty specifications.
+(** [populate_funspec ~do_body ?funspec kf] generates missing
+    specifications for the [kf].
+    By default ~do_body is false, meaning only specification of prototypes will
+    be generated.
+    If None, [Annotations.funspec kf] will be used to get kf's funspec.
     *)
-val populate_funspec : force:bool -> kernel_function -> spec -> bool
+val populate_funspec :
+  ?do_body:bool -> ?funspec:funspec -> kernel_function -> clause list -> unit
-- 
GitLab