-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtype.ml
2666 lines (2002 loc) · 64.6 KB
/
type.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
module Opts = Opts.Get
open Batteries
(* THINK: Maybe encapsulate this into a Name module *)
type name = string
(* Shapes *)
(* TODO: Smart constructors for shapes *)
module rec Shape : sig
type t = Var of var
| Bot
| Ptr of t
| Fun of fun_shape
| Struct of cstruct
| Ref of Region.t * t
(** Two shape variables must be compared using [eq_var] or [uniq_of].
*
* DO NOT use OCaml's structural equality:
* If the variable is pointing to a larger shape such as a struct,
* OCaml's structural equality may use too much memory, or even enter
* an infinite loop if the struct definition is cyclic.
*)
and var
and fun_shape =
{ domain : args
(** The computational effects of a function are always
* defined by an effect variable. This variable has
* subeffecting constraints attached.
*)
; effects : EffectVar.t
; range : result
; varargs : bool
}
and args = t list
and result = t
(* Struct typing needs to be scalable, so structs are handled
* in a simple way. For each struct type we infer a parameterized
* struct shape. These parameters are shape, region and effect
* variables quantified over the shape of its fields. Recursive
* structures are flattened in the same way as arrays, so all
* the elements of a linked list are in the same memory region.
* Each use of a struct type will instantiate these parameters
* with specific values (here, [sargs]).
*
* Structs are handled basically the same way as inductive data
* types in ML or Haskell. But in ML/Haskell the parameters are
* given, while here we need to infer them!
*
* Some of the fields of the [cstruct] record are mutable in order
* to "tie the knot" and build so-convenient cyclic shapes.
* Ideally, these references should be "frozen" after the struct
* shapes are built by [process_structs].
*
* OBS: The majority of interesting operations on struct shapes
* can be performed simply taking their actual arguments. No need
* to write complicated traversals that examine the fields.
*)
and cstruct =
{ sinfo : Cil.compinfo
; mutable sargs : TypeArgs.t
; mutable sparams : QV.t
; mutable fields : fields
}
and fields = field list
and field = { finfo : Cil.fieldinfo; fshape : t }
module ShapeVar : sig
type t = var
val compare : t -> t -> int
val equal : t -> t -> bool
val hash : t -> int
end
(**
* if_meta f ?a x = f ?a x
* if_meta f 'a x = x
*)
val if_meta : (var -> 'a -> 'a) -> var -> 'a -> 'a
(**
* if_bound f ?a x = x
* if_bound f 'a x = f 'a x
*)
val if_bound : (var -> 'a -> 'a) -> var -> 'a -> 'a
val foldv : (Shape.var -> 'a -> 'a) ->
(Region.t -> 'a -> 'a) ->
(EffectVar.t -> 'a -> 'a) ->
'a ->
t -> 'a
val fv_of : t -> Vars.t
val fv_of_fields : fields -> Vars.t
val free_in : var -> t -> bool
val not_free_in : var -> t -> bool
val bv_of : t -> Vars.t
(* THINK: Some of these new_ may better be named fresh_ ? *)
(** Given [z] constructs [Ref (r,z)] with [r] fresh. *)
val new_ref_to : t -> t
val bound_var : unit -> var
val meta_var : unit -> var
(** A fresh shape variable [Var x]. *)
val fresh : unit -> t
(** A fresh pointer-shape [ptr ref[r] z] *)
val fresh_ptr_to : t -> t
val uniq_of : var -> Uniq.t
val is_meta : var -> bool
val eq_var : var -> var -> bool
val read_var : var -> t option
val write_var : var -> t -> unit
val zonk : t -> t
(** Shape of a given CIL type. *)
val of_typ : Cil.typ -> t
(** Reference shape to a given CIL type. *)
val ref_of : Cil.typ -> t
(** This will populate a global struct environment in Type.Shape,
* which is then used by Type.Shape.of_typ.
* THINK: I would like to avoid global variables when possible,
* so we could either have a persistent environment based on
* MapS, or introduce a functor Type.Shape.Make that will return
* a Shape-ish module given a C(IL) file.
*)
val process_structs : Cil.file -> unit
(** Shape from [varinfo]'s type. *)
val of_varinfo : Cil.varinfo -> t
val is_fun : t -> bool
(* THINK: Should it be Unify.match_fun ? *)
val get_ref : t -> Region.t * t
val get_fun : t -> args * EffectVar.t * result
val get_ref_fun : t -> args * EffectVar.t * result
val match_struct_shape : t -> cstruct
(** Look up a field in a struct shape. *)
val field : cstruct -> name -> t
val list_fields : cstruct -> field list
val vsubst_var : Subst.t -> var -> var
val vsubst : Subst.t -> t -> t
(* THINK: Maybe re-add folds when I decide how to handle cstruct *)
val regions_in : t -> Regions.t
val fully_read : t -> Effects.t
val fully_RW : t -> Effects.t
val pp_var : var -> PP.doc
val pp_fun : fun_shape -> PP.doc
val pp : t -> PP.doc
val to_string : t -> string
end
= struct
type t = Var of var
| Bot
| Ptr of t
| Fun of fun_shape
| Struct of cstruct
| Ref of Region.t * t
and var = Bound of Uniq.t
| Meta of Uniq.t * t Meta.t
and fun_shape =
{ domain : args
; effects : EffectVar.t
; range : result
; varargs : bool
}
and args = t list
and result = t
and cstruct =
{ sinfo : Cil.compinfo
; mutable sargs : TypeArgs.t
; mutable sparams : QV.t
; mutable fields : fields
}
and fields = field list
and field = { finfo : Cil.fieldinfo; fshape : t }
let is_meta = function
| Meta _ -> true
| Bound _ -> false
(** Bound variables are NOT free by definition! *)
let rec foldv f g h x = function
| Var a -> f a x
| Bot -> x
| Ptr z -> foldv f g h x z
| Fun fn -> foldv_fun f g h x fn
| Struct s -> foldv_struct f g h x s
| Ref (r,z) ->
let x' = foldv f g h x z in
g r x'
and foldv_list f g h x (zs :t list) =
List.fold_left (foldv f g h) x zs
and foldv_fun f g h x {domain; effects; range} =
let x1 = foldv_list f g h x domain in
let x2 = foldv f g h x1 range in
EffectVar.foldv f g h x2 effects
and foldv_struct f g h x s = TypeArgs.foldv f g h x s.sargs
let if_meta f a x = Utils.apply_if (is_meta a) (f a) x
let if_bound f a x = Utils.apply_if (not (is_meta a)) (f a) x
(* TODO: Note that most, if not all, fv_of variants share use the same
* f,g,h functions. We could refactor fv_of definition in Vars.
*)
let fv_of z =
let f = if_meta Vars.add_shape in
let g = Region.if_meta Vars.add_region in
let h = EffectVar.if_meta Vars.add_effect in
foldv f g h Vars.empty z
let rec fv_of_fields fs = Vars.sum (List.map fv_of_field fs)
and fv_of_field {fshape} = fv_of fshape
let free_in a z = Vars.mem_shape a (fv_of z)
let not_free_in a z = not(free_in a z)
let bv_of z =
let f = if_bound Vars.add_shape in
let g = Region.if_bound Vars.add_region in
let h = EffectVar.if_bound Vars.add_effect in
foldv f g h Vars.empty z
let rec bv_of_struct s = TypeArgs.bv_of s.sargs
and bv_of_fields fs = Vars.sum (List.map bv_of_field fs)
and bv_of_field {fshape} = bv_of fshape
let new_ref_to (z :t) :t =
let r = Region.meta() in
Ref (r,z)
let bound_var () :var =
let id = Uniq.fresh() in
Bound id
let meta_var () :var =
let id = Uniq.fresh() in
let mz = Meta.fresh() in
Meta(id,mz)
let fresh () :t =
Var(meta_var())
let fresh_ptr_to z :t =
let r = Region.meta() in
Ptr(Ref(r,z))
let uniq_of = function
| Meta(u,_) -> u
| Bound u -> u
module ShapeVar = struct
type t = var
let compare = Utils.compare_on uniq_of
let equal = Utils.equal_on uniq_of
let hash = Hashtbl.hash % uniq_of
end
let eq_var a b = uniq_of a = uniq_of b
let read_var : Shape.var -> Shape.t option = function
| Meta(_,mz) -> Meta.read mz
| Bound _ -> Error.panic_with("cannot read bound shape variable")
let write_var x z =
match x with
| Meta(_,mz) -> Meta.write mz z
| Bound _ -> Error.panic_with("cannot write bound shape variable")
let rec pp = function
| Var x -> pp_var x
| Bot -> PP.(!^ "_|_")
| Ptr z -> PP.(!^ "ptr" + space + pp z)
| Fun fz -> pp_fun fz
| Struct s -> pp_struct s
| Ref(r,z) ->
PP.(!^ "ref" + brackets(Region.pp r) + space + pp z)
and pp_var a =
let vt_str = if is_meta a then "?" else "'" in
let id_pp = Uniq.pp (uniq_of a) in
PP.(!^ vt_str + !^ "z" + id_pp)
and pp_fun = fun {domain; effects; range; varargs} ->
let args_pp = pp_args domain varargs in
let res_pp = pp range in
let eff_pp = EffectVar.pp_lb effects in
let arr_pp = PP.(!^ "==" + eff_pp + !^ "==>") in
PP.(parens(args_pp ++ arr_pp ++ res_pp))
(* THINK: Should I turn it into PP.tuple ~pp ? *)
and pp_args args varargs =
let pp_varargs =
if varargs then PP.(comma ++ !^ "...") else PP.empty
in
PP.(parens (comma_sep (List.map pp args) + pp_varargs))
and pp_struct s =
let open PP in
let args_doc = TypeArgs.pp_upto s.sargs 10 in
!^ "struct" ++ !^ Cil.(s.sinfo.cname) + angle_brackets(args_doc)
and pp_fields fs = PP.(separate (!^ "; ") (List.map pp_field fs))
and pp_field {finfo;fshape} = PP.(!^ Cil.(finfo.fname) ++ colon ++ pp fshape)
let to_string = PP.to_string % pp
let string_of_fields = PP.to_string % pp_fields
(* let string_of_sargs sas = PP.(to_string (comma_sep (List.map pp_sarg sas))) *)
(* Check the well-formedness of a shape.
*
* Shapes may have cycles and hence the depth of the search
* is bounded.
*
* TODO: For now we focus on the more problematic struct shapes,
* but potentially more could be done.
*)
let rec lint_shape_aux d = function
| Ptr z
| Ref(_,z) ->
lint_shape_aux d z
| Struct s -> lint_struct_aux d s
| _other -> ()
(* Check that we "tied the knot" correctly. In essence this means
* that the shapes of the fields have been generalized correctly,
* and thus there must not be any free meta variable in them. *)
and lint_struct_aux d (s :cstruct) :unit =
assert(TypeArgs.count s.sargs = QV.length s.sparams);
assert(QV.for_all (not % Shape.is_meta) (not % Region.is_meta) (not % EffectVar.is_meta) s.sparams);
let ffvs = fv_of_fields s.fields in
let valid_params = Vars.is_empty ffvs in
if not valid_params then begin
Log.error "lint_struct: %s\nfields = %s\nffvs = %s"
(PP.to_string (pp_struct s))
(PP.to_string (pp_fields s.fields))
(Vars.to_string ffvs)
end;
assert(valid_params);
if d > 1 then List.iter (lint_field_aux (d-1)) s.fields;
and lint_field_aux d fz = lint_shape_aux d fz.fshape
(* Check the well-formedness of a struct shape
* using a reasonably small depth bound. *)
let lint_struct = lint_struct_aux 2
(* Maps a struct to its generalized struct shape. *)
type struct_memo = (name,cstruct) Hashtbl.t
(* Maps a struct to its pre-shape, needed to handle recursive
* struct shapes in a variety of scenarios. *)
type struct_cache = (name * cstruct) list
(* Global struct memo-table.
* TODO: Move into some kind of environment or make Type a functor.
*)
let memo :struct_memo = Hashtbl.create 10
(* Instantiate a parameterized struct shape with fresh meta variables. *)
let inst_struct s =
let new_args = s.sparams |> QV.instantiate |> Tuple.Tuple2.first |> TypeArgs.of_var_enum in
let s' = { s with sargs = new_args } in
lint_struct s';
s'
let rec zonk z = zonk_aux [] z
and zonk_aux cache :t -> t = function
| Var x -> zonk_var x
| Bot -> Bot
| Ptr z -> Ptr (zonk_aux cache z)
| Fun f -> Fun (zonk_fun_aux cache f)
| Struct s -> Struct (zonk_struct_aux cache s)
| Ref (r,z) ->
let r' = Region.zonk r in
let z' = zonk_aux cache z in
Ref(r',z')
and zonk_var :var -> t = function
| Bound _ as a ->
Var a
| Meta(_,mz) as z ->
Option.(Meta.zonk_with zonk mz |? Var z)
and zonk_fun_aux cache f =
{ domain = zonk_dom_aux cache f.domain
; effects = EffectVar.zonk f.effects
; range = zonk_aux cache f.range
; varargs = f.varargs
}
and zonk_dom_aux cache d = List.map (zonk_aux cache) d
and zonk_struct_aux cache s =
match List.Exceptionless.assoc Cil.(s.sinfo.cname) cache with
| None -> { s with sargs = TypeArgs.zonk s.sargs }
| Some sz -> sz
and zonk_fields_aux cache fs = List.map (zonk_field_aux cache) fs
and zonk_fields fs = zonk_fields_aux [] fs
and zonk_field_aux cache ({fshape} as field) =
{field with fshape = zonk_aux cache fshape}
(* THINK: operate on typesig instead ? *)
let rec of_typ_cache memo cache (ty :Cil.typ) :t =
match ty with
| Cil.TVoid _
| Cil.TInt _
(* Enumerations are treated as integers *)
| Cil.TEnum _ ->
fresh()
| Cil.TFloat _
-> Bot
| Cil.TPtr(ty,_)
| Cil.TArray(ty,_,_)
-> Ptr (ref_of_cache memo cache ty)
| Cil.TFun(res,args_opt,varargs,_) ->
let args = Option.(args_opt |? []) in
let domain = of_args memo cache args in
let effects = EffectVar.meta() in
let range = of_typ_cache memo cache res in
Fun { domain; effects; range; varargs }
| Cil.TNamed (ti,_) -> of_typ_cache memo cache Cil.(ti.ttype)
| Cil.TBuiltin_va_list _ ->
Bot
(* Struct or union (unsound) *)
| Cil.TComp (ci,_) -> Struct (of_struct memo cache ci)
and of_args memo cache : _ -> args = function
| [] -> []
| (_,ty,_)::args -> ref_of_cache memo cache ty :: of_args memo cache args
and of_struct memo cache ci =
let open Cil in
let name = ci.cname in
begin match Hashtbl.Exceptionless.find memo name with
| Some s -> inst_struct s
| None ->
match List.Exceptionless.assoc name cache with
| None ->
(* THINK how to handle extern struct declarations when we
* add support for inter-file analysis. Usually there are also
* a number of function prototypes referring to these structs.
* But that's OK, because such structs are never actually used.
*
* For now, just return a dummy struct.
*)
if ci.cdefined
then Log.error "of_typ: struct defined but not found in cache: %s" name;
{ sinfo = ci; sargs = TypeArgs.empty; sparams = QV.empty; fields = [] }
| Some z -> z
end
and of_fields memo cache fs :field list = List.map (of_field memo cache) fs
(* NOW we consider a struct as a single variable, where all the
* fields share the same memory storage (region). For better
* precision struct fields should be treated as different variables.
* However this introduces some problems, for instance: is the
* struct variable uninitialized after writing to one of its fields?
*)
and of_field memo cache fi =
Cil.({ finfo = fi; fshape = ref_of_cache memo cache fi.ftype })
and ref_of_cache memo cache ty :t =
let z = of_typ_cache memo cache ty in
new_ref_to z
let of_typ =
of_typ_cache memo []
let ref_of ty =
let z = of_typ ty in
new_ref_to z
(* OBS: changes to this function shall be thought and tested properly! *)
let infer_structs (cis :Cil.compinfo list) =
(* STEP 1: Infer the shape of the struct fields while filling
* any recursive occurrence with a pointer to the struct itself:
* we're tying the knot!
*)
let mk_dummy ci =
{ sinfo = ci;
sargs = TypeArgs.empty;
sparams = QV.empty;
fields = []
}
in
let auxs = List.map mk_dummy cis in
let cache = List.map (fun aux -> Cil.(aux.sinfo.cname),aux) auxs in
List.iter2 (fun ci aux ->
aux.fields <- of_fields memo cache Cil.(ci.cfields);
) cis auxs;
(* THINK: if we want to merge fields, it could be done here. *)
(* STEP 2: Compute the type parameters of the structures and
* set the default instance arguments.
* THINK: Can we distinguish between struct declaration and instance?
*)
let fvs =
auxs |> List.enum |> Enum.map (fun aux -> fv_of_fields (aux.fields)) |> Vars.enum_sum
in
let params = QV.quantify fvs in (* NB: lb must be empty so no need to zonk_lb here *)
let sargs = params |> QV.var_enum |> TypeArgs.of_var_enum in
(* STEP 3: Set all the [sparams] and [sargs] pointers appropriately,
* and zonk fields shapes.
*)
List.iter (fun aux ->
aux.sparams <- params;
aux.sargs <- sargs;
) auxs;
List.iter (fun aux ->
aux.fields <- zonk_fields_aux cache (aux.fields)
) auxs;
(* STEP 4: Linter pass and populate memo table.
* Until all fields have been zonked we cannot guarantee coherence.
*)
List.iter (fun aux ->
Log.debug "infer_structs: %s\nfields = %s"
(PP.to_string (pp_struct aux))
(PP.to_string (pp_fields aux.fields));
lint_struct aux;
Hashtbl.add memo Cil.(aux.sinfo.cname) aux
) auxs
let infer_struct ci = infer_structs [ci]
let process_structs (file :Cil.file) :unit =
Structs.Dep.of_file file
|> List.iter infer_structs
let of_varinfo x = ref_of Cil.(x.vtype)
let is_fun = function
| Fun _ -> true
| _else -> false
let get_ref = function
| Ref(r,z) -> r,z
| __other__ ->
Log.error "%s is not a ref shape\n" (Shape.to_string __other__);
Error.panic_with("Shape.get_ref")
let get_fun : t -> args * EffectVar.t * t = function
| Fun {domain; effects; range} ->
domain, effects, range
| __other__ ->
Log.error "%s is not a function shape\n" (Shape.to_string __other__);
Error.panic_with("Shape.get_fun")
let get_ref_fun = function
| Ref(_,z) -> get_fun z
| __other__ ->
Log.error "%s is not a ref-to-function shape\n" (Shape.to_string __other__);
Error.panic_with("Shape.get_ref_fun")
(** Substitute variables with variables.
*
* To substitute variables with arbitrary shapes, simply
* substitute with meta-variables and zonk.
*)
let rec vsubst s : t -> t
= function
| Var a -> Var(vsubst_var s a)
| Bot -> Bot
| Ptr z -> Ptr (vsubst s z)
| Fun fz -> Fun (vsubst_fun s fz)
| Struct z -> Struct (vsubst_struct s z)
| Ref(r,z) -> Ref (Region.vsubst s r,vsubst s z)
and vsubst_var s a =
Subst.find_shape_def a a s
and vsubst_fun s = fun { domain; effects; range; varargs } ->
let d' = List.map (vsubst s) domain in
let f' = EffectVar.vsubst s effects in
let r' = vsubst s range in
{ domain = d'; effects = f'; range = r'; varargs }
and vsubst_struct s z =
{ z with sargs = TypeArgs.vsubst s z.sargs }
and vsubst_fields s fs = List.map (vsubst_field s) fs
and vsubst_field s ({fshape} as field) =
{field with fshape = vsubst s fshape}
(* Instantiate a given shape with the struct param-args substitution.
* Partial application struct_inst sz will compute the substitution
* immediately, and this will be shared by subsequent applications to
* a second shape argument.
*)
let struct_inst (sz :cstruct) : t -> t =
let xs = TypeArgs.var_enum sz.sargs in
let s = Subst.of_enum_pair (QV.var_enum sz.sparams) xs in
fun z ->
let z' = vsubst s z in
zonk_aux [] z'
let match_struct_shape = function
| Struct s -> s
| z ->
match zonk z with
| Struct s -> s
| _other___ -> Error.panic_with "Shape.match_struct_shape"
let field s fn =
try
let fi = List.find Cil.(fun f -> f.finfo.fname = fn) s.fields in
struct_inst s fi.fshape
with Not_found ->
Log.error "Struct %s has no field %s" PP.(to_string (pp_struct s)) fn;
Error.panic_with("Shape.field")
let list_fields s =
let field_inst = struct_inst s in
List.map (fun fz -> {fz with fshape = field_inst fz.fshape}) s.fields
let rec regions_in z =
let f _ x = x in
let g = Regions.add in
let h _ x = x in
foldv f g h Regions.empty z
let fully_read z = z
|> regions_in
|> Regions.enum
|> Enum.map Effects.(fun r -> just (reads r))
|> Effects.(Enum.fold (+) none)
let fully_RW z = z
|> regions_in
|> Regions.enum
|> Enum.map Effects.(fun r -> just (reads r) +. writes r)
|> Effects.(Enum.fold (+) none)
end
(* Memory regions *)
and Region : sig
type t
val uniq_of : t -> Uniq.t
val is_meta : t -> bool
(**
* if_meta f ?r x = f ?r x
* if_meta f 'r x = x
*)
val if_meta : (t -> 'a -> 'a) -> t -> 'a -> 'a
(**
* if_bound f ?r x = x
* if_bound f 'r x = f 'r x
*)
val if_bound : (t -> 'a -> 'a) -> t -> 'a -> 'a
val compare : t -> t -> int
val equal : t -> t -> bool
val hash : t -> int
val bound : unit -> t
val meta : unit -> t
val write : t -> t -> unit
val zonk : t -> t
val fv_of : t -> Vars.t
val (=~) : t -> t -> unit
val vsubst : Subst.t -> t -> t
val pp : t -> PP.doc
val to_string : t -> string
end = struct
type t = Bound of Uniq.t
| Meta of Uniq.t * t Meta.t
let uniq_of = function
| Bound u
| Meta(u,_)
-> u
let is_meta = function
| Meta _ -> true
| Bound _ -> false
let if_meta f r x = Utils.apply_if (is_meta r) (f r) x
let if_bound f r x = Utils.apply_if (not (is_meta r)) (f r) x
let compare = Utils.compare_on uniq_of
let equal = Utils.equal_on uniq_of
let hash = Hashtbl.hash % uniq_of
let bound () : Region.t =
let id = Uniq.fresh() in
Bound id
let meta () : Region.t =
let id = Uniq.fresh() in
let mr = Meta.fresh() in
Meta(id,mr)
(** Here r2 may be a bound region. *)
let write r1 r2 =
match r1 with
| Bound _ -> Error.panic_with("write: not a meta-region")
| Meta(_,mr) -> Meta.write mr r2
let rec zonk :Region.t -> Region.t = function
| Bound _ as r ->
r
| Meta(id,mr) as r ->
Option.(Meta.zonk_with zonk mr |? r)
let fv_of r =
Vars.(if_meta add_region r none)
let unify_unbound id1 mr1 = function
| Meta(id2,_) when id1 = id2 -> ()
| Bound _ -> Error.panic()
| r2 -> Meta.write mr1 r2
(* TODO: Refactor code in Meta *)
let rec (=~) r1 r2 :unit =
assert(is_meta r2);
if equal r1 r2
then ()
else
match r1 with
| Meta(id1,mr1) ->
(match Meta.read mr1 with
| None ->
let r2' = zonk r2 in
unify_unbound id1 mr1 r2'
| Some rr1 -> rr1 =~ r2
)
| ___ -> Error.panic()
let vsubst s r =
Subst.find_region_def r r s
(* THINK: Should I refactor this into PP.tyvar ? *)
let pp r =
let vt_str = if is_meta r then "?" else "'" in
let id_str = Uniq.to_string (uniq_of r) in
PP.(!^ (Utils.green (vt_str ^ "r" ^ id_str)))
let to_string = PP.to_string % pp
end
and Regions : sig
include Set.S with type elt := Region.t
val none : t
val (+) : t -> t -> t
val (-) : t -> t -> t
val sum : t list -> t
val sum_enum : t Enum.t -> t
val pp : t -> PP.doc
val to_string : t -> string
end
= struct
include Set.Make(Region)
let none = empty
let (+) = union
let (-) = diff
let sum = List.fold_left union none
let sum_enum = Enum.fold union none
let pp x = PP.braces (PP.space_sep (List.map Region.pp (elements x)))
let to_string = PP.to_string % pp
end
and EffectVar : sig
type lb = Effects.t
type t
val lb_of : t -> lb
val uniq_of : t -> Uniq.t
val compare : t -> t -> int
val equal : t -> t -> bool
val hash : t -> int
val bound_with : lb -> t
val is_meta : t -> bool
(**
* if_meta h ?f x = h ?f x
* if_meta h 'f x = x
*)
val if_meta : (t -> 'a -> 'a) -> t -> 'a -> 'a
(**
* if_bound h ?f x = x
* if_bound h 'f x = h 'f x
*)
val if_bound : (t -> 'a -> 'a) -> t -> 'a -> 'a
val meta_with : lb -> t
val meta : unit -> t
val fv_of : t -> Vars.t
val add_lb : Effects.t -> t -> t
val write : t -> t -> unit
val zonk_acc : visited:EffectVars.t -> t -> t
val zonk : t -> t
val foldv : (Shape.var -> 'a -> 'a) ->
(Region.t -> 'a -> 'a) ->
(EffectVar.t -> 'a -> 'a) ->
'a ->
t -> 'a
val fv_of : t -> Vars.t
val (=~) : t -> t -> unit
val vsubst : Subst.t -> t -> t
val pp : t -> PP.doc
val pp_lb : t -> PP.doc
val to_string : t -> string
end = struct
type lb = Effects.t
type meta_lb = lb Uref.t
type meta_uniq = Uniq.t Uref.t
type t = Bound of Uniq.t * lb
| Meta of meta_eff
and meta_eff = meta_info ref
and meta_info = Root of Uniq.t * lb
| Link of Uniq.t * t
let uniq_of_meta mf =
match !mf with
| Root(u,_)
| Link(u,_) -> u
let uniq_of = function
| Bound(u,_) -> u
| Meta mf -> uniq_of_meta mf
let bound_with lb =
let id = Uniq.fresh() in
Bound(id,lb)
let is_meta = function
| Bound _ -> false
| Meta _ -> true
let if_meta f ef x = Utils.apply_if (is_meta ef) (f ef) x
let if_bound f ef x = Utils.apply_if (not (is_meta ef)) (f ef) x
let get_meta = function
| Bound _ -> Error.panic_with("get_meta: not a meta-effect variable")
| Meta mf -> mf
let rec lb_of = function
| Bound(_,lb) -> lb
| Meta mf -> lb_of_meta mf
and lb_of_meta mf =
match !mf with
| Root(_,lb) -> lb
| Link(_,ff) -> lb_of ff
let compare f1 f2 = Uniq.compare (uniq_of f1) (uniq_of f2)
let equal = Utils.equal_on uniq_of
let hash = Hashtbl.hash % uniq_of
let meta_with lb :t =
let id = Uniq.fresh() in
let mf = ref (Root(id,lb)) in
Meta mf
let meta () =
meta_with Effects.none
(* Map with path-compression. *)
let rec map (fn :lb -> lb) :t -> t = function
| Bound(id,lb) ->
Bound(id,fn lb)
| Meta mf ->
map_meta fn mf
and map_meta fn mf =
match !mf with
| Root(id,lb) ->
let mi' = Root(id,fn lb) in
mf := mi';
Meta mf
| Link(id,ff) ->
let ff' = map fn ff in
mf := Link(id,ff'); (* path compression *)
ff'
let zonk_acc ~visited ff =
if EffectVars.mem ff visited
then ff
else
let visited' = EffectVars.add ff visited in
(* NB: Intermediate links don't get added to `visited'
* so it may take a bit longer to converge.
*)
map (Effects.zonk_acc ~visited:visited') ff
let zonk = zonk_acc ~visited:EffectVars.empty
let foldv f g h x ef =
let x' = h ef x in
Effects.foldv f g h x' (lb_of ef)
(* FIXME: If there exist cycles in the `lb_of' graph, `fv_of' will diverge.
* I don't handle this for now because I need to re-think the handling
* of subeffecting constraints, and a new design could make this a no problem.
* Otherwise the `h' function passed to `foldv' could check if `ef' is already
* in `x' and, if so, return `None' so that `foldv' will not recurse.
*)
let fv_of ef =
let f = Shape.if_meta Vars.add_shape in
let g = Region.if_meta Vars.add_region in
let h = if_meta Vars.add_effect in