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