v2 / vlib / v / markused / walker.v
3715 lines · 3618 sloc · 106.75 KB · 7596b52c1db0016d2c9a3460d58a957bd7160b07
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
3module markused
4
5// Walk the entire program starting at fn main and marks used (called) functions.
6// Unused functions can be safely skipped by the backends to save CPU time and space.
7import v.ast
8import v.pref
9
10struct MethodFkeyCacheEntry {
11 fkey string
12 receiver ast.Type
13}
14
15struct SpecializedVarTypeCacheEntry {
16 typ ast.Type
17}
18
19pub struct Walker {
20pub mut:
21 table &ast.Table = unsafe { nil }
22 features &ast.UsedFeatures = unsafe { nil }
23 used_fns map[string]bool // used_fns['println'] == true
24 trace_enabled bool
25 used_consts map[string]bool // used_consts['os.args'] == true
26 used_globals map[string]bool
27 used_fields map[string]bool
28 used_structs map[string]bool
29 used_types map[ast.Type]bool
30 used_syms map[int]bool
31 used_arr_method map[string]bool
32 used_map_method map[string]bool
33 used_none int // _option_none
34 used_option int // _option_ok
35 used_result int // _result_ok
36 used_panic int // option/result propagation
37 used_closures int // fn [x] (){}, and `instance.method` used in an expression
38 pref &pref.Preferences = unsafe { nil }
39mut:
40 all_fns map[string]ast.FnDecl
41 generic_fns []&ast.FnDecl
42 all_consts map[string]ast.ConstField
43 all_globals map[string]ast.GlobalField
44 all_fields map[string]ast.StructField
45 all_decltypes map[string]ast.TypeDecl
46 all_structs map[string]ast.StructDecl
47
48 cur_fn string
49 cur_fn_concrete_types []ast.Type
50 level int
51 is_builtin_mod bool
52 is_direct_array_access bool
53 inside_in_op bool
54 inside_comptime int
55 inside_comptime_if int
56 used_fn_generic_types map[string][][]ast.Type
57 used_fn_generic_type_keys map[string]bool
58 walked_fn_generic_types map[string][][]ast.Type
59 walked_fn_generic_type_keys map[string]bool
60 expanding_fn_generic_types map[string]bool
61 keep_all_fn_generic_types map[string]bool
62 json2_encode_field_helpers map[string]bool
63 method_fkey_cache map[string]MethodFkeyCacheEntry
64 specialized_var_type_cache map[string]SpecializedVarTypeCacheEntry
65
66 // dependencies finding flags
67 uses_atomic bool // has atomic
68 uses_array bool // has array
69 uses_array_sumtype bool // has array on sumtype init
70 uses_channel bool // has chan dep
71 uses_lock bool // has mutex dep
72 uses_ct_fields bool // $for .fields
73 uses_ct_methods bool // $for .methods
74 uses_ct_params bool // $for .params
75 uses_ct_values bool // $for .values
76 uses_ct_variants bool // $for .variants
77 uses_ct_attribute bool // $for .attributes
78 uses_external_type bool
79 uses_err_block bool // or { err var }
80 uses_asserts bool // assert
81 uses_map_update bool // has {...expr}
82 uses_debugger bool // has debugger;
83 uses_mem_align bool // @[aligned:N] for structs
84 uses_eq bool // has == op
85 uses_interp bool // string interpolation
86 uses_guard bool
87 uses_orm bool
88 uses_str map[ast.Type]bool // has .str() calls, and for which types
89 uses_free map[ast.Type]bool // has .free() calls, and for which types
90 uses_spawn bool
91 uses_dump bool
92 uses_memdup bool // sumtype cast and &Struct{}
93 uses_arr_void bool // auto arr methods
94 uses_index bool // var[k]
95 uses_index_check bool // var[k] or { }
96 uses_arr_range_index bool // arr[i..j]
97 uses_str_range_index bool // str[i..j]
98 uses_range_index_check bool // var[i..j] or { }
99 uses_arr_range_index_gated bool
100 uses_str_range_index_gated bool
101 uses_str_index bool // string[k]
102 uses_str_index_check bool // string[k] or { }
103 uses_str_range bool // string[a..b]
104 uses_str_literal bool
105 uses_fixed_arr_int bool // fixed_arr[k]
106 uses_append bool // var << item
107 uses_map_setter bool
108 uses_map_getter bool
109 uses_arr_setter bool
110 uses_arr_getter bool
111 uses_arr_clone bool
112 uses_arr_sorted bool
113 uses_type_name bool // sum_type.type_name()
114}
115
116pub fn Walker.new(params Walker) &Walker {
117 mut new_walker := &Walker{
118 ...params
119 }
120 new_walker.features = params.table.used_features
121 new_walker.method_fkey_cache = map[string]MethodFkeyCacheEntry{}
122 new_walker.specialized_var_type_cache = map[string]SpecializedVarTypeCacheEntry{}
123 return new_walker
124}
125
126@[inline]
127fn (mut w Walker) mark_fn_as_used(fkey string) {
128 $if trace_skip_unused_marked ? {
129 eprintln(' fn > |${fkey}|')
130 }
131 w.used_fns[fkey] = true
132}
133
134fn (w &Walker) fn_generic_names(node ast.FnDecl) []string {
135 if !node.is_method {
136 return node.generic_names
137 }
138 mut generic_names := []string{}
139 receiver_sym := w.table.sym(node.receiver.typ)
140 match receiver_sym.info {
141 ast.Struct {
142 generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
143 }
144 ast.Interface {
145 generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
146 }
147 ast.SumType {
148 generic_names << w.table.get_generic_names(receiver_sym.info.generic_types)
149 }
150 else {}
151 }
152
153 // Only add method-level generic names that aren't already from the receiver
154 // (V puts inherited receiver generics into node.generic_names too).
155 for gn in node.generic_names {
156 if gn !in generic_names {
157 generic_names << gn
158 }
159 }
160 return generic_names
161}
162
163fn (mut w Walker) resolve_current_generic_type(typ ast.Type) ast.Type {
164 if typ == 0 || !typ.has_flag(.generic) || w.cur_fn == '' || w.cur_fn_concrete_types.len == 0 {
165 return typ
166 }
167 cur_fn := w.all_fns[w.cur_fn] or { return typ }
168 generic_names := w.fn_generic_names(cur_fn)
169 if generic_names.len == 0 || generic_names.len != w.cur_fn_concrete_types.len {
170 return typ
171 }
172 return w.table.convert_generic_type(typ, generic_names, w.cur_fn_concrete_types) or { typ }
173}
174
175fn (mut w Walker) resolve_current_concrete_types(types []ast.Type) []ast.Type {
176 if types.len == 0 {
177 return []
178 }
179 mut concrete_types := []ast.Type{cap: types.len}
180 for typ in types {
181 concrete_types << w.resolve_current_generic_type(typ)
182 }
183 if concrete_types.any(it.has_flag(.generic)) {
184 return []
185 }
186 return concrete_types
187}
188
189fn (w &Walker) current_generic_context() ([]string, []ast.Type) {
190 if w.cur_fn == '' {
191 return []string{}, []ast.Type{}
192 }
193 if w.cur_fn.contains('_T_') {
194 return w.specialized_generic_context_for(w.cur_fn)
195 }
196 if w.cur_fn_concrete_types.len == 0 {
197 return []string{}, []ast.Type{}
198 }
199 cur_fn := w.all_fns[w.cur_fn] or { return []string{}, []ast.Type{} }
200 generic_names := w.fn_generic_names(cur_fn)
201 if generic_names.len == 0 || generic_names.len != w.cur_fn_concrete_types.len {
202 return []string{}, []ast.Type{}
203 }
204 return generic_names, w.cur_fn_concrete_types.clone()
205}
206
207fn (w &Walker) current_generic_type_by_name(name string) ast.Type {
208 generic_names, concrete_types := w.current_generic_context()
209 if generic_names.len == 0 || generic_names.len != concrete_types.len {
210 return ast.no_type
211 }
212 idx := generic_names.index(name)
213 if idx < 0 || idx >= concrete_types.len {
214 return ast.no_type
215 }
216 return concrete_types[idx]
217}
218
219fn (w &Walker) has_sumtype_generic_context(types []ast.Type) bool {
220 for typ in types {
221 if typ == 0 {
222 continue
223 }
224 if w.table.final_sym(w.table.unaliased_type(typ)).kind == .sum_type {
225 return true
226 }
227 }
228 return false
229}
230
231fn (w &Walker) sumtype_variant_concrete_types(types []ast.Type) [][]ast.Type {
232 if types.len != 1 {
233 return [][]ast.Type{}
234 }
235 sumtype := w.table.unaliased_type(types[0])
236 sumtype_sym := w.table.final_sym(sumtype)
237 if sumtype_sym.kind != .sum_type || sumtype_sym.info !is ast.SumType {
238 return [][]ast.Type{}
239 }
240 mut concrete_types := [][]ast.Type{}
241 for variant in (sumtype_sym.info as ast.SumType).variants {
242 concrete_types << [variant]
243 }
244 return concrete_types
245}
246
247fn (mut w Walker) trusted_source_concrete_types(node ast.CallExpr, receiver_typ ast.Type) []ast.Type {
248 if node.raw_concrete_types.len > 0 {
249 return w.resolve_current_concrete_types(node.raw_concrete_types)
250 }
251 if node.concrete_types.len == 0 {
252 return []ast.Type{}
253 }
254 resolved_source_concrete_types := w.resolve_current_concrete_types(node.concrete_types)
255 if resolved_source_concrete_types.len == 0 {
256 return []ast.Type{}
257 }
258 caller_generic_names, _ := w.current_generic_context()
259 if caller_generic_names.len == 0 || w.inside_comptime_if > 0 {
260 return resolved_source_concrete_types
261 }
262 mut receiver_concrete_types := w.receiver_concrete_types(receiver_typ)
263 if receiver_concrete_types.len == 0 && node.receiver_concrete_type != 0
264 && node.receiver_concrete_type != receiver_typ {
265 receiver_concrete_types = w.receiver_concrete_types(node.receiver_concrete_type)
266 }
267 if receiver_concrete_types.len > 0 && receiver_concrete_types == resolved_source_concrete_types {
268 return resolved_source_concrete_types
269 }
270 return []ast.Type{}
271}
272
273fn (mut w Walker) mark_json2_optional_field_helpers(concrete_typ ast.Type) {
274 concrete_sym := w.table.final_sym(w.table.unaliased_type(concrete_typ))
275 if concrete_sym.kind != .struct || concrete_sym.info !is ast.Struct {
276 return
277 }
278 struct_info := concrete_sym.info as ast.Struct
279 if mut create_value_from_optional_fn := w.all_fns['x.json2.create_value_from_optional'] {
280 for field in struct_info.fields {
281 if field.typ.has_flag(.option) {
282 w.fn_decl_with_concrete_types(mut create_value_from_optional_fn, [
283 field.typ.clear_flag(.option),
284 ])
285 }
286 }
287 }
288}
289
290fn (mut w Walker) remember_generic_fn_instance(fkey string, concrete_types []ast.Type) {
291 resolved_concrete_types := w.resolve_current_concrete_types(concrete_types)
292 if resolved_concrete_types.len == 0 || resolved_concrete_types.any(it.has_flag(.generic)) {
293 return
294 }
295 w.record_used_fn_generic_types(fkey, resolved_concrete_types)
296 w.mark_fn_as_used(fkey)
297}
298
299fn (mut w Walker) mark_json2_encode_field_helpers(receiver_typ ast.Type, concrete_typ ast.Type) {
300 helper_key := '${int(receiver_typ)}:${int(concrete_typ)}'
301 if w.json2_encode_field_helpers[helper_key] {
302 return
303 }
304 w.json2_encode_field_helpers[helper_key] = true
305 concrete_sym := w.table.final_sym(w.table.unaliased_type(concrete_typ))
306 if concrete_sym.kind != .struct || concrete_sym.info !is ast.Struct {
307 return
308 }
309 struct_info := concrete_sym.info as ast.Struct
310 encode_struct_field_value_fkey, _ := w.resolve_method_fkey_for_type(receiver_typ,
311 'encode_struct_field_value')
312 mut encode_struct_field_value_fn := if encode_struct_field_value_fkey != '' {
313 w.all_fns[encode_struct_field_value_fkey] or { ast.FnDecl{} }
314 } else {
315 ast.FnDecl{}
316 }
317 mut struct_field_is_none_fn := w.all_fns['x.json2.struct_field_is_none'] or { ast.FnDecl{} }
318 mut struct_field_is_nil_fn := w.all_fns['x.json2.struct_field_is_nil'] or { ast.FnDecl{} }
319 mut check_not_empty_fn := w.all_fns['x.json2.check_not_empty'] or { ast.FnDecl{} }
320 for field in struct_info.fields {
321 if field.is_embed {
322 continue
323 }
324 if encode_struct_field_value_fn.name != '' {
325 w.remember_generic_fn_instance(encode_struct_field_value_fn.fkey(), [field.typ])
326 }
327 if struct_field_is_none_fn.name != '' && field.typ.has_flag(.option) {
328 w.remember_generic_fn_instance(struct_field_is_none_fn.fkey(), [field.typ])
329 }
330 if struct_field_is_nil_fn.name != '' && field.typ.nr_muls() > 0 {
331 w.remember_generic_fn_instance(struct_field_is_nil_fn.fkey(), [field.typ])
332 }
333 if check_not_empty_fn.name != '' {
334 w.remember_generic_fn_instance(check_not_empty_fn.fkey(), [field.typ])
335 }
336 }
337}
338
339fn (mut w Walker) resolve_comptime_condition_type(expr ast.Expr) ast.Type {
340 match expr {
341 ast.Ident {
342 if expr.obj.typ != 0 {
343 resolved_type := w.resolve_current_specialized_type(expr.obj.typ)
344 if resolved_type != 0 {
345 return resolved_type
346 }
347 }
348 resolved_type := w.resolve_current_specialized_var_type(expr.name)
349 if resolved_type != 0 {
350 return resolved_type
351 }
352 generic_type := w.current_generic_type_by_name(expr.name)
353 if generic_type != 0 {
354 return generic_type
355 }
356 }
357 ast.SelectorExpr {
358 if expr.expr is ast.Ident {
359 mut base_type := ast.no_type
360 if expr.expr.obj.typ != 0 {
361 base_type = w.resolve_current_specialized_type(expr.expr.obj.typ)
362 }
363 if base_type == 0 {
364 base_type = w.resolve_current_specialized_var_type(expr.expr.name)
365 }
366 if base_type == 0 {
367 base_type = w.current_generic_type_by_name(expr.expr.name)
368 }
369 if base_type == 0 {
370 return ast.no_type
371 }
372 return if expr.field_name == 'unaliased_typ' {
373 w.table.unaliased_type(base_type)
374 } else if expr.field_name == 'typ' {
375 base_type
376 } else {
377 ast.no_type
378 }
379 }
380 }
381 ast.TypeNode {
382 if expr.typ.has_flag(.generic) {
383 generic_name := w.table.sym(expr.typ).name
384 if generic_name != '' {
385 generic_type := w.current_generic_type_by_name(generic_name)
386 if generic_type != 0 {
387 return generic_type
388 }
389 }
390 }
391 return w.resolve_current_specialized_type(expr.typ)
392 }
393 else {}
394 }
395
396 return ast.no_type
397}
398
399fn (w &Walker) comptime_type_matches(left_type ast.Type, right ast.Expr) ?bool {
400 if left_type == 0 {
401 return none
402 }
403 match right {
404 ast.ComptimeType {
405 sym := w.table.sym(left_type)
406 return match right.kind {
407 .array { sym.info is ast.Array || sym.info is ast.ArrayFixed }
408 .array_dynamic { sym.info is ast.Array }
409 .array_fixed { sym.info is ast.ArrayFixed }
410 .iface { sym.info is ast.Interface }
411 .map { sym.info is ast.Map }
412 .struct { sym.info is ast.Struct }
413 .enum { sym.info is ast.Enum }
414 .sum_type { sym.info is ast.SumType }
415 .alias { sym.info is ast.Alias }
416 .function { sym.kind == .function }
417 .option { left_type.has_flag(.option) }
418 .shared { left_type.has_flag(.shared_f) }
419 .int { left_type.is_int() }
420 .float { left_type.is_float() }
421 .string { left_type.is_string() }
422 .pointer { left_type.is_ptr() }
423 .voidptr { w.table.unaliased_type(left_type) == ast.voidptr_type }
424 else { none }
425 }
426 }
427 ast.TypeNode {
428 sym := w.table.sym(right.typ)
429 if sym.info is ast.Interface {
430 return w.table.does_type_implement_interface(left_type, right.typ)
431 }
432 return left_type == right.typ
433 }
434 else {
435 return none
436 }
437 }
438}
439
440fn (mut w Walker) comptime_condition_is_true(expr ast.Expr) ?bool {
441 match expr {
442 ast.ParExpr {
443 return w.comptime_condition_is_true(expr.expr)
444 }
445 ast.InfixExpr {
446 match expr.op {
447 .key_is {
448 left_type := w.resolve_comptime_condition_type(expr.left)
449 if left_type == 0 {
450 return none
451 }
452 return w.comptime_type_matches(left_type, expr.right)
453 }
454 .and {
455 left := w.comptime_condition_is_true(expr.left)?
456 right := w.comptime_condition_is_true(expr.right)?
457 return left && right
458 }
459 .logical_or {
460 left := w.comptime_condition_is_true(expr.left)?
461 right := w.comptime_condition_is_true(expr.right)?
462 return left || right
463 }
464 else {
465 return none
466 }
467 }
468 }
469 ast.NodeError {
470 return true
471 }
472 else {
473 return none
474 }
475 }
476}
477
478fn (mut w Walker) resolve_comptime_if_branch(node ast.IfExpr) ?ast.IfBranch {
479 if !node.is_comptime {
480 return none
481 }
482 for i, branch in node.branches {
483 if node.has_else && i == node.branches.len - 1 {
484 return branch
485 }
486 cond_result := w.comptime_condition_is_true(branch.cond) or { return none }
487 if cond_result {
488 return branch
489 }
490 }
491 return none
492}
493
494fn (mut w Walker) record_used_fn_generic_types(fkey string, concrete_types []ast.Type) {
495 if concrete_types.len == 0 || concrete_types.any(it.has_flag(.generic)) {
496 return
497 }
498 key := w.fn_generic_types_key(fkey, concrete_types)
499 if w.used_fn_generic_type_keys[key] {
500 return
501 }
502 w.used_fn_generic_type_keys[key] = true
503 w.used_fn_generic_types[fkey] << concrete_types.clone()
504}
505
506fn (w &Walker) fn_generic_types_key(fkey string, concrete_types []ast.Type) string {
507 mut parts := []string{cap: concrete_types.len}
508 for typ in concrete_types {
509 parts << w.table.type_to_str(typ)
510 }
511 return '${fkey}:${parts.join('|')}'
512}
513
514fn (mut w Walker) record_walked_fn_generic_types(fkey string, concrete_types []ast.Type) bool {
515 key := w.fn_generic_types_key(fkey, concrete_types)
516 if w.walked_fn_generic_type_keys[key] {
517 return false
518 }
519 w.walked_fn_generic_type_keys[key] = true
520 w.walked_fn_generic_types[fkey] << concrete_types.clone()
521 return true
522}
523
524@[inline]
525pub fn (mut w Walker) mark_builtin_array_method_as_used(method_name string) {
526 w.mark_builtin_type_method_as_used('${ast.array_type_idx}.${method_name}',
527 '${int(ast.array_type.ref())}.${method_name}')
528}
529
530@[inline]
531pub fn (mut w Walker) mark_builtin_map_method_as_used(method_name string) {
532 w.mark_builtin_type_method_as_used('${ast.map_type_idx}.${method_name}',
533 '${int(ast.map_type.ref())}.${method_name}')
534}
535
536pub fn (mut w Walker) mark_builtin_type_method_as_used(k string, rk string) {
537 if mut cfn := w.all_fns[k] {
538 w.fn_decl(mut cfn)
539 } else if mut cfn := w.all_fns[rk] {
540 w.fn_decl(mut cfn)
541 }
542}
543
544pub fn (mut w Walker) mark_const_as_used(ckey string) {
545 $if trace_skip_unused_marked ? {
546 eprintln(' const > |${ckey}|')
547 }
548 if w.used_consts[ckey] {
549 return
550 }
551 w.used_consts[ckey] = true
552 cfield := w.all_consts[ckey] or { return }
553 w.expr(cfield.expr)
554 w.mark_by_type(cfield.typ)
555}
556
557pub fn (mut w Walker) mark_global_as_used(ckey string) {
558 $if trace_skip_unused_marked ? {
559 eprintln(' global > |${ckey}|')
560 }
561 if w.used_globals[ckey] {
562 return
563 }
564 w.used_globals[ckey] = true
565 gfield := w.all_globals[ckey] or { return }
566 w.table.used_features.used_attr_weak = w.table.used_features.used_attr_weak || gfield.is_weak
567 w.table.used_features.used_attr_hidden = w.table.used_features.used_attr_hidden
568 || gfield.is_hidden || gfield.is_hidden
569 w.expr(gfield.expr)
570 if !gfield.has_expr {
571 w.mark_by_type(gfield.typ)
572 }
573}
574
575pub fn (mut w Walker) mark_struct_field_default_expr_as_used(sfkey string) {
576 if w.used_fields[sfkey] {
577 return
578 }
579 w.used_fields[sfkey] = true
580 sfield := w.all_fields[sfkey] or { return }
581 w.expr(sfield.default_expr)
582}
583
584pub fn (mut w Walker) mark_markused_fns() {
585 for _, mut func in w.all_fns {
586 // @[export]
587 // @[markused]
588 if func.is_exported || func.is_markused {
589 $if trace_skip_unused_exported_fns ? {
590 if func.is_exported {
591 println('>>>> walking exported func: ${func.name} ...')
592 }
593 if func.is_markused {
594 println('>>>> walking markused func: ${func.name} ...')
595 }
596 }
597 w.fn_decl(mut func)
598 continue
599 }
600 // veb actions
601 if func.return_type == w.table.veb_res_idx_cache {
602 $if trace_skip_veb_actions ? {
603 println('>>>> walking veb action func: ${func.name} ...')
604 }
605 w.fn_decl(mut func)
606 }
607 }
608}
609
610pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) {
611 if w.trace_enabled {
612 eprintln('>>>>>>> ROOT ${all_fn_root_names}')
613 }
614 for fn_name in all_fn_root_names {
615 if fn_name !in w.used_fns {
616 $if trace_skip_unused_roots ? {
617 println('>>>> walking root func: ${fn_name} ...')
618 }
619 unsafe { w.fn_decl(mut w.all_fns[fn_name]) }
620 }
621 }
622}
623
624pub fn (mut w Walker) mark_generic_fn_instances() {
625 for generic_fn in w.generic_fns {
626 if generic_fn.generic_names.len == 0 {
627 continue
628 }
629 if w.table.final_sym(generic_fn.return_type).info is ast.FnType
630 || generic_fn.params.any(w.table.final_sym(it.typ).info is ast.FnType) {
631 continue
632 }
633 base_fkey := generic_fn.fkey()
634 mut concrete_type_sets := [][]ast.Type{}
635 if w.keep_all_fn_generic_types[base_fkey] {
636 concrete_type_sets = w.table.fn_generic_types[base_fkey].clone()
637 } else {
638 for concrete_types in w.used_fn_generic_types[base_fkey] {
639 if concrete_types !in concrete_type_sets {
640 concrete_type_sets << concrete_types.clone()
641 }
642 }
643 for concrete_types in w.walked_fn_generic_types[base_fkey] {
644 if concrete_types !in concrete_type_sets {
645 concrete_type_sets << concrete_types.clone()
646 }
647 }
648 }
649 for concrete_types in concrete_type_sets {
650 if concrete_types.any(it.has_flag(.generic)) {
651 continue
652 }
653 specialized_fkey := w.generic_concrete_name(base_fkey, concrete_types)
654 mut fn_copy := ast.FnDecl{
655 ...*generic_fn
656 }
657 w.mark_fn_as_used(specialized_fkey)
658 w.fn_decl_with_concrete_types(mut fn_copy, concrete_types)
659 }
660 }
661}
662
663pub fn (mut w Walker) mark_markused_consts() {
664 for ckey, mut constfield in w.all_consts {
665 if constfield.is_markused || constfield.is_exported {
666 $if trace_skip_unused_markused_consts ? {
667 println('>>>> walking markused const: ${ckey}')
668 }
669 w.mark_const_as_used(ckey)
670 }
671 }
672}
673
674pub fn (mut w Walker) mark_markused_globals() {
675 for gkey, mut globalfield in w.all_globals {
676 if globalfield.is_markused || globalfield.is_exported {
677 $if trace_skip_unused_markused_globals ? {
678 println('>>>> walking markused global: ${gkey}')
679 }
680 w.mark_global_as_used(gkey)
681 }
682 }
683}
684
685pub fn (mut w Walker) mark_markused_syms() {
686 for sym in w.table.type_symbols {
687 if sym.info is ast.Struct && sym.info.is_markused {
688 w.mark_by_sym(sym)
689 } else if sym.info is ast.Interface && sym.info.is_markused {
690 w.mark_by_sym(sym)
691 }
692 }
693}
694
695pub fn (mut w Walker) mark_markused_decltypes() {
696 for _, decl in w.all_decltypes {
697 if decl.is_markused {
698 w.mark_by_type(decl.typ)
699 }
700 }
701}
702
703pub fn (mut w Walker) stmt(node_ ast.Stmt) {
704 mut node := unsafe { node_ }
705 match mut node {
706 ast.EmptyStmt {}
707 ast.DebuggerStmt {
708 w.uses_debugger = true
709 }
710 ast.AsmStmt {
711 w.asm_io(node.output)
712 w.asm_io(node.input)
713 }
714 ast.AssertStmt {
715 if node.is_used {
716 w.uses_asserts = true
717 w.expr(node.expr)
718 if node.extra !is ast.EmptyExpr {
719 w.expr(node.extra)
720 }
721 }
722 }
723 ast.AssignStmt {
724 w.exprs(node.left)
725 w.exprs(node.right)
726 }
727 ast.Block {
728 w.stmts(node.stmts)
729 }
730 ast.ComptimeFor {
731 w.mark_by_type(node.typ)
732 w.inside_comptime++
733 defer {
734 w.inside_comptime--
735 }
736 w.stmts(node.stmts)
737 match node.kind {
738 .attributes {
739 w.uses_ct_attribute = true
740 }
741 .variants {
742 w.uses_ct_variants = true
743 }
744 .params {
745 w.uses_ct_params = true
746 }
747 .values {
748 w.uses_ct_values = true
749 }
750 .fields {
751 w.uses_ct_fields = true
752 }
753 .methods {
754 w.uses_ct_methods = true
755 }
756 }
757 }
758 ast.ConstDecl {
759 w.const_fields(node.fields)
760 }
761 ast.ExprStmt {
762 w.expr(node.expr)
763 }
764 ast.FnDecl {
765 w.fn_decl(mut node)
766 }
767 ast.ForCStmt {
768 if node.has_init {
769 w.stmt(node.init)
770 }
771 if node.has_cond {
772 w.expr(node.cond)
773 }
774 if node.has_inc {
775 w.stmt(node.inc)
776 }
777 w.stmts(node.stmts)
778 }
779 ast.ForInStmt {
780 w.expr(node.cond)
781 w.expr(node.high)
782 w.stmts(node.stmts)
783 w.mark_by_type(node.cond_type)
784 if node.kind == .map {
785 w.features.used_maps++
786 } else if node.kind == .struct {
787 if node.cond_type == 0 {
788 return
789 }
790 // the .next() method of the struct will be used for iteration:
791 // In generic functions, node.cond_type may have been overwritten by the checker
792 // for the last specialization. Re-resolve from the variable's parameter type.
793 mut resolved_cond_type := node.cond_type
794 cond := node.cond
795 if cond is ast.Ident {
796 specialized_type := w.resolve_current_specialized_var_type(cond.name)
797 if specialized_type != ast.no_type {
798 resolved_cond_type = specialized_type
799 }
800 }
801 cond_type_sym := w.table.sym(resolved_cond_type)
802 if next_fn := cond_type_sym.find_method('next') {
803 unsafe {
804 w.fn_decl(mut &ast.FnDecl(next_fn.source_fn))
805 }
806 }
807 }
808 }
809 ast.ForStmt {
810 if !node.is_inf {
811 w.expr(node.cond)
812 }
813 w.stmts(node.stmts)
814 }
815 ast.Return {
816 w.exprs(node.exprs)
817 }
818 ast.SqlStmt {
819 w.expr(node.db_expr)
820 w.expr(node.or_expr)
821 for line in node.lines {
822 if line.table_expr.typ != 0 {
823 w.mark_by_sym(w.table.sym(line.table_expr.typ))
824 }
825 w.expr(line.where_expr)
826 w.exprs(line.update_exprs)
827 }
828 w.uses_orm = true
829 }
830 ast.StructDecl {
831 for typ in node.implements_types {
832 w.mark_by_type(typ.typ)
833 }
834 w.struct_fields(node.fields)
835 w.uses_mem_align = w.uses_mem_align || node.is_aligned
836 }
837 ast.DeferStmt {
838 w.stmts(node.stmts)
839 }
840 ast.GlobalDecl {
841 for gf in node.fields {
842 if gf.has_expr {
843 w.expr(gf.expr)
844 }
845 }
846 }
847 ast.BranchStmt {}
848 ast.EnumDecl {}
849 ast.GotoLabel {}
850 ast.GotoStmt {}
851 ast.HashStmt {}
852 ast.Import {}
853 ast.InterfaceDecl {}
854 ast.SemicolonStmt {}
855 ast.Module {}
856 ast.TypeDecl {}
857 ast.NodeError {}
858 }
859}
860
861fn (mut w Walker) asm_io(ios []ast.AsmIO) {
862 for io in ios {
863 w.expr(io.expr)
864 }
865}
866
867fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) {
868 for stmt in stmts {
869 w.stmts(stmt.stmts)
870 }
871}
872
873fn (mut w Walker) stmts(stmts []ast.Stmt) {
874 for stmt in stmts {
875 w.stmt(stmt)
876 }
877}
878
879fn (mut w Walker) exprs(exprs []ast.Expr) {
880 for expr in exprs {
881 w.expr(expr)
882 }
883}
884
885fn (mut w Walker) expr(node_ ast.Expr) {
886 mut node := unsafe { node_ }
887 match mut node {
888 ast.EmptyExpr {
889 // TODO: make sure this doesn't happen
890 // panic('Walker: EmptyExpr')
891 }
892 ast.ComptimeType {}
893 ast.AnonFn {
894 w.fn_decl(mut node.decl)
895 }
896 ast.ArrayInit {
897 sym := w.table.sym(node.elem_type)
898 w.mark_by_sym(sym)
899 if sym.info is ast.Thread {
900 w.mark_by_type(w.table.find_or_register_array(sym.info.return_type))
901 }
902 w.expr(node.len_expr)
903 w.expr(node.cap_expr)
904 w.expr(node.init_expr)
905 w.exprs(node.exprs)
906 if w.table.final_sym(node.typ).kind == .array {
907 if !w.inside_in_op {
908 w.uses_array = true
909 w.mark_by_type(node.typ)
910 }
911 } else { // fixed arrays
912 w.mark_by_type(node.typ)
913 }
914 if node.elem_type.has_flag(.option) {
915 w.used_option++
916 }
917 }
918 ast.Assoc {
919 w.exprs(node.exprs)
920 }
921 ast.ArrayDecompose {
922 w.expr(node.expr)
923 }
924 ast.CallExpr {
925 w.call_expr(mut node)
926 if node.is_fn_a_const {
927 w.mark_const_as_used(node.name)
928 } else if node.name == 'json.decode' {
929 w.mark_by_type((node.args[0].expr as ast.TypeNode).typ)
930 } else if node.name == 'json.encode' && node.args[0].typ != 0 {
931 sym := w.table.final_sym(node.args[0].typ)
932 if sym.info is ast.Map {
933 w.mark_by_type(w.table.find_or_register_array(sym.info.key_type))
934 }
935 } else if !node.is_method && node.args.len == 1
936 && node.name in ['println', 'print', 'eprint', 'eprintln'] {
937 if f := w.table.find_fn(node.name) {
938 if f.mod == 'builtin' {
939 if node.args[0].typ != ast.string_type {
940 w.uses_str[node.args[0].typ] = true
941 }
942 if w.pref.gc_mode == .boehm_leak && (node.args[0].typ != ast.string_type
943 || node.args[0].expr !in [ast.Ident, ast.StringLiteral, ast.SelectorExpr, ast.ComptimeSelector]) {
944 w.uses_free[ast.string_type] = true
945 }
946 }
947 }
948 } else if node.is_method && node.name == 'str' {
949 w.uses_str[node.left_type] = true
950 } else if node.is_method && node.name == 'free' {
951 w.uses_free[node.left_type] = true
952 } else if node.is_method && node.name == 'clone' && !w.uses_arr_clone
953 && node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array {
954 w.uses_arr_clone = true
955 } else if node.is_method && node.name == 'sorted' && !w.uses_arr_sorted
956 && node.left_type != 0 && w.table.final_sym(node.left_type).kind == .array {
957 w.uses_arr_sorted = true
958 }
959 if !w.is_builtin_mod && !w.uses_external_type {
960 if node.is_method {
961 w.uses_external_type = node.mod == 'builtin'
962 } else if node.name.contains('.') && node.name.all_before_last('.').len > 1 {
963 w.uses_external_type = true
964 }
965 }
966 if node.is_method && node.left_type != 0
967 && w.table.final_sym(node.left_type).kind in [.array_fixed, .array] {
968 w.mark_by_type(node.return_type)
969 }
970 if node.name.contains('new_array_from_c_array') {
971 if !w.inside_in_op {
972 w.uses_array = true
973 w.mark_by_type(node.return_type) // the transformer fills this with the correct type
974 }
975 }
976 }
977 ast.CastExpr {
978 w.expr(node.expr)
979 w.expr(node.arg)
980 if !w.uses_memdup {
981 fsym := w.table.final_sym(node.typ)
982 w.uses_memdup = fsym.kind in [.sum_type, .interface]
983 }
984 w.mark_by_type(node.typ)
985 if node.typ.has_flag(.option) {
986 w.used_option++
987 } else if node.typ.has_flag(.result) {
988 w.used_result++
989 }
990 }
991 ast.ChanInit {
992 w.expr(node.cap_expr)
993 w.mark_by_type(node.typ)
994 w.uses_channel = true
995 }
996 ast.ConcatExpr {
997 w.exprs(node.vals)
998 w.mark_by_sym(w.table.sym(node.return_type))
999 }
1000 ast.ComptimeSelector {
1001 w.expr(node.left)
1002 w.expr(node.field_expr)
1003 w.or_block(node.or_block)
1004 }
1005 ast.ComptimeCall {
1006 w.inside_comptime++
1007 defer {
1008 w.inside_comptime--
1009 }
1010 w.expr(node.left)
1011 for args in node.args {
1012 w.expr(args.expr)
1013 }
1014 w.expr(node.or_block)
1015 if node.is_template {
1016 w.stmts(node.veb_tmpl.stmts)
1017 }
1018 if node.kind == .embed_file {
1019 w.features.used_maps++
1020 }
1021 if node.kind == .new {
1022 w.uses_memdup = true
1023 }
1024 }
1025 ast.DumpExpr {
1026 w.expr(node.expr)
1027 w.features.dump = true
1028 w.mark_by_type(node.expr_type)
1029 }
1030 ast.SpawnExpr {
1031 if node.is_expr {
1032 w.fn_by_name('free')
1033 }
1034 w.mark_by_type(w.table.find_or_register_thread(node.call_expr.return_type))
1035 w.expr(node.call_expr)
1036 w.uses_spawn = true
1037
1038 if w.pref.os == .windows {
1039 w.fn_by_name('panic_lasterr')
1040 w.fn_by_name('winapi_lasterr_str')
1041 } else {
1042 w.fn_by_name('c_error_number_str')
1043 w.fn_by_name('panic_error_number')
1044 }
1045 }
1046 ast.GoExpr {
1047 if node.is_expr {
1048 w.fn_by_name('free')
1049 }
1050 w.mark_by_type(node.call_expr.return_type)
1051 w.expr(node.call_expr)
1052 }
1053 ast.IndexExpr {
1054 w.expr(node.left)
1055 w.expr(node.index)
1056 if node.or_expr.kind != .absent || node.is_option {
1057 w.uses_index_check = true
1058 }
1059 w.mark_by_type(node.typ)
1060 w.or_block(node.or_expr)
1061 left_type := if node.left_type != 0 {
1062 node.left_type
1063 } else {
1064 w.infer_expr_type(node.left)
1065 }
1066 if left_type == 0 {
1067 return
1068 }
1069 sym := w.table.final_sym(left_type)
1070 if sym.info is ast.Map {
1071 if node.is_setter && !w.uses_map_setter {
1072 w.mark_builtin_map_method_as_used('set')
1073 } else if !node.is_setter && !w.uses_map_getter {
1074 w.mark_builtin_map_method_as_used('get')
1075 }
1076 w.mark_by_type(sym.info.key_type)
1077 w.mark_by_type(sym.info.value_type)
1078 w.features.used_maps++
1079 } else if sym.kind in [.array, .array_fixed, .any] {
1080 if !w.is_direct_array_access || w.features.auto_str_arr {
1081 if node.is_setter && !w.uses_arr_setter {
1082 w.mark_builtin_array_method_as_used('set')
1083 w.uses_arr_setter = true
1084 } else if !node.is_setter && !w.uses_arr_getter {
1085 w.mark_builtin_array_method_as_used('get')
1086 w.uses_arr_getter = true
1087 }
1088 }
1089 if sym.info is ast.Array {
1090 w.mark_by_type(sym.info.elem_type)
1091 } else if sym.info is ast.ArrayFixed {
1092 w.mark_by_type(sym.info.elem_type)
1093 }
1094 if !node.is_gated && node.index is ast.RangeExpr && !w.uses_arr_range_index {
1095 w.uses_arr_range_index = true
1096 }
1097 if !w.uses_fixed_arr_int && sym.kind == .array_fixed {
1098 w.uses_fixed_arr_int = true
1099 }
1100 if !w.uses_index && !w.is_direct_array_access {
1101 w.uses_index = true
1102 }
1103 if node.is_gated && node.index is ast.RangeExpr && !w.uses_arr_range_index_gated {
1104 w.uses_arr_range_index_gated = node.is_gated
1105 }
1106 } else if sym.kind == .string {
1107 w.uses_str_index = true
1108 if node.index is ast.RangeExpr {
1109 if node.is_gated {
1110 w.fn_by_name('${ast.string_type_idx}.substr_ni')
1111 } else if node.or_expr.kind != .absent || node.is_option {
1112 w.fn_by_name('${ast.string_type_idx}.substr_with_check')
1113 } else {
1114 w.fn_by_name('${ast.string_type_idx}.substr')
1115 }
1116 if !w.uses_str_range_index {
1117 w.uses_str_range_index = true
1118 }
1119 if !w.uses_range_index_check {
1120 w.uses_range_index_check = node.or_expr.kind != .absent || node.is_option
1121 }
1122 if !w.uses_str_range_index_gated {
1123 w.uses_str_range_index_gated = node.is_gated
1124 }
1125 } else {
1126 if node.or_expr.kind != .absent || node.is_option {
1127 w.fn_by_name('${ast.string_type_idx}.at_with_check')
1128 } else {
1129 w.fn_by_name('${ast.string_type_idx}.at')
1130 }
1131 if !w.uses_str_index_check {
1132 w.uses_str_index_check = node.or_expr.kind != .absent || node.is_option
1133 }
1134 if !w.uses_str_range {
1135 w.uses_str_range = node.index is ast.RangeExpr
1136 }
1137 }
1138 } else if sym.info is ast.Struct {
1139 w.mark_by_sym(sym)
1140 } else if sym.info is ast.SumType {
1141 w.mark_by_sym(sym)
1142 } else if sym.kind == .any {
1143 if !w.is_direct_array_access {
1144 if node.is_setter && !w.uses_arr_setter {
1145 w.mark_builtin_array_method_as_used('set')
1146 } else if !node.is_setter && !w.uses_arr_getter {
1147 w.mark_builtin_array_method_as_used('get')
1148 }
1149 }
1150 }
1151 }
1152 ast.InfixExpr {
1153 w.expr(node.left)
1154 tmp_inside_in_op := w.inside_in_op
1155 w.inside_in_op = node.op in [.key_in, .not_in]
1156 w.expr(node.right)
1157 w.inside_in_op = tmp_inside_in_op
1158 w.or_block(node.or_block)
1159 if node.left_type != 0 {
1160 sym := w.table.sym(node.left_type)
1161 w.mark_by_sym(sym)
1162 if sym.kind == .struct {
1163 if opmethod := sym.find_method(node.op.str()) {
1164 unsafe {
1165 w.fn_decl(mut &ast.FnDecl(opmethod.source_fn))
1166 }
1167 }
1168 } else {
1169 if !w.uses_append && node.op == .left_shift && (sym.kind == .array
1170 || (sym.kind == .alias && w.table.final_sym(node.left_type).kind == .array)) {
1171 w.uses_append = true
1172 }
1173 }
1174 }
1175 right_type := if node.right_type == 0 && mut node.right is ast.TypeNode {
1176 node.right.typ
1177 } else {
1178 node.right_type
1179 }
1180 if right_type != 0 {
1181 right_sym := w.table.sym(right_type)
1182 if !(w.is_direct_array_access && right_sym.kind == .array) {
1183 w.mark_by_type(right_type)
1184 }
1185 if node.op in [.not_in, .key_in] {
1186 if right_sym.kind == .map {
1187 w.features.used_maps++
1188 }
1189 if !w.uses_arr_void && !w.is_direct_array_access
1190 && right_sym.kind in [.array, .array_fixed] {
1191 w.uses_arr_void = true
1192 }
1193 } else if node.op in [.key_is, .not_is] {
1194 w.mark_by_sym(right_sym)
1195 }
1196 }
1197 if node.op in [.eq, .ne, .gt, .ge, .lt, .le] {
1198 if !w.table.used_features.safe_int && node.left_type != 0 && node.right_type != 0 {
1199 left := w.table.unaliased_type(node.left_type)
1200 right := w.table.unaliased_type(node.right_type)
1201 w.table.used_features.safe_int = (
1202 (left.idx() in [ast.u32_type_idx, ast.u64_type_idx] && right.is_signed())
1203 || (right.idx() in [ast.u32_type_idx, ast.u64_type_idx] && left.is_signed()))
1204 }
1205 w.uses_eq = w.uses_eq || node.op in [.eq, .ne]
1206 }
1207 }
1208 ast.IfGuardExpr {
1209 w.expr(node.expr)
1210 w.mark_by_type(node.expr_type)
1211 w.uses_guard = true
1212 if !w.uses_str_index_check && node.expr is ast.IndexExpr && node.expr_type != 0
1213 && w.table.final_sym(node.expr_type).kind in [.u8, .string] {
1214 w.uses_str_index_check = true
1215 }
1216 }
1217 ast.IfExpr {
1218 w.expr(node.left)
1219 if branch := w.resolve_comptime_if_branch(node) {
1220 w.inside_comptime_if++
1221 defer {
1222 w.inside_comptime_if--
1223 }
1224 w.expr(branch.cond)
1225 w.stmts(branch.stmts)
1226 return
1227 }
1228 for b in node.branches {
1229 w.expr(b.cond)
1230 w.stmts(b.stmts)
1231 }
1232 }
1233 ast.Ident {
1234 match node.kind {
1235 .constant {
1236 w.mark_const_as_used(node.name)
1237 }
1238 .function {
1239 if mut stmt := w.all_fns[node.name] {
1240 ident_concrete_types :=
1241 w.resolve_current_concrete_types(node.concrete_types)
1242 if stmt.generic_names.len > 0
1243 && (ident_concrete_types.len == 0 || w.inside_comptime > 0) {
1244 w.keep_all_fn_generic_types[node.name] = true
1245 }
1246 w.fn_decl_with_concrete_types(mut stmt, ident_concrete_types)
1247 } else {
1248 w.fn_by_name(node.name)
1249 }
1250 if node.info is ast.IdentFn {
1251 w.mark_by_type(node.info.typ)
1252 }
1253 }
1254 .global {
1255 w.mark_global_as_used(node.name)
1256 }
1257 .blank_ident {}
1258 else {
1259 // `.unresolved`, `.variable`
1260 // println('>>> else, ast.Ident ${node.name} kind: ${node.kind} ')
1261 if node.name in w.all_consts {
1262 w.mark_const_as_used(node.name)
1263 } else if node.name in w.all_globals {
1264 w.mark_global_as_used(node.name)
1265 } else {
1266 if (node.kind == .variable && node.obj is ast.Var && node.obj.is_used
1267 && node.obj.typ != 0 && w.table.type_kind(node.obj.typ) == .function)
1268 || (node.kind == .unresolved && (node.name.contains('.')
1269 || node.name.contains('_T_')))
1270 || (node.name.contains('_T_') && node.name in w.all_fns) {
1271 w.fn_by_name(node.name)
1272 }
1273 }
1274 if !w.uses_atomic && node.info is ast.IdentVar {
1275 w.uses_atomic = node.info.typ.has_flag(.atomic_f)
1276 }
1277 }
1278 }
1279
1280 if node.obj is ast.Var && node.obj.is_unwrapped {
1281 w.used_option++
1282 }
1283 w.or_block(node.or_expr)
1284 }
1285 ast.LambdaExpr {
1286 w.expr(node.func)
1287 }
1288 ast.Likely {
1289 w.expr(node.expr)
1290 }
1291 ast.MapInit {
1292 if node.typ == 0 {
1293 return
1294 }
1295 w.exprs(node.keys)
1296 w.exprs(node.vals)
1297 if node.has_update_expr {
1298 w.expr(node.update_expr)
1299 }
1300 if node.vals.len > 0 && node.has_update_expr {
1301 w.uses_map_update = true
1302 }
1303 mapinfo := w.table.final_sym(node.typ).map_info()
1304 ksym := w.table.sym(mapinfo.key_type)
1305 vsym := w.table.sym(mapinfo.value_type)
1306 if node.typ.has_flag(.shared_f) {
1307 w.uses_lock = true
1308 }
1309 w.mark_by_sym(ksym)
1310 w.mark_by_sym(vsym)
1311 w.features.used_maps++
1312 w.mark_by_type(node.typ)
1313 }
1314 ast.MatchExpr {
1315 w.expr(node.cond)
1316 for b in node.branches {
1317 w.exprs(b.exprs)
1318 for expr in b.exprs {
1319 if expr is ast.TypeNode {
1320 w.mark_by_sym(w.table.sym(expr.typ))
1321 }
1322 }
1323 w.stmts(b.stmts)
1324 }
1325 }
1326 ast.None {
1327 w.used_none++
1328 }
1329 ast.Nil {}
1330 ast.ParExpr {
1331 w.expr(node.expr)
1332 }
1333 ast.PrefixExpr {
1334 if !w.uses_memdup && node.op == .amp {
1335 w.uses_memdup = true
1336 }
1337 w.expr(node.right)
1338 }
1339 ast.PostfixExpr {
1340 w.expr(node.expr)
1341 if node.op == .question && node.expr !is ast.Ident {
1342 w.used_option++
1343 w.used_panic++
1344 }
1345 }
1346 ast.RangeExpr {
1347 if node.has_low {
1348 w.expr(node.low)
1349 }
1350 if node.has_high {
1351 w.expr(node.high)
1352 }
1353 }
1354 ast.SizeOf, ast.IsRefType {
1355 w.expr(node.expr)
1356 w.mark_by_type(node.typ)
1357 }
1358 ast.StringInterLiteral {
1359 w.uses_interp = true
1360 w.exprs(node.exprs)
1361 for expr in node.fwidth_exprs {
1362 if expr !is ast.EmptyExpr {
1363 w.expr(expr)
1364 }
1365 }
1366 for expr in node.precision_exprs {
1367 if expr !is ast.EmptyExpr {
1368 w.expr(expr)
1369 }
1370 }
1371 }
1372 ast.SelectorExpr {
1373 w.expr(node.expr)
1374 if node.expr_type != 0 {
1375 w.mark_by_type(node.expr_type)
1376 if method := w.table.find_method(w.table.sym(node.expr_type), node.field_name) {
1377 w.fn_by_name(method.fkey())
1378 }
1379 }
1380 w.mark_by_type(node.typ)
1381 w.or_block(node.or_block)
1382 if node.has_hidden_receiver {
1383 w.used_closures++
1384 }
1385 }
1386 ast.SqlExpr {
1387 w.expr(node.db_expr)
1388 w.expr(node.or_expr)
1389 w.expr(node.offset_expr)
1390 w.expr(node.order_expr)
1391 w.expr(node.limit_expr)
1392 w.expr(node.where_expr)
1393 w.mark_by_type(node.typ)
1394 w.uses_orm = true
1395 }
1396 ast.SqlQueryDataExpr {
1397 for item in node.items {
1398 match item {
1399 ast.SqlQueryDataLeaf {
1400 w.expr(item.expr)
1401 }
1402 ast.SqlQueryDataIf {
1403 for branch in item.branches {
1404 w.expr(branch.cond)
1405 for branch_item in branch.items {
1406 match branch_item {
1407 ast.SqlQueryDataLeaf {
1408 w.expr(branch_item.expr)
1409 }
1410 ast.SqlQueryDataIf {
1411 for nested_branch in branch_item.branches {
1412 w.expr(nested_branch.cond)
1413 for nested_item in nested_branch.items {
1414 if nested_item is ast.SqlQueryDataLeaf {
1415 w.expr(nested_item.expr)
1416 }
1417 }
1418 }
1419 }
1420 }
1421 }
1422 }
1423 }
1424 }
1425 }
1426 w.mark_by_type(node.typ)
1427 w.uses_orm = true
1428 }
1429 ast.StructInit {
1430 if node.typ == 0 {
1431 return
1432 }
1433 sym := w.table.sym(node.typ)
1434 w.mark_by_sym(sym)
1435 if !w.uses_memdup
1436 && (sym.kind == .sum_type || (sym.info is ast.Struct && sym.info.is_heap)) {
1437 w.uses_memdup = true
1438 }
1439 if node.has_update_expr {
1440 w.expr(node.update_expr)
1441 }
1442 for sif in node.init_fields {
1443 w.expr(sif.expr)
1444 }
1445 }
1446 ast.TypeOf {
1447 w.expr(node.expr)
1448 w.mark_by_type(node.typ)
1449 }
1450 ///
1451 ast.AsCast {
1452 w.expr(node.expr)
1453 w.fn_by_name('__as_cast')
1454 w.fn_by_name('new_array_from_c_array')
1455 w.mark_by_sym_name('VCastTypeIndexName')
1456 }
1457 ast.AtExpr {}
1458 ast.BoolLiteral {}
1459 ast.FloatLiteral {}
1460 ast.CharLiteral {}
1461 ast.IntegerLiteral {}
1462 ast.StringLiteral {
1463 if !w.uses_str_literal && !node.is_raw {
1464 w.mark_by_sym_name('string')
1465 w.uses_str_literal = true
1466 }
1467 }
1468 ast.CTempVar {
1469 w.expr(node.orig)
1470 }
1471 ast.Comment {}
1472 ast.EnumVal {
1473 if e := w.table.enum_decls[node.enum_name] {
1474 filtered := e.fields.filter(it.name == node.val)
1475 if filtered.len != 0 && filtered[0].expr !is ast.EmptyExpr {
1476 w.expr(filtered[0].expr)
1477 }
1478 }
1479 w.mark_by_type(node.typ)
1480 }
1481 ast.LockExpr {
1482 w.uses_lock = true
1483 w.stmts(node.stmts)
1484 }
1485 ast.OffsetOf {}
1486 ast.OrExpr {
1487 w.or_block(node)
1488 }
1489 ast.SelectExpr {
1490 for branch in node.branches {
1491 w.stmt(branch.stmt)
1492 w.stmts(branch.stmts)
1493 }
1494 w.uses_channel = true
1495 }
1496 ast.TypeNode {
1497 w.mark_by_type(node.typ)
1498 }
1499 ast.UnsafeExpr {
1500 w.expr(node.expr)
1501 }
1502 ast.NodeError {}
1503 }
1504}
1505
1506pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) {
1507 w.fn_decl_with_concrete_types(mut node, [])
1508}
1509
1510fn (mut w Walker) fn_decl_with_concrete_types(mut node ast.FnDecl, concrete_types []ast.Type) {
1511 if node == unsafe { nil } {
1512 return
1513 }
1514 if w.level == 0 {
1515 last_is_builtin_mod := w.is_builtin_mod
1516 w.is_builtin_mod = node.mod in ['builtin', 'os', 'strconv', 'builtin.closure']
1517 defer(fn) {
1518 w.is_builtin_mod = last_is_builtin_mod
1519 }
1520 }
1521 w.table.used_features.used_attr_weak = w.table.used_features.used_attr_weak || node.is_weak
1522 w.table.used_features.used_attr_noreturn = w.table.used_features.used_attr_noreturn
1523 || node.is_noreturn
1524 if node.language == .c {
1525 w.mark_fn_as_used(node.fkey())
1526 w.mark_fn_ret_and_params(node.return_type, node.params)
1527 return
1528 }
1529
1530 fkey := node.fkey()
1531 resolved_concrete_types := w.resolve_current_concrete_types(concrete_types)
1532 if resolved_concrete_types.len > 0 {
1533 w.record_used_fn_generic_types(fkey, resolved_concrete_types)
1534 if !w.record_walked_fn_generic_types(fkey, resolved_concrete_types) {
1535 w.mark_fn_as_used(fkey)
1536 return
1537 }
1538 } else if w.used_fns[fkey] {
1539 return
1540 }
1541 w.mark_fn_as_used(fkey)
1542 if node.mod == 'x.json2' {
1543 w.mark_by_sym_name('EnumData')
1544 w.mark_by_sym_name('time.Time')
1545 }
1546 last_is_direct_array_access := w.is_direct_array_access
1547 w.is_direct_array_access = node.is_direct_arr || w.pref.no_bounds_checking
1548 defer { w.is_direct_array_access = last_is_direct_array_access }
1549 last_cur_fn := w.cur_fn
1550 last_cur_fn_concrete_types := w.cur_fn_concrete_types.clone()
1551 defer {
1552 w.cur_fn = last_cur_fn
1553 w.cur_fn_concrete_types = last_cur_fn_concrete_types
1554 }
1555 if w.trace_enabled {
1556 w.level++
1557 defer(fn) { w.level-- }
1558 }
1559 if node.is_closure {
1560 w.used_closures++
1561 }
1562 if node.no_body {
1563 w.mark_fn_as_used(fkey)
1564 return
1565 }
1566 if node.is_method {
1567 w.mark_by_type(node.receiver.typ)
1568 }
1569 w.mark_fn_ret_and_params(node.return_type, node.params)
1570 w.mark_fn_as_used(fkey)
1571 // For generic functions, mark the concrete param/return types for all instantiations.
1572 // This is needed because mark_fn_ret_and_params skips types with .generic flag,
1573 // but the cgen will emit concrete versions for all fn_generic_types entries.
1574 generic_names := w.fn_generic_names(node)
1575 if generic_names.len > 0 {
1576 mut concrete_type_lists := [][]ast.Type{}
1577 if resolved_concrete_types.len > 0 {
1578 concrete_type_lists << resolved_concrete_types.clone()
1579 }
1580 for concrete_type_list in w.table.fn_generic_types[fkey] {
1581 if concrete_type_list !in concrete_type_lists {
1582 concrete_type_lists << concrete_type_list
1583 }
1584 }
1585 max_param_len := if node.is_method { node.params.len - 1 } else { node.params.len }
1586 param_i := if node.is_method { 1 } else { 0 }
1587 for concrete_type_list in concrete_type_lists {
1588 if node.is_method {
1589 if resolved := w.table.convert_generic_type(node.receiver.typ, generic_names,
1590 concrete_type_list)
1591 {
1592 w.mark_by_type(resolved)
1593 }
1594 }
1595 // mark concrete return type
1596 if resolved := w.table.convert_generic_type(node.return_type, generic_names,
1597 concrete_type_list)
1598 {
1599 w.mark_by_type(resolved)
1600 }
1601 // mark concrete param types
1602 for k, concrete_type in concrete_type_list {
1603 if k >= max_param_len {
1604 break
1605 }
1606 param_typ := node.params[k + param_i].typ
1607 if resolved := w.table.convert_generic_type(param_typ, generic_names,
1608 concrete_type_list)
1609 {
1610 w.mark_by_type(resolved)
1611 } else if param_typ.has_flag(.generic) && w.table.type_kind(param_typ) == .array {
1612 w.mark_by_type(w.table.find_or_register_array(concrete_type))
1613 }
1614 }
1615 }
1616 }
1617 prev_cur_fn := w.cur_fn
1618 w.cur_fn = fkey
1619 w.cur_fn_concrete_types = resolved_concrete_types
1620 if node.mod == 'x.json2' && node.name == 'get_decoded_sumtype_workaround'
1621 && w.has_sumtype_generic_context(resolved_concrete_types) {
1622 if mut copy_fn := w.all_fns['x.json2.copy_type'] {
1623 for concrete_type_list in w.sumtype_variant_concrete_types(resolved_concrete_types) {
1624 w.fn_decl_with_concrete_types(mut copy_fn, concrete_type_list)
1625 }
1626 }
1627 }
1628 if node.mod == 'x.json2' && node.name == 'get_struct_type_workaround'
1629 && w.has_sumtype_generic_context(resolved_concrete_types) {
1630 check_struct_type_valid_fkey, _ := w.resolve_method_fkey_for_type(node.receiver.typ,
1631 'check_struct_type_valid')
1632 if check_struct_type_valid_fkey != '' {
1633 if mut check_struct_type_valid_fn := w.all_fns[check_struct_type_valid_fkey] {
1634 for concrete_type_list in w.sumtype_variant_concrete_types(resolved_concrete_types) {
1635 if concrete_type_list.len != 1 {
1636 continue
1637 }
1638 concrete_typ := w.table.unaliased_type(concrete_type_list[0])
1639 concrete_sym := w.table.final_sym(concrete_typ)
1640 if concrete_sym.kind == .struct && concrete_sym.name != 'time.Time' {
1641 w.fn_decl_with_concrete_types(mut check_struct_type_valid_fn,
1642 concrete_type_list)
1643 }
1644 }
1645 }
1646 }
1647 }
1648 if node.mod == 'x.json2' && node.name == 'decode_value' && node.is_method
1649 && resolved_concrete_types.len == 1 {
1650 concrete_typ := w.table.unaliased_type(resolved_concrete_types[0])
1651 concrete_sym := w.table.final_sym(concrete_typ)
1652 if concrete_typ.is_number() || concrete_sym.kind == .enum {
1653 decode_number_fkey, _ := w.resolve_method_fkey_for_type(node.receiver.typ,
1654 'decode_number')
1655 if decode_number_fkey != '' {
1656 if mut decode_number_fn := w.all_fns[decode_number_fkey] {
1657 w.fn_decl_with_concrete_types(mut decode_number_fn, resolved_concrete_types)
1658 }
1659 }
1660 }
1661 if concrete_sym.kind == .struct && concrete_sym.name != 'time.Time' {
1662 if mut decode_struct_key_fn := w.all_fns['x.json2.decode_struct_key'] {
1663 w.fn_decl_with_concrete_types(mut decode_struct_key_fn, resolved_concrete_types)
1664 }
1665 if mut check_required_struct_fields_fn := w.all_fns['x.json2.check_required_struct_fields'] {
1666 w.fn_decl_with_concrete_types(mut check_required_struct_fields_fn,
1667 resolved_concrete_types)
1668 }
1669 w.mark_json2_optional_field_helpers(concrete_typ)
1670 }
1671 }
1672 if node.mod == 'x.json2' && node.name == 'decode_struct_key' && resolved_concrete_types.len == 1 {
1673 w.mark_json2_optional_field_helpers(resolved_concrete_types[0])
1674 }
1675 if node.mod == 'x.json2' && node.name == 'decode_enum' {
1676 w.uses_ct_values = true
1677 w.mark_by_sym_name('EnumData')
1678 }
1679 if node.mod == 'x.json2' && node.name == 'encode_value' && node.is_method
1680 && resolved_concrete_types.len == 1 {
1681 concrete_typ := w.table.unaliased_type(resolved_concrete_types[0])
1682 concrete_sym := w.table.final_sym(concrete_typ)
1683 if concrete_sym.kind == .sum_type {
1684 for concrete_type_list in w.sumtype_variant_concrete_types([concrete_typ]) {
1685 if concrete_type_list != resolved_concrete_types {
1686 w.remember_generic_fn_instance(node.fkey(), concrete_type_list)
1687 }
1688 }
1689 }
1690 if concrete_sym.kind == .array {
1691 encode_array_fkey, _ := w.resolve_method_fkey_for_type(node.receiver.typ,
1692 'encode_array')
1693 if encode_array_fkey != '' {
1694 if mut encode_array_fn := w.all_fns[encode_array_fkey] {
1695 w.remember_generic_fn_instance(encode_array_fn.fkey(), resolved_concrete_types)
1696 }
1697 }
1698 }
1699 if concrete_sym.kind == .map {
1700 encode_map_fkey, _ := w.resolve_method_fkey_for_type(node.receiver.typ, 'encode_map')
1701 if encode_map_fkey != '' {
1702 if mut encode_map_fn := w.all_fns[encode_map_fkey] {
1703 w.remember_generic_fn_instance(encode_map_fn.fkey(), resolved_concrete_types)
1704 }
1705 }
1706 }
1707 if concrete_sym.kind == .struct {
1708 w.mark_json2_encode_field_helpers(node.receiver.typ, concrete_typ)
1709 }
1710 json_encoder_typ := w.table.find_type('x.json2.JsonEncoder')
1711 if json_encoder_typ != 0
1712 && w.table.does_type_implement_interface(concrete_typ, json_encoder_typ) {
1713 to_json_fkey, _ := w.resolve_method_fkey_for_type(concrete_typ, 'to_json')
1714 if to_json_fkey != '' {
1715 w.fn_by_name(to_json_fkey)
1716 }
1717 }
1718 encodable_typ := w.table.find_type('x.json2.Encodable')
1719 if encodable_typ != 0 && w.table.does_type_implement_interface(concrete_typ, encodable_typ) {
1720 json_str_fkey, _ := w.resolve_method_fkey_for_type(concrete_typ, 'json_str')
1721 if json_str_fkey != '' {
1722 w.fn_by_name(json_str_fkey)
1723 }
1724 }
1725 }
1726 if node.mod == 'x.json2' && node.name == 'encode_struct_fields'
1727 && resolved_concrete_types.len == 1 {
1728 w.mark_json2_encode_field_helpers(node.receiver.typ, resolved_concrete_types[0])
1729 }
1730 w.stmts(node.stmts)
1731 w.defer_stmts(node.defer_stmts)
1732 w.cur_fn = prev_cur_fn
1733}
1734
1735fn (mut w Walker) fn_decl_with_fkey(mut node ast.FnDecl, walk_fkey string) {
1736 if node == unsafe { nil } {
1737 return
1738 }
1739 w.mark_fn_as_used(walk_fkey)
1740 w.fn_decl_with_concrete_types(mut node, [])
1741}
1742
1743fn (w &Walker) receiver_concrete_types(typ ast.Type) []ast.Type {
1744 sym := w.table.final_sym(typ)
1745 return match sym.info {
1746 ast.Struct {
1747 mut concrete_types := sym.info.concrete_types.clone()
1748 if concrete_types.len == 0 && sym.generic_types.len == sym.info.generic_types.len
1749 && sym.generic_types != sym.info.generic_types {
1750 concrete_types = sym.generic_types.clone()
1751 }
1752 concrete_types.map(it.clear_flag(.generic))
1753 }
1754 ast.Interface {
1755 mut concrete_types := sym.info.concrete_types.clone()
1756 if concrete_types.len == 0 && sym.generic_types.len == sym.info.generic_types.len
1757 && sym.generic_types != sym.info.generic_types {
1758 concrete_types = sym.generic_types.clone()
1759 }
1760 concrete_types.map(it.clear_flag(.generic))
1761 }
1762 ast.SumType {
1763 mut concrete_types := sym.info.concrete_types.clone()
1764 if concrete_types.len == 0 && sym.generic_types.len == sym.info.generic_types.len
1765 && sym.generic_types != sym.info.generic_types {
1766 concrete_types = sym.generic_types.clone()
1767 }
1768 concrete_types.map(it.clear_flag(.generic))
1769 }
1770 ast.GenericInst {
1771 sym.info.concrete_types.map(it.clear_flag(.generic))
1772 }
1773 else {
1774 []ast.Type{}
1775 }
1776 }
1777}
1778
1779fn (w &Walker) used_receiver_generic_instantiations(receiver_typ ast.Type) [][]ast.Type {
1780 if receiver_typ == 0 || !receiver_typ.has_flag(.generic) {
1781 return []
1782 }
1783 receiver_parent := receiver_typ.set_nr_muls(0).set_flag(.generic)
1784 mut concrete_type_lists := [][]ast.Type{}
1785 for sym_idx, _ in w.used_syms {
1786 sym := w.table.type_symbols[sym_idx]
1787 match sym.info {
1788 ast.Struct {
1789 if sym.info.parent_type == receiver_parent && sym.info.concrete_types.len > 0
1790 && sym.info.concrete_types !in concrete_type_lists {
1791 concrete_type_lists << sym.info.concrete_types.clone()
1792 }
1793 }
1794 ast.Interface {
1795 if sym.info.parent_type == receiver_parent && sym.info.concrete_types.len > 0
1796 && sym.info.concrete_types !in concrete_type_lists {
1797 concrete_type_lists << sym.info.concrete_types.clone()
1798 }
1799 }
1800 ast.SumType {
1801 if sym.info.parent_type == receiver_parent && sym.info.concrete_types.len > 0
1802 && sym.info.concrete_types !in concrete_type_lists {
1803 concrete_type_lists << sym.info.concrete_types.clone()
1804 }
1805 }
1806 else {}
1807 }
1808 }
1809 return concrete_type_lists
1810}
1811
1812pub fn (mut w Walker) call_expr(mut node ast.CallExpr) {
1813 if node == unsafe { nil } {
1814 return
1815 }
1816 for arg in node.args {
1817 w.expr(arg.expr)
1818 }
1819 if node.is_variadic && node.expected_arg_types.last().has_flag(.option) {
1820 w.used_option++
1821 }
1822 // Check for non-option args passed to option params (cgen wraps with option_ok)
1823 for i, exp_type in node.expected_arg_types {
1824 if exp_type.has_flag(.option) && i < node.args.len && !node.args[i].typ.has_flag(.option) {
1825 w.used_option++
1826 break
1827 }
1828 }
1829 // Mark function pointer types used in expected arg types, so their typedefs are emitted.
1830 // When cgen casts an argument to the expected function pointer type, the typedef must exist.
1831 for exp_type in node.expected_arg_types {
1832 exp_sym := w.table.sym(exp_type)
1833 if exp_sym.kind == .function && !exp_sym.name.starts_with('fn ') {
1834 w.mark_by_type(exp_type)
1835 }
1836 }
1837 source_concrete_types := if node.is_method
1838 && node.concrete_types.len > node.raw_concrete_types.len {
1839 node.concrete_types
1840 } else if node.raw_concrete_types.len > 0 {
1841 node.raw_concrete_types
1842 } else {
1843 []ast.Type{}
1844 }
1845 mut call_concrete_types := w.resolve_current_concrete_types(source_concrete_types)
1846 for concrete_type in call_concrete_types {
1847 w.mark_by_type(concrete_type)
1848 }
1849 if node.language == .c {
1850 if node.name in ['C.wyhash', 'C.wyhash64'] {
1851 w.features.used_maps++
1852 }
1853 w.mark_by_type(node.return_type)
1854 return
1855 }
1856 mut resolved_left_type := node.left_type
1857 if node.left is ast.Ident {
1858 left_ident := node.left as ast.Ident
1859 if left_ident.obj is ast.Var {
1860 current_specialized_left_type := w.resolve_current_specialized_var_type(left_ident.name)
1861 if current_specialized_left_type != 0 {
1862 resolved_left_type = current_specialized_left_type
1863 } else if left_ident.obj.typ.has_flag(.generic) {
1864 resolved_left_type = left_ident.obj.typ
1865 }
1866 }
1867 }
1868 // When inside a generic function, the checker may have resolved
1869 // the left_type to the last-processed concrete type. Re-resolve
1870 // through the current concrete types to get the correct type for
1871 // this specific instantiation.
1872 if node.is_method && w.cur_fn_concrete_types.len > 0 && w.cur_fn != '' {
1873 if cur_fn_decl := w.all_fns[w.cur_fn] {
1874 generic_names := w.fn_generic_names(cur_fn_decl)
1875 if generic_names.len > 0 && generic_names.len == w.cur_fn_concrete_types.len {
1876 // Check if resolved_left_type matches any concrete type from a
1877 // DIFFERENT instantiation of the same generic function. If so,
1878 // substitute it with the correct one for the current instantiation.
1879 for concrete_type_list in w.table.fn_generic_types[w.cur_fn] {
1880 if concrete_type_list.len != generic_names.len {
1881 continue
1882 }
1883 for i, ct in concrete_type_list {
1884 if ct == resolved_left_type && w.cur_fn_concrete_types[i] != ct {
1885 resolved_left_type = w.cur_fn_concrete_types[i]
1886 break
1887 }
1888 }
1889 }
1890 }
1891 }
1892 }
1893 if node.is_method && resolved_left_type != 0 {
1894 w.mark_by_type(resolved_left_type)
1895 left_sym := w.table.sym(resolved_left_type)
1896 w.uses_type_name = w.uses_type_name
1897 || (left_sym.kind in [.sum_type, .interface] && node.name == 'type_name')
1898 if left_sym.info is ast.Aggregate {
1899 for receiver_type in left_sym.info.types {
1900 receiver_sym := w.table.sym(receiver_type)
1901 if m := receiver_sym.find_method(node.name) {
1902 fn_name := '${int(m.receiver_type)}.${node.name}'
1903 if !w.used_fns[fn_name] {
1904 w.fn_by_name(fn_name)
1905 }
1906 }
1907 }
1908 } else if left_sym.info is ast.Interface {
1909 for typ in left_sym.info.types {
1910 sym := w.table.sym(typ)
1911 method, embed_types := w.table.find_method_from_embeds(sym, node.name) or {
1912 ast.Fn{}, []ast.Type{}
1913 }
1914 if embed_types.len != 0 {
1915 w.fn_by_name(method.fkey())
1916 }
1917 }
1918 } else if node.from_embed_types.len != 0 && !resolved_left_type.has_flag(.generic) {
1919 method, embed_types := w.table.find_method_from_embeds(w.table.final_sym(resolved_left_type),
1920 node.name) or { ast.Fn{}, []ast.Type{} }
1921 if embed_types.len != 0 {
1922 w.fn_by_name(method.fkey())
1923 }
1924 } else if node.left_type.has_flag(.generic) || resolved_left_type != node.left_type {
1925 // Generic type parameter or re-resolved type from generic instantiation.
1926 // Use resolve_method_fkey_for_type which handles value/reference type lookup.
1927 concrete_type := if node.left_type.has_flag(.generic) {
1928 w.resolve_current_generic_type(node.left_type)
1929 } else {
1930 resolved_left_type
1931 }
1932 if concrete_type != 0 && !concrete_type.has_flag(.generic) {
1933 resolved_fkey, _ := w.resolve_method_fkey_for_type(concrete_type, node.name)
1934 method_name := if resolved_fkey != '' {
1935 resolved_fkey
1936 } else {
1937 '${int(concrete_type)}.${node.name}'
1938 }
1939 if !w.used_fns[method_name] {
1940 w.fn_by_name(method_name)
1941 }
1942 }
1943 } else {
1944 match left_sym.info {
1945 ast.Array, ast.ArrayFixed {
1946 if !w.uses_arr_void && node.name in ['contains', 'index', 'last_index'] {
1947 if w.table.final_sym(left_sym.info.elem_type).kind == .function {
1948 w.uses_arr_void = true
1949 }
1950 }
1951 }
1952 else {}
1953 }
1954 }
1955 }
1956 w.expr(node.left)
1957 w.or_block(node.or_block)
1958
1959 mut fn_name := node.fkey()
1960 mut receiver_typ := node.receiver_type
1961 if !node.is_method && node.mod != '' {
1962 qualified_name := '${node.mod}.${node.name}'
1963 if qualified_name in w.all_fns {
1964 fn_name = qualified_name
1965 }
1966 }
1967 $if trace_skip_unused_walker ? {
1968 if node.name in ['decode', 'raw_decode', 'decode_value', 'copy_type', 'from_json_string', 'from_json_number']
1969 || fn_name.contains('json2') {
1970 eprintln('>>> call_expr name=${node.name} mod=${node.mod} fkey=${node.fkey()} fn_name=${fn_name} method=${node.is_method} concrete=${node.concrete_types.map(w.table.type_to_str(it))}')
1971 }
1972 }
1973 if node.is_method {
1974 resolved_fkey, resolved_receiver := w.resolve_method_call_fkey(node)
1975 if resolved_fkey != '' {
1976 fn_name = resolved_fkey
1977 receiver_typ = resolved_receiver
1978 }
1979 }
1980 if node.is_method {
1981 if node.left_type != 0 {
1982 lsym := w.table.sym(node.left_type)
1983 // Note: maps and arrays are implemented in `builtin` as concrete types `map` and `array`.
1984 // They are not normal generics expanded, to separate structs, parametrized on the type of the element.
1985 // All []Type or map[Type]Another types are typedefs to those `map` and `array` types, and all map and array methods
1986 // are actually methods on the `builtin` concrete types.
1987 match lsym.kind {
1988 .array {
1989 if !w.used_arr_method[node.name] {
1990 w.mark_builtin_array_method_as_used(node.name)
1991 w.used_arr_method[node.name] = true
1992 }
1993 }
1994 .map {
1995 if !w.used_map_method[node.name] {
1996 w.mark_builtin_map_method_as_used(node.name)
1997 w.used_map_method[node.name] = true
1998 }
1999 }
2000 else {}
2001 }
2002 }
2003 } else if node.is_fn_a_const {
2004 const_fn_name := if fn_name.contains('.') {
2005 fn_name
2006 } else {
2007 '${node.mod}.${fn_name}'
2008 }
2009 if const_fn_name in w.all_consts {
2010 w.mark_const_as_used(const_fn_name)
2011 }
2012 } else if node.is_fn_var {
2013 w.mark_global_as_used(node.name)
2014 }
2015 if node.is_method && node.receiver_type.has_flag(.generic) {
2016 if call_concrete_types.len == 0 && node.left_type != 0 && !node.left_type.has_flag(.generic) {
2017 call_concrete_types = w.receiver_concrete_types(node.left_type)
2018 receiver_typ = node.left_type
2019 }
2020 if call_concrete_types.len == 0 && node.receiver_concrete_type != 0
2021 && !node.receiver_concrete_type.has_flag(.generic) {
2022 call_concrete_types = w.receiver_concrete_types(node.receiver_concrete_type)
2023 receiver_typ = node.receiver_concrete_type
2024 }
2025 concrete_fn_name := '${int(receiver_typ)}.${node.name}'
2026 if concrete_fn_name in w.all_fns {
2027 fn_name = concrete_fn_name
2028 }
2029 }
2030 // Handle concrete instantiations of generic struct methods: when the
2031 // receiver type is a concrete instantiation (e.g. Vec3[f64]) but the
2032 // method is only registered under the parent generic type key (Vec3[T]),
2033 // remap fn_name to the generic key and preserve all registered generic
2034 // types so that other instantiations are not stripped.
2035 if node.is_method && fn_name !in w.all_fns && !node.receiver_type.has_flag(.generic)
2036 && receiver_typ != 0 {
2037 rsym := w.table.sym(receiver_typ)
2038 parent_type := match rsym.info {
2039 ast.Struct { rsym.info.parent_type }
2040 ast.Interface { rsym.info.parent_type }
2041 ast.SumType { rsym.info.parent_type }
2042 ast.GenericInst { ast.new_type(rsym.info.parent_idx) }
2043 else { ast.Type(0) }
2044 }
2045
2046 if parent_type != 0 && parent_type.has_flag(.generic) {
2047 generic_fn_name := '${int(parent_type.set_nr_muls(0))}.${node.name}'
2048 if generic_fn_name in w.all_fns {
2049 fn_name = generic_fn_name
2050 call_concrete_types = w.receiver_concrete_types(receiver_typ)
2051 receiver_typ = parent_type.set_nr_muls(0)
2052 w.keep_all_fn_generic_types[generic_fn_name] = true
2053 }
2054 }
2055 }
2056 w.mark_by_type(node.return_type)
2057 mut resolved_fn_name := fn_name
2058 if fn_name !in w.all_fns {
2059 if fn_name != node.fkey() && node.fkey() in w.all_fns {
2060 resolved_fn_name = node.fkey()
2061 } else {
2062 return
2063 }
2064 }
2065 if mut stmt := w.all_fns[resolved_fn_name] {
2066 if !stmt.should_be_skipped && stmt.name == node.name {
2067 caller_generic_names, caller_concrete_types := w.current_generic_context()
2068 if call_concrete_types.len == 0 {
2069 call_concrete_types = w.trusted_source_concrete_types(node, receiver_typ)
2070 }
2071 if call_concrete_types.len == 0 {
2072 callee_generic_names := w.fn_generic_names(stmt)
2073 if caller_generic_names.len > 0
2074 && caller_generic_names.len == caller_concrete_types.len
2075 && callee_generic_names.len > 0 {
2076 mut inherited_concrete_types := []ast.Type{cap: callee_generic_names.len}
2077 for generic_name in callee_generic_names {
2078 idx := caller_generic_names.index(generic_name)
2079 if idx < 0 || idx >= caller_concrete_types.len {
2080 inherited_concrete_types = []
2081 break
2082 }
2083 inherited_concrete_types << caller_concrete_types[idx]
2084 }
2085 if inherited_concrete_types.len == callee_generic_names.len
2086 && inherited_concrete_types.all(!it.has_flag(.generic)) {
2087 call_concrete_types = inherited_concrete_types.clone()
2088 }
2089 }
2090 }
2091 if call_concrete_types.len == 0 && node.raw_concrete_types.len == 0 {
2092 call_concrete_types = w.resolve_current_concrete_types(node.concrete_types)
2093 }
2094 for concrete_type in call_concrete_types {
2095 w.mark_by_type(concrete_type)
2096 }
2097 generic_call_inside_generic_caller := w.fn_generic_names(stmt).len > 0
2098 && node.raw_concrete_types.len == 0 && caller_generic_names.len > 0
2099 keep_all_generic_types := (stmt.generic_names.len > 0 && call_concrete_types.len == 0)
2100 || generic_call_inside_generic_caller
2101 if keep_all_generic_types {
2102 w.keep_all_fn_generic_types[fn_name] = true
2103 }
2104 can_walk_method := !node.is_method || receiver_typ == stmt.receiver.typ
2105 || (node.receiver_type.has_flag(.generic) && stmt.receiver.typ.has_flag(.generic)
2106 && call_concrete_types.len > 0)
2107 if can_walk_method {
2108 if keep_all_generic_types {
2109 if !w.expanding_fn_generic_types[fn_name] {
2110 w.expanding_fn_generic_types[fn_name] = true
2111 for concrete_type_list in w.table.fn_generic_types[fn_name] {
2112 w.fn_decl_with_concrete_types(mut stmt, concrete_type_list)
2113 }
2114 w.expanding_fn_generic_types.delete(fn_name)
2115 }
2116 } else {
2117 w.fn_decl_with_concrete_types(mut stmt, call_concrete_types)
2118 if node.raw_concrete_types.len == 0 && w.inside_comptime > 0
2119 && w.has_sumtype_generic_context(caller_concrete_types)
2120 && stmt.mod == 'x.json2' && stmt.name in ['copy_type', 'decode_value'] {
2121 for concrete_type_list in w.sumtype_variant_concrete_types(caller_concrete_types) {
2122 if concrete_type_list != call_concrete_types {
2123 w.fn_decl_with_concrete_types(mut stmt, concrete_type_list)
2124 }
2125 }
2126 }
2127 }
2128 }
2129 if node.return_type.has_flag(.option) {
2130 w.used_option++
2131 } else if node.return_type.has_flag(.result) {
2132 w.used_result++
2133 }
2134 callee_generic_names := w.fn_generic_names(stmt)
2135 if ((node.is_method && stmt.params.len > 1) || !node.is_method)
2136 && callee_generic_names.len > 0 {
2137 // mark concrete generic param types (e.g. []T, ...Node[T]) as used
2138 max_param_len := if node.is_method { stmt.params.len - 1 } else { stmt.params.len }
2139 param_i := if node.is_method { 1 } else { 0 }
2140 concrete_type_lists := if call_concrete_types.len > 0 {
2141 [call_concrete_types]
2142 } else {
2143 w.table.fn_generic_types[fn_name]
2144 }
2145 for concrete_type_list in concrete_type_lists {
2146 for k, concrete_type in concrete_type_list {
2147 if k >= max_param_len {
2148 break
2149 }
2150 param_typ := stmt.params[k + param_i].typ
2151 if param_typ.has_flag(.generic) {
2152 if resolved := w.table.convert_generic_type(param_typ,
2153 callee_generic_names, concrete_type_list)
2154 {
2155 w.mark_by_type(resolved)
2156 } else if w.table.type_kind(param_typ) == .array {
2157 w.mark_by_type(w.table.find_or_register_array(concrete_type))
2158 } else if param_typ.has_flag(.option) {
2159 w.used_option++
2160 }
2161 }
2162 }
2163 }
2164 }
2165 }
2166 }
2167}
2168
2169fn (mut w Walker) resolve_method_fkey_for_type(typ ast.Type, method_name string) (string, ast.Type) {
2170 cache_key := u32(typ).str() + ':' + method_name
2171 if cached := w.method_fkey_cache[cache_key] {
2172 return cached.fkey, cached.receiver
2173 }
2174 mut candidate_types := []ast.Type{}
2175 for candidate in [typ] {
2176 if candidate == 0 {
2177 continue
2178 }
2179 if candidate !in candidate_types {
2180 candidate_types << candidate
2181 }
2182 unaliased := w.table.unaliased_type(candidate)
2183 if unaliased != 0 && unaliased != candidate && unaliased !in candidate_types {
2184 candidate_types << unaliased
2185 }
2186 if !candidate.is_ptr() {
2187 ref_typ := candidate.ref()
2188 if ref_typ != 0 && ref_typ !in candidate_types {
2189 candidate_types << ref_typ
2190 }
2191 }
2192 if candidate.is_ptr() {
2193 deref_typ := candidate.deref()
2194 if deref_typ != 0 && deref_typ !in candidate_types {
2195 candidate_types << deref_typ
2196 }
2197 }
2198 }
2199 for candidate in candidate_types {
2200 sym := w.table.sym(candidate)
2201 parent_fkey, parent_receiver := w.generic_parent_method_fkey(sym, method_name)
2202 if parent_fkey != '' {
2203 w.method_fkey_cache[cache_key] = MethodFkeyCacheEntry{
2204 fkey: parent_fkey
2205 receiver: parent_receiver
2206 }
2207 return parent_fkey, parent_receiver
2208 }
2209 if method := sym.find_method_with_generic_parent(method_name) {
2210 fkey, receiver := w.method_decl_fkey(method)
2211 w.method_fkey_cache[cache_key] = MethodFkeyCacheEntry{
2212 fkey: fkey
2213 receiver: receiver
2214 }
2215 return fkey, receiver
2216 }
2217 if method := w.table.find_method(sym, method_name) {
2218 fkey, receiver := w.method_decl_fkey(method)
2219 w.method_fkey_cache[cache_key] = MethodFkeyCacheEntry{
2220 fkey: fkey
2221 receiver: receiver
2222 }
2223 return fkey, receiver
2224 }
2225 method, embed_types := w.table.find_method_from_embeds(w.table.final_sym(candidate),
2226 method_name) or { ast.Fn{}, []ast.Type{} }
2227 if embed_types.len != 0 {
2228 fkey, receiver := w.method_decl_fkey(method)
2229 w.method_fkey_cache[cache_key] = MethodFkeyCacheEntry{
2230 fkey: fkey
2231 receiver: receiver
2232 }
2233 return fkey, receiver
2234 }
2235 }
2236 w.method_fkey_cache[cache_key] = MethodFkeyCacheEntry{
2237 fkey: ''
2238 receiver: ast.no_type
2239 }
2240 return '', ast.no_type
2241}
2242
2243fn (w &Walker) generic_parent_method_fkey(sym ast.TypeSymbol, method_name string) (string, ast.Type) {
2244 match sym.info {
2245 ast.Struct, ast.Interface, ast.SumType {
2246 if sym.info.parent_type.has_flag(.generic) {
2247 parent_sym := w.table.sym(sym.info.parent_type)
2248 if method := parent_sym.find_method(method_name) {
2249 return w.method_decl_fkey(method)
2250 }
2251 }
2252 }
2253 ast.GenericInst {
2254 if sym.info.parent_idx > 0 {
2255 parent_sym := w.table.sym(ast.idx_to_type(sym.info.parent_idx))
2256 if method := parent_sym.find_method(method_name) {
2257 return w.method_decl_fkey(method)
2258 }
2259 }
2260 }
2261 else {}
2262 }
2263
2264 return '', ast.no_type
2265}
2266
2267fn (w &Walker) method_decl_fkey(method ast.Fn) (string, ast.Type) {
2268 if method.source_fn != unsafe { nil } {
2269 fndecl := unsafe { &ast.FnDecl(method.source_fn) }
2270 return fndecl.fkey(), fndecl.receiver.typ
2271 }
2272 return method.fkey(), method.receiver_type
2273}
2274
2275fn (mut w Walker) resolve_method_call_fkey(node ast.CallExpr) (string, ast.Type) {
2276 mut candidate_types := []ast.Type{}
2277 if node.left is ast.Ident && node.left.obj is ast.Var {
2278 resolved_current_type := w.resolve_current_specialized_var_type(node.left.name)
2279 if resolved_current_type != 0 && resolved_current_type !in candidate_types {
2280 candidate_types << resolved_current_type
2281 } else if node.left.obj.typ.has_flag(.generic) {
2282 generic_names, concrete_types := w.specialized_generic_context_for(w.cur_fn)
2283 if generic_names.len > 0 && generic_names.len == concrete_types.len {
2284 mut muttable := unsafe { &ast.Table(w.table) }
2285 if resolved := muttable.convert_generic_type(node.left.obj.typ, generic_names,
2286 concrete_types)
2287 {
2288 resolved_type := resolved.clear_flag(.generic)
2289 if resolved_type != 0 && resolved_type !in candidate_types {
2290 candidate_types << resolved_type
2291 }
2292 }
2293 }
2294 }
2295 }
2296 for typ in [node.left_type, node.receiver_concrete_type, node.receiver_type] {
2297 if typ == 0 {
2298 continue
2299 }
2300 if typ !in candidate_types {
2301 candidate_types << typ
2302 }
2303 unaliased := w.table.unaliased_type(typ)
2304 if unaliased != 0 && unaliased != typ && unaliased !in candidate_types {
2305 candidate_types << unaliased
2306 }
2307 if !typ.is_ptr() {
2308 ref_typ := typ.ref()
2309 if ref_typ != 0 && ref_typ !in candidate_types {
2310 candidate_types << ref_typ
2311 }
2312 }
2313 if typ.is_ptr() {
2314 deref_typ := typ.deref()
2315 if deref_typ != 0 && deref_typ !in candidate_types {
2316 candidate_types << deref_typ
2317 }
2318 }
2319 }
2320 for typ in candidate_types {
2321 resolved_fkey, resolved_receiver := w.resolve_method_fkey_for_type(typ, node.name)
2322 if resolved_fkey != '' {
2323 return resolved_fkey, resolved_receiver
2324 }
2325 }
2326 return '', ast.no_type
2327}
2328
2329fn (w &Walker) current_fn_concrete_types_list() [][]ast.Type {
2330 if w.cur_fn == '' {
2331 return [][]ast.Type{}
2332 }
2333 generic_names, concrete_types := w.specialized_generic_context_for(w.cur_fn)
2334 if generic_names.len > 0 && generic_names.len == concrete_types.len {
2335 return [concrete_types]
2336 }
2337 return w.table.fn_generic_types[w.cur_fn] or { [][]ast.Type{} }
2338}
2339
2340fn (w &Walker) specialized_generic_context_for(fn_name string) ([]string, []ast.Type) {
2341 if fn_name == '' || !fn_name.contains('_T_') {
2342 return []string{}, []ast.Type{}
2343 }
2344 for generic_fn in w.generic_fns {
2345 if generic_fn.generic_names.len == 0 {
2346 continue
2347 }
2348 for base_name in [generic_fn.name, generic_fn.fkey()] {
2349 if !fn_name.starts_with(base_name + '_T_') {
2350 continue
2351 }
2352 for concrete_types in w.table.fn_generic_types[generic_fn.fkey()] {
2353 if concrete_types.any(it.has_flag(.generic)) {
2354 continue
2355 }
2356 if w.generic_concrete_name(base_name, concrete_types) == fn_name {
2357 return generic_fn.generic_names.clone(), concrete_types.clone()
2358 }
2359 }
2360 }
2361 }
2362 return []string{}, []ast.Type{}
2363}
2364
2365fn (w &Walker) resolve_current_specialized_type(typ ast.Type) ast.Type {
2366 if typ == 0 {
2367 return ast.no_type
2368 }
2369 mut generic_names, mut concrete_types := w.specialized_generic_context_for(w.cur_fn)
2370 if (generic_names.len == 0 || generic_names.len != concrete_types.len) && w.cur_fn != ''
2371 && w.cur_fn_concrete_types.len > 0 {
2372 if cur_fn_decl := w.all_fns[w.cur_fn] {
2373 generic_names = w.fn_generic_names(cur_fn_decl)
2374 concrete_types = w.cur_fn_concrete_types.clone()
2375 }
2376 }
2377 if generic_names.len == 0 || generic_names.len != concrete_types.len {
2378 return typ.clear_flag(.generic)
2379 }
2380 mut muttable := unsafe { &ast.Table(w.table) }
2381 if resolved := muttable.convert_generic_type(typ, generic_names, concrete_types) {
2382 return resolved.clear_flag(.generic)
2383 }
2384 return typ.clear_flag(.generic)
2385}
2386
2387fn (w &Walker) generic_concrete_name(base_name string, concrete_types []ast.Type) string {
2388 mut name := base_name
2389 if concrete_types.len > 0 {
2390 name += '_T'
2391 }
2392 for typ in concrete_types {
2393 name += '_' + w.table.sym(typ.set_nr_muls(0)).scoped_cname()
2394 }
2395 return name
2396}
2397
2398fn (w &Walker) specialized_var_type_cache_key(var_name string) string {
2399 mut key := w.cur_fn + '\x00' + var_name
2400 if !w.cur_fn.contains('_T_') {
2401 for typ in w.cur_fn_concrete_types {
2402 key += '\x00' + u32(typ).str()
2403 }
2404 }
2405 return key
2406}
2407
2408fn (mut w Walker) resolve_current_specialized_var_type(var_name string) ast.Type {
2409 if var_name == '' || w.cur_fn == '' {
2410 return ast.no_type
2411 }
2412 cache_key := w.specialized_var_type_cache_key(var_name)
2413 if cached := w.specialized_var_type_cache[cache_key] {
2414 return cached.typ
2415 }
2416 mut base_name := w.cur_fn
2417 if w.cur_fn.contains('_T_') {
2418 base_name = w.cur_fn.all_before('_T_')
2419 }
2420 mut base_fn := w.all_fns[base_name] or { ast.FnDecl{} }
2421 if base_fn.name == '' {
2422 for generic_fn in w.generic_fns {
2423 if generic_fn.name == base_name || generic_fn.fkey() == base_name {
2424 base_fn = *generic_fn
2425 break
2426 }
2427 }
2428 if base_fn.name == '' {
2429 w.specialized_var_type_cache[cache_key] = SpecializedVarTypeCacheEntry{
2430 typ: ast.no_type
2431 }
2432 return ast.no_type
2433 }
2434 }
2435 mut generic_names := []string{}
2436 mut concrete_types := []ast.Type{}
2437 if w.cur_fn.contains('_T_') {
2438 generic_names, concrete_types = w.specialized_generic_context_for(w.cur_fn)
2439 } else if w.cur_fn_concrete_types.len > 0
2440 && base_fn.generic_names.len == w.cur_fn_concrete_types.len {
2441 generic_names = base_fn.generic_names.clone()
2442 concrete_types = w.cur_fn_concrete_types.clone()
2443 }
2444 if generic_names.len == 0 || generic_names.len != concrete_types.len {
2445 w.specialized_var_type_cache[cache_key] = SpecializedVarTypeCacheEntry{
2446 typ: ast.no_type
2447 }
2448 return ast.no_type
2449 }
2450 for param in base_fn.params {
2451 if param.name != var_name {
2452 continue
2453 }
2454 mut muttable := unsafe { &ast.Table(w.table) }
2455 if resolved := muttable.convert_generic_type(param.typ, generic_names, concrete_types) {
2456 resolved_typ := resolved.clear_flag(.generic)
2457 w.specialized_var_type_cache[cache_key] = SpecializedVarTypeCacheEntry{
2458 typ: resolved_typ
2459 }
2460 return resolved_typ
2461 }
2462 resolved_typ := param.typ.clear_flag(.generic)
2463 w.specialized_var_type_cache[cache_key] = SpecializedVarTypeCacheEntry{
2464 typ: resolved_typ
2465 }
2466 return resolved_typ
2467 }
2468 w.specialized_var_type_cache[cache_key] = SpecializedVarTypeCacheEntry{
2469 typ: ast.no_type
2470 }
2471 return ast.no_type
2472}
2473
2474fn (w &Walker) receiver_concrete_types_for_type(typ ast.Type) []ast.Type {
2475 if typ == 0 {
2476 return []ast.Type{}
2477 }
2478 sym := w.table.sym(typ)
2479 match sym.info {
2480 ast.Struct, ast.Interface, ast.SumType {
2481 mut concrete_types := sym.info.concrete_types.clone()
2482 if concrete_types.len == 0 && sym.generic_types.len == sym.info.generic_types.len
2483 && sym.generic_types != sym.info.generic_types {
2484 concrete_types = sym.generic_types.clone()
2485 }
2486 return concrete_types.map(it.clear_flag(.generic))
2487 }
2488 ast.GenericInst {
2489 return sym.info.concrete_types.map(it.clear_flag(.generic))
2490 }
2491 else {
2492 return []ast.Type{}
2493 }
2494 }
2495}
2496
2497fn (w &Walker) call_specialization_fkey(node ast.CallExpr, stmt ast.FnDecl, base_fkey string, receiver_typ ast.Type) string {
2498 if base_fkey == '' || stmt.generic_names.len == 0 {
2499 return base_fkey
2500 }
2501 source_concrete_types := if node.raw_concrete_types.len > 0 {
2502 node.raw_concrete_types
2503 } else {
2504 node.concrete_types
2505 }
2506 mut concrete_types := []ast.Type{}
2507 for concrete_type in source_concrete_types {
2508 resolved_type := w.resolve_current_specialized_type(concrete_type)
2509 if resolved_type == 0 || resolved_type.has_flag(.generic) {
2510 return base_fkey
2511 }
2512 concrete_types << resolved_type
2513 }
2514 if node.is_method && concrete_types.len < stmt.generic_names.len {
2515 receiver_generic_names := if stmt.params.len > 0 {
2516 mut muttable := unsafe { &ast.Table(w.table) }
2517 muttable.generic_type_names(stmt.params[0].typ)
2518 } else {
2519 []string{}
2520 }
2521 if receiver_generic_names.len > 0 {
2522 mut receiver_concrete_types := w.receiver_concrete_types_for_type(receiver_typ)
2523 if receiver_concrete_types.len == 0 && node.receiver_concrete_type != 0 {
2524 receiver_concrete_types =
2525 w.receiver_concrete_types_for_type(node.receiver_concrete_type)
2526 }
2527 if receiver_concrete_types.len > 0 {
2528 if concrete_types.len == 0 {
2529 concrete_types = receiver_concrete_types.clone()
2530 } else if receiver_generic_names.len + concrete_types.len == stmt.generic_names.len {
2531 mut merged_types := receiver_concrete_types.clone()
2532 merged_types << concrete_types
2533 concrete_types = merged_types.clone()
2534 }
2535 }
2536 }
2537 }
2538 if concrete_types.len == 0 || concrete_types.len != stmt.generic_names.len {
2539 return base_fkey
2540 }
2541 return w.generic_concrete_name(base_fkey, concrete_types)
2542}
2543
2544pub fn (mut w Walker) fn_by_name(fn_name string) {
2545 if w.used_fns[fn_name] {
2546 return
2547 }
2548 if mut stmt := w.all_fns[fn_name] {
2549 w.fn_decl(mut stmt)
2550 } else {
2551 if fn_name.contains('_T_') {
2552 base_name := fn_name.all_before('_T_')
2553 if mut base_fn := w.all_fns[base_name] {
2554 w.fn_decl_with_fkey(mut base_fn, fn_name)
2555 } else {
2556 for generic_fn in w.generic_fns {
2557 if generic_fn.name == base_name || generic_fn.fkey() == base_name {
2558 mut gfn := *generic_fn
2559 w.fn_decl_with_fkey(mut gfn, fn_name)
2560 break
2561 }
2562 }
2563 }
2564 }
2565 }
2566}
2567
2568pub fn (mut w Walker) struct_fields(sfields []ast.StructField) {
2569 for sf in sfields {
2570 if sf.has_default_expr {
2571 w.expr(sf.default_expr)
2572 }
2573 if !w.uses_atomic && sf.typ.has_flag(.atomic_f) {
2574 w.uses_atomic = true
2575 }
2576 }
2577}
2578
2579pub fn (mut w Walker) const_fields(cfields []ast.ConstField) {
2580 for cf in cfields {
2581 w.expr(cf.expr)
2582 }
2583}
2584
2585fn (w &Walker) infer_expr_type(expr ast.Expr) ast.Type {
2586 match expr {
2587 ast.ArrayInit {
2588 return expr.typ
2589 }
2590 ast.AsCast {
2591 return expr.typ
2592 }
2593 ast.BoolLiteral {
2594 return ast.bool_type
2595 }
2596 ast.CallExpr {
2597 return expr.return_type
2598 }
2599 ast.CastExpr {
2600 return expr.typ
2601 }
2602 ast.ChanInit {
2603 return expr.typ
2604 }
2605 ast.CharLiteral {
2606 return ast.u8_type
2607 }
2608 ast.FloatLiteral {
2609 return ast.f64_type
2610 }
2611 ast.Ident {
2612 return match expr.obj {
2613 ast.AsmRegister {
2614 expr.obj.typ
2615 }
2616 ast.ConstField {
2617 expr.obj.typ
2618 }
2619 ast.GlobalField {
2620 expr.obj.typ
2621 }
2622 ast.Var {
2623 if expr.obj.smartcasts.len > 0 {
2624 expr.obj.smartcasts.last()
2625 } else {
2626 expr.obj.typ
2627 }
2628 }
2629 else {
2630 ast.void_type
2631 }
2632 }
2633 }
2634 ast.IfExpr {
2635 return expr.typ
2636 }
2637 ast.IndexExpr {
2638 return expr.typ
2639 }
2640 ast.IntegerLiteral {
2641 return ast.int_type
2642 }
2643 ast.MapInit {
2644 return expr.typ
2645 }
2646 ast.MatchExpr {
2647 return expr.return_type
2648 }
2649 ast.ParExpr {
2650 return w.infer_expr_type(expr.expr)
2651 }
2652 ast.PostfixExpr {
2653 return expr.typ
2654 }
2655 ast.PrefixExpr {
2656 return expr.right_type
2657 }
2658 ast.SelectorExpr {
2659 return expr.typ
2660 }
2661 ast.StringInterLiteral, ast.StringLiteral {
2662 return ast.string_type
2663 }
2664 ast.UnsafeExpr {
2665 return w.infer_expr_type(expr.expr)
2666 }
2667 else {
2668 return ast.void_type
2669 }
2670 }
2671}
2672
2673@[inline]
2674pub fn (mut w Walker) or_block(node ast.OrExpr) {
2675 if node.kind == .block {
2676 w.uses_err_block = true
2677 w.stmts(node.stmts)
2678 } else if node.kind == .propagate_option {
2679 w.used_option++
2680 w.used_panic++
2681 } else if node.kind == .propagate_result {
2682 w.used_result++
2683 w.used_panic++
2684 }
2685}
2686
2687pub fn (mut w Walker) mark_fn_ret_and_params(return_type ast.Type, params []ast.Param) {
2688 if return_type != 0 {
2689 if return_type.has_flag(.option) {
2690 w.used_option++
2691 } else if return_type.has_flag(.result) {
2692 w.used_result++
2693 }
2694 w.mark_by_type(return_type.clear_option_and_result())
2695 }
2696 for param in params {
2697 w.mark_by_type(param.typ)
2698 }
2699}
2700
2701@[inline]
2702pub fn (mut w Walker) mark_by_sym_name(name string) {
2703 if sym := w.table.find_sym(name) {
2704 w.mark_by_sym(sym)
2705 }
2706}
2707
2708@[inline]
2709pub fn (mut w Walker) mark_by_type(typ ast.Type) {
2710 if typ == 0 || typ.has_flag(.generic) {
2711 return
2712 }
2713 if typ in w.used_types {
2714 return
2715 }
2716 w.used_types[typ] = true
2717 cleared_typ := typ.clear_option_and_result()
2718 if cleared_typ != typ {
2719 w.mark_by_type(cleared_typ)
2720 return
2721 }
2722 w.mark_by_sym(w.table.sym(typ))
2723}
2724
2725pub fn (mut w Walker) mark_by_sym(isym ast.TypeSymbol) {
2726 if isym.idx in w.used_syms {
2727 return
2728 }
2729 w.used_syms[isym.idx] = true
2730 match isym.info {
2731 ast.Struct {
2732 for ifield in isym.info.fields {
2733 if ifield.has_default_expr {
2734 w.expr(ifield.default_expr)
2735 }
2736 if ifield.typ != 0 {
2737 fsym := w.table.sym(ifield.typ.idx())
2738 if ifield.typ.has_flag(.option) {
2739 w.used_option++
2740 if !ifield.has_default_expr {
2741 w.used_none++
2742 }
2743 }
2744 w.mark_by_sym(fsym)
2745 }
2746 if !w.features.auto_str_ptr && ifield.typ.is_ptr()
2747 && isym.idx in w.features.print_types {
2748 w.features.auto_str_ptr = true
2749 }
2750 }
2751 for embed in isym.info.embeds {
2752 w.mark_by_type(embed)
2753 }
2754 if decl := w.all_structs[isym.name] {
2755 w.struct_fields(decl.fields)
2756 w.uses_mem_align = w.uses_mem_align || decl.is_aligned
2757 for iface_typ in decl.implements_types {
2758 w.mark_by_type(iface_typ.typ)
2759 iface_sym := w.table.sym(iface_typ.typ)
2760 for method in iface_sym.methods {
2761 if impl_method := isym.find_method_with_generic_parent(method.name) {
2762 w.fn_by_name(impl_method.fkey())
2763 } else {
2764 impl_method, _ := w.table.find_method_from_embeds(isym, method.name) or {
2765 ast.Fn{}, []ast.Type{}
2766 }
2767 if impl_method.name != '' {
2768 w.fn_by_name(impl_method.fkey())
2769 }
2770 }
2771 }
2772 }
2773 }
2774 }
2775 ast.ArrayFixed, ast.Array {
2776 if !w.uses_array && !w.is_direct_array_access {
2777 w.uses_array = true
2778 }
2779 if isym.info.elem_type.has_flag(.option) {
2780 w.used_option++
2781 }
2782 w.mark_by_type(isym.info.elem_type)
2783 }
2784 ast.SumType {
2785 for typ in isym.info.variants {
2786 if typ == ast.map_type {
2787 w.features.used_maps++
2788 continue
2789 }
2790 if typ.has_flag(.option) {
2791 w.used_option++
2792 }
2793 sym := w.table.sym(typ)
2794 w.mark_by_sym(sym)
2795 w.uses_array_sumtype = w.uses_array_sumtype || sym.kind == .array
2796 }
2797 }
2798 ast.Map {
2799 w.mark_by_type(isym.info.key_type)
2800 w.mark_by_type(isym.info.value_type)
2801 w.features.used_maps++
2802 if isym.info.value_type.has_flag(.option) {
2803 w.used_option++
2804 }
2805 }
2806 ast.Alias {
2807 w.mark_by_type(isym.info.parent_type)
2808 }
2809 ast.FnType {
2810 for param in isym.info.func.params {
2811 w.mark_by_type(param.typ)
2812 }
2813 if isym.info.func.return_type != 0 {
2814 w.mark_by_type(isym.info.func.return_type.clear_option_and_result())
2815 }
2816 }
2817 ast.MultiReturn {
2818 for typ in isym.info.types {
2819 w.mark_by_type(typ)
2820 }
2821 }
2822 ast.Chan {
2823 w.uses_channel = true
2824 w.mark_by_type(isym.info.elem_type)
2825 }
2826 ast.Aggregate {
2827 for typ in isym.info.types {
2828 w.mark_by_type(typ)
2829 }
2830 }
2831 ast.GenericInst {
2832 parent_typ := ast.new_type(isym.info.parent_idx)
2833 w.mark_by_type(parent_typ)
2834 for concrete_type in isym.info.concrete_types {
2835 w.mark_by_type(concrete_type)
2836 }
2837 parent_sym := w.table.sym(parent_typ)
2838 match parent_sym.info {
2839 ast.Struct {
2840 generic_names := parent_sym.info.generic_types.map(w.table.sym(it).name)
2841 for field in parent_sym.info.fields {
2842 if resolved := w.table.convert_generic_type(field.typ, generic_names,
2843 isym.info.concrete_types)
2844 {
2845 w.mark_by_type(resolved)
2846 } else {
2847 w.mark_by_type(field.typ)
2848 }
2849 }
2850 for embed in parent_sym.info.embeds {
2851 if resolved := w.table.convert_generic_type(embed, generic_names,
2852 isym.info.concrete_types)
2853 {
2854 w.mark_by_type(resolved)
2855 } else {
2856 w.mark_by_type(embed)
2857 }
2858 }
2859 }
2860 ast.SumType {
2861 generic_names := parent_sym.info.generic_types.map(w.table.sym(it).name)
2862 for variant in parent_sym.info.variants {
2863 if resolved := w.table.convert_generic_type(variant, generic_names,
2864 isym.info.concrete_types)
2865 {
2866 w.mark_by_type(resolved)
2867 } else {
2868 w.mark_by_type(variant)
2869 }
2870 }
2871 }
2872 else {}
2873 }
2874 }
2875 ast.Enum {
2876 w.mark_by_type(isym.info.typ)
2877 if enum_ := w.table.enum_decls[isym.name] {
2878 for field in enum_.fields {
2879 if field.has_expr {
2880 w.expr(field.expr)
2881 }
2882 }
2883 }
2884 }
2885 ast.Interface {
2886 for typ in isym.info.types {
2887 if typ == ast.map_type {
2888 w.features.used_maps++
2889 }
2890 w.mark_by_type(typ)
2891 for method in isym.info.methods {
2892 for ityp in [typ.set_nr_muls(1), typ.set_nr_muls(0)] {
2893 mname := int(ityp.clear_flags()).str() + '.' + method.name
2894 w.fn_by_name(mname)
2895 }
2896 }
2897 for embed_method in w.table.get_embed_methods(w.table.sym(typ)) {
2898 mname := int(embed_method.params[0].typ.clear_flags()).str() + '.' +
2899 embed_method.name
2900 w.fn_by_name(mname)
2901 }
2902 }
2903 for embed in isym.info.embeds {
2904 w.mark_by_type(embed)
2905 }
2906 for generic_type in isym.info.generic_types {
2907 w.mark_by_type(generic_type)
2908 }
2909 w.mark_by_type(isym.info.parent_type)
2910 for field in isym.info.fields {
2911 w.mark_by_type(field.typ)
2912 }
2913 for method in isym.methods {
2914 w.mark_by_type(method.receiver_type)
2915 w.mark_fn_ret_and_params(method.return_type, method.params)
2916 }
2917 }
2918 ast.Thread {
2919 w.mark_by_type(isym.info.return_type)
2920 }
2921 else {}
2922 }
2923}
2924
2925fn (mut w Walker) remove_unused_fn_generic_types() {
2926 // Phase 1: Compute the union of concrete type lists per generic
2927 // receiver type, grouped by arity (number of type parameters).
2928 // Include types from ALL methods (walked and un-walked) in fn_generic_types,
2929 // so that every method on the same receiver type ends up with a consistent
2930 // set of concrete instantiations.
2931 mut receiver_used_types := map[string]map[int][][]ast.Type{}
2932 // First, collect from walked methods (used_fn_generic_types)
2933 for nkey, concrete_types in w.used_fn_generic_types {
2934 if fn_decl := w.all_fns[nkey] {
2935 if fn_decl.receiver.typ.has_flag(.generic) {
2936 rkey := int(fn_decl.receiver.typ).str()
2937 actual_types := if w.keep_all_fn_generic_types[nkey] {
2938 w.table.fn_generic_types[nkey]
2939 } else {
2940 concrete_types
2941 }
2942 for ctl in actual_types {
2943 arity := ctl.len
2944 if ctl !in receiver_used_types[rkey][arity] {
2945 receiver_used_types[rkey][arity] << ctl
2946 }
2947 }
2948 }
2949 }
2950 }
2951 // Also collect from un-walked methods in fn_generic_types whose receiver
2952 // type is already in used_syms (the struct instantiation is used).
2953 for fkey, fgt_types in w.table.fn_generic_types {
2954 if fkey in w.used_fn_generic_types || w.keep_all_fn_generic_types[fkey] {
2955 continue
2956 }
2957 if fn_decl := w.all_fns[fkey] {
2958 if fn_decl.receiver.typ.has_flag(.generic) {
2959 rkey := int(fn_decl.receiver.typ).str()
2960 for ctl in fgt_types {
2961 arity := ctl.len
2962 if ctl !in receiver_used_types[rkey][arity] {
2963 receiver_used_types[rkey][arity] << ctl
2964 }
2965 }
2966 }
2967 }
2968 }
2969 // Phase 2: Strip walked methods. For methods on generic receiver types,
2970 // use the arity-matched union to ensure consistency across all methods on
2971 // the same receiver type.
2972 for nkey, concrete_types in w.used_fn_generic_types {
2973 if concrete_types.len == 0 || w.keep_all_fn_generic_types[nkey] {
2974 continue
2975 }
2976 if fn_decl := w.all_fns[nkey] {
2977 if fn_decl.receiver.typ.has_flag(.generic) {
2978 rkey := int(fn_decl.receiver.typ).str()
2979 if arity_map := receiver_used_types[rkey] {
2980 if concrete_types.len > 0 {
2981 arity := concrete_types[0].len
2982 if union_types := arity_map[arity] {
2983 w.table.fn_generic_types[nkey] = union_types.clone()
2984 continue
2985 }
2986 }
2987 }
2988 }
2989 }
2990 w.table.fn_generic_types[nkey] = concrete_types.clone()
2991 }
2992 // Phase 3: Strip un-walked methods on generic receiver types to the
2993 // arity-matched union. This ensures un-walked methods don't keep a wider
2994 // set of concrete types than walked methods, preventing C errors when
2995 // un-walked method bodies call walked (stripped) methods.
2996 // Also collect these un-walked methods so we can walk them in Phase 4.
2997 mut unwalked_methods := map[string]bool{}
2998 for fkey, fgt_types in w.table.fn_generic_types {
2999 if fkey in w.used_fn_generic_types || w.keep_all_fn_generic_types[fkey] {
3000 continue
3001 }
3002 if fgt_types.len == 0 {
3003 continue
3004 }
3005 mut rkey := ''
3006 if fn_decl := w.all_fns[fkey] {
3007 if fn_decl.receiver.typ.has_flag(.generic) {
3008 rkey = int(fn_decl.receiver.typ).str()
3009 }
3010 }
3011 if rkey == '' {
3012 dot_idx := fkey.index_u8(`.`)
3013 if dot_idx > 0 {
3014 rkey = fkey[..dot_idx]
3015 }
3016 }
3017 if rkey == '' {
3018 continue
3019 }
3020 if arity_map := receiver_used_types[rkey] {
3021 if fgt_types.len > 0 {
3022 arity := fgt_types[0].len
3023 if union_types := arity_map[arity] {
3024 w.table.fn_generic_types[fkey] = union_types.clone()
3025 unwalked_methods[fkey] = true
3026 }
3027 }
3028 }
3029 }
3030 // Phase 4: Walk un-walked method bodies so that any internal method
3031 // calls (e.g. ec.generate_id() inside wait()) get properly marked.
3032 // Iterate until no new un-walked methods are discovered.
3033 // Also walk already-walked methods that got new concrete types from Phase 2.
3034 // Phase 2 may add new concrete type lists (from the receiver union) that
3035 // were not walked during the initial traversal.
3036 for nkey, used_concrete_types in w.used_fn_generic_types {
3037 if w.keep_all_fn_generic_types[nkey] {
3038 continue
3039 }
3040 for concrete_type_list in w.table.fn_generic_types[nkey] {
3041 if concrete_type_list !in used_concrete_types {
3042 unwalked_methods[nkey] = true
3043 }
3044 }
3045 }
3046 for unwalked_methods.len > 0 {
3047 mut next_unwalked := map[string]bool{}
3048 for fkey, _ in unwalked_methods {
3049 if mut fn_decl := w.all_fns[fkey] {
3050 for concrete_type_list in w.table.fn_generic_types[fkey] {
3051 w.fn_decl_with_concrete_types(mut fn_decl, concrete_type_list)
3052 }
3053 }
3054 }
3055 // Check if walking those methods discovered new un-walked methods
3056 for fkey2, fgt_types2 in w.table.fn_generic_types {
3057 if fkey2 in w.used_fn_generic_types || w.keep_all_fn_generic_types[fkey2]
3058 || fkey2 in unwalked_methods {
3059 continue
3060 }
3061 if fgt_types2.len == 0 {
3062 continue
3063 }
3064 mut rkey2 := ''
3065 if fn_decl2 := w.all_fns[fkey2] {
3066 if fn_decl2.receiver.typ.has_flag(.generic) {
3067 rkey2 = int(fn_decl2.receiver.typ).str()
3068 }
3069 }
3070 if rkey2 == '' {
3071 dot_idx := fkey2.index_u8(`.`)
3072 if dot_idx > 0 {
3073 rkey2 = fkey2[..dot_idx]
3074 }
3075 }
3076 if rkey2 == '' {
3077 continue
3078 }
3079 if fkey2 !in w.used_fn_generic_types {
3080 if arity_map := receiver_used_types[rkey2] {
3081 if fgt_types2.len > 0 {
3082 arity := fgt_types2[0].len
3083 if union_types := arity_map[arity] {
3084 w.table.fn_generic_types[fkey2] = union_types.clone()
3085 next_unwalked[fkey2] = true
3086 }
3087 }
3088 }
3089 }
3090 }
3091 unwalked_methods = next_unwalked.move()
3092 }
3093 // Phase 5: Propagate newly-discovered concrete types from walking
3094 // (used_fn_generic_types / walked_fn_generic_types) back into
3095 // fn_generic_types so that the cgen emits all required instantiations.
3096 // Phase 4 may walk generic functions (including non-method ones) with
3097 // new concrete types that were not in fn_generic_types originally.
3098 for fkey, walked_types in w.walked_fn_generic_types {
3099 for ctl in walked_types {
3100 if ctl !in w.table.fn_generic_types[fkey] {
3101 w.table.fn_generic_types[fkey] << ctl
3102 }
3103 }
3104 }
3105 // Phase 6: Prune non-method generic functions to the concrete type sets
3106 // that were actually reached during the markused walk.
3107 for fkey, _ in w.table.fn_generic_types {
3108 if w.keep_all_fn_generic_types[fkey] {
3109 continue
3110 }
3111 fn_decl := w.all_fns[fkey] or { continue }
3112 if fn_decl.is_method {
3113 if w.table.generic_type_names(fn_decl.receiver.typ).len > 0 {
3114 continue
3115 }
3116 mut kept_types := [][]ast.Type{}
3117 for concrete_type_list in w.used_fn_generic_types[fkey] {
3118 if concrete_type_list !in kept_types {
3119 kept_types << concrete_type_list.clone()
3120 }
3121 }
3122 for concrete_type_list in w.walked_fn_generic_types[fkey] {
3123 if concrete_type_list !in kept_types {
3124 kept_types << concrete_type_list.clone()
3125 }
3126 }
3127 w.table.fn_generic_types[fkey] = kept_types
3128 continue
3129 }
3130 mut kept_types := [][]ast.Type{}
3131 for concrete_type_list in w.used_fn_generic_types[fkey] {
3132 if concrete_type_list !in kept_types {
3133 kept_types << concrete_type_list.clone()
3134 }
3135 }
3136 for concrete_type_list in w.walked_fn_generic_types[fkey] {
3137 if concrete_type_list !in kept_types {
3138 kept_types << concrete_type_list.clone()
3139 }
3140 }
3141 w.table.fn_generic_types[fkey] = kept_types
3142 }
3143}
3144
3145fn (mut w Walker) mark_emitted_generic_body_dependencies() {
3146 for generic_fn in w.generic_fns {
3147 w.mark_generic_body_dependencies_in_stmts(generic_fn.stmts)
3148 }
3149 for fkey, concrete_types in w.table.fn_generic_types {
3150 if concrete_types.len == 0 {
3151 continue
3152 }
3153 if fn_decl := w.all_fns[fkey] {
3154 w.mark_generic_body_dependencies_in_stmts(fn_decl.stmts)
3155 }
3156 }
3157}
3158
3159fn (mut w Walker) mark_comptime_resource_kind(kind ast.ComptimeForKind) {
3160 match kind {
3161 .attributes {
3162 w.uses_ct_attribute = true
3163 }
3164 .variants {
3165 w.uses_ct_variants = true
3166 }
3167 .params {
3168 w.uses_ct_params = true
3169 }
3170 .values {
3171 w.uses_ct_values = true
3172 }
3173 .fields {
3174 w.uses_ct_fields = true
3175 }
3176 .methods {
3177 w.uses_ct_methods = true
3178 }
3179 }
3180}
3181
3182fn (mut w Walker) mark_generic_body_dependencies_in_stmts(stmts []ast.Stmt) {
3183 for stmt_ in stmts {
3184 stmt := unsafe { stmt_ }
3185 match stmt {
3186 ast.AssignStmt {
3187 for expr in stmt.left {
3188 w.mark_generic_body_dependencies_in_expr(expr)
3189 }
3190 for expr in stmt.right {
3191 w.mark_generic_body_dependencies_in_expr(expr)
3192 }
3193 }
3194 ast.Block {
3195 w.mark_generic_body_dependencies_in_stmts(stmt.stmts)
3196 }
3197 ast.ComptimeFor {
3198 w.mark_comptime_resource_kind(stmt.kind)
3199 w.mark_generic_body_dependencies_in_stmts(stmt.stmts)
3200 }
3201 ast.ExprStmt {
3202 w.mark_generic_body_dependencies_in_expr(stmt.expr)
3203 }
3204 ast.ForCStmt {
3205 w.mark_generic_body_dependencies_in_stmts(stmt.stmts)
3206 }
3207 ast.ForInStmt {
3208 w.mark_generic_body_dependencies_in_stmts(stmt.stmts)
3209 }
3210 ast.ForStmt {
3211 w.mark_generic_body_dependencies_in_stmts(stmt.stmts)
3212 }
3213 ast.Return {
3214 for expr in stmt.exprs {
3215 w.mark_generic_body_dependencies_in_expr(expr)
3216 }
3217 }
3218 else {}
3219 }
3220 }
3221}
3222
3223fn (mut w Walker) mark_generic_body_dependencies_in_expr(expr_ ast.Expr) {
3224 expr := unsafe { expr_ }
3225 match expr {
3226 ast.AnonFn {
3227 w.mark_generic_body_dependencies_in_stmts(expr.decl.stmts)
3228 }
3229 ast.CallExpr {
3230 w.mark_direct_non_generic_call(expr)
3231 w.mark_generic_body_dependencies_in_expr(expr.left)
3232 for arg in expr.args {
3233 w.mark_generic_body_dependencies_in_expr(arg.expr)
3234 }
3235 }
3236 ast.IfExpr {
3237 for branch in expr.branches {
3238 w.mark_generic_body_dependencies_in_expr(branch.cond)
3239 w.mark_generic_body_dependencies_in_stmts(branch.stmts)
3240 }
3241 }
3242 ast.InfixExpr {
3243 w.mark_generic_body_dependencies_in_expr(expr.left)
3244 w.mark_generic_body_dependencies_in_expr(expr.right)
3245 }
3246 ast.MatchExpr {
3247 for branch in expr.branches {
3248 w.mark_generic_body_dependencies_in_stmts(branch.stmts)
3249 }
3250 }
3251 ast.ParExpr {
3252 w.mark_generic_body_dependencies_in_expr(expr.expr)
3253 }
3254 ast.PrefixExpr {
3255 w.mark_generic_body_dependencies_in_expr(expr.right)
3256 }
3257 ast.StringInterLiteral {
3258 for sub_expr in expr.exprs {
3259 w.mark_generic_body_dependencies_in_expr(sub_expr)
3260 }
3261 for sub_expr in expr.fwidth_exprs {
3262 w.mark_generic_body_dependencies_in_expr(sub_expr)
3263 }
3264 for sub_expr in expr.precision_exprs {
3265 w.mark_generic_body_dependencies_in_expr(sub_expr)
3266 }
3267 }
3268 else {}
3269 }
3270}
3271
3272fn (mut w Walker) mark_direct_non_generic_call(node ast.CallExpr) {
3273 if node.language == .c {
3274 return
3275 }
3276 if node.is_method {
3277 if node.left_type == 0 {
3278 return
3279 }
3280 fkey, _ := w.resolve_method_fkey_for_type(node.left_type, node.name)
3281 if fkey == '' {
3282 return
3283 }
3284 fn_decl := w.all_fns[fkey] or { return }
3285 if w.fn_generic_names(fn_decl).len == 0 {
3286 w.fn_by_name(fkey)
3287 }
3288 return
3289 }
3290 mut fn_name := node.fkey()
3291 if node.mod != '' {
3292 qualified_name := '${node.mod}.${node.name}'
3293 if qualified_name in w.all_fns {
3294 fn_name = qualified_name
3295 }
3296 }
3297 fn_decl := w.all_fns[fn_name] or { return }
3298 if w.fn_generic_names(fn_decl).len == 0 {
3299 w.fn_by_name(fn_name)
3300 }
3301}
3302
3303fn (mut w Walker) mark_resource_dependencies() {
3304 string_idx_str := ast.string_type_idx.str()
3305 array_idx_str := ast.array_type_idx.str()
3306
3307 if w.pref.gc_mode == .boehm_leak {
3308 // `-gc boehm_leak` emits scope-exit frees for used heap-backed values.
3309 w.uses_free[ast.string_type] = true
3310 }
3311
3312 if w.trace_enabled {
3313 eprintln('>>>>>>>>>> DEPS USAGE')
3314 }
3315 if w.features.dump {
3316 w.fn_by_name('eprint')
3317 w.fn_by_name('eprintln')
3318 builderptr_idx := int(w.table.find_type('strings.Builder').ref()).str()
3319 w.fn_by_name(builderptr_idx + '.str')
3320 w.fn_by_name(builderptr_idx + '.free')
3321 w.fn_by_name(builderptr_idx + '.write_rune')
3322 w.fn_by_name(builderptr_idx + '.write_string')
3323 w.fn_by_name('strings.new_builder')
3324 w.uses_free[ast.string_type] = true
3325
3326 if w.table.dumps.keys().any(ast.Type(u32(it)).has_flag(.option)) {
3327 w.fn_by_name('str_intp')
3328 }
3329 }
3330 if w.features.auto_str_ptr {
3331 w.fn_by_name('isnil')
3332 w.fn_by_name('tos4')
3333 w.fn_by_name('str_intp')
3334 }
3335 if w.uses_channel {
3336 w.fn_by_name('sync.new_channel_st')
3337 w.fn_by_name('sync.channel_select')
3338 w.fn_by_name('sync.channel_select_lang')
3339 }
3340 if w.uses_lock {
3341 w.mark_by_sym_name('sync.RwMutex')
3342 }
3343 if w.uses_orm {
3344 w.fn_by_name('__new_array_with_default_noscan')
3345 w.fn_by_name('new_array_from_c_array')
3346 w.fn_by_name('__new_array')
3347 w.fn_by_name('${ast.array_type_idx}.get')
3348 w.fn_by_name(int(ast.array_type.ref()).str() + '.push')
3349 }
3350 if w.uses_ct_fields {
3351 w.mark_by_sym_name('FieldData')
3352 }
3353 if w.uses_ct_methods {
3354 w.mark_by_sym_name('FunctionData')
3355 }
3356 if w.uses_ct_params {
3357 w.mark_by_sym_name('FunctionParam')
3358 }
3359 if w.uses_ct_values {
3360 w.mark_by_sym_name('EnumData')
3361 }
3362 if w.uses_ct_variants {
3363 w.mark_by_sym_name('VariantData')
3364 }
3365 if w.uses_ct_attribute {
3366 w.mark_by_sym_name('VAttribute')
3367 }
3368 if w.uses_map_update {
3369 w.fn_by_name('new_map_update_init')
3370 }
3371 if w.uses_mem_align {
3372 w.fn_by_name('memdup_align')
3373 }
3374 if w.uses_spawn {
3375 w.fn_by_name('malloc')
3376 w.fn_by_name('tos3')
3377 }
3378 if w.uses_memdup || w.used_none > 0 || w.used_option > 0 {
3379 // used_option => used_none => use memdup
3380 w.fn_by_name('memdup')
3381 }
3382 if w.uses_debugger {
3383 w.mark_by_type(w.table.find_or_register_map(ast.string_type, ast.string_type))
3384 }
3385 if w.uses_arr_void {
3386 w.mark_by_type(w.table.find_or_register_array(ast.voidptr_type))
3387 }
3388 if w.features.auto_str || w.uses_dump {
3389 w.fn_by_name(ast.string_type_idx.str() + '.repeat')
3390 w.fn_by_name('tos3')
3391 builderptr_idx := int(w.table.find_type('strings.Builder').ref()).str()
3392 w.fn_by_name(builderptr_idx + '.write_string')
3393 w.fn_by_name(builderptr_idx + '.writeln')
3394 w.fn_by_name(builderptr_idx + '.indent')
3395 }
3396 if w.uses_index || w.pref.is_shared {
3397 w.fn_by_name(array_idx_str + '.slice')
3398 w.fn_by_name(array_idx_str + '.get')
3399 }
3400 if w.pref.backend == .c
3401 && (w.uses_arr_getter || w.uses_arr_setter || w.uses_guard || w.uses_index_check) {
3402 w.mark_builtin_array_method_as_used('get_i64')
3403 w.mark_builtin_array_method_as_used('get_u64')
3404 w.mark_builtin_array_method_as_used('get_with_check_i64')
3405 w.mark_builtin_array_method_as_used('get_with_check_u64')
3406 w.mark_builtin_array_method_as_used('set_i64')
3407 w.mark_builtin_array_method_as_used('set_u64')
3408 }
3409 if w.uses_str_index {
3410 w.fn_by_name(string_idx_str + '.at')
3411 if w.uses_str_index_check {
3412 w.fn_by_name(string_idx_str + '.at_with_check')
3413 }
3414 if w.uses_str_range {
3415 w.fn_by_name(string_idx_str + '.substr')
3416 }
3417 }
3418 if w.pref.backend == .c && w.uses_str_index {
3419 w.fn_by_name(string_idx_str + '.at_i64')
3420 w.fn_by_name(string_idx_str + '.at_u64')
3421 w.fn_by_name(string_idx_str + '.at_with_check_i64')
3422 w.fn_by_name(string_idx_str + '.at_with_check_u64')
3423 }
3424 for typ, _ in w.table.used_features.print_types {
3425 w.mark_by_type(typ)
3426 }
3427 if w.trace_enabled {
3428 ptypes := w.table.used_features.print_types.keys().map(w.table.type_to_str(it))
3429 eprintln('>>>>>>>>>> PRINT TYPES ${ptypes}')
3430 stypes := w.uses_str.keys().filter(it != 0).map(w.table.type_to_str(it))
3431 eprintln('>>>>>>>>>> USES .str() CALLS ON TYPES ${stypes}')
3432 ftypes := w.uses_free.keys().map(w.table.type_to_str(it))
3433 eprintln('>>>>>>>>>> USES .free() CALLS ON TYPES ${ftypes}')
3434 }
3435 if w.trace_enabled {
3436 eprintln('>>>>>>>>>> ALL_FNS LOOP')
3437 }
3438 mut has_ptr_print := false
3439 mut map_fns := map[string]ast.FnDecl{}
3440 has_str_call := w.uses_interp || w.uses_asserts || w.uses_str.len > 0
3441 || w.features.print_types.len > 0
3442
3443 orm_impls := w.table.iface_types['orm.Connection'] or { []ast.Type{} }
3444 for k, mut func in w.all_fns {
3445 if has_str_call && k.ends_with('.str') {
3446 if func.receiver.typ.has_flag(.generic) {
3447 concrete_type_lists := w.used_receiver_generic_instantiations(func.receiver.typ)
3448 if concrete_type_lists.len > 0 {
3449 for concrete_type_list in concrete_type_lists {
3450 w.fn_decl_with_concrete_types(mut func, concrete_type_list)
3451 }
3452 } else if func.receiver.typ.idx() in w.used_syms {
3453 w.fn_by_name(k)
3454 }
3455 if !has_ptr_print && func.receiver.typ.is_ptr() {
3456 w.fn_by_name('ptr_str')
3457 has_ptr_print = true
3458 }
3459 } else if func.receiver.typ.idx() in w.used_syms {
3460 w.fn_by_name(k)
3461 if !has_ptr_print && func.receiver.typ.is_ptr() {
3462 w.fn_by_name('ptr_str')
3463 has_ptr_print = true
3464 }
3465 }
3466 continue
3467 }
3468 if (w.pref.autofree || (w.uses_free.len > 0 && func.receiver.typ.idx() in w.used_syms))
3469 && k.ends_with('.free') {
3470 w.fn_by_name(k)
3471 continue
3472 }
3473 if w.uses_atomic && k.starts_with('_Atomic') {
3474 w.fn_by_name(k)
3475 continue
3476 }
3477 if func.name in ['+', '-', '*', '%', '/', '<', '=='] {
3478 if func.receiver.typ.idx() in w.used_syms {
3479 w.fn_by_name(k)
3480 }
3481 continue
3482 }
3483 if !func.is_static_type_method && func.receiver.typ != ast.void_type
3484 && func.generic_names.len > 0 {
3485 if func.receiver.typ.set_nr_muls(0) in w.table.used_features.comptime_syms
3486 || func.receiver.typ in w.table.used_features.comptime_syms {
3487 w.fn_by_name(k)
3488 }
3489 continue
3490 }
3491 if func.is_method && !func.receiver.typ.has_flag(.generic) && func.receiver.typ.is_ptr() {
3492 method_receiver_typename := w.table.type_to_str(func.receiver.typ)
3493 if method_receiver_typename in ['&map', '&mapnode', '&SortedMap', '&DenseArray'] {
3494 map_fns[k] = func
3495 }
3496 continue
3497 } else if k.starts_with('map_') {
3498 map_fns[k] = func
3499 continue
3500 }
3501 if orm_impls.len > 0 && k.starts_with('orm.') {
3502 w.fn_by_name(k)
3503 continue
3504 }
3505 }
3506 if w.uses_err_block {
3507 w.fn_by_name('error')
3508 }
3509 if w.uses_guard || w.uses_index_check {
3510 w.fn_by_name('error')
3511 w.fn_by_name(array_idx_str + '.get_with_check')
3512 }
3513 if w.uses_append {
3514 ref_array_idx_str := int(ast.array_type.ref()).str()
3515 w.fn_by_name(ref_array_idx_str + '.push')
3516 w.fn_by_name(ref_array_idx_str + '.push_many')
3517 w.fn_by_name(ref_array_idx_str + '.push_many_noscan')
3518 w.fn_by_name(ref_array_idx_str + '.push_noscan')
3519 }
3520 if w.uses_array {
3521 if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt, .vgc] {
3522 w.fn_by_name('__new_array_noscan')
3523 w.fn_by_name('new_array_from_c_array_noscan')
3524 w.fn_by_name('__new_array_with_multi_default_noscan')
3525 w.fn_by_name('__new_array_with_array_default_noscan')
3526 w.fn_by_name('__new_array_with_default_noscan')
3527 }
3528 w.fn_by_name('__new_array')
3529 w.fn_by_name('new_array_from_c_array')
3530 w.fn_by_name('__new_array_with_multi_default')
3531 w.fn_by_name('__new_array_with_array_default')
3532 w.fn_by_name('__new_array_with_default')
3533 w.fn_by_name('__new_array_with_default_noscan')
3534 w.fn_by_name(int(ast.array_type.ref()).str() + '.set')
3535 w.fn_by_name('clone_static_to_depth')
3536 }
3537 if w.uses_array_sumtype {
3538 w.fn_by_name('__new_array')
3539 }
3540 if w.uses_fixed_arr_int {
3541 w.fn_by_name('v_fixed_index')
3542 }
3543 if w.pref.backend == .c && w.uses_fixed_arr_int {
3544 w.fn_by_name('v_fixed_index_i64')
3545 w.fn_by_name('v_fixed_index_u64')
3546 }
3547 if w.pref.backend == .c
3548 && (w.uses_arr_range_index || w.uses_str_range_index || w.uses_range_index_check) {
3549 w.fn_by_name('v_slice_index_i64')
3550 w.fn_by_name('v_slice_index_u64')
3551 }
3552 if w.uses_str_range_index {
3553 w.fn_by_name(string_idx_str + '.substr')
3554 }
3555 if w.uses_arr_range_index {
3556 w.fn_by_name(array_idx_str + '.slice')
3557 }
3558 if w.uses_range_index_check {
3559 w.fn_by_name(string_idx_str + '.substr_with_check')
3560 w.fn_by_name(array_idx_str + '.get_with_check')
3561 }
3562 if w.uses_str_range_index_gated {
3563 w.fn_by_name(string_idx_str + '.substr_ni')
3564 }
3565 if w.uses_arr_range_index_gated {
3566 w.fn_by_name(array_idx_str + '.slice_ni')
3567 }
3568 if w.uses_array || w.uses_arr_clone || w.uses_arr_sorted {
3569 w.fn_by_name(array_idx_str + '.clone_static_to_depth')
3570 }
3571 // handle ORM drivers:
3572 if orm_impls.len > 0 {
3573 for orm_type in orm_impls {
3574 typ := int(orm_type).str()
3575 w.fn_by_name(typ + '.select')
3576 w.fn_by_name(typ + '.insert')
3577 w.fn_by_name(typ + '.update')
3578 w.fn_by_name(typ + '.delete')
3579 w.fn_by_name(typ + '.create')
3580 w.fn_by_name(typ + '.drop')
3581 w.fn_by_name(typ + '.last_id')
3582 }
3583 }
3584 if w.features.used_maps == 0 && w.pref.autofree {
3585 w.features.used_maps++
3586 }
3587 if w.features.used_maps > 0 {
3588 w.fn_by_name('new_map')
3589 w.fn_by_name('new_map_init')
3590 w.fn_by_name('map_hash_string')
3591
3592 if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt, .vgc] {
3593 w.fn_by_name('new_map_noscan_key')
3594 w.fn_by_name('new_map_noscan_value')
3595 w.fn_by_name('new_map_noscan_key_value')
3596 w.fn_by_name('new_map_init_noscan_key')
3597 w.fn_by_name('new_map_init_noscan_value')
3598 w.fn_by_name('new_map_init_noscan_key_value')
3599 }
3600 for _, mut func in map_fns {
3601 if !func.is_method {
3602 w.fn_decl(mut func)
3603 } else {
3604 method_receiver_typename := w.table.type_to_str(func.receiver.typ)
3605 if method_receiver_typename in ['&map', '&DenseArray'] {
3606 w.fn_decl(mut func)
3607 }
3608 }
3609 }
3610 } else {
3611 for k, func in map_fns {
3612 if !func.is_method {
3613 continue
3614 }
3615 w.used_fns.delete(k)
3616 }
3617 w.used_fns.delete('new_map')
3618 w.used_fns.delete('new_map_init')
3619 w.used_fns.delete('map_hash_string')
3620 w.used_fns.delete('new_dense_array')
3621 w.used_fns.delete('new_dense_array_noscan')
3622 }
3623}
3624
3625pub fn (mut w Walker) finalize(include_panic_deps bool) {
3626 w.mark_resource_dependencies()
3627 if w.trace_enabled {
3628 eprintln('>>>>>>>>>> FINALIZE')
3629 }
3630 if w.uses_asserts {
3631 w.fn_by_name('__print_assert_failure')
3632 w.fn_by_name('isnil')
3633 w.mark_by_sym_name('VAssertMetaInfo')
3634 }
3635 if w.used_panic > 0 {
3636 w.fn_by_name('panic_option_not_set')
3637 w.fn_by_name('panic_result_not_set')
3638 }
3639 if w.used_none > 0 || w.table.used_features.auto_str {
3640 w.fn_by_name('_option_none')
3641 w.mark_by_sym_name('_option')
3642 }
3643 if w.used_option > 0 {
3644 w.fn_by_name('_option_clone')
3645 w.fn_by_name('_option_ok')
3646 w.mark_by_sym_name('_option')
3647 }
3648 if w.used_result > 0 {
3649 w.fn_by_name('_result_ok')
3650 w.mark_by_sym_name('_result')
3651 }
3652 if (w.used_option + w.used_result + w.used_none) > 0 {
3653 w.mark_const_as_used('none__')
3654 }
3655 if include_panic_deps || w.uses_external_type || w.uses_asserts || w.uses_debugger
3656 || w.uses_interp {
3657 if w.trace_enabled {
3658 eprintln('>>>>> PANIC DEPS ${include_panic_deps} | external_type=${w.uses_external_type} | asserts=${w.uses_asserts} | dbg=${w.uses_debugger} interp=${w.uses_interp}')
3659 }
3660 ref_array_idx_str := int(ast.array_type.ref()).str()
3661 string_idx_str := ast.string_type_idx.str()
3662
3663 if w.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
3664 w.fn_by_name('__new_array_with_default_noscan')
3665 w.fn_by_name(ref_array_idx_str + '.push_noscan')
3666 }
3667 w.fn_by_name('str_intp')
3668 w.fn_by_name('__new_array_with_default')
3669 w.fn_by_name(ref_array_idx_str + '.push')
3670 w.fn_by_name(string_idx_str + '.substr')
3671 w.fn_by_name('v_fixed_index')
3672 w.mark_by_sym_name('StrIntpData')
3673 w.mark_by_sym_name('StrIntpMem')
3674 }
3675 if w.uses_eq {
3676 w.fn_by_name('fast_string_eq')
3677 }
3678 if w.uses_type_name {
3679 charptr_idx_str := ast.charptr_type_idx.str()
3680 w.fn_by_name(charptr_idx_str + '.vstring_literal')
3681 }
3682 if w.used_arr_method['map'] || w.used_arr_method['filter'] {
3683 ref_array_idx_str := int(ast.array_type.ref()).str()
3684 w.fn_by_name(ref_array_idx_str + '.push')
3685 w.fn_by_name(ref_array_idx_str + '.push_noscan')
3686 }
3687 // remove unused symbols
3688 w.remove_unused_fn_generic_types()
3689 // Generic pruning can leave additional generic bodies to emit, which may
3690 // need direct helper calls or resources like FieldData for `$for T.fields`.
3691 w.mark_emitted_generic_body_dependencies()
3692 w.mark_resource_dependencies()
3693
3694 if w.trace_enabled {
3695 syms := w.used_syms.keys().map(w.table.type_to_str(it))
3696 eprintln('>>>>>>>>>> USED SYMS ${syms}')
3697 }
3698}
3699
3700pub fn (mut w Walker) mark_generic_types() {
3701 if w.trace_enabled {
3702 eprintln('>>>>>>>>>> COMPTIME SYMS+CALLS')
3703 }
3704 for k, _ in w.table.used_features.comptime_calls {
3705 w.fn_by_name(k)
3706 }
3707
3708 for k, _ in w.table.used_features.comptime_syms {
3709 sym := w.table.sym(k)
3710 w.mark_by_sym(sym)
3711 for method in sym.get_methods() {
3712 w.fn_by_name('${k}.${method.name}')
3713 }
3714 }
3715}
3716