Skip to content

Commit 9d80b56

Browse files
committed
Use warnings for duplicate and missing extensions
1 parent a81b001 commit 9d80b56

File tree

14 files changed

+211
-97
lines changed

14 files changed

+211
-97
lines changed

doc/extensions.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ The `dolmen` command-line tool looks up user-defined extensions using the Dune
1414
plugin mechanism. A plugin named `plugin.typing` will be picked up when
1515
`--ext plugin` or `--ext plugin.typing` is provided on the command-line, and
1616
the plugin must register a typing extension named `"plugin"` using
17-
`Dolmen_loop.Typer.Ext.create`. A plugin named `plugin.model` will be picked up
17+
`Dolmen_loop.Typer.Ext.register`. A plugin named `plugin.model` will be picked up
1818
when `--ext plugin` or `--ext plugin.model` is provided on the command-line and
1919
the plugin must register a model extension named `"plugin"` using
20-
`Dolmen_model.Env.Ext.create`. A plugin named `plugin` (without dots) will be
20+
`Dolmen_model.Ext.register`. A plugin named `plugin` (without dots) will be
2121
picked up when either of the above command line flags is provided, and must
2222
provide both a typing and model extension.
2323

examples/extensions/abs_real/abs_real.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,4 @@ let builtins ~eval:_ _ (cst : Dolmen.Std.Expr.Term.Const.t) =
4646
| _ -> None
4747

4848
let model_ext =
49-
Dolmen_model.Ext.create ~name:"abs_real" ~builtins
49+
Dolmen_model.Ext.create ~name:"abs_real" ~builtins

examples/extensions/abs_real_split/abs_real_model.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ let builtins ~eval:_ _ (cst : Dolmen.Std.Expr.Term.Const.t) =
1313
| _ -> None
1414

1515
let model_ext =
16-
Dolmen_model.Ext.create ~name:"abs_real_split" ~builtins
16+
Dolmen_model.Ext.create ~name:"abs_real_split" ~builtins

examples/extensions/abs_real_split/abs_real_typing.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ let builtins _lang env s =
2929
| _ -> `Not_found
3030

3131
let typing_ext =
32-
Dolmen_loop.Typer.Ext.create ~name:"abs_real_split" ~builtins
32+
Dolmen_loop.Typer.Ext.create ~name:"abs_real_split" ~builtins

src/bin/extensions.ml

Lines changed: 119 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ let is_available = function
1515
| Unavailable -> false
1616
| Builtin _ | Dune_plugin _ -> true
1717

18-
(* Merge two possible locations for the same plugin. Prefer external plugins
19-
over built-in plugins to allow overrides. *)
18+
(* Merge two possible locations for the same plugin. Prefer builtin plugins over
19+
external plugins. *)
2020
let merge_location p1 p2 =
2121
match p1, p2 with
2222
| Dune_plugin _ as p, _ | _, (Dune_plugin _ as p) -> p
@@ -82,15 +82,15 @@ let infos =
8282
{ extension_name = Dolmen_loop.Typer.Ext.name ext
8383
; typing_plugin = Builtin ext
8484
; model_plugin = Unavailable }
85-
) (Dolmen_loop.Typer.Ext.list ());
85+
) [ Dolmen_loop.Typer.Ext.bvconv ];
8686

8787
(* Add builtin model extensions. *)
8888
List.iter (fun ext ->
8989
add_ext extensions
9090
{ extension_name = Dolmen_model.Ext.name ext
9191
; typing_plugin = Unavailable
9292
; model_plugin = Builtin ext }
93-
) (Dolmen_model.Ext.list ());
93+
) [ Dolmen_model.Ext.bvconv ];
9494

9595
(* Add extensions from plugins. *)
9696
let add_plugin invalid plugin = function
@@ -134,33 +134,127 @@ let find_ext name =
134134
name
135135
Fmt.(list (box pp)) (list ())
136136

137-
let load_typing_extension ext =
137+
let load_plugin_or_fail plugin =
138+
try Ok (Dolmen.Sites.Plugins.Plugins.load plugin)
139+
with Dynlink.Error err ->
140+
Fmt.error_msg "while loading plugin %s: %s"
141+
plugin (Dynlink.error_message err)
142+
143+
let missing_extension =
144+
Dolmen_loop.Report.Warning.mk ~mnemonic:"missing-extension"
145+
~message:(fun ppf (kind, ext, _) ->
146+
Format.fprintf ppf "There is no %s extension named '%s'." kind ext)
147+
~hints:[fun (kind, _, plugin) ->
148+
Some (Format.dprintf
149+
"Expected plugin '%s' to register this %s extension." plugin kind)]
150+
~name:"Missing extension" ()
151+
152+
let duplicate_extension =
153+
Dolmen_loop.Report.Warning.mk ~mnemonic:"duplicate-extension"
154+
~message:(fun ppf (kind, name, _) ->
155+
Format.fprintf ppf
156+
"%s extension '%s' was registered multiple times."
157+
(String.capitalize_ascii kind)
158+
name)
159+
~hints:[
160+
(fun (kind, name, _) ->
161+
Some (
162+
Format.dprintf "%a@ %s@ extension@ '%s'."
163+
Fmt.words
164+
"This is likely caused by multiple plugins trying to register the"
165+
kind name));
166+
(fun (kind, _, plugin) ->
167+
Some (
168+
Format.dprintf "Expected plugin '%s' to register this %s extension."
169+
plugin kind))]
170+
~name:"Duplicate extension" ()
171+
172+
let cannot_override_builtin_extension =
173+
Dolmen_loop.Report.Warning.mk ~mnemonic:"cannot-override-builtin-extension"
174+
~message:(fun ppf (kind, name) ->
175+
Format.fprintf ppf "Cannot override builtin %s extension '%s'."
176+
kind name)
177+
~name:"Cannot override builtin extension" ()
178+
179+
let add_typing_extension ext st =
180+
Loop.State.update Loop.Typer.extension_builtins (List.cons ext) st
181+
182+
let load_typing_extension ext st =
138183
match ext.typing_plugin with
139184
| Unavailable ->
140-
Fmt.error_msg "No typing extension '%s'" ext.extension_name
141-
| Builtin e -> Ok e
185+
Fmt.error_msg
186+
"No plugin provides the typing extension '%s'" ext.extension_name
187+
| Builtin e -> (
188+
match Dolmen_loop.Typer.Ext.find_all (Dolmen_loop.Typer.Ext.name e) with
189+
| [] ->
190+
Ok (add_typing_extension e st)
191+
| _ ->
192+
let st =
193+
Loop.State.warn st
194+
cannot_override_builtin_extension
195+
("typing", Dolmen_loop.Typer.Ext.name e)
196+
in
197+
Ok (add_typing_extension e st)
198+
)
142199
| Dune_plugin plugin ->
143-
Dolmen.Sites.Plugins.Plugins.load plugin;
144-
try
145-
Ok (Dolmen_loop.Typer.Ext.find_exn ext.extension_name)
146-
with Not_found ->
147-
Fmt.error_msg
148-
"Plugin '%s' did not register a typing extension for '%s'"
149-
plugin ext.extension_name
150-
151-
let load_model_extension ext =
200+
Result.bind (load_plugin_or_fail plugin) @@ fun () ->
201+
match Dolmen_loop.Typer.Ext.find_all ext.extension_name with
202+
| [] ->
203+
Ok (
204+
Loop.State.warn st
205+
missing_extension
206+
("typing", ext.extension_name, plugin)
207+
)
208+
| [ e ] ->
209+
Ok (add_typing_extension e st)
210+
| e :: _ ->
211+
let st =
212+
Loop.State.warn st
213+
duplicate_extension
214+
("typing", ext.extension_name, plugin)
215+
in
216+
Ok (add_typing_extension e st)
217+
218+
let add_model_extension b st =
219+
Loop.State.update Loop.Check.builtins
220+
(fun bs -> Dolmen_model.Eval.builtins [ Dolmen_model.Ext.builtins b ; bs ])
221+
st
222+
223+
let load_model_extension ext st =
152224
match ext.model_plugin with
153225
| Unavailable ->
154-
Fmt.error_msg "No model extension '%s'" ext.extension_name
155-
| Builtin e -> Ok e
226+
Fmt.error_msg
227+
"No plugin provides the model extension '%s'" ext.extension_name
228+
| Builtin e -> (
229+
match Dolmen_model.Ext.find_all (Dolmen_model.Ext.name e) with
230+
| [] ->
231+
Ok (add_model_extension e st)
232+
| _ ->
233+
let st =
234+
Loop.State.warn st
235+
cannot_override_builtin_extension
236+
("model", Dolmen_model.Ext.name e)
237+
in
238+
Ok (add_model_extension e st)
239+
)
156240
| Dune_plugin plugin ->
157-
Dolmen.Sites.Plugins.Plugins.load plugin;
158-
try
159-
Ok (Dolmen_model.Ext.find_exn ext.extension_name)
160-
with Not_found ->
161-
Fmt.error_msg
162-
"Plugin '%s' did not register a model extension for '%s'"
163-
plugin ext.extension_name
241+
Result.bind (load_plugin_or_fail plugin) @@ fun () ->
242+
match Dolmen_model.Ext.find_all ext.extension_name with
243+
| [] ->
244+
Ok (
245+
Loop.State.warn st
246+
missing_extension
247+
("model", ext.extension_name, plugin)
248+
)
249+
| [ e ] ->
250+
Ok (add_model_extension e st)
251+
| e :: _ ->
252+
let st =
253+
Loop.State.warn st
254+
duplicate_extension
255+
("model", ext.extension_name, plugin)
256+
in
257+
Ok (add_model_extension e st)
164258

165259
let parse s =
166260
match parse_ext_opt s with

src/bin/extensions.mli

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@ val name : t -> string
66
(** Returns the name of the extension. *)
77

88
val load_typing_extension :
9-
t -> (Dolmen_loop.Typer.Ext.t, [> `Msg of string]) result
9+
t -> Loop.State.t -> (Loop.State.t, [> `Msg of string]) result
1010
(** [load_typing_extension e] loads and returns the typing extension associated
1111
with [e].
1212
13-
Fails if [e] has no typing extension, or an error occurs during loading of
13+
Fails if an error occurs during loading of
1414
an external typing extension. *)
1515

1616
val load_model_extension :
17-
t -> (Dolmen_model.Ext.t, [> `Msg of string]) result
17+
t -> Loop.State.t -> (Loop.State.t, [> `Msg of string]) result
1818
(** [load_model_extension e] loads and returns the model extension associated
1919
with [e].
2020

src/bin/options.ml

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -378,36 +378,8 @@ let mk_run_state
378378
let () = if gc then at_exit (fun () -> Gc.print_stat stdout;) in
379379
let () = if abort_on_bug then Dolmen_loop.Code.abort Dolmen_loop.Code.bug in
380380
let () = Hints.model ~check_model (* ~check_model_mode *) in
381-
(* Extensions *)
382-
let typing_exts =
383-
List.fold_left (fun typing_exts (ext, kind) ->
384-
Result.bind typing_exts @@ fun typing_exts ->
385-
match kind with
386-
| None | Some Extensions.Typing ->
387-
Result.map
388-
(fun e -> e :: typing_exts)
389-
(Extensions.load_typing_extension ext)
390-
| Some _ -> Ok typing_exts
391-
) (Ok []) extensions
392-
in
393-
Result.bind typing_exts @@ fun typing_extension_builtins ->
394-
let model_exts =
395-
if check_model then
396-
List.fold_left (fun model_exts (ext, kind) ->
397-
Result.bind model_exts @@ fun model_exts ->
398-
match kind with
399-
| None | Some Extensions.Model ->
400-
Result.map
401-
(fun e -> e :: model_exts)
402-
(Extensions.load_model_extension ext)
403-
| Some _ -> Ok model_exts
404-
) (Ok []) extensions
405-
else
406-
Ok []
407-
in
408-
Result.bind model_exts @@ fun model_extension_builtins ->
409381
(* State creation *)
410-
Ok (
382+
let st =
411383
Loop.State.empty
412384
|> Loop.State.init
413385
~bt ~debug ~report_style ~reports
@@ -418,17 +390,34 @@ let mk_run_state
418390
~interactive_prompt:Loop.Parser.interactive_prompt_lang
419391
|> Loop.Typer.init
420392
~smtlib2_forced_logic
421-
~extension_builtins:typing_extension_builtins
422393
|> Loop.Typer_Pipe.init ~type_check
423394
|> Loop.Check.init
424-
~check_model ~extension_builtins:model_extension_builtins
395+
~check_model
425396
(* ~check_model_mode *)
426397
|> Loop.Flow.init ~flow_check
427398
|> Loop.Header.init
428399
~header_check
429400
~header_licenses
430401
~header_lang_version
431-
)
402+
in
403+
(* Extensions *)
404+
let st =
405+
List.fold_left (fun st (ext, kind) ->
406+
match kind with
407+
| None | Some Extensions.Typing ->
408+
Result.bind st (Extensions.load_typing_extension ext)
409+
| Some _ -> st
410+
) (Ok st) extensions
411+
in
412+
if check_model then
413+
List.fold_left (fun st (ext, kind) ->
414+
match kind with
415+
| None | Some Extensions.Model ->
416+
Result.bind st (Extensions.load_model_extension ext)
417+
| Some _ -> st
418+
) st extensions
419+
else
420+
st
432421

433422
(* Profiling *)
434423
(* ************************************************************************* *)

src/loop/report.mli

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ module T : sig
4343

4444
val doc : [< t ] -> (Format.formatter -> unit)
4545
(** documentation for a report. *)
46-
4746
end
4847

4948

src/loop/typer.ml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,22 @@ module Ext = struct
8383
builtins : Typer_intf.lang -> T.builtin_symbols;
8484
}
8585

86-
let all = Hashtbl.create 17
87-
let list () =
88-
List.fast_sort (fun e e' -> String.compare e.name e'.name) @@
89-
Hashtbl.fold (fun _ e acc -> e :: acc) all []
90-
let find_exn = Hashtbl.find all
9186
let name { name; _ } = name
9287
let builtins { builtins; _ } = builtins
9388

89+
let registry = Hashtbl.create 17
90+
91+
let register ({ name; _ } as ext) =
92+
match Hashtbl.find registry name with
93+
| exception Not_found -> Hashtbl.replace registry name [ ext ]
94+
| exts -> Hashtbl.replace registry name (ext :: exts)
95+
96+
let find_all name =
97+
try Hashtbl.find registry name with Not_found -> []
98+
9499
let create ~name ~builtins =
95-
let t = { name; builtins; } in
96-
Hashtbl.replace all name t;
100+
let t = { name ; builtins } in
101+
register t;
97102
t
98103

99104
let bvconv =

src/loop/typer.mli

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,10 @@ module Ext : sig
6464
val bvconv : t
6565
(** Typing extension to add `bv2nat` and `int2bv`. *)
6666

67-
val list : unit -> t list
68-
(** The list of all extensions. *)
67+
val find_all : string -> t list
68+
(** Returns the extensions that have been registered with the given name.
6969
70-
val find_exn : string -> t
71-
(** Returns the typing extension with the given name.
72-
73-
@raise Not_found if no such extension exists.
74-
@since 0.11 *)
70+
@since 0.11 *)
7571
end
7672

7773
(** {2 Typechecker Functor} *)

src/model/ext.ml

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,22 @@ type t = {
88
builtins : Env.builtins;
99
}
1010

11-
let all = Hashtbl.create 17
12-
let list () =
13-
List.fast_sort (fun e e' -> String.compare e.name e'.name) @@
14-
Hashtbl.fold (fun _ e acc -> e :: acc) all []
15-
let find_exn = Hashtbl.find all
1611
let name { name; _ } = name
1712
let builtins { builtins; _ } = builtins
1813

14+
let registry = Hashtbl.create 17
15+
16+
let register ({ name; _ } as ext) =
17+
match Hashtbl.find registry name with
18+
| exception Not_found -> Hashtbl.replace registry name [ ext ]
19+
| exts -> Hashtbl.replace registry name (ext :: exts)
20+
21+
let find_all name =
22+
try Hashtbl.find registry name with Not_found -> []
23+
1924
let create ~name ~builtins =
20-
let t = { name; builtins; } in
21-
Hashtbl.replace all name t;
25+
let t = { name ; builtins } in
26+
register t;
2227
t
2328

2429
let bvconv =

0 commit comments

Comments
 (0)