Skip to content

Commit b6a1777

Browse files
Merge PR rocq-prover#21252: Ltac2: provide user API for printing errors
Reviewed-by: thomas-lamiaux Ack-by: JasonGross Co-authored-by: thomas-lamiaux <thomas-lamiaux@users.noreply.github.com>
2 parents 10eb4cc + aa2a045 commit b6a1777

File tree

7 files changed

+126
-3
lines changed

7 files changed

+126
-3
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- **Added:**
2+
APIs `Control.print_err` and `Control.print_exn` which may be used to customize printing of Ltac2 errors
3+
(`#21252 <https://github.com/rocq-prover/rocq/pull/21252>`_,
4+
by Gaëtan Gilbert).

plugins/ltac2/tac2core.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,8 @@ let () = define "current_exninfo" (unit @-> tac exninfo) @@ fun () ->
992992

993993
let () = define "message_of_exninfo" (exninfo @-> ret pp) CErrors.print_extra
994994

995+
let () = define "print_err" (err @-> ret pp) @@ fun (e,_) -> CErrors.print e
996+
995997
(** Control *)
996998

997999
(** exn -> 'a *)

plugins/ltac2/tac2entries.ml

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,13 +1333,45 @@ let pr_frame = function
13331333
str "Extn " ++ str (Tac2dyn.Arg.repr tag) ++ str ":" ++ spc () ++
13341334
obj.Tac2env.ml_print env sigma arg
13351335

1336-
let () = register_handler begin function
1337-
| Tac2interp.LtacError (kn, args) ->
1336+
let print_raw_error kn args =
13381337
let t_exn = KerName.make Tac2env.rocq_prefix (Id.of_string "exn") in
13391338
let v = Tac2ffi.of_open (kn, args) in
13401339
let t = GTypRef (Other t_exn, []) in
13411340
let c = Tac2print.pr_valexpr (Global.env ()) Evd.empty v t in
1342-
Some (hov 0 (str "Uncaught Ltac2 exception:" ++ spc () ++ hov 0 c))
1341+
hov 0 (str "Uncaught Ltac2 exception:" ++ spc () ++ hov 0 c)
1342+
1343+
let print_error kn args =
1344+
let env = Global.env() in
1345+
let sigma = Evd.from_env env in
1346+
let user_print = KerName.make Tac2quote.Refs.control_prefix (Id.of_string "print_exn") in
1347+
let user_print = Tac2interp.eval_global user_print in
1348+
let user_print = Tac2ffi.(to_fun1 of_exn (to_option to_pp)) user_print in
1349+
let user_print () =
1350+
let res, _, _, _, _ =
1351+
Proofview.apply ~name:(Id.of_string_soft "ltac2 error printing") ~poly:PolyFlags.default
1352+
env
1353+
(user_print (Tac2interp.LtacError (kn, args), Exninfo.null))
1354+
(snd @@ Proofview.init sigma [])
1355+
in
1356+
res
1357+
in
1358+
match user_print() with
1359+
| Some msg -> msg
1360+
| None -> print_raw_error kn args
1361+
| exception e when CErrors.noncritical e ->
1362+
let e = Exninfo.capture e in
1363+
let ppe = match e with
1364+
| Tac2interp.LtacError (kn', args'), _info ->
1365+
(* don't use iprint: high risk of looping *)
1366+
(* XXX print the info? currently CErrors.print_extra is not exposed *)
1367+
print_raw_error kn' args'
1368+
| _ -> CErrors.iprint e
1369+
in
1370+
print_raw_error kn args ++ fnl() ++
1371+
hov 2 (str "Custom Ltac2 printer failed:" ++ spc() ++ ppe)
1372+
1373+
let () = register_handler begin function
1374+
| Tac2interp.LtacError (kn, args) -> Some (print_error kn args)
13431375
| _ -> None
13441376
end
13451377

plugins/ltac2/tac2quote.mli

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ module Refs : sig
4949

5050
val t_module : type_constant
5151

52+
(** Modules *)
53+
val control_prefix : ModPath.t
5254
end
5355

5456
val constructor : ?loc:Loc.t -> ltac_constructor -> raw_tacexpr list -> raw_tacexpr
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
File "./output/ltac2_print_exn.v", line 11, characters 0-23:
2+
The command has indeed failed with message:
3+
hello
4+
Backtrace:
5+
Call foo
6+
Prim <rocq-runtime.plugins.ltac2:zero>
7+
8+
File "./output/ltac2_print_exn.v", line 24, characters 0-23:
9+
The command has indeed failed with message:
10+
Uncaught Ltac2 exception: WithTerm constr:(?X1)
11+
File "./output/ltac2_print_exn.v", line 33, characters 0-23:
12+
The command has indeed failed with message:
13+
test ?X1
14+
File "./output/ltac2_print_exn.v", line 38, characters 0-23:
15+
The command has indeed failed with message:
16+
Uncaught Ltac2 exception: Tactic_failure (Some message:(hello))
17+
Custom Ltac2 printer failed: Uncaught Ltac2 exception: Assertion_failure
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Require Import Ltac2.Ltac2.
2+
Import Printf.
3+
4+
(** basic test, we also check that the backtrace isn't forgotten when
5+
using the custom printer *)
6+
7+
Set Ltac2 Backtrace.
8+
9+
Ltac2 foo () := Control.zero (Tactic_failure (Some (Message.of_string "hello"))).
10+
11+
Fail Ltac2 Eval foo ().
12+
13+
Unset Ltac2 Backtrace.
14+
15+
(** Test printing constr even though we have a bad evar map *)
16+
17+
Ltac2 Type exn ::= [ WithTerm (constr) ].
18+
19+
Ltac2 bar () :=
20+
let c := open_constr:(_ :> nat) in
21+
Control.zero (WithTerm c).
22+
23+
(* default printer doesn't have the evar map but doesn't fail *)
24+
Fail Ltac2 Eval bar ().
25+
26+
Ltac2 Set Control.print_exn := fun e =>
27+
match e with
28+
| WithTerm c => Some (fprintf "test %t" c)
29+
| _ => None
30+
end.
31+
32+
(* custom printer also doesn't have the evar map but doesn't fail *)
33+
Fail Ltac2 Eval bar ().
34+
35+
(** Test custom printer producing an error *)
36+
Ltac2 Set Control.print_exn := fun _ => Control.throw Assertion_failure.
37+
38+
Fail Ltac2 Eval foo ().

theories/Ltac2/Control.v

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,31 @@ Ltac2 @ external timeout : int -> (unit -> 'a) -> 'a :=
224224
(** [timeoutf t thunk] calls [thunk ()] with a timeout of [t] seconds. *)
225225
Ltac2 @ external timeoutf : float -> (unit -> 'a) -> 'a :=
226226
"rocq-runtime.plugins.ltac2" "timeoutf".
227+
228+
(** Error printing *)
229+
230+
(** Print internal errors. *)
231+
Ltac2 @external print_err : err -> message
232+
:= "rocq-runtime.plugins.ltac2" "print_err".
233+
234+
(** Print exceptions as errors. Used by the runtime when printing uncaught errors.
235+
Extensible by mutation, see uses below.
236+
237+
IMPORTANT: when called for printing uncaught errors, it is run in an empty state
238+
(no goals, empty evar map).
239+
240+
Also note that the "Internal" branch is not used when printing
241+
uncaught errors as Internal exceptions are not considered as Ltac2
242+
errors. *)
243+
Ltac2 mutable print_exn : exn -> message option := fun e =>
244+
match e with
245+
| Internal e => Some (print_err e)
246+
| _ => None
247+
end.
248+
249+
#[global]
250+
Ltac2 Set print_exn as print_other := fun e =>
251+
match e with
252+
| Tactic_failure (Some msg) => Some msg
253+
| _ => print_other e
254+
end.

0 commit comments

Comments
 (0)