diff --git a/.Makefile.lint b/.Makefile.lint
index c2648ca66aafcbf1311b68871dfb983d47a9fbd4..80fb691a8370490eea2a7db9b60c1fae64ab498a 100644
--- a/.Makefile.lint
+++ b/.Makefile.lint
@@ -52,7 +52,6 @@ ML_LINT_KO+=src/kernel_services/analysis/dataflow2.mli
 ML_LINT_KO+=src/kernel_services/analysis/dataflows.ml
 ML_LINT_KO+=src/kernel_services/analysis/dataflows.mli
 ML_LINT_KO+=src/kernel_services/analysis/dominators.ml
-ML_LINT_KO+=src/kernel_services/analysis/exn_flow.ml
 ML_LINT_KO+=src/kernel_services/analysis/interpreted_automata.ml
 ML_LINT_KO+=src/kernel_services/analysis/interpreted_automata.mli
 ML_LINT_KO+=src/kernel_services/analysis/logic_interp.ml
diff --git a/src/kernel_internals/typing/allocates.ml b/src/kernel_internals/typing/allocates.ml
index 7b732f4edf83cefc6aa9e672b86c541c014648b6..21d04239663de36f3e20c3b6e81bc6a4e9a292be 100644
--- a/src/kernel_internals/typing/allocates.ml
+++ b/src/kernel_internals/typing/allocates.ml
@@ -35,7 +35,7 @@ let add_allocates_loop stmt =
   let all_default = Annotations.fold_code_annot all_default stmt true in
   if all_default then
     let ca = AAllocation ([], FreeAlloc ([], [])) in
-    Annotations.add_code_annot Emitter.kernel stmt
+    Annotations.add_code_annot ~keep_empty:false Emitter.kernel stmt
       (Logic_const.new_code_annotation ca)
 
 let add_allocates_nothing_funspec kf =
diff --git a/src/kernel_internals/typing/asm_contracts.ml b/src/kernel_internals/typing/asm_contracts.ml
index d87771d648f135516e46dbb69a3eb2c97856afb8..b61d0d1c2b343fee034e3bf7e7c162d7affd86e3 100644
--- a/src/kernel_internals/typing/asm_contracts.ml
+++ b/src/kernel_internals/typing/asm_contracts.ml
@@ -184,7 +184,7 @@ class visit_assembly =
            let ca =
              Logic_const.new_code_annotation (AStmtSpec ([],spec))
            in
-           Annotations.add_code_annot emitter ~kf stmt ca;
+           Annotations.add_code_annot ~keep_empty:false emitter ~kf stmt ca;
            if not mem_clobbered && Kernel.AsmContractsAutoValidate.get()
            then begin
              let active = [] in
diff --git a/src/kernel_services/analysis/exn_flow.ml b/src/kernel_services/analysis/exn_flow.ml
index a75151352f640348731a343532d6cfa4da1a5646..f31a727c80275794f9265fc86312e1fd673e7dbf 100644
--- a/src/kernel_services/analysis/exn_flow.ml
+++ b/src/kernel_services/analysis/exn_flow.ml
@@ -23,20 +23,20 @@
 open Cil
 open Cil_types
 
-(* all exceptions that can be raised somewhere in the AST. 
+(* all exceptions that can be raised somewhere in the AST.
    Used to handle function pointers without exn specification
- *)
+*)
 module All_exn =
   State_builder.Option_ref(Cil_datatype.Typ.Set)
     (struct let name = "Exn_flow.All_exn" let dependencies = [Ast.self] end)
-  
+
 module Exns =
   State_builder.Hashtbl(Kernel_function.Hashtbl)(Cil_datatype.Typ.Set)
     (struct
-        let name = "Exn_flow.Exns"
-        let dependencies = [Ast.self; All_exn.self]
-        let size = 47
-     end)
+      let name = "Exn_flow.Exns"
+      let dependencies = [Ast.self; All_exn.self]
+      let size = 47
+    end)
 
 module ExnsStmt =
   State_builder.Hashtbl(Cil_datatype.Stmt.Hashtbl)(Cil_datatype.Typ.Set)
@@ -61,10 +61,10 @@ class all_exn =
     method get_exn = all_exn
     method! vstmt_aux s =
       match s.skind with
-        | Throw (Some (_,t),_) ->
-          all_exn <- Cil_datatype.Typ.Set.add (purify t) all_exn;
-          SkipChildren
-        | _ -> DoChildren
+      | Throw (Some (_,t),_) ->
+        all_exn <- Cil_datatype.Typ.Set.add (purify t) all_exn;
+        SkipChildren
+      | _ -> DoChildren
   end
 
 let compute_all_exn () =
@@ -82,41 +82,41 @@ let add_exn_var exns v =
 let add_exn_clause exns (v,_) = add_exn_var exns v
 
 (* We're not really interested by intra-procedural Dataflow here: all the
-   interesting stuff happens at inter-procedural level (except for Throw 
+   interesting stuff happens at inter-procedural level (except for Throw
    encapsulated directly in a TryCatch, but even then it is easily captured
-   at syntactical level). Therefore, we can as well use a syntactic pass 
+   at syntactical level). Therefore, we can as well use a syntactic pass
    at intra-procedural level
- *)
+*)
 class exn_visit =
-object (self)
-  inherit Visitor.frama_c_inplace
-  val stack = Stack.create ()
-  val possible_exn = Stack.create ()
-  (* current set of exn included in a catch-all clause. Used to
-     handle Throw None;
-   *)
-  val current_exn = Stack.create ()
-
-  method private recursive_call kf =
-    try
-      Stack.iter
-        (fun (kf',_) -> if Kernel_function.equal kf kf' then raise Exit) stack;
-      false
-    with Exit -> true
-
-  method private add_exn t =
-    let current_uncaught = Stack.top possible_exn in
-    current_uncaught:= Cil_datatype.Typ.Set.add t !current_uncaught
-
-  method private union_exn s =
-    let current_uncaught = Stack.top possible_exn in
-    current_uncaught := Cil_datatype.Typ.Set.union s !current_uncaught
-
-  method! vstmt_aux s =
-    match s.skind with
+  object (self)
+    inherit Visitor.frama_c_inplace
+    val stack = Stack.create ()
+    val possible_exn = Stack.create ()
+    (* current set of exn included in a catch-all clause. Used to
+       handle Throw None;
+    *)
+    val current_exn = Stack.create ()
+
+    method private recursive_call kf =
+      try
+        Stack.iter
+          (fun (kf',_) -> if Kernel_function.equal kf kf' then raise Exit) stack;
+        false
+      with Exit -> true
+
+    method private add_exn t =
+      let current_uncaught = Stack.top possible_exn in
+      current_uncaught:= Cil_datatype.Typ.Set.add t !current_uncaught
+
+    method private union_exn s =
+      let current_uncaught = Stack.top possible_exn in
+      current_uncaught := Cil_datatype.Typ.Set.union s !current_uncaught
+
+    method! vstmt_aux s =
+      match s.skind with
       | Throw (None,_) ->
-          let my_exn = Stack.top current_exn in
-          self#union_exn my_exn; ExnsStmt.replace s my_exn; SkipChildren
+        let my_exn = Stack.top current_exn in
+        self#union_exn my_exn; ExnsStmt.replace s my_exn; SkipChildren
       | Throw(Some (_,t),_) ->
         let t = Cil.unrollTypeDeep t in
         let t = Cil.type_remove_qualifier_attributes t in
@@ -127,14 +127,14 @@ object (self)
         let catch, catch_all =
           List.fold_left
             (fun (catch, catch_all) ->
-              function 
-                | (Catch_all,_) -> catch, true
-                | (Catch_exn(v,[]),_) ->
-                    let catch = add_exn_var catch v in
-                    catch, catch_all
-                | (Catch_exn(_,aux), _) ->
-                    let catch = List.fold_left add_exn_clause catch aux in
-                    catch, catch_all)
+               function
+               | (Catch_all,_) -> catch, true
+               | (Catch_exn(v,[]),_) ->
+                 let catch = add_exn_var catch v in
+                 catch, catch_all
+               | (Catch_exn(_,aux), _) ->
+                 let catch = List.fold_left add_exn_clause catch aux in
+                 catch, catch_all)
             (Cil_datatype.Typ.Set.empty,false) c
         in
         Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
@@ -147,7 +147,7 @@ object (self)
         if not catch_all then self#union_exn uncaught;
         List.iter
           (fun (v,b) ->
-             (match v with 
+             (match v with
               | Catch_all ->
                 (* catch_all will catch all exceptions that are not
                    already handled by a previous clause. It must be the
@@ -164,9 +164,9 @@ object (self)
                     add_exn_clause Cil_datatype.Typ.Set.empty aux
                 in
                 Stack.push catch current_exn);
-            ignore 
-              (Visitor.visitFramacBlock (self:>Visitor.frama_c_inplace) b);
-            ignore (Stack.pop current_exn))
+             ignore
+               (Visitor.visitFramacBlock (self:>Visitor.frama_c_inplace) b);
+             ignore (Stack.pop current_exn))
           c;
         let my_exn = !(Stack.pop possible_exn) in
         ExnsStmt.replace s my_exn;
@@ -175,34 +175,34 @@ object (self)
       | If _ | Switch _ | Loop _ | Block _ | UnspecifiedSequence _
       | TryFinally _ | TryExcept _
       | Instr _ -> (* must take into account exceptions thrown by a fun call*)
-          Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
-          DoChildrenPost
-            (fun s ->
+        Stack.push (ref Cil_datatype.Typ.Set.empty) possible_exn;
+        DoChildrenPost
+          (fun s ->
              let my_exn = !(Stack.pop possible_exn) in
              ExnsStmt.replace s my_exn;
              self#union_exn my_exn;
              s)
       (* No exception can be thrown here. *)
       | Return _ | Goto _ | Break _ | Continue _ ->
-         ExnsStmt.replace s Cil_datatype.Typ.Set.empty;
-         SkipChildren
+        ExnsStmt.replace s Cil_datatype.Typ.Set.empty;
+        SkipChildren
 
-  method! vinst =
-    function
+    method! vinst =
+      function
       | Call(_,{ enode = Lval(Var f,NoOffset) },_,_)
       | Local_init(_, ConsInit(f, _, _), _) ->
         let kf = Globals.Functions.get f in
         if self#recursive_call kf then begin
           let module Found =
-              struct
-                exception F of Cil_datatype.Typ.Set.t
-              end
+          struct
+            exception F of Cil_datatype.Typ.Set.t
+          end
           in
           let computed_exn =
             try
               Stack.iter
                 (fun (kf', exns) ->
-                  if Kernel_function.equal kf kf' then raise (Found.F !exns))
+                   if Kernel_function.equal kf kf' then raise (Found.F !exns))
                 stack;
               Kernel.fatal "No cycle found!"
             with Found.F exns -> exns
@@ -220,7 +220,7 @@ object (self)
             let kf_exn = Cil_datatype.Typ.Set.union computed_exn known_exn in
             Exns.replace kf kf_exn;
             ignore
-              (Visitor.visitFramacFunction 
+              (Visitor.visitFramacFunction
                  (self:>Visitor.frama_c_visitor)
                  (Kernel_function.get_definition kf));
             let callee_exn = Exns.find kf in
@@ -236,9 +236,9 @@ object (self)
           self#union_exn callee_exn
         end else begin (* TODO: introduce extension to declare
                           exceptions that can be thrown by prototypes. *)
-            Kernel.warning
-              "Assuming declared function %a can't throw any exception"
-              Kernel_function.pretty kf
+          Kernel.warning
+            "Assuming declared function %a can't throw any exception"
+            Kernel_function.pretty kf
         end;
         SkipChildren
       | Call _ ->
@@ -247,60 +247,60 @@ object (self)
         self#union_exn (all_exn()); SkipChildren
       | _ -> SkipChildren
 
-  method! vfunc f =
-    let my_exns = ref Cil_datatype.Typ.Set.empty in
-    let kf = Globals.Functions.get f.svar in
-    Stack.push (kf,my_exns) stack;
-    Stack.push my_exns possible_exn;
-    let after_visit f =
-      let callee_exn = Stack.pop possible_exn in
-      Exns.add kf !callee_exn;
-      ignore (Stack.pop stack); f
-    in
-    DoChildrenPost after_visit
+    method! vfunc f =
+      let my_exns = ref Cil_datatype.Typ.Set.empty in
+      let kf = Globals.Functions.get f.svar in
+      Stack.push (kf,my_exns) stack;
+      Stack.push my_exns possible_exn;
+      let after_visit f =
+        let callee_exn = Stack.pop possible_exn in
+        Exns.add kf !callee_exn;
+        ignore (Stack.pop stack); f
+      in
+      DoChildrenPost after_visit
 
-end
+  end
 
 let compute_kf kf =
   if Kernel_function.is_definition kf then
     ignore
       (Visitor.visitFramacFunction (new exn_visit)
          (Kernel_function.get_definition kf))
-  (* just ignore prototypes. *)
+(* just ignore prototypes. *)
 
 let compute () = Globals.Functions.iter compute_kf
 
 let get_type_tag t =
   let rec aux t =
     match t with
-      | TVoid _ -> "v"
-      | TInt (IBool,_) -> "B"
-      | TInt (IChar,_) -> "c"
-      | TInt (ISChar,_) -> "sc"
-      | TInt (IUChar,_) -> "uc"
-      | TInt (IInt,_) -> "i"
-      | TInt (IUInt,_) -> "ui"
-      | TInt (IShort,_) -> "s"
-      | TInt (IUShort,_) -> "us"
-      | TInt (ILong,_) -> "l"
-      | TInt (IULong,_) -> "ul"
-      | TInt (ILongLong,_) -> "ll"
-      | TInt (IULongLong,_) -> "ull"
-      | TFloat(FFloat,_) -> "f"
-      | TFloat(FDouble,_) -> "d"
-      | TFloat (FLongDouble,_) -> "ld"
-      | TPtr(t,_) -> "p" ^ aux t
-      | TArray(t,_,_,_) -> "a" ^ aux t
-      | TFun(rt,l,_,_) ->
-        let base = "fun" ^ aux rt in
-        (match l with
-          | None -> base
-          | Some l ->
-            List.fold_left (fun acc (_,t,_) -> acc ^ aux t) base l)
-      | TNamed _ -> Kernel.fatal "named type not correctly unrolled"
-      | TComp (s,_,_) -> (if s.cstruct then "S" else "U") ^ s.cname
-      | TEnum (e,_) -> "E" ^ e.ename
-      | TBuiltin_va_list _ -> "va"
+    | TVoid _ -> "v"
+    | TInt (IBool,_) -> "B"
+    | TInt (IChar,_) -> "c"
+    | TInt (ISChar,_) -> "sc"
+    | TInt (IUChar,_) -> "uc"
+    | TInt (IInt,_) -> "i"
+    | TInt (IUInt,_) -> "ui"
+    | TInt (IShort,_) -> "s"
+    | TInt (IUShort,_) -> "us"
+    | TInt (ILong,_) -> "l"
+    | TInt (IULong,_) -> "ul"
+    | TInt (ILongLong,_) -> "ll"
+    | TInt (IULongLong,_) -> "ull"
+    | TFloat(FFloat,_) -> "f"
+    | TFloat(FDouble,_) -> "d"
+    | TFloat (FLongDouble,_) -> "ld"
+    | TPtr(t,_) -> "p" ^ aux t
+    | TArray(t,_,_,_) -> "a" ^ aux t
+    | TFun(rt,l,_,_) ->
+      let base = "fun" ^ aux rt in
+      (match l with
+       | None -> base
+       | Some l ->
+         List.fold_left (fun acc (_,t,_) -> acc ^ aux t) base l)
+    | TNamed _ -> Kernel.fatal "named type not correctly unrolled"
+    | TComp (s,_,_) -> (if s.cstruct then "S" else "U") ^ s.cname
+    | TEnum (e,_) -> "E" ^ e.ename
+    | TBuiltin_va_list _ -> "va"
   in "__fc_" ^ aux t
 
 let get_type_enum t = "__fc_exn_kind_" ^ (get_type_tag t)
@@ -372,9 +372,9 @@ let generate_exn_union e exns =
 let add_types_and_globals typs globs f =
   let iter_globs (acc,added) g =
     match g with
-      | GVarDecl _ | GVar _  | GFun _ as g when not added ->
-        (g :: List.rev_append globs (List.rev_append typs acc), true)
-      | _ -> g :: acc, added
+    | GVarDecl _ | GVar _  | GFun _ as g when not added ->
+      (g :: List.rev_append globs (List.rev_append typs acc), true)
+    | _ -> g :: acc, added
   in
   let globs, added = List.fold_left iter_globs ([],false) f.globals in
   let globs =
@@ -399,130 +399,130 @@ let find_exns_func v =
 
 let find_exns e =
   match e.enode with
-    | Lval(Var v, NoOffset) -> find_exns_func v
-    | _ -> all_exn ()
+  | Lval(Var v, NoOffset) -> find_exns_func v
+  | _ -> all_exn ()
 
 class erase_exn =
-object(self)
-  inherit Visitor.frama_c_inplace
-  (* reverse before filling. *)
-  val mutable new_types = []
-
-  val exn_enum = Cil_datatype.Typ.Hashtbl.create 7
+  object(self)
+    inherit Visitor.frama_c_inplace
+    (* reverse before filling. *)
+    val mutable new_types = []
 
-  val exn_union = Cil_datatype.Typ.Hashtbl.create 7
+    val exn_enum = Cil_datatype.Typ.Hashtbl.create 7
 
-  val mutable modified_funcs = Cil_datatype.Fundec.Set.empty
+    val exn_union = Cil_datatype.Typ.Hashtbl.create 7
 
-  val mutable exn_struct = None
+    val mutable modified_funcs = Cil_datatype.Fundec.Set.empty
 
-  val mutable exn_var = None
+    val mutable exn_struct = None
 
-  val mutable can_throw = false
+    val mutable exn_var = None
 
-  val mutable fct_can_throw = false
+    val mutable can_throw = false
 
-  val mutable catched_var = None
+    val mutable fct_can_throw = false
 
-  val mutable label_counter = 0
+    val mutable catched_var = None
 
-  val exn_labels = Cil_datatype.Typ.Hashtbl.create 7
-  val catch_all_label = Stack.create ()
+    val mutable label_counter = 0
 
-  method modified_funcs = modified_funcs
+    val exn_labels = Cil_datatype.Typ.Hashtbl.create 7
+    val catch_all_label = Stack.create ()
 
-  method private update_enum_bindings enum exns =
-    let update_one_binding t =
-      let s = get_type_enum t in
-      let ei = List.find (fun ei -> ei.einame = s) enum.eitems in
-      Cil_datatype.Typ.Hashtbl.add exn_enum t ei
-    in
-    Cil_datatype.Typ.Set.iter update_one_binding exns
+    method modified_funcs = modified_funcs
 
-  method private update_union_bindings union exns =
-    let update_one_binding t =
-      let s = get_type_tag t in
-      Kernel.debug ~dkey:Kernel.dkey_exn_flow
-        "Registering %a as possible exn type" Cil_datatype.Typ.pretty t;
-      let fi = List.find (fun fi -> fi.fname = s) union.cfields in
-      Cil_datatype.Typ.Hashtbl.add exn_union t fi
-    in
-    Cil_datatype.Typ.Set.iter update_one_binding exns
-
-  method private exn_kind t = Cil_datatype.Typ.Hashtbl.find exn_enum t
-
-  method private is_thrown t = Cil_datatype.Typ.Hashtbl.mem exn_enum t
+    method private update_enum_bindings enum exns =
+      let update_one_binding t =
+        let s = get_type_enum t in
+        let ei = List.find (fun ei -> ei.einame = s) enum.eitems in
+        Cil_datatype.Typ.Hashtbl.add exn_enum t ei
+      in
+      Cil_datatype.Typ.Set.iter update_one_binding exns
+
+    method private update_union_bindings union exns =
+      let update_one_binding t =
+        let s = get_type_tag t in
+        Kernel.debug ~dkey:Kernel.dkey_exn_flow
+          "Registering %a as possible exn type" Cil_datatype.Typ.pretty t;
+        let fi = List.find (fun fi -> fi.fname = s) union.cfields in
+        Cil_datatype.Typ.Hashtbl.add exn_union t fi
+      in
+      Cil_datatype.Typ.Set.iter update_one_binding exns
 
-  method private exn_field_off name =
-    List.find (fun fi -> fi.fname = name) (Extlib.the exn_struct).cfields
+    method private exn_kind t = Cil_datatype.Typ.Hashtbl.find exn_enum t
 
-  method private exn_field name =
-    Var (Extlib.the exn_var), Field(self#exn_field_off name, NoOffset)
+    method private is_thrown t = Cil_datatype.Typ.Hashtbl.mem exn_enum t
 
-  method private exn_field_term name =
-    TVar(Cil.cvar_to_lvar (Extlib.the exn_var)),
-    TField(self#exn_field_off name, TNoOffset)
+    method private exn_field_off name =
+      List.find (fun fi -> fi.fname = name) (Extlib.the exn_struct).cfields
 
-  method private exn_obj_field = self#exn_field exn_obj_name
+    method private exn_field name =
+      Var (Extlib.the exn_var), Field(self#exn_field_off name, NoOffset)
 
-  method private exn_obj_field_term = self#exn_field_term exn_obj_name
+    method private exn_field_term name =
+      TVar(Cil.cvar_to_lvar (Extlib.the exn_var)),
+      TField(self#exn_field_off name, TNoOffset)
 
-  method private exn_kind_field = self#exn_field exn_kind_name
+    method private exn_obj_field = self#exn_field exn_obj_name
 
-  method private exn_kind_field_term = self#exn_field_term exn_kind_name
+    method private exn_obj_field_term = self#exn_field_term exn_obj_name
 
-  method private uncaught_flag_field = self#exn_field exn_uncaught_name
+    method private exn_kind_field = self#exn_field exn_kind_name
 
-  method private uncaught_flag_field_term =
-    self#exn_field_term exn_uncaught_name
+    method private exn_kind_field_term = self#exn_field_term exn_kind_name
 
-  method private exn_obj_kind_field t =
-    Kernel.debug ~dkey:Kernel.dkey_exn_flow
-      "Searching for %a as possible exn type" Cil_datatype.Typ.pretty t; 
-    Cil_datatype.Typ.Hashtbl.find exn_union t
+    method private uncaught_flag_field = self#exn_field exn_uncaught_name
 
-  method private test_uncaught_flag loc b =
-    let e1 = Cil.new_exp ~loc (Lval self#uncaught_flag_field) in
-    let e2 = if b then Cil.one ~loc else Cil.zero ~loc in
-    Cil.new_exp ~loc (BinOp(Eq,e1,e2,Cil.intType))
+    method private uncaught_flag_field_term =
+      self#exn_field_term exn_uncaught_name
 
-  method private pred_uncaught_flag loc b =
-    let e1 =
-      Logic_const.term
-        ~loc (TLval self#uncaught_flag_field_term) Linteger
-    in
-    let e2 =
-      if b then Logic_const.tinteger ~loc 1
-      else Logic_const.tinteger ~loc 0
-    in
-    Logic_const.prel ~loc (Req,e1,e2)
-
-  method private set_uncaught_flag loc b =
-    let e = if b then Cil.one ~loc else Cil.zero ~loc in
-    Cil.mkStmtOneInstr (Set(self#uncaught_flag_field,e,loc))
-
-  method private set_exn_kind loc t =
-    let e = self#exn_kind (purify t) in
-    let e = Cil.new_exp ~loc (Const (CEnum e)) in
-    Cil.mkStmtOneInstr(Set(self#exn_kind_field,e,loc))
-
-  method private set_exn_value loc t e =
-    let lv = self#exn_obj_field in
-    let union_field = self#exn_obj_kind_field (purify t) in
-    let lv = Cil.addOffsetLval (Field (union_field, NoOffset)) lv in
-    Cil.mkStmtOneInstr (Set(lv,e,loc))
-
-  method private jumps_to_default_handler loc =
-    if Stack.is_empty catch_all_label then begin
-      (* no catch-all clause in the function: just go up in the stack. *)
-      fct_can_throw <- true;
-      let kf = Extlib.the self#current_kf in
-      let ret = Kernel_function.find_return kf in
-      let rtyp = Kernel_function.get_return_type kf in
-      if ret.labels = [] then
-        ret.labels <- [Label("__ret_label",Cil_datatype.Stmt.loc ret,false)];
-      let goto = mkStmt (Goto (ref ret,loc)) in
-      match ret.skind with
+    method private exn_obj_kind_field t =
+      Kernel.debug ~dkey:Kernel.dkey_exn_flow
+        "Searching for %a as possible exn type" Cil_datatype.Typ.pretty t;
+      Cil_datatype.Typ.Hashtbl.find exn_union t
+
+    method private test_uncaught_flag loc b =
+      let e1 = Cil.new_exp ~loc (Lval self#uncaught_flag_field) in
+      let e2 = if b then Cil.one ~loc else Cil.zero ~loc in
+      Cil.new_exp ~loc (BinOp(Eq,e1,e2,Cil.intType))
+
+    method private pred_uncaught_flag loc b =
+      let e1 =
+        Logic_const.term
+          ~loc (TLval self#uncaught_flag_field_term) Linteger
+      in
+      let e2 =
+        if b then Logic_const.tinteger ~loc 1
+        else Logic_const.tinteger ~loc 0
+      in
+      Logic_const.prel ~loc (Req,e1,e2)
+
+    method private set_uncaught_flag loc b =
+      let e = if b then Cil.one ~loc else Cil.zero ~loc in
+      Cil.mkStmtOneInstr (Set(self#uncaught_flag_field,e,loc))
+
+    method private set_exn_kind loc t =
+      let e = self#exn_kind (purify t) in
+      let e = Cil.new_exp ~loc (Const (CEnum e)) in
+      Cil.mkStmtOneInstr(Set(self#exn_kind_field,e,loc))
+
+    method private set_exn_value loc t e =
+      let lv = self#exn_obj_field in
+      let union_field = self#exn_obj_kind_field (purify t) in
+      let lv = Cil.addOffsetLval (Field (union_field, NoOffset)) lv in
+      Cil.mkStmtOneInstr (Set(lv,e,loc))
+
+    method private jumps_to_default_handler loc =
+      if Stack.is_empty catch_all_label then begin
+        (* no catch-all clause in the function: just go up in the stack. *)
+        fct_can_throw <- true;
+        let kf = Extlib.the self#current_kf in
+        let ret = Kernel_function.find_return kf in
+        let rtyp = Kernel_function.get_return_type kf in
+        if ret.labels = [] then
+          ret.labels <- [Label("__ret_label",Cil_datatype.Stmt.loc ret,false)];
+        let goto = mkStmt (Goto (ref ret,loc)) in
+        match ret.skind with
         | Return (None,_) -> [goto]
         (* rt is void: do not need to create a dummy return value *)
         | Return (Some { enode = Lval(Var rv, NoOffset) },_) ->
@@ -532,232 +532,232 @@ object(self)
           Kernel.fatal "exception removal should be used after oneRet"
         | _ ->
           Kernel.fatal "find_return did not give a Return statement"
-    end else begin
-      let stmt = Stack.top catch_all_label in
-      [mkStmt (Goto (ref stmt, loc))]
-    end
-
-  method private jumps_to_handler loc t =
-    let t = purify t in
-    try
-      let stmt = Cil_datatype.Typ.Hashtbl.find exn_labels t in
-      [mkStmt (Goto (ref stmt, loc))]
-    with
+      end else begin
+        let stmt = Stack.top catch_all_label in
+        [mkStmt (Goto (ref stmt, loc))]
+      end
+
+    method private jumps_to_handler loc t =
+      let t = purify t in
+      try
+        let stmt = Cil_datatype.Typ.Hashtbl.find exn_labels t in
+        [mkStmt (Goto (ref stmt, loc))]
+      with
       | Not_found -> self#jumps_to_default_handler loc
 
-  method! vfile f =
-    let exns = all_exn () in
-    if not (Cil_datatype.Typ.Set.is_empty exns) then begin
-      let loc = Cil_datatype.Location.unknown in
-      let e = generate_exn_enum exns in
-      let u,s = generate_exn_union e exns in
-      let exn =
-        Cil.makeGlobalVar "__fc_exn" (TComp (s,{scache = Not_Computed},[]))
-      in
-      self#update_enum_bindings e exns;
-      self#update_union_bindings u exns;
-      exn_struct <- Some s;
-      can_throw <- true;
-      new_types <-
-        GCompTag (s,loc) ::
-        GCompTag (u,loc) ::
-        GEnumTag (e,loc) :: new_types;
-      exn_var <- Some exn;
-      let exn_init = Cil.makeZeroInit ~loc (TComp(s,{scache=Not_Computed},[]))
-      in
-      let gexn_var = GVar(exn, { init = Some exn_init }, loc) in
-      ChangeDoChildrenPost(
-        f,add_types_and_globals (List.rev new_types) [gexn_var])
-    end else (* nothing can be thrown in the first place, but we still have
-                to get rid of (useless) try/catch blocks if any. *)
+    method! vfile f =
+      let exns = all_exn () in
+      if not (Cil_datatype.Typ.Set.is_empty exns) then begin
+        let loc = Cil_datatype.Location.unknown in
+        let e = generate_exn_enum exns in
+        let u,s = generate_exn_union e exns in
+        let exn =
+          Cil.makeGlobalVar "__fc_exn" (TComp (s,{scache = Not_Computed},[]))
+        in
+        self#update_enum_bindings e exns;
+        self#update_union_bindings u exns;
+        exn_struct <- Some s;
+        can_throw <- true;
+        new_types <-
+          GCompTag (s,loc) ::
+          GCompTag (u,loc) ::
+          GEnumTag (e,loc) :: new_types;
+        exn_var <- Some exn;
+        let exn_init = Cil.makeZeroInit ~loc (TComp(s,{scache=Not_Computed},[]))
+        in
+        let gexn_var = GVar(exn, { init = Some exn_init }, loc) in
+        ChangeDoChildrenPost(
+          f,add_types_and_globals (List.rev new_types) [gexn_var])
+      end else (* nothing can be thrown in the first place, but we still have
+                  to get rid of (useless) try/catch blocks if any. *)
         DoChildren
 
-  method private visit_catch_clause loc (v,b) =
-    let loc =
-      match b.bstmts with
+    method private visit_catch_clause loc (v,b) =
+      let loc =
+        match b.bstmts with
         | [] -> loc
         | [x] -> Cil_datatype.Stmt.loc x
         | x::tl ->
           fst (Cil_datatype.Stmt.loc x),
           snd (Cil_datatype.Stmt.loc (Extlib.last tl))
-    in
-    let add_unreachable_block b =
-      Cil.mkStmt (If(Cil.zero ~loc, b, Cil.mkBlock [], loc))
-    in
-    let assign_catched_obj v b =
-      let exn_obj = self#exn_obj_field in
-      let kind_field = self#exn_obj_kind_field (purify v.vtype) in
-      let lv = Cil.addOffsetLval (Field (kind_field,NoOffset)) exn_obj in
-      let s =
-        Cil.mkStmtOneInstr
-          (Set ((Var v, NoOffset), Cil.new_exp ~loc (Lval lv), loc))
       in
-      b.bstmts <- s :: b.bstmts
-    in
-    let f = Extlib.the self#current_func in
-    let update_locals v b =
-      if not (List.memq v b.blocals) then b.blocals <- v::b.blocals;
-      if not (List.memq v f.slocals) then f.slocals <- v::f.slocals
-    in
-    let b =
-      (match v with
-        | Catch_all -> b
-        | Catch_exn (v,[]) ->
-          Cil.update_var_type v (purify v.vtype);
-          update_locals v b;
-          assign_catched_obj v b; b
-        | Catch_exn(v,aux) ->
-          let add_one_aux stmts (v,b) =
-            Cil.update_var_type v (purify v.vtype);
-            update_locals v b;
-            assign_catched_obj v b;
-            add_unreachable_block b :: stmts
-          in
-          b.blocals <- List.filter (fun v' -> v!=v') b.blocals;
-          let aux_blocks =
-            List.fold_left add_one_aux [Cil.mkStmt (Block b)] aux
-          in
-          let main_block = Cil.mkBlock aux_blocks in
-          Cil.update_var_type v (purify v.vtype);
-          update_locals v main_block;
-          main_block)
-    in
-    ignore (Visitor.visitFramacBlock (self :> Visitor.frama_c_visitor) b);
-    add_unreachable_block b
-
-  method! vfunc _ =
-    label_counter <- 0;
-    fct_can_throw <- false;
-    let update_body f =
-      if fct_can_throw then begin
-        Oneret.encapsulate_local_vars f
-      end;
-      f
-    in
-    DoChildrenPost update_body
+      let add_unreachable_block b =
+        Cil.mkStmt (If(Cil.zero ~loc, b, Cil.mkBlock [], loc))
+      in
+      let assign_catched_obj v b =
+        let exn_obj = self#exn_obj_field in
+        let kind_field = self#exn_obj_kind_field (purify v.vtype) in
+        let lv = Cil.addOffsetLval (Field (kind_field,NoOffset)) exn_obj in
+        let s =
+          Cil.mkStmtOneInstr
+            (Set ((Var v, NoOffset), Cil.new_exp ~loc (Lval lv), loc))
+        in
+        b.bstmts <- s :: b.bstmts
+      in
+      let f = Extlib.the self#current_func in
+      let update_locals v b =
+        if not (List.memq v b.blocals) then b.blocals <- v::b.blocals;
+        if not (List.memq v f.slocals) then f.slocals <- v::f.slocals
+      in
+      let b =
+        (match v with
+         | Catch_all -> b
+         | Catch_exn (v,[]) ->
+           Cil.update_var_type v (purify v.vtype);
+           update_locals v b;
+           assign_catched_obj v b; b
+         | Catch_exn(v,aux) ->
+           let add_one_aux stmts (v,b) =
+             Cil.update_var_type v (purify v.vtype);
+             update_locals v b;
+             assign_catched_obj v b;
+             add_unreachable_block b :: stmts
+           in
+           b.blocals <- List.filter (fun v' -> v!=v') b.blocals;
+           let aux_blocks =
+             List.fold_left add_one_aux [Cil.mkStmt (Block b)] aux
+           in
+           let main_block = Cil.mkBlock aux_blocks in
+           Cil.update_var_type v (purify v.vtype);
+           update_locals v main_block;
+           main_block)
+      in
+      ignore (Visitor.visitFramacBlock (self :> Visitor.frama_c_visitor) b);
+      add_unreachable_block b
+
+    method! vfunc _ =
+      label_counter <- 0;
+      fct_can_throw <- false;
+      let update_body f =
+        if fct_can_throw then begin
+          Oneret.encapsulate_local_vars f
+        end;
+        f
+      in
+      DoChildrenPost update_body
 
-  method private modify_current () =
-    modified_funcs <-
-      Cil_datatype.Fundec.Set.add (Extlib.the self#current_func) modified_funcs;
+    method private modify_current () =
+      modified_funcs <-
+        Cil_datatype.Fundec.Set.add (Extlib.the self#current_func) modified_funcs;
 
-  method private aux_handler_goto target (v,b) =
-    let loc = v.vdecl in
-    let goto_main_handler = Cil.mkStmt (Goto (ref target,loc)) in
-    let suf =
-      if label_counter = 0 then "" else "_" ^ (string_of_int label_counter)
-    in
-    let lab = (get_type_tag (purify v.vtype)) ^ suf in
-    label_counter <- label_counter + 1;
-    b.bstmts <- b.bstmts @ [goto_main_handler];
-    (* we have at least the goto statement in the block *)
-    let s = List.hd b.bstmts in
-    s.labels <- (Label(lab,loc,false)::s.labels);
-    Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) s
-
-  method private guard_post_cond (kind,pred as orig) =
-    match kind with
-        (* If we exit explicitly with exit,
-           we haven't seen an uncaught exception anyway. *)
+    method private aux_handler_goto target (v,b) =
+      let loc = v.vdecl in
+      let goto_main_handler = Cil.mkStmt (Goto (ref target,loc)) in
+      let suf =
+        if label_counter = 0 then "" else "_" ^ (string_of_int label_counter)
+      in
+      let lab = (get_type_tag (purify v.vtype)) ^ suf in
+      label_counter <- label_counter + 1;
+      b.bstmts <- b.bstmts @ [goto_main_handler];
+      (* we have at least the goto statement in the block *)
+      let s = List.hd b.bstmts in
+      s.labels <- (Label(lab,loc,false)::s.labels);
+      Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) s
+
+    method private guard_post_cond (kind,pred as orig) =
+      match kind with
+      (* If we exit explicitly with exit,
+         we haven't seen an uncaught exception anyway. *)
       | Exits | Breaks | Continues -> orig
       | Returns | Normal ->
-          let loc = pred.ip_content.pred_loc in
-          let p = self#pred_uncaught_flag loc false in
-          let pred' = Logic_const.pred_of_id_pred pred in
-          (kind,
-           (Logic_const.new_predicate
-              (Logic_const.pimplies ~loc (p,pred'))))
-
-  method! vbehavior b =
-    match self#current_kf, self#current_stmt with
+        let loc = pred.ip_content.pred_loc in
+        let p = self#pred_uncaught_flag loc false in
+        let pred' = Logic_const.pred_of_id_pred pred in
+        (kind,
+         (Logic_const.new_predicate
+            (Logic_const.pimplies ~loc (p,pred'))))
+
+    method! vbehavior b =
+      match self#current_kf, self#current_stmt with
       | None, None -> SkipChildren
-        (* Prototype is assumed to not throw any exception. *)
+      (* Prototype is assumed to not throw any exception. *)
       | None, Some _ ->
-          Kernel.fatal
-            "Inconsistent visitor state: visiting a statement \
-             outside of any function."
-      | Some f, None when not (Kernel_function.is_definition f) -> 
-          (* By hypothesis, prototypes do not throw anything. *)
-          SkipChildren
+        Kernel.fatal
+          "Inconsistent visitor state: visiting a statement \
+           outside of any function."
+      | Some f, None when not (Kernel_function.is_definition f) ->
+        (* By hypothesis, prototypes do not throw anything. *)
+        SkipChildren
       | Some f, None -> (* function contract *)
-          let exns = Exns.find f in
-          if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
-          else begin 
-            b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
-            ChangeTo b (* need to register the new clauses. *)
-          end
+        let exns = Exns.find f in
+        if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
+        else begin
+          b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
+          ChangeTo b (* need to register the new clauses. *)
+        end
       | Some _, Some s -> (* statement contract *)
-          let exns = ExnsStmt.find s in
-          if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
-          else begin
-            b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
-            ChangeTo b
-          end
-
-  method private clean_catch_clause (bind,b as handler) acc =
-    let remove_local v =
-      let f =
-        Visitor_behavior.Get.fundec self#behavior (Extlib.the self#current_func)
-      in
-      let v = Visitor_behavior.Get.varinfo self#behavior v in
-      f.slocals <- List.filter (fun v' -> v!=v') f.slocals;
-    in
-    match bind with
-    | Catch_all -> handler :: acc
-    | Catch_exn (v, []) ->
-      if self#is_thrown (purify v.vtype) then handler :: acc
-      else begin remove_local v; acc end
-    | Catch_exn (v, l) ->
-      let caught, remove =
-        List.partition (fun (v,_) -> self#is_thrown (purify v.vtype)) l
-      in
-      List.iter (fun (v,_) -> remove_local v) remove;
-      if caught = [] then begin remove_local v; acc end
-      else (Catch_exn (v,caught), b) :: acc
-
-  method! vstmt_aux s =
-    let generate_jumps instr exns loc =
-      if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
-      else begin
-        self#modify_current ();
-        let make_jump t (stmts, uncaught) =
-          let t = purify t in
-          if Cil_datatype.Typ.Hashtbl.mem exn_labels t then begin
-            let e = self#exn_kind t in
-            let e = Cil.new_exp ~loc (Const (CEnum e)) in
-            let b = self#jumps_to_handler loc t in
-            let s = Cil.mkStmt (Block (Cil.mkBlock b)) in
-            s.labels <- [Case (e,loc)];
-            s::stmts, uncaught
-          end else stmts, true
+        let exns = ExnsStmt.find s in
+        if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
+        else begin
+          b.b_post_cond <- List.map self#guard_post_cond b.b_post_cond;
+          ChangeTo b
+        end
+
+    method private clean_catch_clause (bind,b as handler) acc =
+      let remove_local v =
+        let f =
+          Visitor_behavior.Get.fundec self#behavior (Extlib.the self#current_func)
         in
-        let stmts, uncaught =
-          Cil_datatype.Typ.Set.fold make_jump exns ([],false)
+        let v = Visitor_behavior.Get.varinfo self#behavior v in
+        f.slocals <- List.filter (fun v' -> v!=v') f.slocals;
+      in
+      match bind with
+      | Catch_all -> handler :: acc
+      | Catch_exn (v, []) ->
+        if self#is_thrown (purify v.vtype) then handler :: acc
+        else begin remove_local v; acc end
+      | Catch_exn (v, l) ->
+        let caught, remove =
+          List.partition (fun (v,_) -> self#is_thrown (purify v.vtype)) l
         in
-        let stmts =
-          if uncaught then begin
-            let default =
+        List.iter (fun (v,_) -> remove_local v) remove;
+        if caught = [] then begin remove_local v; acc end
+        else (Catch_exn (v,caught), b) :: acc
+
+    method! vstmt_aux s =
+      let generate_jumps instr exns loc =
+        if Cil_datatype.Typ.Set.is_empty exns then SkipChildren
+        else begin
+          self#modify_current ();
+          let make_jump t (stmts, uncaught) =
+            let t = purify t in
+            if Cil_datatype.Typ.Hashtbl.mem exn_labels t then begin
+              let e = self#exn_kind t in
+              let e = Cil.new_exp ~loc (Const (CEnum e)) in
+              let b = self#jumps_to_handler loc t in
+              let s = Cil.mkStmt (Block (Cil.mkBlock b)) in
+              s.labels <- [Case (e,loc)];
+              s::stmts, uncaught
+            end else stmts, true
+          in
+          let stmts, uncaught =
+            Cil_datatype.Typ.Set.fold make_jump exns ([],false)
+          in
+          let stmts =
+            if uncaught then begin
+              let default =
                 Cil.mkStmt (
                   Block (Cil.mkBlock (self#jumps_to_default_handler loc)))
-            in
-            default.labels <- [Default loc];
-            List.rev_append stmts [default]
-          end else List.rev stmts
-        in
-        let test = self#test_uncaught_flag loc true in
-        let cases = Cil.new_exp ~loc (Lval self#exn_kind_field) in
-        let switch = Cil.mkStmt (Switch(cases,Cil.mkBlock stmts,stmts,loc)) in
-        let handler =
-          Cil.mkStmt (If(test,Cil.mkBlock [switch],Cil.mkBlock [],loc))
-        in
-        let instr =
-          Visitor.visitFramacInstr (self:>Visitor.frama_c_visitor) instr
-        in
-        let call = Cil.mkStmtOneInstr (List.hd instr) in
-        s.skind <- Block (Cil.mkBlockNonScoping [call;handler]);
-        SkipChildren
-      end
-    in
-    match s.skind with
+              in
+              default.labels <- [Default loc];
+              List.rev_append stmts [default]
+            end else List.rev stmts
+          in
+          let test = self#test_uncaught_flag loc true in
+          let cases = Cil.new_exp ~loc (Lval self#exn_kind_field) in
+          let switch = Cil.mkStmt (Switch(cases,Cil.mkBlock stmts,stmts,loc)) in
+          let handler =
+            Cil.mkStmt (If(test,Cil.mkBlock [switch],Cil.mkBlock [],loc))
+          in
+          let instr =
+            Visitor.visitFramacInstr (self:>Visitor.frama_c_visitor) instr
+          in
+          let call = Cil.mkStmtOneInstr (List.hd instr) in
+          s.skind <- Block (Cil.mkBlockNonScoping [call;handler]);
+          SkipChildren
+        end
+      in
+      match s.skind with
       | Instr (Call (_,f,_,loc) as instr) ->
         generate_jumps instr (find_exns f) loc
       | Instr (Local_init(_, ConsInit(f,_,_), loc) as instr) ->
@@ -795,7 +795,7 @@ object(self)
            than the current block. As we are adding statements in the
            auxiliary blocks, we need to do that before adding labels to the
            entry points of these blocks.
-         *)
+        *)
         let stmts = List.map (self#visit_catch_clause loc) c in
         let suf =
           if label_counter = 0 then "" else "_" ^ (string_of_int label_counter)
@@ -812,10 +812,10 @@ object(self)
               stmt.labels <- [Label (label,v.vdecl,false)];
               b.bstmts <- stmt :: b.bstmts;
               (match aux with
-                | [] ->
-                  Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) stmt
-                | _ :: _ ->
-                  List.iter (self#aux_handler_goto stmt) aux)
+               | [] ->
+                 Cil_datatype.Typ.Hashtbl.add exn_labels (purify v.vtype) stmt
+               | _ :: _ ->
+                 List.iter (self#aux_handler_goto stmt) aux)
             | (Catch_all, b) ->
               let loc =
                 match b.bstmts with [] -> loc | s::_ -> Cil_datatype.Stmt.loc s
@@ -837,7 +837,7 @@ object(self)
             | Catch_exn(_,l), _ ->
               List.iter
                 (fun (v,_) ->
-                  Cil_datatype.Typ.Hashtbl.remove exn_labels (purify v.vtype))
+                   Cil_datatype.Typ.Hashtbl.remove exn_labels (purify v.vtype))
                 l
             | Catch_all,_ -> ignore (Stack.pop catch_all_label))
           c; (* we remove bindings in the reverse order as we added them,
@@ -846,8 +846,8 @@ object(self)
         s.skind <- Block t;
         SkipChildren
       | _ -> DoChildren
-        
-end
+
+  end
 
 let prepare_file f =
   if Kernel.SimplifyCfg.get () then begin
diff --git a/src/kernel_services/ast_data/annotations.ml b/src/kernel_services/ast_data/annotations.ml
index 9152b5e455fd39d1c64ef5e1836b8beb0872b28a..7f6f2802239ca20044ef97d8dbd18fd2146d74a1 100644
--- a/src/kernel_services/ast_data/annotations.ml
+++ b/src/kernel_services/ast_data/annotations.ml
@@ -290,85 +290,64 @@ 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))
 
-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_emitters annots =
-  let merge_assigns bhvs (a,e) acc =
+let merge_annots_emitters extract merge make annots =
+  let merge_same_bhvs bhvs (ca,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
+      | Some (ca', a',e') ->
+        let a' = merge a a' in
         let e' = if Emitter.equal e e' then e else Emitter.kernel in
-        a', e'
-      | None -> a,e
+        let ca' =
+          if Cil_datatype.Code_annotation.compare ca' ca < 0 then ca' else ca
+        in
+        let annot_content = make (Datatype.String.Set.elements bhvs) a' in
+        let ca' = { ca' with annot_content } in
+        ca', a', e'
+      | None -> ca,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
+    match extract ca with
+    | Some(bhvs,a) ->
+      merge_same_bhvs (Datatype.String.Set.of_list bhvs) (ca,a,e) acc
+    | None -> 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 merge_loop_allocation annots =
-  let merge_allocates bhvs (a,e) acc =
-    let elt =
-      match Behavior_set_map.find_opt bhvs acc with
-      | Some (a', e') ->
-        let a' = merge_allocation ~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
+  Behavior_set_map.fold (fun _ (ca,_,e) acc -> (ca,e) :: acc) bhvs []
+
+let merge_stmt_contracts_emitters =
+  let extract ca =
+    match ca.annot_content with
+    | AStmtSpec(bhvs, spec) -> Some (bhvs,spec)
+    | _ -> None
   in
-  let treat_code_annot acc (ca, e) =
+  let merge s s' =
+    let e = Cil.empty_funspec() in
+    merge_funspec e s;
+    merge_funspec e s';
+    e
+  in
+  let make bhvs s = AStmtSpec(bhvs,s) in
+  merge_annots_emitters extract merge make
+
+let merge_loop_assigns_emitter =
+  let extract ca =
     match ca.annot_content with
-    | AAllocation(bhvs,a) ->
-      merge_allocates (Datatype.String.Set.of_list bhvs) (a,e) acc
-    | _ -> acc
+    | AAssigns(bhvs,a) -> Some(bhvs,a)
+    | _ -> None
   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
-          (AAllocation (Datatype.String.Set.elements bhvs, a)),e)
-       ::acc)
-    bhvs []
+  let merge a a' = merge_assigns ~keep_empty:false a a' in
+  let make bhvs a = AAssigns(bhvs,a) in
+  merge_annots_emitters extract merge make
+
+let merge_loop_allocation_emitter =
+  let extract ca =
+    match ca.annot_content with AAllocation(bhvs,a) -> Some (bhvs,a) | _ -> None
+  in
+  let merge a a' = merge_allocation ~keep_empty:false a a' in
+  let make bhvs a = AAllocation(bhvs,a) in
+  merge_annots_emitters extract merge make
 
 let partition_code_annot_emitter l =
   let add_one_ca (contracts, assigns, alloc, others) (ca,_ as v) =
@@ -410,8 +389,8 @@ let code_annot_emitter ?filter stmt =
     in
     let contracts,assigns,allocation,others = partition_code_annot_emitter l in
     merge_stmt_contracts_emitters contracts @
-    merge_loop_assigns_emitters assigns @
-    merge_loop_allocation allocation @
+    merge_loop_assigns_emitter assigns @
+    merge_loop_allocation_emitter allocation @
     others
   with Not_found ->
     []
@@ -541,25 +520,12 @@ let model_fields ?emitter t =
 (**************************************************************************)
 
 let iter_code_annot f stmt =
-  try
-    let tbl = Code_annots.find stmt in
-    Emitter.Usable_emitter.Hashtbl.iter
-      (fun e l -> List.iter (f (Emitter.Usable_emitter.get e)) !l)
-      tbl
-  with Not_found ->
-    ()
+  let l = code_annot_emitter stmt in
+  List.iter (fun (a,e) -> f e a) l
 
 let fold_code_annot f stmt acc =
-  try
-    let tbl = Code_annots.find stmt in
-    Emitter.Usable_emitter.Hashtbl.fold
-      (fun e l acc ->
-         let e = Emitter.Usable_emitter.get e in
-         List.fold_left (fun acc x -> f e x acc) acc !l)
-      tbl
-      acc
-  with Not_found ->
-    acc
+  let l = code_annot_emitter stmt in
+  List.fold_left (fun acc (a,e) -> f e a acc) acc l
 
 let iter_all_code_annot ?(sorted=true) f =
   let cmp s1 s2 =
@@ -1071,6 +1037,25 @@ let add_ensures e kf ?stmt ?active ?behavior l =
   in
   extend_behavior e kf ?stmt ?active ?behavior set_bhv
 
+let get_full_spec kf ?stmt ?(behavior=[]) () =
+  match stmt with
+  | None ->
+    (try funspec ~populate:false kf with Not_found -> Cil.empty_funspec())
+  | Some stmt ->
+    let filter a =
+      match a.annot_content with
+      | AStmtSpec(bhvs,_) when is_same_behavior_set bhvs behavior -> true
+      | _ -> false
+    in
+    (match code_annot ~filter stmt with
+     | [] -> Cil.empty_funspec ()
+     | [ { annot_content = AStmtSpec(_,s)} ] -> s
+     | _ -> Kernel.fatal "More than one contract associated to a statement")
+
+let get_spec_behavior s = function
+  | None -> Cil.find_default_behavior s
+  | Some name -> List.find_opt (fun { b_name } -> b_name = name) s.spec_behavior
+
 let add_assigns ~keep_empty e kf ?stmt ?active ?behavior a =
   let set_bhv full_bhv e_bhv =
     let keep_empty = keep_empty && full_bhv.b_assigns = WritesAny in
@@ -1093,7 +1078,15 @@ let add_assigns ~keep_empty e kf ?stmt ?active ?behavior a =
          (Property.ip_from_of_behavior kf ki active full_bhv);
     )
   in
-  extend_behavior e kf ?stmt ?active ?behavior set_bhv
+  let old_spec = get_full_spec kf ?stmt ?behavior:active () in
+  let bhv = get_spec_behavior old_spec behavior in
+  let is_empty =
+    match bhv with
+    | None -> true
+    | Some b -> b.b_assigns = WritesAny
+  in
+  if not (keep_empty && is_empty) then
+    extend_behavior e kf ?stmt ?active ?behavior set_bhv
 
 let add_allocates ~keep_empty e kf ?stmt ?active ?behavior a =
   let ki = kinstr stmt in
@@ -1109,7 +1102,15 @@ let add_allocates ~keep_empty e kf ?stmt ?active ?behavior a =
     Extlib.may Property_status.register
       (Property.ip_allocation_of_behavior kf ki active full_bhv);
   in
-  extend_behavior e kf ?stmt ?active ?behavior set_bhv
+  let old_spec = get_full_spec kf ?stmt ?behavior:active () in
+  let bhv = get_spec_behavior old_spec behavior in
+  let is_empty =
+    match bhv with
+    | None -> true
+    | Some b -> b.b_allocation = FreeAllocAny
+  in
+  if not (keep_empty && is_empty) then
+    extend_behavior e kf ?stmt ?active ?behavior set_bhv
 
 let add_extended e kf ?stmt ?active ?behavior ext =
   let set_bhv _ e_bhv =
@@ -1121,212 +1122,165 @@ let add_extended e kf ?stmt ?active ?behavior ext =
 
 (** {3 Adding code annotations} *)
 
-let add_code_annot emitter ?kf stmt ca =
+let add_code_annot ?(keep_empty=true) emitter ?kf stmt ca =
   (*  Kernel.feedback "%a: adding code annot %a with stmt %a (%d)"
       Project.pretty (Project.current ())
       Code_annotation.pretty ca
       Stmt.pretty stmt
       stmt.sid;*)
   let kf = find_englobing_kf ?kf stmt in
-  let convert a =
-    match a.annot_content with
-    | AAssert(l, kind, p) ->
-      let a = { a with annot_content=AAssert(l,kind,extend_name emitter p) } in
-      a, Property.ip_of_code_annot kf stmt a
-    | AInvariant(l, b, p) ->
-      let a={a with annot_content=AInvariant(l,b,extend_name emitter p)} in
-      a, Property.ip_of_code_annot kf stmt a
-    | AStmtSpec (bhvs, spec) ->
-      let filter ca =
-        match ca.annot_content with
-        | AStmtSpec(bhvs',_) -> is_same_behavior_set bhvs bhvs'
-        | _ -> false
-      in
-      let contract = code_annot ~filter stmt in
-      (match contract with
-       | [] -> a, Property.ip_of_code_annot kf stmt a
-       | [ { annot_content = AStmtSpec _ } ] ->
-         let register_children = true in
-         let active = bhvs in
-         add_behaviors
-           ~register_children emitter kf ~stmt ~active spec.spec_behavior;
-         if spec.spec_variant <> None then
-           Kernel.fatal
-             "statement contract cannot have a decrease clause";
-         Extlib.may
-           (add_terminates emitter kf ~stmt ~active) spec.spec_terminates;
-         List.iter
-           (add_complete emitter kf ~stmt ~active)
-           spec.spec_complete_behaviors;
-         List.iter
-           (add_disjoint emitter kf ~stmt ~active)
-           spec.spec_disjoint_behaviors;
-         (* By construction, we have exactly one contract
-            corresponding to our criterion and emitter. *)
-         let ca = List.hd (code_annot ~emitter ~filter stmt) in
-         (* remove the previous binding in order to replace it
-            with the updated one. Statuses being attached to sub-elements
-            of the contract, they do not need any update here.
-          *)
-         remove_code_annot_internal emitter ~remove_statuses:false ~kf stmt ca;
-         { a with annot_content = ca.annot_content }, []
-       | _ ->
-         Kernel.fatal
-           "more than one contract attached to a given statement for \
-            emitter %a. Invariant of annotations management broken."
-           Emitter.pretty emitter)
-    | AVariant _ ->
-      let v = code_annot ~filter:Logic_utils.is_variant stmt in
-      (match v with
-       | [] -> a, Property.ip_of_code_annot kf stmt a
-       | _ ->
-         let source = fst (Cil_datatype.Stmt.loc stmt) in
-         Kernel.fatal ~source
-           "trying to register a second variant for statement %a"
-           Stmt.pretty stmt)
-    | AAssigns (bhvs, assigns) ->
-      let filter ca =
-        match ca.annot_content with
-        | AAssigns (bhvs', _) -> is_same_behavior_set bhvs bhvs'
-        | _ -> false
-      in
-      let assigns' = code_annot ~filter stmt in
-      (match assigns' with
-       | [] -> a, Property.ip_of_code_annot kf stmt a
-       | l ->
-         let merge_assigns_ca acc ca =
-           match ca.annot_content with
-           | AAssigns(_,assigns') ->
-             merge_assigns ~keep_empty:false assigns' acc
-           | _ -> acc
-         in
-         let assigns' = List.fold_left merge_assigns_ca WritesAny l in
-         let merged_ca = { a with annot_content = AAssigns(bhvs,assigns') }
-         in
-         let ip =
-           Property.(
-             ip_of_assigns
-               kf (Kstmt stmt) (Id_loop merged_ca) assigns')
-         in
-         Extlib.may Property_status.remove ip;
-         (match assigns' with
-          | WritesAny -> ()
-          | Writes l ->
-            List.iter
-              (fun from ->
-                 let ip =
-                   Property.(
-                     ip_of_from kf (Kstmt stmt)
-                       (Id_loop merged_ca) from)
-                 in
-                 Extlib.may Property_status.remove ip)
-              l);
-         let new_assigns =
-           merge_assigns ~keep_empty:false assigns' assigns
-         in
-         let new_ca = { a with annot_content = AAssigns(bhvs,new_assigns) }
-         in
-         let ips =
-           Extlib.list_of_opt
-             Property.(
-               ip_of_assigns
-                 kf (Kstmt stmt) (Id_loop new_ca) new_assigns)
-         in
-         let ips =
-           match new_assigns with
-           | WritesAny -> ips
-           | Writes l ->
-             List.fold_left
-               (fun acc f ->
-                  Extlib.opt_fold
-                    (fun x y -> x::y)
-                    Property.(
-                      ip_of_from kf (Kstmt stmt)
-                        (Id_loop new_ca) f)
-                    acc)
-               ips l
-         in
-         let ca' = code_annot ~filter stmt in
-         let new_a =
-           match ca' with
-           | [] -> a
-           | [ { annot_content = AAssigns(_, assigns') } as ca ] ->
-             remove_code_annot_internal emitter ~kf stmt ca;
-             let merged =
-               merge_assigns ~keep_empty:false assigns' assigns
-             in
-             { a with annot_content = AAssigns (bhvs, merged) }
-           | _ ->
-             Kernel.fatal
-               "More than one loop assigns clause for a statement. \
-                Annotations internal state broken."
-         in
-         new_a, ips)
-    | AAllocation (bhvs, alloc) ->
-      let filter ca =
-        match ca.annot_content with
-        | AAllocation(bhvs',_) -> is_same_behavior_set bhvs bhvs'
-        | _ -> false
-      in
-      (match code_annot ~filter stmt with
-       | [] -> a, Property.ip_of_code_annot kf stmt a
-       | l ->
-         let merge_alloc_ca acc alloc =
-           match alloc.annot_content with
-           | AAllocation(_,a) -> merge_allocation ~keep_empty:false acc a
-           | _ -> acc
-         in
-         let alloc' = List.fold_left merge_alloc_ca FreeAllocAny l in
-         let merged_a =
-           { a with annot_content = AAllocation(bhvs,alloc') }
-         in
-         let ip =
-           Property.(
-             ip_of_allocation kf (Kstmt stmt)
-               (Id_loop merged_a) alloc')
-         in
-         Extlib.may Property_status.remove ip;
-         let new_alloc = merge_allocation ~keep_empty:false alloc' alloc in
-         let new_a =
-           { a with annot_content = AAllocation(bhvs,new_alloc) }
-         in
-         let ip =
-           Property.(
-             ip_of_allocation
-               kf (Kstmt stmt) (Id_loop new_a) new_alloc)
-         in
-         let emit_a =
-           match code_annot ~emitter ~filter stmt with
-           | [] -> a
-           | [ { annot_content = AAllocation(_,alloc') } as ca ] ->
-             remove_code_annot_internal emitter ~kf stmt ca;
-             { a with annot_content =
-                        AAllocation(
-                          bhvs,
-                          merge_allocation ~keep_empty:false alloc' alloc) }
-           | _ ->
-             Kernel.fatal
-               "More than one allocation clause for a statement. \
-                Annotations internal state broken"
-         in
-         emit_a, Extlib.list_of_opt ip)
-    | APragma _ | AExtended _ -> a, Property.ip_of_code_annot kf stmt a
-  in
-  let ca, ppts = convert ca in
-  let e = Emitter.get emitter in
-  List.iter Property_status.register ppts;
-  let add_emitter tbl = Emitter.Usable_emitter.Hashtbl.add tbl e (ref [ ca ]) in
-  try
-    let tbl = Code_annots.find stmt in
+  let fill_tables ca ppts =
+    let e = Emitter.get emitter in
+    List.iter Property_status.register ppts;
+    let add_emitter tbl =
+      Emitter.Usable_emitter.Hashtbl.add tbl e (ref [ ca ]) in
     try
-      let l = Emitter.Usable_emitter.Hashtbl.find tbl e in
-      l := ca :: !l;
+      let tbl = Code_annots.find stmt in
+      try
+        let l = Emitter.Usable_emitter.Hashtbl.find tbl e in
+        l := ca :: !l;
+      with Not_found ->
+        add_emitter tbl
     with Not_found ->
-      add_emitter tbl
-  with Not_found ->
-    let tbl = Emitter.Usable_emitter.Hashtbl.create 7 in
-    add_emitter tbl;
-    Code_annots.add stmt tbl
+      let tbl = Emitter.Usable_emitter.Hashtbl.create 7 in
+      add_emitter tbl;
+      Code_annots.add stmt tbl
+  in
+  match ca.annot_content with
+  | AAssert(l, kind, p) ->
+    let a = { ca with annot_content=AAssert(l,kind,extend_name emitter p) } in
+    fill_tables a (Property.ip_of_code_annot kf stmt a)
+  | AInvariant(l, b, p) ->
+    let a={ca with annot_content=AInvariant(l,b,extend_name emitter p)} in
+    fill_tables a (Property.ip_of_code_annot kf stmt a)
+  | AStmtSpec (bhvs, spec) ->
+    let filter ca =
+      match ca.annot_content with
+      | AStmtSpec(bhvs',_) -> is_same_behavior_set bhvs bhvs'
+      | _ -> false
+    in
+    let contract = code_annot ~filter stmt in
+    (match contract with
+     | [] ->
+       if not (keep_empty && Logic_utils.funspec_has_only_assigns spec) then
+         fill_tables ca (Property.ip_of_code_annot kf stmt ca)
+     | [ { annot_content = AStmtSpec _ } ] ->
+       let register_children = true in
+       let active = bhvs in
+       add_behaviors
+         ~register_children emitter kf ~stmt ~active spec.spec_behavior;
+       if spec.spec_variant <> None then
+         Kernel.fatal
+           "statement contract cannot have a decrease clause";
+       Extlib.may
+         (add_terminates emitter kf ~stmt ~active) spec.spec_terminates;
+       List.iter
+         (add_complete emitter kf ~stmt ~active)
+         spec.spec_complete_behaviors;
+       List.iter
+         (add_disjoint emitter kf ~stmt ~active)
+         spec.spec_disjoint_behaviors;
+       (* By construction, we have exactly one contract
+          corresponding to our criterion and emitter. *)
+       let ca' = List.hd (code_annot ~emitter ~filter stmt) in
+       (* remove the previous binding in order to replace it
+          with the updated one. Statuses being attached to sub-elements
+          of the contract, they do not need any update here.
+       *)
+       remove_code_annot_internal emitter ~remove_statuses:false ~kf stmt ca';
+       fill_tables ca' []
+     | _ ->
+       Kernel.fatal
+         "more than one contract attached to a given statement for \
+          emitter %a. Invariant of annotations management broken."
+         Emitter.pretty emitter)
+  | AVariant _ ->
+    let v = code_annot ~filter:Logic_utils.is_variant stmt in
+    (match v with
+     | [] -> fill_tables ca (Property.ip_of_code_annot kf stmt ca)
+     | _ ->
+       let source = fst (Cil_datatype.Stmt.loc stmt) in
+       Kernel.fatal ~source
+         "trying to register a second variant for statement %a"
+         Stmt.pretty stmt)
+  | AAssigns (bhvs, assigns) ->
+    let filter_ca ca =
+      match ca.annot_content with
+      | AAssigns (bhvs', _) -> is_same_behavior_set bhvs bhvs'
+      | _ -> false
+    in
+    let filter e ca = Emitter.equal e emitter && filter_ca ca in
+    let ca_e = code_annot_emitter ~filter stmt in
+    let ca_total = code_annot ~filter:filter_ca stmt in
+    (match ca_total with
+     | [] when keep_empty -> ()
+     | [] -> fill_tables ca (Property.ip_of_code_annot kf stmt ca)
+     | [{ annot_content = AAssigns(_,assigns_total)}] ->
+       let assigns_e =
+         match ca_e with
+         | [] -> WritesAny
+         | [ { annot_content = AAssigns(_, assigns') } as ca,_ ] ->
+           remove_code_annot_internal emitter ~kf stmt ca;
+           assigns'
+         | _ ->
+           Kernel.fatal
+             "More than one loop assigns clause for a statement. \
+              Annotations internal state broken."
+       in
+       (* we have assigns at statement level, just not from this
+          emitter yet, hence merge at emitter level regardless of keep_empty *)
+       let merged_e = merge_assigns ~keep_empty:false assigns_e assigns in
+       let new_a = { ca with annot_content = AAssigns(bhvs,merged_e) } in
+       let merged_total = merge_assigns ~keep_empty assigns_total assigns in
+       let ips =
+         Property.ip_of_code_annot kf stmt
+           { ca with annot_content = AAssigns(bhvs,merged_total) }
+       in
+       fill_tables new_a ips
+     | _ ->
+       Kernel.fatal
+         "More than one loop assigns clause for a statement. \
+          Annotations internal state broken.")
+  | AAllocation (bhvs, alloc) ->
+    let filter_ca ca =
+      match ca.annot_content with
+      | AAllocation(bhvs',_) -> is_same_behavior_set bhvs bhvs'
+      | _ -> false
+    in
+    let filter e ca = Emitter.equal e emitter && filter_ca ca in
+    let ca_e = code_annot_emitter ~filter stmt in
+    let ca_total = code_annot ~filter:filter_ca stmt in
+    (match ca_total with
+     | [] when keep_empty -> ()
+     | [] -> fill_tables ca (Property.ip_of_code_annot kf stmt ca)
+     | [{ annot_content = AAllocation(_,alloc_total)}] ->
+       let alloc_e =
+         match ca_e with
+         | [] -> FreeAllocAny
+         | [ { annot_content = AAllocation(_, alloc') } as ca,_ ] ->
+           remove_code_annot_internal emitter ~kf stmt ca;
+           alloc'
+         | _ ->
+           Kernel.fatal
+             "More than one loop assigns clause for a statement. \
+              Annotations internal state broken."
+       in
+       (* we have assigns at statement level, just not from this
+          emitter yet, hence merge at emitter level regardless of keep_empty *)
+       let merged_e = merge_allocation ~keep_empty:false alloc_e alloc in
+       let new_a = { ca with annot_content = AAllocation(bhvs,merged_e) } in
+       let merged_total = merge_allocation ~keep_empty alloc_total alloc in
+       let ips =
+         Property.ip_of_code_annot kf stmt
+           { ca with annot_content = AAllocation(bhvs,merged_total) }
+       in
+       fill_tables new_a ips
+     | _ ->
+       Kernel.fatal
+         "More than one loop assigns clause for a statement. \
+          Annotations internal state broken.")
+  | APragma _ | AExtended _ ->
+    fill_tables ca (Property.ip_of_code_annot kf stmt ca)
 
 let add_assert e ?kf stmt a =
   let a = Logic_const.new_code_annotation (AAssert ([],Assert,a)) in
diff --git a/src/kernel_services/ast_data/annotations.mli b/src/kernel_services/ast_data/annotations.mli
index 1b65f10d2efddf9672f05edb67d95f407c459c3c..4bc65d57621c0a9b55781d84375b210e71206629 100644
--- a/src/kernel_services/ast_data/annotations.mli
+++ b/src/kernel_services/ast_data/annotations.mli
@@ -261,6 +261,7 @@ val fold_decreases:
 (**************************************************************************)
 
 val add_code_annot:
+  ?keep_empty:bool ->
   Emitter.t -> ?kf:kernel_function -> stmt -> code_annotation -> unit
 (** Add a new code annotation attached to the given statement. If [kf] is
     provided, the function runs faster.
@@ -270,9 +271,16 @@ val add_code_annot:
     Trying to associate more than one will result in a merge of the contracts.
 
     The same things happens with loop assigns and allocates/frees.
+    The [keep_empty] argument is only used for loop assigns
+    and loop allocates, where it is used to decide whether to add the given
+    code annot in case the corresponding category was empty. It defaults to
+    [true], which is sound wrt ACSL semantics of equating an absence of
+    annotation with assigns/allocates \everything.
 
     There can be at most one loop variant registered per statement.
     Attempting to register a second one will result in a fatal error.
+
+    @modify Frama-C+dev: add keep_empty argument
  *)
 
 val add_assert:
diff --git a/src/kernel_services/ast_queries/file.ml b/src/kernel_services/ast_queries/file.ml
index 676c7dbcf56e4c289c1ed465dc7d0b46cebce977..412c945293053724d86025b26b3ebab33b0905ce 100644
--- a/src/kernel_services/ast_queries/file.ml
+++ b/src/kernel_services/ast_queries/file.ml
@@ -701,7 +701,7 @@ let emit_all_statuses _ =
 let () = Ast.apply_after_computed emit_all_statuses
 
 let add_annotation kf st a =
-  Annotations.add_code_annot Emitter.end_user ~kf st a;
+  Annotations.add_code_annot ~keep_empty:false Emitter.end_user ~kf st a;
   (* Now check if the annotation is valid by construction
      (provided normalization is correct). *)
   match a.annot_content with
@@ -741,7 +741,7 @@ let synchronize_source_annot has_new_stmt kf =
                       st_ann.skind <- (Block (Cil.mkBlockNonScoping [st]));
                       has_new_stmt := true;
                       Annotations.add_code_annot
-                        Emitter.end_user ~kf st_ann annot;
+                        ~keep_empty:false Emitter.end_user ~kf st_ann annot;
                       (true, st_ann)
                     end else begin
                       add_annotation kf st annot;
diff --git a/src/kernel_services/ast_queries/logic_utils.ml b/src/kernel_services/ast_queries/logic_utils.ml
index 242feb6c6a054e09f4016d0168be57a3e4a9c38f..bf0b1e828dbf5c66679c255ebaf1c2e306b066c2 100644
--- a/src/kernel_services/ast_queries/logic_utils.ml
+++ b/src/kernel_services/ast_queries/logic_utils.ml
@@ -676,6 +676,18 @@ let rec add_attribute_glob_annot a g =
   | Dcustom_annot(c,n,al,l) -> Dcustom_annot(c,n,Cil.addAttribute a al, l)
   | Dextended (e,al,l) -> Dextended(e,Cil.addAttribute a al,l)
 
+let behavior_has_only_assigns bhvs =
+  bhvs.b_requires = [] && bhvs.b_assumes = [] &&
+  bhvs.b_post_cond = [] && bhvs.b_allocation = FreeAllocAny &&
+  bhvs.b_extended = []
+
+let funspec_has_only_assigns spec =
+  List.for_all behavior_has_only_assigns spec.spec_behavior &&
+  spec.spec_variant = None &&
+  spec.spec_terminates = None &&
+  spec.spec_complete_behaviors = [] &&
+  spec.spec_disjoint_behaviors = []
+
 let is_same_list f l1 l2 =
   try List.for_all2 f l1 l2 with Invalid_argument _ -> false
 
diff --git a/src/kernel_services/ast_queries/logic_utils.mli b/src/kernel_services/ast_queries/logic_utils.mli
index 1394e8408fbb3f58bf5200c5413f5dd934ca236c..b1e4074364ff4216e5558b9c7b449678405f7df5 100644
--- a/src/kernel_services/ast_queries/logic_utils.mli
+++ b/src/kernel_services/ast_queries/logic_utils.mli
@@ -312,6 +312,18 @@ val is_annot_next_stmt: code_annotation -> bool
 val add_attribute_glob_annot:
   attribute -> global_annotation -> global_annotation
 
+(** {2 Contracts } *)
+
+(** [true] if the behavior has only an assigns clause.
+    @since Frama-C+dev
+*)
+val behavior_has_only_assigns: behavior -> bool
+
+(** [true] if the only non-empty fields of the contract are assigns clauses
+    @since Frama-C+dev
+*)
+val funspec_has_only_assigns: funspec -> bool
+
 (** {2 Structural equality between annotations} *)
 
 val is_same_list: ('a -> 'a -> bool) -> 'a list -> 'a list -> bool
diff --git a/src/kernel_services/ast_transformations/inline.ml b/src/kernel_services/ast_transformations/inline.ml
index 36adae8427fe37790298e9a576b3e4847eaba0a6..b50798b0e70b5fa79be73f4ff36a9cd19e35fbc3 100644
--- a/src/kernel_services/ast_transformations/inline.ml
+++ b/src/kernel_services/ast_transformations/inline.ml
@@ -123,7 +123,7 @@ let inline_call loc caller callee return args =
              fd.sbody.bstmts <- inits @ fd.sbody.bstmts;
            end else begin
              (* put a statement contract on top of the function's body,
-                but only after we have assigned the formals. Not that there
+                but only after we have assigned the formals. Note that there
                 is no need to rename behaviors: they will only shadow behaviors
                 of the caller within callee's body, just as we need.
              *)
diff --git a/src/kernel_services/visitors/visitor.ml b/src/kernel_services/visitors/visitor.ml
index ce00bb69329b5755fcc162379f73ecaa3ce86d8e..cc918e122f511db3fe8acf500aaaa9c701204b9f 100644
--- a/src/kernel_services/visitors/visitor.ml
+++ b/src/kernel_services/visitors/visitor.ml
@@ -124,7 +124,8 @@ object(self)
                remove;
              List.iter
                (fun (e, a) ->
-                  Annotations.add_code_annot e ~kf:new_kf stmt a)
+                  Annotations.add_code_annot
+                    ~keep_empty:false e ~kf:new_kf stmt a)
                add)
           self#get_filling_actions
       end
diff --git a/src/plugins/aorai/aorai_visitors.ml b/src/plugins/aorai/aorai_visitors.ml
index 004941db6742cd482c8910fc0e7a1d3f3d0f197a..7ce63778b530c98bf41e20013662ec11946bf0c9 100644
--- a/src/plugins/aorai/aorai_visitors.ml
+++ b/src/plugins/aorai/aorai_visitors.ml
@@ -272,7 +272,10 @@ let update_loop_assigns kf stmt state vi code_annot =
         (AAssigns (bhvs, Logic_utils.concat_assigns old_assigns assigns))
     | _ -> Aorai_option.fatal "Expecting an assigns clause here"
   in
-  Annotations.add_code_annot Aorai_option.emitter ~kf stmt new_assigns
+  (* we're putting the annotation on a new statement, with an empty
+     loop assigns by construction. Don't keep_empty *)
+  Annotations.add_code_annot
+    ~keep_empty:false Aorai_option.emitter ~kf stmt new_assigns
 
 let get_action_post_cond kf ?init_trans return_states =
   let to_consider pre_state int_states =
diff --git a/src/plugins/aorai/tests/aorai/oracle/assigns.0.res.oracle b/src/plugins/aorai/tests/aorai/oracle/assigns.0.res.oracle
index 6c66a3027f5e460bd9c2f940af4e5161e5b44de4..ef6224bb447e03550381c8c205c6feb41da0f37e 100644
--- a/src/plugins/aorai/tests/aorai/oracle/assigns.0.res.oracle
+++ b/src/plugins/aorai/tests/aorai/oracle/assigns.0.res.oracle
@@ -275,8 +275,8 @@ int main(void)
   /*@ ghost main_pre_func(); */
   /*@ assigns X; */
   X ++;
-  /*@ assigns aorai_CurOpStatus, aorai_CurOperation, S1, S2, S_in_f, Sf,
-              in_main, X;
+  /*@ assigns X, aorai_CurOpStatus, aorai_CurOperation, S1, S2, S_in_f, Sf,
+              in_main;
   */
   f();
   /*@ ghost main_post_func(X); */
diff --git a/src/plugins/aorai/tests/aorai/oracle/assigns.1.res.oracle b/src/plugins/aorai/tests/aorai/oracle/assigns.1.res.oracle
index 2434556b05d580bac96d12dfa195f2e84cc9ee09..7e00479c453e3e785b4726a59de9906430c04c39 100644
--- a/src/plugins/aorai/tests/aorai/oracle/assigns.1.res.oracle
+++ b/src/plugins/aorai/tests/aorai/oracle/assigns.1.res.oracle
@@ -213,7 +213,7 @@ int main(void)
   /*@ ghost main_pre_func(); */
   /*@ assigns X; */
   X ++;
-  /*@ assigns aorai_CurOpStatus, aorai_CurOperation, aorai_CurStates, X; */
+  /*@ assigns X, aorai_CurOpStatus, aorai_CurOperation, aorai_CurStates; */
   f();
   /*@ ghost main_post_func(X); */
   return X;
diff --git a/src/plugins/e-acsl/src/code_generator/label.ml b/src/plugins/e-acsl/src/code_generator/label.ml
index 9225fdb571810e50ef40d96f5953ffa23cf573aa..f06c2232dfc45e230fda88c989d10349a949426d 100644
--- a/src/plugins/e-acsl/src/code_generator/label.ml
+++ b/src/plugins/e-acsl/src/code_generator/label.ml
@@ -48,7 +48,7 @@ let move kf ~old new_stmt =
     List.iter
       (fun (e, ca) ->
          Annotations.remove_code_annot ~kf e old ca;
-         Annotations.add_code_annot ~kf e new_stmt ca)
+         Annotations.add_code_annot ~keep_empty:false ~kf e new_stmt ca)
       l;
     (* update the gotos of the function jumping to one of the labels *)
     let f =
diff --git a/tests/spec/loop_assigns_generated.i b/tests/spec/loop_assigns_generated.i
index 79fba31ec9f994b9302948f03a58372f3ec0ab71..f85c8c5a1ccec3e842a459fe4f1df20aee349285 100644
--- a/tests/spec/loop_assigns_generated.i
+++ b/tests/spec/loop_assigns_generated.i
@@ -7,4 +7,6 @@ void f() {
   int i = 0;
   /*@ loop assigns i; */
   while (i< 10) { i++; }
+
+  while (i>0) { i--; }
 }
diff --git a/tests/spec/loop_assigns_generated.ml b/tests/spec/loop_assigns_generated.ml
index 30d64b3ffebd318e7ce1585b73f21428fc2e389e..7e9340737ab5ed66e19981bec6e1ba8d3d2b5de8 100644
--- a/tests/spec/loop_assigns_generated.ml
+++ b/tests/spec/loop_assigns_generated.ml
@@ -3,7 +3,7 @@ open Cil_types
 let e1 = Emitter.(create "emitter1" [ Code_annot ] [] [])
 let e2 = Emitter.(create "emitter2" [ Code_annot ] [] [])
 
-let add_assigns e kf stmt v =
+let mk_assigns e v =
   let lv = Cil.cvar_to_lvar v in
   let term_v  = Logic_const.tvar lv in
   let named_term_v =
@@ -11,12 +11,15 @@ let add_assigns e kf stmt v =
       term_name = ("added_by_"^(Emitter.get_name e))::term_v.term_name }
   in
   let id_v = Logic_const.new_identified_term named_term_v  in
-  Annotations.add_code_annot e ~kf stmt
+  Writes[id_v,FromAny]
+
+let add_assigns ?(keep_empty=false) e kf stmt v =
+  Annotations.add_code_annot ~keep_empty e ~kf stmt
     (Logic_const.new_code_annotation
-       (AAssigns ([], Writes [id_v, FromAny])));
+       (AAssigns ([], mk_assigns e v)));
   Filecheck.check_ast ("after insertion of loop assigns " ^ v.vname)
 
-let add_allocates e kf stmt v =
+let mk_allocates e v =
   let lv = Cil.cvar_to_lvar v in
   let term_v = Logic_const.tvar lv in
   let named_term_v =
@@ -24,11 +27,23 @@ let add_allocates e kf stmt v =
       term_name = ("added_by_"^(Emitter.get_name e))::term_v.term_name }
   in
   let id_v = Logic_const.new_identified_term named_term_v in
-  Annotations.add_code_annot e ~kf stmt
-    (Logic_const.new_code_annotation
-       (AAllocation([],FreeAlloc ([],[id_v]))));
+  FreeAlloc ([],[id_v])
+
+let add_allocates ?(keep_empty=false) e kf stmt v =
+  Annotations.add_code_annot ~keep_empty e ~kf stmt
+    (Logic_const.new_code_annotation (AAllocation([],mk_allocates e v)));
   Filecheck.check_ast ("after insertion of loop allocates " ^ v.vname )
 
+let check_only_one f =
+  let seen = ref false in
+  fun _ a ->
+    if f a then begin
+      assert (not !seen);
+      seen := true
+    end
+
+let filter_category f _ a acc = if f a then a :: acc else acc
+
 let main () =
   Ast.compute();
   let kf = Globals.Functions.find_by_name "f" in
@@ -37,14 +52,44 @@ let main () =
     List.find
       (fun s -> match s.skind with Loop _ -> true | _ -> false) def.sallstmts
   in
+  let s2 =
+    List.find
+      (fun s' -> s!=s' && (match s'.skind with Loop _ -> true | _ -> false))
+      def.sallstmts
+  in
+  let s3 = Kernel_function.find_return kf in
   let j = Cil.makeLocalVar def ~insert:true "j" Cil.intType in
   let k = Cil.makeLocalVar def ~insert:true "k" Cil.intType in
+  let l = Cil.makeLocalVar def ~insert:true "l" Cil.intType in
   let p = Cil.makeLocalVar def ~insert:true "p" Cil.intPtrType in
   let q = Cil.makeLocalVar def ~insert:true "q" Cil.intPtrType in
+  let r = Cil.makeLocalVar def ~insert:true "r" Cil.intPtrType in
   add_assigns e1 kf s j;
   add_assigns e2 kf s k;
+  add_assigns e1 kf s l;
+  add_assigns ~keep_empty:true e2 kf s2 j;
+  Annotations.add_assigns ~keep_empty:true e1 kf ~stmt:s3 (mk_assigns e1 k);
   add_allocates e1 kf s p;
   add_allocates e2 kf s q;
+  add_allocates e1 kf s r;
+  add_allocates ~keep_empty:true e2 kf s2 p;
+  Annotations.add_allocates ~keep_empty:true e1 kf ~stmt:s3 (mk_allocates e1 k);
+  Annotations.iter_code_annot (check_only_one Logic_utils.is_assigns) s;
+  Annotations.iter_code_annot (check_only_one Logic_utils.is_allocation) s;
+  let lassigns =
+    Annotations.fold_code_annot (filter_category Logic_utils.is_assigns) s []
+  in
+  assert (List.length lassigns = 1);
+  let lalloc =
+    Annotations.fold_code_annot (filter_category Logic_utils.is_allocation) s []
+  in
+  assert (List.length lalloc = 1);
+  ignore
+    (Property_status.get
+       (Property.ip_of_code_annot_single kf s (List.hd lassigns)));
+  ignore
+    (Property_status.get
+       (Property.ip_of_code_annot_single kf s (List.hd lalloc)));
   File.pretty_ast ()
 
 let () = Db.Main.extend main
diff --git a/tests/spec/oracle/assigns_array.res.oracle b/tests/spec/oracle/assigns_array.res.oracle
index 778268bcc22bfd3fab2ede1fbea4c8539be364f5..6e4846e218d9c35e30edec7a74b34aa5e7f1492c 100644
--- a/tests/spec/oracle/assigns_array.res.oracle
+++ b/tests/spec/oracle/assigns_array.res.oracle
@@ -36,8 +36,8 @@ int h(int reset, int n)
   int i;
   int r = 0;
   i = 0;
-  /*@ for bar: loop assigns \nothing;
-      for foo: loop assigns Tab[0 .. i]; */
+  /*@ for foo: loop assigns Tab[0 .. i];
+      for bar: loop assigns \nothing; */
   while (i < n) {
     r += Tab[i];
     if (reset) Tab[i] = 0;
diff --git a/tests/spec/oracle/bts0578.res.oracle b/tests/spec/oracle/bts0578.res.oracle
index f25516888c6bafe4e5e1910dfa516174a2f9c2d2..7c6d8517d86a1a289b4f02db5da2cf00e985955e 100644
--- a/tests/spec/oracle/bts0578.res.oracle
+++ b/tests/spec/oracle/bts0578.res.oracle
@@ -7,11 +7,11 @@ void main(void)
   int i;
   int t[10];
   i = 0;
-  /*@ loop invariant \true;
-      loop assigns t[0 .. i];
+  /*@ loop assigns t[0 .. i];
+      loop invariant \true;
+      for foo: loop assigns t[0 .. i];
       for foo: loop invariant \true;
       for foo: loop invariant \true;
-      for foo: loop assigns t[0 .. i];
       loop variant 0;
   */
   while (i < 10) {
diff --git a/tests/spec/oracle/loop_assigns_generated.res.oracle b/tests/spec/oracle/loop_assigns_generated.res.oracle
index a30771642fda30636515438444f889311c81bf84..64f6610b539d47a1612fa69853c4760b6970cae5 100644
--- a/tests/spec/oracle/loop_assigns_generated.res.oracle
+++ b/tests/spec/oracle/loop_assigns_generated.res.oracle
@@ -2,15 +2,20 @@
 /* Generated by Frama-C */
 void f(void)
 {
+  int *r;
   int *q;
   int *p;
+  int l;
   int k;
   int j;
   int i = 0;
-  /*@ loop allocates (added_by_emitter1: p), (added_by_emitter2: q);
-      loop assigns i, (added_by_emitter1: j), (added_by_emitter2: k);
+  /*@ loop assigns i, (added_by_emitter1: j), (added_by_emitter1: l),
+                   (added_by_emitter2: k);
+      loop allocates
+      (added_by_emitter1: p), (added_by_emitter1: r), (added_by_emitter2: q);
   */
   while (i < 10) i ++;
+  while (i > 0) i --;
   return;
 }
 
diff --git a/tests/syntax/oracle/asm_with_contracts.res.oracle b/tests/syntax/oracle/asm_with_contracts.res.oracle
index de45604755bf733ffeb4e6885baede947f7b9fb3..aab309b2eeb8fce6f14a24fcacbb3bd5d283cdc1 100644
--- a/tests/syntax/oracle/asm_with_contracts.res.oracle
+++ b/tests/syntax/oracle/asm_with_contracts.res.oracle
@@ -9,9 +9,9 @@ int f(int z)
   int y = 2;
   /*@ assigns y; */
   __asm__ ("mov %1, %0\n\t" : "=r" (y) : "r" (x));
+  /*@ for b: assigns x, y; */
   /*@ assigns x;
       assigns x \from y; */
-  /*@ for b: assigns x, y; */
   __asm__ ("mov %1, %0\n\t" : "=r" (x) : "r" (y));
   /*@ assigns x, y;