-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathtypechecker.ml
2381 lines (2146 loc) · 77.6 KB
/
typechecker.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
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(******************************************************************************)
(* *)
(* The Alt-Ergo theorem prover *)
(* Copyright (C) 2006-2013 *)
(* *)
(* Sylvain Conchon *)
(* Evelyne Contejean *)
(* *)
(* Francois Bobot *)
(* Mohamed Iguernelala *)
(* Stephane Lescuyer *)
(* Alain Mebsout *)
(* *)
(* CNRS - INRIA - Universite Paris Sud *)
(* *)
(* This file is distributed under the terms of the Apache Software *)
(* License version 2.0 *)
(* *)
(* ------------------------------------------------------------------------ *)
(* *)
(* Alt-Ergo: The SMT Solver For Software Verification *)
(* Copyright (C) 2013-2018 --- OCamlPro SAS *)
(* *)
(* This file is distributed under the terms of the Apache Software *)
(* License version 2.0 *)
(* *)
(******************************************************************************)
open Parsed
open Typed
module S = Set.Make(String)
module HSS = Hstring.Set
module MString =
Map.Make(struct type t = string let compare = String.compare end)
module Types = struct
(* environment for user-defined types *)
type t = {
to_ty : Ty.t MString.t;
from_labels : string MString.t; }
let to_tyvars = ref MString.empty
let empty =
{ to_ty = MString.empty;
from_labels = MString.empty }
let fresh_vars ~recursive vars loc =
List.map
(fun x ->
if recursive then
try MString.find x !to_tyvars
with Not_found -> assert false
else
begin
if MString.mem x !to_tyvars then
Errors.typing_error (TypeDuplicateVar x) loc;
let nv = Ty.Tvar (Ty.fresh_var ()) in
to_tyvars := MString.add x nv !to_tyvars;
nv
end
) vars
let check_number_args loc lty ty =
match ty with
| Ty.Text (lty', s)
| Ty.Trecord { Ty.args = lty'; name = s; _ }
| Ty.Tadt (s,lty') ->
if List.length lty <> List.length lty' then
Errors.typing_error (WrongNumberofArgs (Hstring.view s)) loc;
lty'
| Ty.Tsum (s, _) ->
if List.length lty <> 0 then
Errors.typing_error (WrongNumberofArgs (Hstring.view s)) loc;
[]
| _ -> assert false
let equal_pp_vars lpp lvars =
try
List.for_all2
(fun pp x ->
match pp with
| PPTvarid (y, _) -> Stdlib.(=) x y
| _ -> false
) lpp lvars
with Invalid_argument _ -> false
let rec ty_of_pp _loc env rectype = function
| PPTint -> Ty.Tint
| PPTbool -> Ty.Tbool
| PPTunit -> Ty.Tunit
| PPTreal -> Ty.Treal
| PPTbitv n -> Ty.Tbitv n
| PPTvarid (s, _) ->
begin
try MString.find s !to_tyvars
with Not_found ->
let nty = Ty.Tvar (Ty.fresh_var ()) in
to_tyvars := MString.add s nty !to_tyvars;
nty
end
| PPTexternal (l, s, loc) when String.equal s "farray" ->
let t1,t2 = match l with
| [t2] -> PPTint,t2
| [t1;t2] -> t1,t2
| _ -> Errors.typing_error (WrongArity(s,2)) loc in
let ty1 = ty_of_pp loc env rectype t1 in
let ty2 = ty_of_pp loc env rectype t2 in
Ty.Tfarray (ty1, ty2)
| PPTexternal (l, s, loc) ->
begin
match rectype with
| Some (id, vars, ty) when Stdlib.(=) s id &&
equal_pp_vars l vars -> ty
| _ ->
try
let lty = List.map (ty_of_pp loc env rectype) l in
let ty = MString.find s env.to_ty in
let vars = check_number_args loc lty ty in
Ty.instantiate vars lty ty
with Not_found -> Errors.typing_error (UnknownType s) loc
end
let add_decl ~recursive env vars id body loc =
if MString.mem id env.to_ty && not recursive then
Errors.typing_error (ClashType id) loc;
let ty_vars = fresh_vars ~recursive vars loc in
match body with
| Abstract ->
let ty = Ty.text ty_vars id in
ty, { env with to_ty = MString.add id ty env.to_ty }
| Enum lc ->
let ty = Ty.tsum id lc in
ty, { env with to_ty = MString.add id ty env.to_ty }
| Record (record_constr, lbs) ->
let lbs =
List.map (fun (x, pp) -> x, ty_of_pp loc env None pp) lbs in
let ty = Ty.trecord ~record_constr ty_vars id lbs in
ty, { to_ty = MString.add id ty env.to_ty;
from_labels =
List.fold_left
(fun fl (l,_) -> MString.add l id fl) env.from_labels lbs }
| Algebraic l ->
let l = (* convert ppure_type to Ty.t in l *)
List.map (fun (constr, l) ->
constr,
List.map (fun (field, pp) -> field, ty_of_pp loc env None pp) l
) l
in
let body =
if l == [] then None (* in initialization step, no body *)
else Some l
in
let ty = Ty.t_adt ~body id ty_vars in
ty, { env with to_ty = MString.add id ty env.to_ty }
module SH = Set.Make(Hstring)
let check_labels lbs ty loc =
let rec check_duplicates s = function
| [] -> ()
| (lb, _) :: l ->
if SH.mem lb s then Errors.typing_error (DuplicateLabel lb) loc;
check_duplicates (SH.add lb s) l
in
check_duplicates SH.empty lbs;
match ty with
| Ty.Trecord { Ty.lbs = l; _ } ->
if List.length lbs <> List.length l then
Errors.typing_error WrongNumberOfLabels loc;
List.iter
(fun (lb, _) ->
try ignore (Hstring.list_assoc lb l)
with Not_found ->
Errors.typing_error (WrongLabel(lb, ty)) loc) lbs;
ty
| _ -> assert false
let from_labels env lbs loc =
match lbs with
| [] -> assert false
| (l, _) :: _ ->
try
let l = Hstring.view l in
let ty = MString.find (MString.find l env.from_labels) env.to_ty in
check_labels lbs ty loc
with Not_found -> Errors.typing_error (NoRecordType l) loc
let rec monomorphized = function
| PPTvarid (x, _) when not (MString.mem x !to_tyvars) ->
to_tyvars := MString.add x (Ty.fresh_empty_text ()) !to_tyvars;
| PPTexternal (args, _, _) ->
List.iter monomorphized args
| _ -> ()
end
module Env = struct
type profile = { args : Ty.t list; result : Ty.t }
type logic_kind =
| RecordConstr
| RecordDestr
| AdtConstr
| AdtDestr
| Other
type t = {
var_map : (Symbols.t * Ty.t) MString.t ; (* variables' map*)
types : Types.t ;
logics : (Symbols.t * profile * logic_kind) MString.t;
(* logic symbols' map *)
}
let empty = {
var_map = MString.empty;
types = Types.empty;
logics = MString.empty
}
let add env lv fvar ty =
let vmap =
List.fold_left
(fun vmap x -> MString.add x (fvar x, ty) vmap) env.var_map lv in
{ env with var_map = vmap }
let add_var env lv pp_ty loc =
let ty = Types.ty_of_pp loc env.types None pp_ty in
let fvar s = Symbols.var @@ Var.of_string s in
add env lv fvar ty
let add_ty_var env lv ty =
let fvar s = Symbols.var @@ Var.of_string s in
add env lv fvar ty
let add_names env lv pp_ty loc =
Types.monomorphized pp_ty;
let ty = Types.ty_of_pp loc env.types None pp_ty in
add env lv Symbols.name ty
let add_names_lbl env lv pp_ty loc =
Types.monomorphized pp_ty;
let ty = Types.ty_of_pp loc env.types None pp_ty in
let rlv =
List.fold_left (fun acc (x, lbl) ->
let lbl = Hstring.make lbl in
if not (Hstring.equal lbl Hstring.empty) then
Symbols.add_label lbl (Symbols.name x);
x::acc
) [] lv in
let lv = List.rev rlv in
add env lv Symbols.name ty
let add_logics ?(kind=Other) env mk_symb names pp_profile loc =
let decl, profile =
match pp_profile with
| PPredicate args ->
let args = List.map (Types.ty_of_pp loc env.types None) args in
TPredicate args,
{ args = args; result = Ty.Tbool }
(*| PFunction ([], PPTvarid (_, loc)) ->
typing_error CannotGeneralize loc*)
| PFunction(args, res) ->
let args = List.map (Types.ty_of_pp loc env.types None) args in
let res = Types.ty_of_pp loc env.types None res in
TFunction (args, res),
{ args = args; result = res }
in
let logics =
List.fold_left
(fun logics (n, lbl) ->
let sy = mk_symb n in
if MString.mem n logics then
Errors.typing_error (SymbAlreadyDefined n) loc;
let lbl = Hstring.make lbl in
if not (Hstring.equal lbl Hstring.empty) then
Symbols.add_label lbl sy;
MString.add n (sy, profile, kind) logics)
env.logics names
in
decl, { env with logics = logics }
let add_constr ~record env constr args_ty ty loc =
let pp_profile = PFunction (args_ty, ty) in
let kind = if record then RecordConstr else AdtConstr in
add_logics ~kind env Symbols.constr [constr, ""] pp_profile loc
let add_destr ~record env destr pur_ty lbl_ty loc =
let pp_profile = PFunction ([pur_ty], lbl_ty) in
let mk_sy s =
if record then (Symbols.Op (Access (Hstring.make s)))
else Symbols.destruct ~guarded:true s
in
let kind = if record then RecordDestr else AdtDestr in
add_logics ~kind env mk_sy [destr, ""] pp_profile loc
let find { var_map = m; _ } n = MString.find n m
let list_of { var_map = m; _ } = MString.fold (fun _ c acc -> c::acc) m []
let add_type_decl ?(recursive=false) env vars id body loc =
let ty, types = Types.add_decl ~recursive env.types vars id body loc in
ty, { env with types = types; }
(* returns a type with fresh variables *)
let fresh_type env n loc =
try
let s, { args = args; result = r}, kind = MString.find n env.logics in
let args, subst = Ty.fresh_list args Ty.esubst in
let res, _ = Ty.fresh r subst in
s, { args = args; result = res }, kind
with Not_found -> Errors.typing_error (SymbUndefined n) loc
end
let symbol_of = function
PPadd -> Symbols.Op Symbols.Plus
| PPsub -> Symbols.Op Symbols.Minus
| PPmul -> Symbols.Op Symbols.Mult
| PPdiv -> Symbols.Op Symbols.Div
| PPmod -> Symbols.Op Symbols.Modulo
| PPpow_int -> Symbols.Op Symbols.Pow
| PPpow_real -> Symbols.Op Symbols.Pow
| _ -> assert false
let append_type msg ty =
Format.asprintf "%s %a" msg Ty.print ty
let type_var_desc env p loc =
try
let s,t = Env.find env p in
Options.tool_req 1 (append_type "TR-Typing-Var$_\\Gamma$ type" t);
TTvar s , t
with Not_found ->
match Env.fresh_type env p loc with
| s,
{ Env.args = []; result = ty},
(Env.Other | Env.AdtConstr (*more exactly, Enum constr*) ) ->
TTapp (s, []) , ty
| _ -> Errors.typing_error (ShouldBeApply p) loc
let check_no_duplicates =
let rec aux loc l ss =
match l with
| [] -> ()
| e :: l ->
if S.mem e ss then Errors.typing_error (ClashParam e) loc;
aux loc l (S.add e ss)
in
fun loc args -> aux loc args S.empty
let filter_patterns pats ty_body _loc =
let cases =
List.fold_left (fun s {Ty.constr=c; _} -> HSS.add c s) HSS.empty ty_body
in
let missing, filtered_pats, dead =
List.fold_left
(fun (miss, filtered_pats, dead) ((p, _) as u) ->
match p with
| Constr { name; _ } ->
assert (HSS.mem name cases); (* pattern is well typed *)
if HSS.mem name miss then (* not encountered yet *)
HSS.remove name miss, u :: filtered_pats, dead
else (* case already seen --> dead pattern *)
miss, pats, p :: dead
| Var _ ->
if HSS.is_empty miss then (* match already exhaussive -> dead case *)
miss, filtered_pats, p :: dead
else (* covers all remaining cases, miss becomes empty *)
HSS.empty, u :: filtered_pats, dead
)(cases, [], []) pats
in
missing, List.rev filtered_pats, dead
let check_pattern_matching missing dead loc =
if not (HSS.is_empty missing) then
Errors.typing_error (MatchNotExhaustive (HSS.elements missing)) loc;
if dead != [] then
let dead =
List.rev_map
(function
| Constr { name; _ } -> name
| Var v -> (Var.view v).Var.hs
) dead
in
Printer.print_wrn "%a"
Errors.report (Typing_error(loc,MatchUnusedCases dead));
Errors.warning_as_error ()
let mk_adequate_app p s te_args ty logic_kind =
let hp = Hstring.make p in
match logic_kind, te_args, ty with
| (Env.AdtConstr | Env.Other), _, _ ->
(* symbol 's' alreadt contains the information *)
TTapp(s, te_args)
| Env.RecordConstr, _, Ty.Trecord { Ty.lbs; _ } ->
let lbs =
try List.map2 (fun (hs, _) e -> hs, e) lbs te_args
with Invalid_argument _ -> assert false
in
TTrecord lbs
| Env.RecordDestr, [te], _ -> TTdot(te, hp)
| Env.AdtDestr, [te], _ -> TTproject (true, te, hp)
| Env.RecordDestr, _, _ -> assert false
| Env.RecordConstr, _, _ -> assert false
| Env.AdtDestr, _, _ -> assert false
let rec type_term ?(call_from_type_form=false) env f =
let {pp_loc = loc; pp_desc} = f in
let e, ty = match pp_desc with
| PPconst ConstTrue ->
Options.tool_req 1 (append_type "TR-Typing-Const type" Ty.Tbool);
TTconst Ttrue, Ty.Tbool
| PPconst ConstFalse ->
Options.tool_req 1 (append_type "TR-Typing-Const type" Ty.Tbool);
TTconst Tfalse, Ty.Tbool
| PPconst ConstVoid ->
Options.tool_req 1 (append_type "TR-Typing-Const type" Ty.Tunit);
TTconst Tvoid, Ty.Tunit
| PPconst (ConstInt n) ->
Options.tool_req 1 (append_type "TR-Typing-Const type" Ty.Tint);
TTconst(Tint n), Ty.Tint
| PPconst (ConstReal n) ->
Options.tool_req 1 (append_type "TR-Typing-Const type" Ty.Treal);
TTconst(Treal n), Ty.Treal
| PPconst (ConstBitv n) ->
Options.tool_req 1
(append_type "TR-Typing-Const type" (Ty.Tbitv (String.length n)));
TTconst(Tbitv n), Ty.Tbitv (String.length n)
| PPvar p ->
type_var_desc env p loc
| PPapp(p,args) ->
begin
let te_args = List.map (type_term env) args in
let lt_args = List.map (
fun { c = { tt_ty = t; _ }; _ } -> t
) te_args in
let s, {Env.args = lt; result = t}, kind = Env.fresh_type env p loc in
try
List.iter2 Ty.unify lt lt_args;
Options.tool_req 1 (append_type "TR-Typing-App type" t);
mk_adequate_app p s te_args t kind, t
with
| Ty.TypeClash(t1,t2) -> Errors.typing_error (Unification(t1,t2)) loc
| Invalid_argument _ -> Errors.typing_error (WrongNumberofArgs p) loc
end
| PPinfix(t1,(PPadd | PPsub | PPmul | PPdiv as op),t2) ->
begin
let s = symbol_of op in
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let ty1 = Ty.shorten te1.c.tt_ty in
let ty2 = Ty.shorten te2.c.tt_ty in
match ty1, ty2 with
| Ty.Tint, Ty.Tint ->
Options.tool_req 1 (append_type "TR-Typing-OpBin type" ty1);
TTinfix(te1,s,te2) , ty1
| Ty.Treal, Ty.Treal ->
Options.tool_req 1 (append_type "TR-Typing-OpBin type" ty2);
TTinfix(te1,s,te2), ty2
| Ty.Tint, _ ->
Errors.typing_error (ShouldHaveType(ty2,Ty.Tint)) t2.pp_loc
| Ty.Treal, _ ->
Errors.typing_error (ShouldHaveType(ty2,Ty.Treal)) t2.pp_loc
| _ ->
Errors.typing_error (ShouldHaveTypeIntorReal ty1) t1.pp_loc
end
| PPinfix(t1, PPmod, t2) ->
begin
let s = symbol_of PPmod in
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let ty1 = Ty.shorten te1.c.tt_ty in
let ty2 = Ty.shorten te2.c.tt_ty in
match ty1, ty2 with
| Ty.Tint, Ty.Tint ->
Options.tool_req 1 (append_type "TR-Typing-OpMod type" ty1);
TTinfix(te1,s,te2) , ty1
| _ -> Errors.typing_error (ShouldHaveTypeInt ty1) t1.pp_loc
end
| PPinfix(t1, (PPpow_int | PPpow_real as op), t2) ->
begin
let s = symbol_of op in
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let ty1 = Ty.shorten te1.c.tt_ty in
let ty2 = Ty.shorten te2.c.tt_ty in
match ty1, ty2, op with
| Ty.Tint, Ty.Tint, PPpow_int ->
Options.tool_req 1 (append_type "TR-Typing-Oppow_int type" ty1);
TTinfix(te1,s,te2) , Ty.Tint
| (Ty.Tint | Ty.Treal), (Ty.Tint | Ty.Treal), PPpow_real ->
Options.tool_req 1 (append_type "TR-Typing-Oppow_real type" ty1);
TTinfix(te1,s,te2) , Ty.Treal
| Ty.Treal , _, PPpow_int ->
Errors.typing_error (ShouldHaveTypeInt Ty.Tint) t1.pp_loc
| _, Ty.Treal, PPpow_int ->
Errors.typing_error (ShouldHaveTypeInt Ty.Tint) t2.pp_loc
| _, _, PPpow_real ->
Errors.typing_error (ShouldHaveTypeInt Ty.Treal) t1.pp_loc
| _, _, PPpow_int ->
Errors.typing_error (ShouldHaveTypeInt Ty.Tint) t1.pp_loc
| _ -> assert false (* can't happen *)
end
| PPprefix(PPneg, { pp_desc=PPconst (ConstInt n); _ }) ->
Options.tool_req 1 (append_type "TR-Typing-OpUnarith type" Ty.Tint);
TTconst(Tint ("-"^n)), Ty.Tint
| PPprefix(PPneg, { pp_desc=PPconst (ConstReal n); _ }) ->
Options.tool_req 1 (append_type "TR-Typing-OpUnarith type" Ty.Treal);
TTconst(Treal (Num.minus_num n)), Ty.Treal
| PPprefix(PPneg, e) ->
let te = type_term env e in
let ty = Ty.shorten te.c.tt_ty in
if ty!=Ty.Tint && ty!=Ty.Treal then
Errors.typing_error (ShouldHaveTypeIntorReal ty) e.pp_loc;
Options.tool_req 1 (append_type "TR-Typing-OpUnarith type" ty);
TTprefix(Symbols.Op Symbols.Minus, te), ty
| PPconcat(t1, t2) ->
begin
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let ty1 = Ty.shorten te1.c.tt_ty in
let ty2 = Ty.shorten te2.c.tt_ty in
match ty1, ty2 with
| Ty.Tbitv n , Ty.Tbitv m ->
Options.tool_req 1
(append_type "TR-Typing-OpConcat type" (Ty.Tbitv (n+m)));
TTconcat(te1, te2), Ty.Tbitv (n+m)
| Ty.Tbitv _ , _ ->
Errors.typing_error (ShouldHaveTypeBitv ty2) t2.pp_loc
| _ , Ty.Tbitv _ ->
Errors.typing_error (ShouldHaveTypeBitv ty1) t1.pp_loc
| _ ->
Errors.typing_error (ShouldHaveTypeBitv ty1) t1.pp_loc
end
| PPextract(e, ({ pp_desc=PPconst(ConstInt i); _ } as ei),
({ pp_desc = PPconst(ConstInt j); _ } as ej)) ->
begin
let te = type_term env e in
let tye = Ty.shorten te.c.tt_ty in
let i = int_of_string i in
let j = int_of_string j in
match tye with
| Ty.Tbitv n ->
if i>j then Errors.typing_error (BitvExtract(i,j)) loc;
if j>=n then Errors.typing_error (BitvExtractRange(n,j) ) loc;
let tei = type_term env ei in
let tej = type_term env ej in
Options.tool_req 1
(append_type "TR-Typing-OpExtract type" (Ty.Tbitv (j-i+1)));
TTextract(te, tei, tej), Ty.Tbitv (j-i+1)
| _ ->
Errors.typing_error (ShouldHaveType(tye,Ty.Tbitv (j+1))) loc
end
| PPget (t1, t2) ->
begin
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let tyarray = Ty.shorten te1.c.tt_ty in
let tykey2 = Ty.shorten te2.c.tt_ty in
match tyarray with
| Ty.Tfarray (tykey,tyval) ->
begin
try
Ty.unify tykey tykey2;
Options.tool_req 1 (append_type "TR-Typing-OpGet type" tyval);
TTget(te1, te2), tyval
with
| Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) loc
end
| _ -> Errors.typing_error ShouldHaveTypeArray t1.pp_loc
end
| PPset (t1, t2, t3) ->
begin
let te1 = type_term env t1 in
let te2 = type_term env t2 in
let te3 = type_term env t3 in
let ty1 = Ty.shorten te1.c.tt_ty in
let tykey2 = Ty.shorten te2.c.tt_ty in
let tyval2 = Ty.shorten te3.c.tt_ty in
try
match ty1 with
| Ty.Tfarray (tykey,tyval) ->
Ty.unify tykey tykey2;Ty.unify tyval tyval2;
Options.tool_req 1 (append_type "TR-Typing-OpSet type" ty1);
TTset(te1, te2, te3), ty1
| _ -> Errors.typing_error ShouldHaveTypeArray t1.pp_loc
with
| Ty.TypeClash(t, t') -> Errors.typing_error (Unification(t, t')) loc
end
| PPif(cond,t2,t3) ->
begin
let cond = type_form env cond in
(* TODO : should use _fv somewhere ? *)
let te2 = type_term env t2 in
let te3 = type_term env t3 in
let ty2 = Ty.shorten te2.c.tt_ty in
let ty3 = Ty.shorten te3.c.tt_ty in
begin
try Ty.unify ty2 ty3
with Ty.TypeClash _ ->
Errors.typing_error (ShouldHaveType(ty3,ty2)) t3.pp_loc;
end;
Options.tool_req 1 (append_type "TR-Typing-Ite type" ty2);
TTite (cond, te2, te3) , ty2
end
| PPdot(t, a) ->
begin
let te = type_term env t in
let ty = Ty.shorten te.c.tt_ty in
match ty with
| Ty.Trecord { Ty.name = g; lbs; _ } ->
begin
try
let a = Hstring.make a in
TTdot(te, a), Hstring.list_assoc a lbs
with Not_found ->
let g = Hstring.view g in
Errors.typing_error (ShouldHaveLabel(g,a)) t.pp_loc
end
| _ -> Errors.typing_error (ShouldHaveTypeRecord ty) t.pp_loc
end
| PPrecord lbs ->
begin
let lbs =
List.map (fun (lb, t) -> Hstring.make lb, type_term env t) lbs in
let lbs = List.sort
(fun (l1, _) (l2, _) -> Hstring.compare l1 l2) lbs in
let ty = Types.from_labels env.Env.types lbs loc in
let ty, _ = Ty.fresh (Ty.shorten ty) Ty.esubst in
match ty with
| Ty.Trecord { Ty.lbs=ty_lbs; _ } ->
begin
try
let lbs =
List.map2
(fun (_, te) (lb,ty_lb)->
Ty.unify te.c.tt_ty ty_lb;
lb, te) lbs ty_lbs
in
TTrecord(lbs), ty
with Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) loc
end
| _ -> Errors.typing_error ShouldBeARecord loc
end
| PPwith(e, lbs) ->
begin
let te = type_term env e in
let lbs =
List.map
(fun (lb, t) -> Hstring.make lb, (type_term env t, t.pp_loc)) lbs in
let ty = Ty.shorten te.c.tt_ty in
match ty with
| Ty.Trecord { Ty.lbs = ty_lbs; _ } ->
let nlbs =
List.map
(fun (lb, ty_lb) ->
try
let v, _ = Hstring.list_assoc lb lbs in
Ty.unify ty_lb v.c.tt_ty;
lb, v
with
| Not_found ->
lb, {c = { tt_desc = TTdot(te, lb); tt_ty = ty_lb};
annot = te.annot }
| Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) loc
) ty_lbs
in
List.iter
(fun (lb, _) ->
try ignore (Hstring.list_assoc lb ty_lbs)
with Not_found ->
Errors.typing_error (NoLabelInType(lb, ty)) loc) lbs;
TTrecord(nlbs), ty
| _ -> Errors.typing_error ShouldBeARecord loc
end
| PPlet(l, t2) ->
let _ =
List.fold_left (fun z (sy,_) ->
if Util.SS.mem sy z then
Errors.typing_error (DuplicatePattern sy) loc;
Util.SS.add sy z
)Util.SS.empty l
in
let rev_l = List.rev_map (fun (sy, t) -> sy, type_term env t) l in
let env =
List.fold_left
(fun env (sy, te1) ->
let ty1 = Ty.shorten te1.c.tt_ty in
let fvar s = Symbols.var @@ Var.of_string s in
Env.add env [sy] fvar ty1
)env rev_l
in
let te2 = type_term env t2 in
let ty2 = Ty.shorten te2.c.tt_ty in
let l = List.rev_map (fun (sy, t) -> fst (Env.find env sy), t) rev_l in
Options.tool_req 1 (append_type "TR-Typing-Let type" ty2);
TTlet(l, te2), ty2
(* | PPnamed(lbl, t) -> *)
(* let te = type_term env t in *)
(* te.c.tt_desc, te.c.tt_ty *)
| PPnamed (lbl, t) ->
let te = type_term env t in
let ty = Ty.shorten te.c.tt_ty in
let lbl = Hstring.make lbl in
TTnamed (lbl, te), ty
| PPcast (t,ty) ->
let ty = Types.ty_of_pp loc env.Env.types None ty in
let te = type_term env t in
begin try
Ty.unify te.c.tt_ty ty;
te.c.tt_desc, Ty.shorten te.c.tt_ty
with
| Ty.TypeClash(t1,t2) -> Errors.typing_error (Unification(t1,t2)) loc
end
| PPproject (grded, t, lbl) ->
let te = type_term env t in
begin
try
match Env.fresh_type env lbl loc with
| _, {Env.args = [arg] ; result}, Env.AdtDestr ->
Ty.unify te.c.tt_ty arg;
TTproject (grded, te, Hstring.make lbl), Ty.shorten result
| _, {Env.args = [arg] ; result}, Env.RecordDestr ->
Ty.unify te.c.tt_ty arg;
TTdot (te, Hstring.make lbl), Ty.shorten result
| _ -> assert false
with Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) loc
end
| PPmatch (e, pats) ->
(* we can match on ADTs including records and enumerations *)
let e = type_term env e in
let ty = Ty.shorten e.c.tt_ty in
let ty_body = match ty with
| Ty.Tadt (name, params) ->
begin match Ty.type_body name params with
| Ty.Adt cases -> cases
end
| Ty.Trecord { Ty.record_constr; lbs; _ } ->
[{Ty.constr = record_constr; destrs = lbs}]
| Ty.Tsum (_,l) ->
List.map (fun e -> {Ty.constr = e; destrs = []}) l
| _ -> Errors.typing_error (ShouldBeADT ty) loc
in
let pats =
List.rev @@
List.rev_map
(fun (p, v) ->
let p, env = type_pattern p env ty ty_body in
p, type_term env v
) pats
in
let missing, filtered_pats, dead = filter_patterns pats ty_body loc in
check_pattern_matching missing dead loc;
let ty =
match filtered_pats with
| [] -> assert false
| (_, e) :: l ->
let ty = e.c.tt_ty in
List.iter
(fun (_, e) ->
Ty.unify ty e.c.tt_ty;
if not (Ty.equal ty e.c.tt_ty) then
Errors.typing_error (ShouldHaveType(e.c.tt_ty, ty)) loc
)l;
ty
in
TTmatch (e, filtered_pats), ty
| _ ->
if call_from_type_form then Errors.typing_error SyntaxError loc;
TTform (type_form env f), Ty.Tbool
in
{c = { tt_desc = e ; tt_ty = Ty.shorten ty }; annot = new_id ()}
and join_forall f = match f.pp_desc with
| PPforall(vs_ty, trs1, hyp1, f) ->
let tyvars,trs2,hyp2, f = join_forall f in
vs_ty @ tyvars , trs1@trs2 , hyp1@hyp2, f
| PPforall_named (named_vs_ty, trs1, hyp1, f) ->
let vs_ty = List.map (fun (v, _, ty) -> v, ty) named_vs_ty in
join_forall {f with pp_desc = PPforall (vs_ty, trs1, hyp1, f)}
| PPnamed(_, f) ->
join_forall f
| _ -> [] , [] , [], f
and join_exists f = match f.pp_desc with
| PPexists (vs_ty, trs1, hyp1, f) ->
let tyvars,trs2, hyp2,f = join_exists f in
vs_ty @ tyvars , trs1@trs2, hyp1@hyp2, f
| PPexists_named (named_vs_ty, trs1, hyp1, f) ->
let vs_ty = List.map (fun (v, _, ty) -> v, ty) named_vs_ty in
join_exists {f with pp_desc = PPexists (vs_ty, trs1, hyp1, f)}
| PPnamed (_, f) -> join_exists f
| _ -> [] , [] , [], f
and type_bound env bnd ty ~is_open ~is_lower =
let bk, ty_x = match bnd.pp_desc with
| PPvar s ->
assert (String.length s > 0);
begin match s.[0] with
| '?' -> Symbols.VarBnd (Var.of_string s), ty
| _ ->
let vx, ty_x = type_var_desc env s bnd.pp_loc in
let var_x =
match vx with TTvar Symbols.Var vx -> vx | _ -> assert false
in
Symbols.VarBnd var_x, ty_x
end
| PPconst num ->
let ty_x, q =
try match num with
| ConstInt s ->
Ty.Tint, Numbers.Q.from_string s
| ConstReal s ->
Ty.Treal, Numbers.Q.from_string (Num.string_of_num s)
| _ -> assert false
with _ -> assert false (*numbers well constructed with regular exprs*)
in
Symbols.ValBnd q, ty_x
| _ -> assert false
in
if not (Ty.equal ty ty_x) then
Errors.typing_error (ShouldHaveType(ty, ty_x)) bnd.pp_loc;
Symbols.mk_bound bk ty ~is_open ~is_lower
and mk_ta_eq t1 t2 =
let c =
if t1.c.tt_ty != Ty.Tbool then TAeq [t1; t2]
else
match t1.c.tt_desc, t2.c.tt_desc with
| TTconst Ttrue, _ -> TApred (t2, false)
| _, TTconst Ttrue -> TApred (t1, false)
| TTconst Tfalse, _ -> TApred (t2, true)
| _, TTconst Tfalse -> TApred (t1, true)
| _ -> TAeq [t1; t2]
in
{c ; annot=new_id ()}
and mk_ta_neq t1 t2 =
let c =
if t1.c.tt_ty != Ty.Tbool then TAneq [t1; t2]
else
match t1.c.tt_desc, t2.c.tt_desc with
| TTconst Ttrue, _ -> TApred (t2, true)
| _, TTconst Ttrue -> TApred (t1, true)
| TTconst Tfalse, _ -> TApred (t2, false)
| _, TTconst Tfalse -> TApred (t1, false)
| _ -> TAneq [t1; t2]
in
{c ; annot=new_id ()}
and type_form ?(in_theory=false) env f =
let rec type_pp_desc pp_desc = match pp_desc with
| PPconst ConstTrue ->
Options.tool_req 1 "TR-Typing-True$_F$";
TFatom {c=TAtrue; annot=new_id ()}
| PPconst ConstFalse ->
Options.tool_req 1 "TR-Typing-False$_F$";
TFatom {c=TAfalse; annot=new_id ()}
| PPvar p ->
Options.tool_req 1 "TR-Typing-Var$_F$";
let res =
try
(* allow type cast bool to predicate in some simple situations *)
let s, ty = Env.find env p in
Options.tool_req 1 (append_type "TR-Typing-Var$_\\Gamma$ type" ty);
s, { Env.args = []; result = ty}
with Not_found ->
let s, p, kd = Env.fresh_type env p f.pp_loc in
assert (kd == Env.Other);
s, p
in
let r =
match res with
| s, { Env.args = []; result = Ty.Tbool} ->
let t2 = {c = {tt_desc=TTconst Ttrue;tt_ty=Ty.Tbool};
annot = new_id ()} in
let t1 = {c = {tt_desc=TTvar s; tt_ty=Ty.Tbool};
annot = new_id ()} in
TFatom (mk_ta_eq t1 t2)
| _ -> Errors.typing_error (NotAPropVar p) f.pp_loc
in
r
| PPapp(p,args ) ->
Options.tool_req 1 "TR-Typing-App$_F$";
let te_args = List.map (type_term env) args in
let lt_args = List.map (fun { c = { tt_ty = t; _}; _ } -> t) te_args in
let s , { Env.args = lt; result }, kd = Env.fresh_type env p f.pp_loc in
begin
try
if result != Ty.Tbool then (* consider polymorphic functions *)
Ty.unify result Ty.Tbool;
try
List.iter2 Ty.unify lt lt_args;
let app = mk_adequate_app p s te_args result kd in
let r =
let t1 = {
c = {tt_desc=app; tt_ty=Ty.Tbool};
annot=new_id (); }
in
TFatom { c = TApred (t1, false); annot=new_id () }
in
r
with
| Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) f.pp_loc
| Invalid_argument _ ->
Errors.typing_error (WrongNumberofArgs p) f.pp_loc
with Ty.TypeClash _ -> Errors.typing_error (NotAPredicate p) f.pp_loc
end
| PPdistinct (args) ->
Options.tool_req 1 "TR-Typing-Distinct$_F$";
let r =
begin
let te_args = List.map (type_term env) args in
let lt_args = List.map (
fun { c = { tt_ty = t; _ }; _ } -> t
) te_args in
try
let t = match lt_args with
| t::_ -> t
| [] ->
Errors.typing_error (WrongNumberofArgs "distinct") f.pp_loc
in
List.iter (Ty.unify t) lt_args;
TFatom { c = TAdistinct te_args; annot=new_id () }
with
| Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) f.pp_loc
end
in r
| PPinfix
({ pp_desc = PPinfix (_, (PPlt|PPle|PPgt|PPge|PPeq|PPneq), a); _ } as p,
(PPlt | PPle | PPgt | PPge | PPeq | PPneq as r), b) ->
Options.tool_req 1 "TR-Typing-OpComp$_F$";
let r =
let q = { pp_desc = PPinfix (a, r, b); pp_loc = f.pp_loc } in
let f1 = type_form env p in
let f2 = type_form env q in
TFop(OPand, [f1;f2])
in r
| PPinfix(t1, (PPeq | PPneq as op), t2) ->
Options.tool_req 1 "TR-Typing-OpBin$_F$";
let r =
let tt1 = type_term env t1 in
let tt2 = type_term env t2 in
try
Ty.unify tt1.c.tt_ty tt2.c.tt_ty;
match op with
| PPeq -> TFatom (mk_ta_eq tt1 tt2)
| PPneq -> TFatom (mk_ta_neq tt1 tt2)
| _ -> assert false
with Ty.TypeClash(t1,t2) ->
Errors.typing_error (Unification(t1,t2)) f.pp_loc
in r
| PPinfix(t1, (PPlt | PPgt | PPge | PPle as op), t2) ->
Options.tool_req 1 "TR-Typing-OpComp$_F$";
let r =
let tt1 = type_term env t1 in
let tt2 = type_term env t2 in
try
Ty.unify tt1.c.tt_ty tt2.c.tt_ty;
let ty = Ty.shorten tt1.c.tt_ty in
match ty with
| Ty.Tint | Ty.Treal ->
let top =
match op with