vxx2 / vlib / v3 / transform / fn.v
4051 lines · 3912 sloc · 126.81 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module transform
2
3import v3.flat
4import v3.types
5
6// resolve_call_name resolves the function name from a .call node.
7// child[0] is the function expression: .ident for plain calls, .selector for method calls.
8fn (t &Transformer) resolve_call_name(node flat.Node) string {
9 if node.children_count == 0 {
10 return ''
11 }
12 fn_id := t.a.children[node.children_start]
13 if int(fn_id) < 0 {
14 return ''
15 }
16 fn_node := t.a.nodes[int(fn_id)]
17 match fn_node.kind {
18 .ident {
19 name := fn_node.value
20 if t.var_type(name).len > 0 {
21 return name
22 }
23 // Try qualified with current module
24 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
25 qname := '${t.cur_module}.${name}'
26 if t.is_known_fn_name(qname) {
27 return qname
28 }
29 }
30 // Try unqualified name after current-module authority.
31 if t.is_known_fn_name(name) {
32 return name
33 }
34 return name
35 }
36 .selector {
37 if fn_node.children_count > 0 {
38 base_id := t.a.children[fn_node.children_start]
39 base := t.a.nodes[int(base_id)]
40 if base.kind == .ident {
41 full := '${base.value}.${fn_node.value}'
42 if t.is_known_fn_name(full) {
43 return full
44 }
45 }
46 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
47 if method_name.len > 0 {
48 return method_name
49 }
50 if base.kind == .ident {
51 return '${base.value}.${fn_node.value}'
52 }
53 }
54 return ''
55 }
56 else {
57 return ''
58 }
59 }
60}
61
62fn (t &Transformer) local_fn_decl_return_type(name string) ?string {
63 if name.len == 0 {
64 return none
65 }
66 qname := transform_qualified_fn_name(t.cur_module, name)
67 if ret := t.fn_ret_types[qname] {
68 return ret
69 }
70 if !isnil(t.tc) {
71 if ret := t.tc.fn_ret_types[qname] {
72 return ret.name()
73 }
74 }
75 return none
76}
77
78fn (t &Transformer) local_fn_value_return_type_from_type(typ string) ?string {
79 if typ.len == 0 {
80 return none
81 }
82 normalized := t.normalize_type_alias(typ)
83 return fn_type_return_type_text(normalized)
84}
85
86fn fn_type_return_type_text(typ string) ?string {
87 clean := typ.trim_space()
88 if !(clean.starts_with('fn(') || clean.starts_with('fn (')) {
89 return none
90 }
91 open_idx := clean.index_u8(`(`)
92 if open_idx < 0 {
93 return none
94 }
95 mut depth := 0
96 for i in open_idx .. clean.len {
97 if clean[i] == `(` {
98 depth++
99 } else if clean[i] == `)` {
100 depth--
101 if depth == 0 {
102 ret := clean[i + 1..].trim_space()
103 if ret.len == 0 {
104 return 'void'
105 }
106 return ret
107 }
108 }
109 }
110 return none
111}
112
113// is_known_fn_name reports whether is known fn name applies in transform.
114fn (t &Transformer) is_known_fn_name(name string) bool {
115 if name in t.fn_ret_types {
116 return true
117 }
118 if !isnil(t.tc) {
119 return name in t.tc.fn_ret_types || name in t.tc.fn_param_types
120 }
121 return false
122}
123
124// resolve_receiver_method_name resolves resolve receiver method name information for transform.
125fn (t &Transformer) resolve_receiver_method_name(base_id flat.NodeId, method string) string {
126 if method.len == 0 {
127 return ''
128 }
129 mut base_type := t.lvalue_type(base_id)
130 if base_type.starts_with('&') {
131 base_type = base_type[1..]
132 }
133 if base_type.len == 0 {
134 return ''
135 }
136 if method_name := t.resolve_receiver_method_for_type(base_type, method) {
137 return method_name
138 }
139 mut raw_var_clean := ''
140 if raw_var_type := t.raw_var_type_for_expr(base_id) {
141 raw_clean := if raw_var_type.starts_with('&') {
142 raw_var_type[1..]
143 } else {
144 raw_var_type
145 }
146 raw_var_clean = raw_clean
147 if raw_clean.len > 0 && raw_clean != base_type {
148 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
149 return method_name
150 }
151 }
152 }
153 if raw_const_type := t.raw_const_type_name_for_expr(base_id) {
154 raw_clean := if raw_const_type.starts_with('&') {
155 raw_const_type[1..]
156 } else {
157 raw_const_type
158 }
159 if raw_clean.len > 0 && raw_clean != base_type && raw_clean != raw_var_clean {
160 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
161 return method_name
162 }
163 }
164 }
165 if alias_method := t.resolve_alias_receiver_method(base_type, method) {
166 return alias_method
167 }
168 if embedded_method := t.resolve_embedded_receiver_method(base_type, method) {
169 return embedded_method
170 }
171 return ''
172}
173
174fn (t &Transformer) resolve_collection_receiver_method_name(base_id flat.NodeId, method string, clean_base_type string) string {
175 if method.len == 0 {
176 return ''
177 }
178 if raw_var_type := t.raw_var_type_for_expr(base_id) {
179 raw_clean := if raw_var_type.starts_with('&') { raw_var_type[1..] } else { raw_var_type }
180 if raw_clean.len > 0 && raw_clean != clean_base_type {
181 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
182 return method_name
183 }
184 }
185 }
186 if raw_const_type := t.raw_const_type_name_for_expr(base_id) {
187 raw_clean := if raw_const_type.starts_with('&') {
188 raw_const_type[1..]
189 } else {
190 raw_const_type
191 }
192 if raw_clean.len > 0 && raw_clean != clean_base_type {
193 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
194 return method_name
195 }
196 }
197 }
198 if method_name := t.resolve_receiver_method_for_type(clean_base_type, method) {
199 return method_name
200 }
201 return ''
202}
203
204// resolve_receiver_method_for_type resolves resolve_receiver_method_for_type logic in transform.
205fn (t &Transformer) resolve_receiver_method_for_type(receiver_type string, method string) ?string {
206 mut clean_type := receiver_type
207 if clean_type.starts_with('&') {
208 clean_type = clean_type[1..]
209 }
210 if clean_type.starts_with('map[') {
211 for candidate in t.map_receiver_method_candidates(clean_type, method) {
212 if t.is_known_fn_name(candidate) {
213 return candidate
214 }
215 }
216 } else {
217 direct := '${clean_type}.${method}'
218 if t.is_known_fn_name(direct) {
219 return direct
220 }
221 }
222 if clean_type.starts_with('[]') {
223 elem_type := clean_type[2..]
224 short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
225 short_array := '[]${short_elem}.${method}'
226 if t.is_known_fn_name(short_array) {
227 return short_array
228 }
229 if elem_type.contains('.') {
230 qualified_array := '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
231 if t.is_known_fn_name(qualified_array) {
232 return qualified_array
233 }
234 } else if transform_can_prefix_collection_receiver(t.cur_module) {
235 current_module_array := '${t.cur_module}.[]${short_elem}.${method}'
236 if t.is_known_fn_name(current_module_array) {
237 return current_module_array
238 }
239 }
240 } else if clean_type.contains('.') {
241 short_type := clean_type.all_after_last('.')
242 short_method := '${short_type}.${method}'
243 if t.is_known_fn_name(short_method) {
244 return short_method
245 }
246 qualified_short_method := '${clean_type.all_before_last('.')}.${short_type}.${method}'
247 if t.is_known_fn_name(qualified_short_method) {
248 return qualified_short_method
249 }
250 }
251 if !isnil(t.tc) {
252 if target := t.tc.type_aliases[clean_type] {
253 alias_method := '${target}.${method}'
254 if t.is_known_fn_name(alias_method) {
255 return alias_method
256 }
257 }
258 }
259 return none
260}
261
262// resolve_alias_receiver_method converts resolve alias receiver method data for transform.
263fn (t &Transformer) resolve_alias_receiver_method(base_type string, method string) ?string {
264 if isnil(t.tc) || base_type.len == 0 || method.len == 0 {
265 return none
266 }
267 clean_base := t.normalize_type_alias(base_type)
268 if alias_method := t.alias_methods['${clean_base}.${method}'] {
269 return alias_method
270 }
271 if !t.is_integer_type_name(clean_base) {
272 return none
273 }
274 for name, params in t.tc.fn_param_types {
275 if !name.ends_with('.${method}') || params.len == 0 {
276 continue
277 }
278 receiver_name := name.all_before_last('.')
279 if receiver_name.len == 0 || receiver_name !in t.tc.type_aliases {
280 continue
281 }
282 param_name := params[0].name()
283 if t.alias_receiver_type_matches(clean_base, param_name) {
284 return name
285 }
286 }
287 return none
288}
289
290fn (t &Transformer) resolve_embedded_receiver_method(base_type string, method string) ?string {
291 if base_type.len == 0 || method.len == 0 {
292 return none
293 }
294 mut lookup_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
295 if lookup_type !in t.structs && lookup_type.contains('.') {
296 short_type := lookup_type.all_after_last('.')
297 if short_type in t.structs {
298 lookup_type = short_type
299 }
300 }
301 info := t.lookup_struct_info(lookup_type) or { return none }
302 for field in info.fields {
303 if !t.is_embedded_field(field) {
304 continue
305 }
306 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
307 clean_field := if field_type.starts_with('&') { field_type[1..] } else { field_type }
308 if method_name := t.resolve_receiver_method_for_type(clean_field, method) {
309 return method_name
310 }
311 if method_name := t.resolve_embedded_receiver_method(clean_field, method) {
312 return method_name
313 }
314 }
315 return none
316}
317
318// alias_receiver_type_matches converts alias receiver type matches data for transform.
319fn (t &Transformer) alias_receiver_type_matches(base_type string, alias_type string) bool {
320 if base_type.len == 0 || alias_type.len == 0 {
321 return false
322 }
323 clean_alias := if alias_type.starts_with('&') { alias_type[1..] } else { alias_type }
324 alias_target := t.normalize_type_alias(clean_alias)
325 if alias_target == base_type {
326 return true
327 }
328 if !isnil(t.tc) {
329 alias_c_type := t.tc.c_type(t.tc.parse_type(alias_target))
330 base_c_type := t.tc.c_type(t.tc.parse_type(base_type))
331 if alias_c_type == base_c_type {
332 return true
333 }
334 }
335 return t.is_integer_type_name(alias_target) && t.is_integer_type_name(base_type)
336}
337
338// is_integer_type_name reports whether is integer type name applies in transform.
339fn (t &Transformer) is_integer_type_name(typ string) bool {
340 return typ in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'rune',
341 'isize', 'usize']
342}
343
344// raw_var_type_for_expr supports raw var type for expr handling for Transformer.
345fn (t &Transformer) raw_var_type_for_expr(id flat.NodeId) ?string {
346 if int(id) < 0 {
347 return none
348 }
349 node := t.a.nodes[int(id)]
350 if node.kind == .ident {
351 typ := t.var_type(node.value)
352 if typ.len > 0 {
353 return typ
354 }
355 }
356 if node.typ.len > 0 {
357 return node.typ
358 }
359 return none
360}
361
362// raw_const_type_name_for_expr supports raw const type name for expr handling for Transformer.
363fn (t &Transformer) raw_const_type_name_for_expr(id flat.NodeId) ?string {
364 if int(id) < 0 || isnil(t.tc) {
365 return none
366 }
367 name := t.expr_key(id)
368 if name.len == 0 {
369 return none
370 }
371 key := t.const_type_key(name) or { return none }
372 typ := t.tc.const_types[key] or { return none }
373 return typ.name()
374}
375
376// resolve_method_receiver_type determines the receiver type for method calls.
377// For a call where child[0] is a .selector, resolves the type of the selector's base expression.
378fn (t &Transformer) resolve_method_receiver_type(call_node flat.Node) string {
379 if call_node.children_count == 0 {
380 return ''
381 }
382 fn_id := t.a.children[call_node.children_start]
383 if int(fn_id) < 0 {
384 return ''
385 }
386 fn_node := t.a.nodes[int(fn_id)]
387 if fn_node.kind != .selector || fn_node.children_count == 0 {
388 return ''
389 }
390 base_id := t.a.children[fn_node.children_start]
391 return t.resolve_expr_type(base_id)
392}
393
394// normalize_generic_call_expr transforms normalize generic call expr data for transform.
395fn (mut t Transformer) normalize_generic_call_expr(id flat.NodeId, node flat.Node) flat.NodeId {
396 if node.children_count == 0 {
397 return id
398 }
399 fn_id := t.a.child(&node, 0)
400 if int(fn_id) < 0 {
401 return id
402 }
403 fn_node := t.a.nodes[int(fn_id)]
404 if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {
405 return id
406 }
407 base_id := t.a.child(&fn_node, 0)
408 base := t.a.nodes[int(base_id)]
409 if base.kind !in [.ident, .selector] {
410 return id
411 }
412 type_arg := t.generic_call_type_args_name(fn_node)
413 if type_arg.len == 0 {
414 return id
415 }
416 mut children := []flat.NodeId{cap: int(node.children_count)}
417 children << base_id
418 for i in 1 .. node.children_count {
419 children << t.a.child(&node, i)
420 }
421 start := t.a.children.len
422 for child in children {
423 t.a.children << child
424 }
425 return t.a.add_node(flat.Node{
426 kind: .call
427 op: node.op
428 children_start: start
429 children_count: flat.child_count(children.len)
430 pos: node.pos
431 value: type_arg
432 typ: node.typ
433 })
434}
435
436// generic_call_type_arg_name supports generic call type arg name handling for Transformer.
437fn (t &Transformer) generic_call_type_arg_name(id flat.NodeId) string {
438 if int(id) < 0 {
439 return ''
440 }
441 node := t.a.nodes[int(id)]
442 match node.kind {
443 .ident {
444 return node.value
445 }
446 .selector {
447 if node.children_count == 0 {
448 return node.value
449 }
450 base := t.generic_call_type_arg_name(t.a.child(&node, 0))
451 if base.len == 0 {
452 return node.value
453 }
454 return '${base}.${node.value}'
455 }
456 .index {
457 if node.children_count < 2 || node.value == 'range' {
458 return ''
459 }
460 base := t.generic_call_type_arg_name(t.a.child(&node, 0))
461 if base.len == 0 {
462 return ''
463 }
464 mut args := []string{}
465 for i in 1 .. node.children_count {
466 arg := t.generic_call_type_arg_name(t.a.child(&node, i))
467 if arg.len == 0 {
468 return ''
469 }
470 args << arg
471 }
472 return '${base}[${args.join(', ')}]'
473 }
474 .array_init {
475 if node.value.len > 0 {
476 return '[]${node.value}'
477 }
478 return ''
479 }
480 .map_init {
481 return node.value
482 }
483 .prefix {
484 if node.children_count == 0 {
485 return ''
486 }
487 child := t.generic_call_type_arg_name(t.a.child(&node, 0))
488 if child.len == 0 {
489 return ''
490 }
491 if node.op == .amp {
492 return '&${child}'
493 }
494 return child
495 }
496 else {
497 return ''
498 }
499 }
500}
501
502fn (t &Transformer) generic_call_type_args_name(index_node flat.Node) string {
503 if index_node.kind != .index || index_node.children_count < 2 || index_node.value == 'range' {
504 return ''
505 }
506 mut args := []string{}
507 for i in 1 .. index_node.children_count {
508 arg := t.generic_call_type_arg_name(t.a.child(&index_node, i))
509 if arg.len == 0 {
510 return ''
511 }
512 args << arg
513 }
514 return args.join(', ')
515}
516
517// transform_call_args transforms all children of a call expression.
518// child[0] is the function expression, children[1..n] are arguments.
519fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.NodeId {
520 if node.children_count == 0 {
521 return t.a.add_node(flat.Node{
522 kind: .call
523 op: node.op
524 pos: node.pos
525 value: node.value
526 typ: node.typ
527 })
528 }
529 call_name := t.call_name_for_node(id, node)
530 mut params := t.call_param_types(call_name)
531 if concrete_params := t.concrete_generic_call_param_types(id, node) {
532 params = concrete_params.clone()
533 }
534 param_offset := t.call_param_offset(call_name, node, params)
535 is_variadic := t.call_is_variadic(call_name)
536 variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
537 params.len - 1
538 } else {
539 -1
540 }
541 mut new_children := []flat.NodeId{cap: int(node.children_count)}
542 saved_in_call_callee := t.in_call_callee
543 t.in_call_callee = true
544 new_children << t.transform_expr(t.a.children[node.children_start])
545 t.in_call_callee = saved_in_call_callee
546 mut i := 1
547 for i < node.children_count {
548 arg_idx := new_children.len - 1
549 param_idx := arg_idx + param_offset
550 arg_id := t.a.child(&node, i)
551 arg_node := t.a.nodes[int(arg_id)]
552 param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
553 if arg_node.kind == .field_init {
554 if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
555 new_children << packed_arg
556 i = t.next_non_field_init_arg(node, i)
557 continue
558 }
559 }
560 if variadic_idx >= 0 && param_idx == variadic_idx {
561 variadic_type := params[variadic_idx]
562 if variadic_type is types.Array {
563 if arg_node.kind == .prefix && arg_node.value == '...'
564 && arg_node.children_count > 0 {
565 spread_id := t.a.child(&arg_node, 0)
566 new_children << t.transform_call_arg_for_param(spread_id, param_type)
567 i++
568 break
569 }
570 remaining := int(node.children_count) - i
571 if remaining == 1 {
572 arg_type := t.node_type(arg_id)
573 if arg_type.starts_with('[]') {
574 new_children << t.transform_call_arg_for_param(arg_id, param_type)
575 } else {
576 new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
577 }
578 } else {
579 new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
580 }
581 break
582 }
583 }
584 new_children << t.transform_call_arg_for_param(arg_id, param_type)
585 i++
586 }
587 explicit_args := int(node.children_count) - 1
588 if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
589 variadic_type := params[variadic_idx]
590 if variadic_type is types.Array {
591 new_children << t.pack_variadic_args(node, int(node.children_count),
592 variadic_type.elem_type)
593 }
594 }
595 t.append_missing_params_struct_args(mut new_children, params, param_offset)
596 start := t.a.children.len
597 for nc in new_children {
598 t.a.children << nc
599 }
600 return t.a.add_node(flat.Node{
601 kind: .call
602 op: node.op
603 children_start: start
604 children_count: flat.child_count(new_children.len)
605 pos: node.pos
606 value: node.value
607 typ: node.typ
608 })
609}
610
611// try_lower_join_path_call supports try lower join path call handling for Transformer.
612fn (mut t Transformer) try_lower_join_path_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
613 call_name := t.call_name_for_node(id, node)
614 if call_name != 'join_path' && call_name != 'os.join_path' {
615 return none
616 }
617 // A spread argument (`...rest`) has a runtime-determined length and cannot be
618 // unrolled into nested join_path_single calls at compile time. Defer to the real
619 // variadic os.join_path in that case.
620 for i in 1 .. node.children_count {
621 arg := t.a.child_node(&node, i)
622 if arg.kind == .prefix && arg.value == '...' {
623 return none
624 }
625 }
626 if node.children_count <= 1 {
627 return t.make_string_literal('')
628 }
629 t.mark_fn_used('os.join_path_single')
630 mut result := t.transform_expr(t.a.child(&node, 1))
631 for i in 2 .. node.children_count {
632 arg := t.transform_expr(t.a.child(&node, i))
633 result = t.make_call_typed('os.join_path_single', arr2(result, arg), 'string')
634 }
635 return result
636}
637
638// transform_params_struct_call_arg transforms transform params struct call arg data for transform.
639fn (mut t Transformer) transform_params_struct_call_arg(node flat.Node, field_start int, param_type string) ?flat.NodeId {
640 struct_type := t.params_struct_type_name(param_type) or { return none }
641 mut field_ids := []flat.NodeId{}
642 for i in field_start .. node.children_count {
643 field_id := t.a.child(&node, i)
644 field := t.a.nodes[int(field_id)]
645 if field.kind != .field_init {
646 break
647 }
648 field_ids << field_id
649 }
650 if field_ids.len == 0 {
651 return none
652 }
653 start := t.a.children.len
654 for field_id in field_ids {
655 t.a.children << field_id
656 }
657 struct_id := t.a.add_node(flat.Node{
658 kind: .struct_init
659 children_start: start
660 children_count: flat.child_count(field_ids.len)
661 value: struct_type
662 typ: struct_type
663 })
664 return t.transform_struct_fields(struct_id, t.a.nodes[int(struct_id)])
665}
666
667// next_non_field_init_arg returns next non field init arg data for Transformer.
668fn (t &Transformer) next_non_field_init_arg(node flat.Node, field_start int) int {
669 mut i := field_start
670 for i < node.children_count {
671 field := t.a.child_node(&node, i)
672 if field.kind != .field_init {
673 break
674 }
675 i++
676 }
677 return i
678}
679
680// params_struct_type_name supports params struct type name handling for Transformer.
681fn (t &Transformer) params_struct_type_name(param_type string) ?string {
682 if param_type.len == 0 {
683 return none
684 }
685 mut typ := param_type
686 if typ.starts_with('&') {
687 typ = typ[1..]
688 }
689 if info := t.lookup_struct_info(typ) {
690 if info.is_params {
691 return typ
692 }
693 }
694 normalized := t.normalize_type_alias(typ)
695 if normalized != typ {
696 if info := t.lookup_struct_info(normalized) {
697 if info.is_params {
698 return normalized
699 }
700 }
701 }
702 return none
703}
704
705// call_name_for_node updates call name for node state for Transformer.
706fn (t &Transformer) call_name_for_node(id flat.NodeId, node flat.Node) string {
707 if !isnil(t.tc) {
708 if name := t.tc.resolved_call_name(id) {
709 return name
710 }
711 }
712 return t.resolve_call_name(node)
713}
714
715// call_param_offset updates call param offset state for Transformer.
716fn (t &Transformer) call_param_offset(call_name string, node flat.Node, params []types.Type) int {
717 if params.len == 0 || node.children_count == 0 {
718 return 0
719 }
720 fn_node := t.a.child_node(&node, 0)
721 if fn_node.kind != .selector || fn_node.children_count == 0 {
722 return 0
723 }
724 base_id := t.a.child(fn_node, 0)
725 // `module.Type.fn(...)` / `Type.fn(...)` is a static associated function call, not a
726 // method: the base names a type, not a value, so no receiver must be prepended.
727 if _ := t.static_assoc_fn_name(base_id, fn_node.value) {
728 return 0
729 }
730 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
731 if method_name.len == 0 {
732 return 0
733 }
734 if t.receiver_method_param_offset(base_id, node, params) == 1 {
735 return 1
736 }
737 if call_name.len == 0 || call_name == method_name || call_name == c_name(method_name) {
738 return 1
739 }
740 return 0
741}
742
743// static_assoc_fn_name returns the name of the static associated function a selector
744// call resolves to, if the base names a type (`Type.fn` or `module.Type.fn`) and that
745// function exists. Such calls take no receiver, so the base must not be prepended as an
746// argument. Returns none for ordinary method calls (base is a value).
747fn (t &Transformer) static_assoc_fn_name(base_id flat.NodeId, method string) ?string {
748 if method.len == 0 {
749 return none
750 }
751 base := t.a.nodes[int(base_id)]
752 if base.kind == .ident {
753 if base.value == 'C' || t.is_import_alias_ident(base_id) {
754 return none
755 }
756 for type_name in t.static_assoc_type_candidates(base.value) {
757 name := '${type_name}.${method}'
758 if t.is_known_fn_name(name) {
759 return name
760 }
761 }
762 } else if base.kind == .selector && base.children_count > 0 {
763 inner := t.a.child_node(&base, 0)
764 if inner.kind == .ident {
765 type_ident := '${inner.value}.${base.value}'
766 for type_name in t.static_assoc_type_candidates(type_ident) {
767 name := '${type_name}.${method}'
768 if t.is_known_fn_name(name) {
769 return name
770 }
771 }
772 }
773 }
774 return none
775}
776
777fn (t &Transformer) is_import_alias_ident(id flat.NodeId) bool {
778 if int(id) < 0 || isnil(t.tc) {
779 return false
780 }
781 node := t.a.nodes[int(id)]
782 return node.kind == .ident && node.value in t.tc.imports
783}
784
785fn (t &Transformer) static_assoc_type_candidates(type_ident string) []string {
786 if type_ident.len == 0 {
787 return []string{}
788 }
789 mut candidates := []string{}
790 candidates << type_ident
791 if !type_ident.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
792 && t.cur_module != 'builtin' {
793 candidates << '${t.cur_module}.${type_ident}'
794 }
795 if !isnil(t.tc) {
796 qname := t.tc.qualify_name(type_ident)
797 if qname !in candidates {
798 candidates << qname
799 }
800 }
801 mut result := []string{}
802 for candidate in candidates {
803 if t.is_static_assoc_type_name(candidate) && candidate !in result {
804 result << candidate
805 }
806 }
807 return result
808}
809
810fn (t &Transformer) is_static_assoc_type_name(type_name string) bool {
811 if type_name in t.structs || type_name in t.enum_types || type_name in t.sum_types {
812 return true
813 }
814 if isnil(t.tc) {
815 return false
816 }
817 return type_name in t.tc.structs || type_name in t.tc.enum_names || type_name in t.tc.sum_types
818 || type_name in t.tc.interface_names || type_name in t.tc.type_aliases
819}
820
821// try_lower_static_assoc_call lowers `Type.method(args)` to a direct call to
822// `Type.method(args)` once the checker/transformer has proven that the base is a
823// type, not a value receiver.
824fn (mut t Transformer) try_lower_static_assoc_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
825 if node.children_count == 0 {
826 return none
827 }
828 fn_id := t.a.child(&node, 0)
829 fn_node := t.a.nodes[int(fn_id)]
830 if fn_node.kind != .selector || fn_node.children_count == 0 {
831 return none
832 }
833 base_id := t.a.child(&fn_node, 0)
834 static_fn := t.static_assoc_fn_name(base_id, fn_node.value) or { return none }
835 normalized := t.transform_call_args(id, node)
836 normalized_node := t.a.nodes[int(normalized)]
837 mut args := []flat.NodeId{cap: int(normalized_node.children_count) - 1}
838 for i in 1 .. normalized_node.children_count {
839 args << t.a.child(&normalized_node, i)
840 }
841 ret_type := t.receiver_method_return_type(static_fn, node.typ)
842 return t.make_call_typed(static_fn, args, ret_type)
843}
844
845// call_param_types updates call param types state for Transformer.
846fn (t &Transformer) call_param_types(call_name string) []types.Type {
847 if call_name.len == 0 || isnil(t.tc) {
848 return []types.Type{}
849 }
850 params := t.tc.fn_param_types[call_name] or { return []types.Type{} }
851 return params
852}
853
854// call_is_variadic updates call is variadic state for Transformer.
855fn (t &Transformer) call_is_variadic(call_name string) bool {
856 if call_name.len == 0 || isnil(t.tc) {
857 return false
858 }
859 if t.tc.c_variadic_fns[call_name] {
860 return false
861 }
862 return t.tc.fn_variadic[call_name] or { false }
863}
864
865// call_param_type_name updates call param type name state for Transformer.
866fn (t &Transformer) call_param_type_name(call_name string, idx int) string {
867 if idx < 0 || call_name.len == 0 || isnil(t.tc) {
868 return ''
869 }
870 params := t.tc.fn_param_types[call_name] or { return '' }
871 if idx >= params.len {
872 return ''
873 }
874 return params[idx].name()
875}
876
877// transform_call_arg_for_param transforms transform call arg for param data for transform.
878fn (mut t Transformer) transform_call_arg_for_param(arg_id flat.NodeId, param_type string) flat.NodeId {
879 if int(arg_id) < 0 {
880 return arg_id
881 }
882 mut arg_node := &t.a.nodes[int(arg_id)]
883 if arg_node.kind == .array_literal && arg_node.typ.len == 0 && param_type.starts_with('[]') {
884 arg_node.typ = param_type
885 }
886 if arg_node.kind == .enum_val && param_type in t.enum_types {
887 return t.transform_enum_shorthand(arg_id, *arg_node, param_type)
888 }
889 if param_type.starts_with('&') && arg_node.kind == .selector
890 && t.selector_chain_has_sum_variant_field(arg_id) {
891 value := t.transform_expr(arg_id)
892 mut value_type := t.node_type(arg_id)
893 if value_type.len == 0 {
894 value_type = t.node_type(value)
895 }
896 stable := t.stable_transformed_expr_for_reuse(value, value_type, 'addr')
897 addr := t.make_prefix(.amp, stable)
898 t.a.nodes[int(addr)].typ = param_type
899 return addr
900 }
901 if param_type.starts_with('&') && t.is_sum_type_name(param_type[1..]) {
902 target_sum := param_type[1..]
903 arg_type := t.node_type(arg_id)
904 arg_key := t.expr_key(arg_id)
905 has_smartcast := t.has_smartcast(arg_key)
906 if !has_smartcast
907 && t.resolve_sum_name(t.trim_pointer_type(arg_type)) == t.resolve_sum_name(target_sum) {
908 return t.transform_expr(arg_id)
909 }
910 if arg_node.kind == .prefix && arg_node.op == .amp && arg_node.children_count > 0 {
911 inner_id := t.a.child(arg_node, 0)
912 inner_type := t.node_type(inner_id)
913 if t.resolve_sum_name(t.trim_pointer_type(inner_type)) == t.resolve_sum_name(target_sum) {
914 return t.transform_expr(arg_id)
915 }
916 }
917 wrapped := t.wrap_sum_value(arg_id, target_sum)
918 tmp_name := t.new_temp('sum_arg')
919 t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, target_sum)
920 return t.make_prefix(.amp, t.make_ident(tmp_name))
921 }
922 if ptr_arg := t.transform_pointer_rvalue_arg(arg_id, *arg_node, param_type) {
923 return ptr_arg
924 }
925 if t.is_sum_type_name(param_type) {
926 return t.wrap_sum_value(arg_id, param_type)
927 }
928 if param_type.starts_with('[]') {
929 arg_type := t.node_type(arg_id)
930 if t.is_fixed_array_type(arg_type) {
931 return t.fixed_array_value_to_array(arg_id, arg_type, param_type)
932 }
933 if const_arg := t.transform_const_array_arg_for_param(arg_id, param_type) {
934 return const_arg
935 }
936 }
937 if param_type.len > 0 && t.type_text_has_generic_placeholder(param_type, t.cur_module) {
938 return t.transform_expr(arg_id)
939 }
940 return t.transform_expr_for_type(arg_id, param_type)
941}
942
943fn (mut t Transformer) transform_pointer_rvalue_arg(arg_id flat.NodeId, arg_node flat.Node, param_type string) ?flat.NodeId {
944 if !param_type.starts_with('&') {
945 return none
946 }
947 mut value_id := arg_id
948 mut value_node := arg_node
949 if arg_node.kind == .prefix && arg_node.op == .amp && arg_node.children_count > 0 {
950 value_id = t.a.child(&arg_node, 0)
951 value_node = t.a.nodes[int(value_id)]
952 }
953 if !is_pointer_arg_temp_rvalue(value_node) {
954 return none
955 }
956 value_type := param_type[1..]
957 if value_type.len == 0 || value_type == 'void' || value_type == 'unknown' {
958 return none
959 }
960 arg_type := t.node_type(value_id)
961 if arg_type.len == 0 || arg_type == 'void' || arg_type == 'unknown'
962 || is_pointer_like_type_name(arg_type) {
963 return none
964 }
965 value := t.transform_expr_for_type(value_id, value_type)
966 tmp_name := t.new_temp('ptr_arg')
967 t.pending_stmts << t.make_decl_assign_typed(tmp_name, value, value_type)
968 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
969 t.a.nodes[int(addr)].typ = param_type
970 return addr
971}
972
973fn is_pointer_arg_temp_rvalue(node flat.Node) bool {
974 return node.kind == .call || (node.kind == .index && node.value == 'range')
975}
976
977fn is_pointer_like_type_name(typ string) bool {
978 mut clean := typ
979 if clean.starts_with('mut ') {
980 clean = clean[4..]
981 }
982 return clean.starts_with('&') || clean in ['voidptr', 'byteptr', 'charptr']
983}
984
985fn (mut t Transformer) append_missing_params_struct_args(mut args []flat.NodeId, params []types.Type, param_offset int) {
986 mut param_idx := param_offset
987 if args.len > 0 {
988 param_idx = args.len - 1 + param_offset
989 }
990 for param_idx < params.len {
991 param_type := params[param_idx].name()
992 struct_type := t.params_struct_type_name(param_type) or { break }
993 args << t.zero_value_for_type(struct_type)
994 param_idx++
995 }
996}
997
998// is_fn_pointer_type_name reports whether is fn pointer type name applies in transform.
999fn (t &Transformer) is_fn_pointer_type_name(type_name string) bool {
1000 if type_name.len == 0 || isnil(t.tc) {
1001 return false
1002 }
1003 typ := t.tc.parse_type(type_name)
1004 if typ is types.FnType {
1005 return true
1006 }
1007 if typ is types.Alias {
1008 return typ.base_type is types.FnType
1009 }
1010 return false
1011}
1012
1013// is_named_fn_value_arg reports whether is named fn value arg applies in transform.
1014fn (t &Transformer) is_named_fn_value_arg(arg_id flat.NodeId) bool {
1015 if int(arg_id) < 0 || isnil(t.tc) {
1016 return false
1017 }
1018 node := t.a.nodes[int(arg_id)]
1019 if node.kind == .ident {
1020 if _ := t.resolve_fn_value_ident(node.value) {
1021 return true
1022 }
1023 return false
1024 }
1025 if node.kind == .selector && node.children_count > 0 {
1026 key := t.expr_key(arg_id)
1027 return key in t.tc.fn_ret_types || key in t.tc.fn_param_types
1028 }
1029 return false
1030}
1031
1032// transform_const_array_arg_for_param supports transform_const_array_arg_for_param handling.
1033fn (mut t Transformer) transform_const_array_arg_for_param(arg_id flat.NodeId, param_type string) ?flat.NodeId {
1034 expr_id := t.const_expr_for_arg(arg_id) or { return none }
1035 expr := t.a.nodes[int(expr_id)]
1036 elem_type := param_type[2..]
1037 if expr.kind == .array_init && expr.children_count == 0 {
1038 return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
1039 }
1040 if expr.kind != .array_literal {
1041 return none
1042 }
1043 if expr.children_count == 0 {
1044 return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
1045 }
1046 mut values := []flat.NodeId{cap: int(expr.children_count)}
1047 for i in 0 .. expr.children_count {
1048 values << t.transform_expr(t.a.child(&expr, i))
1049 }
1050 return t.make_array_literal_typed(values, param_type)
1051}
1052
1053// const_expr_for_arg supports const expr for arg handling for Transformer.
1054fn (t &Transformer) const_expr_for_arg(arg_id flat.NodeId) ?flat.NodeId {
1055 if isnil(t.tc) || int(arg_id) < 0 {
1056 return none
1057 }
1058 node := t.a.nodes[int(arg_id)]
1059 if node.kind == .ident {
1060 if t.var_type(node.value).len > 0 {
1061 return none
1062 }
1063 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1064 if expr_id := t.const_expr_for_name('${t.cur_module}.${node.value}') {
1065 return expr_id
1066 }
1067 }
1068 return t.const_expr_for_name(node.value)
1069 }
1070 if node.kind == .selector && node.children_count > 0 {
1071 base := t.a.child_node(&node, 0)
1072 if base.kind == .ident {
1073 return t.const_expr_for_name('${base.value}.${node.value}')
1074 }
1075 }
1076 return none
1077}
1078
1079// const_expr_for_name supports const expr for name handling for Transformer.
1080fn (t &Transformer) const_expr_for_name(name string) ?flat.NodeId {
1081 if isnil(t.tc) || name.len == 0 {
1082 return none
1083 }
1084 if expr_id := t.tc.const_exprs[name] {
1085 return expr_id
1086 }
1087 key := t.const_type_key(name) or { return none }
1088 if expr_id := t.tc.const_exprs[key] {
1089 return expr_id
1090 }
1091 return none
1092}
1093
1094// pack_variadic_args supports pack variadic args handling for Transformer.
1095fn (mut t Transformer) pack_variadic_args(node flat.Node, first_arg int, elem_type types.Type) flat.NodeId {
1096 expected_enum := elem_type.name()
1097 array_type := '[]${expected_enum}'
1098 if named_arg := t.transform_variadic_struct_fields(node, first_arg, elem_type) {
1099 if t.in_const_init {
1100 return t.make_array_literal_typed([named_arg], array_type)
1101 }
1102 tmp_name := t.new_temp('varargs')
1103 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(expected_enum,
1104 t.make_int_literal(0), t.make_int_literal(1)), array_type)
1105 value_name := t.new_temp('vararg')
1106 t.pending_stmts << t.make_decl_assign_typed(value_name, named_arg, expected_enum)
1107 t.pending_stmts << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
1108 t.make_ident(tmp_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
1109 t.set_var_type(tmp_name, array_type)
1110 return t.make_ident(tmp_name)
1111 }
1112 if t.in_const_init {
1113 mut values := []flat.NodeId{cap: int(node.children_count) - first_arg}
1114 for i in first_arg .. node.children_count {
1115 arg_id := t.a.child(&node, i)
1116 arg := t.a.nodes[int(arg_id)]
1117 if arg.kind == .enum_val && expected_enum in t.enum_types {
1118 values << t.transform_enum_shorthand(arg_id, arg, expected_enum)
1119 } else if t.is_sum_type_name(expected_enum) {
1120 values << t.wrap_sum_value(arg_id, expected_enum)
1121 } else {
1122 values << t.transform_expr(arg_id)
1123 }
1124 }
1125 return t.make_array_literal_typed(values, array_type)
1126 }
1127 tmp_name := t.new_temp('varargs')
1128 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(expected_enum,
1129 t.make_int_literal(0), t.make_int_literal(int(node.children_count) - first_arg)),
1130 array_type)
1131 for i in first_arg .. node.children_count {
1132 arg_id := t.a.child(&node, i)
1133 arg := t.a.nodes[int(arg_id)]
1134 value := if arg.kind == .enum_val && expected_enum in t.enum_types {
1135 t.transform_enum_shorthand(arg_id, arg, expected_enum)
1136 } else if t.is_sum_type_name(expected_enum) {
1137 t.wrap_sum_value(arg_id, expected_enum)
1138 } else {
1139 t.transform_expr(arg_id)
1140 }
1141 value_name := t.new_temp('vararg')
1142 t.pending_stmts << t.make_decl_assign_typed(value_name, value, expected_enum)
1143 t.pending_stmts << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
1144 t.make_ident(tmp_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
1145 }
1146 t.set_var_type(tmp_name, array_type)
1147 return t.make_ident(tmp_name)
1148}
1149
1150fn (mut t Transformer) transform_variadic_struct_fields(node flat.Node, field_start int, elem_type types.Type) ?flat.NodeId {
1151 if field_start >= node.children_count {
1152 return none
1153 }
1154 if elem_type !is types.Struct {
1155 return none
1156 }
1157 first := t.a.child_node(&node, field_start)
1158 if first.kind != .field_init {
1159 return none
1160 }
1161 mut field_ids := []flat.NodeId{}
1162 for i in field_start .. node.children_count {
1163 field_id := t.a.child(&node, i)
1164 field := t.a.nodes[int(field_id)]
1165 if field.kind != .field_init {
1166 break
1167 }
1168 field_ids << field_id
1169 }
1170 if field_ids.len == 0 {
1171 return none
1172 }
1173 start := t.a.children.len
1174 for field_id in field_ids {
1175 t.a.children << field_id
1176 }
1177 struct_id := t.a.add_node(flat.Node{
1178 kind: .struct_init
1179 children_start: start
1180 children_count: flat.child_count(field_ids.len)
1181 value: elem_type.name()
1182 typ: elem_type.name()
1183 })
1184 return t.transform_struct_fields(struct_id, t.a.nodes[int(struct_id)])
1185}
1186
1187// make_array_literal_typed builds make array literal typed data for transform.
1188fn (mut t Transformer) make_array_literal_typed(values []flat.NodeId, typ string) flat.NodeId {
1189 start := t.a.children.len
1190 for value in values {
1191 t.a.children << value
1192 }
1193 return t.a.add_node(flat.Node{
1194 kind: .array_literal
1195 children_start: start
1196 children_count: flat.child_count(values.len)
1197 typ: typ
1198 })
1199}
1200
1201// stringify_expr supports stringify expr handling for Transformer.
1202fn (mut t Transformer) stringify_expr(expr_id flat.NodeId) flat.NodeId {
1203 expr := t.transform_expr(expr_id)
1204 mut typ := t.node_type(expr)
1205 if typ.len == 0 {
1206 typ = t.node_type(expr_id)
1207 }
1208 if typ.len == 0 {
1209 // Structural fallback for compound arguments (infix, prefix, cast,
1210 // paren, ...) so e.g. `println(a + b)` for ints is stringified via
1211 // strconv__format_int instead of being passed to println as a raw
1212 // number. Mirrors the fallback already used by string interpolation.
1213 typ = t.reliable_stringify_type(expr)
1214 if typ.len == 0 {
1215 typ = t.reliable_stringify_type(expr_id)
1216 }
1217 }
1218 return t.wrap_string_conversion(expr, typ)
1219}
1220
1221// reliable_stringify_type supports reliable stringify type handling for Transformer.
1222fn (t &Transformer) reliable_stringify_type(id flat.NodeId) string {
1223 mut typ := t.node_type(id)
1224 if typ.len > 0 {
1225 return typ
1226 }
1227 if int(id) >= 0 {
1228 node := t.a.nodes[int(id)]
1229 if node.typ.len > 0 {
1230 return node.typ
1231 }
1232 match node.kind {
1233 .int_literal {
1234 return 'int'
1235 }
1236 .float_literal {
1237 return 'f64'
1238 }
1239 .bool_literal {
1240 return 'bool'
1241 }
1242 .char_literal {
1243 return 'rune'
1244 }
1245 .string_literal, .string_interp {
1246 return 'string'
1247 }
1248 .infix {
1249 return t.reliable_infix_stringify_type(node)
1250 }
1251 .prefix {
1252 if node.op == .not {
1253 return 'bool'
1254 }
1255 if node.children_count > 0 {
1256 return t.reliable_stringify_type(t.a.child(&node, 0))
1257 }
1258 }
1259 .paren {
1260 if node.children_count > 0 {
1261 return t.reliable_stringify_type(t.a.child(&node, 0))
1262 }
1263 }
1264 .cast_expr {
1265 if node.value.len > 0 {
1266 return t.normalize_type_alias(node.value)
1267 }
1268 if node.children_count > 0 {
1269 return t.reliable_stringify_type(t.a.child(&node, 0))
1270 }
1271 }
1272 else {}
1273 }
1274 }
1275 return ''
1276}
1277
1278// reliable_infix_stringify_type supports reliable infix stringify type handling for Transformer.
1279fn (t &Transformer) reliable_infix_stringify_type(node flat.Node) string {
1280 if node.children_count < 2 {
1281 return ''
1282 }
1283 lhs_type := t.reliable_stringify_type(t.a.child(&node, 0))
1284 rhs_type := t.reliable_stringify_type(t.a.child(&node, 1))
1285 if lhs_type == 'string' || rhs_type == 'string' {
1286 return 'string'
1287 }
1288 match node.op {
1289 .eq, .ne, .lt, .gt, .le, .ge, .logical_and, .logical_or {
1290 return 'bool'
1291 }
1292 .left_shift, .right_shift, .right_shift_unsigned {
1293 // shifts keep the left operand's type/width
1294 if lhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type) {
1295 return lhs_type
1296 }
1297 }
1298 .plus, .minus, .mul, .div, .mod, .amp, .pipe, .xor {
1299 if lhs_type.len > 0 && rhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type)
1300 && t.is_numeric_stringify_type(rhs_type) {
1301 // Use the promoted result type, not the lhs, so e.g.
1302 // `1 + u64(x)` formats as unsigned rather than signed int.
1303 return promote_numeric_stringify_type(lhs_type, rhs_type)
1304 }
1305 }
1306 else {}
1307 }
1308
1309 return ''
1310}
1311
1312// promote_numeric_stringify_type returns the result type of a binary numeric
1313// operation for stringify purposes: floats dominate, the wider integer wins,
1314// and on equal width an explicit type beats the untyped-literal default `int`.
1315fn promote_numeric_stringify_type(a string, b string) string {
1316 if a == b {
1317 return a
1318 }
1319 if a == 'f64' || b == 'f64' {
1320 return 'f64'
1321 }
1322 if a == 'f32' || b == 'f32' {
1323 return 'f32'
1324 }
1325 ra := int_stringify_rank(a)
1326 rb := int_stringify_rank(b)
1327 if ra > rb {
1328 return a
1329 }
1330 if rb > ra {
1331 return b
1332 }
1333 if a == 'int' {
1334 return b
1335 }
1336 if b == 'int' {
1337 return a
1338 }
1339 return a
1340}
1341
1342fn int_stringify_rank(typ string) int {
1343 return match typ {
1344 'i8', 'u8', 'byte' { 8 }
1345 'i16', 'u16' { 16 }
1346 'i32', 'u32', 'int', 'rune' { 32 }
1347 'i64', 'u64', 'isize', 'usize' { 64 }
1348 else { 32 }
1349 }
1350}
1351
1352// is_numeric_stringify_type reports whether is numeric stringify type applies in transform.
1353fn (t &Transformer) is_numeric_stringify_type(typ string) bool {
1354 is_number := typ in ['int', 'int literal', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8',
1355 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', 'float literal', 'rune']
1356 return is_number || typ in t.enum_types
1357}
1358
1359// is_enum_stringify_type reports whether is enum stringify type applies in transform.
1360fn (t &Transformer) is_enum_stringify_type(typ string) bool {
1361 mut clean_typ := typ
1362 if clean_typ.starts_with('&') {
1363 clean_typ = clean_typ[1..]
1364 }
1365 if clean_typ.starts_with('mut ') {
1366 clean_typ = clean_typ[4..]
1367 }
1368 if clean_typ in t.enum_types {
1369 return true
1370 }
1371 mut qtyp := clean_typ
1372 if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1373 && t.cur_module != 'builtin' {
1374 qtyp = '${t.cur_module}.${clean_typ}'
1375 if qtyp in t.enum_types {
1376 return true
1377 }
1378 }
1379 if isnil(t.tc) {
1380 return false
1381 }
1382 if alias := t.tc.type_aliases[clean_typ] {
1383 return t.is_enum_stringify_type(alias)
1384 }
1385 if qtyp != clean_typ {
1386 if alias := t.tc.type_aliases[qtyp] {
1387 return t.is_enum_stringify_type(alias)
1388 }
1389 }
1390 parsed := t.tc.parse_type(clean_typ)
1391 if parsed is types.Enum {
1392 return true
1393 }
1394 if qtyp != clean_typ {
1395 qparsed := t.tc.parse_type(qtyp)
1396 if qparsed is types.Enum {
1397 return true
1398 }
1399 }
1400 return false
1401}
1402
1403// enum_str_method_name supports enum str method name handling for Transformer.
1404fn (t &Transformer) enum_str_method_name(typ string) ?string {
1405 mut candidates := []string{cap: 3}
1406 candidates << typ
1407 if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1408 && t.cur_module != 'builtin' {
1409 candidates << '${t.cur_module}.${typ}'
1410 }
1411 if !isnil(t.tc) {
1412 parsed := t.tc.parse_type(typ)
1413 if parsed is types.Enum {
1414 candidates << parsed.name
1415 }
1416 }
1417 for candidate in candidates {
1418 method := '${candidate}.str'
1419 if t.is_known_fn_name(method) {
1420 return method
1421 }
1422 }
1423 return none
1424}
1425
1426// enum_autostr_call builds a call to the compiler-synthesized `<Enum>__autostr` helper
1427// (emitted by cgen's enum_str_defs) which returns the enum field NAME. Used as the default
1428// `${enum}` stringification when the user has not defined a custom `.str()` — V auto-derives
1429// one. Mirrors the struct-str qualification so the C name matches cgen's enum_decls naming.
1430fn (mut t Transformer) enum_autostr_call(expr flat.NodeId, typ string) flat.NodeId {
1431 mut qualified := typ
1432 if qualified.starts_with('main.') {
1433 qualified = qualified[5..]
1434 } else if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1435 && t.cur_module != 'builtin' {
1436 q := '${t.cur_module}.${typ}'
1437 if q in t.enum_types {
1438 qualified = q
1439 }
1440 }
1441 return t.make_call_typed('${c_name(qualified)}__autostr', arr1(expr), 'string')
1442}
1443
1444// wrap_string_conversion transforms wrap string conversion data for transform.
1445fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
1446 mut clean_typ := typ
1447 is_ref := clean_typ.starts_with('&')
1448 if clean_typ.starts_with('&') {
1449 clean_typ = clean_typ[1..]
1450 }
1451 if clean_typ.starts_with('builtin.') {
1452 clean_typ = clean_typ.all_after_last('.')
1453 }
1454 if t.is_optional_type_name(clean_typ) {
1455 return t.wrap_optional_string_conversion(expr, clean_typ)
1456 }
1457 if clean_typ.len == 0 || clean_typ == 'unknown' {
1458 inferred := t.resolve_expr_type(expr)
1459 if inferred.len > 0 && inferred != clean_typ {
1460 return t.wrap_string_conversion(expr, inferred)
1461 }
1462 }
1463 if !isnil(t.tc) {
1464 if alias := t.tc.type_aliases[clean_typ] {
1465 return t.wrap_string_conversion(expr, alias)
1466 }
1467 mut qtyp := clean_typ
1468 if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1469 && t.cur_module != 'builtin' {
1470 qtyp = '${t.cur_module}.${clean_typ}'
1471 }
1472 if alias := t.tc.type_aliases[qtyp] {
1473 return t.wrap_string_conversion(expr, alias)
1474 }
1475 if !clean_typ.contains('.') {
1476 for aname, target in t.tc.type_aliases {
1477 if aname.all_after_last('.') == clean_typ {
1478 return t.wrap_string_conversion(expr, target)
1479 }
1480 }
1481 }
1482 parsed := t.tc.parse_type(clean_typ)
1483 if parsed is types.Enum {
1484 if method := t.enum_str_method_name(clean_typ) {
1485 return t.make_call_typed(method, arr1(expr), 'string')
1486 }
1487 return t.enum_autostr_call(expr, clean_typ)
1488 }
1489 if qtyp != clean_typ {
1490 qparsed := t.tc.parse_type(qtyp)
1491 if qparsed is types.Enum {
1492 if method := t.enum_str_method_name(qtyp) {
1493 return t.make_call_typed(method, arr1(expr), 'string')
1494 }
1495 return t.enum_autostr_call(expr, qtyp)
1496 }
1497 }
1498 }
1499 if clean_typ == 'string' {
1500 return expr
1501 }
1502 if is_ref || clean_typ in ['voidptr', 'byteptr', 'charptr'] {
1503 return t.make_call_typed('ptr_str', arr1(expr), 'string')
1504 }
1505 if clean_typ == 'IError' || clean_typ == 'builtin.IError' {
1506 return t.make_call_typed('IError.str', arr1(expr), 'string')
1507 }
1508 match clean_typ {
1509 'bool' {
1510 return t.make_call_typed('bool.str', arr1(expr), 'string')
1511 }
1512 'u8', 'byte', 'u16', 'u32', 'usize' {
1513 return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(10)),
1514 'string')
1515 }
1516 'u64' {
1517 return t.make_call_typed('u64.str', arr1(expr), 'string')
1518 }
1519 'int', 'int literal' {
1520 return t.make_call_typed('int.str', arr1(expr), 'string')
1521 }
1522 'i8' {
1523 return t.make_call_typed('i8.str', arr1(expr), 'string')
1524 }
1525 'i16' {
1526 return t.make_call_typed('i16.str', arr1(expr), 'string')
1527 }
1528 'i32' {
1529 return t.make_call_typed('i32.str', arr1(expr), 'string')
1530 }
1531 'i64' {
1532 return t.make_call_typed('i64.str', arr1(expr), 'string')
1533 }
1534 'isize' {
1535 return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
1536 'string')
1537 }
1538 'f32' {
1539 return t.make_call_typed('strconv__f32_to_str_l', arr1(expr), 'string')
1540 }
1541 'f64', 'float literal' {
1542 return t.make_call_typed('strconv__f64_to_str_l', arr1(expr), 'string')
1543 }
1544 else {
1545 if clean_typ in t.enum_types {
1546 if method := t.enum_str_method_name(clean_typ) {
1547 return t.make_call_typed(method, arr1(expr), 'string')
1548 }
1549 return t.enum_autostr_call(expr, clean_typ)
1550 }
1551 mut qenum := clean_typ
1552 if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1553 && t.cur_module != 'builtin' {
1554 qenum = '${t.cur_module}.${clean_typ}'
1555 }
1556 if qenum in t.enum_types {
1557 if method := t.enum_str_method_name(qenum) {
1558 return t.make_call_typed(method, arr1(expr), 'string')
1559 }
1560 return t.enum_autostr_call(expr, qenum)
1561 }
1562 if clean_typ in t.structs || clean_typ in t.sum_types {
1563 mut qualified := clean_typ
1564 if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1565 && t.cur_module != 'builtin' {
1566 q := '${t.cur_module}.${clean_typ}'
1567 if q in t.structs || q in t.sum_types {
1568 qualified = q
1569 }
1570 }
1571 str_fn := '${c_name(qualified)}__str'
1572 known_str := str_fn in t.fn_ret_types
1573 || (!isnil(t.tc) && str_fn in t.tc.fn_ret_types)
1574 if known_str {
1575 return t.make_call_typed(str_fn, arr1(expr), 'string')
1576 }
1577 return t.make_string_literal('${qualified}{}')
1578 } else if t.is_fixed_array_type(clean_typ) {
1579 elem_type := fixed_array_elem_type(clean_typ)
1580 arr := t.fixed_array_value_to_array(expr, clean_typ, '[]${elem_type}')
1581 return t.wrap_string_conversion(arr, '[]${elem_type}')
1582 } else if clean_typ.len > 0 && clean_typ.starts_with('[]') {
1583 return t.lower_array_str(expr, clean_typ)
1584 } else if clean_typ.len > 0 && clean_typ.starts_with('map[') {
1585 return t.lower_map_str(expr, clean_typ)
1586 } else if clean_typ == 'rune' {
1587 return t.make_call_typed('rune.str', arr1(expr), 'string')
1588 } else {
1589 return expr
1590 }
1591 }
1592 }
1593}
1594
1595fn (mut t Transformer) wrap_formatted_string_conversion(expr flat.NodeId, typ string, format string) flat.NodeId {
1596 if format.len == 0 {
1597 return t.wrap_string_conversion(expr, typ)
1598 }
1599 mut clean_typ := typ
1600 if clean_typ.starts_with('&') {
1601 clean_typ = clean_typ[1..]
1602 }
1603 if clean_typ.starts_with('builtin.') {
1604 clean_typ = clean_typ.all_after_last('.')
1605 }
1606 if precision := fixed_decimal_precision(format) {
1607 if clean_typ in ['f32', 'f64', 'float_literal'] {
1608 arg := if clean_typ == 'f64' {
1609 expr
1610 } else {
1611 t.make_cast('f64', expr, 'f64')
1612 }
1613 return t.make_call_typed('v3_f64_fixed', arr2(arg, t.make_int_literal(precision)),
1614 'string')
1615 }
1616 }
1617 if format == 'c' {
1618 if clean_typ in ['u8', 'byte', 'char', 'rune', 'int'] {
1619 arg := if clean_typ == 'int' {
1620 expr
1621 } else {
1622 t.make_cast('int', expr, 'int')
1623 }
1624 return t.make_call_typed('v3_char_string', arr1(arg), 'string')
1625 }
1626 }
1627 if base := integer_format_base(format) {
1628 if clean_typ in ['u8', 'byte', 'u16', 'u32', 'u64', 'usize'] {
1629 return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(base)),
1630 'string')
1631 }
1632 if clean_typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize'] {
1633 return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(base)),
1634 'string')
1635 }
1636 }
1637 if width := zero_padded_decimal_width(format) {
1638 if clean_typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8', 'byte', 'u16',
1639 'u32', 'u64'] {
1640 if clean_typ in ['u64', 'usize', 'u32', 'u16', 'u8', 'byte'] {
1641 arg := if clean_typ == 'u64' {
1642 expr
1643 } else {
1644 t.make_cast('u64', expr, 'u64')
1645 }
1646 return t.make_call_typed('v3_u64_zpad', arr2(arg, t.make_int_literal(width)),
1647 'string')
1648 }
1649 if clean_typ in ['i64', 'isize', 'i32', 'i16', 'i8'] {
1650 arg := if clean_typ == 'i64' {
1651 expr
1652 } else {
1653 t.make_cast('i64', expr, 'i64')
1654 }
1655 return t.make_call_typed('v3_i64_zpad', arr2(arg, t.make_int_literal(width)),
1656 'string')
1657 }
1658 return t.make_call_typed('v3_int_zpad', arr2(expr, t.make_int_literal(width)), 'string')
1659 }
1660 }
1661 return t.wrap_string_conversion(expr, typ)
1662}
1663
1664fn fixed_decimal_precision(format string) ?int {
1665 if format.len < 3 || format[0] != `.` || format[format.len - 1] != `f` {
1666 return none
1667 }
1668 mut n := 0
1669 for i in 1 .. format.len - 1 {
1670 ch := format[i]
1671 if ch < `0` || ch > `9` {
1672 return none
1673 }
1674 n = n * 10 + int(ch - `0`)
1675 }
1676 return n
1677}
1678
1679fn integer_format_base(format string) ?int {
1680 match format {
1681 'x' {
1682 return 16
1683 }
1684 'o' {
1685 return 8
1686 }
1687 else {}
1688 }
1689
1690 return none
1691}
1692
1693fn zero_padded_decimal_width(format string) ?int {
1694 if format.len < 2 || format[0] != `0` {
1695 return none
1696 }
1697 mut end := format.len
1698 if format[end - 1] == `d` {
1699 end--
1700 }
1701 if end >= 3 && format[end - 2] == `.` && format[end - 1] == `0` {
1702 end -= 2
1703 }
1704 if end <= 1 {
1705 return none
1706 }
1707 mut width := 0
1708 for i in 1 .. end {
1709 ch := format[i]
1710 if ch < `0` || ch > `9` {
1711 return none
1712 }
1713 width = width * 10 + int(ch - `0`)
1714 }
1715 if width <= 0 {
1716 return none
1717 }
1718 return width
1719}
1720
1721// append_string builds `result = result + piece` using the runtime string concat helper.
1722// Using string__plus directly (instead of `+=`) keeps the synthesized node independent of
1723// type resolution for the freshly-introduced temp.
1724fn (mut t Transformer) append_string(result_name string, piece flat.NodeId) flat.NodeId {
1725 concat := t.make_call_typed('string__plus', arr2(t.make_ident(result_name), piece), 'string')
1726 return t.make_assign(t.make_ident(result_name), concat)
1727}
1728
1729// lower_array_str expands `${arr}` for a `[]T` into a runtime loop that formats each element
1730// via wrap_string_conversion, so nested arrays, structs with `str`, enums, etc. all recurse
1731// correctly. Produces `[e0, e1, ...]`; string elements are wrapped in single quotes to match V.
1732fn (mut t Transformer) lower_array_str(arr_expr flat.NodeId, base_type string) flat.NodeId {
1733 src := t.a.nodes[int(arr_expr)]
1734 elem_type := base_type[2..]
1735 base := t.stable_expr_for_reuse(arr_expr)
1736 mut prefix := []flat.NodeId{}
1737 t.drain_pending(mut prefix)
1738 result_name := t.new_temp('arr_str')
1739 idx_name := t.new_temp('arr_str_idx')
1740 prefix << t.make_decl_assign_typed(result_name, t.make_string_literal('['), 'string')
1741 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1742 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
1743 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1744 elem_name := t.new_temp('arr_str_it')
1745 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
1746 elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
1747 mut loop_body := []flat.NodeId{}
1748 loop_body << elem_decl
1749 // `if idx > 0 { result = result + ', ' }`
1750 sep_cond := t.make_infix(.gt, t.make_ident(idx_name), t.make_int_literal(0))
1751 sep_stmt := t.append_string(result_name, t.make_string_literal(', '))
1752 loop_body << t.make_if(sep_cond, t.make_block(arr1(sep_stmt)), t.make_empty())
1753 // element text (recurses; may push its own statements for nested arrays/optionals)
1754 t.set_var_type(elem_name, elem_type)
1755 elem_str := t.wrap_string_conversion(t.make_ident(elem_name), elem_type)
1756 t.unset_var_type(elem_name)
1757 t.drain_pending(mut loop_body)
1758 if elem_type == 'string' {
1759 loop_body << t.append_string(result_name, t.make_string_literal("'"))
1760 loop_body << t.append_string(result_name, elem_str)
1761 loop_body << t.append_string(result_name, t.make_string_literal("'"))
1762 } else {
1763 loop_body << t.append_string(result_name, elem_str)
1764 }
1765 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
1766 prefix << t.append_string(result_name, t.make_string_literal(']'))
1767 for stmt in prefix {
1768 t.pending_stmts << stmt
1769 }
1770 return t.make_ident(result_name)
1771}
1772
1773fn (mut t Transformer) lower_map_str(map_expr flat.NodeId, map_type string) flat.NodeId {
1774 key_type, value_type := t.map_type_parts(map_type)
1775 if key_type.len == 0 || value_type.len == 0 {
1776 return map_expr
1777 }
1778 lowered := t.transform_expr_for_type(map_expr, map_type)
1779 return t.make_call_typed('v3_map_str', arr4(lowered,
1780 t.make_int_literal(t.map_str_kind_for_type(key_type)),
1781 t.make_int_literal(t.map_str_kind_for_type(value_type)),
1782 t.make_int_literal(t.map_str_fixed_len_for_type(value_type))), 'string')
1783}
1784
1785fn (t &Transformer) map_str_kind_for_type(typ string) int {
1786 mut clean := t.normalize_type_alias(typ).trim_space()
1787 if clean.starts_with('builtin.') {
1788 clean = clean.all_after_last('.')
1789 }
1790 match clean {
1791 'string' {
1792 return 1
1793 }
1794 'rune' {
1795 return 4
1796 }
1797 'isize', 'char', 'i8', 'i16', 'i32', 'i64', 'int' {
1798 return 2
1799 }
1800 'usize', 'u8', 'byte', 'u16', 'u32', 'u64' {
1801 return 3
1802 }
1803 'f32', 'f64' {
1804 return 5
1805 }
1806 'bool' {
1807 return 7
1808 }
1809 else {
1810 if clean.starts_with('[]') && clean[2..] in ['f32', 'f64'] {
1811 return 6
1812 }
1813 if transform_type_text_is_fixed_array(clean) {
1814 elem := t.normalize_type_alias(fixed_array_elem_type(clean))
1815 if elem == 'f32' {
1816 return 9
1817 }
1818 if elem == 'f64' {
1819 return 6
1820 }
1821 }
1822 return 0
1823 }
1824 }
1825}
1826
1827fn (t &Transformer) map_str_fixed_len_for_type(typ string) int {
1828 clean := t.normalize_type_alias(typ).trim_space()
1829 if transform_type_text_is_fixed_array(clean) {
1830 return fixed_array_len(clean)
1831 }
1832 return 0
1833}
1834
1835// wrap_optional_string_conversion transforms wrap optional string conversion data for transform.
1836fn (mut t Transformer) wrap_optional_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
1837 opt_type := t.qualify_optional_type(typ)
1838 mut value_type := t.optional_base_type(opt_type)
1839 if value_type.len == 0 || value_type == 'void' {
1840 value_type = 'int'
1841 }
1842 opt_name := t.new_temp('opt_str')
1843 res_name := t.new_temp('opt_str_text')
1844 t.pending_stmts << t.make_decl_assign_typed(opt_name, expr, opt_type)
1845 t.pending_stmts << t.make_decl_assign_typed(res_name, t.make_string_literal('Option(none)'),
1846 'string')
1847 value := t.make_selector(t.make_ident(opt_name), 'value', value_type)
1848 mut value_str := t.wrap_string_conversion(value, value_type)
1849 if value_type == 'string' {
1850 value_str = t.string_plus(t.string_plus(t.make_string_literal("'"), value_str),
1851 t.make_string_literal("'"))
1852 }
1853 some_str := t.string_plus(t.string_plus(t.make_string_literal('Option('), value_str),
1854 t.make_string_literal(')'))
1855 assign_some := t.make_assign(t.make_ident(res_name), some_str)
1856 t.pending_stmts << t.make_if(t.make_selector(t.make_ident(opt_name), 'ok', 'bool'),
1857 t.make_block(arr1(assign_some)), t.make_empty())
1858 return t.make_ident(res_name)
1859}
1860
1861// string_plus supports string plus handling for Transformer.
1862fn (mut t Transformer) string_plus(left flat.NodeId, right flat.NodeId) flat.NodeId {
1863 return t.make_call_typed('string__plus', arr2(left, right), 'string')
1864}
1865
1866// is_flag_enum_type reports whether is flag enum type applies in transform.
1867fn (t &Transformer) is_flag_enum_type(typ string) bool {
1868 mut clean := typ
1869 if clean.starts_with('&') {
1870 clean = clean[1..]
1871 }
1872 if clean.len == 0 {
1873 return false
1874 }
1875 if !isnil(t.tc) {
1876 parsed := t.tc.parse_type(clean)
1877 if parsed is types.Enum {
1878 return parsed.is_flag
1879 }
1880 }
1881 return false
1882}
1883
1884// is_runtime_array_flags_selector reports is_runtime_array_flags_selector logic in transform.
1885fn (t &Transformer) is_runtime_array_flags_selector(id flat.NodeId) bool {
1886 if int(id) < 0 {
1887 return false
1888 }
1889 node := t.a.nodes[int(id)]
1890 if node.kind != .selector || node.value != 'flags' || node.children_count == 0 {
1891 return false
1892 }
1893 owner_id := t.a.child(&node, 0)
1894 owner_type := t.node_type(owner_id).trim_left('&')
1895 return owner_type.starts_with('[]') || owner_type == 'strings.Builder'
1896}
1897
1898// try_lower_flag_enum_stmt supports try lower flag enum stmt handling for Transformer.
1899fn (mut t Transformer) try_lower_flag_enum_stmt(call_id flat.NodeId) ?flat.NodeId {
1900 if int(call_id) < 0 {
1901 return none
1902 }
1903 call := t.a.nodes[int(call_id)]
1904 if call.kind != .call || call.children_count < 2 {
1905 return none
1906 }
1907 fn_id := t.a.children[call.children_start]
1908 fn_node := t.a.nodes[int(fn_id)]
1909 if fn_node.kind != .selector || fn_node.children_count == 0
1910 || fn_node.value !in ['set', 'clear'] {
1911 return none
1912 }
1913 base_id := t.a.children[fn_node.children_start]
1914 if t.is_runtime_array_flags_selector(base_id) {
1915 return none
1916 }
1917 base_type := t.node_type(base_id)
1918 if !t.is_flag_enum_type(base_type) {
1919 return none
1920 }
1921 base := t.transform_expr(base_id)
1922 arg := t.transform_expr(t.a.children[call.children_start + 1])
1923 if fn_node.value == 'set' {
1924 return t.make_assign_op(base, arg, .pipe_assign)
1925 }
1926 return t.make_assign_op(base, t.make_prefix(.bit_not, arg), .amp_assign)
1927}
1928
1929// try_lower_flag_enum_call supports try lower flag enum call handling for Transformer.
1930fn (mut t Transformer) try_lower_flag_enum_call(node flat.Node) ?flat.NodeId {
1931 if node.children_count == 1 {
1932 fn_id := t.a.children[node.children_start]
1933 fn_node := t.a.nodes[int(fn_id)]
1934 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'zero' {
1935 return none
1936 }
1937 base_id := t.a.children[fn_node.children_start]
1938 base_node := t.a.nodes[int(base_id)]
1939 if base_node.kind != .ident || !t.is_flag_enum_type(base_node.value) {
1940 return none
1941 }
1942 return t.make_cast(base_node.value, t.make_int_literal(0), base_node.value)
1943 }
1944 if node.children_count < 2 {
1945 return none
1946 }
1947 fn_id := t.a.children[node.children_start]
1948 fn_node := t.a.nodes[int(fn_id)]
1949 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value !in ['has', 'all'] {
1950 return none
1951 }
1952 base_id := t.a.children[fn_node.children_start]
1953 if t.is_runtime_array_flags_selector(base_id) {
1954 return none
1955 }
1956 mut base_type := t.node_type(base_id)
1957 if base_type.len == 0 {
1958 base_type = t.lvalue_type(base_id)
1959 }
1960 if !t.is_flag_enum_type(base_type) {
1961 return none
1962 }
1963 base := t.transform_expr(base_id)
1964 arg_id := t.a.children[node.children_start + 1]
1965 arg := t.transform_expr(arg_id)
1966 masked := t.make_infix(.amp, base, arg)
1967 if fn_node.value == 'has' {
1968 return t.make_infix(.ne, masked, t.make_int_literal(0))
1969 }
1970 arg_copy := t.transform_expr(arg_id)
1971 return t.make_infix(.eq, masked, arg_copy)
1972}
1973
1974// try_lower_array_method_call supports try lower array method call handling for Transformer.
1975fn (mut t Transformer) try_lower_array_method_call(call_id flat.NodeId, node flat.Node) ?flat.NodeId {
1976 if node.children_count == 0 {
1977 return none
1978 }
1979 fn_id := t.a.children[node.children_start]
1980 fn_node := t.a.nodes[int(fn_id)]
1981 if fn_node.kind != .selector || fn_node.children_count == 0 {
1982 return none
1983 }
1984 array_builtin_method := t.array_builtin_method_name(fn_node.value) or { '' }
1985 if fn_node.value !in ['clone', 'reverse', 'contains', 'index', 'last_index', 'join', 'any',
1986 'all', 'count', 'equals', 'prepend', 'insert', 'push_many', 'str'] {
1987 if fn_node.value !in ['filter', 'map', 'sort', 'sorted', 'sort_with_compare', 'sorted_with_compare']
1988 && array_builtin_method.len == 0 {
1989 return none
1990 }
1991 }
1992 base_id := t.a.children[fn_node.children_start]
1993 mut base_type := t.node_type(base_id)
1994 base_node := t.a.nodes[int(base_id)]
1995 if (array_type_has_generic_placeholder(base_type) || base_type.contains('unknown'))
1996 && base_node.kind == .call {
1997 concrete_base_type := t.concrete_generic_call_return_type(base_id, base_node)
1998 if concrete_base_type.starts_with('[]') {
1999 base_type = concrete_base_type
2000 }
2001 }
2002 if (!base_type.starts_with('[]') && !t.is_fixed_array_type(base_type)) || base_type == 'array' {
2003 lvalue_base_type := t.lvalue_type(base_id)
2004 if lvalue_base_type.starts_with('[]') || t.is_fixed_array_type(lvalue_base_type) {
2005 base_type = lvalue_base_type
2006 }
2007 }
2008 if !base_type.starts_with('[]') && !t.is_fixed_array_type(base_type) {
2009 if base_node.kind in [.call, .selector, .as_expr] {
2010 new_base := t.transform_expr(base_id)
2011 new_base_type := t.node_type(new_base)
2012 if new_base_type.starts_with('[]') || t.is_fixed_array_type(new_base_type) {
2013 selector := t.make_selector(new_base, fn_node.value, '')
2014 mut children := []flat.NodeId{cap: int(node.children_count)}
2015 children << selector
2016 for i in 1 .. node.children_count {
2017 children << t.a.child(&node, i)
2018 }
2019 start := t.a.children.len
2020 for child in children {
2021 t.a.children << child
2022 }
2023 new_node := flat.Node{
2024 kind: .call
2025 children_start: start
2026 children_count: node.children_count
2027 pos: node.pos
2028 typ: node.typ
2029 }
2030 return t.try_lower_array_method_call(call_id, new_node)
2031 }
2032 }
2033 }
2034 if fn_node.value == 'str' && base_node.kind == .call {
2035 new_base := t.transform_expr(base_id)
2036 new_base_type := t.node_type(new_base)
2037 if new_base_type.starts_with('[]') {
2038 return t.lower_array_str(new_base, new_base_type)
2039 }
2040 if new_base_type.starts_with('map[') {
2041 return t.lower_map_str(new_base, new_base_type)
2042 }
2043 }
2044 clean_base_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2045 if fn_node.value == 'str' && t.is_builder_receiver(base_id, base_type) {
2046 return none
2047 }
2048 if t.is_fixed_array_type(clean_base_type) && fn_node.value == 'pointers'
2049 && array_builtin_method.len > 0 {
2050 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
2051 if method_name.len > 0 && method_name != array_builtin_method
2052 && t.call_resolved_to_method(call_id, method_name) {
2053 args := t.transform_receiver_method_args(node, base_id, method_name)
2054 ret_type := t.receiver_method_return_type(method_name, node.typ)
2055 t.mark_fn_used(method_name)
2056 return t.make_call_typed(method_name, args, ret_type)
2057 }
2058 if dynamic_method := t.resolve_fixed_array_dynamic_receiver_method(clean_base_type,
2059 fn_node.value)
2060 {
2061 return t.lower_fixed_array_dynamic_receiver_method_call(node, base_id, clean_base_type,
2062 dynamic_method)
2063 }
2064 args := t.transform_receiver_method_args(node, base_id, array_builtin_method)
2065 ret_type := t.receiver_method_return_type(array_builtin_method, node.typ)
2066 return t.make_call_typed(array_builtin_method, args, ret_type)
2067 }
2068 if t.is_fixed_array_type(clean_base_type) {
2069 elem_type := fixed_array_elem_type(clean_base_type)
2070 array_type := '[]${elem_type}'
2071 tmp_name := t.new_temp('fixed_arr')
2072 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.fixed_array_value_to_array(base_id,
2073 clean_base_type, array_type), array_type)
2074 selector := t.make_selector(t.make_ident(tmp_name), fn_node.value, '')
2075 mut children := []flat.NodeId{cap: int(node.children_count)}
2076 children << selector
2077 for i in 1 .. node.children_count {
2078 children << t.a.child(&node, i)
2079 }
2080 start := t.a.children.len
2081 for child in children {
2082 t.a.children << child
2083 }
2084 new_node := flat.Node{
2085 kind: .call
2086 children_start: start
2087 children_count: node.children_count
2088 pos: node.pos
2089 typ: node.typ
2090 }
2091 return t.try_lower_array_method_call(call_id, new_node)
2092 }
2093 if !clean_base_type.starts_with('[]') {
2094 return none
2095 }
2096 if exact_call := t.lower_checker_selected_receiver_method(call_id, node, base_id,
2097 array_builtin_method)
2098 {
2099 return exact_call
2100 }
2101 elem_type := clean_base_type[2..]
2102 if fn_node.value == 'prepend' {
2103 return t.lower_array_prepend_call(node, fn_node, base_type, elem_type)
2104 }
2105 if fn_node.value == 'insert' {
2106 return t.lower_array_insert_call(node, fn_node, base_type, elem_type)
2107 }
2108 if fn_node.value == 'push_many' {
2109 return t.lower_array_push_many_call(node, fn_node, base_type, elem_type)
2110 }
2111 if fn_node.value == 'contains' {
2112 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
2113 if method_name.len > 0 && t.call_resolved_to_method(call_id, method_name) {
2114 args := t.transform_receiver_method_args(node, base_id, method_name)
2115 ret_type := t.receiver_method_return_type(method_name, node.typ)
2116 t.mark_fn_used(method_name)
2117 return t.make_call_typed(method_name, args, ret_type)
2118 }
2119 }
2120 match fn_node.value {
2121 'filter' {
2122 return t.lower_array_filter_call(node, fn_node, clean_base_type)
2123 }
2124 'map' {
2125 return t.lower_array_map_call(node, fn_node, clean_base_type)
2126 }
2127 'sort' {
2128 return t.lower_array_sort_call(node, fn_node, base_type)
2129 }
2130 'sorted' {
2131 return t.lower_array_sorted_call(node, fn_node, clean_base_type)
2132 }
2133 'sort_with_compare' {
2134 return t.lower_array_sort_with_compare_call(node, fn_node, base_type)
2135 }
2136 'sorted_with_compare' {
2137 return t.lower_array_sorted_with_compare_call(node, fn_node, clean_base_type)
2138 }
2139 'any', 'all' {
2140 return t.lower_array_any_all_call(node, fn_node, clean_base_type, fn_node.value)
2141 }
2142 'count' {
2143 return t.lower_array_count_call(node, fn_node, clean_base_type)
2144 }
2145 'str' {
2146 return t.wrap_string_conversion(t.transform_expr(base_id), clean_base_type)
2147 }
2148 'equals' {
2149 if node.children_count < 2 {
2150 return none
2151 }
2152 method_name := t.resolve_receiver_method_name(base_id, 'equals')
2153 if method_name.len > 0 && t.call_resolved_to_method(call_id, method_name) {
2154 args := t.transform_receiver_method_args(node, base_id, method_name)
2155 t.mark_fn_used(method_name)
2156 return t.make_call_typed(method_name, args, 'bool')
2157 }
2158 receiver := t.transform_expr(base_id)
2159 arg := t.transform_expr(t.a.children[node.children_start + 1])
2160 if t.array_elem_needs_element_eq(elem_type) {
2161 return t.make_array_elementwise_eq_call(receiver, arg, elem_type, clean_base_type,
2162 clean_base_type, node)
2163 }
2164 if elem_type.starts_with('[]') {
2165 return t.make_call_typed('array_eq_array', arr3(receiver, arg,
2166 t.make_int_literal(array_nested_eq_depth(clean_base_type))), 'bool')
2167 }
2168 if elem_type == 'string' {
2169 return t.make_call_typed('array_eq_string', arr2(receiver, arg), 'bool')
2170 }
2171 return t.make_call_typed('array_eq_raw', arr3(receiver, arg,
2172 t.make_sizeof_type(elem_type)), 'bool')
2173 }
2174 else {}
2175 }
2176
2177 match fn_node.value {
2178 'clone' {
2179 method_name := t.resolve_collection_receiver_method_name(base_id, fn_node.value,
2180 clean_base_type)
2181 if method_name.len > 0 && t.call_resolved_to_method(call_id, method_name) {
2182 args := t.transform_receiver_method_args(node, base_id, method_name)
2183 ret_type := t.receiver_method_return_type(method_name, node.typ)
2184 t.mark_fn_used(method_name)
2185 return t.make_call_typed(method_name, args, ret_type)
2186 }
2187 return t.make_array_clone_call(base_id, base_type)
2188 }
2189 'reverse' {
2190 mut receiver := t.transform_expr(base_id)
2191 if base_type.starts_with('&') {
2192 receiver = t.make_prefix(.mul, receiver)
2193 t.a.nodes[int(receiver)].typ = clean_base_type
2194 }
2195 return t.make_call_typed('array__reverse', arr1(receiver), clean_base_type)
2196 }
2197 'contains' {
2198 if node.children_count < 2 {
2199 return none
2200 }
2201 arg_id := t.a.children[node.children_start + 1]
2202 if lowered := t.lower_array_membership_expr(base_id, arg_id, base_type, true, node) {
2203 return lowered
2204 }
2205 receiver := t.transform_expr(base_id)
2206 arg := t.transform_expr(arg_id)
2207 fn_name := if elem_type == 'string' {
2208 'array_contains_string'
2209 } else {
2210 'array_contains_int'
2211 }
2212 return t.make_call_typed(fn_name, arr2(receiver, arg), 'bool')
2213 }
2214 'index' {
2215 if node.children_count < 2 {
2216 return none
2217 }
2218 arg_id := t.a.children[node.children_start + 1]
2219 if lowered := t.lower_array_index_expr(base_id, arg_id, base_type, true, node) {
2220 return lowered
2221 }
2222 receiver := t.transform_expr(base_id)
2223 arg := t.transform_expr(arg_id)
2224 fn_name := if elem_type == 'string' { 'array_index_string' } else { 'array_index_int' }
2225 return t.make_call_typed(fn_name, arr2(receiver, arg), 'int')
2226 }
2227 'last_index' {
2228 if node.children_count < 2 {
2229 return none
2230 }
2231 arg_id := t.a.children[node.children_start + 1]
2232 if lowered := t.lower_array_last_index_expr(base_id, arg_id, base_type, true, node) {
2233 return lowered
2234 }
2235 return none
2236 }
2237 'join' {
2238 if node.children_count < 2 {
2239 return none
2240 }
2241 receiver := t.transform_expr(base_id)
2242 arg := t.transform_expr(t.a.children[node.children_start + 1])
2243 return t.make_call_typed('Array_string__join', arr2(receiver, arg), 'string')
2244 }
2245 else {
2246 if array_method_stays_in_cgen(fn_node.value) {
2247 method_name := t.resolve_collection_receiver_method_name(base_id, fn_node.value,
2248 clean_base_type)
2249 if method_name.len > 0 && method_name != array_builtin_method
2250 && t.call_resolved_to_method(call_id, method_name) {
2251 args := t.transform_receiver_method_args(node, base_id, method_name)
2252 ret_type := t.receiver_method_return_type(method_name, node.typ)
2253 t.mark_fn_used(method_name)
2254 return t.make_call_typed(method_name, args, ret_type)
2255 }
2256 if fn_node.value in ['pop', 'clear', 'trim'] {
2257 t.mark_fn_used('array__${fn_node.value}')
2258 }
2259 return none
2260 }
2261 if array_builtin_method.len > 0 {
2262 method_name := t.resolve_collection_receiver_method_name(base_id, fn_node.value,
2263 clean_base_type)
2264 if method_name.len > 0 && method_name != array_builtin_method {
2265 args := t.transform_receiver_method_args(node, base_id, method_name)
2266 ret_type := t.receiver_method_return_type(method_name, node.typ)
2267 t.mark_fn_used(method_name)
2268 return t.make_call_typed(method_name, args, ret_type)
2269 }
2270 args := t.transform_receiver_method_args(node, base_id, array_builtin_method)
2271 ret_type := t.receiver_method_return_type(array_builtin_method, node.typ)
2272 return t.make_call_typed(array_builtin_method, args, ret_type)
2273 }
2274 return none
2275 }
2276 }
2277}
2278
2279fn (t &Transformer) array_builtin_method_name(method string) ?string {
2280 method_name := 'array.${method}'
2281 if t.is_known_fn_name(method_name) {
2282 return method_name
2283 }
2284 return none
2285}
2286
2287fn array_method_stays_in_cgen(method string) bool {
2288 return method in ['last', 'first', 'delete_last', 'pop', 'pop_left', 'clear', 'repeat',
2289 'repeat_to_depth', 'trim', 'ensure_cap', 'delete', 'free', 'str', 'bytestr', 'wait']
2290}
2291
2292// try_lower_map_method_call supports try lower map method call handling for Transformer.
2293fn (mut t Transformer) try_lower_map_method_call(call_id flat.NodeId, node flat.Node) ?flat.NodeId {
2294 if node.children_count == 0 {
2295 return none
2296 }
2297 fn_id := t.a.children[node.children_start]
2298 fn_node := t.a.nodes[int(fn_id)]
2299 if fn_node.kind != .selector || fn_node.children_count == 0
2300 || fn_node.value !in ['keys', 'values', 'delete', 'clear', 'free', 'move', 'reserve'] {
2301 return none
2302 }
2303 base_id := t.a.children[fn_node.children_start]
2304 mut base_type := t.node_type(base_id)
2305 if base_type.len == 0 {
2306 base_type = t.checker_node_type(base_id)
2307 }
2308 clean_type := t.clean_map_type(base_type)
2309 if !clean_type.starts_with('map[') {
2310 return none
2311 }
2312 builtin_method := 'map.${fn_node.value}'
2313 if exact_call := t.lower_checker_selected_receiver_method(call_id, node, base_id,
2314 builtin_method)
2315 {
2316 return exact_call
2317 }
2318 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
2319 if method_name.len > 0 && method_name != builtin_method
2320 && t.call_resolved_to_method(call_id, method_name) {
2321 args := t.transform_receiver_method_args(node, base_id, method_name)
2322 ret_type := t.receiver_method_return_type(method_name, node.typ)
2323 t.mark_fn_used(method_name)
2324 return t.make_call_typed(method_name, args, ret_type)
2325 }
2326 base := t.transform_expr(base_id)
2327 if fn_node.value in ['clear', 'free'] {
2328 t.mark_fn_used('map__${fn_node.value}')
2329 return t.make_call_typed('map__${fn_node.value}', arr1(t.runtime_addr(base, base_type)),
2330 'void')
2331 }
2332 if fn_node.value == 'move' {
2333 t.mark_fn_used('map__move')
2334 return t.make_call_typed('map__move', arr1(t.runtime_addr(base, base_type)), clean_type)
2335 }
2336 if fn_node.value == 'reserve' {
2337 if node.children_count < 2 {
2338 return none
2339 }
2340 t.mark_fn_used('map__reserve')
2341 capacity := t.transform_expr_for_type(t.a.child(&node, 1), 'u32')
2342 return t.make_call_typed('map__reserve', arr2(t.runtime_addr(base, base_type), capacity),
2343 'void')
2344 }
2345 if fn_node.value == 'delete' {
2346 if node.children_count < 2 {
2347 return none
2348 }
2349 key_type := t.map_key_type(clean_type)
2350 if key_type.len == 0 {
2351 return none
2352 }
2353 t.mark_fn_used('map__delete')
2354 key_name := t.new_temp('map_key')
2355 t.pending_stmts << t.make_decl_assign_typed(key_name, t.transform_expr_for_type(t.a.child(&node, 1),
2356 key_type), key_type)
2357 return t.make_call_typed('map__delete', arr2(t.runtime_addr(base, base_type), t.make_prefix(.amp,
2358 t.make_ident(key_name))), 'void')
2359 }
2360 elem_type := if fn_node.value == 'keys' {
2361 t.map_key_type(clean_type)
2362 } else {
2363 t.map_value_type(clean_type)
2364 }
2365 if elem_type.len == 0 {
2366 return none
2367 }
2368 t.mark_fn_used('map__${fn_node.value}')
2369 return t.make_call_typed('map__${fn_node.value}', arr1(t.runtime_addr(base, base_type)),
2370 '[]${elem_type}')
2371}
2372
2373// try_lower_channel_method_call lowers channel source methods to runtime calls before
2374// backend selection.
2375fn (mut t Transformer) try_lower_channel_method_call(call_id flat.NodeId, node flat.Node) ?flat.NodeId {
2376 if node.children_count == 0 {
2377 return none
2378 }
2379 fn_id := t.a.children[node.children_start]
2380 fn_node := t.a.nodes[int(fn_id)]
2381 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'close' {
2382 return none
2383 }
2384 base_id := t.a.child(&fn_node, 0)
2385 if exact_call := t.lower_checker_selected_receiver_method(call_id, node, base_id, 'chan.close') {
2386 return exact_call
2387 }
2388 mut base_type := t.node_type(base_id)
2389 if base_type.len == 0 {
2390 base_type = t.lvalue_type(base_id)
2391 }
2392 mut clean_type := base_type
2393 mut ptr_depth := 0
2394 for clean_type.starts_with('&') {
2395 ptr_depth++
2396 clean_type = clean_type[1..].trim_space()
2397 }
2398 if !clean_type.starts_with('chan ') {
2399 return none
2400 }
2401 t.mark_fn_used('sync__Channel__close')
2402 errs := t.make_array_new_call('IError', t.make_int_literal(0), t.make_int_literal(0))
2403 fn_expr := t.make_selector(t.make_ident('C'), 'sync__Channel__close', '')
2404 mut receiver := t.transform_expr(base_id)
2405 for _ in 0 .. ptr_depth {
2406 receiver = t.make_prefix(.mul, receiver)
2407 }
2408 return t.make_call_expr_typed(fn_expr, arr2(receiver, errs), 'void')
2409}
2410
2411// try_lower_move_method_call supports try lower move method call handling for Transformer.
2412fn (mut t Transformer) try_lower_move_method_call(call_id flat.NodeId, node flat.Node) ?flat.NodeId {
2413 if node.children_count != 1 {
2414 return none
2415 }
2416 fn_id := t.a.children[node.children_start]
2417 fn_node := t.a.nodes[int(fn_id)]
2418 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'move' {
2419 return none
2420 }
2421 base_id := t.a.child(&fn_node, 0)
2422 base_type := t.node_type(base_id)
2423 clean_array_type := t.membership_container_type(base_type)
2424 if clean_array_type.starts_with('[]') {
2425 if !isnil(t.tc) {
2426 if resolved := t.tc.resolved_call_name(call_id) {
2427 if !is_builtin_collection_resolved_call(resolved) && t.is_known_fn_name(resolved) {
2428 return none
2429 }
2430 }
2431 }
2432 return t.transform_expr(base_id)
2433 }
2434 return none
2435}
2436
2437// lift_fn_literal supports lift fn literal handling for Transformer.
2438fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.NodeId {
2439 name := t.new_temp('anon_fn')
2440 mut param_types := []types.Type{}
2441 mut param_ids := []flat.NodeId{}
2442 mut param_names := []string{}
2443 mut capture_names := []string{}
2444 mut capture_types := map[string]string{}
2445 mut capture_globals := map[string]string{}
2446 mut body_ids := []flat.NodeId{}
2447 for i in 0 .. node.children_count {
2448 child_id := t.a.child(&node, i)
2449 child := t.a.nodes[int(child_id)]
2450 if child.kind == .param {
2451 param_ids << child_id
2452 if child.value.len > 0 {
2453 param_names << child.value
2454 }
2455 if !isnil(t.tc) {
2456 param_types << t.tc.parse_type(child.typ)
2457 }
2458 } else if child.kind == .ident {
2459 if child.value.len > 0 && child.value !in capture_names {
2460 mut capture_type := t.var_type(child.value)
2461 if capture_type.len == 0 {
2462 capture_type = t.node_type(child_id)
2463 }
2464 if capture_type.len == 0 && !isnil(t.tc) {
2465 if typ := t.tc.expr_type(child_id) {
2466 capture_type = typ.name()
2467 }
2468 }
2469 if capture_type.len == 0 || capture_type == 'unknown' {
2470 capture_type = 'int'
2471 }
2472 capture_names << child.value
2473 capture_types[child.value] = capture_type
2474 capture_globals[child.value] = '${name}_${child.value}'
2475 }
2476 } else {
2477 body_ids << child_id
2478 }
2479 }
2480 ret_type := if node.typ.len > 0 { node.typ } else { 'void' }
2481 saved_fn_name := t.cur_fn_name
2482 saved_ret_type := t.cur_fn_ret_type
2483 saved_vars := t.var_types.clone()
2484 t.cur_fn_name = name
2485 t.cur_fn_ret_type = ret_type
2486 t.reset_var_types()
2487 for param_id in param_ids {
2488 param := t.a.nodes[int(param_id)]
2489 if param.value.len > 0 && param.typ.len > 0 {
2490 t.set_var_type(param.value, param.typ)
2491 }
2492 }
2493 mut lifted_body := []flat.NodeId{cap: capture_names.len + body_ids.len}
2494 for capture_name in capture_names {
2495 if capture_name in param_names {
2496 continue
2497 }
2498 capture_type := capture_types[capture_name] or { continue }
2499 capture_global := capture_globals[capture_name] or { continue }
2500 t.add_fn_literal_capture_global(capture_global, capture_type)
2501 t.pending_stmts << t.make_assign(t.make_ident(capture_global), t.make_ident(capture_name))
2502 t.set_var_type(capture_name, capture_type)
2503 lifted_body << t.make_decl_assign_typed(capture_name, t.make_ident(capture_global),
2504 capture_type)
2505 }
2506 for body_id in body_ids {
2507 lifted_body << body_id
2508 }
2509 outer_pending := t.pending_stmts.clone()
2510 t.pending_stmts.clear()
2511 new_body := t.transform_stmts(lifted_body)
2512 t.pending_stmts = outer_pending
2513 t.var_types = saved_vars
2514 t.cur_fn_name = saved_fn_name
2515 t.cur_fn_ret_type = saved_ret_type
2516 mut all_ids := []flat.NodeId{cap: param_ids.len + new_body.len}
2517 for param_id in param_ids {
2518 all_ids << param_id
2519 }
2520 for body_id in new_body {
2521 all_ids << body_id
2522 }
2523 if t.cur_module.len > 0 {
2524 t.a.add_node(flat.Node{
2525 kind: .module_decl
2526 value: t.cur_module
2527 })
2528 }
2529 start := t.a.children.len
2530 for child_id in all_ids {
2531 t.a.children << child_id
2532 }
2533 t.a.add_node(flat.Node{
2534 kind: .fn_decl
2535 value: name
2536 typ: ret_type
2537 children_start: start
2538 children_count: flat.child_count(all_ids.len)
2539 })
2540 if !isnil(t.tc) {
2541 ret := t.tc.parse_type(ret_type)
2542 t.tc.fn_ret_types[name] = ret
2543 t.tc.fn_param_types[name] = param_types.clone()
2544 t.tc.fn_variadic[name] = false
2545 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2546 qname := '${t.cur_module}.${name}'
2547 t.tc.fn_ret_types[qname] = ret
2548 t.tc.fn_param_types[qname] = param_types.clone()
2549 t.tc.fn_variadic[qname] = false
2550 }
2551 }
2552 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2553 return t.make_ident('${t.cur_module}.${name}')
2554 }
2555 return t.make_ident(name)
2556}
2557
2558fn (mut t Transformer) add_fn_literal_capture_global(name string, typ string) {
2559 if typ.len == 0 {
2560 return
2561 }
2562 if t.cur_module.len > 0 {
2563 t.a.add_node(flat.Node{
2564 kind: .module_decl
2565 value: t.cur_module
2566 })
2567 }
2568 field := t.a.add_node(flat.Node{
2569 kind: .field_decl
2570 value: name
2571 typ: typ
2572 })
2573 start := t.a.children.len
2574 t.a.children << field
2575 t.a.add_node(flat.Node{
2576 kind: .global_decl
2577 children_start: start
2578 children_count: 1
2579 })
2580 t.globals[name] = typ
2581 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2582 t.globals['${t.cur_module}.${name}'] = typ
2583 }
2584}
2585
2586// try_lower_builtin_call checks if a call is to a builtin that needs special lowering.
2587// Returns none for most calls so the caller falls through to generic call transform.
2588fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?flat.NodeId {
2589 if node.children_count == 0 {
2590 return none
2591 }
2592 if cast_call := t.try_lower_primitive_cast_call(node) {
2593 return cast_call
2594 }
2595 if flag_call := t.try_lower_flag_enum_call(node) {
2596 return flag_call
2597 }
2598 if flag_default := t.try_lower_flag_default_value_call(node) {
2599 return flag_default
2600 }
2601 if pool_call := t.try_lower_pool_generic_method_call(node) {
2602 return pool_call
2603 }
2604 if static_call := t.try_lower_static_assoc_call(_id, node) {
2605 return static_call
2606 }
2607 if move_call := t.try_lower_move_method_call(_id, node) {
2608 return move_call
2609 }
2610 if channel_call := t.try_lower_channel_method_call(_id, node) {
2611 return channel_call
2612 }
2613 if map_call := t.try_lower_map_method_call(_id, node) {
2614 return map_call
2615 }
2616 if array_call := t.try_lower_array_method_call(_id, node) {
2617 return array_call
2618 }
2619 if type_name_call := t.try_lower_sum_type_name_method_call(node) {
2620 return type_name_call
2621 }
2622 if receiver_call := t.try_lower_receiver_method_call(_id, node) {
2623 return receiver_call
2624 }
2625 if string_call := t.try_lower_string_method_call(node) {
2626 return string_call
2627 }
2628 fn_id := t.a.children[node.children_start]
2629 if int(fn_id) < 0 {
2630 return none
2631 }
2632 fn_node := t.a.nodes[int(fn_id)]
2633 if fn_node.kind != .ident {
2634 return none
2635 }
2636 name := fn_node.value
2637 if name in ['maxof', 'minof'] && node.value.len > 0 && node.children_count == 1
2638 && t.is_std_minmaxof_call(_id, name) {
2639 if value := t.try_lower_minmaxof_call(name, node.value) {
2640 return value
2641 }
2642 }
2643 match name {
2644 'copy' {
2645 return t.try_lower_copy_call(node)
2646 }
2647 'println', 'eprintln', 'print', 'eprint' {
2648 if node.children_count < 2 {
2649 return t.transform_call_args(_id, node)
2650 }
2651 arg := t.stringify_expr(t.a.child(&node, 1))
2652 return t.make_call(name, arr1(arg))
2653 }
2654 'panic' {
2655 if node.children_count == 2 {
2656 arg_id := t.a.child(&node, 1)
2657 arg_type := t.node_type(arg_id)
2658 if arg_type == 'IError' {
2659 arg := t.transform_expr(arg_id)
2660 return t.make_call('panic',
2661 arr1(t.make_method_call(arg, 'str', []flat.NodeId{})))
2662 }
2663 }
2664 return none
2665 }
2666 'sizeof' {
2667 return none
2668 }
2669 'typeof' {
2670 return none
2671 }
2672 else {
2673 return none
2674 }
2675 }
2676}
2677
2678fn (t &Transformer) is_std_minmaxof_call(id flat.NodeId, name string) bool {
2679 if isnil(t.tc) {
2680 return false
2681 }
2682 resolved := t.tc.resolved_call_name(id) or { return false }
2683 return resolved == 'math.${name}'
2684}
2685
2686fn (mut t Transformer) try_lower_minmaxof_call(name string, raw_type string) ?flat.NodeId {
2687 mut typ := raw_type
2688 if !isnil(t.tc) {
2689 typ = t.normalize_type_in_module(raw_type, t.cur_module)
2690 }
2691 if typ.starts_with('builtin.') {
2692 typ = typ.all_after_last('.')
2693 }
2694 value := if name == 'maxof' {
2695 match typ {
2696 'i8' { '127' }
2697 'i16' { '32767' }
2698 'int', 'i32' { '2147483647' }
2699 'i64' { '9223372036854775807' }
2700 'u8' { '255' }
2701 'u16' { '65535' }
2702 'u32' { '4294967295' }
2703 'u64' { '18446744073709551615' }
2704 'f32' { '3.40282346638528859811704183484516925440e+38' }
2705 'f64' { '1.797693134862315708145274237317043567981e+308' }
2706 else { return none }
2707 }
2708 } else {
2709 match typ {
2710 'i8' { '-128' }
2711 'i16' { '-32768' }
2712 'int', 'i32' { '(-2147483647 - 1)' }
2713 'i64' { '(-9223372036854775807 - 1)' }
2714 'u8', 'u16', 'u32', 'u64' { '0' }
2715 'f32' { '-3.40282346638528859811704183484516925440e+38' }
2716 'f64' { '-1.797693134862315708145274237317043567981e+308' }
2717 else { return none }
2718 }
2719 }
2720 if typ in ['f32', 'f64'] {
2721 return t.make_float_literal_typed(value, typ)
2722 }
2723 return t.make_int_literal_typed(value, typ)
2724}
2725
2726fn (mut t Transformer) try_lower_copy_call(node flat.Node) ?flat.NodeId {
2727 if node.children_count != 3 {
2728 return none
2729 }
2730 dst_arg_id := t.a.child(&node, 1)
2731 src_arg_id := t.a.child(&node, 2)
2732 dst_id := t.copy_mut_arg_value(dst_arg_id)
2733 src := t.transform_expr_for_type(src_arg_id, '[]u8')
2734 if t.copy_destination_is_range(dst_id) {
2735 slice := t.transform_expr(dst_id)
2736 tmp_name := t.new_temp('copy_dst')
2737 t.pending_stmts << t.make_decl_assign_typed(tmp_name, slice, '[]u8')
2738 return t.make_call_typed('copy', arr2(t.make_prefix(.amp, t.make_ident(tmp_name)), src),
2739 'int')
2740 }
2741 dst := t.make_prefix(.amp, t.transform_expr(dst_id))
2742 return t.make_call_typed('copy', arr2(dst, src), 'int')
2743}
2744
2745fn (t &Transformer) copy_mut_arg_value(id flat.NodeId) flat.NodeId {
2746 if int(id) < 0 {
2747 return id
2748 }
2749 node := t.a.nodes[int(id)]
2750 if node.kind == .paren && node.children_count > 0 {
2751 return t.copy_mut_arg_value(t.a.child(&node, 0))
2752 }
2753 if node.kind == .prefix && node.op == .amp && node.children_count > 0 {
2754 return t.copy_mut_arg_value(t.a.child(&node, 0))
2755 }
2756 return id
2757}
2758
2759fn (t &Transformer) copy_destination_is_range(id flat.NodeId) bool {
2760 if int(id) < 0 {
2761 return false
2762 }
2763 node := t.a.nodes[int(id)]
2764 if node.kind != .index {
2765 return false
2766 }
2767 if node.value == 'range' {
2768 return true
2769 }
2770 if node.children_count > 1 {
2771 index := t.a.child_node(&node, 1)
2772 return index.kind == .range
2773 }
2774 return false
2775}
2776
2777const primitive_cast_type_names = ['bool', 'int', 'i8', 'i16', 'i32', 'i64', 'isize', 'u8', 'byte',
2778 'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'rune', 'char']
2779
2780// try_lower_primitive_cast_call supports try lower primitive cast call handling for Transformer.
2781fn (mut t Transformer) try_lower_primitive_cast_call(node flat.Node) ?flat.NodeId {
2782 if node.children_count != 2 {
2783 return none
2784 }
2785 fn_id := t.a.child(&node, 0)
2786 if int(fn_id) < 0 {
2787 return none
2788 }
2789 fn_node := t.a.nodes[int(fn_id)]
2790 if fn_node.kind != .ident || fn_node.value !in primitive_cast_type_names {
2791 return none
2792 }
2793 arg_id := t.a.child(&node, 1)
2794 return t.make_cast(fn_node.value, t.transform_expr(arg_id), fn_node.value)
2795}
2796
2797// try_lower_flag_default_value_call
2798// supports helper handling in transform.
2799fn (mut t Transformer) try_lower_flag_default_value_call(node flat.Node) ?flat.NodeId {
2800 if node.children_count != 2 {
2801 return none
2802 }
2803 fn_id := t.a.child(&node, 0)
2804 fn_node := t.a.nodes[int(fn_id)]
2805 mut name := ''
2806 if fn_node.kind == .ident {
2807 name = fn_node.value
2808 } else if fn_node.kind == .selector {
2809 name = fn_node.value
2810 }
2811 if name != 'flag_default_value' {
2812 return none
2813 }
2814 arg_id := t.a.child(&node, 1)
2815 arg := t.transform_expr(arg_id)
2816 mut arg_type := t.resolve_expr_type(arg_id)
2817 if arg_type.len == 0 {
2818 arg_type = t.reliable_stringify_type(arg_id)
2819 }
2820 if arg_type.len == 0 {
2821 arg_type = t.node_type(arg)
2822 }
2823 if t.normalize_type_alias(arg_type) == 'string' {
2824 escaped := t.make_call_typed('escape_default_string', arr1(arg), 'string')
2825 return t.string_plus(t.string_plus(t.make_string_literal('"'), escaped),
2826 t.make_string_literal('"'))
2827 }
2828 return t.wrap_string_conversion(arg, arg_type)
2829}
2830
2831// try_lower_sum_type_name_method_call supports try_lower_sum_type_name_method_call handling.
2832fn (mut t Transformer) try_lower_sum_type_name_method_call(node flat.Node) ?flat.NodeId {
2833 if node.children_count != 1 {
2834 return none
2835 }
2836 fn_id := t.a.child(&node, 0)
2837 fn_node := t.a.nodes[int(fn_id)]
2838 if fn_node.kind != .selector || fn_node.value != 'type_name' || fn_node.children_count == 0 {
2839 return none
2840 }
2841 base_id := t.a.child(&fn_node, 0)
2842 mut base_type := t.node_type(base_id)
2843 if base_type.len == 0 {
2844 base_type = t.lvalue_type(base_id)
2845 }
2846 clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2847 resolved_sum := t.resolve_sum_name(clean_type)
2848 variants := t.sum_types[resolved_sum] or { return none }
2849 base := t.stable_transformed_expr_for_reuse(t.transform_expr(base_id), base_type, 'sum_type')
2850 tag := t.make_selector_op(base, 'typ', 'int', if base_type.starts_with('&') {
2851 .arrow
2852 } else {
2853 .dot
2854 })
2855 return t.build_sum_type_name_chain(tag, resolved_sum, variants, 0)
2856}
2857
2858// build_sum_type_name_chain builds sum type name chain data for transform.
2859fn (mut t Transformer) build_sum_type_name_chain(tag flat.NodeId, sum_name string, variants []string, idx int) flat.NodeId {
2860 if idx >= variants.len {
2861 return t.make_string_literal('')
2862 }
2863 variant := variants[idx]
2864 display := if variant.contains('.') { variant.all_after_last('.') } else { variant }
2865 cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(sum_name, variant)))
2866 then_block := t.make_block(arr1(t.make_expr_stmt(t.make_string_literal(display))))
2867 else_expr := t.build_sum_type_name_chain(tag, sum_name, variants, idx + 1)
2868 else_block := t.make_block(arr1(t.make_expr_stmt(else_expr)))
2869 start := t.a.children.len
2870 t.a.children << cond
2871 t.a.children << then_block
2872 t.a.children << else_block
2873 return t.a.add_node(flat.Node{
2874 kind: .if_expr
2875 children_start: start
2876 children_count: 3
2877 typ: 'string'
2878 })
2879}
2880
2881// try_lower_pool_generic_method_call
2882// supports helper handling in transform.
2883fn (mut t Transformer) try_lower_pool_generic_method_call(node flat.Node) ?flat.NodeId {
2884 if node.children_count == 0 || node.value.len == 0 {
2885 return none
2886 }
2887 fn_id := t.a.child(&node, 0)
2888 fn_node := t.a.nodes[int(fn_id)]
2889 if fn_node.kind != .selector || fn_node.children_count == 0 {
2890 return none
2891 }
2892 method := fn_node.value
2893 if method !in ['get_item', 'get_results', 'get_results_ref'] {
2894 return none
2895 }
2896 base_id := t.a.child(&fn_node, 0)
2897 base_type := t.lvalue_type(base_id)
2898 if !is_pool_processor_type(base_type) {
2899 return none
2900 }
2901 elem_type := node.value
2902 base := t.stable_expr_for_reuse(base_id)
2903 mut prefix := []flat.NodeId{}
2904 t.drain_pending(mut prefix)
2905 if method == 'get_item' {
2906 if node.children_count < 2 {
2907 return none
2908 }
2909 idx := t.transform_expr(t.a.child(&node, 1))
2910 t.drain_pending(mut prefix)
2911 items := t.pool_processor_field(base, base_type, 'items', '[]voidptr')
2912 item := t.make_index(items, idx, 'voidptr')
2913 cast := t.make_cast('&${elem_type}', item, '&${elem_type}')
2914 value := t.make_prefix(.mul, cast)
2915 t.a.nodes[int(value)].typ = elem_type
2916 for stmt in prefix {
2917 t.pending_stmts << stmt
2918 }
2919 return value
2920 }
2921 result_name := t.new_temp('pool_results')
2922 idx_name := t.new_temp('pool_idx')
2923 results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2924 results_len := t.make_selector(results, 'len', 'int')
2925 is_ref_results := method == 'get_results_ref'
2926 out_elem_type := if is_ref_results { '&${elem_type}' } else { elem_type }
2927 out_type := '[]${out_elem_type}'
2928 prefix << t.make_decl_assign_typed(result_name, t.make_array_new_call(out_elem_type,
2929 t.make_int_literal(0), results_len), out_type)
2930 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
2931 cond_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2932 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(cond_results, 'len', 'int'))
2933 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
2934 body_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2935 item := t.make_index(body_results, t.make_ident(idx_name), 'voidptr')
2936 value := if is_ref_results {
2937 t.make_cast('&${elem_type}', item, '&${elem_type}')
2938 } else {
2939 t.make_prefix(.mul, t.make_cast('&${elem_type}', item, '&${elem_type}'))
2940 }
2941 t.a.nodes[int(value)].typ = out_elem_type
2942 value_name := t.new_temp('pool_result')
2943 value_decl := t.make_decl_assign_typed(value_name, value, out_elem_type)
2944 push_call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(result_name)), t.make_prefix(.amp,
2945 t.make_ident(value_name))), 'void')
2946 loop_body := [value_decl, t.make_expr_stmt(push_call)]
2947 prefix << t.make_for_stmt(init, cond, post, loop_body, node)
2948 for stmt in prefix {
2949 t.pending_stmts << stmt
2950 }
2951 return t.make_ident(result_name)
2952}
2953
2954// is_pool_processor_type reports whether is pool processor type applies in transform.
2955fn is_pool_processor_type(typ string) bool {
2956 mut clean := typ
2957 if clean.starts_with('&') {
2958 clean = clean[1..]
2959 }
2960 if clean.starts_with('mut ') {
2961 clean = clean[4..]
2962 }
2963 return clean == 'PoolProcessor' || clean.ends_with('.PoolProcessor')
2964}
2965
2966// pool_processor_field supports pool processor field handling for Transformer.
2967fn (mut t Transformer) pool_processor_field(base flat.NodeId, base_type string, field string, typ string) flat.NodeId {
2968 op := if base_type.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
2969 return t.make_selector_op(base, field, typ, op)
2970}
2971
2972// try_lower_receiver_method_call supports try lower receiver method call handling for Transformer.
2973fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
2974 if node.children_count == 0 {
2975 return none
2976 }
2977 fn_id := t.a.children[node.children_start]
2978 fn_node := t.a.nodes[int(fn_id)]
2979 if fn_node.kind != .selector || fn_node.children_count == 0 {
2980 return none
2981 }
2982 method := fn_node.value
2983 base_id := t.a.child(&fn_node, 0)
2984 base_node := t.a.nodes[int(base_id)]
2985 if t.is_import_alias_ident(base_id) {
2986 return none
2987 }
2988 // `Type.fn(...)` / `module.Type.fn(...)` is a static associated function, not a method:
2989 // the base names a type, so it must not be lowered into `fn(receiver, ...)`.
2990 if _ := t.static_assoc_fn_name(base_id, method) {
2991 return none
2992 }
2993 mut base_type := if base_node.kind in [.selector, .index] {
2994 t.lvalue_type(base_id)
2995 } else {
2996 t.node_type(base_id)
2997 }
2998 if base_type.len == 0 {
2999 base_type = t.lvalue_type(base_id)
3000 }
3001 base_is_pointer := base_type.starts_with('&')
3002 if base_type.starts_with('&') {
3003 base_type = base_type[1..]
3004 }
3005 if base_type.len == 0 {
3006 return none
3007 }
3008 mut builtin_base_type := t.normalize_type_alias(base_type)
3009 if builtin_base_type == 'byte' {
3010 builtin_base_type = 'u8'
3011 }
3012 if base_type == '[]rune' && method == 'string' {
3013 return t.make_call_typed('Array_rune__string', arr1(t.transform_expr(base_id)), 'string')
3014 }
3015 if method == 'str' && t.is_array_transform_call(base_id) {
3016 base := t.transform_expr(base_id)
3017 transformed_type := t.node_type(base)
3018 if transformed_type.starts_with('[]') {
3019 return t.wrap_string_conversion(base, transformed_type)
3020 }
3021 }
3022 if method == 'str' && t.is_builder_receiver(base_id, base_type) {
3023 method_name := 'strings.Builder.str'
3024 args := t.transform_receiver_method_args(node, base_id, method_name)
3025 return t.make_call_typed(method_name, args, 'string')
3026 }
3027 if base_type.starts_with('[]') && method == 'str' {
3028 return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
3029 }
3030 if method == 'str' {
3031 if t.is_enum_stringify_type(base_type) {
3032 return none
3033 }
3034 return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
3035 }
3036 if builtin_base_type == 'string' && method == 'hex' && !base_is_pointer {
3037 return t.make_call_typed('string.hex', arr1(t.transform_expr(base_id)), 'string')
3038 }
3039 if base_type == '[]u8' || base_type == '[]byte' {
3040 if method == 'bytestr' {
3041 return t.make_call_typed('Array_u8__bytestr', arr1(t.transform_expr(base_id)), 'string')
3042 }
3043 if method == 'hex' && !base_is_pointer {
3044 return t.make_call_typed('Array_u8__hex', arr1(t.transform_expr(base_id)), 'string')
3045 }
3046 }
3047 if t.is_builder_receiver(base_id, base_type) {
3048 for method_name in ['strings.Builder.${method}', 'Builder.${method}'] {
3049 if t.is_known_fn_name(method_name) {
3050 args := t.transform_receiver_method_args(node, base_id, method_name)
3051 ret_type := t.receiver_method_return_type(method_name, node.typ)
3052 return t.make_call_typed(method_name, args, ret_type)
3053 }
3054 }
3055 }
3056 if builtin_base_type == 'u8' && method in ['is_space', 'is_digit', 'is_hex_digit', 'is_letter'] {
3057 return t.make_call_typed('u8__${method}', arr1(t.transform_expr(base_id)), 'bool')
3058 }
3059 if !base_is_pointer
3060 && builtin_base_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'int', 'u64', 'i64', 'rune']
3061 && method in ['hex', 'hex_full'] {
3062 return t.make_call_typed('${builtin_base_type}__${method}',
3063 arr1(t.transform_expr(base_id)), 'string')
3064 }
3065 if base_type.starts_with('[]') || base_type.starts_with('map[') {
3066 if base_type.starts_with('[]') {
3067 return none
3068 }
3069 }
3070 method_name := t.resolve_receiver_method_name(base_id, method)
3071 if method_name.len > 0 {
3072 args := t.transform_receiver_method_args(node, base_id, method_name)
3073 ret_type := t.receiver_method_return_type(method_name, node.typ)
3074 return t.make_call_typed(method_name, args, ret_type)
3075 }
3076 if !isnil(t.tc) {
3077 if resolved_method := t.tc.resolved_call_name(id) {
3078 if t.is_known_fn_name(resolved_method) {
3079 params := t.call_param_types(resolved_method)
3080 if !t.resolved_call_uses_receiver_type(base_id, base_type, params) {
3081 return none
3082 }
3083 args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
3084 resolved_method), resolved_method)
3085 ret_type := t.receiver_method_return_type(resolved_method, node.typ)
3086 return t.make_call_typed(resolved_method, args, ret_type)
3087 }
3088 }
3089 }
3090 if sum_method := t.resolve_smartcast_sum_receiver_method(base_id, method) {
3091 args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
3092 sum_method), sum_method)
3093 ret_type := t.receiver_method_return_type(sum_method, node.typ)
3094 return t.make_call_typed(sum_method, args, ret_type)
3095 }
3096 return none
3097}
3098
3099// is_builder_receiver reports whether is builder receiver applies in transform.
3100fn (t &Transformer) is_builder_receiver(base_id flat.NodeId, base_type string) bool {
3101 if is_builder_type_name(base_type) {
3102 return true
3103 }
3104 if raw_type := t.raw_var_type_for_expr(base_id) {
3105 return is_builder_type_name(raw_type)
3106 }
3107 if raw_field_type := t.raw_selector_field_type(base_id) {
3108 return is_builder_type_name(raw_field_type)
3109 }
3110 return false
3111}
3112
3113// raw_selector_field_type supports raw selector field type handling for Transformer.
3114fn (t &Transformer) raw_selector_field_type(id flat.NodeId) ?string {
3115 if int(id) < 0 {
3116 return none
3117 }
3118 node := t.a.nodes[int(id)]
3119 if node.kind != .selector || node.children_count == 0 {
3120 return none
3121 }
3122 base_id := t.a.child(&node, 0)
3123 mut base_type := t.original_expr_type(base_id)
3124 if base_type.len == 0 {
3125 base_type = t.node_type(base_id)
3126 }
3127 if base_type.starts_with('&') {
3128 base_type = base_type[1..]
3129 }
3130 return t.lookup_struct_field_raw_type(base_type, node.value)
3131}
3132
3133// is_builder_type_name reports whether is builder type name applies in transform.
3134fn is_builder_type_name(typ string) bool {
3135 mut clean := typ
3136 if clean.starts_with('&') {
3137 clean = clean[1..]
3138 }
3139 return clean == 'strings.Builder' || clean == 'Builder'
3140}
3141
3142// resolved_call_uses_receiver_type
3143// supports helper handling in transform.
3144fn (t &Transformer) resolved_call_uses_receiver_type(base_id flat.NodeId, receiver_type string, params []types.Type) bool {
3145 if params.len == 0 {
3146 return false
3147 }
3148 mut param_type := params[0].name()
3149 if param_type.starts_with('&') {
3150 param_type = param_type[1..]
3151 }
3152 if sc := t.find_smartcast(t.expr_key(base_id)) {
3153 sc_type := t.trim_pointer_type(t.smartcast_target_type(sc))
3154 if sc_type.len > 0 && t.normalize_type_alias(sc_type) == t.normalize_type_alias(param_type) {
3155 return true
3156 }
3157 sc_sum := t.trim_pointer_type(t.resolve_sum_name(sc.sum_type_name))
3158 if sc_sum.len > 0 && t.normalize_type_alias(sc_sum) == t.normalize_type_alias(param_type) {
3159 return true
3160 }
3161 if t.is_sum_type_name(param_type) {
3162 for parent in t.sum_type_parents_for_variant(sc.variant_name) {
3163 if t.normalize_type_alias(parent) == t.normalize_type_alias(param_type) {
3164 return true
3165 }
3166 }
3167 }
3168 }
3169 mut base_type := receiver_type
3170 if base_type.starts_with('&') {
3171 base_type = base_type[1..]
3172 }
3173 if base_type.len == 0 {
3174 return true
3175 }
3176 if t.normalize_type_alias(base_type) == t.normalize_type_alias(param_type) {
3177 return true
3178 }
3179 if _ := t.embedded_receiver_path(base_type, param_type) {
3180 return true
3181 }
3182 return false
3183}
3184
3185// receiver_base_for_resolved_method
3186// supports helper handling in transform.
3187fn (mut t Transformer) receiver_base_for_resolved_method(base_id flat.NodeId, method_name string) flat.NodeId {
3188 if embedded_base := t.embedded_receiver_base(base_id, method_name) {
3189 return embedded_base
3190 }
3191 key := t.expr_key(base_id)
3192 sc := t.find_smartcast(key) or { return t.transform_expr(base_id) }
3193 params := t.call_param_types(method_name)
3194 if params.len == 0 {
3195 return t.transform_expr(base_id)
3196 }
3197 mut param_type := params[0].name()
3198 if param_type.starts_with('&') {
3199 param_type = param_type[1..]
3200 }
3201 target_type := t.trim_pointer_type(t.smartcast_target_type(sc))
3202 if target_type.len > 0
3203 && t.normalize_type_alias(param_type) == t.normalize_type_alias(target_type) {
3204 return t.apply_smartcast_contexts(t.make_plain_expr_for_smartcast(base_id),
3205 t.original_expr_type(base_id), t.smartcasts_for(key))
3206 }
3207 method_receiver := method_name.all_before_last('.')
3208 if method_receiver.len > 0 && t.is_sum_type_name(method_receiver)
3209 && t.normalize_type_alias(param_type) == t.normalize_type_alias(method_receiver) {
3210 return t.make_plain_expr_for_smartcast(base_id)
3211 }
3212 mut sum_type := t.resolve_sum_name(sc.sum_type_name)
3213 if sum_type.starts_with('&') {
3214 sum_type = sum_type[1..]
3215 }
3216 if sum_type.len > 0 && t.normalize_type_alias(param_type) == t.normalize_type_alias(sum_type) {
3217 return t.make_plain_expr_for_smartcast(base_id)
3218 }
3219 original_type := t.trim_pointer_type(t.original_expr_type(base_id))
3220 if original_type.len > 0
3221 && t.normalize_type_alias(param_type) == t.normalize_type_alias(original_type) {
3222 return t.make_plain_expr_for_smartcast(base_id)
3223 }
3224 return t.transform_expr(base_id)
3225}
3226
3227fn (mut t Transformer) embedded_receiver_base(base_id flat.NodeId, method_name string) ?flat.NodeId {
3228 params := t.call_param_types(method_name)
3229 if params.len == 0 {
3230 return none
3231 }
3232 mut param_type := params[0].name()
3233 if param_type.starts_with('&') {
3234 param_type = param_type[1..]
3235 }
3236 mut base_type := t.node_type(base_id)
3237 if base_type.len == 0 {
3238 base_type = t.lvalue_type(base_id)
3239 }
3240 mut is_ptr := false
3241 if base_type.starts_with('&') {
3242 is_ptr = true
3243 base_type = base_type[1..]
3244 }
3245 path := t.embedded_receiver_path(base_type, param_type) or { return none }
3246 mut base := t.transform_expr(base_id)
3247 mut current_is_ptr := is_ptr
3248 for field in path {
3249 op := if current_is_ptr { flat.Op.arrow } else { flat.Op.dot }
3250 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
3251 base = t.make_selector_op(base, field.name, field_type, op)
3252 current_is_ptr = field_type.starts_with('&')
3253 }
3254 return base
3255}
3256
3257fn (t &Transformer) embedded_receiver_field(base_type string, receiver_type string) ?FieldInfo {
3258 path := t.embedded_receiver_path(base_type, receiver_type) or { return none }
3259 if path.len == 0 {
3260 return none
3261 }
3262 return path[0]
3263}
3264
3265fn (t &Transformer) embedded_receiver_path(base_type string, receiver_type string) ?[]FieldInfo {
3266 if base_type.len == 0 || receiver_type.len == 0 {
3267 return none
3268 }
3269 mut lookup_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
3270 if lookup_type !in t.structs && lookup_type.contains('.') {
3271 short_type := lookup_type.all_after_last('.')
3272 if short_type in t.structs {
3273 lookup_type = short_type
3274 }
3275 }
3276 info := t.structs[lookup_type] or { return none }
3277 clean_receiver := t.normalize_type_alias(receiver_type)
3278 for field in info.fields {
3279 if !t.is_embedded_field(field) {
3280 continue
3281 }
3282 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
3283 clean_field := t.normalize_type_alias(field_type.trim_left('&'))
3284 short_field := if clean_field.contains('.') {
3285 clean_field.all_after_last('.')
3286 } else {
3287 clean_field
3288 }
3289 if (field.name == clean_field || field.name == short_field) && clean_field == clean_receiver {
3290 return [field]
3291 }
3292 if sub_path := t.embedded_receiver_path(clean_field, receiver_type) {
3293 mut path := []FieldInfo{}
3294 path << field
3295 path << sub_path
3296 return path
3297 }
3298 }
3299 return none
3300}
3301
3302// receiver_method_return_type supports receiver method return type handling for Transformer.
3303fn (t &Transformer) receiver_method_return_type(method_name string, fallback string) string {
3304 if !isnil(t.tc) {
3305 if typ := t.tc.fn_ret_types[method_name] {
3306 return t.normalize_type_alias(typ.name())
3307 }
3308 }
3309 if ret := t.fn_ret_types[method_name] {
3310 return ret
3311 }
3312 return fallback
3313}
3314
3315fn (t &Transformer) call_resolved_to_method(call_id flat.NodeId, method_name string) bool {
3316 if isnil(t.tc) {
3317 return true
3318 }
3319 if resolved := t.tc.resolved_call_name(call_id) {
3320 return resolved == method_name
3321 }
3322 return false
3323}
3324
3325fn (mut t Transformer) lower_checker_selected_receiver_method(call_id flat.NodeId, node flat.Node, base_id flat.NodeId, builtin_name string) ?flat.NodeId {
3326 if isnil(t.tc) {
3327 return none
3328 }
3329 resolved := t.tc.resolved_call_name(call_id) or { return none }
3330 if resolved == builtin_name || is_builtin_collection_resolved_call(resolved)
3331 || !t.is_known_fn_name(resolved) {
3332 return none
3333 }
3334 args := t.transform_receiver_method_args(node, base_id, resolved)
3335 ret_type := t.receiver_method_return_type(resolved, node.typ)
3336 t.mark_fn_used(resolved)
3337 return t.make_call_typed(resolved, args, ret_type)
3338}
3339
3340fn is_builtin_collection_resolved_call(name string) bool {
3341 return name.len == 0 || is_raw_collection_method_name(name, 'array.') || name == 'array_clone'
3342 || is_runtime_collection_helper_name(name) || is_raw_collection_method_name(name, 'map.')
3343}
3344
3345fn is_raw_collection_method_name(name string, prefix string) bool {
3346 if !name.starts_with(prefix) {
3347 return false
3348 }
3349 rest := name[prefix.len..]
3350 return rest.len > 0 && !rest.contains('.')
3351}
3352
3353fn is_runtime_collection_helper_name(name string) bool {
3354 return name in ['array__clone', 'array__reverse', 'array__prepend', 'array__insert',
3355 'array__push_many', 'array__needs_unique_shift', 'map__delete', 'map__move', 'map__reserve',
3356 'map__keys', 'map__values', 'map__clear', 'map__free', 'map__get', 'map__get_check',
3357 'map__exists', 'map__set']
3358}
3359
3360// resolve_smartcast_sum_receiver_method supports resolve_smartcast_sum_receiver_method handling.
3361fn (t &Transformer) resolve_smartcast_sum_receiver_method(base_id flat.NodeId, method string) ?string {
3362 key := t.expr_key(base_id)
3363 sc := t.find_smartcast(key) or { return none }
3364 variant := t.resolve_variant(sc.sum_type_name, sc.variant_name)
3365 mut receiver_types := []string{}
3366 receiver_types << variant
3367 original_type := t.trim_pointer_type(t.original_expr_type(base_id))
3368 if original_type.len > 0 && original_type !in receiver_types {
3369 receiver_types << original_type
3370 }
3371 if sc.sum_type_name.len > 0 && sc.sum_type_name !in receiver_types {
3372 receiver_types << sc.sum_type_name
3373 }
3374 sum_type := t.resolve_sum_name(sc.sum_type_name)
3375 if sum_type.len > 0 && sum_type !in receiver_types {
3376 receiver_types << sum_type
3377 }
3378 for parent in t.sum_type_parents_for_variant(sc.variant_name) {
3379 if parent !in receiver_types {
3380 receiver_types << parent
3381 }
3382 }
3383 for receiver_type in receiver_types {
3384 for candidate in t.receiver_method_candidates(receiver_type, method) {
3385 if t.is_known_fn_name(candidate) {
3386 return candidate
3387 }
3388 }
3389 }
3390 return none
3391}
3392
3393// sum_type_parents_for_variant supports sum type parents for variant handling for Transformer.
3394fn (t &Transformer) sum_type_parents_for_variant(variant string) []string {
3395 if parents := t.sum_variant_parents[variant] {
3396 return parents.clone()
3397 }
3398 short := t.variant_short_name(variant)
3399 if short != variant {
3400 if parents := t.sum_variant_parents[short] {
3401 return parents.clone()
3402 }
3403 }
3404 mut result := []string{}
3405 for sum_name, variants in t.sum_types {
3406 for v in variants {
3407 short_v := t.variant_short_name(v)
3408 if v == variant || short_v == short {
3409 result << sum_name
3410 break
3411 }
3412 }
3413 }
3414 if !isnil(t.tc) {
3415 for sum_name, variants in t.tc.sum_types {
3416 if sum_name in result {
3417 continue
3418 }
3419 for v in variants {
3420 short_v := t.variant_short_name(v)
3421 if v == variant || short_v == short {
3422 result << sum_name
3423 break
3424 }
3425 }
3426 }
3427 }
3428 return result
3429}
3430
3431// receiver_method_candidates supports receiver method candidates handling for Transformer.
3432fn (t &Transformer) receiver_method_candidates(receiver_type string, method string) []string {
3433 mut clean_type := receiver_type
3434 if clean_type.starts_with('&') {
3435 clean_type = clean_type[1..]
3436 }
3437 if clean_type.starts_with('map[') {
3438 return t.map_receiver_method_candidates(clean_type, method)
3439 }
3440 mut candidates := []string{}
3441 candidates << '${clean_type}.${method}'
3442 if clean_type.starts_with('[]') {
3443 elem_type := clean_type[2..]
3444 short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
3445 candidates << '[]${short_elem}.${method}'
3446 if elem_type.contains('.') {
3447 candidates << '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
3448 } else if transform_can_prefix_collection_receiver(t.cur_module) {
3449 candidates << '${t.cur_module}.[]${short_elem}.${method}'
3450 }
3451 } else if clean_type.contains('.') {
3452 short_type := clean_type.all_after_last('.')
3453 candidates << '${short_type}.${method}'
3454 } else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
3455 candidates << '${t.cur_module}.${clean_type}.${method}'
3456 }
3457 return candidates
3458}
3459
3460fn (t &Transformer) resolve_fixed_array_dynamic_receiver_method(fixed_type string, method string) ?string {
3461 elem_type := fixed_array_outer_elem_type(fixed_type)
3462 if elem_type.len == 0 {
3463 return none
3464 }
3465 return t.resolve_receiver_method_for_type('[]${elem_type}', method)
3466}
3467
3468fn (mut t Transformer) lower_fixed_array_dynamic_receiver_method_call(node flat.Node, base_id flat.NodeId, fixed_type string, method_name string) flat.NodeId {
3469 elem_type := fixed_array_outer_elem_type(fixed_type)
3470 array_type := '[]${elem_type}'
3471 tmp_name := t.new_temp('fixed_arr')
3472 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.fixed_array_value_to_array(base_id,
3473 fixed_type, array_type), array_type)
3474 args := t.transform_receiver_method_args_with_base(node, t.make_ident(tmp_name), method_name)
3475 ret_type := t.receiver_method_return_type(method_name, node.typ)
3476 t.mark_fn_used(method_name)
3477 return t.make_call_typed(method_name, args, ret_type)
3478}
3479
3480fn fixed_array_outer_elem_type(type_text string) string {
3481 clean := type_text.trim_space()
3482 if clean.len == 0 {
3483 return ''
3484 }
3485 if clean.starts_with('[') {
3486 bracket_end := generic_matching_bracket(clean, 0)
3487 if bracket_end < clean.len {
3488 return clean[bracket_end + 1..]
3489 }
3490 return ''
3491 }
3492 elem, dims := transform_postfix_fixed_array_parts(clean)
3493 if elem.len == 0 || dims.len == 0 {
3494 return fixed_array_elem_type(clean)
3495 }
3496 mut out := elem
3497 for i := dims.len - 1; i > 0; i-- {
3498 out += '[${dims[i]}]'
3499 }
3500 return out
3501}
3502
3503fn transform_push_receiver_candidate(mut candidates []string, candidate string) {
3504 if candidate.len > 0 && candidate !in candidates {
3505 candidates << candidate
3506 }
3507}
3508
3509fn transform_can_prefix_collection_receiver(module_name string) bool {
3510 return module_name.len > 0 && module_name != 'main' && module_name != 'builtin'
3511}
3512
3513fn (t &Transformer) receiver_type_text_variants(type_text string) []string {
3514 clean := type_text.trim_space()
3515 mut names := []string{}
3516 transform_push_receiver_candidate(mut names, clean)
3517 transform_push_receiver_candidate(mut names, receiver_type_text_short_spelling(clean))
3518 if t.is_fixed_array_type(clean) {
3519 source := t.receiver_type_text_source_fixed_spelling(clean)
3520 transform_push_receiver_candidate(mut names, source)
3521 transform_push_receiver_candidate(mut names, receiver_type_text_short_spelling(source))
3522 }
3523 return names
3524}
3525
3526fn (t &Transformer) receiver_type_text_source_fixed_spelling(type_text string) string {
3527 clean := type_text.trim_space()
3528 if clean.len == 0 || clean.starts_with('[') || !t.is_fixed_array_type(clean) {
3529 return clean
3530 }
3531 elem, dims := transform_postfix_fixed_array_parts(clean)
3532 if elem.len == 0 || dims.len == 0 {
3533 return clean
3534 }
3535 mut source := elem
3536 for i := dims.len; i > 0; i-- {
3537 source = '[${dims[i - 1]}]${source}'
3538 }
3539 return source
3540}
3541
3542fn transform_postfix_fixed_array_parts(type_text string) (string, []string) {
3543 clean := type_text.trim_space()
3544 mut end := clean.len
3545 mut dims := []string{}
3546 for end > 0 && clean[end - 1] == `]` {
3547 start := transform_trailing_matching_bracket_start(clean, end)
3548 if start < 0 {
3549 break
3550 }
3551 dims << clean[start + 1..end - 1].trim_space()
3552 end = start
3553 }
3554 return clean[..end], dims
3555}
3556
3557fn transform_trailing_matching_bracket_start(s string, end int) int {
3558 mut depth := 0
3559 for i := end - 1; i >= 0; i-- {
3560 if s[i] == `]` {
3561 depth++
3562 } else if s[i] == `[` {
3563 depth--
3564 if depth == 0 {
3565 return i
3566 }
3567 }
3568 }
3569 return -1
3570}
3571
3572fn receiver_type_text_short_spelling(type_text string) string {
3573 clean := type_text.trim_space()
3574 if clean.starts_with('[]') {
3575 return '[]' + receiver_type_text_short_spelling(clean[2..])
3576 }
3577 if clean.starts_with('[') {
3578 bracket_end := generic_matching_bracket(clean, 0)
3579 if bracket_end < clean.len {
3580 return clean[..bracket_end + 1] + receiver_type_text_short_spelling(clean[bracket_end +
3581 1..])
3582 }
3583 }
3584 if clean.starts_with('map[') {
3585 bracket_end := generic_matching_bracket(clean, 3)
3586 if bracket_end < clean.len {
3587 key := receiver_type_text_short_spelling(clean[4..bracket_end])
3588 value := receiver_type_text_short_spelling(clean[bracket_end + 1..])
3589 return 'map[${key}]${value}'
3590 }
3591 }
3592 if clean.contains('.') {
3593 return clean.all_after_last('.')
3594 }
3595 return clean
3596}
3597
3598fn (t &Transformer) receiver_type_text_module_names(type_text string) []string {
3599 clean := type_text.trim_space()
3600 mut names := []string{}
3601 if clean.starts_with('[]') {
3602 return t.receiver_type_text_module_names(clean[2..])
3603 }
3604 if clean.starts_with('[') {
3605 bracket_end := generic_matching_bracket(clean, 0)
3606 if bracket_end < clean.len {
3607 return t.receiver_type_text_module_names(clean[bracket_end + 1..])
3608 }
3609 }
3610 if clean.starts_with('map[') {
3611 key_type := t.map_key_type(clean)
3612 value_type := t.map_value_type(clean)
3613 for name in t.receiver_type_text_module_names(key_type) {
3614 transform_push_receiver_candidate(mut names, name)
3615 }
3616 for name in t.receiver_type_text_module_names(value_type) {
3617 transform_push_receiver_candidate(mut names, name)
3618 }
3619 return names
3620 }
3621 if t.is_fixed_array_type(clean) {
3622 return t.receiver_type_text_module_names(fixed_array_elem_type(clean))
3623 }
3624 if clean.contains('.') {
3625 transform_push_receiver_candidate(mut names, clean.all_before_last('.'))
3626 }
3627 return names
3628}
3629
3630// map_receiver_method_candidates supports map receiver method candidates handling for Transformer.
3631fn (t &Transformer) map_receiver_method_candidates(receiver_type string, method string) []string {
3632 clean_type := t.clean_map_type(receiver_type)
3633 key_type := t.map_key_type(clean_type)
3634 value_type := t.map_value_type(clean_type)
3635 mut candidates := []string{}
3636 if key_type.len == 0 || value_type.len == 0 {
3637 transform_push_receiver_candidate(mut candidates, '${clean_type}.${method}')
3638 return candidates
3639 }
3640 key_types := t.receiver_type_text_variants(key_type)
3641 value_types := t.receiver_type_text_variants(value_type)
3642 mut map_types := []string{}
3643 for key in key_types {
3644 for value in value_types {
3645 transform_push_receiver_candidate(mut map_types, 'map[${key}]${value}')
3646 }
3647 }
3648 for map_type in map_types {
3649 transform_push_receiver_candidate(mut candidates, '${map_type}.${method}')
3650 }
3651 mut module_names := []string{}
3652 if transform_can_prefix_collection_receiver(t.cur_module) {
3653 transform_push_receiver_candidate(mut module_names, t.cur_module)
3654 }
3655 for mod_name in t.receiver_type_text_module_names(key_type) {
3656 transform_push_receiver_candidate(mut module_names, mod_name)
3657 }
3658 for mod_name in t.receiver_type_text_module_names(value_type) {
3659 transform_push_receiver_candidate(mut module_names, mod_name)
3660 }
3661 for mod_name in module_names {
3662 for map_type in map_types {
3663 transform_push_receiver_candidate(mut candidates, '${mod_name}.${map_type}.${method}')
3664 }
3665 }
3666 return candidates
3667}
3668
3669// transform_receiver_method_args transforms transform receiver method args data for transform.
3670fn (mut t Transformer) transform_receiver_method_args(node flat.Node, base_id flat.NodeId, method_name string) []flat.NodeId {
3671 return t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
3672 method_name), method_name)
3673}
3674
3675// transform_receiver_method_args_with_base
3676// transforms helper data for transform.
3677fn (mut t Transformer) transform_receiver_method_args_with_base(node flat.Node, base flat.NodeId, method_name string) []flat.NodeId {
3678 mut args := []flat.NodeId{cap: int(node.children_count)}
3679 args << base
3680 params := t.call_param_types(method_name)
3681 param_offset := t.receiver_method_param_offset(base, node, params)
3682 explicit_args := int(node.children_count) - 1
3683 expected_explicit := params.len - param_offset
3684 is_variadic := t.call_is_variadic(method_name) || (params.len > 0
3685 && params[params.len - 1] is types.Array && explicit_args > expected_explicit)
3686 variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
3687 params.len - 1
3688 } else {
3689 -1
3690 }
3691 mut i := 1
3692 for i < node.children_count {
3693 param_idx := (args.len - 1) + param_offset
3694 arg_id := t.a.child(&node, i)
3695 arg_node := t.a.nodes[int(arg_id)]
3696 param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
3697 if arg_node.kind == .field_init {
3698 if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
3699 args << packed_arg
3700 i = t.next_non_field_init_arg(node, i)
3701 continue
3702 }
3703 }
3704 if variadic_idx >= 0 && param_idx == variadic_idx {
3705 variadic_type := params[variadic_idx]
3706 if variadic_type is types.Array {
3707 if arg_node.kind == .prefix && arg_node.value == '...'
3708 && arg_node.children_count > 0 {
3709 spread_id := t.a.child(&arg_node, 0)
3710 args << t.transform_call_arg_for_param(spread_id, param_type)
3711 i++
3712 break
3713 }
3714 remaining := int(node.children_count) - i
3715 if remaining == 1 {
3716 arg_type := t.node_type(arg_id)
3717 if arg_type.starts_with('[]') {
3718 args << t.transform_call_arg_for_param(arg_id, param_type)
3719 } else {
3720 args << t.pack_variadic_args(node, i, variadic_type.elem_type)
3721 }
3722 } else {
3723 args << t.pack_variadic_args(node, i, variadic_type.elem_type)
3724 }
3725 break
3726 }
3727 }
3728 args << t.transform_call_arg_for_param(arg_id, param_type)
3729 i++
3730 }
3731 if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
3732 variadic_type := params[variadic_idx]
3733 if variadic_type is types.Array {
3734 args << t.pack_variadic_args(node, int(node.children_count), variadic_type.elem_type)
3735 }
3736 }
3737 t.append_missing_params_struct_args(mut args, params, param_offset)
3738 return args
3739}
3740
3741// receiver_method_param_offset supports receiver method param offset handling for Transformer.
3742fn (t &Transformer) receiver_method_param_offset(base_id flat.NodeId, node flat.Node, params []types.Type) int {
3743 if params.len == 0 {
3744 return 0
3745 }
3746 base_type := t.node_type(base_id)
3747 first_type := t.normalize_type_alias(params[0].name())
3748 clean_first := if first_type.starts_with('&') { first_type[1..] } else { first_type }
3749 clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
3750 if clean_first == clean_base
3751 || t.normalize_type_alias(clean_first) == t.normalize_type_alias(clean_base)
3752 || clean_first.all_after_last('.') == clean_base.all_after_last('.') {
3753 return 1
3754 }
3755 if params.len >= int(node.children_count) {
3756 return 1
3757 }
3758 return 0
3759}
3760
3761// try_lower_string_method_call supports try lower string method call handling for Transformer.
3762fn (mut t Transformer) try_lower_string_method_call(node flat.Node) ?flat.NodeId {
3763 if node.children_count == 0 {
3764 return none
3765 }
3766 fn_id := t.a.children[node.children_start]
3767 fn_node := t.a.nodes[int(fn_id)]
3768 if fn_node.kind != .selector || fn_node.children_count == 0 {
3769 return none
3770 }
3771 method := fn_node.value
3772 if method == 'count' && node.children_count == 2 && t.expr_uses_ident(t.a.child(&node, 1), 'it') {
3773 return t.lower_string_count_call(node, fn_node)
3774 }
3775 if method !in ['replace', 'replace_once', 'trim', 'trim_left', 'trim_right', 'all_before',
3776 'all_after', 'all_before_last', 'all_after_last', 'contains', 'starts_with', 'ends_with',
3777 'bytes', 'substr', 'substr_unsafe', 'repeat', 'plus_two'] {
3778 return none
3779 }
3780 base_id := t.a.child(&fn_node, 0)
3781 base_type := t.node_type(base_id)
3782 if base_type != 'string' {
3783 return none
3784 }
3785 mut args := []flat.NodeId{cap: int(node.children_count)}
3786 args << t.transform_expr(base_id)
3787 for i in 1 .. node.children_count {
3788 args << t.transform_expr(t.a.child(&node, i))
3789 }
3790 ret_type := match method {
3791 'contains', 'starts_with', 'ends_with' { 'bool' }
3792 'bytes' { '[]u8' }
3793 else { 'string' }
3794 }
3795
3796 return t.make_call_typed('string__${method}', args, ret_type)
3797}
3798
3799// lower_string_count_call builds lower string count call data for transform.
3800fn (mut t Transformer) lower_string_count_call(node flat.Node, fn_node flat.Node) ?flat.NodeId {
3801 base_id := t.a.child(&fn_node, 0)
3802 base_type := t.node_type(base_id)
3803 if base_type != 'string' {
3804 return none
3805 }
3806 base := t.stable_expr_for_reuse(base_id)
3807 mut prefix := []flat.NodeId{}
3808 t.drain_pending(mut prefix)
3809 result_name := t.new_temp('count')
3810 idx_name := t.new_temp('count_idx')
3811 prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(0), 'int')
3812 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
3813 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
3814 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
3815 elem_expr := t.make_index(base, t.make_ident(idx_name), 'u8')
3816 elem_decl := t.make_decl_assign_typed('it', elem_expr, 'u8')
3817 predicate_id := t.a.child(&node, 1)
3818 old_it := t.var_type('it')
3819 t.set_var_type('it', 'u8')
3820 predicate := t.transform_expr(predicate_id)
3821 if old_it.len > 0 {
3822 t.set_var_type('it', old_it)
3823 } else {
3824 t.unset_var_type('it')
3825 }
3826 mut loop_body := []flat.NodeId{}
3827 loop_body << elem_decl
3828 t.drain_pending(mut loop_body)
3829 inc := t.make_assign_op(t.make_ident(result_name), t.make_int_literal(1), .plus_assign)
3830 loop_body << t.make_if(predicate, t.make_block(arr1(inc)), t.make_empty())
3831 prefix << t.make_for_stmt(init, cond, post, loop_body, node)
3832 for stmt in prefix {
3833 t.pending_stmts << stmt
3834 }
3835 return t.make_ident(result_name)
3836}
3837
3838// expr_uses_ident supports expr uses ident handling for Transformer.
3839fn (t &Transformer) expr_uses_ident(id flat.NodeId, name string) bool {
3840 if int(id) < 0 {
3841 return false
3842 }
3843 node := t.a.nodes[int(id)]
3844 if node.kind == .ident && node.value == name {
3845 return true
3846 }
3847 for i in 0 .. node.children_count {
3848 if t.expr_uses_ident(t.a.child(&node, i), name) {
3849 return true
3850 }
3851 }
3852 return false
3853}
3854
3855// is_method_call checks if a .call node is a method call (child[0] is .selector).
3856// Returns true for `obj.method(args)` patterns.
3857fn (mut t Transformer) is_method_call(node flat.Node) bool {
3858 if node.children_count == 0 {
3859 return false
3860 }
3861 fn_id := t.a.children[node.children_start]
3862 if int(fn_id) < 0 {
3863 return false
3864 }
3865 fn_node := t.a.nodes[int(fn_id)]
3866 return fn_node.kind == .selector
3867}
3868
3869// get_call_return_type looks up the return type for a resolved call.
3870// Handles both checker-resolved calls and transform-time name resolution.
3871fn (t &Transformer) get_call_return_type(id flat.NodeId, node flat.Node) string {
3872 if t.is_local_fn_value_call(node) {
3873 return ''
3874 }
3875 if ret := t.checker_resolved_non_builtin_return_type(id, node) {
3876 return ret
3877 }
3878 if node.children_count > 0 {
3879 fn_node := t.a.child_node(&node, 0)
3880 if fn_node.kind == .ident {
3881 local_type := t.var_type(fn_node.value)
3882 if local_type.len > 0 {
3883 if ret := t.local_fn_value_return_type_from_type(local_type) {
3884 return t.call_return_type_name(ret, node)
3885 }
3886 } else {
3887 if ret := t.local_fn_decl_return_type(fn_node.value) {
3888 return t.call_return_type_name(ret, node)
3889 }
3890 }
3891 }
3892 if fn_node.kind == .selector
3893 && fn_node.value in ['clone', 'reverse', 'repeat', 'repeat_to_depth']
3894 && fn_node.children_count > 0 {
3895 base_id := t.a.child(fn_node, 0)
3896 base_type := t.node_type(base_id)
3897 clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
3898 if clean_type.starts_with('[]') {
3899 return clean_type
3900 }
3901 }
3902 if fn_node.kind == .selector && fn_node.value == 'wait' && fn_node.children_count > 0 {
3903 base_id := t.a.child(fn_node, 0)
3904 base_type := t.node_type(base_id)
3905 clean_type := t.membership_container_type(base_type)
3906 if clean_type.starts_with('[]') {
3907 elem_type := clean_type[2..]
3908 if elem_type == 'thread' {
3909 return 'void'
3910 }
3911 if elem_type.starts_with('thread ') {
3912 return '[]${elem_type[7..]}'
3913 }
3914 }
3915 }
3916 if fn_node.kind == .selector && fn_node.value in ['keys', 'values']
3917 && fn_node.children_count > 0 {
3918 base_id := t.a.child(fn_node, 0)
3919 mut base_type := t.node_type(base_id)
3920 if base_type.len == 0 {
3921 base_type = t.checker_node_type(base_id)
3922 }
3923 clean_type := t.clean_map_type(base_type)
3924 if clean_type.starts_with('map[') {
3925 elem_type := if fn_node.value == 'keys' {
3926 t.map_key_type(clean_type)
3927 } else {
3928 t.map_value_type(clean_type)
3929 }
3930 if elem_type.len > 0 {
3931 return '[]${elem_type}'
3932 }
3933 }
3934 }
3935 // A method on a concrete generic instance (`Box[int].clone`) is registered under
3936 // the open form (`Box[T].clone`), whose stored return type collapsed `Box[T]` to
3937 // the bare base. Resolve it through the checker, which re-substitutes the concrete
3938 // arguments from the signature text, so the inferred decl type is `Box[int]`.
3939 if !isnil(t.tc) && fn_node.kind == .selector && fn_node.children_count > 0 {
3940 base_type := t.node_type(t.a.child(fn_node, 0))
3941 clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
3942 if clean_base.contains('[') && clean_base.ends_with(']') {
3943 if ci := t.tc.resolve_generic_struct_method(clean_base, fn_node.value) {
3944 rn := ci.return_type.name()
3945 if rn.len > 0 && rn != 'void' && rn != 'unknown' {
3946 return t.normalize_type_alias(rn)
3947 }
3948 }
3949 }
3950 }
3951 }
3952 if !isnil(t.tc) {
3953 if name := t.tc.resolved_call_name(id) {
3954 if ret := t.tc.fn_ret_types[name] {
3955 return t.call_return_type_name(ret.name(), node)
3956 }
3957 }
3958 }
3959 name := t.resolve_call_name(node)
3960 if name.len == 0 {
3961 return ''
3962 }
3963 if !isnil(t.tc) {
3964 if ret := t.tc.fn_ret_types[name] {
3965 return t.call_return_type_name(ret.name(), node)
3966 }
3967 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
3968 qname := '${t.cur_module}.${name}'
3969 if ret := t.tc.fn_ret_types[qname] {
3970 return t.call_return_type_name(ret.name(), node)
3971 }
3972 }
3973 }
3974 // Try exact name first
3975 if ret := t.fn_ret_types[name] {
3976 return t.call_return_type_name(ret, node)
3977 }
3978 // Try qualified with current module
3979 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
3980 qname := '${t.cur_module}.${name}'
3981 if ret := t.fn_ret_types[qname] {
3982 return t.call_return_type_name(ret, node)
3983 }
3984 }
3985 return ''
3986}
3987
3988fn (t &Transformer) checker_resolved_non_builtin_return_type(id flat.NodeId, node flat.Node) ?string {
3989 if isnil(t.tc) {
3990 return none
3991 }
3992 name := t.tc.resolved_call_name(id) or { return none }
3993 if is_builtin_collection_resolved_call(name) {
3994 return none
3995 }
3996 ret := t.tc.fn_ret_types[name] or { return none }
3997 return t.call_return_type_name(ret.name(), node)
3998}
3999
4000// call_return_type_name updates call return type name state for Transformer.
4001fn (t &Transformer) call_return_type_name(ret_name string, node flat.Node) string {
4002 mut typ := ret_name
4003 if node.value.len > 0 {
4004 generic_arg := t.normalize_type_in_module(node.value, t.cur_module)
4005 if generic_arg.len > 0 {
4006 typ = t.specialize_generic_type_name(typ, generic_arg)
4007 }
4008 }
4009 if t.is_optional_type_name(typ) {
4010 return typ
4011 }
4012 return t.normalize_type_alias(typ)
4013}
4014
4015// specialize_generic_type_name supports specialize generic type name handling for Transformer.
4016fn (t &Transformer) specialize_generic_type_name(typ string, generic_arg string) string {
4017 clean := typ.trim_space()
4018 if clean.len == 0 || generic_arg.len == 0 {
4019 return typ
4020 }
4021 if clean == 'T' {
4022 return generic_arg
4023 }
4024 if clean.starts_with('&') {
4025 return '&' + t.specialize_generic_type_name(clean[1..], generic_arg)
4026 }
4027 if clean.starts_with('[]') {
4028 return '[]' + t.specialize_generic_type_name(clean[2..], generic_arg)
4029 }
4030 if clean.starts_with('?') {
4031 return '?' + t.specialize_generic_type_name(clean[1..], generic_arg)
4032 }
4033 if clean.starts_with('!') {
4034 return '!' + t.specialize_generic_type_name(clean[1..], generic_arg)
4035 }
4036 if clean.starts_with('...') {
4037 return '...' + t.specialize_generic_type_name(clean[3..], generic_arg)
4038 }
4039 if clean.starts_with('map[') {
4040 bracket_end := clean.index(']') or { return typ }
4041 key_type := t.specialize_generic_type_name(clean[4..bracket_end], generic_arg)
4042 value_type := t.specialize_generic_type_name(clean[bracket_end + 1..], generic_arg)
4043 return 'map[${key_type}]${value_type}'
4044 }
4045 if clean.starts_with('[') {
4046 bracket_end := clean.index(']') or { return typ }
4047 return clean[..bracket_end + 1] + t.specialize_generic_type_name(clean[bracket_end +
4048 1..], generic_arg)
4049 }
4050 return typ
4051}
4052