-
Notifications
You must be signed in to change notification settings - Fork 99
Expand file tree
/
Copy pathpanStaticScript.sml
More file actions
1908 lines (1823 loc) · 65.6 KB
/
Copy pathpanStaticScript.sml
File metadata and controls
1908 lines (1823 loc) · 65.6 KB
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
(*
Static checking for Pancake.
General checks:
- Errors:
- Main function parameters
- Exported main function
- Exported function with >4 arguments
- Missing function exit (return, tail call, etc)
- Loop exit outside loop (break, continue)
- Incorrect number of function arguments
- Incorrect number of Op arguments (impossible from parser)
- Warnings:
- Unreachable statements (after function exit, after loop exit)
- Note: To minimise output, subsequent warnings of this kind after the
first guaranteed-unreachable line within a block are silenced. If an
inner block occurs *before* this line, warnings within that block do not
count towards this first. However, if an inner block occurs *after* this
line, the line is recognised as the first for the inner block as well
- Base-calculated address in shared memory operation
- Non-base -calculated address in local memory operation
Scope checks:
- Errors:
- Undefined/out-of-scope functions
- Undefined/out-of-scope variables
- Undefined/out-of-scope struct names
- Redefined functions
- Redefined function parameter names
- Redefined struct names
- Redefined struct field names
- Warnings:
- Redefined variables
Shape checks:
- Errors:
- Mismatched variable declarations
- Mismatched variable assignments
- Mismatched function arguments
- Mismatched function returns
- Mismatched struct fields
- Incorrect number of struct field values
- Mismatched source/destination for memory operations
- Non-word main function return declarations
- Non-word FFI arguments
- Non-word exported argument declarations
- Non-word exported return declarations
- Non-word addresses for memory operations
- Non-word/mismatched operator operands
- Non-word condition expressions
- Invalid field index
- Invalid field name
- Returned shape size >32 words (TODO: raised shape size)
Primitive checks:
- Errors:
- Mismatched destination shape
- Incorrect number of arguments
- Unsupported position
- Assignment to global value
- Mismatched operands
*)
Theory panStatic
Libs
preamble
Ancestors
errorLogMonad panLang mlmap mlint mllist
val _ = monadsyntax.enable_monadsyntax();
val _ = monadsyntax.enable_monad "errorLog";
(* Error categories for printing *)
Datatype:
staterr = ScopeErr mlstring
| WarningErr mlstring
| GenErr mlstring
| ShapeErr mlstring
End
(*
Return type of static_check_* functions:
(retval, proper error) error # warning list
*)
Type static_result = ``:('a, staterr) error # staterr list``
(*
Exp-level state for references to @base
"Priority": `Based` > `NotTrusted` > `Trusted` > `NotBased`
where an exp can never have a lower priority state to its components,
ie. an exp containing a `NotTrusted` exp can never be `Trusted` or `NotBased`
*)
Datatype:
based =
Based | NotBased (* warned as appropriate *)
| Trusted (* excempt from warning *)
| NotTrusted (* always warned *)
End
(* Exp-level state for shape-aware basedness *)
Datatype:
shaped_based =
WordB based
| StructB (shaped_based list)
| NamedB stcname ((fldname # shaped_based) list)
End
(*
Prog-level state for reachability
Possible transitions are linear: `IsReach` -> `WarnReach` -> `NotReach`
where `NotReach` only comes after a reachability warning
*)
Datatype:
reachable =
IsReach | NotReach (* as named *)
| WarnReach (* "unreachable but has a pending warning" *)
End
(* Prog-level state for exit-ness of final statement *)
Datatype:
last_stmt =
RetLast | RaiseLast | TailLast (* function exit *)
| BreakLast | ContLast (* loop exit *)
| CondExitLast (* ambiguous exit (after conditionals) *)
| InvisLast | OtherLast (* non-exit *)
End
(* Type for function state *)
Datatype:
func_info = <|
ret_shape : shape (* shape of return value *)
; params : (varname # shape) list (* parameter info *)
|>
End
(* Type for local var state *)
Datatype:
local_info = <|
vsh_bd : shaped_based (* shaped basedness of var *)
|>
End
(* Type for global var state *)
Datatype:
global_info = <|
vshape : shape (* shape of var *)
|>
End
(* Type for error scope *)
Datatype:
scope =
FunScope funname mlstring (* in a function, possibly more specific *)
| DeclScope varname (* in a global declaration *)
| StcScope stcname fldname (* in a struct name declaration *)
| TopLevel
End
(* Record for current (per-func) context *)
Datatype:
context = <|
locals : (varname , local_info ) map (* tracked var state *)
; globals : (varname , global_info) map (* declared globals *)
; funcs : (funname , func_info ) map (* all function info *)
; structs : (stcname # struct_info) list (* struct name context *)
; scope : scope (* current scope info *)
; in_loop : bool (* loop status *)
; is_reachable : reachable (* reachability *)
; last : last_stmt (* exit-ness of last statement *)
; loc : mlstring (* location string *)
|>
End
(* Record for static_check_exp returns *)
Datatype:
exp_return = <|
sh_bd : shaped_based (* shaped basedness of exp *)
|>
End
(* Record for static_check_exps returns *)
Datatype:
exps_return = <|
sh_bds : shaped_based list (* shaped basedness of exps *)
|>
End
(* Record for static_check_prog returns *)
Datatype:
prog_return = <|
exits_fun : bool (* func exit status for all paths *)
; exits_loop : bool (* loop exit status for all paths *)
; last : last_stmt (* new exit-ness of last statement *)
; var_delta : (varname, local_info) map (* change in var state *)
; curr_loc : mlstring (* latest location string *)
|>
End
(* Varieties of identifiers that can be out of scope *)
Datatype:
scoped_id = Var | Fun | Stc
End
(* Functions for `based` and `shaped_based` *)
(*
Builds a shaped based from a given shape and single based
USED ON PRE-CHECKED SHAPES ONLY (will never be NONE)
*)
Definition sh_bd_from_sh_def:
sh_bd_from_sh sctxt b One = SOME $ WordB b /\
sh_bd_from_sh sctxt b (Comb shs) =
(case OPT_MMAP (sh_bd_from_sh sctxt b) shs of
| SOME sbs => SOME $ StructB sbs
| NONE => NONE) /\
sh_bd_from_sh sctxt b (Named nm) =
(case dropWhile (\(n,i). ~(n = nm)) sctxt of
| (nm,info)::sctxt' =>
let (field_nms, field_shs) = UNZIP info.fields in
(case OPT_MMAP (sh_bd_from_sh sctxt' b) field_shs of
| SOME field_sbs => SOME $ NamedB nm $ ZIP (field_nms, field_sbs)
| NONE => NONE)
| _ => NONE)
Termination
wf_rel_tac `measure LENGTH LEX measure (shape_size o SND)`
>> rw []
>> imp_res_tac (Q.prove (`dropWhile P xs = ys ==> LENGTH ys <= LENGTH xs`,
metis_tac [LENGTH_dropWhile_LESS_EQ]))
>> fs []
End
(* Builds a shaped based with the shape of a given shaped based and single based *)
Definition sh_bd_from_bd_def:
sh_bd_from_bd b (WordB b') = WordB b /\
sh_bd_from_bd b (StructB sbs) = StructB $ MAP (sh_bd_from_bd b) sbs /\
sh_bd_from_bd b (NamedB nm flds) =
NamedB nm $ MAP (\(nm, sb). (nm, sh_bd_from_bd b sb)) flds
End
(* Determine if shaped based has a given shape *)
Definition sh_bd_has_shape_def:
(sh_bd_has_shape sh sb =
case (sh, sb) of
| (One, WordB b) => T
| (Comb shs, StructB sbs) => sh_bd_has_shape_list shs sbs
| (Named nm, NamedB nm' flds) => nm = nm'
| _ => F) /\
(sh_bd_has_shape_list [] [] = T) /\
(sh_bd_has_shape_list (sh::shs) [] = F) /\
(sh_bd_has_shape_list [] (sb::sbs) = F) /\
sh_bd_has_shape_list (sh::shs) (sb::sbs) =
(sh_bd_has_shape sh sb /\ sh_bd_has_shape_list shs sbs)
End
(* Determine if two shaped baseds have the same shape *)
Definition sh_bd_eq_shapes_def:
(sh_bd_eq_shapes sb sh =
case (sb, sh) of
| (WordB b, WordB b') => T
| (StructB sbs, StructB sbs') => sh_bd_eq_shapes_list sbs sbs'
| (NamedB nm flds, NamedB nm' flds') => nm = nm'
| _ => F) /\
(sh_bd_eq_shapes_list [] [] = T) /\
(sh_bd_eq_shapes_list (sb::sbs) [] = F) /\
(sh_bd_eq_shapes_list [] (sb::sbs) = F) /\
sh_bd_eq_shapes_list (sb::sbs) (sb'::sbs') =
(sh_bd_eq_shapes sb sb' /\ sh_bd_eq_shapes_list sbs sbs')
End
(* Lookup shaped basedness at a certain struct index *)
Definition index_sh_bd_def:
index_sh_bd i (WordB b) = NONE ∧
index_sh_bd i (StructB sbs) = LLOOKUP sbs i ∧
index_sh_bd i (NamedB nm flds) = NONE
End
(* Lookup shaped basedness at a certain struct field *)
Definition field_sh_bd_def:
field_sh_bd fld (WordB b) = NONE ∧
field_sh_bd fld (StructB sbs) = NONE ∧
field_sh_bd fld (NamedB nm flds) = ALOOKUP flds fld
End
(* Merge basedness according to priority *)
Definition based_merge_def:
based_merge x y =
case (x,y) of
| (Based, _) => Based
| (_, Based) => Based
| (NotTrusted, _) => NotTrusted
| (_, NotTrusted) => NotTrusted
| (Trusted, _) => Trusted
| (_, Trusted) => Trusted
| (NotBased, NotBased) => NotBased
End
(* Comparison for combining based-ness between If/While branches *)
Definition sh_bd_branch_def:
sh_bd_branch x y = if x = y then x else sh_bd_from_bd NotTrusted x
End
(*
Combine local var state deltas of If/While branches
where states are either combined between the two deltas (if in both) or with
prior context (if in just one)
Needs extension with extension of `local_info` type
*)
Definition branch_loc_inf_def:
branch_loc_inf vctxt x y =
let x' =
mapWithKey (\k v. v with <|
vsh_bd :=
(if ~(member k y) then
case lookup vctxt k of
| SOME v' => sh_bd_branch v.vsh_bd v'.vsh_bd
| NONE => sh_bd_from_bd NotTrusted v.vsh_bd
else v.vsh_bd)
|>) x in
let y' =
mlmap$mapWithKey (\k v. v with <|
vsh_bd :=
(if ~(member k x) then
case lookup vctxt k of
| SOME v' => sh_bd_branch v.vsh_bd v'.vsh_bd
| NONE => sh_bd_from_bd NotTrusted v.vsh_bd
else v.vsh_bd)
|>) y in
mlmap$unionWith (\vx vy. vx with <|
vsh_bd := sh_bd_branch vx.vsh_bd vy.vsh_bd
|>) x' y'
End
(* Combine local var state deltas of Seq progs *)
Definition seq_loc_inf_def:
seq_loc_inf x y = union y x
End
(* Get shape string from shaped based *)
Definition sh_bd_to_str_def:
sh_bd_to_str (WordB b) = strlit "1" ∧
sh_bd_to_str (StructB []) = strlit "{}" ∧ (* should never happen *)
sh_bd_to_str (StructB (x::xs)) = concat (
strlit "{" :: sh_bd_to_str x ::
MAP (λx. strlit "," ^ x) (MAP sh_bd_to_str xs) ++
[strlit "}"]) ∧
sh_bd_to_str (NamedB nm flds) = nm
End
(* Functions for `last_stmt` and `reachable` *)
(*
Get string name for statement exit-ness
USED FOR PRINTING ASSOCIATED WARNINGS ONLY
*)
Definition last_to_str_def:
last_to_str l =
case l of
| RetLast => «return»
| RaiseLast => «raise»
| TailLast => «tail call»
| BreakLast => «break»
| ContLast => «continue»
| CondExitLast => «exiting conditional»
| _ => «»
End
(*
Determine next reachability state for prog sequences based on current state
and exit-ness
Handles `IsReach` -> `WarnReach` transition
*)
Definition next_is_reachable_def:
next_is_reachable r x =
case r of
| IsReach => if ~(x = InvisLast \/ x = OtherLast) then WarnReach else IsReach
| _ => r
End
(* Determine whether reachability has decreased *)
Definition next_now_unreachable_def:
next_now_unreachable r r' = (r = IsReach /\ ~(r' = IsReach))
End
(*
Determine whether a prog that can trigger a reachability warning is reached,
and return last exit-ness and update context if so
Handles `WarnReach` -> `NotReach` transition
*)
Definition reached_warnable_def:
reached_warnable s ctxt =
case s of
| Seq prog1 prog2 => (NONE, ctxt)
| Tick => (NONE, ctxt)
| Annot str1 str2 => (NONE, ctxt)
| _ =>
if ctxt.is_reachable = WarnReach then
(SOME ctxt.last, ctxt with is_reachable := NotReach)
else (NONE, ctxt)
End
(*
Determine exit-ness of branches, based on whether a definite func or loop exit
occurred
*)
Definition branch_last_stmt_def:
branch_last_stmt double_ret double_loop_exit =
if (double_ret \/ double_loop_exit) then CondExitLast else OtherLast
End
(* Determine exit-ness of Seq'd progs by ignoring invisible exit-ness *)
Definition seq_last_stmt_def:
seq_last_stmt x y = if y = InvisLast then x else y
End
(* Error message helpers *)
(* Get description of current scope *)
Definition get_scope_desc_def:
get_scope_desc scope =
case scope of
| FunScope fname desc =>
concat [strlit "function "; fname; desc]
| DeclScope vname =>
concat [«initialisation of global variable »; vname]
| StcScope sname fld =>
concat [
strlit "declaration of field "; fld;
strlit " in named struct "; sname]
| TopLevel =>
«top-level declaration»
End
(*
Get message for out of scope identifiers
id_type :scoped id
*)
Definition get_scope_msg_def:
get_scope_msg id_type loc id scope =
let id_desc =
case id_type of
| Var => strlit "variable "
| Fun => strlit "function "
| Stc => strlit "struct name " in
concat [loc; id_desc; id;
« is not in scope in »;
get_scope_desc scope; «\n»]
End
(*
List of recognised Primitive identifiers for usage hints
*)
Definition primitive_idents_def:
primitive_idents = [«__add_with_carry__»]
End
(*
Adds hint when a primitive is used in an unsupported position
*)
Definition add_primitive_hint_def:
add_primitive_hint fname msg =
if MEM fname primitive_idents
then concat [msg;
« note: »; fname;
« is a built-in primitive only available in »;
«declaration or assignment RHS positions\n»]
else msg
End
(*
Get message for redefined identifiers
id_type :scoped id
*)
Definition get_redec_msg_def:
get_redec_msg id_type loc id scope =
let id_desc =
case id_type of
| Var => strlit "variable "
| Fun => strlit "function "
| Stc => strlit "struct name " in
concat [
loc; id_desc; id;
strlit " is redeclared in ";
get_scope_desc scope; strlit "\n"]
End
(*
Get message for memory op addresses
is_local: local vs shared
is_load: load vs store
is_untrust: NotTrusted vs other
*)
Definition get_memop_msg_def:
get_memop_msg is_local is_load is_untrust loc scope =
let mem_type = if is_local then «local » else «shared » in
let op_type = if is_load then «load » else «store » in
let issue =
case (is_local, is_untrust) of
| (F, F) => «is »
| (F, T) => «may be »
| (T, F) => «is not »
| (T, T) => «may not be » in
concat [
loc; mem_type; op_type;
«address »; issue; «calculated from base in »;
get_scope_desc scope; «\n»]
End
(*
Get message for op argument number
is_exact: exactly vs at least
*)
Definition get_oparg_msg_def:
get_oparg_msg is_exact n_expected n_given loc op scope =
let issue =
if is_exact then strlit " only accepts "
else strlit " requires at least " in
concat [
loc; strlit "operation "; op;
issue; n_expected; strlit " operands, ";
n_given; strlit " provided in ";
get_scope_desc scope; strlit "\n"]
End
(* Get message for unreachable statement *)
Definition get_unreach_msg_def:
get_unreach_msg loc last scope = concat [
loc;
strlit "unreachable statement(s) after "; last;
strlit " in " ;
get_scope_desc scope; strlit "\n"]
End
(*
Get message for rogue loop exit
is_break: break vs continue
*)
Definition get_rogue_msg_def:
get_rogue_msg is_break loc scope =
let stmt = if is_break then «break » else «continue » in
concat [
loc; stmt;
strlit "statement outside loop in ";
get_scope_desc scope; strlit "\n"]
End
(* Get message for non-word shape *)
Definition get_non_word_msg_def:
get_non_word_msg desc sh_str loc scope = concat [
loc; desc; strlit " has shape "; sh_str;
strlit " instead of a word in ";
get_scope_desc scope; strlit "\n"]
End
(* Get message for declared shape mismatch *)
Definition get_shape_mismatch_msg_def:
get_shape_mismatch_msg desc sh_str_actual sh_str_expect loc scope = concat [
loc; desc; strlit " has shape "; sh_str_actual;
strlit " instead of declared shape "; sh_str_expect;
strlit " in ";
get_scope_desc scope; strlit "\n"]
End
Definition get_implementation_err_msg_def:
get_implementation_err_msg desc loc scope = concat [
loc; desc; strlit " in "; get_scope_desc scope; strlit "\n";
strlit "this should never happen. please report to a compiler developer\n"]
End
(* Misc functions *)
(* Find the first element in a sorted list that is repeated *)
Definition first_repeat_def:
first_repeat xs =
case xs of
| (x1::x2::xs) =>
if x1 = x2 then SOME x1
else first_repeat $ x2::xs
| _ => NONE
End
(* Get string name for binary ops *)
Definition binop_to_str_def:
binop_to_str op =
case op of
| Add => «Add»
| Sub => «Sub»
| And => «And»
| Or => «Or»
| Xor => «Xor»
End
(* Get string name for Pancake ops *)
Definition panop_to_str_def:
panop_to_str op =
case op of
| Mul => «Mul»
End
(* Get string name for Pancake primitives *)
Definition primop_to_str_def:
primop_to_str pop =
case pop of
| AddCarry => «AddCarry»
End
(* Static check helpers *)
(* Check for out of scope func *)
Definition check_fun_name_def:
check_fun_name ctxt fname =
case lookup ctxt.funcs fname of
| NONE => error (ScopeErr $
add_primitive_hint fname (get_scope_msg Fun ctxt.loc fname ctxt.scope))
| SOME f => return f
End
(* Check for out of scope global *)
Definition check_global_var_def:
check_global_var ctxt vname =
case lookup ctxt.globals vname of
| NONE => error (ScopeErr $ get_scope_msg Var ctxt.loc vname ctxt.scope)
| SOME v => return v
End
(* Check for out of scope local *)
Definition check_local_var_def:
check_local_var ctxt vname =
case lookup ctxt.locals vname of
| NONE => error (ScopeErr $ get_scope_msg Var ctxt.loc vname ctxt.scope)
| SOME v => return v
End
(* Check for redeclared variable *)
Definition check_redec_var_def:
check_redec_var ctxt vname =
case (lookup ctxt.locals vname, lookup ctxt.globals vname) of
| (NONE, NONE) => return ()
| _ => log (WarningErr $ get_redec_msg Var ctxt.loc vname ctxt.scope)
End
(* Check shapes of exported arguments *)
Definition check_export_params_def:
check_export_params loc scope [] = return () /\
check_export_params loc scope ((vname,shape)::ps) =
if ~(shape = One) then
error (ShapeErr $ get_non_word_msg (
concat [strlit "exported function parameter "; vname]
) (shape_to_str shape) loc scope)
else check_export_params loc scope ps
End
(* Check operand shape and return merged shaped basedness *)
Definition check_operands_def:
check_operands ctxt op_str [] = return $ NotBased /\
check_operands ctxt op_str (sb::sbs) =
case sb of
| WordB b =>
do
b' <- check_operands ctxt op_str sbs;
return $ based_merge b b'
od
| _ => error (ShapeErr $ get_non_word_msg (
concat [strlit "operation "; op_str; strlit " operand"]
) (sh_bd_to_str sb) ctxt.loc ctxt.scope)
End
(* Check args for primitives and return result shaped basedness *)
Definition check_primitive_args_def:
check_primitive_args ctxt AddCarry sh_bds =
do
op_str <<- primop_to_str AddCarry;
nargs <<- LENGTH sh_bds;
if ~(nargs = 3)
then error (GenErr $ get_oparg_msg T «3»
(num_to_str nargs) ctxt.loc op_str ctxt.scope)
else return ();
b <- check_operands ctxt op_str sh_bds;
return $ StructB [WordB b; WordB NotBased]
od
End
(* Check for arg number and shape *)
Definition check_func_args_def:
check_func_args ctxt fname params sh_bds =
case (params, sh_bds) of
(* check declared vs provided *)
| ((p,s)::ps, sb::sbs) =>
if ~(sh_bd_has_shape s sb) then
error (ShapeErr $ get_shape_mismatch_msg (concat [
«value for argument »; p;
« given to function »; fname
]) (sh_bd_to_str sb) (shape_to_str s) ctxt.loc ctxt.scope)
else check_func_args ctxt fname ps sbs
(* no more provided args *)
| ((p,s)::ps, []) => error (GenErr $ concat [
ctxt.loc; strlit "argument "; p;
strlit " for call to function "; fname;
strlit " is missing in ";
get_scope_desc ctxt.scope; strlit "\n"])
(* no more declared params *)
| ([], sb::sbs) => error (GenErr $ concat [
ctxt.loc; strlit "extra arguments given to function "; fname;
strlit " in ";
get_scope_desc ctxt.scope; strlit "\n"])
(* checks complete *)
| ([], []) => return ()
End
(* Check all fields are given exactly one value and shape *)
Definition check_struct_fields_def:
check_struct_fields ctxt sname fields fsbs =
case (fields, fsbs) of
(* check declared vs provided *)
| ((fld,sh)::fss, fsbs) =>
do
(* check number of provided values *)
sb <-
case FILTER (\(fld',sb). fld = fld') fsbs of
(* exactly one *)
| [(fld,sb)] => return sb
(* zero *)
| [] => error (ShapeErr $ concat [
ctxt.loc; strlit "missing field "; fld;
strlit " in named struct "; sname;
strlit " constant in ";
get_scope_desc ctxt.scope; strlit "\n"])
(* more than one *)
| _ => error (ShapeErr $ concat [
ctxt.loc; strlit "multiple values for field "; fld;
strlit " in named struct "; sname;
strlit " constant in ";
get_scope_desc ctxt.scope; strlit "\n"]);
(* check shape of provided value *)
if ~(sh_bd_has_shape sh sb) then
error (ShapeErr $ get_shape_mismatch_msg (concat [
strlit "value for field "; fld;
strlit " given to named struct "; sname
]) (sh_bd_to_str sb) (shape_to_str sh) ctxt.loc ctxt.scope)
else check_struct_fields ctxt sname fss (ADELKEY fld fsbs)
od
(* no more declared fields *)
| ([], fsb::fsbs) => error (GenErr $ concat [
ctxt.loc; strlit "unexpected field "; FST fsb;
strlit " given to named struct "; sname;
strlit " in ";
get_scope_desc ctxt.scope; strlit "\n"])
(* checks complete *)
| ([], []) => return ()
End
(* Check shape names in scope *)
Definition check_shape_def:
check_shape sctxt loc scope One = return () /\
check_shape sctxt loc scope (Comb shs) =
check_shapes sctxt loc scope shs /\
check_shape sctxt loc scope (Named nm) =
(case ALOOKUP sctxt nm of
| SOME flds => return ()
| NONE => error (ScopeErr $ get_scope_msg Stc loc nm scope)) /\
check_shapes sctxt loc scope [] = return () /\
check_shapes sctxt loc scope (sh::shs) =
do
check_shape sctxt loc scope sh;
check_shapes sctxt loc scope shs
od
End
(* Check field/param shape *)
Definition check_id_shapes_def:
check_id_shapes sctxt loc scope [] =
return () /\
check_id_shapes sctxt loc scope ((id,shape)::ids) =
do
(* setup specific scope message *)
scope' <-
case scope of
| FunScope fname _ =>
return $ FunScope fname (concat [strlit " parameter "; id])
| StcScope sname _ =>
return $ StcScope sname id
(* should never occur if static checker implemented correctly *)
| s => error (GenErr $ get_implementation_err_msg
(strlit "parameter or field found in unexpected scope")
loc scope);
check_shape sctxt loc scope' shape;
check_id_shapes sctxt loc scope ids
od
End
(* Main static checking functions *)
(*
static_check_exp returns:
(exp info (:exp_return)) static_result
static_check_exps returns:
(exps info (:exps_return)) static_result
*)
Definition static_check_exp_def:
static_check_exp ctxt (Const num) =
(* return exp info *)
return <| sh_bd := WordB NotBased |> ∧
static_check_exp ctxt (Var Local vname) =
do
(* check for out of scope var *)
vinf <- check_local_var ctxt vname;
(* return stored info *)
return <| sh_bd := vinf.vsh_bd |>
od ∧
static_check_exp ctxt (Var Global vname) =
do
(* check for out of scope var *)
vinf <- check_global_var ctxt vname;
case sh_bd_from_sh ctxt.structs Trusted vinf.vshape of
(* return exp info with stored shape *)
| SOME sb => return <| sh_bd := sb |>
(* should never occur if static checker implemented correctly *)
| NONE => error (ScopeErr $ get_implementation_err_msg
(strlit "static analysis failed to convert in-scope shape")
ctxt.loc ctxt.scope)
od ∧
static_check_exp ctxt (RStruct exps) =
do
(* check struct field exps *)
esret <- static_check_exps ctxt exps;
(* return exp info with found shape *)
return <| sh_bd := StructB esret.sh_bds |>
od ∧
static_check_exp ctxt (RField index exp) =
do
(* check struct exp *)
eret <- static_check_exp ctxt exp;
case index_sh_bd index eret.sh_bd of
| NONE => error (ShapeErr $ concat [
ctxt.loc; strlit "expression shape "; sh_bd_to_str eret.sh_bd;
strlit " has no field at index "; num_to_str index;
strlit " in "; get_scope_desc ctxt.scope; strlit "\n"])
(* return exp info with found shape *)
| SOME sb => return <| sh_bd := sb |>
od ∧
static_check_exp ctxt (NStruct name eflds) =
do
sinfo <-
case ALOOKUP ctxt.structs name of
| SOME info => return info
| NONE => error (ScopeErr $ get_scope_msg Stc ctxt.loc name ctxt.scope);
(* check struct field exps *)
(field_names, field_exps) <<- UNZIP eflds;
esret <- static_check_exps ctxt field_exps;
field_sbs <<- ZIP (field_names, esret.sh_bds);
check_struct_fields ctxt name sinfo.fields field_sbs;
(* return exp info with found shape *)
return <| sh_bd := NamedB name field_sbs |>
od ∧
static_check_exp ctxt (NField field exp) =
do
(* check struct exp *)
eret <- static_check_exp ctxt exp;
case field_sh_bd field eret.sh_bd of
| NONE => error (ShapeErr $ concat [
ctxt.loc; strlit "expression shape "; sh_bd_to_str eret.sh_bd;
strlit " has no field "; field; strlit " in ";
get_scope_desc ctxt.scope; strlit "\n"])
(* return exp info with found shape *)
| SOME sb => return <| sh_bd := sb |>
od ∧
static_check_exp ctxt (Load shape addr) =
do
(* check shape *)
check_shape ctxt.structs ctxt.loc ctxt.scope shape;
(* check addr exp *)
aret <- static_check_exp ctxt addr;
(* check address shape and references base *)
case aret.sh_bd of
| StructB _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| NamedB _ _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| WordB NotBased =>
log (WarningErr $ get_memop_msg T T F ctxt.loc ctxt.scope)
| WordB NotTrusted =>
log (WarningErr $ get_memop_msg T T T ctxt.loc ctxt.scope)
| _ => return ();
case sh_bd_from_sh ctxt.structs Trusted shape of
(* return exp info *)
| SOME sb => return <| sh_bd := sb |>
(* should never occur if static checker implemented correctly *)
| NONE => error (ScopeErr $ get_implementation_err_msg
(strlit "static analysis failed to convert in-scope shape")
ctxt.loc ctxt.scope)
od ∧
static_check_exp ctxt (Load32 addr) =
do
(* check addr exp *)
aret <- static_check_exp ctxt addr;
(* check address shape and references base *)
case aret.sh_bd of
| StructB _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| NamedB _ _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| WordB NotBased =>
log (WarningErr $ get_memop_msg T T F ctxt.loc ctxt.scope)
| WordB NotTrusted =>
log (WarningErr $ get_memop_msg T T T ctxt.loc ctxt.scope)
| _ => return ();
(* return exp info *)
return <| sh_bd := WordB Trusted |>
od ∧
static_check_exp ctxt (LoadByte addr) =
do
(* check addr exp *)
aret <- static_check_exp ctxt addr;
(* check address shape and references base *)
case aret.sh_bd of
| StructB _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| NamedB _ _ =>
error (ShapeErr $ get_non_word_msg
(strlit "load address") (sh_bd_to_str aret.sh_bd) ctxt.loc ctxt.scope)
| WordB NotBased =>
log (WarningErr $ get_memop_msg T T F ctxt.loc ctxt.scope)
| WordB NotTrusted =>
log (WarningErr $ get_memop_msg T T T ctxt.loc ctxt.scope)
| _ => return ();
(* return exp info *)
return <| sh_bd := WordB Trusted |>
od ∧
static_check_exp ctxt (Op bop exps) =
do
op_str <<- binop_to_str bop;
(* check num of op args *)
nargs <<- LENGTH exps;
case bop of
| Sub =>
if ~(nargs = 2) then
error (GenErr $ get_oparg_msg T (strlit "2") (num_to_str nargs)
ctxt.loc op_str ctxt.scope)
else return ()
| _ =>
if nargs < 2 then
error (GenErr $ get_oparg_msg F (strlit "2") (num_to_str nargs)
ctxt.loc op_str ctxt.scope)
else return ();
(* check op args *)
esret <- static_check_exps ctxt exps;
(* check arg shapes *)
b <- check_operands ctxt op_str esret.sh_bds;
return <| sh_bd := WordB b |>
od ∧
static_check_exp ctxt (Panop pop exps) =
do
op_str <<- panop_to_str pop;
(* check num of op args *)
nargs <<- LENGTH exps;
case pop of
| Mul =>
if ~(nargs = 2) then
error (GenErr $ get_oparg_msg T (strlit "2") (num_to_str nargs)
ctxt.loc op_str ctxt.scope)
else return ();
(* check op args *)
esret <- static_check_exps ctxt exps;
(* check arg shapes *)
b <- check_operands ctxt op_str esret.sh_bds;
return <| sh_bd := WordB b |>
od ∧
static_check_exp ctxt (Cmp cop exp1 exp2) =
do
(* check cmp arg exps *)
eret1 <- static_check_exp ctxt exp1;
eret2 <- static_check_exp ctxt exp2;
(* check for shape match *)
if ~(sh_bd_eq_shapes eret1.sh_bd eret2.sh_bd) then
error (ShapeErr $ concat [
ctxt.loc; strlit "comparison given operands of different shapes in ";
get_scope_desc ctxt.scope; strlit "\n"])
else return ();
(* return exp info *)
return <| sh_bd := WordB NotBased |>
od ∧
static_check_exp ctxt (Shift sop exp n) =
do
(* check shifted exp *)
eret <- static_check_exp ctxt exp;
(* check exp shape *)
if ~(sh_bd_has_shape One eret.sh_bd) then
error (ShapeErr $ get_non_word_msg
(strlit "shifted expression")