From 7295a3eb6cc59c2f2a235cd7b39da7277ea67117 Mon Sep 17 00:00:00 2001
From: Lionel Blatter <lionel.blatter@kit.edu>
Date: Mon, 6 Jul 2020 16:19:48 +0200
Subject: [PATCH] Add support of recursive extended global annotations

---
 src/kernel_internals/parsing/logic_lexer.mll  |   7 +-
 src/kernel_internals/parsing/logic_parser.mly |  34 +++-
 src/kernel_internals/typing/mergecil.ml       |   8 +-
 src/kernel_services/ast_data/cil_types.mli    |   2 +-
 .../ast_printing/cil_types_debug.ml           |   3 +-
 .../ast_printing/logic_print.ml               |  12 +-
 .../ast_queries/acsl_extension.ml             | 168 +++++++++++++++---
 .../ast_queries/acsl_extension.mli            |  14 ++
 src/kernel_services/ast_queries/cil.ml        |   4 +-
 src/kernel_services/ast_queries/logic_env.ml  |  11 +-
 src/kernel_services/ast_queries/logic_env.mli |   8 +
 .../ast_queries/logic_typing.ml               |  18 +-
 .../ast_queries/logic_typing.mli              |  17 ++
 .../ast_queries/logic_utils.ml                |   4 +-
 src/kernel_services/parsetree/logic_ptree.mli |  12 +-
 15 files changed, 275 insertions(+), 47 deletions(-)

diff --git a/src/kernel_internals/parsing/logic_lexer.mll b/src/kernel_internals/parsing/logic_lexer.mll
index b86e9200489..d31c8c679e0 100644
--- a/src/kernel_internals/parsing/logic_lexer.mll
+++ b/src/kernel_internals/parsing/logic_lexer.mll
@@ -207,7 +207,12 @@
             match Logic_env.extension_category s with
             | exception Not_found -> None
             | Cil_types.Ext_contract -> Some (EXT_CONTRACT s)
-            | Cil_types.Ext_global -> Some (EXT_GLOBAL s)
+            | Cil_types.Ext_global ->
+              begin
+                match Logic_env.is_extension_block s with
+                | false -> Some (EXT_GLOBAL s)
+                | true -> Some (EXT_GLOBAL_BLOCK s)
+              end
             | Cil_types.Ext_code_annot _ -> Some (EXT_CODE_ANNOT s)
           end
           else None
diff --git a/src/kernel_internals/parsing/logic_parser.mly b/src/kernel_internals/parsing/logic_parser.mly
index 0bff4b69eda..121ad96facc 100644
--- a/src/kernel_internals/parsing/logic_parser.mly
+++ b/src/kernel_internals/parsing/logic_parser.mly
@@ -107,6 +107,8 @@
 
   let loc_decl d = { decl_node = d; decl_loc = loc () }
 
+  let loc_ext d = { extended_node = d; extended_loc = loc () }
+
   let concat_froms a1 a2 =
     let compare_pair (b1,_) (b2,_) = is_same_lexpr b1 b2 in
     (* NB: the following has an horrible complexity, but the order of
@@ -253,7 +255,7 @@
 %token REQUIRES ENSURES ALLOCATES FREES ASSIGNS LOOP NOTHING SLICE IMPACT PRAGMA FROM
 %token CHECK_REQUIRES CHECK_LOOP CHECK_INVARIANT CHECK_LEMMA
 %token CHECK_ENSURES CHECK_EXITS CHECK_CONTINUES CHECK_BREAKS CHECK_RETURNS
-%token <string> EXT_CODE_ANNOT EXT_GLOBAL EXT_CONTRACT
+%token <string> EXT_CODE_ANNOT EXT_GLOBAL EXT_GLOBAL_BLOCK EXT_CONTRACT
 %token EXITS BREAKS CONTINUES RETURNS
 %token VOLATILE READS WRITES
 %token LOGIC PREDICATE INDUCTIVE AXIOMATIC AXIOM LEMMA LBRACE RBRACE
@@ -1532,13 +1534,34 @@ decl:
 | type_annot {LDtype_annot $1}
 | model_annot {LDmodel_annot $1}
 | logic_def  { $1 }
-| EXT_GLOBAL grammar_extension SEMICOLON {
-    let processed = Logic_env.preprocess_extension $1 $2 in
-	  LDextended ($1, processed)
-  }
+| ext_decl { LDextended $1 }
 | deprecated_logic_decl { $1 }
 ;
 
+ext_decl:
+| EXT_GLOBAL grammar_extension SEMICOLON {
+     let processed = Logic_env.preprocess_extension $1 $2 in
+     Ext_lexpr($1, processed)
+   }
+| EXT_GLOBAL_BLOCK any_identifier LBRACE ext_decls RBRACE {
+    let processed_id,processed_block =
+       Logic_env.preprocess_extension_block $1 ($2,$4)
+    in
+    Ext_extension($1,processed_id,processed_block)
+   }
+;
+
+ext_decls:
+| /* epsilon */
+    { [] }
+| ext_decl_loc ext_decls
+    { $1::$2 }
+;
+
+ext_decl_loc:
+| ext_decl { loc_ext $1 }
+;
+
 volatile_opt:
 | /* empty */ { None, None }
 | READS any_identifier volatile_opt
@@ -1893,6 +1916,7 @@ is_acsl_spec:
 is_acsl_decl_or_code_annot:
 | EXT_CODE_ANNOT { $1 }
 | EXT_GLOBAL     { $1 }
+| EXT_GLOBAL_BLOCK     { $1 }
 | ASSUMES   { "assumes" }
 | ASSERT    { "assert" }
 | CHECK     { "check" }
diff --git a/src/kernel_internals/typing/mergecil.ml b/src/kernel_internals/typing/mergecil.ml
index 21989d0a2e8..ed8914b346e 100644
--- a/src/kernel_internals/typing/mergecil.ml
+++ b/src/kernel_internals/typing/mergecil.ml
@@ -452,7 +452,7 @@ module ExtMerging =
           | Ext_id i -> Datatype.Int.hash i
           | Ext_terms terms -> 29 * (hash_list Logic_utils.hash_term terms)
           | Ext_preds preds -> 47 * (hash_list Logic_utils.hash_predicate preds)
-          | Ext_annot annots -> 5 * (hash_list hash annots)
+          | Ext_annot (id, annots) -> Datatype.String.hash id + 5 * (hash_list hash annots)
         in
         Datatype.String.hash e.ext_name + 5 * hash_ext_kind e.ext_kind
       let rec compare (e1 : acsl_extension) (e2 : acsl_extension) =
@@ -466,8 +466,10 @@ module ExtMerging =
           | Ext_preds p1, Ext_preds p2 ->
             Extlib.list_compare Logic_utils.compare_predicate p1 p2
           | Ext_preds _, _ -> 1 | _, Ext_preds _ -> -1
-          | Ext_annot a1 , Ext_annot a2  ->
-            Extlib.list_compare compare a1 a2
+          | Ext_annot (id1, a1) , Ext_annot (id2, a2)  ->
+            match String.compare id1 id2 with
+            | 0 -> Extlib.list_compare compare a1 a2
+            | n -> n
         in
         let res = Datatype.String.compare e1.ext_name e2.ext_name in
         if res <> 0 then res
diff --git a/src/kernel_services/ast_data/cil_types.mli b/src/kernel_services/ast_data/cil_types.mli
index 5f182a4a885..c13132d03e3 100644
--- a/src/kernel_services/ast_data/cil_types.mli
+++ b/src/kernel_services/ast_data/cil_types.mli
@@ -1692,7 +1692,7 @@ and acsl_extension_kind =
   | Ext_id of int (** id used internally by the extension itself. *)
   | Ext_terms of term list
   | Ext_preds of predicate list
-  | Ext_annot of acsl_extension list
+  | Ext_annot of string * acsl_extension list
   (** a list of predicates, the most common case of for extensions *)
 
 (** Where are we expected to find corresponding extension keyword.
diff --git a/src/kernel_services/ast_printing/cil_types_debug.ml b/src/kernel_services/ast_printing/cil_types_debug.ml
index 45e5515947e..9ce30a3dac1 100644
--- a/src/kernel_services/ast_printing/cil_types_debug.ml
+++ b/src/kernel_services/ast_printing/cil_types_debug.ml
@@ -921,7 +921,8 @@ and pp_acsl_extension_kind fmt = function
   | Ext_id(int) -> Format.fprintf fmt "Ext_id(%a)"  pp_int int
   | Ext_terms(term_list) -> Format.fprintf fmt "Ext_terms(%a)"  (pp_list pp_term) term_list
   | Ext_preds(predicate_list) -> Format.fprintf fmt "Ext_preds(%a)"  (pp_list pp_predicate) predicate_list
-  | Ext_annot(annotation_list ) -> Format.fprintf fmt "Ext_annots(%a)" (pp_list pp_acsl_extension) annotation_list
+  | Ext_annot(string,annotation_list) ->
+    Format.fprintf fmt "Ext_annots(%a,%a)" pp_string string (pp_list pp_acsl_extension) annotation_list
 
 and pp_behavior fmt behavior =
   Format.fprintf fmt
diff --git a/src/kernel_services/ast_printing/logic_print.ml b/src/kernel_services/ast_printing/logic_print.ml
index 1b3c0544fc3..59f6809727d 100644
--- a/src/kernel_services/ast_printing/logic_print.ml
+++ b/src/kernel_services/ast_printing/logic_print.ml
@@ -311,6 +311,15 @@ let print_model_annot fmt ty =
     (print_logic_type None) ty.model_type
     ty.model_name
 
+let rec print_extended_decl fmt d =
+  let aux fmt d = print_extended_decl fmt d.extended_node in
+  match d with
+  | Ext_lexpr(name,d) ->
+    fprintf fmt "@[<2>%s@ %a@]" name (pp_list ~sep:",@ " print_lexpr) d
+  | Ext_extension(name,id,d) ->
+    fprintf fmt "@[<2>%s@ %s@ {@\n%a@]@\n}" name id
+      (pp_list ~sep:"@\n" aux) d
+
 let rec print_decl fmt d =
   match d.decl_node with
   | LDlogic_def(name,labels,tvar,rt,prms,body) ->
@@ -374,8 +383,7 @@ let rec print_decl fmt d =
       (pp_list ~pre:"@[" ~sep:",@ " ~suf:"@]" print_lexpr) tsets
       (pp_opt ~pre:"@ reads@ " pp_print_string) read
       (pp_opt ~pre:"@ writes@ " pp_print_string) write
-  | LDextended (s,l) ->
-    fprintf fmt "@[<2>%s@ %a@]" s (pp_list ~sep:",@ " print_lexpr) l
+  | LDextended d -> print_extended_decl fmt d
 
 let print_deps fmt deps =
   match deps with
diff --git a/src/kernel_services/ast_queries/acsl_extension.ml b/src/kernel_services/ast_queries/acsl_extension.ml
index 0d3efe6680d..3fb8572e723 100644
--- a/src/kernel_services/ast_queries/acsl_extension.ml
+++ b/src/kernel_services/ast_queries/acsl_extension.ml
@@ -28,16 +28,36 @@ type extension_preprocessor =
   lexpr list -> lexpr list
 type extension_typer =
   typing_context -> location -> lexpr list -> acsl_extension_kind
+type extension_preprocessor_block =
+  string * extended_decl list -> string * extended_decl list
+type extension_typer_block =
+  typing_context -> location -> string * extended_decl list -> acsl_extension_kind
 type extension_visitor =
   Cil.cilVisitor -> acsl_extension_kind -> acsl_extension_kind Cil.visitAction
 type extension_printer =
   Printer_api.extensible_printer_type -> Format.formatter ->
   acsl_extension_kind -> unit
-type extension = {
+type extension_part1 = {
+  preprocessor: extension_preprocessor ;
+  typer: extension_typer ;
+  status: bool ;
+}
+type extension_part2 = {
   category: ext_category ;
+  visitor: extension_visitor ;
+  printer: extension_printer ;
+  short_printer: extension_printer ;
+}
+type extension_part3 = {
+  preprocessor: extension_preprocessor_block ;
+  typer: extension_typer_block ;
   status: bool ;
+}
+type extension = {
   preprocessor: extension_preprocessor ;
   typer: extension_typer ;
+  status: bool ;
+  category: ext_category ;
   visitor: extension_visitor ;
   printer: extension_printer ;
   short_printer: extension_printer ;
@@ -47,10 +67,13 @@ let default_printer printer fmt = function
   | Ext_id i -> Format.fprintf fmt "%d" i
   | Ext_terms ts -> Pretty_utils.pp_list ~sep:",@ " printer#term fmt ts
   | Ext_preds ps -> Pretty_utils.pp_list ~sep:",@ " printer#predicate fmt ps
-  | Ext_annot an -> Pretty_utils.pp_list ~sep:",@ " printer#extended fmt an
+  | Ext_annot (id,an) -> Format.fprintf fmt "@[<v 2>@[%s@ {@]@\n%a}@]@\n"
+                           id (Pretty_utils.pp_list ~pre:"@[<v 0>" ~suf:"@]@\n" ~sep:"@\n"
+                                 printer#extended) an
 
-let default_short_printer name _printer fmt _ext_kind =
-  Format.fprintf fmt "%s" name
+let default_short_printer name printer fmt = function
+  | Ext_id _ | Ext_terms _ | Ext_preds _ -> Format.fprintf fmt "%s" name
+  | Ext_annot (id,an) -> Format.fprintf fmt "%s@ {@\n%a@]@\n}" id (Pretty_utils.pp_list ~sep:"@\n" printer#extended) an
 
 let make
     name category
@@ -59,15 +82,39 @@ let make
     ?(visitor=fun _ _ -> Cil.DoChildren)
     ?(printer=default_printer)
     ?(short_printer=default_short_printer name)
-    status =
-  { category; status; preprocessor; typer; visitor; printer; short_printer }
+    status : extension_part1*extension_part2 =
+  { preprocessor; typer; status},{ category; visitor; printer; short_printer }
+
+let make_block
+    name category
+    ?(preprocessor=Extlib.id)
+    typer
+    ?(visitor=fun _ _ -> Cil.DoChildren)
+    ?(printer=default_printer)
+    ?(short_printer=default_short_printer name)
+    status : extension_part3*extension_part2 =
+  { preprocessor; typer; status},{ category; visitor; printer; short_printer }
 
 module Extensions = struct
+  (*hash table for  category, visitor, printer and short_priner of extensions*)
   let ext_tbl = Hashtbl.create 5
 
-  let find name =
-    try Hashtbl.find ext_tbl name
-    with Not_found ->
+  (*hash table for status, preprocessor and typer of standard extensions*)
+  let ext_sta_tbl = Hashtbl.create 5
+
+  (*hash table for status, preprocessor and visitor of block extensions*)
+  let ext_block_tbl = Hashtbl.create 5
+
+  let find_part1 name :extension_part1 =
+    try Hashtbl.find ext_sta_tbl name with Not_found ->
+      Kernel.fatal ~current:true "unsupported clause of name '%s'" name
+
+  let find_part2 name :extension_part2 =
+    try Hashtbl.find ext_tbl name with Not_found ->
+      Kernel.fatal ~current:true "unsupported clause of name '%s'" name
+
+  let find_part3 name :extension_part3 =
+    try Hashtbl.find ext_block_tbl name with Not_found ->
       Kernel.fatal ~current:true "unsupported clause of name '%s'" name
 
   (* [Logic_lexer] can ask for something that is not a category, which is not
@@ -76,21 +123,60 @@ module Extensions = struct
 
   let is_extension = Hashtbl.mem ext_tbl
 
+  let is_extension_block = Hashtbl.mem ext_block_tbl
+
   let register
       cat name ?preprocessor typer ?visitor ?printer ?short_printer status =
-    let info =
+    let info1,info2 =
       make name cat ?preprocessor typer ?visitor ?printer ?short_printer status
     in
     if is_extension name then
       Kernel.warning ~wkey:Kernel.wkey_acsl_extension
         "Trying to register ACSL extension %s twice. Ignoring second extension"
         name
-    else Hashtbl.add ext_tbl name info
+    else
+      begin
+        Hashtbl.add ext_sta_tbl name info1;
+        Hashtbl.add ext_tbl name info2
+      end
+
+ let register_block
+      cat name ?preprocessor typer ?visitor ?printer ?short_printer status =
+    let info1,info2 =
+      make_block name cat ?preprocessor typer ?visitor ?printer ?short_printer status
+    in
+    if is_extension name then
+      Kernel.warning ~wkey:Kernel.wkey_acsl_extension
+        "Trying to register ACSL extension %s twice. Ignoring second extension"
+        name
+    else
+      begin
+        Hashtbl.add ext_block_tbl name info1;
+        Hashtbl.add ext_tbl name info2
+      end
 
-  let preprocess name = (find name).preprocessor
+ let preprocess name = (find_part1 name).preprocessor
+
+ let preprocess_block name = (find_part3 name).preprocessor
+
+ let typing name typing_context loc es =
+    let ext_info = find_part1 name in
+    let status = ext_info.status in
+    let typer =  ext_info.typer in
+    let normal_error = ref false in
+    let has_error () = normal_error := true in
+    let wrapper =
+      typing_context.on_error (typer typing_context loc) has_error
+    in
+    try status, wrapper es
+    with
+    | (Log.AbortError _ | Log.AbortFatal _) as exn -> raise exn
+    | exn when not !normal_error ->
+      Kernel.fatal "Typechecking ACSL extension %s raised exception %s"
+        name (Printexc.to_string exn)
 
-  let typing name typing_context loc es =
-    let ext_info = find name in
+ let typing_block name typing_context loc es =
+    let ext_info = find_part3 name in
     let status = ext_info.status in
     let typer =  ext_info.typer in
     let normal_error = ref false in
@@ -105,14 +191,14 @@ module Extensions = struct
       Kernel.fatal "Typechecking ACSL extension %s raised exception %s"
         name (Printexc.to_string exn)
 
-  let visit name = (find name).visitor
+  let visit name = (find_part2 name).visitor
 
   let print name printer fmt kind =
-    let pp = (find name).printer printer in
+    let pp = (find_part2 name).printer printer in
     Format.fprintf fmt "@[<hov 2>%s %a;@]" name pp kind
 
   let short_print name printer fmt kind =
-    let pp = (find name).short_printer in
+    let pp = (find_part2 name).short_printer in
     Format.fprintf fmt "%a" (pp printer) kind
 end
 
@@ -122,6 +208,8 @@ let register_behavior =
   Extensions.register Ext_contract
 let register_global =
   Extensions.register Ext_global
+let register_global_block =
+  Extensions.register_block Ext_global
 let register_code_annot =
   Extensions.register (Ext_code_annot Ext_here)
 let register_code_annot_next_stmt =
@@ -137,10 +225,13 @@ let () =
   Logic_env.set_extension_handler
     ~category:Extensions.category
     ~is_extension: Extensions.is_extension
-    ~preprocess: Extensions.preprocess ;
+    ~preprocess: Extensions.preprocess
+    ~is_extension_block: Extensions.is_extension_block
+    ~preprocess_block: Extensions.preprocess_block;
   Logic_typing.set_extension_handler
     ~is_extension: Extensions.is_extension
-    ~typer: Extensions.typing ;
+    ~typer: Extensions.typing
+    ~typer_block: Extensions.typing_block ;
   Cil.set_extension_handler
     ~visit: Extensions.visit ;
   Cil_printer.set_extension_handler
@@ -149,21 +240,45 @@ let () =
 
 (* For Deprecation: *)
 
-let deprecated_replace name ext = Hashtbl.add Extensions.ext_tbl name ext
+let deprecated_replace name ext =
+  let info1:extension_part1 = {
+    preprocessor = ext.preprocessor ;
+    typer = ext.typer ;
+    status = ext.status ;
+  }
+  in
+  let info2:extension_part2  = {
+    category = ext.category ;
+    visitor = ext.visitor ;
+    printer = ext.printer ;
+    short_printer = ext.printer ;
+  }
+  in
+  Hashtbl.add Extensions.ext_sta_tbl name info1;
+  Hashtbl.add Extensions.ext_tbl name info2
 
 let strong_cat = Hashtbl.create 5
 
 let default_typer _typing_context _loc _l = assert false
 
+let merge ((info1:extension_part1),(info2:extension_part2)) :extension =
+    {preprocessor = info1.preprocessor ;
+    typer = info1.typer ;
+    status = info1.status ;
+    category = info2.category ;
+    visitor = info2.visitor ;
+    printer = info2.printer ;
+    short_printer = info2.printer}
 
 let deprecated_find ?(strong=true) name cat op_name =
-  match Hashtbl.find_opt Extensions.ext_tbl name with
+  match Hashtbl.find_opt Extensions.ext_sta_tbl name with
   | None ->
     if strong then Hashtbl.add strong_cat name cat ;
-    (make name cat default_typer false)
-  | Some ext ->
+    merge (make name cat default_typer false)
+  | Some ext1 ->
+    let ext2 = Extensions.find_part2 name in
     if strong && Hashtbl.mem strong_cat name then begin
-      if ext.category = cat then ext
+      if ext2.category = cat then merge (ext1,ext2)
       else
         Kernel.fatal
           "Registring %s for %s: this extension already exists for another \
@@ -171,8 +286,9 @@ let deprecated_find ?(strong=true) name cat op_name =
           op_name name
     end else if strong then begin
       Hashtbl.add strong_cat name cat ;
-      { ext with category = cat }
-    end else ext
+      let ext2 = { ext2 with category = cat } in
+      merge (ext1,ext2)
+    end else merge (ext1,ext2)
 
 let deprecated_register_typing name cat status typer =
   deprecated_replace name
diff --git a/src/kernel_services/ast_queries/acsl_extension.mli b/src/kernel_services/ast_queries/acsl_extension.mli
index c2778433fb9..c615564b73e 100644
--- a/src/kernel_services/ast_queries/acsl_extension.mli
+++ b/src/kernel_services/ast_queries/acsl_extension.mli
@@ -38,6 +38,10 @@ type extension_typer =
 (** Visitor functions for ACSL extensions *)
 type extension_visitor =
   Cil.cilVisitor -> acsl_extension_kind -> acsl_extension_kind Cil.visitAction
+type extension_preprocessor_block =
+  string * extended_decl list -> string * extended_decl list
+type extension_typer_block =
+  typing_context -> location -> string * extended_decl list -> acsl_extension_kind
 (** Pretty printers for ACSL extensions *)
 type extension_printer =
   Printer_api.extensible_printer_type -> Format.formatter ->
@@ -106,6 +110,16 @@ val register_global:
   ?printer:extension_printer -> ?short_printer:extension_printer -> bool ->
   unit
 
+(** Registers extension for global block annotation. See [register_behavior].
+
+    @plugin development guide
+*)
+val register_global_block:
+  string -> ?preprocessor:extension_preprocessor_block -> extension_typer_block ->
+  ?visitor:extension_visitor ->
+  ?printer:extension_printer -> ?short_printer:extension_printer -> bool ->
+  unit
+
 (** Registers extension for code annotation to be evaluated at _current_
     program point. See [register_behavior].
 
diff --git a/src/kernel_services/ast_queries/cil.ml b/src/kernel_services/ast_queries/cil.ml
index f99204de3a4..6f90ae11d73 100644
--- a/src/kernel_services/ast_queries/cil.ml
+++ b/src/kernel_services/ast_queries/cil.ml
@@ -1994,9 +1994,9 @@ and childrenCilExtended vis p =
   | Ext_preds preds ->
     let preds' = mapNoCopy (visitCilPredicate vis) preds in
     if preds == preds' then p else Ext_preds preds'
-  | Ext_annot annots ->
+  | Ext_annot (id,annots) ->
     let annots' = mapNoCopy (visitCilExtended vis) annots in
-    if annots == annots' then p else Ext_annot annots'
+    if annots == annots' then p else Ext_annot (id,annots')
 
 and visitCilPredicates vis ps = mapNoCopy (visitCilIdPredicate vis) ps
 
diff --git a/src/kernel_services/ast_queries/logic_env.ml b/src/kernel_services/ast_queries/logic_env.ml
index dde299d846e..18381986562 100644
--- a/src/kernel_services/ast_queries/logic_env.ml
+++ b/src/kernel_services/ast_queries/logic_env.ml
@@ -29,23 +29,32 @@ module Extensions = struct
   let ref_is_extension = ref (fun _ -> assert false)
   let ref_category = ref (fun _ -> assert false)
   let ref_preprocess = ref (fun _ -> assert false)
+  let ref_is_extension_block = ref (fun _ -> assert false)
+  let ref_preprocess_block = ref (fun _ -> assert false)
 
-  let set_extension_handler ~category ~is_extension ~preprocess =
+  let set_extension_handler
+      ~category ~is_extension ~preprocess ~is_extension_block ~preprocess_block =
     assert (not !initialized) ;
     ref_is_extension := is_extension ;
     ref_category := category ;
     ref_preprocess := preprocess ;
+    ref_is_extension_block := is_extension_block;
+    ref_preprocess_block := preprocess_block;
     initialized := true ;
     ()
 
   let is_extension s = !ref_is_extension s
+  let is_extension_block s = !ref_is_extension_block s
   let category s = !ref_category s
   let preprocess s = !ref_preprocess s
+  let preprocess_block s = !ref_preprocess_block s
 end
 let set_extension_handler = Extensions.set_extension_handler
 let is_extension = Extensions.is_extension
+let is_extension_block = Extensions.is_extension_block
 let extension_category = Extensions.category
 let preprocess_extension = Extensions.preprocess
+let preprocess_extension_block = Extensions.preprocess_block
 
 module CurrentLoc = Cil_const.CurrentLoc
 
diff --git a/src/kernel_services/ast_queries/logic_env.mli b/src/kernel_services/ast_queries/logic_env.mli
index 407ac539994..954eb3645d1 100644
--- a/src/kernel_services/ast_queries/logic_env.mli
+++ b/src/kernel_services/ast_queries/logic_env.mli
@@ -29,12 +29,16 @@ open Cil_types
 (** {2 registered ACSL extensions } *)
 
 val is_extension: string -> bool
+val is_extension_block: string -> bool
 
 val extension_category: string -> ext_category
 
 val preprocess_extension:
   string -> Logic_ptree.lexpr list -> Logic_ptree.lexpr list
 
+val preprocess_extension_block:
+  string -> string * Logic_ptree.extended_decl list -> string * Logic_ptree.extended_decl list
+
 (** {2 Global Tables} *)
 module Logic_info: State_builder.Hashtbl
   with type key = string and type data = Cil_types.logic_info list
@@ -208,6 +212,10 @@ val set_extension_handler:
   category:(string -> ext_category) ->
   is_extension:(string -> bool) ->
   preprocess:(string -> Logic_ptree.lexpr list -> Logic_ptree.lexpr list) ->
+  is_extension_block:(string -> bool) ->
+  preprocess_block:
+    (string -> string * Logic_ptree.extended_decl list ->
+     string * Logic_ptree.extended_decl list) ->
   unit
 (** Used to setup references related to the handling of ACSL extensions.
     If your name is not [Acsl_extension], do not call this
diff --git a/src/kernel_services/ast_queries/logic_typing.ml b/src/kernel_services/ast_queries/logic_typing.ml
index 477044c5523..fcf4ab3aed6 100644
--- a/src/kernel_services/ast_queries/logic_typing.ml
+++ b/src/kernel_services/ast_queries/logic_typing.ml
@@ -528,11 +528,13 @@ module Extensions = struct
   let initialized = ref false
   let ref_is_extension = ref (fun _ -> assert false)
   let ref_typer = ref (fun _ _ _ _ -> assert false)
+  let ref_typer_block = ref (fun _ _ _ _ -> assert false)
 
-  let set_handler ~is_extension ~typer =
+  let set_handler ~is_extension ~typer ~typer_block =
     assert (not !initialized) ;
     ref_is_extension := is_extension ;
     ref_typer := typer ;
+    ref_typer_block := typer_block;
     initialized := true
 
   let is_extension name = !ref_is_extension name
@@ -540,6 +542,9 @@ module Extensions = struct
   let typer name ~typing_context:typing_context ~loc =
     !ref_typer name typing_context loc
 
+  let typer_block name ~typing_context:typing_context ~loc =
+    !ref_typer_block name typing_context loc
+
   (* For deprecated functions *)
   let ref_deprecated_handler = ref (fun _ _ _ _  -> assert false)
 
@@ -551,6 +556,8 @@ module Extensions = struct
     !ref_deprecated_handler name category status typer
 end
 let set_extension_handler = Extensions.set_handler
+let get_typer = Extensions.typer
+let get_typer_block = Extensions.typer_block
 
 (* Deprecated ACSL extensions functions *)
 let set_deprecated_extension_handler =
@@ -4281,11 +4288,18 @@ struct
       let rvi_opt = get_volatile_fct checks_reads_fct rd_opt in
       let wvi_opt = get_volatile_fct checks_writes_fct wr_opt in
       Dvolatile (tsets, rvi_opt, wvi_opt, [], loc)
-    | LDextended (kind, content) ->
+    | LDextended (Ext_lexpr(kind, content)) ->
       let typing_context = base_ctxt (Lenv.empty ()) in
       let status,tcontent = Extensions.typer kind ~typing_context ~loc content in
       let textended = Logic_const.new_acsl_extension kind loc status tcontent in
       Dextended (textended, [], loc)
+    | LDextended (Ext_extension (kind, name, content)) ->
+      let typing_context = base_ctxt (Lenv.empty ()) in
+      let status,tcontent =
+        Extensions.typer_block kind ~typing_context ~loc (name,content)
+      in
+      let textended = Logic_const.new_acsl_extension kind loc status tcontent in
+      Dextended (textended, [], loc)
 
   let annot a =
     start_transaction ();
diff --git a/src/kernel_services/ast_queries/logic_typing.mli b/src/kernel_services/ast_queries/logic_typing.mli
index 3c4c2975d97..e1c613d9e29 100644
--- a/src/kernel_services/ast_queries/logic_typing.mli
+++ b/src/kernel_services/ast_queries/logic_typing.mli
@@ -389,12 +389,29 @@ val set_extension_handler:
   is_extension:(string -> bool) ->
   typer:(string -> typing_context -> location -> Logic_ptree.lexpr list ->
          (bool * acsl_extension_kind)) ->
+  typer_block:(string -> typing_context ->
+               Filepath.position * Filepath.position ->
+               string * Logic_ptree.extended_decl list ->
+               bool * Cil_types.acsl_extension_kind) ->
   unit
 (** Used to setup references related to the handling of ACSL extensions.
     If your name is not [Acsl_extension], do not call this
     @since 21.0-Scandium
 *)
 
+val get_typer :
+  string ->
+  typing_context:typing_context ->
+  loc:Filepath.position * Filepath.position ->
+  Logic_ptree.lexpr list -> bool * Cil_types.acsl_extension_kind
+
+val get_typer_block:
+  string ->
+  typing_context:typing_context ->
+  loc:Logic_ptree.location ->
+  string * Logic_ptree.extended_decl list ->
+  bool * Cil_types.acsl_extension_kind
+
 (**/**)
 val set_deprecated_extension_handler:
   handler:(string -> ext_category -> bool ->
diff --git a/src/kernel_services/ast_queries/logic_utils.ml b/src/kernel_services/ast_queries/logic_utils.ml
index 481fc909734..acdcb97f4f8 100644
--- a/src/kernel_services/ast_queries/logic_utils.ml
+++ b/src/kernel_services/ast_queries/logic_utils.ml
@@ -1195,8 +1195,8 @@ let rec is_same_extension x1 x2 =
     is_same_list is_same_term t1 t2
   | Ext_preds p1, Ext_preds p2 ->
     is_same_list is_same_predicate p1 p2
-  | Ext_annot a1, Ext_annot a2 ->
-    is_same_list is_same_extension a1 a2
+  | Ext_annot (id1, a1), Ext_annot (id2, a2) ->
+    is_same_string id1 id2 && is_same_list is_same_extension a1 a2
   | (Ext_id _ | Ext_preds _ | Ext_terms _ | Ext_annot _ ), _ -> false
 
 let is_same_code_annotation (ca1:code_annotation) (ca2:code_annotation) =
diff --git a/src/kernel_services/parsetree/logic_ptree.mli b/src/kernel_services/parsetree/logic_ptree.mli
index bff3332bb8d..ccc7a1d24af 100644
--- a/src/kernel_services/parsetree/logic_ptree.mli
+++ b/src/kernel_services/parsetree/logic_ptree.mli
@@ -248,7 +248,8 @@ and decl_node =
   | LDmodel_annot of model_annot    (** model field. *)
   | LDvolatile of lexpr list * (string option * string option)
       (** volatile clause read/write. *)
-  | LDextended of extension (** extended global annotation. *)
+  | LDextended of global_extension (** extended global annotation. *)
+
 
 (** dependencies of an assigned location. *)
 and deps =
@@ -273,6 +274,15 @@ and allocation =
 (** variant of a loop or a recursive function. *)
 and variant = lexpr * string option
 
+and global_extension =
+  | Ext_lexpr of string * lexpr list
+  | Ext_extension of string * string * extended_decl list
+
+and extended_decl = {
+  extended_node : global_extension;
+  extended_loc : location
+}
+
 (** Behavior in a specification. This type shares the name of its constructors
     with {!Cil_types.behavior}. *)
 type behavior = {
-- 
GitLab