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
(** Abstraction of control flows *)
open Mopsa_utils
open Lattice
open Context
open Token
open Alarm
open Callstack
(** {2 Flows} *)
type 'a flow = {
tmap : 'a TokenMap.t;
ctx : 'a ctx;
report: report;
}
(** A flow is a flow map augmented with an context *)
let bottom ctx report : 'a flow = {
tmap = TokenMap.bottom;
ctx;
report;
}
let top ctx : 'a flow = {
tmap = TokenMap.top;
ctx;
report = empty_report;
}
let singleton (ctx:'a Context.ctx) (tk:token) (env:'a) : 'a flow = {
tmap = TokenMap.singleton tk env;
ctx;
report = empty_report;
}
let is_bottom (lattice: 'a lattice) (flow: 'a flow) : bool =
TokenMap.is_bottom lattice flow.tmap
let is_top (lattice: 'a lattice) (flow: 'a flow) : bool =
TokenMap.is_top lattice flow.tmap
let is_empty (flow:'a flow) : bool =
TokenMap.is_empty flow.tmap && is_empty_report flow.report
let is_singleton (flow:'a flow) : bool =
TokenMap.is_singleton flow.tmap
let subset (lattice: 'a lattice) (flow1: 'a flow) (flow2: 'a flow) : bool =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
TokenMap.subset lattice ctx flow1.tmap flow2.tmap
let join (lattice: 'a lattice) (flow1: 'a flow) (flow2: 'a flow) : 'a flow =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
{
tmap = TokenMap.join lattice ctx flow1.tmap flow2.tmap;
ctx;
report = join_report flow1.report flow2.report;
}
let join_list lattice ~empty l =
match l with
| [] -> empty ()
| [f] -> f
| hd :: tl ->
let ctx = List.fold_left (fun acc f ->
most_recent_ctx acc f.ctx
) hd.ctx tl
in
{
tmap = TokenMap.join_list lattice ctx (List.map (function {tmap} -> tmap) l);
ctx;
report = List.fold_left (fun acc {report} -> join_report report acc) hd.report tl;
}
let meet (lattice: 'a lattice) (flow1: 'a flow) (flow2: 'a flow) : 'a flow =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
{
tmap = TokenMap.meet lattice ctx flow1.tmap flow2.tmap;
ctx;
report = meet_report flow1.report flow2.report;
}
let meet_list lattice ~empty l =
match l with
| [] -> empty
| [f] -> f
| hd :: tl ->
let ctx = List.fold_left (fun acc f ->
most_recent_ctx acc f.ctx
) hd.ctx tl
in
{
tmap = TokenMap.meet_list lattice ctx (List.map (function {tmap} -> tmap) l);
ctx;
report = List.fold_left (fun acc {report} -> meet_report report acc) hd.report tl;
}
let widen (lattice: 'a lattice) (flow1: 'a flow) (flow2: 'a flow) : 'a flow =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
{
tmap = TokenMap.widen lattice ctx flow1.tmap flow2.tmap;
ctx;
report = join_report flow1.report flow2.report;
}
let get_ctx flow = flow.ctx
let set_ctx ctx flow =
if ctx == flow.ctx then flow else {flow with ctx}
let map_ctx f flow = set_ctx (f @@ get_ctx flow) flow
let copy_ctx flow1 flow2 =
let ctx = get_ctx flow1 in
set_ctx ctx flow2
let get_report flow = flow.report
let set_report report flow = if report == flow.report then flow else { flow with report }
let copy_report src dst = set_report (join_report src.report dst.report) dst
let remove_report flow = { flow with report = empty_report }
let create ctx report tmap = {
tmap;
ctx;
report;
}
let get_callstack flow =
get_ctx flow |>
find_ctx callstack_ctx_key
let set_callstack cs flow =
set_ctx (
get_ctx flow |>
add_ctx Context.callstack_ctx_key cs
) flow
let push_callstack fname ?(uniq=fname) range flow =
set_callstack (
get_callstack flow |>
push_callstack fname ~uniq range
) flow
let pop_callstack flow =
let hd, cs = get_callstack flow |>
pop_callstack
in
hd, set_callstack cs flow
let bottom_from flow : 'a flow =
bottom (get_ctx flow) (get_report flow)
let print (pp: Print.printer -> 'a -> unit) printer flow =
TokenMap.print pp printer flow.tmap
let get (tk: token) (lattice: 'a lattice) (flow: 'a flow) : 'a =
TokenMap.get tk lattice flow.tmap
let set (tk: token) (a: 'a) (lattice:'a lattice) (flow: 'a flow) : 'a flow =
{ flow with tmap = TokenMap.set tk a lattice flow.tmap }
let set_bottom tk flow =
{ flow with tmap = TokenMap.remove tk flow.tmap }
let copy (tk1:token) (tk2:token) (lattice:'a lattice) (flow1:'a flow) (flow2:'a flow) : 'a flow =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
{
tmap = TokenMap.copy tk1 tk2 lattice flow1.tmap flow2.tmap;
ctx;
report = flow2.report;
}
let add (tk: token) (a: 'a) (lattice: 'a lattice) (flow: 'a flow) : 'a flow =
let a' = get tk lattice flow in
let aa = lattice.join flow.ctx a a' in
set tk aa lattice flow
let remove (tk: token) (flow: 'a flow) : 'a flow =
{ flow with tmap = TokenMap.remove tk flow.tmap }
let rename (tki: token) (tko: token) (lattice: 'a lattice) (flow: 'a flow) : 'a flow =
let ai = get tki lattice flow in
remove tki flow |>
add tko ai lattice
let mem (tk:token) (flow:'a flow) = TokenMap.mem tk flow.tmap
let filter (f: token -> 'a -> bool) (flow: 'a flow) : 'a flow =
{ flow with tmap = TokenMap.filter f flow.tmap }
let partition (f: token -> 'a -> bool) (flow: 'a flow) : 'a flow * 'a flow =
let l, r = TokenMap.partition f flow.tmap in
{ flow with tmap = l }, { flow with tmap = r }
let map (f: token -> 'a -> 'a) (flow: 'a flow) : 'a flow =
{ flow with tmap = TokenMap.map f flow.tmap }
let fold (f: 'b -> token -> 'a -> 'b) (init: 'b) (flow: 'a flow) : 'b =
TokenMap.fold f init flow.tmap
let map2zo
(f1: token -> 'a -> 'a)
(f2: token -> 'a -> 'a)
(f: token -> 'a -> 'a -> 'a)
(falarm: report -> report -> report)
(flow1: 'a flow) (flow2: 'a flow) : 'a flow =
let ctx = most_recent_ctx flow1.ctx flow2.ctx in
{
tmap = TokenMap.map2zo f1 f2 f flow1.tmap flow2.tmap;
ctx;
report = falarm flow1.report flow2.report;
}
let merge lattice ~merge_report pre (flow1,effect1) (flow2,effect2) =
let ctx = most_recent_ctx (get_ctx flow1) (get_ctx flow2) in
map2zo
(fun _ a1 -> lattice.bottom)
(fun _ a2 -> lattice.bottom)
(fun tk a1 a2 ->
match tk with
| T_cur ->
let p = get T_cur lattice pre in
lattice.merge p (a1,effect1) (a2,effect2)
| _ -> lattice.meet ctx a1 a2
) merge_report flow1 flow2
let get_token_map flow = flow.tmap
let add_alarm ?(force=false) ?(warning=false) alarm lattice flow =
let cur = get T_cur lattice flow in
if not force
&& lattice.is_bottom cur
then { flow with report = Alarm.add_diagnostic (Alarm.mk_unreachable_diagnostic (check_of_alarm alarm) alarm.alarm_callstack alarm.alarm_range) flow.report }
else { flow with report = Alarm.add_alarm ~warning alarm flow.report }
let raise_alarm ?(force=false) ?(bottom=false) ?(warning=false) alarm lattice flow =
let flow = add_alarm ~force ~warning alarm lattice flow in
if not bottom
then flow
else set T_cur lattice.bottom lattice flow
let add_diagnostic diag flow =
let report = Alarm.add_diagnostic diag flow.report in
if report == flow.report then flow else { flow with report }
let add_safe_check check range flow =
let cs = get_callstack flow in
add_diagnostic (Alarm.mk_safe_diagnostic check cs range) flow
let add_unreachable_check check range flow =
let cs = get_callstack flow in
add_diagnostic (Alarm.mk_unreachable_diagnostic check cs range) flow
let add_warning_check check range flow =
let cs = get_callstack flow in
add_diagnostic (Alarm.mk_warning_diagnostic check cs range) flow
let add_assumption assumption flow =
set_report (Alarm.add_assumption assumption flow.report) flow
let add_global_assumption assumption_kind flow =
set_report (Alarm.add_global_assumption assumption_kind flow.report) flow
let add_local_assumption assumption_kind range flow =
set_report (Alarm.add_local_assumption assumption_kind range flow.report) flow