Skip to content

Commit ca415d1

Browse files
committed
Typecheck ignoring errors instead of globalize for Ltac2 Globalize
This adds a mode to ltac2 typechecking which accumulates errors instead of raising an exception at the first error. Ltac2 Globalize then uses this mode and ignores the accumulated errors (we could print them instead). The accumulation mode could also be used to provide all errors from an ltac2 expression in regular code in the future (with a flag). Together with #21617 this gets us closer to removing the globalize code (which doesn't work on quotations so should be avoided).
1 parent a894f52 commit ca415d1

File tree

8 files changed

+109
-70
lines changed

8 files changed

+109
-70
lines changed

plugins/ltac2/tac2entries.ml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1599,8 +1599,9 @@ let typecheck_expr e =
15991599

16001600
let globalize_expr e =
16011601
let avoid = Id.Set.empty in
1602-
let e = Tac2intern.debug_globalize_allow_ext avoid e in
1603-
Feedback.msg_notice (Tac2print.pr_rawexpr_gen E5 ~avoid e)
1602+
let e, t, errors = Tac2intern.intern_accumulate_errors ~strict:false [] e in
1603+
(* XXX print type and errors? *)
1604+
Feedback.msg_notice (Tac2print.pr_glbexpr_gen E5 ~avoid e)
16041605

16051606
(** Calling tactics *)
16061607

plugins/ltac2/tac2extravals.ml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,6 @@ let () =
418418
let pr_raw e = Genprint.PrinterBasic (fun _env _sigma ->
419419
let avoid = Id.Set.empty in
420420
(* FIXME avoid set, same as pr_glb *)
421-
let e = Tac2intern.debug_globalize_allow_ext avoid e in
422421
Tac2print.pr_rawexpr_gen ~avoid E5 e) in
423422
let pr_glb (ids, e) =
424423
let ids =
@@ -438,7 +437,6 @@ let () =
438437

439438
let () =
440439
let pr_raw e = Genprint.PrinterBasic (fun _ _ ->
441-
let e = Tac2intern.debug_globalize_allow_ext Id.Set.empty e in
442440
Tac2print.pr_rawexpr_gen ~avoid:Id.Set.empty E5 e)
443441
in
444442
let pr_glb e = Genprint.PrinterBasic (fun _ _ -> Tac2print.pr_glbexpr ~avoid:Id.Set.empty e) in

plugins/ltac2/tac2intern.ml

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1076,9 +1076,12 @@ let tycon_fun_body ?loc env tycon dom =
10761076
let () = unify ?loc env (GTypArrow (dom,codom)) tycon in
10771077
codom
10781078
| GTypRef _ ->
1079-
CErrors.user_err ?loc
1080-
Pp.(str "This expression should not be a function, the expected type is" ++ spc() ++
1081-
pr_glbtype env tycon ++ str ".")
1079+
let () =
1080+
add_error env ?loc
1081+
Pp.(str "This expression should not be a function, the expected type is" ++ spc() ++
1082+
pr_glbtype env tycon ++ str ".")
1083+
in
1084+
GTypVar (fresh_id env)
10821085

10831086
let tycon_app ?loc env ~ft t =
10841087
match kind env t with
@@ -1093,14 +1096,16 @@ let tycon_app ?loc env ~ft t =
10931096
| GTypArrow _ -> true
10941097
| _ -> false
10951098
in
1096-
if is_fun then
1097-
CErrors.user_err ?loc
1099+
let () = if is_fun then
1100+
add_error env ?loc
10981101
Pp.(str "This function has type" ++ spc() ++ pr_glbtype env ft ++
10991102
spc() ++ str "and is applied to too many arguments.")
11001103
else
1101-
CErrors.user_err ?loc
1104+
add_error env ?loc
11021105
Pp.(str "This expression has type" ++ spc() ++ pr_glbtype env ft ++ str"." ++
11031106
spc() ++ str "It is not a function and cannot be applied.")
1107+
in
1108+
GTypVar (fresh_id env), GTypVar (fresh_id env)
11041109

11051110
let warn_useless_record_with = CWarnings.create ~name:"ltac2-useless-record-with" ~default:AsError
11061111
~category:CWarnings.CoreCategories.ltac2
@@ -1521,29 +1526,32 @@ and intern_constructor env loc tycon kn args = match kn with
15211526
else
15221527
error_nargs_mismatch ?loc kn nargs (List.length args)
15231528
| Tuple n ->
1524-
let () = if not (Int.equal n (List.length args)) then begin
1525-
if Int.equal 0 n then
1526-
(* parsing [() bla] produces [CTacApp (Tuple 0, [bla])] but parsing
1527-
[((), ()) bla] produces [CTacApp (CTacApp (Tuple 2, [(); ()]), [bla])]
1528-
so we only need to produce a sensible error for [Tuple 0] *)
1529-
let t = GTypRef (Tuple 0, []) in
1530-
CErrors.user_err ?loc Pp.(
1531-
str "This expression has type" ++ spc () ++ pr_glbtype env t ++
1532-
spc () ++ str "and is not a function")
1533-
else assert false
1534-
end
1535-
in
1536-
let types = List.init n (fun i -> GTypVar (fresh_id env)) in
1537-
let ans = GTypRef (Tuple n, types) in
1538-
let ans = match tycon with
1539-
| None -> ans
1540-
| Some tycon ->
1541-
let () = unify ?loc env ans tycon in
1542-
tycon
1543-
in
1544-
let map arg tpe = intern_rec_with_constraint env arg tpe in
1545-
let args = List.map2 map args types in
1546-
GTacCst (Tuple n, 0, args), ans
1529+
if not (Int.equal n (List.length args)) then begin
1530+
assert (Int.equal 0 n);
1531+
(* parsing [() bla] produces [CTacApp (Tuple 0, [bla])] but parsing
1532+
[((), ()) bla] produces [CTacApp (CTacApp (Tuple 2, [(); ()]), [bla])]
1533+
so we only need to produce a sensible error for [Tuple 0] *)
1534+
let t = GTypRef (Tuple 0, []) in
1535+
let () =
1536+
add_error env ?loc Pp.(
1537+
str "This expression has type" ++ spc () ++ pr_glbtype env t ++
1538+
spc () ++ str "and is not a function.")
1539+
in
1540+
let args = List.map (fun arg -> fst @@ intern_rec env None arg) args in
1541+
GTacApp (GTacCst (Tuple 0, 0, []), args), GTypVar (fresh_id env)
1542+
end
1543+
else
1544+
let types = List.init n (fun i -> GTypVar (fresh_id env)) in
1545+
let ans = GTypRef (Tuple n, types) in
1546+
let ans = match tycon with
1547+
| None -> ans
1548+
| Some tycon ->
1549+
let () = unify ?loc env ans tycon in
1550+
tycon
1551+
in
1552+
let map arg tpe = intern_rec_with_constraint env arg tpe in
1553+
let args = List.map2 map args types in
1554+
GTacCst (Tuple n, 0, args), ans
15471555

15481556
and intern_case env loc e tycon pl =
15491557
let e, et = intern_rec env None e in
@@ -1583,6 +1591,17 @@ let intern ~strict ctx e =
15831591
let t = normalize env (count, vars) t in
15841592
(e, (!count, t))
15851593

1594+
let intern_accumulate_errors ~strict ctx e =
1595+
let env = empty_env ~strict ~accumulate_errors:true () in
1596+
(* XXX not doing check_unused_variables *)
1597+
let fold accu (id, t) = push_name (Name id) (polymorphic t) accu in
1598+
let env = List.fold_left fold env ctx in
1599+
let (e, t) = intern_rec env None e in
1600+
let count = ref 0 in
1601+
let vars = ref TVar.Map.empty in
1602+
let t = normalize env (count, vars) t in
1603+
(e, (!count, t), get_errors env)
1604+
15861605
let intern_typedef self (ids, t) : glb_quant_typedef =
15871606
let env = set_rec self (empty_env ()) in
15881607
(* Initialize type parameters *)
@@ -1776,10 +1795,6 @@ let globalize ids tac =
17761795
in
17771796
globalize_gen ~tacext ids tac
17781797

1779-
let debug_globalize_allow_ext ids tac =
1780-
let tacext ?loc (RawExt (tag,arg)) = CAst.make ?loc @@ CTacExt (tag,arg) in
1781-
globalize_gen ~tacext ids tac
1782-
17831798
let { Goptions.get = typed_notations } =
17841799
Goptions.declare_bool_option_and_ref
17851800
~key:["Ltac2";"Typed";"Notations"] ~value:true ()

plugins/ltac2/tac2intern.mli

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ val intern_typedef : (KerName.t * int) Id.Map.t -> raw_quant_typedef -> glb_quan
1919
val intern_open_type : raw_typexpr -> type_scheme
2020
val intern_notation_data : Id.Set.t -> raw_tacexpr -> Tac2env.notation_data
2121

22+
val intern_accumulate_errors : strict:bool -> context -> raw_tacexpr ->
23+
glb_tacexpr * type_scheme * Pp.t Loc.located list
24+
2225
(** [check_unused] is default true *)
2326
val genintern_warn_not_unit : ?check_unused:bool ->
2427
Genintern.glob_sign ->
@@ -66,10 +69,6 @@ val globalize : Id.Set.t -> raw_tacexpr -> raw_tacexpr
6669
(** Replaces all qualified identifiers by their corresponding kernel name. The
6770
set represents bound variables in the context. *)
6871

69-
val debug_globalize_allow_ext : Id.Set.t -> raw_tacexpr -> raw_tacexpr
70-
(** Variant of globalize which can accept CTacExt using the provided function.
71-
Intended for debugging. *)
72-
7372
(** Errors *)
7473

7574
val error_nargs_mismatch : ?loc:Loc.t -> ltac_constructor -> int -> int -> 'a

plugins/ltac2/tac2typing_env.ml

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ type mix_type_scheme = int * mix_var glb_typexpr
105105
so instead we use mutation to detect them *)
106106
type used = { mutable used : bool }
107107

108+
(* TODO delay printing? but printing depends on env which is mutable *)
109+
type error = Pp.t Loc.located
110+
108111
type t = {
109112
env_var : (mix_type_scheme * used) Id.Map.t;
110113
(** Type schemes of bound variables *)
@@ -118,17 +121,30 @@ type t = {
118121
(** Recursive type definitions *)
119122
env_strict : bool;
120123
(** True iff in strict mode *)
124+
env_errs : error list ref option;
125+
(** [None] if raise on first error, [Some] if accumulate errors *)
121126
}
122127

123-
let empty_env ?(strict=true) () = {
128+
let empty_env ?(strict=true) ?(accumulate_errors=false) () = {
124129
env_var = Id.Map.empty;
125130
env_cst = UF.create ();
126131
env_als = ref Id.Map.empty;
127132
env_opn = true;
128133
env_rec = Id.Map.empty;
129134
env_strict = strict;
135+
env_errs = if accumulate_errors then Some (ref []) else None;
130136
}
131137

138+
let add_error ?loc env msg =
139+
match env.env_errs with
140+
| None -> CErrors.user_err ?loc msg
141+
| Some errs -> errs := (loc,msg) :: !errs
142+
143+
let get_errors env =
144+
match env.env_errs with
145+
| None -> assert false
146+
| Some errs -> !errs
147+
132148
let env_strict env = env.env_strict
133149

134150
let set_rec self env = { env with env_rec = self }
@@ -328,7 +344,7 @@ let rec unify0 env t1 t2 = match kind env t1, kind env t2 with
328344
let unify ?loc env t1 t2 =
329345
try unify0 env t1 t2
330346
with CannotUnify (u1, u2) ->
331-
CErrors.user_err ?loc Pp.(str "This expression has type" ++ spc () ++ pr_glbtype env t1 ++
347+
add_error env ?loc Pp.(str "This expression has type" ++ spc () ++ pr_glbtype env t1 ++
332348
spc () ++ str "but an expression was expected of type" ++ spc () ++ pr_glbtype env t2)
333349

334350
let unify_arrow ?loc env ft args =
@@ -343,12 +359,17 @@ let unify_arrow ?loc env ft args =
343359
let () = unify ?loc env (GTypVar id) (GTypArrow (t, ft)) in
344360
iter ft args true
345361
| GTypRef _, _ :: _ ->
346-
if is_fun then
347-
CErrors.user_err ?loc Pp.(str "This function has type" ++ spc () ++ pr_glbtype env ft0 ++
348-
spc () ++ str "and is applied to too many arguments")
349-
else
350-
CErrors.user_err ?loc Pp.(str "This expression has type" ++ spc () ++ pr_glbtype env ft0 ++
351-
spc () ++ str "and is not a function")
362+
let () =
363+
if is_fun then
364+
add_error env ?loc
365+
Pp.(str "This function has type" ++ spc () ++ pr_glbtype env ft0 ++
366+
spc () ++ str "and is applied to too many arguments")
367+
else
368+
add_error env ?loc
369+
Pp.(str "This expression has type" ++ spc () ++ pr_glbtype env ft0 ++
370+
spc () ++ str "and is not a function")
371+
in
372+
GTypVar (fresh_id env)
352373
in
353374
iter ft args false
354375

plugins/ltac2/tac2typing_env.mli

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ end
2121

2222
type t
2323

24-
(** default strict:true *)
25-
val empty_env : ?strict:bool -> unit -> t
24+
(** default strict:true, accumulate_errors:false *)
25+
val empty_env : ?strict:bool -> ?accumulate_errors:bool -> unit -> t
26+
27+
(** In accumulate mode, add the error to the list in the env. Otherwise raise UserError. *)
28+
val add_error : ?loc:Loc.t -> t -> Pp.t -> unit
29+
30+
(** Get accumulated errors. Assertion failure if not in accumulate mode. *)
31+
val get_errors : t -> Pp.t Loc.located list
2632

2733
val set_rec : (KerName.t * int) Id.Map.t -> t -> t
2834

test-suite/output/ltac2_check_globalize.out

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,26 @@ let x := fun x => x in
1616
let _ := x 1 in
1717
let _ := x "" in
1818
() : unit
19-
let accu := { contents := []} in
20-
(let x := fun x => accu.(contents) := (x :: accu.(contents)) in
21-
let _ := x 1 in
22-
let _ := x "" in
23-
());
19+
let accu := { contents := [] } in
20+
let _ :=
21+
let x := fun x => accu.(contents) := (x :: accu.(contents)) in
22+
let _ := x 1 in
23+
let _ := x "" in
24+
()
25+
in
2426
accu.(contents)
2527
File "./output/ltac2_check_globalize.v", line 38, characters 0-144:
2628
The command has indeed failed with message:
2729
This expression has type string but an expression was expected of type
2830
int
29-
let (m : '__α Pattern.goal_matching) :=
30-
([(([(None, (Pattern.MatchPattern, pat:(_)))],
31+
let m :=
32+
[(([(None, (Pattern.MatchPattern, pat:(_)))],
3133
(Pattern.MatchPattern, pat:(_))),
3234
(fun h =>
3335
let h := Array.get h 0 in
3436
fun _ => fun _ => fun _ => fun _ => Std.clear h));
35-
(([], (Pattern.MatchPattern, pat:(_))),
37+
(([], (Pattern.MatchPattern, pat:(_))),
3638
(fun _ => fun _ => fun _ => fun _ => fun _ => ()))]
37-
: _ Pattern.goal_matching)
3839
in
39-
Pattern.lazy_goal_match0 false m :'__α
40+
Pattern.lazy_goal_match0 false m
4041
constr:(ltac2:(()))

test-suite/output/ltac2_typed_notations.out

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@ File "./output/ltac2_typed_notations.v", line 5, characters 9-10:
22
The command has indeed failed with message:
33
This expression has type bool but an expression was expected of type
44
constr
5-
fun (b : bool) =>
6-
(let c := b in
7-
let (m : '__α Pattern.constr_matching) :=
8-
[(Pattern.MatchPattern, pat:(true),
9-
(fun _ => fun (_ : constr array) => true));
10-
(Pattern.MatchPattern, pat:(false),
11-
(fun _ => fun (_ : constr array) => false))]
12-
with (t : constr) := c in
13-
Pattern.one_match0 t m :'__α : bool)
5+
fun b =>
6+
let c := b in
7+
let m :=
8+
[(Pattern.MatchPattern, pat:(true), (fun _ => fun _ => true));
9+
(Pattern.MatchPattern, pat:(false), (fun _ => fun _ => false))]
10+
with t := c in
11+
Pattern.one_match0 t m

0 commit comments

Comments
 (0)