This repository has been archived by the owner on Sep 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinference.ml
2251 lines (2078 loc) · 79.4 KB
/
inference.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
open Ast
open RefinementTypes
open Sexplib.Std
open Std
open Std.StateMonad
open SimpleChecker.SideAnalysis
module SM = StringMap
module SS = StringSet
module P = Paths
module OI = OwnershipInference
type concr_ap = P.concr_ap
type pred_loc =
| LCond of int
| LArg of string * string
| LReturn of string
| LOutput of string * string
| LAlias of int
| LLet of int
| LCall of int * string
| LRead of int
| LUpdate of int
| LNull of int
| LMkArray of int
| LFold of int [@@deriving sexp]
type funenv = funtype SM.t
type tenv = typ SM.t
let sexp_of_tenv = SM.sexp_of_t ~v:sexp_of_typ
let string_of_concr_refinement =
PrettyPrint.pp_gen_rev (pp_ref (fun (k,_) ->
pp_alist (k :> refine_ap list)
))[@@ocaml.warning "-32"]
type tcon = {
env: (Paths.concr_ap * concr_refinement * (concr_nullity list)) list;
ante: concr_refinement;
conseq: concr_refinement;
nullity: concr_nullity list;
target: Paths.concr_ap option
}[@@deriving sexp]
exception Incompleteness of string
type context = {
theta: funenv;
gamma: tenv;
refinements: tcon list;
pred_arity: (bool * int) StringMap.t;
v_counter: int;
store_env: int -> tenv -> unit;
iso: SimpleChecker.SideAnalysis.results;
o_hints: float OI.ownership_ops;
}
type ('i,'o) osrc =
| Prev : (bool,bool) osrc
| Ownership : OI.magic_loc -> ('a,bool) osrc
module Result = struct
type t = {
theta: funenv;
refinements: tcon list;
arity: (bool * int) StringMap.t;
ty_envs: (int,tenv) Hashtbl.t
}
end
let mk_counter () =
let counter = ref 0 in
fun () ->
let nxt = !counter in
incr counter;
nxt
let fresh_tvar = mk_counter ()
let type_mismatch t1 t2 =
let tag1 = Obj.repr t1 |> Obj.tag in
let tag2 = Obj.repr t2 |> Obj.tag in
(assert (tag1 <> tag2); failwith "Mismatched types")
let unsupported s = raise @@ Incompleteness s
let do_with f l = ignore (f l); l
let ignore_iref = (fun r _ -> r),(fun _ r -> r)
let update_map v t m =
SM.remove v m
|> SM.add v t
let update_type v t ctxt =
{ ctxt with gamma = update_map v t ctxt.gamma }
let%lm mupdate_type v t ctxt = update_type v t ctxt
let add_type v t ctxt =
let te =
SM.add v t ctxt.gamma
in
{ ctxt with gamma = te }
let update_array_bind arr_bind root b =
(bind_of_arr arr_bind root) @ b
let update_tuple_bind t_bind root b1 =
(compile_bindings t_bind root) @ b1
let update_array_env ~nullity env path bind len_r =
let len_ap = (P.len path) in
let len_comp = compile_refinement len_ap bind len_r in
let ind_ap = (P.ind path) in
[
(len_ap,len_comp,nullity);
(ind_ap, NamedPred ("valid-ind", ([ len_ap ],ind_ap)), nullity)
] @ env
let update_tuple_env ~nullity env root bind b tl =
List.fold_left (fun acc (_,p) ->
match p with
| SVar _ -> acc
| SProj i ->
let cap = P.t_ind root i in
let t_ref = match List.nth tl i with
| Int r -> r
| _ -> assert false
in
(cap,compile_refinement cap bind t_ref,nullity)::acc
) env b
(* this denotes CONSTANT PATHS ONLY *)
let rec denote_type path (bind: (int * Paths.concr_ap) list) acc t =
match t with
| Array (b,len_r,_,et) ->
let arr_b = update_array_bind b path bind in
let acc' = update_array_env ~nullity:[] acc path bind len_r in
denote_type (P.elem path) arr_b acc' et
| Ref (_,_,_) -> acc
| Int r ->
let comp_r = compile_refinement path bind r in
(path,comp_r,[])::acc
| Tuple (b,t) ->
let (bind' : (int * Paths.concr_ap) list) = update_tuple_bind b path bind in
fold_lefti (fun i acc te ->
denote_type (P.t_ind path i) bind' acc te
) acc t
| TVar _ -> acc
| Mu (_,_,_,_) -> acc
let denote_gamma gamma =
SM.fold (fun v t acc ->
denote_type (P.var v) [] acc t
) gamma []
let denote_gamma_m ctxt = (ctxt,denote_gamma ctxt.gamma)
let%lq gen_own loc p ctxt =
let f = OI.GenMap.find (loc,p) ctxt.o_hints.OI.gen in
f > 0.0
let%lq split_own loc p ctxt =
let (f1,f2) = OI.SplitMap.find (loc,p) ctxt.o_hints.OI.splits in
(f1 > 0.0, f2 > 0.0)
let mtmap p f l = mmapi (fun i e ->
f (P.t_ind p i) e
) l
let rec to_top_type = function
| TVar id -> TVar id
| Int _ -> Int Top
| Ref (t,_,_) ->
Ref (to_top_type t,false,NUnk)
| Tuple (b,tl) ->
Tuple (b,List.map to_top_type tl)
| Array (a,_,_,t) ->
Array (a,Top,false,to_top_type t)
| Mu (s,{fv_map; pred_symbols},id,t) ->
Mu (s,{fv_map; pred_symbols = List.map (fun (ma,_) ->
(ma, Top)
) pred_symbols},id,to_top_type t)
let rec split_by_own loc p o1 o2 t =
if o1 && o2 then
split_type loc p t
else if o1 then
return (t,to_top_type t)
else if o2 then
return (to_top_type t,t)
else
return (to_top_type t,to_top_type t)
and split_type loc p t =
match t with
| Int _ -> return (t,t)
| Ref (t,_,n) ->
let%bind (o1,o2) = split_own loc p in
let%bind (t1,t2) = split_by_own loc (P.deref p) o1 o2 t in
return ((Ref (t1,o1,n), Ref (t2,o2,n)))
| Array (b,len_r,_,et) ->
let%bind (o1,o2) = split_own loc p in
let%bind (et1,et2) = split_by_own loc (P.elem p) o1 o2 et in
return (Array (b,len_r,o1,et1), Array (b,len_r,o2,et2))
| Tuple (b,tl) ->
let%bind split_tl = mtmap p (split_type loc) tl in
let (tl1,tl2) = List.split split_tl in
return (Tuple (b,tl1),Tuple (b,tl2))
| TVar id -> return (TVar id,TVar id)
| Mu (a,n,i,t) ->
let%bind (t1,t2) = split_type loc p t in
return (Mu (a,n,i,t1), Mu (a,n,i,t2))
let add_constraint gamma ?ante_ext path ante conseq nullity ctxt =
{ ctxt with
refinements = {
env = gamma;
ante = Option.fold ~some:(fun r -> And (r,ante)) ~none:ante ante_ext;
conseq;
nullity;
target = Some path
}::ctxt.refinements
}
let%lm add_type_implication ?(path_filter=(fun _ -> true)) ?(init_bind=[]) ?ante_ext gamma root t1_ t2_ ctxt_ =
let add_constraint_local gamma path c1 c2 nullity ctxt =
if path_filter path then
add_constraint ?ante_ext gamma path c1 c2 nullity ctxt
else
ctxt
in
let fresh_nvar = mk_counter () in
let rec impl_loop ~nullity gamma_curr path b1 b2 t1 t2 ctxt =
match t1,t2 with
| Int r1, Int r2 ->
let c1 = compile_refinement path b1 r1 in
let c2 = compile_refinement path b2 r2 in
add_constraint_local gamma_curr path c1 c2 nullity ctxt
| Array (ab1,len_r1,_,et1), Array (ab2,len_r2,_,et2) ->
let clen1 = compile_refinement (P.len path) b1 len_r1 in
let clen2 = compile_refinement (P.len path) b2 len_r2 in
let b1' = update_array_bind ab1 path b1 in
let b2' = update_array_bind ab2 path b2 in
let gamma' = update_array_env ~nullity gamma_curr path b1 len_r1 in
let ctxt' = add_constraint_local gamma_curr (P.len path) clen1 clen2 nullity ctxt in
impl_loop ~nullity gamma' (P.elem path) b1' b2' et1 et2 ctxt'
| Ref (t1',_,n), Ref (t2',_,_) ->
let n' =
match n with
| NLive -> `NLive
| NNull -> `NNull
| NUnk -> `NVar (fresh_nvar ())
in
impl_loop ~nullity:(n'::nullity) gamma_curr (P.deref path) b1 b2 t1' t2' ctxt
| Tuple (bt1,tl1), Tuple (bt2,tl2) ->
let b1' = update_tuple_bind bt1 path b1 in
let b2' = update_tuple_bind bt2 path b2 in
let env' = update_tuple_env ~nullity gamma_curr path b1' bt1 tl1 in
fold_left2i (fun i acc te1 te2 ->
impl_loop ~nullity env' (P.t_ind path i) b1' b2' te1 te2 acc
) ctxt tl1 tl2
| TVar _,TVar _ -> ctxt
| Mu (_,_,_,t1'), Mu (_,_,_,t2') ->
impl_loop ~nullity gamma_curr path b1 b2 t1' t2' ctxt
| t1,t2 -> type_mismatch t1 t2
in
impl_loop ~nullity:[] gamma root init_bind init_bind t1_ t2_ ctxt_
let add_var_type_implication dg var t1 t2 ctxt =
add_type_implication dg (P.var var) t1 t2 ctxt
let add_var_implication dg gamma var t ctxt =
add_var_type_implication dg var (SM.find var gamma) t ctxt
let ext_names = true
let mk_pred_name ~mu n target_var loc =
let c =
match loc with
| LCond i -> Printf.sprintf "join-%d" i
| LArg (f_name,a_name) -> Printf.sprintf "%s-%s-in" f_name a_name
| LReturn f_name -> Printf.sprintf "%s-ret" f_name
| LOutput (f_name, a_name) -> Printf.sprintf "%s-%s-out" f_name a_name
| LAlias i -> Printf.sprintf "shuf-%d" i
| LLet i -> Printf.sprintf "scope-%d" i
| LCall (i,a) -> Printf.sprintf "call-%d-%s-out" i a
| LNull i -> Printf.sprintf "ifnull-%d" i
| LFold i -> Printf.sprintf "fold-%d" i
| LRead i -> Printf.sprintf "read-%d" i
| LUpdate i -> Printf.sprintf "update-%d" i
| LMkArray i -> Printf.sprintf "mkarray-%d" i
in
if ext_names then
c ^ "-" ^ (Paths.to_z3_ident target_var) ^ (if mu then "-mu" else "")
else
c ^ "-" ^ (string_of_int n)
let alloc_pred ~mu ~ground ~loc ?(add_post_var=false) fv target_var ctxt =
let n = ctxt.v_counter in
let arity = (List.length fv) +
1 + !KCFA.cfa + (* 1 for nu and k for context *)
(if add_post_var then 1 else 0) (* add an extra variable for post *)
in
let p_name = mk_pred_name ~mu n target_var loc in
({ ctxt with
v_counter = n + 1;
pred_arity = StringMap.add p_name (ground,arity) ctxt.pred_arity
}, p_name)
let make_fresh_pred ~mu ~ground ~pred_vars:(fv,target) ~loc =
let%bind p = alloc_pred ~mu ~ground ~loc fv target in
return @@ Pred (p,fv)
let rec root_mu_path path = function
| MRoot -> path
| MProj (m_ap,i) -> P.t_ind (root_mu_path path m_ap) i
| MDeref m_ap -> P.deref @@ root_mu_path path m_ap
| MElem m_ap -> P.elem @@ root_mu_path path m_ap
| MLen m_ap -> P.len @@ root_mu_path path m_ap
let mu_bind_update_cb =
let apply_bind_set path {fv_map; pred_symbols} (ctxt,(bind_set,_)) =
let ps' = List.map (fun (m_ap,r) ->
let c_ap = root_mu_path path m_ap in
let new_r = Paths.PathMap.find_opt c_ap bind_set in
match new_r with
| Some r' -> (m_ap,r')
| None -> (m_ap, r)
) pred_symbols in
(ctxt,(Paths.PathMap.empty,Paths.PathMap.empty)),{fv_map; pred_symbols = ps' }
in
let generate_fv_map root _ {fv_map; _} (ctxt,(_,_)) =
let fv_manager = List.fold_left (fun acc (mu_ap,ll) ->
Paths.PathMap.add (root_mu_path root mu_ap) ll acc
) Paths.PathMap.empty fv_map
in
(ctxt,(Paths.PathMap.empty,fv_manager)),()
in
generate_fv_map,apply_bind_set
let initial_walk_state = (Paths.PathMap.empty,Paths.PathMap.empty)
let plift (f: ('a,'b,'c) Let_syntax.context_monad) ((ctxt,(ps,fv)) : ('b * ('d * 'e))) : ('c * ('d * 'e)) * 'a =
let (ctxt',res) = f ctxt in
(ctxt',(ps,fv)),res
type inference_refinement = (refine_ap list,refine_ap) refinement;;
type extra_context = (inference_refinement Paths.PathMap.t) * (int list * int list) Paths.PathMap.t
(* we very much assume fv does not contain (directly or through sym_map) path *)
let generate_refinement =
let get_path_set = Fun.id in
let return_with_path p r ctxt =
((ctxt,p),r)
in
fun ~ground ~loc ~pos (fv,_) path ->
(* easy or hard *)
if not pos.under_mu then
let%bind path_set = get_path_set in
let%bind r' = make_fresh_pred ~mu:false ~ground ~loc ~pred_vars:(fv,path) in
return_with_path path_set r'
else
(* let's generate a recursive refinement.
A recursive refinement has two parts: the local refinement (LR),
and the inductive refinement (IR). We'll call the tuple
names bound by the tuple containing the mu binder OV and those
inside the mu IV. We will call the variables in scope that are
in neither set FV.
Then the local refinement is a predicate over IV U FV. The inductive
refinement is over IV U OV. The total refinement, called R, is
the conjunction of these two predicates.
When unfolding a type such that
a fresh set of IV names called IV' are generated, we give the new
refinement under the mu as: R[IV -> IV'] /\ IR[OV -> IV, IV -> IV']
for a position with refinement R = IR /\ LR.
And now on with the show.
But WAIT! THERE'S MORE!
How do we know what OV, IV, and FV are? This is where the "path set"
comes into play (which needs to be renamed). When walking a type
for generation, provides a map fv_manager which maps a concrete
ap to the sets of ov and iv. This set is actually stored in
the mu binders as map from mu_aps (or relative APs if you prefer)
to these sets.
But wait! How do we know when unfolding what the IV is? That's where
the other component of the extra context comes into play, the (real)
path set (WHICH REALLY NEEDS TO BE RENAMED!)
If a new inductive invariant is generated, it is expected that the generated
invariant is stored into the path set. After completely walking a recursive
type, the mu_update_bind_cb will extract these predicates, map them BACK
to relative paths, and then merge the results with the current binder list.
(This "incremental" approach is necessitated by remove_var, which can piecewise
replace components of types).
All of the above complexity is handled by the mu_bind_update_cb which should be
passed into the walk_with_bindings_own as the mu_map argument.
*)
let%bind (path_set,fv_manager) : extra_context = get_path_set in
(* This will crash if the free variables haven't been bound for us *)
let (ov_list,iv_list) = Paths.PathMap.find path fv_manager in
let ov_set = IntSet.of_list ov_list in
let iv_set = IntSet.of_list iv_list in
let mu_vars = IntSet.union iv_set ov_set in
let local_pred_fv = List.filter (function
| `Sym i -> not (IntSet.mem i ov_set)
| _ -> true) fv
in
(* inductive pred free variables *)
let ind_pred_fv = List.filter (function
| `Sym i -> IntSet.mem i mu_vars
| _ -> false) fv
in
let%bind ind_pred = make_fresh_pred ~ground:true ~mu:true ~pred_vars:(ind_pred_fv,path) ~loc in
let%bind local_pred = make_fresh_pred ~ground:true ~mu:false ~pred_vars:(local_pred_fv,path) ~loc in
return_with_path (Paths.PathMap.add path ind_pred path_set,fv_manager) (And (ind_pred,local_pred))
let is_max_ref pos = (List.nth_opt pos.olist 0) = Some false
let generate_type (type a) ~ground ~os:(os: (a,bool) osrc) ~target ~loc ~fv_seed ?(bind=[]) ~fv_gen t context =
let o_map : a -> Paths.concr_ap -> (bool,'c,'c) Let_syntax.context_monad = match os with
| Prev -> (fun o _ -> return o)
| Ownership loc -> (fun _ p ->
plift @@ gen_own loc p
)
in
let ((ctxt,_),t') =
walk_with_bindings_own ~o_map:o_map ~mu_map:mu_bind_update_cb
(fun ~pos root (fv,bind) r ->
let is_top = is_max_ref pos in
if is_top then
return Top
else
let fv_add = fv_gen root fv r in
generate_refinement ~loc ~ground ~pos (fv_add,bind) root
) target (fv_seed,bind) t (context,initial_walk_state)
in
(ctxt,t')
let make_fresh_type (type a) ~os:(os: (a,bool) osrc) ?(ground=false) ~target_var ~loc ~fv ?(bind=[]) =
generate_type ~os ~ground ~target:target_var ~loc ~fv_seed:fv ~bind ~fv_gen:(fun _ fv _ ->
fv
)
let rec free_vars_contains (r: concr_refinement) v_set =
let root_pred ap = Paths.has_root_p (fun root -> SS.mem root v_set) ap in
let imm_is_var ri = match ri with RConst _ -> false | RAp ap -> root_pred ap in
match r with
| Pred (_,(pv,_))
| NamedPred (_,(pv,_))
| CtxtPred (_,_,(pv,_)) -> List.exists root_pred pv
| Relation { rel_op1 = op1; rel_op2 = op2; _ } ->
imm_is_var op2 || (match op1 with
RImm v -> imm_is_var v | Nu -> false)
| And (r1, r2) -> free_vars_contains r1 v_set || free_vars_contains r2 v_set
| _ -> false
let dump_env ?(msg) tev =
(match msg with
| Some m -> print_endline m;
| None -> ());
sexp_of_tenv tev |> Sexplib.Sexp.to_string_hum |> print_endline;
flush stdout
[@@ocaml.warning "-32"]
let predicate_vars kv =
List.fold_left (fun acc (k, t) ->
match t with
| Int _ -> (`Concr (P.var k))::acc
| Array _ -> (`Concr (P.len (P.var k)))::acc
| _ -> acc
) [] kv |> List.rev
let gamma_predicate_vars gamma =
SM.bindings gamma |> predicate_vars
let predicate_vars_m = mwith @@ (fun ctxt ->
SM.bindings ctxt.gamma |> predicate_vars)
let with_type t ctxt = (ctxt,t)
let map_tuple f b tl =
Tuple (b,List.map f tl)
let map_ref f t o n =
Ref (f t, o,n)
let rec unfold_once_gen unfolder = function
| Int r -> Int r
| Array (b,len_p,o,t) ->
Array (b,len_p,o,unfold_once_gen unfolder t)
| Ref (r, o,n) -> map_ref (unfold_once_gen unfolder) r o n
| Tuple (b,tl) ->
map_tuple (unfold_once_gen unfolder) b tl
| Mu (a,fv,i,t) -> unfolder a fv i t
| TVar _ -> assert false
let unfold_once = unfold_once_gen (unfold ~gen:fresh_tvar)
let rec make_null = function
| Ref (t,o,_) ->
Ref (make_null t,o,NNull)
| Tuple (b,tl) ->
Tuple (b,List.map make_null tl)
| Int r -> Int r
| TVar id -> TVar id
| Mu (a,b,c,t) -> Mu (a,b,c,make_null t)
| Array (a,lr,o,t) ->
Array (a,lr,o,make_null t)
(*
Generates a refinement type from a simple type.
This is nightmarishly complicated, but briefly, it works as follows.
* We generate a skeleton with no refinements, and just tuple positions (lift_to_skeleton)
* We then unfold any recursive types exactly once, generating for the
unfolded mu type:
- a substitution map, describing how tuple positions in the (now outer type) are mapped to their corresponding inner type positions
- a scope map, describing what inner vars/outer vars are in scope at each mu positions
- an application map, describing what predicates generated in the outer type should be pushed into the inner type
This is implemented by unfold_once, push_subst, and do_subst
* We then walk the type, generating the actual predicates. Under the mu, we restrict the free variables to those computed in the previous step. The predicates here are then stored as the inductive predicates for the mu binder. This is implemented by the first call to walk_with_bindings_own
* We finally push the outer type refinements into the inner types, substituting as appropriate. This is implemented with the second call to walk_with_bindings_own
*)
(* TODO: explain this more, because I will not remember how it works in 2 minutes *)
let lift_to_refinement loc ~nullity ~pred initial_path fv t =
let rec lift_to_skeleton t =
match t with
| `Int -> Int ()
| `Ref t' -> Ref (lift_to_skeleton t', (), nullity)
| `Tuple stl ->
let i_stl = List.mapi (fun i st -> (i,st)) stl in
let b = List.filter (fun (_,t) -> t = `Int) i_stl
|> List.map (fun (i,_) -> (fresh_tvar (),SProj i))
in
Tuple (b,List.map lift_to_skeleton stl)
| `Mu (id,t) ->
Mu ([],(),id,lift_to_skeleton t)
| `TVar id -> TVar id
| `Array et ->
let ind_var = fresh_tvar () in
let len_var = fresh_tvar () in
let b = {len = len_var; ind = ind_var } in
Array (b,(),(),lift_to_skeleton (et :> SimpleTypes.r_typ))
in
let do_subst outer_scope t =
let put_scope mu_ap inner_scope (sub_map,app_map,scope_map) =
(sub_map,app_map,(mu_ap,(outer_scope,inner_scope))::scope_map),()
in
let put_sub sub (sub_map,app_map,scope_map) =
(sub::sub_map,app_map,scope_map),()
in
let rec loop mu_ap local_scope = function
| TVar id -> return @@ TVar id
| Mu (_,_,_,_) -> raise @@ Incompleteness "Nested recursive types"
| Int () ->
let%bind () = put_scope mu_ap local_scope in
return @@ Int ()
| Tuple (b,tl) ->
let%bind b' = mmap (fun (k,v) ->
let k' = fresh_tvar () in
let%bind () = put_sub (k,k') in
return @@ (k',v)
) b in
let%bind tl' = mmapi (fun i t_e ->
let local_scope' = local_scope @ List.filter_map (fun (k,p) ->
match p with
| SProj j when i = j -> None
| _ -> Some k
) b'
in
loop (MProj (mu_ap, i)) local_scope' t_e
) tl
in
return @@ Tuple (b',tl')
| Array (_,(),(),t) ->
let%bind () = put_scope (MLen mu_ap) local_scope in
let ind_v = fresh_tvar () in
let len_v = fresh_tvar () in
let%bind t' = loop (MElem mu_ap) (local_scope @ [ ind_v; len_v ]) t in
return @@ Array ({len = len_v; ind = ind_v},(),(),t')
| Ref (t,(),n) ->
let%bind t' = loop (MDeref mu_ap) local_scope t in
return @@ Ref (t',(),n)
in
loop MRoot [] t
in
let put_app (abs,mu) app_map = (Paths.PathMap.add abs mu app_map,()) in
let rec push_subst mu_type (abs_path,mu_path) in_scope (t: (unit, unit, unit,nullity) RefinementTypes._typ) =
match t with
| TVar id ->
let%bind () = mutate (fun st -> ([],st,[])) in
let%bind unfolded = do_subst in_scope mu_type in
let%bind (sub_map,st',scope_map) = get_state in
let%bind () = put_state st' in
return @@ Mu (sub_map,scope_map,id,unfolded)
| Mu (_,_,_,_) -> raise @@ Incompleteness "NESTED! TYPES!"
| Ref (t,(),n) ->
let%bind t' = push_subst mu_type (P.deref abs_path, MDeref mu_path) in_scope t in
return @@ Ref (t',(),n)
| Int () ->
let%bind () = put_app (abs_path,mu_path) in
return @@ Int ()
| Array (b,(),(),t) ->
let%bind () = put_app (P.len abs_path,MLen mu_path) in
let%bind t' = push_subst mu_type (P.elem abs_path, MElem mu_path) (in_scope @ [b.ind;b.len]) t in
return @@ Array (b,(),(),t')
| Tuple (b,tl) ->
let in_scope' = in_scope @ (List.map fst b) in
let%bind tl' = mmapi (fun i t_e ->
push_subst mu_type (P.t_ind abs_path i, MProj (mu_path,i)) in_scope' t_e
) tl
in
return @@ Tuple (b,tl')
in
let rec unfold_once path =
function
| Int () ->
return @@ Int ()
| TVar _ ->
assert false
| Mu (l,(),_,t) ->
assert (l = []);
push_subst t (path,MRoot) [] t
| Array (a,b,c,t) ->
let%bind t' = unfold_once (P.elem path) t in
return @@ Array (a,b,c,t')
| Tuple (b,tl) ->
let%bind tl' = mmapi (fun i te ->
unfold_once (P.t_ind path i) te
) tl
in
return @@ Tuple (b,tl')
| Ref (t,(),n) ->
let%bind t' = unfold_once (P.deref path) t in
return @@ Ref (t',(),n)
in
let%bind ctxt = get_state in
let%bind () = put_state Paths.PathMap.empty in
let skeleton = lift_to_skeleton t in
let%bind unfolded = unfold_once initial_path skeleton in
let%bind app_map = get_state in
let%bind () = put_state (ctxt,None,None) in
let pre_mu path _ fv_list =
let%bind (ctxt,fv_map_emp,bound_map_emp) = get_state in
assert (fv_map_emp = None);
assert (bound_map_emp = None);
let fv_map = List.fold_left (fun acc (mu_ap,fv) ->
Paths.PathMap.add (root_mu_path path mu_ap) fv acc
) Paths.PathMap.empty fv_list
in
put_state (ctxt,Some fv_map,Some Paths.PathMap.empty)
in
let post_mu path fv_list =
let%bind (ctxt,_,bound_map_opt) = get_state in
let bound_map = Option.get bound_map_opt in
let pred_symbols = List.map (fun (mu_ap,_) ->
(mu_ap,Paths.PathMap.find (root_mu_path path mu_ap) bound_map)
) fv_list
in
let%bind () = put_state (ctxt,None,None) in
return @@ { pred_symbols; fv_map = fv_list }
in
let mu_map = pre_mu,post_mu in
let maybe_add pos path r (ctxt,fv_map,bound_map) =
if pos.under_mu then
(ctxt,fv_map,Option.map (P.PathMap.add path r) bound_map),r
else
(ctxt,fv_map,bound_map),r
in
let%bind t_inst = walk_with_bindings_own ~o_map:(fun () path (ctxt,f,p) ->
let (ctxt',o) = gen_own loc path ctxt in
(ctxt',f,p),o
) ~mu_map (fun ~pos root (fv_loc,_) () ->
if (List.nth_opt pos.olist 0) = Some false then
maybe_add pos root Top
else if pos.under_mu then
let%bind (ctxt,fv_map,bound_map) = get_state in
let (ol,il) = Paths.PathMap.find root @@ Option.get fv_map in
let rec_fv = IntSet.union (IntSet.of_list ol) (IntSet.of_list il) in
let fv_act = List.filter (function
| `Sym i -> IntSet.mem i rec_fv
| _ -> false
) fv_loc
in
let (ctxt',r) = pred ~mu:true ~pos fv_act root ctxt in
let bound_map' = Option.map (fun m ->
Paths.PathMap.add root r m
) bound_map in
let%bind () = put_state (ctxt',fv_map,bound_map') in
return r
else
let%bind (ctxt,fv_map,bound_map) = get_state in
let (ctxt',r) = pred ~mu:false ~pos fv_loc root ctxt in
let%bind () = put_state (ctxt',fv_map,bound_map) in
return r
) initial_path (fv,[]) unfolded
in
let%bind (ctxt',_,_) = get_state in
let apply_preds = fold_with_bindings (fun ~pos p _ r acc ->
if Paths.PathMap.mem p app_map then
let app_pred =
if pos.array <> [] then
`ArrayPred (pos.array,r)
else
`Pred r
in
(Paths.PathMap.find p app_map,app_pred)::acc
else
acc
) initial_path ([],[]) t_inst []
in
(* now walk the type one final time, pushing the local refinements
into the leaves *)
let setup_push path bind _ =
let subst_map = (List.map (fun (k,i) -> (k,`Sym i)) bind) in
let subst = function
| `ArrayPred (al,r) -> `ArrayPred(al,partial_subst subst_map r)
| `Pred r -> `Pred (partial_subst subst_map r)
in
let new_state = List.fold_left (fun acc (mu_ap,r) ->
Paths.PathMap.add (root_mu_path path mu_ap) (subst r) acc
) Paths.PathMap.empty apply_preds
in
let%bind _ = get_state in
put_state new_state
in
let post_push _ fv = return fv in
let (_,applied) =
walk_with_bindings_own ~o_map:(fun c _ -> return c)
~mu_map:(setup_push,post_push) (fun ~pos root _ r ->
let%swith to_conjoin = Paths.PathMap.find_opt root in
let some_comb = function
| `Pred r' -> And (r,r')
| `ArrayPred (al,r') ->
assert ((List.length al) = (List.length pos.array));
let subst_map = List.map2 (fun i j ->
[(i.len,`Sym j.len);
(i.ind,`Sym j.ind)]
) al pos.array |> List.concat
in
And (r,partial_subst subst_map r')
in
return @@ Option.fold ~none:r ~some:some_comb to_conjoin
) initial_path ([],[]) t_inst Paths.PathMap.empty
in
let%bind () = put_state ctxt' in
return applied
let lift_src_ap = function
| AVar v -> P.var v
| ADeref v -> P.deref (P.var v)
| AProj (v,i) -> P.t_ind (P.var v) i
| APtrProj (v,i) -> P.t_ind (P.deref (P.var v)) i
let get_ref_aps_gen proj =
fold_refinement_args
~rel_arg:(fun l ap -> ap::l)
~pred_arg:(fun l ap -> proj ap @ l) []
let get_ref_aps = get_ref_aps_gen fst
(* t2 is the type to be copied w.r.t mu, tuple binding, etc. *)
let merge_types ~gloc ~loc ~path ?(fv_filter=(fun _ -> true)) ?(e1_expl=[]) ?(bind_seed=([],[])) ?(unfold_t1=false) ?(unfold_t2=false) ~t1 ~t2 ctxt =
let filter_expl em = List.filter_map (function
| `Sym i -> if List.mem_assoc i em then
Some (List.assoc i em)
else None
| `Concr c -> Some c)
in
let counter = mk_counter () in
let gen_ref expl r =
let fv_set = get_ref_aps_gen Fun.id r |> filter_expl expl |> List.filter fv_filter |> Paths.PathSet.of_list in
let id = counter () in
(id,fv_set)
in
let nullity_counter = mk_counter () in
let rec to_type_template expl = function
| Int r ->
Int (gen_ref expl r)
| Ref (r,_,n) ->
Ref (to_type_template expl r, nullity_counter (), n)
| Mu (a,{pred_symbols;fv_map},i,t) ->
Mu (a,{
pred_symbols = List.map (fun (a,_) -> (a,Top)) pred_symbols; fv_map
},i,to_type_template expl t)
| TVar id -> TVar id
| Tuple (b,tl) ->
let expl' = List.fold_left (fun acc (i,p) ->
match p with
| SProj _ -> acc
| SVar v -> (i,P.var v)::acc
) expl b in
Tuple (b,List.map (to_type_template expl') tl)
| Array (b,l,_,t) ->
(* we don't use the id below, it's just for the types to work out *)
Array (b,gen_ref expl l,nullity_counter (),to_type_template expl t)
in
let t1_fv_templ = to_type_template e1_expl t1 in
let t2_fv_templ = to_type_template [] t2 in
(* IT'S AN N2 MINE *)
let merge_nullity id n1 n2 nmap =
IntMap.update id (fun bind ->
let join_n = join_nullity n1 n2 in
Some (Option.fold ~none:join_n ~some:(join_nullity join_n) bind)
) nmap
in
let merge_fvs (_,r1) (id,r2) map =
IntMap.update id (fun bind ->
Option.value ~default:(Paths.PathSet.empty) bind
|> Paths.PathSet.union @@ Paths.PathSet.union r1 r2
|> Option.some) map
in
let rec merge_loop t1_templ t2_templ ((fv_map,n_map) as map) =
match t1_templ, t2_templ with
| Int r1,Int r2 -> (merge_fvs r1 r2 fv_map,n_map)
| Mu (_,_,_,t1), Mu (_,_,_,t2) ->
merge_loop t1 t2 map
| Ref (t1,_,n1), Ref (t2,id,n2) -> merge_loop t1 t2 (fv_map, merge_nullity id n1 n2 n_map)
| Array (_,l1,_,t1), Array (_,l2,_,t2) ->
merge_loop t1 t2 (merge_fvs l1 l2 fv_map,n_map)
| Tuple (_,tl1), Tuple (_,tl2) ->
List.fold_left2 (fun acc t1 t2 ->
merge_loop t1 t2 acc
) map tl1 tl2
| TVar _,TVar _ -> map
| _,_ -> type_mismatch t1_templ t2_templ
in
let maybe_unfold f t =
if f then
unfold_once_gen (unfold_gen ~gen:fresh_tvar ~iref:ignore_iref ~rmap:(fun _ r -> r)) t
else
t
in
let (merge_map,n_map) =
merge_loop
(maybe_unfold unfold_t1 t1_fv_templ)
(maybe_unfold unfold_t2 t2_fv_templ)
(IntMap.empty,IntMap.empty)
in
let rec apply_nullity t =
match t with
| TVar id -> TVar id
| Int r -> Int r
| Array (a,lr,_,t) ->
Array (a,lr,(),apply_nullity t)
| Ref (t,id,_) ->
Ref (apply_nullity t,(),IntMap.find id n_map)
| Mu (ar,fv,i,t) ->
Mu (ar,fv,i,apply_nullity t)
| Tuple (b,tl) ->
Tuple (b,List.map apply_nullity tl)
in
let t2_templ_appn = apply_nullity t2_fv_templ in
let (fv_seed,bind) = bind_seed in
generate_type ~os:(Ownership gloc) ~ground:false ~target:path ~loc ~fv_seed ~bind ~fv_gen:(fun _ sym_fv (id,_) ->
(List.map (fun l -> `Concr l) @@ Paths.PathSet.elements @@ IntMap.find id merge_map) @ sym_fv
) t2_templ_appn ctxt
let inject_mu (get,put) (f1,f2) =
let inj1 =
(fun r ar fv ctxt ->
let (c1,rem) = get ctxt in
let (c1',ret) = f1 r ar fv c1 in
let c_ret = put c1' rem in
(c_ret,ret)
)
in
let inj2 =
(fun p fv ctxt ->
let (c1,rem) = get ctxt in
let (c1',ret) = f2 p fv c1 in
let c_ret = put c1' rem in
(c_ret,ret)
)
in
(inj1,inj2)
let remove_var_from_pred ~loc ~mk_fv ~oracle ~pos path (sym_vars,sym_val) r =
let curr_comp = compile_refinement path sym_val r in
if oracle curr_comp path then begin
assert ((List.nth_opt pos.olist 0) <> Some false);
let fv : refine_ap list = (mk_fv path curr_comp sym_vars sym_val) in
(* extract out the update set *)
let%bind (update_set : Paths.PathSet.t) = (fun (c1,c2) -> (c1,c2)) in
let%bind new_pred = generate_refinement ~ground:false ~loc ~pos (fv,sym_val) path in
let update_set' = Paths.PathSet.add path update_set in
let%bind () = mutate (fun c -> (c,update_set')) in
return new_pred
end else
return r
let remove_var_from_type ~loc ~oracle ~mk_fv root_var t context =
let staged = remove_var_from_pred ~mk_fv ~loc ~oracle in
let type_gen_state = ((context,initial_walk_state),Paths.PathSet.empty) in
let getput = (Fun.id,fun i j -> (i,j)) in
let ((ctxt',_),impl_paths),t' = walk_with_bindings_own ~mu_map:(inject_mu getput mu_bind_update_cb) ~o_map:(fun c _ -> return c) staged root_var ([],[]) t type_gen_state in
(ctxt',impl_paths),t'
let rec purge_tuple_fv to_remove = function
| Tuple (b,tl) ->
let tl' = List.map (purge_tuple_fv to_remove) tl in
let b' = List.filter (fun (_,p) ->
match p with
| SVar v -> not (SS.mem v to_remove)
| _ -> true
) b in
Tuple (b',tl')
| Ref (t,o,n) -> Ref (purge_tuple_fv to_remove t,o,n)
| Int r -> Int r
| TVar id -> TVar id
| Mu (n,i,a,t) -> Mu (n,i,a,purge_tuple_fv to_remove t)
| Array (a,len_r,o,t) ->
Array (a,len_r,o,purge_tuple_fv to_remove t)
let remove_var ~loc to_remove ctxt =
let curr_te = denote_gamma ctxt.gamma in
let module StrUF = UnionFind.Make(UnionFind.MakeHashed(struct
type t = Paths.concr_ap
let equal = (=)
end)) in
let eq_ref = StrUF.mk () in
let has_remove_root = Paths.has_root_p @@ Fun.flip SS.mem @@ to_remove in
let by_ref,ref_vars = SS.fold (fun var (by_var,all_ref) ->
let ref_in = fold_with_bindings (fun ~pos:_ root (_,sym_vals) r a ->
compile_refinement root sym_vals r
|> get_ref_aps
|> List.filter @@ Fun.negate has_remove_root
(* this is very important because it implies that any free variables not
rooted in predicate being removed can be safely referred to in the
refinements of other predicates *)
|> do_with @@ List.iter (fun l ->
if not (Paths.is_const_ap l) then
failwith @@ Printf.sprintf "Invariant broken: %s %s (%s)"
(Paths.to_z3_ident l)
(Paths.to_z3_ident root)
(string_of_refinement r)
else ()
)
|> do_with @@ List.iter @@ StrUF.register eq_ref
|> do_with @@ Std.fold_left_fst (fun a ->
do_with @@ StrUF.union eq_ref a)
|> List.fold_left (Fun.flip Paths.PathSet.add) a
) (P.var var) ([],[]) (SM.find var ctxt.gamma) Paths.PathSet.empty
in
(SM.add var ref_in by_var, Paths.PathSet.union all_ref ref_in)
) to_remove (StringMap.empty,Paths.PathSet.empty)
in
let add_vars = Paths.PathSet.fold (fun p ->
let key = StrUF.find eq_ref p in
Paths.PathMap.update
key (fun v -> Option.some @@ Option.fold
~none:(Paths.PathSet.singleton p)
~some:(Paths.PathSet.add p) v)
) ref_vars Paths.PathMap.empty
in
let removal_oracle = (fun r path ->
(Paths.PathSet.mem path ref_vars) || (free_vars_contains r to_remove)
) in
let free_var_manager root_var path curr_ref sym_vars sym_subst =
let outer_var_p = Fun.negate @@ Paths.has_root root_var in
let sym_vars_filtered = List.filter (function
| `Sym i ->
let concr = List.assoc i sym_subst in
not (has_remove_root concr)
| _ -> assert false
) sym_vars in
let all_free_vars = get_ref_aps curr_ref in