Source file zero_alloc.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
module ZA = Zero_alloc_utils
type check = Builtin_attributes.zero_alloc_check =
{ strict: bool;
opt: bool;
arity: int;
loc: Location.t;
custom_error_msg : string option;
}
type assume = Builtin_attributes.zero_alloc_assume =
{ strict: bool;
never_returns_normally: bool;
never_raises: bool;
arity: int;
loc: Location.t;
}
type const = Builtin_attributes.zero_alloc_attribute =
| Default_zero_alloc
| Ignore_assert_all
| Check of check
| Assume of assume
type desc = { strict : bool; opt : bool; custom_error_msg : string option; }
type var =
{ loc : Location.t;
arity : int;
mutable desc : desc option;
}
type t =
| Const of const
| Var of var
let debug_printer ppf t =
let head c = match c with
| Default_zero_alloc -> "Default"
| Ignore_assert_all -> "Ignore"
| Check _ -> "Check"
| Assume _ -> "Assume"
in
let pp_custom ppf c =
match c with
| None -> Format.fprintf ppf "None"
| Some msg -> Format.fprintf ppf "%S" msg
in
match t with
| Const c -> Format.fprintf ppf "Const %s" (head c)
| Var v ->
let print_desc ppf desc =
match desc with
| None -> Format.fprintf ppf "None"
| Some { strict; opt; custom_error_msg; } ->
Format.fprintf ppf "{ strict = %b; opt = %b; custom_error_message = %a}" strict opt
pp_custom custom_error_msg
in
Format.fprintf ppf "Var { arity = %d; desc = %a }" v.arity print_desc v.desc
type change = desc option * var
let undo_change (d, v) = v.desc <- d
let log_change = ref (fun _ -> ())
let set_change_log f = log_change := f
let create_const x = Const x
let create_var loc arity =
Var { loc; arity; desc = None }
let default = Const Default_zero_alloc
let ignore_assert_all = Const Ignore_assert_all
let get (t : t) =
match t with
| Const c -> c
| Var { loc; arity; desc } ->
match desc with
| None -> Default_zero_alloc
| Some { strict; opt; custom_error_msg; } ->
Check { loc; arity; strict; opt; custom_error_msg }
type error =
| Less_general of { missing_entirely : bool }
| Arity_mismatch of int * int
exception Error of error
let print_error ppf error =
let pr fmt = Format_doc.fprintf ppf fmt in
match error with
| Less_general { missing_entirely } ->
pr "The former provides a weaker \"zero_alloc\" guarantee than the latter.";
if missing_entirely then
pr "@ Hint: Add a \"zero_alloc\" attribute to the implementation."
| Arity_mismatch (n1, n2) ->
pr "zero_alloc arity mismatch:@ \
When using \"zero_alloc\" in a signature, the syntactic arity of@ \
the implementation must match the function type in the interface.@ \
Here the former is %d and the latter is %d."
n1 n2
let sub_const_const_exn za1 za2 =
let open Builtin_attributes in
let abstract_value za =
match za with
| Default_zero_alloc | Ignore_assert_all -> ZA.Assume_info.Value.top ()
| Check { strict; _ } ->
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally:false
~never_raises:false
| Assume { strict; never_returns_normally; never_raises; } ->
ZA.Assume_info.Value.of_annotation ~strict ~never_returns_normally
~never_raises
in
let v1 = abstract_value za1 in
let v2 = abstract_value za2 in
if not (ZA.Assume_info.Value.lessequal v1 v2) then
begin let missing_entirely =
match za1 with
| Default_zero_alloc -> true
| Ignore_assert_all | Check _ | Assume _ -> false
in
raise (Error (Less_general {missing_entirely}))
end;
begin match za1, za2 with
| Check { opt = opt1; _ }, Check { opt = opt2; _ } ->
if opt1 && not opt2 then
raise (Error (Less_general {missing_entirely = false}))
| (Check _ | Default_zero_alloc | Assume _ | Ignore_assert_all), _ -> ()
end;
let get_arity = function
| Check { arity; _ } | Assume { arity; _ } -> Some arity
| Default_zero_alloc | Ignore_assert_all -> None
in
match get_arity za1, get_arity za2 with
| Some arity1, Some arity2 ->
if not (arity1 = arity2) then
raise (Error (Arity_mismatch (arity1, arity2)))
| Some _, None -> ()
| None, Some _ ->
Misc.fatal_error "Zero_alloc: sub_const_exn"
| None, None -> ()
let sub_var_const_exn v c =
match v, c with
| _, (Default_zero_alloc | Ignore_assert_all | Assume _) -> assert false
| { arity = arity1; _ }, Check { arity = arity2; _ }
when arity1 <> arity2 ->
raise (Error (Arity_mismatch (arity1, arity2)))
| { desc = None; _ }, Check { strict; opt; custom_error_msg; } ->
!log_change (None, v);
v.desc <- Some { strict; opt; custom_error_msg }
| { desc = (Some { strict = strict1; opt = opt1; custom_error_msg = msg1; } as desc); _ },
Check { strict = strict2; opt = opt2; custom_error_msg = msg2 } ->
let strict = strict1 || strict2 in
let opt = opt1 && opt2 in
let custom_error_msg, msg_changed =
match msg1, msg2 with
| None, None -> msg1, false;
| None, Some _ -> msg2, true;
| Some _, None -> msg1, false;
| Some m1, Some m2 ->
let b = String.equal m1 m2 in
let msg =
if b then msg1 else Some (String.concat "\n" [m1; m2])
in
msg, not b
in
if strict <> strict1 || opt <> opt1 || msg_changed then begin
!log_change (desc, v);
v.desc <- Some { strict; opt; custom_error_msg; }
end
let sub_exn za1 za2 =
match za1, za2 with
| _, Var _ ->
if not (za1 == za2) then
Misc.fatal_error "zero_alloc: variable constraint"
| _, Const (Assume _) ->
Misc.fatal_error "zero_alloc: invalid constraint"
| _, (Const (Default_zero_alloc | Ignore_assert_all)) -> ()
| Var v, Const c -> sub_var_const_exn v c
| Const c1, Const c2 -> sub_const_const_exn c1 c2
let sub za1 za2 =
try
sub_exn za1 za2;
Ok ()
with
| Error e -> Result.Error e