forked from CakeML/candle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
preterm.ml
455 lines (387 loc) · 19.4 KB
/
preterm.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
(* ========================================================================= *)
(* Preterms and pretypes; typechecking; translation to types and terms. *)
(* *)
(* John Harrison, University of Cambridge Computer Laboratory *)
(* *)
(* (c) Copyright, University of Cambridge 1998 *)
(* (c) Copyright, John Harrison 1998-2007 *)
(* (c) Copyright, Marco Maggesi 2012 *)
(* (c) Copyright, Vincent Aravantinos 2012 *)
(* ========================================================================= *)
needs "printer.ml";;
(* ------------------------------------------------------------------------- *)
(* Flag to say whether to treat varstruct "\const. bod" as variable. *)
(* ------------------------------------------------------------------------- *)
let ignore_constant_varstruct = ref true;;
(* ------------------------------------------------------------------------- *)
(* Flags controlling the treatment of invented type variables in quotations. *)
(* It can be treated as an error, result in a warning, or neither of those. *)
(* ------------------------------------------------------------------------- *)
let type_invention_warning = ref true;;
let type_invention_error = ref false;;
(* ------------------------------------------------------------------------- *)
(* Implicit types or type schemes for non-constants. *)
(* ------------------------------------------------------------------------- *)
let the_implicit_types = ref ([]:(string*hol_type)list);;
(* ------------------------------------------------------------------------- *)
(* Overloading and interface mapping. *)
(* ------------------------------------------------------------------------- *)
let make_overloadable s gty =
if can (assoc s) (!the_overload_skeletons)
then if assoc s (!the_overload_skeletons) = gty then ()
else failwith "make_overloadable: differs from existing skeleton"
else the_overload_skeletons := (s,gty)::(!the_overload_skeletons);;
let remove_interface sym =
let interface = filter ((<>)sym o fst) (!the_interface) in
the_interface := interface;;
let reduce_interface (sym,tm) =
let namty = try dest_const tm with Failure _ -> dest_var tm in
the_interface := filter ((<>) (sym,namty)) (!the_interface);;
let override_interface (sym,tm) =
let namty = try dest_const tm with Failure _ -> dest_var tm in
let interface = filter ((<>)sym o fst) (!the_interface) in
the_interface := (sym,namty)::interface;;
let overload_interface (sym,tm) =
let gty = try assoc sym (!the_overload_skeletons) with Failure _ ->
failwith ("symbol \""^sym^"\" is not overloadable") in
let (name,ty) as namty = try dest_const tm with Failure _ -> dest_var tm in
if not (can (type_match gty ty) [])
then failwith "Not an instance of type skeleton" else
let interface = filter ((<>) (sym,namty)) (!the_interface) in
the_interface := (sym,namty)::interface;;
let prioritize_overload ty =
do_list
(fun (s,gty) ->
try let _,(n,t) = find
(fun (s',(n,t)) -> s' = s && mem ty (map fst (type_match gty t [])))
(!the_interface) in
overload_interface(s,mk_var(n,t))
with Failure _ -> ())
(!the_overload_skeletons);;
(* ------------------------------------------------------------------------- *)
(* Type abbreviations. *)
(* ------------------------------------------------------------------------- *)
let new_type_abbrev,remove_type_abbrev,type_abbrevs =
let the_type_abbreviations = ref ([]:(string*hol_type)list) in
let remove_type_abbrev s =
the_type_abbreviations :=
filter (fun (s',_) -> s' <> s) (!the_type_abbreviations) in
let new_type_abbrev(s,ty) =
(remove_type_abbrev s;
the_type_abbreviations :=
merge (fun x y -> Pair.compare String.compare Type.compare x y = Less)
[s,ty] (!the_type_abbreviations)) in
let type_abbrevs() = !the_type_abbreviations in
new_type_abbrev,remove_type_abbrev,type_abbrevs;;
(* ------------------------------------------------------------------------- *)
(* Handle constant hiding. *)
(* ------------------------------------------------------------------------- *)
let hide_constant,unhide_constant,is_hidden =
let hcs = ref ([]:string list) in
let hide_constant c = hcs := union [c] (!hcs)
and unhide_constant c = hcs := subtract (!hcs) [c]
and is_hidden c = mem c (!hcs) in
hide_constant,unhide_constant,is_hidden;;
(* ------------------------------------------------------------------------- *)
(* The type of pretypes. *)
(* ------------------------------------------------------------------------- *)
type pretype = Utv of string (* User type variable *)
| Ptycon of string * pretype list (* Type constructor *)
| Stv of int;; (* System type variable *)
(* ------------------------------------------------------------------------- *)
(* Dummy pretype for the parser to stick in before a proper typing pass. *)
(* ------------------------------------------------------------------------- *)
let dpty = Ptycon("",[]);;
(* ------------------------------------------------------------------------- *)
(* Convert type to pretype. *)
(* ------------------------------------------------------------------------- *)
let rec pretype_of_type ty =
match ty with
Tyvar s -> Utv s
| Tyapp(con,args) -> Ptycon(con,map pretype_of_type args);;
(* ------------------------------------------------------------------------- *)
(* Preterm syntax. *)
(* ------------------------------------------------------------------------- *)
type preterm = Varp of string * pretype (* Variable - v *)
| Constp of string * pretype (* Constant - c *)
| Combp of preterm * preterm (* Combination - f x *)
| Absp of preterm * preterm (* Lambda-abstraction - \x. t *)
| Typing of preterm * pretype;; (* Type constraint - t : ty *)
(* ------------------------------------------------------------------------- *)
(* Convert term to preterm. *)
(* ------------------------------------------------------------------------- *)
let rec preterm_of_term tm =
try let n,ty = dest_var tm in
Varp(n,pretype_of_type ty)
with Failure _ -> try
let n,ty = dest_const tm in
Constp(n,pretype_of_type ty)
with Failure _ -> try
let v,bod = dest_abs tm in
Absp(preterm_of_term v,preterm_of_term bod)
with Failure _ ->
let l,r = dest_comb tm in
Combp(preterm_of_term l,preterm_of_term r);;
(* ------------------------------------------------------------------------- *)
(* Main pretype->type, preterm->term and retypechecking functions. *)
(* ------------------------------------------------------------------------- *)
let type_of_pretype,term_of_preterm,retypecheck =
let tyv_num = ref 0 in
let new_type_var() = let n = !tyv_num in (tyv_num := n + 1; Stv(n)) in
let pmk_cv(s,pty) =
if can get_const_type s then Constp(s,pty)
else Varp(s,pty) in
let pmk_numeral =
let num_pty = Ptycon("num",[]) in
let NUMERAL = Constp("NUMERAL",Ptycon("fun",[num_pty; num_pty]))
and BIT0 = Constp("BIT0",Ptycon("fun",[num_pty; num_pty]))
and BIT1 = Constp("BIT1",Ptycon("fun",[num_pty; num_pty]))
and t_0 = Constp("_0",num_pty) in
let rec pmk_numeral(n) =
if n =/ num_0 then t_0 else
let m = quo_num n (num_2) and b = mod_num n (num_2) in
let op = if b =/ num_0 then BIT0 else BIT1 in
Combp(op,pmk_numeral(m)) in
fun n -> Combp(NUMERAL,pmk_numeral n) in
(* ----------------------------------------------------------------------- *)
(* Pretype substitution for a pretype resulting from translation of type. *)
(* ----------------------------------------------------------------------- *)
let rec pretype_subst th ty =
match ty with
Ptycon(tycon,args) -> Ptycon(tycon,map (pretype_subst th) args)
| Utv v -> rev_assocd ty th ty
| _ -> failwith "pretype_subst: Unexpected form of pretype" in
(* ----------------------------------------------------------------------- *)
(* Convert type to pretype with new Stvs for all type variables. *)
(* ----------------------------------------------------------------------- *)
let pretype_instance ty =
let gty = pretype_of_type ty
and tyvs = map pretype_of_type (tyvars ty) in
let subs = map (fun tv -> new_type_var(),tv) tyvs in
pretype_subst subs gty in
(* ----------------------------------------------------------------------- *)
(* Get a new instance of a constant's generic type modulo interface. *)
(* ----------------------------------------------------------------------- *)
let get_generic_type cname =
match filter ((=) cname o fst) (!the_interface) with
[_,(c,ty)] -> ty
| _::_::_ -> assoc cname (!the_overload_skeletons)
| [] -> get_const_type cname in
(* ----------------------------------------------------------------------- *)
(* Get the implicit generic type of a variable. *)
(* ----------------------------------------------------------------------- *)
let get_var_type vname =
assoc vname (!the_implicit_types) in
(* ----------------------------------------------------------------------- *)
(* Unravel unifications and apply them to a type. *)
(* ----------------------------------------------------------------------- *)
let rec solve env pty =
match pty with
Ptycon(f,args) -> Ptycon(f,map (solve env) args)
| Stv(i) -> if defined env i then solve env (apply env i) else pty
| _ -> pty in
(* ----------------------------------------------------------------------- *)
(* Functions for display of preterms and pretypes, by converting them *)
(* to terms and types then re-using standard printing functions. *)
(* ----------------------------------------------------------------------- *)
let free_stvs =
let rec free_stvs stv = match stv with
|Stv n -> [n]
|Utv _ -> []
|Ptycon(_,args) -> flat (map free_stvs args)
in
setify Int.(<=) o free_stvs
in
let string_of_pretype stvs =
let rec type_of_pretype' ns = function
|Stv n -> mk_vartype (if mem n ns then "?" ^ string_of_int n else "_")
|Utv v -> mk_vartype v
|Ptycon(con,args) -> mk_type(con,map (type_of_pretype' ns) args)
in
string_of_type o type_of_pretype' stvs
in
let string_of_preterm =
let rec untyped_t_of_pt pt = match pt with
|Varp(s,pty) -> mk_var(s,aty)
|Constp(s,pty) -> mk_mconst(s,get_const_type s)
|Combp(l,r) -> mk_comb(untyped_t_of_pt l,untyped_t_of_pt r)
|Absp(v,bod) -> mk_gabs(untyped_t_of_pt v,untyped_t_of_pt bod)
|Typing(ptm,pty) -> untyped_t_of_pt ptm
in
string_of_term o untyped_t_of_pt
in
let string_of_ty_error env = function
|None ->
"unify: types cannot be unified "
^ "(you should not see this message, please report)"
|Some(t,ty1,ty2) ->
let ty1 = solve env ty1 and ty2 = solve env ty2 in
let sty1 = string_of_pretype (free_stvs ty2) ty1 in
let sty2 = string_of_pretype (free_stvs ty1) ty2 in
let default_msg s =
" " ^ s ^ " cannot have type " ^ sty1 ^ " and " ^ sty2
^ " simultaneously"
in
match t with
|Constp(s,_) ->
" " ^ s ^ " has type " ^ string_of_type (get_const_type s) ^ ", "
^ "it cannot be used with type " ^ sty2
|Varp(s,_) -> default_msg s
|t -> default_msg (string_of_preterm t)
in
(* ----------------------------------------------------------------------- *)
(* Unification of types *)
(* ----------------------------------------------------------------------- *)
let rec istrivial ptm env x = function
|Stv y ->
y = x || defined env y && istrivial ptm env x (apply env y)
|Ptycon(f,args) when exists (istrivial ptm env x) args ->
failwith (string_of_ty_error env ptm)
|(Ptycon _ | Utv _) -> false
in
let unify ptm env ty1 ty2 =
let rec unify env = function
|[] -> env
|(ty1,ty2,_)::oth when ty1 = ty2 -> unify env oth
|(Ptycon(f,fargs),Ptycon(g,gargs),ptm)::oth ->
if f = g && length fargs = length gargs
then unify env (map2 (fun x y -> x,y,ptm) fargs gargs @ oth)
else failwith (string_of_ty_error env ptm)
|(Stv x,t,ptm)::oth ->
if defined env x then unify env ((apply env x,t,ptm)::oth)
else unify (if istrivial ptm env x t then env else (x|->t) env) oth
|(t,Stv x,ptm)::oth -> unify env ((Stv x,t,ptm)::oth)
|(_,_,ptm)::oth -> failwith (string_of_ty_error env ptm)
in
unify env [ty1,ty2,(match ptm with None -> None | Some t -> Some(t,ty1,ty2))]
in
(* ----------------------------------------------------------------------- *)
(* Attempt to attach a given type to a term, performing unifications. *)
(* ----------------------------------------------------------------------- *)
let rec typify ty (ptm,venv,uenv) =
match ptm with
|Varp(s,_) when can (assoc s) venv ->
let ty' = assoc s venv in
Varp(s,ty'),[],unify (Some ptm) uenv ty' ty
|Varp(s,_) when can num_of_string s ->
let t = pmk_numeral(num_of_string s) in
let ty' = Ptycon("num",[]) in
t,[],unify (Some ptm) uenv ty' ty
|Varp(s,_) ->
warn (s <> "" && isnum s) "Non-numeral begins with a digit";
if not(is_hidden s) && can get_generic_type s then
let pty = pretype_instance(get_generic_type s) in
let ptm = Constp(s,pty) in
ptm,[],unify (Some ptm) uenv pty ty
else
let ptm = Varp(s,ty) in
if not(can get_var_type s) then ptm,[s,ty],uenv
else
let pty = pretype_instance(get_var_type s) in
ptm,[s,ty],unify (Some ptm) uenv pty ty
|Combp(f,x) ->
let ty'' = new_type_var() in
let ty' = Ptycon("fun",[ty'';ty]) in
let f',venv1,uenv1 = typify ty' (f,venv,uenv) in
let x',venv2,uenv2 = typify ty'' (x,venv1@venv,uenv1) in
Combp(f',x'),(venv1@venv2),uenv2
|Typing(tm,pty) -> typify ty (tm,venv,unify (Some tm) uenv ty pty)
|Absp(v,bod) ->
let ty',ty'' =
match ty with
|Ptycon("fun",[ty';ty'']) -> ty',ty''
|_ -> new_type_var(),new_type_var()
in
let ty''' = Ptycon("fun",[ty';ty'']) in
let uenv0 = unify (Some ptm) uenv ty''' ty in
let v',venv1,uenv1 =
let v',venv1,uenv1 = typify ty' (v,[],uenv0) in
match v' with
|Constp(s,_) when !ignore_constant_varstruct ->
Varp(s,ty'),[s,ty'],uenv0
|_ -> v',venv1,uenv1
in
let bod',venv2,uenv2 = typify ty'' (bod,venv1@venv,uenv1) in
Absp(v',bod'),venv2,uenv2
|_ -> failwith "typify: unexpected constant at this stage"
in
(* ----------------------------------------------------------------------- *)
(* Further specialize type constraints by resolving overloadings. *)
(* ----------------------------------------------------------------------- *)
let rec resolve_interface ptm cont env =
match ptm with
Combp(f,x) -> resolve_interface f (resolve_interface x cont) env
| Absp(v,bod) -> resolve_interface v (resolve_interface bod cont) env
| Varp(_,_) -> cont env
| Constp(s,ty) ->
let maps = filter (fun (s',_) -> s' = s) (!the_interface) in
if maps = [] then cont env else
tryfind (fun (_,(_,ty')) ->
let ty' = pretype_instance ty' in
cont(unify (Some ptm) env ty' ty)) maps
in
(* ----------------------------------------------------------------------- *)
(* Hence apply throughout a preterm. *)
(* ----------------------------------------------------------------------- *)
let rec solve_preterm env ptm =
match ptm with
Varp(s,ty) -> Varp(s,solve env ty)
| Combp(f,x) -> Combp(solve_preterm env f,solve_preterm env x)
| Absp(v,bod) -> Absp(solve_preterm env v,solve_preterm env bod)
| Constp(s,ty) -> let tys = solve env ty in
try let _,(c',_) = find
(fun (s',(c',ty')) ->
s = s' && can (unify None env (pretype_instance ty')) ty)
(!the_interface) in
pmk_cv(c',tys)
with Failure _ -> Constp(s,tys)
in
(* ----------------------------------------------------------------------- *)
(* Flag to indicate that Stvs were translated to real type variables. *)
(* ----------------------------------------------------------------------- *)
let stvs_translated = ref false in
(* ----------------------------------------------------------------------- *)
(* Pretype <-> type conversion; -> flags system type variable translation. *)
(* ----------------------------------------------------------------------- *)
let rec type_of_pretype ty =
match ty with
Stv n -> stvs_translated := true;
let s = "?"^(string_of_int n) in
mk_vartype(s)
| Utv(v) -> mk_vartype(v)
| Ptycon(con,args) -> mk_type(con,map type_of_pretype args) in
(* ----------------------------------------------------------------------- *)
(* Maps preterms to terms. *)
(* ----------------------------------------------------------------------- *)
let term_of_preterm =
let rec term_of_preterm ptm =
match ptm with
Varp(s,pty) -> mk_var(s,type_of_pretype pty)
| Constp(s,pty) -> mk_mconst(s,type_of_pretype pty)
| Combp(l,r) -> mk_comb(term_of_preterm l,term_of_preterm r)
| Absp(v,bod) -> mk_gabs(term_of_preterm v,term_of_preterm bod)
| Typing(ptm,pty) -> term_of_preterm ptm in
let report_type_invention () =
if !stvs_translated then
if !type_invention_error
then failwith "typechecking error (cannot infer type of variables)"
else warn (!type_invention_warning) "inventing type variables" in
fun ptm -> stvs_translated := false;
let tm = term_of_preterm ptm in
report_type_invention (); tm in
(* ----------------------------------------------------------------------- *)
(* Overall typechecker: initial typecheck plus overload resolution pass. *)
(* ----------------------------------------------------------------------- *)
let retypecheck venv ptm =
let ty = new_type_var() in
let ptm',_,env =
try typify ty (ptm,venv,undefined Int.compare)
with Failure e -> failwith
("typechecking error (initial type assignment):" ^ e) in
let env' =
try resolve_interface ptm' (fun e -> e) env
with Failure _ -> failwith "typechecking error (overload resolution)" in
let ptm'' = solve_preterm env' ptm' in
ptm''
in
type_of_pretype,term_of_preterm,retypecheck;;