Skip to content

Commit 384b676

Browse files
Merge PR #21618: Typecheck ignoring errors instead of globalize for Ltac2 Globalize
Reviewed-by: ppedrot Co-authored-by: ppedrot <ppedrot@users.noreply.github.com>
2 parents 44b2136 + ca415d1 commit 384b676

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
@@ -417,7 +417,6 @@ let () =
417417
let pr_raw e = Genprint.PrinterBasic (fun _env _sigma ->
418418
let avoid = Id.Set.empty in
419419
(* FIXME avoid set, same as pr_glb *)
420-
let e = Tac2intern.debug_globalize_allow_ext avoid e in
421420
Tac2print.pr_rawexpr_gen ~avoid E5 e) in
422421
let pr_glb (ids, e) =
423422
let ids =
@@ -437,7 +436,6 @@ let () =
437436

438437
let () =
439438
let pr_raw e = Genprint.PrinterBasic (fun _ _ ->
440-
let e = Tac2intern.debug_globalize_allow_ext Id.Set.empty e in
441439
Tac2print.pr_rawexpr_gen ~avoid:Id.Set.empty E5 e)
442440
in
443441
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)