From a336be9b3f29a2bf18f7501e6637cddcaa66abc6 Mon Sep 17 00:00:00 2001
From: Thibault Martin <thi.martin.pro@pm.me>
Date: Wed, 2 Aug 2023 15:29:38 +0200
Subject: [PATCH] Only generate proto specs from Annotations.funspec

---
 src/kernel_internals/typing/populate_spec.ml  | 21 +++++++++++++------
 src/kernel_internals/typing/populate_spec.mli |  2 +-
 src/kernel_services/ast_queries/file.ml       |  3 ++-
 tests/spec/default_spec_combine.ml            |  8 ++++++-
 tests/spec/default_spec_custom.ml             | 10 ++++++++-
 tests/spec/default_spec_mode.i                |  1 +
 .../oracle/default_spec_custom.0.res.oracle   |  4 ++++
 .../oracle/default_spec_custom.1.res.oracle   |  4 ++++
 .../oracle/default_spec_mode.0.res.oracle     |  1 +
 .../oracle/default_spec_mode.1.res.oracle     |  1 +
 .../oracle/default_spec_mode.2.res.oracle     |  5 ++---
 .../oracle/default_spec_mode.3.res.oracle     |  4 +---
 .../oracle/default_spec_mode.4.res.oracle     |  7 ++-----
 .../oracle/default_spec_mode.5.res.oracle     |  5 +----
 14 files changed, 51 insertions(+), 25 deletions(-)

diff --git a/src/kernel_internals/typing/populate_spec.ml b/src/kernel_internals/typing/populate_spec.ml
index 24d88efe2ed..bbbbd7b7fc2 100644
--- a/src/kernel_internals/typing/populate_spec.ml
+++ b/src/kernel_internals/typing/populate_spec.ml
@@ -549,11 +549,11 @@ let build_config mode =
     terminates = mode;
   }
 
-let get_config_default () =
+let get_config_mode () =
   build_config @@ get_mode @@ Kernel.GeneratedSpecMode.get ()
 
 let get_config () =
-  let default = get_config_default () in
+  let default = get_config_mode () in
   let collect (k,v) conf =
     let mode = get_mode (Option.get v) in
     match k with
@@ -572,7 +572,6 @@ let get_config () =
 
 let do_populate kf original_spec =
   let config = get_config () in
-
   let exits = Exits.get_default config.exits kf original_spec in
   let assigns = Assigns.get_default config.assigns kf original_spec in
   let requires = Requires.get_default config.requires kf original_spec in
@@ -613,12 +612,22 @@ module Is_populated =
 
 let () = Ast.add_linked_state Is_populated.self
 
-let populate_funspec kf spec =
-  if not @@ Kernel.GenerateDefaultSpec.get() || Is_populated.mem kf then false
+let populate_funspec ~force kf spec =
+  let is_proto = not @@ Kernel_function.has_definition kf in
+  let skip_generation = not @@ Kernel.GenerateDefaultSpec.get () in
+  let is_empty_spec = Cil.is_empty_funspec spec in
+  let skip_proto = is_proto && not force && is_empty_spec in
+  if (not force && skip_generation) || Is_populated.mem kf || skip_proto
+  then false
   else begin
+    if is_proto && is_empty_spec then
+      Kernel.warning ~once:true ~current:true ~wkey:Kernel.wkey_missing_spec
+        "Neither code nor specification for function %a, \
+        generating default specs from the prototype"
+        Kernel_function.pretty kf;
     do_populate kf spec;
     Is_populated.add kf ();
     true
   end
 
-let () = Annotations.populate_spec_ref := populate_funspec
+let () = Annotations.populate_spec_ref := populate_funspec ~force:true
diff --git a/src/kernel_internals/typing/populate_spec.mli b/src/kernel_internals/typing/populate_spec.mli
index 940da158cfb..27ae84f04b6 100644
--- a/src/kernel_internals/typing/populate_spec.mli
+++ b/src/kernel_internals/typing/populate_spec.mli
@@ -60,4 +60,4 @@ val register :
   ?status_terminates:status ->
   string -> unit
 
-val populate_funspec : kernel_function -> spec -> bool
+val populate_funspec : force:bool -> kernel_function -> spec -> bool
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index 3b93f038bb9..36bb0e417a4 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -1189,7 +1189,8 @@ let () =
 
 let populate_spec () =
   let add_spec kf =
-    ignore @@ Populate_spec.populate_funspec kf (Annotations.funspec kf)
+    ignore @@ Populate_spec.populate_funspec ~force:false kf
+      (Annotations.funspec ~populate:false kf)
   in
   Globals.Functions.iter add_spec
 
diff --git a/tests/spec/default_spec_combine.ml b/tests/spec/default_spec_combine.ml
index dd81d458061..6261077f996 100644
--- a/tests/spec/default_spec_combine.ml
+++ b/tests/spec/default_spec_combine.ml
@@ -14,6 +14,12 @@ let status_allocates = Property_status.Dont_know
 let gen_terminates _ _ = None
 let status_terminates = Property_status.Dont_know
 
+let run () =
+  let get_spec kf =
+    ignore(Annotations.funspec kf)
+  in
+  Globals.Functions.iter get_spec
+
 let populate () =
   Format.printf "Registering an mode that does nothing@.";
   Populate_spec.register
@@ -25,4 +31,4 @@ let populate () =
     "donothing"
 
 
-  let () = Cmdline.run_after_configuring_stage populate
+let () = Cmdline.run_after_configuring_stage populate
diff --git a/tests/spec/default_spec_custom.ml b/tests/spec/default_spec_custom.ml
index 5220b814145..826cd252d44 100644
--- a/tests/spec/default_spec_custom.ml
+++ b/tests/spec/default_spec_custom.ml
@@ -26,6 +26,12 @@ let status_assigns = Property_status.True
 let status_allocates = Property_status.Dont_know
 let status_terminates = Property_status.Dont_know
 
+let run () =
+  let get_spec kf =
+    ignore(Annotations.funspec kf)
+  in
+  Globals.Functions.iter get_spec
+
 let populate () =
   Format.printf "Registering an empty spec generation mode@.";
   Populate_spec.register "emptymode";
@@ -39,4 +45,6 @@ let populate () =
     "mymode"
 
 
-  let () = Cmdline.run_after_configuring_stage populate
+let () = Cmdline.run_after_configuring_stage populate
+
+let () = Db.Main.extend run
diff --git a/tests/spec/default_spec_mode.i b/tests/spec/default_spec_mode.i
index 8c987bb116f..de2d911f8ef 100644
--- a/tests/spec/default_spec_mode.i
+++ b/tests/spec/default_spec_mode.i
@@ -21,6 +21,7 @@ void f2(){
 }
 
 // Test for automatic assigns
+//@ requires \true;
 int f3(int* a);
 
 // Has behavior by default
diff --git a/tests/spec/oracle/default_spec_custom.0.res.oracle b/tests/spec/oracle/default_spec_custom.0.res.oracle
index 72faead364b..fda41e6d630 100644
--- a/tests/spec/oracle/default_spec_custom.0.res.oracle
+++ b/tests/spec/oracle/default_spec_custom.0.res.oracle
@@ -6,10 +6,14 @@ Registering a new spec generation mode
 [kernel] Warning: Custom generation from mode emptymode not defined for requires, using frama-c mode instead
 [kernel] Warning: Custom generation from mode emptymode not defined for allocates, using frama-c mode instead
 [kernel] Warning: Custom generation from mode emptymode not defined for terminates, using frama-c mode instead
+[kernel:annot:missing-spec] default_spec_custom.i:18: Warning: 
+  Neither code nor specification for function f1, generating default specs from the prototype
 [kernel] Warning: Custom status from mode emptymode not defined for exits
 [kernel] Warning: Custom status from mode emptymode not defined for assigns
 [kernel] Warning: Custom status from mode emptymode not defined for allocates
 [kernel] Warning: Custom status from mode emptymode not defined for terminates
+[kernel:annot:missing-spec] default_spec_custom.i:18: Warning: 
+  Neither code nor specification for function f3, generating default specs from the prototype
 /* Generated by Frama-C */
 /*@ terminates \true;
     exits \false;
diff --git a/tests/spec/oracle/default_spec_custom.1.res.oracle b/tests/spec/oracle/default_spec_custom.1.res.oracle
index 7c6188e9550..3151473f64c 100644
--- a/tests/spec/oracle/default_spec_custom.1.res.oracle
+++ b/tests/spec/oracle/default_spec_custom.1.res.oracle
@@ -1,6 +1,10 @@
 Registering an empty spec generation mode
 Registering a new spec generation mode
 [kernel] Parsing default_spec_custom.i (no preprocessing)
+[kernel:annot:missing-spec] default_spec_custom.i:18: Warning: 
+  Neither code nor specification for function f1, generating default specs from the prototype
+[kernel:annot:missing-spec] default_spec_custom.i:18: Warning: 
+  Neither code nor specification for function f3, generating default specs from the prototype
 /* Generated by Frama-C */
 /*@ requires \false;
     terminates \false;
diff --git a/tests/spec/oracle/default_spec_mode.0.res.oracle b/tests/spec/oracle/default_spec_mode.0.res.oracle
index 9624686acc4..e36e7137656 100644
--- a/tests/spec/oracle/default_spec_mode.0.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.0.res.oracle
@@ -8,6 +8,7 @@ void f2(void)
   return;
 }
 
+/*@ requires \true; */
 int f3(int *a);
 
 /*@ requires \true;
diff --git a/tests/spec/oracle/default_spec_mode.1.res.oracle b/tests/spec/oracle/default_spec_mode.1.res.oracle
index 9624686acc4..e36e7137656 100644
--- a/tests/spec/oracle/default_spec_mode.1.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.1.res.oracle
@@ -8,6 +8,7 @@ void f2(void)
   return;
 }
 
+/*@ requires \true; */
 int f3(int *a);
 
 /*@ requires \true;
diff --git a/tests/spec/oracle/default_spec_mode.2.res.oracle b/tests/spec/oracle/default_spec_mode.2.res.oracle
index ecda6945556..e3d1b2a70b4 100644
--- a/tests/spec/oracle/default_spec_mode.2.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.2.res.oracle
@@ -1,7 +1,5 @@
 [kernel] Parsing default_spec_mode.i (no preprocessing)
 /* Generated by Frama-C */
-/*@ terminates \true;
-    allocates \nothing; */
 void f1(void);
 
 /*@ terminates \true;
@@ -12,7 +10,8 @@ void f2(void)
   return;
 }
 
-/*@ terminates \true;
+/*@ requires \true;
+    terminates \true;
     allocates \nothing; */
 int f3(int *a);
 
diff --git a/tests/spec/oracle/default_spec_mode.3.res.oracle b/tests/spec/oracle/default_spec_mode.3.res.oracle
index 9ae4d897289..a9f745a0148 100644
--- a/tests/spec/oracle/default_spec_mode.3.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.3.res.oracle
@@ -1,7 +1,5 @@
 [kernel] Parsing default_spec_mode.i (no preprocessing)
 /* Generated by Frama-C */
-/*@ requires \false;
-    terminates \false; */
 void f1(void);
 
 /*@ terminates \true;
@@ -14,7 +12,7 @@ void f2(void)
   return;
 }
 
-/*@ requires \false;
+/*@ requires \true;
     terminates \false; */
 int f3(int *a);
 
diff --git a/tests/spec/oracle/default_spec_mode.4.res.oracle b/tests/spec/oracle/default_spec_mode.4.res.oracle
index 272ef95df50..90038462eb5 100644
--- a/tests/spec/oracle/default_spec_mode.4.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.4.res.oracle
@@ -1,9 +1,5 @@
 [kernel] Parsing default_spec_mode.i (no preprocessing)
 /* Generated by Frama-C */
-/*@ terminates \true;
-    exits \false;
-    assigns \nothing;
-    allocates \nothing; */
 void f1(void);
 
 /*@ terminates \true;
@@ -15,7 +11,8 @@ void f2(void)
   return;
 }
 
-/*@ terminates \true;
+/*@ requires \true;
+    terminates \true;
     exits \false;
     assigns \result, *a;
     assigns \result \from *a;
diff --git a/tests/spec/oracle/default_spec_mode.5.res.oracle b/tests/spec/oracle/default_spec_mode.5.res.oracle
index 1fa027e6baf..69c1b68dab3 100644
--- a/tests/spec/oracle/default_spec_mode.5.res.oracle
+++ b/tests/spec/oracle/default_spec_mode.5.res.oracle
@@ -1,8 +1,5 @@
 [kernel] Parsing default_spec_mode.i (no preprocessing)
 /* Generated by Frama-C */
-/*@ requires \false;
-    terminates \true;
-    assigns \nothing; */
 void f1(void);
 
 /*@ terminates \true;
@@ -13,7 +10,7 @@ void f2(void)
   return;
 }
 
-/*@ requires \false;
+/*@ requires \true;
     terminates \true;
     assigns \result, *a;
     assigns \result \from *a;
-- 
GitLab