vq / vlib / v / gen / c / autofree.v
368 lines · 352 sloc · 10.4 KB · 1a2d0e5b70d6dd1baaa60290f2446e9ece4324bf
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module c
5
6import strings
7import v.ast
8
9fn (g &Gen) needs_scope_cleanup() bool {
10 return g.is_autofree || g.pref.gc_mode == .boehm_leak
11}
12
13fn (mut g Gen) autofree_scope_vars(pos int, line_nr int, free_parent_scopes bool) {
14 if !g.needs_scope_cleanup() {
15 return
16 }
17 // g.writeln('// afsv pos=${pos} line_nr=${line_nr} freeparent_scopes=${free_parent_scopes}')
18 g.autofree_scope_vars_stop(pos, line_nr, free_parent_scopes, -1)
19}
20
21fn (mut g Gen) autofree_scope_vars_stop(pos int, line_nr int, free_parent_scopes bool, stop_pos int) {
22 if !g.needs_scope_cleanup() {
23 return
24 }
25 if g.is_builtin_mod {
26 // In `builtin` everything is freed manually.
27 return
28 }
29 if pos == -1 {
30 // TODO: why can pos be -1?
31 return
32 }
33 // eprintln('> free_scope_vars(${pos})')
34 scope := g.file.scope.innermost(pos)
35 // g.writeln('// scope start pos=${scope.start_pos} ')
36 if scope.start_pos == 0 {
37 // TODO: why can scope.pos be 0? (only outside fns?)
38 return
39 }
40 g.trace_autofree('// autofree_scope_vars(pos=${pos} line_nr=${line_nr} scope.pos=${scope.start_pos} scope.end_pos=${scope.end_pos})')
41 g.autofree_scope_vars2(scope, scope.start_pos, scope.end_pos, line_nr, free_parent_scopes,
42 stop_pos)
43}
44
45@[if trace_autofree ?]
46fn (mut g Gen) trace_autofree(line string) {
47 g.writeln(line)
48}
49
50//@[if print_autofree_vars ?]
51// fn (mut g Gen) print_autofree_var(var string, position string, comment string) {
52fn (mut g Gen) print_autofree_var(var ast.Var, comment string) {
53 if !g.pref.print_autofree_vars && g.pref.print_autofree_vars_in_fn == '' {
54 return
55 }
56 println('autofree: ${g.file.path}:${var.pos.line_nr}: skipping `${var.name}` in fn `${g.last_fn_c_name}`. ${comment}')
57}
58
59fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int, line_nr int, free_parent_scopes bool,
60 stop_pos int) {
61 if scope == unsafe { nil } {
62 return
63 }
64 g.trace_autofree('// scopeobjects.len == ${scope.objects.len}')
65 for _, obj in scope.objects {
66 match obj {
67 ast.Var {
68 g.trace_autofree('// var "${obj.name}" var.pos=${obj.pos.pos} var.line_nr=${obj.pos.line_nr}')
69 if obj.name in g.returned_var_names {
70 g.print_autofree_var(obj, 'returned from function')
71 g.trace_autofree('// skipping returned var')
72 continue
73 }
74 if obj.is_or {
75 // Skip vars inited with the `or {}`, since they are generated
76 // after the or block in C.
77 g.trace_autofree('// skipping `or {}` var "${obj.name}"')
78 continue
79 }
80 if obj.is_tmp {
81 // Skip for loop vars
82 g.print_autofree_var(obj, 'tmp var (loop?)')
83 g.trace_autofree('// skipping tmp var "${obj.name}"')
84 continue
85 }
86 if obj.is_inherited {
87 g.print_autofree_var(obj, 'inherited')
88 g.trace_autofree('// skipping inherited var "${obj.name}"')
89 continue
90 }
91 if obj.name in g.for_c_init_autofree_keep_vars {
92 g.print_autofree_var(obj, 'ForC init')
93 g.trace_autofree('// skipping ForC init var "${obj.name}"')
94 continue
95 }
96 // if var.typ == 0 {
97 // // TODO: why 0?
98 // continue
99 // }
100 // if v.pos.pos > end_pos {
101 if obj.pos.pos > end_pos
102 || (obj.pos.pos < start_pos && obj.pos.line_nr == line_nr)
103 || (end_pos < scope.end_pos && obj.expr is ast.IfExpr) {
104 // Do not free vars that were declared after this scope
105 continue
106 }
107 if obj.expr is ast.IfGuardExpr {
108 continue
109 }
110 if obj.expr is ast.UnsafeExpr && obj.expr.expr is ast.CallExpr
111 && (obj.expr.expr as ast.CallExpr).is_method {
112 if left_var := scope.objects[obj.expr.expr.left.str()] {
113 if func := g.table.find_method(g.table.final_sym(left_var.typ),
114 obj.expr.expr.name)
115 {
116 if func.attrs.contains('reused') && left_var is ast.Var
117 && left_var.expr is ast.CastExpr {
118 if left_var.expr.expr.is_literal() {
119 continue
120 }
121 }
122 }
123 }
124 }
125 g.autofree_variable(obj)
126 }
127 else {}
128 }
129 }
130 for g.autofree_scope_stmts.len > 0 {
131 g.write(g.autofree_scope_stmts.pop())
132 }
133 // Free all vars in parent scopes as well:
134 // ```
135 // s := ...
136 // if ... {
137 // s.free()
138 // return
139 // }
140 // ```
141 // if scope.parent != unsafe { nil } && line_nr > 0 {
142 if free_parent_scopes && scope.parent != unsafe { nil } && !scope.detached_from_parent
143 && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) {
144 g.trace_autofree('// af parent scope:')
145 g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos)
146 }
147}
148
149fn (mut g Gen) autofree_variable(v ast.Var) {
150 // filter out invalid variables
151 if v.typ == 0 {
152 return
153 }
154 sym := g.table.sym(v.typ)
155 // if v.name.contains('output2') {
156 if g.is_autofree {
157 // eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}')
158 }
159 // }
160 base_typ := v.typ.set_nr_muls(0).clear_option_and_result()
161 if g.type_has_unresolved_generic_parts(base_typ) {
162 g.print_autofree_var(v, 'unresolved generic type')
163 return
164 }
165 mut free_fn := g.styp(base_typ) + '_free'
166 if sym.kind == .array {
167 free_fn = g.get_free_method(base_typ)
168 g.autofree_var_call(free_fn, v)
169 return
170 }
171 if sym.kind == .map {
172 free_fn = g.get_free_method(base_typ)
173 g.autofree_var_call(free_fn, v)
174 return
175 }
176 if sym.kind == .string {
177 // Don't free simple string literals.
178 match v.expr {
179 ast.StringLiteral {
180 g.print_autofree_var(v, 'string literal')
181 g.trace_autofree('// str literal')
182 }
183 else {
184 // NOTE/TODO: assign_stmt multi returns variables have no expr
185 // since the type comes from the called fns return type
186 /*
187 f := v.name[0]
188 if
189 //!(f >= `a` && f <= `d`) {
190 //f != `c` {
191 v.name!='cvar_name' {
192 t := typeof(v.expr)
193 return '// other ' + t + '\n'
194 }
195 */
196 }
197 }
198
199 g.autofree_var_call('builtin__string_free', v)
200 return
201 }
202 if sym.is_builtin() {
203 free_fn = 'builtin__${free_fn}'
204 }
205 // Free user reference types
206 is_user_ref := v.typ.is_ptr() && sym.name.after('.')[0].is_capital()
207 // if g.pref.experimental && v.typ.is_ptr() && sym.name.after('.')[0].is_capital() {
208 if is_user_ref {
209 if g.pref.experimental {
210 g.autofree_var_call('free', v)
211 } else {
212 g.print_autofree_var(v, 'user reference type, use -experimental to autofree those')
213 }
214 }
215 if sym.has_method('free') {
216 g.autofree_var_call(free_fn, v)
217 }
218}
219
220fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
221 if v.is_arg {
222 // fn args should not be autofreed
223 return
224 }
225 if v.is_used && v.is_autofree_tmp {
226 // tmp expr vars do not need to be freed again here
227 return
228 }
229 if g.is_builtin_mod {
230 return
231 }
232 if !g.needs_scope_cleanup() {
233 return
234 }
235 // if v.is_autofree_tmp && !g.doing_autofree_tmp {
236 // return
237 // }
238 if v.name.contains('expr_write_string_1_') {
239 // TODO: remove this temporary hack
240 return
241 }
242 mut af := strings.new_builder(128)
243 if v.typ.is_ptr() && v.typ.idx() != ast.u8_type_idx {
244 if !v.is_auto_heap && !g.table.sym(v.typ).has_method('free') {
245 return
246 }
247 af.write_string('\t')
248 if v.typ.share() == .shared_t {
249 af.write_string(free_fn_name.replace_each(['__shared__', '']))
250 } else {
251 af.write_string(free_fn_name)
252 }
253 af.write_string('(')
254 if v.typ.has_flag(.option) {
255 base_type := g.base_type(v.typ)
256 af.write_string('(${base_type}*)')
257 }
258 if v.typ.share() == .shared_t {
259 af.write_string('&')
260 }
261 af.write_string(strings.repeat(`*`, v.typ.nr_muls() - 1)) // dereference if it is a pointer to a pointer
262 af.write_string(c_name(v.name))
263 if v.typ.share() == .shared_t {
264 af.write_string('->val')
265 }
266 if v.typ.has_flag(.option) {
267 af.write_string('.data)')
268 }
269
270 af.writeln('); // autofreed ptr var')
271 } else {
272 if v.typ == ast.error_type && !v.is_autofree_tmp {
273 return
274 }
275 if v.is_auto_heap {
276 af.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed heap var ${g.cur_mod.name} ${g.is_builtin_mod}')
277 } else if v.typ.has_flag(.option) {
278 base_type := g.base_type(v.typ)
279 af.writeln('\tif (${c_name(v.name)}.state != 2) {')
280 af.writeln('\t\t${free_fn_name}((${base_type}*)${c_name(v.name)}.data); // autofreed option var ${g.cur_mod.name} ${g.is_builtin_mod}')
281 af.writeln('\t}')
282 } else if v.typ.idx() != ast.u8_type_idx {
283 af.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var ${g.cur_mod.name} ${g.is_builtin_mod}')
284 }
285 }
286 g.autofree_scope_stmts << af.str()
287}
288
289fn (mut g Gen) detect_used_var_on_return(expr ast.Expr) {
290 match expr {
291 ast.Ident {
292 g.returned_var_names[expr.name] = true
293 }
294 ast.StructInit {
295 for field_expr in expr.init_fields {
296 g.detect_used_var_on_return(field_expr.expr)
297 }
298 }
299 else {}
300 }
301}
302
303fn selector_root_name(expr ast.SelectorExpr) ?string {
304 mut root_expr := expr.expr
305 for root_expr is ast.SelectorExpr {
306 root_expr = root_expr.expr
307 }
308 if root_expr is ast.Ident {
309 return root_expr.name
310 }
311 return none
312}
313
314fn (mut g Gen) selector_return_preserves_owner(expr ast.SelectorExpr) bool {
315 if expr.typ == 0 {
316 return false
317 }
318 if expr.typ.is_any_kind_of_pointer() {
319 return true
320 }
321 base_typ := expr.typ.set_nr_muls(0).clear_option_and_result()
322 if base_typ == 0 || g.type_has_unresolved_generic_parts(base_typ) {
323 return false
324 }
325 unwrapped_typ := g.unwrap_generic(base_typ)
326 sym := g.table.sym(unwrapped_typ)
327 if sym.has_method('free') {
328 return true
329 }
330 unaliased_typ :=
331 g.table.fully_unaliased_type(unwrapped_typ).set_nr_muls(0).clear_option_and_result()
332 if unaliased_typ == 0 || g.type_has_unresolved_generic_parts(unaliased_typ) {
333 return false
334 }
335 final_sym := g.table.final_sym(unaliased_typ)
336 return final_sym.kind in [.array, .map, .string, .struct, .sum_type, .interface]
337 || final_sym.has_method('free')
338}
339
340fn (mut g Gen) collect_returned_var_names(expr ast.Expr, mut names map[string]bool, mut selector_owner_names map[string]bool) {
341 match expr {
342 ast.Ident {
343 names[expr.name] = true
344 }
345 ast.SelectorExpr {
346 if g.selector_return_preserves_owner(expr) {
347 if root_name := selector_root_name(expr) {
348 selector_owner_names[root_name] = true
349 }
350 }
351 }
352 ast.StructInit {
353 for field_expr in expr.init_fields {
354 g.collect_returned_var_names(field_expr.expr, mut names, mut selector_owner_names)
355 }
356 }
357 else {}
358 }
359}
360
361fn (mut g Gen) returned_var_names_from_return(node ast.Return) (map[string]bool, map[string]bool) {
362 mut names := map[string]bool{}
363 mut selector_owner_names := map[string]bool{}
364 for expr in node.exprs {
365 g.collect_returned_var_names(expr, mut names, mut selector_owner_names)
366 }
367 return names, selector_owner_names
368}
369