-
Notifications
You must be signed in to change notification settings - Fork 85
/
Copy pathdafny_to_cakemlScript.sml
1758 lines (1645 loc) · 61 KB
/
dafny_to_cakemlScript.sml
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
(*
Definitions to convert Dafny's AST into CakeML's AST.
*)
open preamble
open cfTacticsLib (* process_topdecs *)
open result_monadTheory
open astTheory semanticPrimitivesTheory (* CakeML AST *)
open dafny_astTheory
open alistTheory
open mlintTheory
val _ = new_theory "dafny_to_cakeml";
(* TODO Instead of breaking strings with ++, use SML style \ in all files *)
(* Defines the Dafny module containing the Dafny runtime *)
Quote dafny_module = process_topdecs:
structure Dafny =
struct
exception Return;
exception Break;
exception LabeledBreak string;
(* Adapted from ABS, ediv, and emod from HOL's integerTheory *)
fun abs n = if n < 0 then ~n else n;
fun ediv i j = if 0 < j then i div j else ~(i div ~j);
fun emod i j = i mod (abs j);
(* Array functions *)
fun array_to_list arr = Array.foldr (fn x => fn xs => x :: xs) [] arr;
(* to_string functions to be used for "Dafny-conform" printing *)
fun bool_to_string b = if b then "true" else "false";
fun int_to_string i = Int.int_to_string #"-" i;
fun escape_char c =
if c = #"'" then "\\'"
else if c = #"\"" then "\\\""
else if c = #"\\" then "\\\\"
else if c = #"\000" then "\\0"
else if c = #"\n" then "\\n"
(* #"\r" leads to "not terminated with nil" exception *)
else if c = #"\013" then "\\r"
else if c = #"\t" then "\\t"
else String.str c;
fun char_to_string c = String.concat ["'", escape_char c, "'"];
fun list_to_string f lst =
String.concat ["[", String.concatWith ", " (List.map f lst), "]"];
fun char_list_to_string cs = list_to_string char_to_string cs;
(* f converts the tuple into a list of strings *)
fun tuple_to_string f tpl =
String.concat ["(", String.concatWith ", " (f tpl), ")"];
(* Commented out because we use CakeML char list's instead of strings *)
(* In Dafny, strings within collections are printed as a list of chars *)
(* fun string_element_to_string s = *)
(* list_to_string char_to_string (String.explode s); *)
end
End
(* TODO Check if we can reduce the number of generated cases *)
(* TODO Check if it makes sense to extract Var (...) into a function
* in particular look at from_name *)
Overload True = “Con (SOME (Short "True")) []”
Overload False = “Con (SOME (Short "False")) []”
Overload None = “Con (SOME (Short "None")) []”
Overload Some = “λx. Con (SOME (Short "Some")) [x]”
Overload Unit = “Con NONE []”
Definition int_to_string_def:
int_to_string (i: int) = explode (toString i)
End
Definition num_to_string_def:
num_to_string (n: num) = int_to_string (&n)
End
Definition string_to_int_def:
string_to_int s =
case (fromString (implode s)) of
| SOME i =>
return i
| NONE =>
fail ("Could not convert into int: " ++ s)
End
Definition string_to_num_def:
string_to_num s =
do
i <- string_to_int s;
if i < 0
then fail "string_to_num: Number was negative"
else return (Num i)
od
End
Definition strip_prefix_def:
strip_prefix prefix str =
if isPREFIX prefix str
then SOME (DROP (LENGTH prefix) str)
else NONE
End
(* TODO move this to dafny_ast? *)
Definition is_DeclareVar_def:
is_DeclareVar s =
case s of
| DeclareVar _ _ _ => T
| _ => F
End
(* TODO? Merge is_<Con> and dest_<Con> into one def returning an option *)
Definition is_Eq_def:
is_Eq bop =
case bop of
| Eq _ => T
| _ => F
End
Definition is_Seq_def:
is_Seq typ =
case typ of
| Seq _ => T
| _ => F
End
Definition is_moditem_module_def:
is_moditem_module (ModuleItem_Module _) = T ∧
is_moditem_module _ = F
End
Definition is_moditem_class_def:
is_moditem_class (ModuleItem_Class _) = T ∧
is_moditem_class _ = F
End
Definition is_moditem_trait_def:
is_moditem_trait (ModuleItem_Trait _) = T ∧
is_moditem_trait _ = F
End
Definition is_moditem_newtype_def:
is_moditem_newtype (ModuleItem_Newtype _) = T ∧
is_moditem_newtype _ = F
End
Definition is_moditem_synonymtype_def:
is_moditem_synonymtype (ModuleItem_SynonymType _) = T ∧
is_moditem_synonymtype _ = F
End
Definition is_moditem_datatype_def:
is_moditem_datatype (ModuleItem_Datatype _) = T ∧
is_moditem_datatype _ = F
End
Definition dest_Array_def:
dest_Array (Array t dims) = return (t, dims) ∧
dest_Array _ = fail "dest_Array: Not an Array"
End
Definition dest_Arrow_def:
dest_Arrow (Arrow argTs retT) = return (argTs, retT) ∧
dest_Arrow _ = fail "dest_Arrow: Not an arrow"
End
Definition dest_singleton_list_def:
dest_singleton_list [x] = return x ∧
dest_singleton_list _ = fail "dest_singleton_list: Not a singleton list"
End
Definition dest_Seq_def:
dest_Seq (Seq t) = return t ∧
dest_Seq _ = fail "dest_Seq: Not a Seq"
End
Definition dest_Module_def:
dest_Module (Module nam attrs requiresExtern body) =
(nam, attrs, requiresExtern, body)
End
(* TODO? Move to dafny_ast *)
Definition dest_Name_def:
dest_Name (Name s) = s
End
(* TODO move this to dafny_ast? *)
Definition dest_DeclareVar_def:
dest_DeclareVar s =
case s of
| DeclareVar n t e_opt => return (n, t, e_opt)
| _ => fail "dest_DeclareVar: Not a DeclareVar"
End
(* TODO move this to result_monad? *)
Definition dest_SOME_def:
dest_SOME (SOME x) = return x ∧
dest_SOME NONE = fail "dest_SOME: Not a SOME"
End
Definition cml_ref_ass_def:
cml_ref_ass lhs rhs = (App Opassign [lhs; rhs])
End
Definition cml_ref_def:
cml_ref n val_e in_e = (Let (SOME n) ((App Opref [val_e])) in_e)
End
(* Converts a HOL list of CakeML expressions into a CakeML list. *)
Definition cml_list_def:
(cml_list [] = Con (SOME (Short "[]")) []) ∧
(cml_list (x::rest) =
Con (SOME (Short "::")) [x; cml_list rest])
End
(* Converts a HOL string into a CakeML char list *)
Definition string_to_cml_char_list_def:
string_to_cml_char_list s = cml_list (MAP (λx. (Lit (Char x))) s)
End
Definition cml_fapp_aux_def:
cml_fapp_aux fun_name [] = App Opapp [fun_name; Unit] ∧
cml_fapp_aux fun_name (e::rest) =
if rest = [] then (App Opapp [fun_name; e])
else
let inner_app = cml_fapp_aux fun_name rest in
(App Opapp [inner_app; e])
End
Definition cml_fapp_def:
cml_fapp fun_name args = cml_fapp_aux fun_name (REVERSE args)
End
Definition cml_id_def:
cml_id [] = fail "cml_id: Cannot make id from empty list" ∧
cml_id [n] = return (Short n) ∧
cml_id (n::rest) = do rest' <- cml_id rest; return (Long n rest') od
End
Definition cml_tuple_def:
cml_tuple [_] = fail "cml_tuple: Cannot create 1-tuple" ∧
cml_tuple cml_es = return (Con NONE cml_es)
End
Definition cml_tuple_select_def:
cml_tuple_select len cml_te (idx: num) =
let field_vars = GENLIST (λn. Pvar ("t" ++ (num_to_string n))) len in
Mat cml_te [Pcon NONE field_vars, Var (Short ("t" ++ (num_to_string idx)))]
End
Definition zip_with_def:
zip_with f [] _ = [] ∧
zip_with f _ [] = [] ∧
zip_with f (x::xs) (y::ys) = (f x y)::(zip_with f xs ys)
End
(* TODO Find better way to generate internal names *)
Definition varN_from_formal_def:
varN_from_formal (Formal n _ attrs) =
if attrs ≠ [] then
fail "param_from_formals: Attributes currently unsupported"
else
return (dest_varName n)
End
Definition internal_varN_from_formal_def:
internal_varN_from_formal fo =
do
n <- varN_from_formal fo;
return (n ++ "_CML_param")
od
End
Definition internal_varN_from_varName_def:
internal_varN_from_varName (VarName n) = (n ++ "_CML_out_ref")
End
(* Declares additional parameters using Fun x => ... *)
Definition fun_from_params_def:
(fun_from_params preamble [] = return preamble) ∧
(fun_from_params preamble (fo::rest) =
do
param_name <- internal_varN_from_formal fo;
preamble <<- (λx. preamble (Fun param_name x));
fun_from_params preamble rest
od)
End
Definition local_env_name_def:
local_env_name n = (Companion [] [], n)
End
(* In Dafny, the string and seq<char> type are synonyms. We replace the
* former with the latter for consistency. We also replace newtypes by their
* base type (which may be incorrect) *)
(* TODO? Maybe it is better to implement this with a map as Dafny does it *)
Definition normalize_type_def:
normalize_type t =
(case t of
| Primitive String => Seq (Primitive Char)
(* TODO Unsure whether we can ignore these like that *)
| UserDefined
(ResolvedType _ [] (ResolvedTypeBase_Newtype bt _ _)
attrs [] extendedTs) =>
(* Returning t in this case means it is unsupported *)
if attrs ≠ [Attribute "axiom" []] ∧ attrs ≠ [] then
t
else if extendedTs ≠ [Primitive Int] ∧ extendedTs ≠ [] then
t
else
normalize_type bt
| Tuple ts => Tuple (map_normalize_type ts)
| Array elem dims => Array (normalize_type elem) dims
| Seq t => Seq (normalize_type t)
| Set t => Set (normalize_type t)
| Multiset t => Multiset (normalize_type t)
| Map kt vt => Map (normalize_type kt) (normalize_type vt)
| SetBuilder t => SetBuilder (normalize_type t)
| MapBuilder kt vt => MapBuilder (normalize_type kt)
(normalize_type vt)
| Arrow argsT resT => Arrow (map_normalize_type argsT)
(normalize_type resT)
| _ => t) ∧
map_normalize_type ts =
(case ts of
| [] => []
| (t::rest) =>
(normalize_type t)::(map_normalize_type rest))
Termination
cheat
End
Definition compose_all_def:
compose_all = FOLDR (o) I
End
Definition cml_tuple_to_string_list_def:
cml_tuple_to_string_list fs =
let field_name = GENLIST (λn. ("t" ++ (num_to_string n))) (LENGTH fs);
field_pvars = (MAP (λx. Pvar x) field_name);
field_vars = (MAP (λx. [Var (Short x)]) field_name);
string_list = cml_list (zip_with cml_fapp fs field_vars);
tuple_name = "tpl" in
Fun tuple_name
(Mat (Var (Short tuple_name))
[Pcon NONE field_pvars, string_list])
End
Definition raise_return_def:
raise_return = Raise (Con (SOME (Long "Dafny" (Short "Return"))) [])
End
Definition raise_break_def:
raise_break = Raise (Con (SOME (Long "Dafny" (Short "Break"))) [])
End
Definition add_break_handle_def:
add_break_handle e = Handle e [Pcon (SOME (Long "Dafny" (Short "Break"))) [],
Unit]
End
Definition raise_labeled_break_def:
raise_labeled_break lbl = Raise (Con (SOME (Long "Dafny"
(Short "LabeledBreak")))
[Lit (StrLit lbl)])
End
Definition add_labeled_break_handle_def:
add_labeled_break_handle cur_lbl e =
Handle e [Pcon (SOME (Long "Dafny" (Short "LabeledBreak"))) [Pvar "lbl"],
(If (App Equality [Var (Short "lbl"); Lit (StrLit cur_lbl)])
(Unit)
(Raise (Con (SOME (Long "Dafny" (Short "LabeledBreak")))
[Var (Short "lbl")])))]
End
Definition cml_while_name_def:
cml_while_name (lvl: int) = "CML_while_" ++ (int_to_string lvl)
End
Definition cml_get_arr_def:
cml_get_arr cml_e = (cml_fapp (Var (Long "Option" (Short "valOf"))) [cml_e])
End
Definition fun_from_lambda_params_def:
fun_from_lambda_params [] = return [] ∧
fun_from_lambda_params ((Formal nam _ attrs)::rest) =
if attrs ≠ [] then
fail "fun_from_lambda_params: attributes unsupported"
else
do
rest <- fun_from_lambda_params rest;
return ((λe. Fun (dest_varName nam) e)::rest)
od
End
Definition param_type_env_def:
param_type_env [] acc = return acc ∧
param_type_env ((Formal n t attrs)::rest) acc =
if attrs ≠ [] then
fail "param_type_env: Attributes unsupported"
else
let n = dest_varName n in
param_type_env rest ((local_env_name n, (normalize_type t))::acc)
End
Definition env_and_epilogue_from_outs_def:
(env_and_epilogue_from_outs env epilogue [] [] =
do
epilogue <<- epilogue raise_return;
return (env, epilogue)
od) ∧
(env_and_epilogue_from_outs env epilogue (t::rest_t) (v::rest_v) =
do
if LENGTH rest_t ≠ LENGTH rest_v then
fail ("env_and_epilogue_from_outs: Length of outTypes and outVars " ++
"is different")
else
do
v_str <<- dest_varName v;
env <<- ((local_env_name v_str, t)::env);
out_ref_name <<- internal_varN_from_varName v;
epilogue <<- (λx. epilogue (Let NONE (cml_ref_ass
(Var (Short out_ref_name))
((App Opderef
[Var (Short v_str)])))
x));
env_and_epilogue_from_outs env epilogue rest_t rest_v
od
od)
End
(* Allows us to pass in out references as parameters *)
Definition fun_from_outs_def:
fun_from_outs preamble [] = preamble ∧
fun_from_outs preamble (v::rest_v) =
fun_from_outs (λx. preamble (Fun (internal_varN_from_varName v) x)) rest_v
End
(* Declares and initializes references for the parameters that the body will use
*
* We do this since within the body the parameter names act as variables, which
* we encode using references. *)
Definition ref_from_params_def:
(ref_from_params preamble [] = return preamble) ∧
(ref_from_params preamble (fo::rest) =
do
ref_name <- varN_from_formal fo;
ref_value_name <- internal_varN_from_formal fo;
preamble <<- (λx. preamble ((cml_ref ref_name
(Var (Short ref_value_name)) x)));
ref_from_params preamble rest
od)
End
Definition gen_param_preamble_epilogue_def:
gen_param_preamble_epilogue env [] [] [] =
do
param <<- "";
preamble <<- λx. x;
(env, epilogue) <- env_and_epilogue_from_outs env (λx. x)
[] [];
return (env, param, preamble, epilogue)
od ∧
gen_param_preamble_epilogue env [] (t::rest_t) (v::rest_v) =
do
param <<- internal_varN_from_varName v;
preamble <<- fun_from_outs (λx. x) rest_v;
(env, epilogue) <- env_and_epilogue_from_outs env (λx. x)
(t::rest_t) (v::rest_v);
return (env, param, preamble, epilogue)
od ∧
gen_param_preamble_epilogue env (p::rest_p) outTypes outVars =
do
param <- internal_varN_from_formal p;
preamble <- fun_from_params (λx. x) rest_p;
preamble <<- fun_from_outs preamble outVars;
preamble <- ref_from_params preamble (p::rest_p);
env <- param_type_env (p::rest_p) env;
(env, epilogue) <- env_and_epilogue_from_outs env (λx. x)
outTypes outVars;
return (env, param, preamble, epilogue)
od
End
Definition gen_param_preamble_def:
gen_param_preamble env params =
do
(env, param, preamble, _) <- gen_param_preamble_epilogue env params [] [];
return (env, param, preamble)
od
End
(* Returns a function that can be applied to a CakeML expression to turn it into
* a string *)
Definition to_string_fun_def:
(to_string_fun _ (Primitive Char) =
return (Var (Long "Dafny" (Short "char_to_string")))) ∧
(to_string_fun _ (Primitive Int) =
return (Var (Long "Dafny" (Short "int_to_string")))) ∧
(to_string_fun _ (Primitive Bool) =
return (Var (Long "Dafny" (Short "bool_to_string")))) ∧
(to_string_fun inCol (Seq (Primitive Char)) =
if inCol then
return (Var (Long "Dafny" (Short "char_list_to_string")))
else return (Var (Long "String" (Short "implode")))) ∧
(to_string_fun _ (Seq t) =
do
elem_to_string <- to_string_fun T t;
return (cml_fapp (Var (Long "Dafny" (Short "list_to_string")))
[elem_to_string])
od) ∧
(to_string_fun _ (Tuple ts) =
do
elems_to_string <- map_to_string_fun T ts;
string_list <<- cml_tuple_to_string_list elems_to_string;
return (cml_fapp (Var (Long "Dafny" (Short "tuple_to_string")))
[string_list])
od) ∧
(to_string_fun _ t = fail ("to_string_fun: Unsupported type")) ∧
(map_to_string_fun inCol ts =
case ts of
| [] => return []
| (t::rest) =>
do
t <- to_string_fun inCol t;
rest <- map_to_string_fun inCol rest;
return (t::rest)
od)
Termination
cheat
End
(* An independent statement must not depend on statements outside of it *)
(* TODO Fill out cases properly; wildcard means that I did not explicitly
consider it yet *)
Definition is_indep_stmt_def:
is_indep_stmt (s: dafny_ast$statement) =
case s of
| DeclareVar _ _ _=> F
| Assign _ _ => T
| If _ _ _ => T
| Labeled _ _ => T
| While _ _ => T
| Call _ _ _ _ _ => T
| Return _ => T
| EarlyReturn => T
| Break _ => T
| Print _ => T
| _ => F
End
Definition dest_resolvedType_def:
dest_resolvedType (ResolvedType path typeArgs kind attrs properMethods
extendedTypes) =
(path, typeArgs, kind, attrs, properMethods, extendedTypes)
End
Definition arb_value_def:
arb_value (t: dafny_ast$type) =
(case t of
| Primitive Int => return (Lit (IntLit 0))
| Primitive Bool => return False
| Seq _ => return (cml_list [])
| Tuple ts =>
do
arb_elems <- map_arb_value ts;
cml_tuple arb_elems
od
| Array _ _ => return None (* we essentially initialize with null *)
| UserDefined resT => arb_value_resolved_type resT
| Arrow ts t =>
do
arb_ret <- arb_value t;
nr_params <<- (LENGTH ts);
params <<- if nr_params = 0
then [λx. Fun "u" x]
else GENLIST (λx. Fun ("p" ++ (num_to_string x)))
(LENGTH ts);
return (compose_all params arb_ret)
od
| _ => fail "arb_value_def: Unsupported type") ∧
(arb_value_resolved_type resT =
let (_, typeArgs, kind, attrs,
properMethods, extendedTypes) = dest_resolvedType resT in
if typeArgs ≠ [] then
fail "arb_value_resolved_type: type args unsupported"
else if attrs ≠ [] ∧ attrs ≠ [Attribute "axiom" []] then
fail "arb_value_resolved_type: unsupported attributes"
else if properMethods ≠ [] then
fail "arb_value_resolved_type: proper methods unsupported"
else if extendedTypes ≠ [] ∧ extendedTypes ≠ [Primitive Int] then
fail "arb_value_resolved_type: unsupported extended types"
else
case kind of
| ResolvedTypeBase_Newtype baseT rang eras =>
if baseT ≠ Primitive Int then
fail "arb_value_resolved_type: Unsupported base type"
else if rang ≠ NoRange then
fail "arb_value_resolved_type: Unsupported range"
else if ¬eras then
fail "arb_value_resolved_type: non-erased newtypes unsupported"
else
arb_value baseT
| _ => fail "arb_value_resolved_type: unsupported resolved type base") ∧
map_arb_value ts =
(case ts of
| [] => return []
| t::rest =>
do
v <- arb_value t;
vs <- map_arb_value rest;
return (v::vs)
od)
Termination
cheat
End
(* TODO double check whether this is used *)
Definition from_varName_def:
from_varName n = Var (Short (dest_varName n))
End
(* TODO double check if this is still used *)
Definition from_name_def:
from_name n = Var (Short (dest_Name n))
End
(* TODO double check if this is still used *)
Definition from_ident_def:
from_ident (Ident n) = from_name n
End
(* It appears that function and methods in Dafny are both represented by the
* same datatype, even though they are slightly different *)
(* This assumption is incorrect; at the IR level only methods exist since
* expression can be compiled down to statements in it *)
(* TODO Handle everything as "method"*)
Definition method_is_function_def:
method_is_function (Method attrs isStatic hasBody _ _ overridingPath nam
typeParams params body outTypes outVars) =
(* Function has one outType and no outVars *)
if LENGTH outTypes ≠ 1 ∨ outVars ≠ NONE then
return F
else if attrs ≠ [] ∧ attrs ≠ [Attribute "tailrecursion" ["false"]] then
fail ("method_is_function: unsupported attributes")
else if ¬isStatic then
fail ("method_is_function: Did not expect function " ++ (dest_Name nam) ++
" to be static")
else if ¬hasBody then
fail ("method_is_function: Function " ++ (dest_Name nam) ++
" did not have a body, even though it must")
else if overridingPath ≠ NONE then
fail ("method_is_function: Did not expect function " ++ (dest_Name nam) ++
" to have an overriding path")
else if typeParams ≠ [] then
fail ("method_is_function: Type parameters are currently unsupported (in "
++ (dest_Name nam) ++ ")")
else
return T
End
Definition method_is_method_def:
method_is_method (Method attrs isStatic hasBody _ _ overridingPath nam
typeParams params body outTypes outVars) =
case outVars of
| SOME outVars_list =>
if LENGTH outTypes ≠ LENGTH outVars_list then
fail ("method_is_method: Function " ++ (dest_Name nam) ++
" did not have the same number of outTypes and outVars, even" ++
" though it must")
else if attrs ≠ [] ∧ attrs ≠ [Attribute "tailrecursion" ["false"]] then
fail ("method_is_method: unsupported attributes")
else if ¬isStatic then
fail ("method_is_method: non-static methods are currently " ++
"unsupported (in " ++ (dest_Name nam) ++ ")")
else if ¬hasBody then
fail ("method_is_method: Method " ++ (dest_Name nam) ++
" did not have a body, even though it must")
else if overridingPath ≠ NONE then
fail ("method_is_method: Overriding path for methods are currently " ++
"unsupported (in " ++ (dest_Name nam) ++ ")")
else if typeParams ≠ [] then
fail ("method_is_method: Type parameters are currently unsupported (in "
++ (dest_Name nam) ++ ")")
else
return T
| NONE => return F
End
Definition from_literal_def:
from_literal (l: dafny_ast$literal) =
case l of
| BoolLiteral T =>
return True
| BoolLiteral F =>
return False
| IntLiteral s (Primitive Int) =>
do
i <- string_to_int s;
return (Lit (IntLit i))
od
| IntLiteral _ _ =>
fail "IntLiteral _ _: Unclear how to handle"
(* TODO Look into Rat module in basis *)
| DecLiteral s1 s2 typ =>
fail "DecLiteral s1 s2 typ: TODO"
(* TODO String/Char support incomplete or incorrect - see
to_dafny_astScript for more details *)
| StringLiteral s verbatim =>
return (string_to_cml_char_list (unescape_string s verbatim))
| CharLiteral ch =>
return (Lit (Char ch))
| CharLiteralUTF16 n =>
fail "CharLiteralUTF16 n: Unsupported"
(* Encode a nullable type as ((a' ref) option) *)
| Null typ =>
return None
End
Definition type_from_formals_def:
type_from_formals [] = return [] ∧
type_from_formals ((Formal _ t attrs)::rest) =
if attrs ≠ [] then
fail "type_from_formals: Attributes unsupported"
else
do
restTs <- type_from_formals rest;
return ((normalize_type t)::(map_normalize_type restTs))
od
End
(* Cannot merge with from_classitem, since methods may be mutually recursive/
* not defined in the proper order, resulting in being referenced before they
* have been added to the context *)
Definition call_type_env_from_class_body_def:
call_type_env_from_class_body acc _ [] = return acc ∧
call_type_env_from_class_body acc comp ((ClassItem_Method m)::rest) =
do
is_function <- method_is_function m;
is_method <- method_is_method m;
if is_function then
do
(_, _, _, _, _, _, nam, _, params, _, outTypes, _) <<- dest_Method m;
nam <<- dest_Name nam;
param_t <- type_from_formals params;
outTypes <<- MAP normalize_type outTypes;
acc <<- (((comp, nam), Arrow param_t (HD outTypes))::acc);
call_type_env_from_class_body acc comp rest;
od
else if is_method then
do
(_, _, _, _, _, _, nam, _, params, _, _, _) <<- dest_Method m;
nam <<- dest_Name nam;
(* outTypes refers to the type of outVars; the method itself returns
* Unit (I think) *)
param_t <- type_from_formals params;
acc <<- (((comp, nam), Arrow param_t (Tuple []))::acc);
call_type_env_from_class_body acc comp rest
od
else
fail "call_type_env_from_class_body: ClassItem_Method m was neither \
\method nor function."
od
End
Definition tuple_len_def:
tuple_len (Tuple ts) = return (LENGTH ts) ∧
tuple_len _ = fail "tuple_len: Not a tuple type"
End
Definition dafny_type_of_def:
dafny_type_of (env: (((expression # string), type) alist))
(e: dafny_ast$expression) =
(case e of
| Literal (BoolLiteral _) => return (Primitive Bool)
| Literal (IntLiteral _ (Primitive Int)) => return (Primitive Int)
| Literal (StringLiteral _ _) => return (Seq (Primitive Char))
| Literal (CharLiteral _) => return (Primitive Char)
| Expression_Ident n =>
let n = dest_varName n in
(case ALOOKUP env (local_env_name n) of
| SOME t => return t
| NONE => fail ("dafny_type_of: Unknown name " ++ n))
| Expression_Tuple es =>
do
ts <- map_dafny_type_of env es;
return (Tuple ts)
od
| NewUninitArray [_] typ => return (Array (normalize_type typ) 1)
| FinalizeNewArray e t =>
do
e_t <- dafny_type_of env e;
(* Sanity check *)
if e_t ≠ (normalize_type t) then
fail "dafny_type_of (FinalizeNewArray): Unexpected type mismatch"
else
return (e_t)
od
| Convert _ fro tot =>
(* Assume that we are given a "correct" Dafny program, and this convert
* is safe *)
return (normalize_type tot)
| SeqConstruct _ fe =>
do
fe_t <- dafny_type_of env fe;
(argTs, retT) <- dest_Arrow fe_t;
argT <- dest_singleton_list argTs;
if argT ≠ Primitive Int then
fail "dafny_type_of (SeqConstruct): Argument of function was not \
\an int"
else
return (Seq retT)
od
| SeqValue _ t =>
return (Seq (normalize_type t))
| SeqUpdate se idx v =>
do
se_t <- dafny_type_of env se;
idx_t <- dafny_type_of env idx;
v_t <- dafny_type_of env v;
if se_t ≠ Seq v_t then
fail "dafny_type_of (SeqUpdate): Unexpectedly, se_t <> Seq v_t"
else if idx_t ≠ Primitive Int then
fail "dafny_type_of (SeqUpdate): Unexpectedly, idx_t wasn't an int"
else
return se_t
od
| Ite cnd thn els =>
do
thn_t <- dafny_type_of env thn;
els_t <- dafny_type_of env els;
if (thn_t ≠ els_t) then
fail ("dafny_type_of (Ite): Unexpectedly, branches have " ++
"different types")
else
return thn_t
od
| UnOp Not e =>
do
e_t <- dafny_type_of env e;
if e_t = (Primitive Bool) then
return (Primitive Bool)
else
fail "dafny_type_of (UnOp Not): Unsupported type for e"
od
| UnOp BitwiseNot _ =>
fail "dafny_type_of (UnOp BitwiseNot): Unsupported"
| UnOp Cardinality e' =>
do
e'_t <- dafny_type_of env e';
if is_Seq e'_t then
return (Primitive Int)
else
fail "dafny_type_of (UnOp Cardinality): Unsupported type"
od
| BinOp bop e1 e2 =>
(* TODO? Figure out why using | BinOp Lt _ _ and
* | BinOp (Eq _ _) _ _ results in the | BinOp bop e1 e2 case to be
* repeated over and over again *)
(* TODO Add sanity checks as done below *)
if (bop = Lt ∨ (is_Eq bop)) then return (Primitive Bool)
else
do
e1_t <- dafny_type_of env e1;
e2_t <- dafny_type_of env e2;
if (bop = EuclidianDiv ∧ e1_t = (Primitive Int) ∧ e1_t = e2_t) then
return (Primitive Int)
else if (bop = EuclidianMod ∧ e1_t = (Primitive Int) ∧
e1_t = e2_t) then
return (Primitive Int)
else if (bop = Plus ∧ e1_t = (Primitive Int) ∧ e1_t = e2_t) then
return (Primitive Int)
else if (bop = Minus ∧ e1_t = (Primitive Int) ∧ e1_t = e2_t) then
return (Primitive Int)
else if (bop = Times ∧ e1_t = (Primitive Int) ∧ e1_t = e2_t) then
return (Primitive Int)
else if (bop = And ∧ e1_t = Primitive Bool ∧ e1_t = e2_t) then
return (Primitive Bool)
else if (bop = Or ∧ e1_t = Primitive Bool ∧ e1_t = e2_t) then
return (Primitive Bool)
else if (bop = Concat ∧ e1_t = (Seq (Primitive Char)) ∧
e1_t = e2_t) then
return (Seq (Primitive Char))
else if (bop = Concat ∧ is_Seq e1_t ∧ e1_t = e2_t) then
return e1_t
else if (bop = In ∧ is_Seq e2_t) then
do
seq_t <- dest_Seq e2_t;
if e1_t ≠ seq_t then
fail "dafny_type_of (BinOp In): Types did not match"
else
return (Primitive Bool)
od
else if (bop = SeqPrefix ∧ is_Seq e1_t ∧ is_Seq e2_t) then
do
seq1_t <- dest_Seq e1_t;
seq2_t <- dest_Seq e2_t;
if seq1_t ≠ seq2_t then
fail "dafny_type_of (BinOp SeqPrefix): Types did not match"
else
return (Primitive Bool)
od
else if (bop = SeqProperPrefix ∧ is_Seq e1_t ∧ is_Seq e2_t) then
do
seq1_t <- dest_Seq e1_t;
seq2_t <- dest_Seq e2_t;
if seq1_t ≠ seq2_t then
fail ("dafny_type_of (BinOp SeqProperPrefix): Types did " ++
"not match")
else
return (Primitive Bool)
od
else
fail "dafny_type_of (BinOp): Unsupported bop/types"
od
| ArrayLen _ _ _ _ => return (Primitive Int)
| SelectFn comp nam onDt isStatic isConstant _ =>
(* TODO Figure out whether it is fine to ignore arguments *)
if onDt then
fail "dafny_type_of (SelectFn): On datatype unsupported"
else if ¬isStatic then
fail "dafny_type_of (SelectFn): non-static unsupported"
else if ¬isConstant then
fail "dafny_type_of (SelectFn): non-constant unsupported"
else
(let nam = dest_varName nam in
(case ALOOKUP env (comp, nam) of
| SOME t => return t
| NONE => fail ("dafny_type_of (SelectFn): Unknown name" ++ nam)))
| Index e' cok idxs =>
do
e'_t <- dafny_type_of env e';
if cok = CollKind_Seq then
if LENGTH idxs ≠ 1 then
fail "dafny_type_of (Index/Seq): Unexpectedly, did not receive \
\exactly one index"
else
dest_Seq e'_t
else if cok = CollKind_Array then
if idxs = [] then
fail "dafny_type_of (Index/Array): Unexpectedly idxs was empty"
else if LENGTH idxs > 1 then
fail "dafny_type_of (Index/Array): multi-dimensional indexing \
\unsupported"
else
do
(t, _) <- dest_Array e'_t;
return t
od
else
fail "dafny_type_of (Index): Unsupported kind of collection"
od
| IndexRange se isArray lo hi =>
if isArray then
do
arr_t <- dafny_type_of env se;
(elem_t, _) <- dest_Array arr_t;
return (Seq elem_t)
od
else
dafny_type_of env se
| TupleSelect _ _ fieldT =>
return (normalize_type fieldT)
| Expression_Call on (CallName nam onType receiverArg receiverAsArgument _)
_ _ =>
if onType ≠ NONE then
fail "dafny_type_of: (Call) Unsupported onType"
else if receiverArg ≠ NONE then
fail "dafny_type_of: (Call) Receiver argument unsupported"
else if receiverAsArgument then
fail "dafny_type_of: (Call) Receiver as argument unsupported"
else
(let ct_name = dest_Name nam in
case ALOOKUP env (on, ct_name) of
| SOME (Arrow _ retT) => return retT
| NONE => fail ("dafny_type_of: " ++ ct_name ++
" not found or bad type"))
| Lambda params retT _ =>
do
argT <- type_from_formals params;
retT <<- normalize_type retT;
return (Arrow argT retT)
od