v4 / vlib / v3 / transform / fn.v
2955 lines · 2859 sloc · 93.65 KB · af90e2747ae2e56909e026a22a1668e7b02f4b3b
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
62// is_known_fn_name reports whether is known fn name applies in transform.
63fn (t &Transformer) is_known_fn_name(name string) bool {
64 if name in t.fn_ret_types {
65 return true
66 }
67 if !isnil(t.tc) {
68 return name in t.tc.fn_ret_types || name in t.tc.fn_param_types
69 }
70 return false
71}
72
73// resolve_receiver_method_name resolves resolve receiver method name information for transform.
74fn (t &Transformer) resolve_receiver_method_name(base_id flat.NodeId, method string) string {
75 if method.len == 0 {
76 return ''
77 }
78 mut base_type := t.lvalue_type(base_id)
79 if base_type.starts_with('&') {
80 base_type = base_type[1..]
81 }
82 if base_type.len == 0 {
83 return ''
84 }
85 if method_name := t.resolve_receiver_method_for_type(base_type, method) {
86 return method_name
87 }
88 mut raw_var_clean := ''
89 if raw_var_type := t.raw_var_type_for_expr(base_id) {
90 raw_clean := if raw_var_type.starts_with('&') {
91 raw_var_type[1..]
92 } else {
93 raw_var_type
94 }
95 raw_var_clean = raw_clean
96 if raw_clean.len > 0 && raw_clean != base_type {
97 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
98 return method_name
99 }
100 }
101 }
102 if raw_const_type := t.raw_const_type_name_for_expr(base_id) {
103 raw_clean := if raw_const_type.starts_with('&') {
104 raw_const_type[1..]
105 } else {
106 raw_const_type
107 }
108 if raw_clean.len > 0 && raw_clean != base_type && raw_clean != raw_var_clean {
109 if method_name := t.resolve_receiver_method_for_type(raw_clean, method) {
110 return method_name
111 }
112 }
113 }
114 if alias_method := t.resolve_alias_receiver_method(base_type, method) {
115 return alias_method
116 }
117 if embedded_method := t.resolve_embedded_receiver_method(base_type, method) {
118 return embedded_method
119 }
120 return ''
121}
122
123// resolve_receiver_method_for_type resolves resolve_receiver_method_for_type logic in transform.
124fn (t &Transformer) resolve_receiver_method_for_type(receiver_type string, method string) ?string {
125 mut clean_type := receiver_type
126 if clean_type.starts_with('&') {
127 clean_type = clean_type[1..]
128 }
129 if clean_type.starts_with('map[') {
130 for candidate in t.map_receiver_method_candidates(clean_type, method) {
131 if t.is_known_fn_name(candidate) {
132 return candidate
133 }
134 }
135 } else {
136 direct := '${clean_type}.${method}'
137 if t.is_known_fn_name(direct) {
138 return direct
139 }
140 }
141 if clean_type.starts_with('[]') {
142 elem_type := clean_type[2..]
143 short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
144 short_array := '[]${short_elem}.${method}'
145 if t.is_known_fn_name(short_array) {
146 return short_array
147 }
148 if elem_type.contains('.') {
149 qualified_array := '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
150 if t.is_known_fn_name(qualified_array) {
151 return qualified_array
152 }
153 }
154 } else if clean_type.contains('.') {
155 short_type := clean_type.all_after_last('.')
156 short_method := '${short_type}.${method}'
157 if t.is_known_fn_name(short_method) {
158 return short_method
159 }
160 qualified_short_method := '${clean_type.all_before_last('.')}.${short_type}.${method}'
161 if t.is_known_fn_name(qualified_short_method) {
162 return qualified_short_method
163 }
164 }
165 if !isnil(t.tc) {
166 if target := t.tc.type_aliases[clean_type] {
167 alias_method := '${target}.${method}'
168 if t.is_known_fn_name(alias_method) {
169 return alias_method
170 }
171 }
172 }
173 return none
174}
175
176// resolve_alias_receiver_method converts resolve alias receiver method data for transform.
177fn (t &Transformer) resolve_alias_receiver_method(base_type string, method string) ?string {
178 if isnil(t.tc) || base_type.len == 0 || method.len == 0 {
179 return none
180 }
181 clean_base := t.normalize_type_alias(base_type)
182 if alias_method := t.alias_methods['${clean_base}.${method}'] {
183 return alias_method
184 }
185 if !t.is_integer_type_name(clean_base) {
186 return none
187 }
188 for name, params in t.tc.fn_param_types {
189 if !name.ends_with('.${method}') || params.len == 0 {
190 continue
191 }
192 receiver_name := name.all_before_last('.')
193 if receiver_name.len == 0 || receiver_name !in t.tc.type_aliases {
194 continue
195 }
196 param_name := params[0].name()
197 if t.alias_receiver_type_matches(clean_base, param_name) {
198 return name
199 }
200 }
201 return none
202}
203
204fn (t &Transformer) resolve_embedded_receiver_method(base_type string, method string) ?string {
205 if base_type.len == 0 || method.len == 0 {
206 return none
207 }
208 mut lookup_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
209 if lookup_type !in t.structs && lookup_type.contains('.') {
210 short_type := lookup_type.all_after_last('.')
211 if short_type in t.structs {
212 lookup_type = short_type
213 }
214 }
215 info := t.lookup_struct_info(lookup_type) or { return none }
216 for field in info.fields {
217 if !t.is_embedded_field(field) {
218 continue
219 }
220 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
221 clean_field := if field_type.starts_with('&') { field_type[1..] } else { field_type }
222 if method_name := t.resolve_receiver_method_for_type(clean_field, method) {
223 return method_name
224 }
225 if method_name := t.resolve_embedded_receiver_method(clean_field, method) {
226 return method_name
227 }
228 }
229 return none
230}
231
232// alias_receiver_type_matches converts alias receiver type matches data for transform.
233fn (t &Transformer) alias_receiver_type_matches(base_type string, alias_type string) bool {
234 if base_type.len == 0 || alias_type.len == 0 {
235 return false
236 }
237 clean_alias := if alias_type.starts_with('&') { alias_type[1..] } else { alias_type }
238 alias_target := t.normalize_type_alias(clean_alias)
239 if alias_target == base_type {
240 return true
241 }
242 if !isnil(t.tc) {
243 alias_c_type := t.tc.c_type(t.tc.parse_type(alias_target))
244 base_c_type := t.tc.c_type(t.tc.parse_type(base_type))
245 if alias_c_type == base_c_type {
246 return true
247 }
248 }
249 return t.is_integer_type_name(alias_target) && t.is_integer_type_name(base_type)
250}
251
252// is_integer_type_name reports whether is integer type name applies in transform.
253fn (t &Transformer) is_integer_type_name(typ string) bool {
254 return typ in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'byte', 'u16', 'u32', 'u64', 'rune',
255 'isize', 'usize']
256}
257
258// raw_var_type_for_expr supports raw var type for expr handling for Transformer.
259fn (t &Transformer) raw_var_type_for_expr(id flat.NodeId) ?string {
260 if int(id) < 0 {
261 return none
262 }
263 node := t.a.nodes[int(id)]
264 if node.kind == .ident {
265 typ := t.var_type(node.value)
266 if typ.len > 0 {
267 return typ
268 }
269 }
270 if node.typ.len > 0 {
271 return node.typ
272 }
273 return none
274}
275
276// raw_const_type_name_for_expr supports raw const type name for expr handling for Transformer.
277fn (t &Transformer) raw_const_type_name_for_expr(id flat.NodeId) ?string {
278 if int(id) < 0 || isnil(t.tc) {
279 return none
280 }
281 name := t.expr_key(id)
282 if name.len == 0 {
283 return none
284 }
285 key := t.const_type_key(name) or { return none }
286 typ := t.tc.const_types[key] or { return none }
287 return typ.name()
288}
289
290// resolve_method_receiver_type determines the receiver type for method calls.
291// For a call where child[0] is a .selector, resolves the type of the selector's base expression.
292fn (t &Transformer) resolve_method_receiver_type(call_node flat.Node) string {
293 if call_node.children_count == 0 {
294 return ''
295 }
296 fn_id := t.a.children[call_node.children_start]
297 if int(fn_id) < 0 {
298 return ''
299 }
300 fn_node := t.a.nodes[int(fn_id)]
301 if fn_node.kind != .selector || fn_node.children_count == 0 {
302 return ''
303 }
304 base_id := t.a.children[fn_node.children_start]
305 return t.resolve_expr_type(base_id)
306}
307
308// normalize_generic_call_expr transforms normalize generic call expr data for transform.
309fn (mut t Transformer) normalize_generic_call_expr(id flat.NodeId, node flat.Node) flat.NodeId {
310 if node.children_count == 0 {
311 return id
312 }
313 fn_id := t.a.child(&node, 0)
314 if int(fn_id) < 0 {
315 return id
316 }
317 fn_node := t.a.nodes[int(fn_id)]
318 if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {
319 return id
320 }
321 base_id := t.a.child(&fn_node, 0)
322 base := t.a.nodes[int(base_id)]
323 if base.kind !in [.ident, .selector] {
324 return id
325 }
326 type_arg := t.generic_call_type_arg_name(t.a.child(&fn_node, 1))
327 if type_arg.len == 0 {
328 return id
329 }
330 mut children := []flat.NodeId{cap: int(node.children_count)}
331 children << base_id
332 for i in 1 .. node.children_count {
333 children << t.a.child(&node, i)
334 }
335 start := t.a.children.len
336 for child in children {
337 t.a.children << child
338 }
339 return t.a.add_node(flat.Node{
340 kind: .call
341 op: node.op
342 children_start: start
343 children_count: flat.child_count(children.len)
344 pos: node.pos
345 value: type_arg
346 typ: node.typ
347 })
348}
349
350// generic_call_type_arg_name supports generic call type arg name handling for Transformer.
351fn (t &Transformer) generic_call_type_arg_name(id flat.NodeId) string {
352 if int(id) < 0 {
353 return ''
354 }
355 node := t.a.nodes[int(id)]
356 match node.kind {
357 .ident {
358 return node.value
359 }
360 .selector {
361 if node.children_count == 0 {
362 return node.value
363 }
364 base := t.generic_call_type_arg_name(t.a.child(&node, 0))
365 if base.len == 0 {
366 return node.value
367 }
368 return '${base}.${node.value}'
369 }
370 .prefix {
371 if node.children_count == 0 {
372 return ''
373 }
374 child := t.generic_call_type_arg_name(t.a.child(&node, 0))
375 if child.len == 0 {
376 return ''
377 }
378 if node.op == .amp {
379 return '&${child}'
380 }
381 return child
382 }
383 else {
384 return ''
385 }
386 }
387}
388
389// transform_call_args transforms all children of a call expression.
390// child[0] is the function expression, children[1..n] are arguments.
391fn (mut t Transformer) transform_call_args(id flat.NodeId, node flat.Node) flat.NodeId {
392 if node.children_count == 0 {
393 return t.a.add_node(flat.Node{
394 kind: .call
395 op: node.op
396 pos: node.pos
397 value: node.value
398 typ: node.typ
399 })
400 }
401 call_name := t.call_name_for_node(id, node)
402 params := t.call_param_types(call_name)
403 param_offset := t.call_param_offset(call_name, node, params)
404 is_variadic := t.call_is_variadic(call_name)
405 variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
406 params.len - 1
407 } else {
408 -1
409 }
410 mut new_children := []flat.NodeId{cap: int(node.children_count)}
411 saved_in_call_callee := t.in_call_callee
412 t.in_call_callee = true
413 new_children << t.transform_expr(t.a.children[node.children_start])
414 t.in_call_callee = saved_in_call_callee
415 mut i := 1
416 for i < node.children_count {
417 arg_idx := new_children.len - 1
418 param_idx := arg_idx + param_offset
419 arg_id := t.a.child(&node, i)
420 arg_node := t.a.nodes[int(arg_id)]
421 param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
422 if arg_node.kind == .field_init {
423 if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
424 new_children << packed_arg
425 i = t.next_non_field_init_arg(node, i)
426 continue
427 }
428 }
429 if variadic_idx >= 0 && param_idx == variadic_idx {
430 variadic_type := params[variadic_idx]
431 if variadic_type is types.Array {
432 remaining := int(node.children_count) - i
433 if remaining == 1 {
434 arg_type := t.node_type(arg_id)
435 if arg_type.starts_with('[]') {
436 new_children << t.transform_call_arg_for_param(arg_id, param_type)
437 } else {
438 new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
439 }
440 } else {
441 new_children << t.pack_variadic_args(node, i, variadic_type.elem_type)
442 }
443 break
444 }
445 }
446 new_children << t.transform_call_arg_for_param(arg_id, param_type)
447 i++
448 }
449 explicit_args := int(node.children_count) - 1
450 if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
451 variadic_type := params[variadic_idx]
452 if variadic_type is types.Array {
453 new_children << t.pack_variadic_args(node, int(node.children_count),
454 variadic_type.elem_type)
455 }
456 }
457 t.append_missing_params_struct_args(mut new_children, params, param_offset)
458 start := t.a.children.len
459 for nc in new_children {
460 t.a.children << nc
461 }
462 return t.a.add_node(flat.Node{
463 kind: .call
464 op: node.op
465 children_start: start
466 children_count: flat.child_count(new_children.len)
467 pos: node.pos
468 value: node.value
469 typ: node.typ
470 })
471}
472
473// try_lower_join_path_call supports try lower join path call handling for Transformer.
474fn (mut t Transformer) try_lower_join_path_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
475 call_name := t.call_name_for_node(id, node)
476 if call_name != 'join_path' && call_name != 'os.join_path' {
477 return none
478 }
479 // A spread argument (`...rest`) has a runtime-determined length and cannot be
480 // unrolled into nested join_path_single calls at compile time. Defer to the real
481 // variadic os.join_path in that case.
482 for i in 1 .. node.children_count {
483 arg := t.a.child_node(&node, i)
484 if arg.kind == .prefix && arg.value == '...' {
485 return none
486 }
487 }
488 if node.children_count <= 1 {
489 return t.make_string_literal('')
490 }
491 mut result := t.transform_expr(t.a.child(&node, 1))
492 for i in 2 .. node.children_count {
493 arg := t.transform_expr(t.a.child(&node, i))
494 result = t.make_call_typed('join_path_single', arr2(result, arg), 'string')
495 }
496 return result
497}
498
499// transform_params_struct_call_arg transforms transform params struct call arg data for transform.
500fn (mut t Transformer) transform_params_struct_call_arg(node flat.Node, field_start int, param_type string) ?flat.NodeId {
501 struct_type := t.params_struct_type_name(param_type) or { return none }
502 mut field_ids := []flat.NodeId{}
503 for i in field_start .. node.children_count {
504 field_id := t.a.child(&node, i)
505 field := t.a.nodes[int(field_id)]
506 if field.kind != .field_init {
507 break
508 }
509 field_ids << field_id
510 }
511 if field_ids.len == 0 {
512 return none
513 }
514 start := t.a.children.len
515 for field_id in field_ids {
516 t.a.children << field_id
517 }
518 struct_id := t.a.add_node(flat.Node{
519 kind: .struct_init
520 children_start: start
521 children_count: flat.child_count(field_ids.len)
522 value: struct_type
523 typ: struct_type
524 })
525 return t.transform_struct_fields(struct_id, t.a.nodes[int(struct_id)])
526}
527
528// next_non_field_init_arg returns next non field init arg data for Transformer.
529fn (t &Transformer) next_non_field_init_arg(node flat.Node, field_start int) int {
530 mut i := field_start
531 for i < node.children_count {
532 field := t.a.child_node(&node, i)
533 if field.kind != .field_init {
534 break
535 }
536 i++
537 }
538 return i
539}
540
541// params_struct_type_name supports params struct type name handling for Transformer.
542fn (t &Transformer) params_struct_type_name(param_type string) ?string {
543 if param_type.len == 0 {
544 return none
545 }
546 mut typ := param_type
547 if typ.starts_with('&') {
548 typ = typ[1..]
549 }
550 if info := t.lookup_struct_info(typ) {
551 if info.is_params {
552 return typ
553 }
554 }
555 normalized := t.normalize_type_alias(typ)
556 if normalized != typ {
557 if info := t.lookup_struct_info(normalized) {
558 if info.is_params {
559 return normalized
560 }
561 }
562 }
563 return none
564}
565
566// call_name_for_node updates call name for node state for Transformer.
567fn (t &Transformer) call_name_for_node(id flat.NodeId, node flat.Node) string {
568 if !isnil(t.tc) {
569 if name := t.tc.resolved_call_name(id) {
570 return name
571 }
572 }
573 return t.resolve_call_name(node)
574}
575
576// call_param_offset updates call param offset state for Transformer.
577fn (t &Transformer) call_param_offset(call_name string, node flat.Node, params []types.Type) int {
578 if params.len == 0 || node.children_count == 0 {
579 return 0
580 }
581 fn_node := t.a.child_node(&node, 0)
582 if fn_node.kind != .selector || fn_node.children_count == 0 {
583 return 0
584 }
585 base_id := t.a.child(fn_node, 0)
586 // `module.Type.fn(...)` / `Type.fn(...)` is a static associated function call, not a
587 // method: the base names a type, not a value, so no receiver must be prepended.
588 if _ := t.static_assoc_fn_name(base_id, fn_node.value) {
589 return 0
590 }
591 if t.receiver_method_param_offset(base_id, node, params) == 1 {
592 return 1
593 }
594 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
595 if method_name.len == 0 {
596 return 0
597 }
598 if call_name.len == 0 || call_name == method_name || call_name == c_name(method_name) {
599 return 1
600 }
601 return 0
602}
603
604// static_assoc_fn_name returns the name of the static associated function a selector
605// call resolves to, if the base names a type (`Type.fn` or `module.Type.fn`) and that
606// function exists. Such calls take no receiver, so the base must not be prepended as an
607// argument. Returns none for ordinary method calls (base is a value).
608fn (t &Transformer) static_assoc_fn_name(base_id flat.NodeId, method string) ?string {
609 if method.len == 0 {
610 return none
611 }
612 base := t.a.nodes[int(base_id)]
613 if base.kind == .ident {
614 name := '${base.value}.${method}'
615 if t.is_known_fn_name(name) {
616 return name
617 }
618 } else if base.kind == .selector && base.children_count > 0 {
619 inner := t.a.child_node(&base, 0)
620 if inner.kind == .ident {
621 name := '${inner.value}.${base.value}.${method}'
622 if t.is_known_fn_name(name) {
623 return name
624 }
625 }
626 }
627 return none
628}
629
630// call_param_types updates call param types state for Transformer.
631fn (t &Transformer) call_param_types(call_name string) []types.Type {
632 if call_name.len == 0 || isnil(t.tc) {
633 return []types.Type{}
634 }
635 params := t.tc.fn_param_types[call_name] or { return []types.Type{} }
636 return params
637}
638
639// call_is_variadic updates call is variadic state for Transformer.
640fn (t &Transformer) call_is_variadic(call_name string) bool {
641 if call_name.len == 0 || isnil(t.tc) {
642 return false
643 }
644 if t.tc.c_variadic_fns[call_name] {
645 return false
646 }
647 return t.tc.fn_variadic[call_name] or { false }
648}
649
650// call_param_type_name updates call param type name state for Transformer.
651fn (t &Transformer) call_param_type_name(call_name string, idx int) string {
652 if idx < 0 || call_name.len == 0 || isnil(t.tc) {
653 return ''
654 }
655 params := t.tc.fn_param_types[call_name] or { return '' }
656 if idx >= params.len {
657 return ''
658 }
659 return params[idx].name()
660}
661
662// transform_call_arg_for_param transforms transform call arg for param data for transform.
663fn (mut t Transformer) transform_call_arg_for_param(arg_id flat.NodeId, param_type string) flat.NodeId {
664 if int(arg_id) < 0 {
665 return arg_id
666 }
667 mut arg_node := &t.a.nodes[int(arg_id)]
668 if arg_node.kind == .array_literal && arg_node.typ.len == 0 && param_type.starts_with('[]') {
669 arg_node.typ = param_type
670 }
671 if arg_node.kind == .enum_val && param_type in t.enum_types {
672 return t.transform_enum_shorthand(arg_id, *arg_node, param_type)
673 }
674 if param_type.starts_with('&') && arg_node.kind == .selector
675 && t.selector_chain_has_sum_variant_field(arg_id) {
676 value := t.transform_expr(arg_id)
677 mut value_type := t.node_type(arg_id)
678 if value_type.len == 0 {
679 value_type = t.node_type(value)
680 }
681 stable := t.stable_transformed_expr_for_reuse(value, value_type, 'addr')
682 addr := t.make_prefix(.amp, stable)
683 t.a.nodes[int(addr)].typ = param_type
684 return addr
685 }
686 if param_type.starts_with('&') && t.is_sum_type_name(param_type[1..]) {
687 target_sum := param_type[1..]
688 arg_type := t.node_type(arg_id)
689 arg_key := t.expr_key(arg_id)
690 has_smartcast := t.has_smartcast(arg_key)
691 if !has_smartcast
692 && t.resolve_sum_name(t.trim_pointer_type(arg_type)) == t.resolve_sum_name(target_sum) {
693 return t.transform_expr(arg_id)
694 }
695 if arg_node.kind == .prefix && arg_node.op == .amp && arg_node.children_count > 0 {
696 inner_id := t.a.child(arg_node, 0)
697 inner_type := t.node_type(inner_id)
698 if t.resolve_sum_name(t.trim_pointer_type(inner_type)) == t.resolve_sum_name(target_sum) {
699 return t.transform_expr(arg_id)
700 }
701 }
702 wrapped := t.wrap_sum_value(arg_id, target_sum)
703 tmp_name := t.new_temp('sum_arg')
704 t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, target_sum)
705 return t.make_prefix(.amp, t.make_ident(tmp_name))
706 }
707 if t.is_sum_type_name(param_type) {
708 return t.wrap_sum_value(arg_id, param_type)
709 }
710 if param_type.starts_with('[]') {
711 arg_type := t.node_type(arg_id)
712 if t.is_fixed_array_type(arg_type) {
713 return t.fixed_array_value_to_array(arg_id, arg_type, param_type)
714 }
715 if const_arg := t.transform_const_array_arg_for_param(arg_id, param_type) {
716 return const_arg
717 }
718 }
719 return t.transform_expr_for_type(arg_id, param_type)
720}
721
722fn (mut t Transformer) append_missing_params_struct_args(mut args []flat.NodeId, params []types.Type, param_offset int) {
723 mut param_idx := args.len - 1 + param_offset
724 for param_idx < params.len {
725 param_type := params[param_idx].name()
726 struct_type := t.params_struct_type_name(param_type) or { break }
727 args << t.zero_value_for_type(struct_type)
728 param_idx++
729 }
730}
731
732// is_fn_pointer_type_name reports whether is fn pointer type name applies in transform.
733fn (t &Transformer) is_fn_pointer_type_name(type_name string) bool {
734 if type_name.len == 0 || isnil(t.tc) {
735 return false
736 }
737 typ := t.tc.parse_type(type_name)
738 if typ is types.FnType {
739 return true
740 }
741 if typ is types.Alias {
742 return typ.base_type is types.FnType
743 }
744 return false
745}
746
747// is_named_fn_value_arg reports whether is named fn value arg applies in transform.
748fn (t &Transformer) is_named_fn_value_arg(arg_id flat.NodeId) bool {
749 if int(arg_id) < 0 || isnil(t.tc) {
750 return false
751 }
752 node := t.a.nodes[int(arg_id)]
753 if node.kind == .ident {
754 if _ := t.resolve_fn_value_ident(node.value) {
755 return true
756 }
757 return false
758 }
759 if node.kind == .selector && node.children_count > 0 {
760 key := t.expr_key(arg_id)
761 return key in t.tc.fn_ret_types || key in t.tc.fn_param_types
762 }
763 return false
764}
765
766// transform_const_array_arg_for_param supports transform_const_array_arg_for_param handling.
767fn (mut t Transformer) transform_const_array_arg_for_param(arg_id flat.NodeId, param_type string) ?flat.NodeId {
768 expr_id := t.const_expr_for_arg(arg_id) or { return none }
769 expr := t.a.nodes[int(expr_id)]
770 elem_type := param_type[2..]
771 if expr.kind == .array_init && expr.children_count == 0 {
772 return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
773 }
774 if expr.kind != .array_literal {
775 return none
776 }
777 if expr.children_count == 0 {
778 return t.make_array_new_call(elem_type, t.make_int_literal(0), t.make_int_literal(0))
779 }
780 mut values := []flat.NodeId{cap: int(expr.children_count)}
781 for i in 0 .. expr.children_count {
782 values << t.transform_expr(t.a.child(&expr, i))
783 }
784 return t.make_array_literal_typed(values, param_type)
785}
786
787// const_expr_for_arg supports const expr for arg handling for Transformer.
788fn (t &Transformer) const_expr_for_arg(arg_id flat.NodeId) ?flat.NodeId {
789 if isnil(t.tc) || int(arg_id) < 0 {
790 return none
791 }
792 node := t.a.nodes[int(arg_id)]
793 if node.kind == .ident {
794 if t.var_type(node.value).len > 0 {
795 return none
796 }
797 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
798 if expr_id := t.const_expr_for_name('${t.cur_module}.${node.value}') {
799 return expr_id
800 }
801 }
802 return t.const_expr_for_name(node.value)
803 }
804 if node.kind == .selector && node.children_count > 0 {
805 base := t.a.child_node(&node, 0)
806 if base.kind == .ident {
807 return t.const_expr_for_name('${base.value}.${node.value}')
808 }
809 }
810 return none
811}
812
813// const_expr_for_name supports const expr for name handling for Transformer.
814fn (t &Transformer) const_expr_for_name(name string) ?flat.NodeId {
815 if isnil(t.tc) || name.len == 0 {
816 return none
817 }
818 if expr_id := t.tc.const_exprs[name] {
819 return expr_id
820 }
821 key := t.const_type_key(name) or { return none }
822 if expr_id := t.tc.const_exprs[key] {
823 return expr_id
824 }
825 return none
826}
827
828// pack_variadic_args supports pack variadic args handling for Transformer.
829fn (mut t Transformer) pack_variadic_args(node flat.Node, first_arg int, elem_type types.Type) flat.NodeId {
830 expected_enum := elem_type.name()
831 array_type := '[]${expected_enum}'
832 if named_arg := t.transform_variadic_struct_fields(node, first_arg, elem_type) {
833 if t.in_const_init {
834 return t.make_array_literal_typed([named_arg], array_type)
835 }
836 tmp_name := t.new_temp('varargs')
837 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(expected_enum,
838 t.make_int_literal(0), t.make_int_literal(1)), array_type)
839 value_name := t.new_temp('vararg')
840 t.pending_stmts << t.make_decl_assign_typed(value_name, named_arg, expected_enum)
841 t.pending_stmts << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
842 t.make_ident(tmp_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
843 t.set_var_type(tmp_name, array_type)
844 return t.make_ident(tmp_name)
845 }
846 if t.in_const_init {
847 mut values := []flat.NodeId{cap: int(node.children_count) - first_arg}
848 for i in first_arg .. node.children_count {
849 arg_id := t.a.child(&node, i)
850 arg := t.a.nodes[int(arg_id)]
851 if arg.kind == .enum_val && expected_enum in t.enum_types {
852 values << t.transform_enum_shorthand(arg_id, arg, expected_enum)
853 } else if t.is_sum_type_name(expected_enum) {
854 values << t.wrap_sum_value(arg_id, expected_enum)
855 } else {
856 values << t.transform_expr(arg_id)
857 }
858 }
859 return t.make_array_literal_typed(values, array_type)
860 }
861 tmp_name := t.new_temp('varargs')
862 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.make_array_new_call(expected_enum,
863 t.make_int_literal(0), t.make_int_literal(int(node.children_count) - first_arg)),
864 array_type)
865 for i in first_arg .. node.children_count {
866 arg_id := t.a.child(&node, i)
867 arg := t.a.nodes[int(arg_id)]
868 value := if arg.kind == .enum_val && expected_enum in t.enum_types {
869 t.transform_enum_shorthand(arg_id, arg, expected_enum)
870 } else if t.is_sum_type_name(expected_enum) {
871 t.wrap_sum_value(arg_id, expected_enum)
872 } else {
873 t.transform_expr(arg_id)
874 }
875 value_name := t.new_temp('vararg')
876 t.pending_stmts << t.make_decl_assign_typed(value_name, value, expected_enum)
877 t.pending_stmts << t.make_expr_stmt(t.make_call_typed('array_push', arr2(t.make_prefix(.amp,
878 t.make_ident(tmp_name)), t.make_prefix(.amp, t.make_ident(value_name))), 'void'))
879 }
880 t.set_var_type(tmp_name, array_type)
881 return t.make_ident(tmp_name)
882}
883
884fn (mut t Transformer) transform_variadic_struct_fields(node flat.Node, field_start int, elem_type types.Type) ?flat.NodeId {
885 if field_start >= node.children_count {
886 return none
887 }
888 if elem_type !is types.Struct {
889 return none
890 }
891 first := t.a.child_node(&node, field_start)
892 if first.kind != .field_init {
893 return none
894 }
895 mut field_ids := []flat.NodeId{}
896 for i in field_start .. node.children_count {
897 field_id := t.a.child(&node, i)
898 field := t.a.nodes[int(field_id)]
899 if field.kind != .field_init {
900 break
901 }
902 field_ids << field_id
903 }
904 if field_ids.len == 0 {
905 return none
906 }
907 start := t.a.children.len
908 for field_id in field_ids {
909 t.a.children << field_id
910 }
911 struct_id := t.a.add_node(flat.Node{
912 kind: .struct_init
913 children_start: start
914 children_count: flat.child_count(field_ids.len)
915 value: elem_type.name()
916 typ: elem_type.name()
917 })
918 return t.transform_struct_fields(struct_id, t.a.nodes[int(struct_id)])
919}
920
921// make_array_literal_typed builds make array literal typed data for transform.
922fn (mut t Transformer) make_array_literal_typed(values []flat.NodeId, typ string) flat.NodeId {
923 start := t.a.children.len
924 for value in values {
925 t.a.children << value
926 }
927 return t.a.add_node(flat.Node{
928 kind: .array_literal
929 children_start: start
930 children_count: flat.child_count(values.len)
931 typ: typ
932 })
933}
934
935// stringify_expr supports stringify expr handling for Transformer.
936fn (mut t Transformer) stringify_expr(expr_id flat.NodeId) flat.NodeId {
937 expr := t.transform_expr(expr_id)
938 mut typ := t.node_type(expr)
939 if typ.len == 0 {
940 typ = t.node_type(expr_id)
941 }
942 if typ.len == 0 {
943 // Structural fallback for compound arguments (infix, prefix, cast,
944 // paren, ...) so e.g. `println(a + b)` for ints is stringified via
945 // strconv__format_int instead of being passed to println as a raw
946 // number. Mirrors the fallback already used by string interpolation.
947 typ = t.reliable_stringify_type(expr)
948 if typ.len == 0 {
949 typ = t.reliable_stringify_type(expr_id)
950 }
951 }
952 return t.wrap_string_conversion(expr, typ)
953}
954
955// reliable_stringify_type supports reliable stringify type handling for Transformer.
956fn (t &Transformer) reliable_stringify_type(id flat.NodeId) string {
957 mut typ := t.node_type(id)
958 if typ.len > 0 {
959 return typ
960 }
961 if int(id) >= 0 {
962 node := t.a.nodes[int(id)]
963 if node.typ.len > 0 {
964 return node.typ
965 }
966 match node.kind {
967 .int_literal {
968 return 'int'
969 }
970 .float_literal {
971 return 'f64'
972 }
973 .bool_literal {
974 return 'bool'
975 }
976 .char_literal {
977 return 'rune'
978 }
979 .string_literal, .string_interp {
980 return 'string'
981 }
982 .infix {
983 return t.reliable_infix_stringify_type(node)
984 }
985 .prefix {
986 if node.op == .not {
987 return 'bool'
988 }
989 if node.children_count > 0 {
990 return t.reliable_stringify_type(t.a.child(&node, 0))
991 }
992 }
993 .paren {
994 if node.children_count > 0 {
995 return t.reliable_stringify_type(t.a.child(&node, 0))
996 }
997 }
998 .cast_expr {
999 if node.value.len > 0 {
1000 return t.normalize_type_alias(node.value)
1001 }
1002 if node.children_count > 0 {
1003 return t.reliable_stringify_type(t.a.child(&node, 0))
1004 }
1005 }
1006 else {}
1007 }
1008 }
1009 return ''
1010}
1011
1012// reliable_infix_stringify_type supports reliable infix stringify type handling for Transformer.
1013fn (t &Transformer) reliable_infix_stringify_type(node flat.Node) string {
1014 if node.children_count < 2 {
1015 return ''
1016 }
1017 lhs_type := t.reliable_stringify_type(t.a.child(&node, 0))
1018 rhs_type := t.reliable_stringify_type(t.a.child(&node, 1))
1019 if lhs_type == 'string' || rhs_type == 'string' {
1020 return 'string'
1021 }
1022 match node.op {
1023 .eq, .ne, .lt, .gt, .le, .ge, .logical_and, .logical_or {
1024 return 'bool'
1025 }
1026 .left_shift, .right_shift, .right_shift_unsigned {
1027 // shifts keep the left operand's type/width
1028 if lhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type) {
1029 return lhs_type
1030 }
1031 }
1032 .plus, .minus, .mul, .div, .mod, .amp, .pipe, .xor {
1033 if lhs_type.len > 0 && rhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type)
1034 && t.is_numeric_stringify_type(rhs_type) {
1035 // Use the promoted result type, not the lhs, so e.g.
1036 // `1 + u64(x)` formats as unsigned rather than signed int.
1037 return promote_numeric_stringify_type(lhs_type, rhs_type)
1038 }
1039 }
1040 else {}
1041 }
1042
1043 return ''
1044}
1045
1046// promote_numeric_stringify_type returns the result type of a binary numeric
1047// operation for stringify purposes: floats dominate, the wider integer wins,
1048// and on equal width an explicit type beats the untyped-literal default `int`.
1049fn promote_numeric_stringify_type(a string, b string) string {
1050 if a == b {
1051 return a
1052 }
1053 if a == 'f64' || b == 'f64' {
1054 return 'f64'
1055 }
1056 if a == 'f32' || b == 'f32' {
1057 return 'f32'
1058 }
1059 ra := int_stringify_rank(a)
1060 rb := int_stringify_rank(b)
1061 if ra > rb {
1062 return a
1063 }
1064 if rb > ra {
1065 return b
1066 }
1067 if a == 'int' {
1068 return b
1069 }
1070 if b == 'int' {
1071 return a
1072 }
1073 return a
1074}
1075
1076fn int_stringify_rank(typ string) int {
1077 return match typ {
1078 'i8', 'u8', 'byte' { 8 }
1079 'i16', 'u16' { 16 }
1080 'i32', 'u32', 'int', 'rune' { 32 }
1081 'i64', 'u64', 'isize', 'usize' { 64 }
1082 else { 32 }
1083 }
1084}
1085
1086// is_numeric_stringify_type reports whether is numeric stringify type applies in transform.
1087fn (t &Transformer) is_numeric_stringify_type(typ string) bool {
1088 is_number := typ in ['int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize', 'u8', 'byte', 'u16',
1089 'u32', 'u64', 'f32', 'f64', 'rune']
1090 return is_number || typ in t.enum_types
1091}
1092
1093// is_enum_stringify_type reports whether is enum stringify type applies in transform.
1094fn (t &Transformer) is_enum_stringify_type(typ string) bool {
1095 mut clean_typ := typ
1096 if clean_typ.starts_with('&') {
1097 clean_typ = clean_typ[1..]
1098 }
1099 if clean_typ.starts_with('mut ') {
1100 clean_typ = clean_typ[4..]
1101 }
1102 if clean_typ in t.enum_types {
1103 return true
1104 }
1105 mut qtyp := clean_typ
1106 if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1107 && t.cur_module != 'builtin' {
1108 qtyp = '${t.cur_module}.${clean_typ}'
1109 if qtyp in t.enum_types {
1110 return true
1111 }
1112 }
1113 if isnil(t.tc) {
1114 return false
1115 }
1116 if alias := t.tc.type_aliases[clean_typ] {
1117 return t.is_enum_stringify_type(alias)
1118 }
1119 if qtyp != clean_typ {
1120 if alias := t.tc.type_aliases[qtyp] {
1121 return t.is_enum_stringify_type(alias)
1122 }
1123 }
1124 parsed := t.tc.parse_type(clean_typ)
1125 if parsed is types.Enum {
1126 return true
1127 }
1128 if qtyp != clean_typ {
1129 qparsed := t.tc.parse_type(qtyp)
1130 if qparsed is types.Enum {
1131 return true
1132 }
1133 }
1134 return false
1135}
1136
1137// enum_str_method_name supports enum str method name handling for Transformer.
1138fn (t &Transformer) enum_str_method_name(typ string) ?string {
1139 mut candidates := []string{cap: 3}
1140 candidates << typ
1141 if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1142 && t.cur_module != 'builtin' {
1143 candidates << '${t.cur_module}.${typ}'
1144 }
1145 if !isnil(t.tc) {
1146 parsed := t.tc.parse_type(typ)
1147 if parsed is types.Enum {
1148 candidates << parsed.name
1149 }
1150 }
1151 for candidate in candidates {
1152 method := '${candidate}.str'
1153 if t.is_known_fn_name(method) {
1154 return method
1155 }
1156 }
1157 return none
1158}
1159
1160// enum_autostr_call builds a call to the compiler-synthesized `<Enum>__autostr` helper
1161// (emitted by cgen's enum_str_defs) which returns the enum field NAME. Used as the default
1162// `${enum}` stringification when the user has not defined a custom `.str()` — V auto-derives
1163// one. Mirrors the struct-str qualification so the C name matches cgen's enum_decls naming.
1164fn (mut t Transformer) enum_autostr_call(expr flat.NodeId, typ string) flat.NodeId {
1165 mut qualified := typ
1166 if qualified.starts_with('main.') {
1167 qualified = qualified[5..]
1168 } else if !typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1169 && t.cur_module != 'builtin' {
1170 q := '${t.cur_module}.${typ}'
1171 if q in t.enum_types {
1172 qualified = q
1173 }
1174 }
1175 return t.make_call_typed('${c_name(qualified)}__autostr', arr1(expr), 'string')
1176}
1177
1178// wrap_string_conversion transforms wrap string conversion data for transform.
1179fn (mut t Transformer) wrap_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
1180 mut clean_typ := typ
1181 is_ref := clean_typ.starts_with('&')
1182 if clean_typ.starts_with('&') {
1183 clean_typ = clean_typ[1..]
1184 }
1185 if clean_typ.starts_with('builtin.') {
1186 clean_typ = clean_typ.all_after_last('.')
1187 }
1188 if t.is_optional_type_name(clean_typ) {
1189 return t.wrap_optional_string_conversion(expr, clean_typ)
1190 }
1191 if clean_typ.len == 0 || clean_typ == 'unknown' {
1192 inferred := t.resolve_expr_type(expr)
1193 if inferred.len > 0 && inferred != clean_typ {
1194 return t.wrap_string_conversion(expr, inferred)
1195 }
1196 }
1197 if !isnil(t.tc) {
1198 if alias := t.tc.type_aliases[clean_typ] {
1199 return t.wrap_string_conversion(expr, alias)
1200 }
1201 mut qtyp := clean_typ
1202 if !qtyp.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1203 && t.cur_module != 'builtin' {
1204 qtyp = '${t.cur_module}.${clean_typ}'
1205 }
1206 if alias := t.tc.type_aliases[qtyp] {
1207 return t.wrap_string_conversion(expr, alias)
1208 }
1209 if !clean_typ.contains('.') {
1210 for aname, target in t.tc.type_aliases {
1211 if aname.all_after_last('.') == clean_typ {
1212 return t.wrap_string_conversion(expr, target)
1213 }
1214 }
1215 }
1216 parsed := t.tc.parse_type(clean_typ)
1217 if parsed is types.Enum {
1218 if method := t.enum_str_method_name(clean_typ) {
1219 return t.make_call_typed(method, arr1(expr), 'string')
1220 }
1221 return t.enum_autostr_call(expr, clean_typ)
1222 }
1223 if qtyp != clean_typ {
1224 qparsed := t.tc.parse_type(qtyp)
1225 if qparsed is types.Enum {
1226 if method := t.enum_str_method_name(qtyp) {
1227 return t.make_call_typed(method, arr1(expr), 'string')
1228 }
1229 return t.enum_autostr_call(expr, qtyp)
1230 }
1231 }
1232 }
1233 if clean_typ == 'string' {
1234 return expr
1235 }
1236 if is_ref || clean_typ in ['voidptr', 'byteptr', 'charptr'] {
1237 return t.make_call_typed('ptr_str', arr1(expr), 'string')
1238 }
1239 if clean_typ == 'IError' || clean_typ == 'builtin.IError' {
1240 return t.make_call_typed('IError.str', arr1(expr), 'string')
1241 }
1242 match clean_typ {
1243 'bool' {
1244 return t.make_call_typed('bool.str', arr1(expr), 'string')
1245 }
1246 'u8', 'byte', 'u16', 'u32', 'u64' {
1247 return t.make_call_typed('strconv__format_uint', arr2(expr, t.make_int_literal(10)),
1248 'string')
1249 }
1250 'int', 'i8', 'i16', 'i32', 'i64', 'isize', 'usize' {
1251 return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
1252 'string')
1253 }
1254 'f32' {
1255 return t.make_call_typed('strconv__f32_to_str_l', arr1(expr), 'string')
1256 }
1257 'f64' {
1258 return t.make_call_typed('strconv__f64_to_str_l', arr1(expr), 'string')
1259 }
1260 else {
1261 if clean_typ in t.enum_types {
1262 if method := t.enum_str_method_name(clean_typ) {
1263 return t.make_call_typed(method, arr1(expr), 'string')
1264 }
1265 return t.enum_autostr_call(expr, clean_typ)
1266 }
1267 mut qenum := clean_typ
1268 if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1269 && t.cur_module != 'builtin' {
1270 qenum = '${t.cur_module}.${clean_typ}'
1271 }
1272 if qenum in t.enum_types {
1273 if method := t.enum_str_method_name(qenum) {
1274 return t.make_call_typed(method, arr1(expr), 'string')
1275 }
1276 return t.enum_autostr_call(expr, qenum)
1277 }
1278 if clean_typ in t.structs || clean_typ in t.sum_types {
1279 mut qualified := clean_typ
1280 if !clean_typ.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
1281 && t.cur_module != 'builtin' {
1282 q := '${t.cur_module}.${clean_typ}'
1283 if q in t.structs || q in t.sum_types {
1284 qualified = q
1285 }
1286 }
1287 str_fn := '${c_name(qualified)}__str'
1288 known_str := str_fn in t.fn_ret_types
1289 || (!isnil(t.tc) && str_fn in t.tc.fn_ret_types)
1290 if known_str {
1291 return t.make_call_typed(str_fn, arr1(expr), 'string')
1292 }
1293 return t.make_string_literal('${qualified}{}')
1294 } else if t.is_fixed_array_type(clean_typ) {
1295 elem_type := fixed_array_elem_type(clean_typ)
1296 arr := t.fixed_array_value_to_array(expr, clean_typ, '[]${elem_type}')
1297 return t.wrap_string_conversion(arr, '[]${elem_type}')
1298 } else if clean_typ.len > 0 && clean_typ.starts_with('[]') {
1299 return t.lower_array_str(expr, clean_typ)
1300 } else if clean_typ == 'rune' {
1301 return t.make_call_typed('strconv__format_int', arr2(expr, t.make_int_literal(10)),
1302 'string')
1303 } else {
1304 return expr
1305 }
1306 }
1307 }
1308}
1309
1310// append_string builds `result = result + piece` using the runtime string concat helper.
1311// Using string__plus directly (instead of `+=`) keeps the synthesized node independent of
1312// type resolution for the freshly-introduced temp.
1313fn (mut t Transformer) append_string(result_name string, piece flat.NodeId) flat.NodeId {
1314 concat := t.make_call_typed('string__plus', arr2(t.make_ident(result_name), piece), 'string')
1315 return t.make_assign(t.make_ident(result_name), concat)
1316}
1317
1318// lower_array_str expands `${arr}` for a `[]T` into a runtime loop that formats each element
1319// via wrap_string_conversion, so nested arrays, structs with `str`, enums, etc. all recurse
1320// correctly. Produces `[e0, e1, ...]`; string elements are wrapped in single quotes to match V.
1321fn (mut t Transformer) lower_array_str(arr_expr flat.NodeId, base_type string) flat.NodeId {
1322 src := t.a.nodes[int(arr_expr)]
1323 elem_type := base_type[2..]
1324 base := t.stable_expr_for_reuse(arr_expr)
1325 mut prefix := []flat.NodeId{}
1326 t.drain_pending(mut prefix)
1327 result_name := t.new_temp('arr_str')
1328 idx_name := t.new_temp('arr_str_idx')
1329 prefix << t.make_decl_assign_typed(result_name, t.make_string_literal('['), 'string')
1330 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
1331 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
1332 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
1333 elem_name := t.new_temp('arr_str_it')
1334 elem_expr := t.array_get_value(base, t.make_ident(idx_name), elem_type)
1335 elem_decl := t.make_decl_assign_typed(elem_name, elem_expr, elem_type)
1336 mut loop_body := []flat.NodeId{}
1337 loop_body << elem_decl
1338 // `if idx > 0 { result = result + ', ' }`
1339 sep_cond := t.make_infix(.gt, t.make_ident(idx_name), t.make_int_literal(0))
1340 sep_stmt := t.append_string(result_name, t.make_string_literal(', '))
1341 loop_body << t.make_if(sep_cond, t.make_block(arr1(sep_stmt)), t.make_empty())
1342 // element text (recurses; may push its own statements for nested arrays/optionals)
1343 t.set_var_type(elem_name, elem_type)
1344 elem_str := t.wrap_string_conversion(t.make_ident(elem_name), elem_type)
1345 t.unset_var_type(elem_name)
1346 t.drain_pending(mut loop_body)
1347 if elem_type == 'string' {
1348 loop_body << t.append_string(result_name, t.make_string_literal("'"))
1349 loop_body << t.append_string(result_name, elem_str)
1350 loop_body << t.append_string(result_name, t.make_string_literal("'"))
1351 } else {
1352 loop_body << t.append_string(result_name, elem_str)
1353 }
1354 prefix << t.make_for_stmt(init, cond, post, loop_body, src)
1355 prefix << t.append_string(result_name, t.make_string_literal(']'))
1356 for stmt in prefix {
1357 t.pending_stmts << stmt
1358 }
1359 return t.make_ident(result_name)
1360}
1361
1362// wrap_optional_string_conversion transforms wrap optional string conversion data for transform.
1363fn (mut t Transformer) wrap_optional_string_conversion(expr flat.NodeId, typ string) flat.NodeId {
1364 opt_type := t.qualify_optional_type(typ)
1365 mut value_type := t.optional_base_type(opt_type)
1366 if value_type.len == 0 || value_type == 'void' {
1367 value_type = 'int'
1368 }
1369 opt_name := t.new_temp('opt_str')
1370 res_name := t.new_temp('opt_str_text')
1371 t.pending_stmts << t.make_decl_assign_typed(opt_name, expr, opt_type)
1372 t.pending_stmts << t.make_decl_assign_typed(res_name, t.make_string_literal('Option(none)'),
1373 'string')
1374 value := t.make_selector(t.make_ident(opt_name), 'value', value_type)
1375 mut value_str := t.wrap_string_conversion(value, value_type)
1376 if value_type == 'string' {
1377 value_str = t.string_plus(t.string_plus(t.make_string_literal("'"), value_str),
1378 t.make_string_literal("'"))
1379 }
1380 some_str := t.string_plus(t.string_plus(t.make_string_literal('Option('), value_str),
1381 t.make_string_literal(')'))
1382 assign_some := t.make_assign(t.make_ident(res_name), some_str)
1383 t.pending_stmts << t.make_if(t.make_selector(t.make_ident(opt_name), 'ok', 'bool'),
1384 t.make_block(arr1(assign_some)), t.make_empty())
1385 return t.make_ident(res_name)
1386}
1387
1388// string_plus supports string plus handling for Transformer.
1389fn (mut t Transformer) string_plus(left flat.NodeId, right flat.NodeId) flat.NodeId {
1390 return t.make_call_typed('string__plus', arr2(left, right), 'string')
1391}
1392
1393// is_flag_enum_type reports whether is flag enum type applies in transform.
1394fn (t &Transformer) is_flag_enum_type(typ string) bool {
1395 mut clean := typ
1396 if clean.starts_with('&') {
1397 clean = clean[1..]
1398 }
1399 if clean.len == 0 {
1400 return false
1401 }
1402 if !isnil(t.tc) {
1403 parsed := t.tc.parse_type(clean)
1404 if parsed is types.Enum {
1405 return parsed.is_flag
1406 }
1407 }
1408 return false
1409}
1410
1411// is_runtime_array_flags_selector reports is_runtime_array_flags_selector logic in transform.
1412fn (t &Transformer) is_runtime_array_flags_selector(id flat.NodeId) bool {
1413 if int(id) < 0 {
1414 return false
1415 }
1416 node := t.a.nodes[int(id)]
1417 if node.kind != .selector || node.value != 'flags' || node.children_count == 0 {
1418 return false
1419 }
1420 owner_id := t.a.child(&node, 0)
1421 owner_type := t.node_type(owner_id).trim_left('&')
1422 return owner_type.starts_with('[]') || owner_type == 'strings.Builder'
1423}
1424
1425// try_lower_flag_enum_stmt supports try lower flag enum stmt handling for Transformer.
1426fn (mut t Transformer) try_lower_flag_enum_stmt(call_id flat.NodeId) ?flat.NodeId {
1427 if int(call_id) < 0 {
1428 return none
1429 }
1430 call := t.a.nodes[int(call_id)]
1431 if call.kind != .call || call.children_count < 2 {
1432 return none
1433 }
1434 fn_id := t.a.children[call.children_start]
1435 fn_node := t.a.nodes[int(fn_id)]
1436 if fn_node.kind != .selector || fn_node.children_count == 0
1437 || fn_node.value !in ['set', 'clear'] {
1438 return none
1439 }
1440 base_id := t.a.children[fn_node.children_start]
1441 if t.is_runtime_array_flags_selector(base_id) {
1442 return none
1443 }
1444 base_type := t.node_type(base_id)
1445 if !t.is_flag_enum_type(base_type) {
1446 return none
1447 }
1448 base := t.transform_expr(base_id)
1449 arg := t.transform_expr(t.a.children[call.children_start + 1])
1450 if fn_node.value == 'set' {
1451 return t.make_assign_op(base, arg, .pipe_assign)
1452 }
1453 return t.make_assign_op(base, t.make_prefix(.bit_not, arg), .amp_assign)
1454}
1455
1456// try_lower_flag_enum_call supports try lower flag enum call handling for Transformer.
1457fn (mut t Transformer) try_lower_flag_enum_call(node flat.Node) ?flat.NodeId {
1458 if node.children_count == 1 {
1459 fn_id := t.a.children[node.children_start]
1460 fn_node := t.a.nodes[int(fn_id)]
1461 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'zero' {
1462 return none
1463 }
1464 base_id := t.a.children[fn_node.children_start]
1465 base_node := t.a.nodes[int(base_id)]
1466 if base_node.kind != .ident || !t.is_flag_enum_type(base_node.value) {
1467 return none
1468 }
1469 return t.make_cast(base_node.value, t.make_int_literal(0), base_node.value)
1470 }
1471 if node.children_count < 2 {
1472 return none
1473 }
1474 fn_id := t.a.children[node.children_start]
1475 fn_node := t.a.nodes[int(fn_id)]
1476 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value !in ['has', 'all'] {
1477 return none
1478 }
1479 base_id := t.a.children[fn_node.children_start]
1480 if t.is_runtime_array_flags_selector(base_id) {
1481 return none
1482 }
1483 base_type := t.node_type(base_id)
1484 if !t.is_flag_enum_type(base_type) {
1485 return none
1486 }
1487 base := t.transform_expr(base_id)
1488 arg_id := t.a.children[node.children_start + 1]
1489 arg := t.transform_expr(arg_id)
1490 masked := t.make_infix(.amp, base, arg)
1491 if fn_node.value == 'has' {
1492 return t.make_infix(.ne, masked, t.make_int_literal(0))
1493 }
1494 arg_copy := t.transform_expr(arg_id)
1495 return t.make_infix(.eq, masked, arg_copy)
1496}
1497
1498// try_lower_array_method_call supports try lower array method call handling for Transformer.
1499fn (mut t Transformer) try_lower_array_method_call(node flat.Node) ?flat.NodeId {
1500 if node.children_count == 0 {
1501 return none
1502 }
1503 fn_id := t.a.children[node.children_start]
1504 fn_node := t.a.nodes[int(fn_id)]
1505 if fn_node.kind != .selector || fn_node.children_count == 0 {
1506 return none
1507 }
1508 if fn_node.value !in ['clone', 'reverse', 'contains', 'index', 'join', 'any', 'all', 'count',
1509 'equals', 'prepend', 'insert', 'push_many'] {
1510 if fn_node.value !in ['filter', 'map', 'sort', 'sorted', 'sort_with_compare',
1511 'sorted_with_compare'] {
1512 return none
1513 }
1514 }
1515 base_id := t.a.children[fn_node.children_start]
1516 mut base_type := t.node_type(base_id)
1517 if !base_type.starts_with('[]') && !t.is_fixed_array_type(base_type) {
1518 base_node := t.a.nodes[int(base_id)]
1519 if base_node.kind in [.call, .selector, .as_expr] {
1520 new_base := t.transform_expr(base_id)
1521 new_base_type := t.node_type(new_base)
1522 if new_base_type.starts_with('[]') || t.is_fixed_array_type(new_base_type) {
1523 selector := t.make_selector(new_base, fn_node.value, '')
1524 mut children := []flat.NodeId{cap: int(node.children_count)}
1525 children << selector
1526 for i in 1 .. node.children_count {
1527 children << t.a.child(&node, i)
1528 }
1529 start := t.a.children.len
1530 for child in children {
1531 t.a.children << child
1532 }
1533 new_node := flat.Node{
1534 kind: .call
1535 children_start: start
1536 children_count: node.children_count
1537 pos: node.pos
1538 typ: node.typ
1539 }
1540 return t.try_lower_array_method_call(new_node)
1541 }
1542 }
1543 }
1544 clean_base_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
1545 if t.is_fixed_array_type(clean_base_type) {
1546 elem_type := fixed_array_elem_type(clean_base_type)
1547 array_type := '[]${elem_type}'
1548 tmp_name := t.new_temp('fixed_arr')
1549 t.pending_stmts << t.make_decl_assign_typed(tmp_name, t.fixed_array_value_to_array(base_id,
1550 clean_base_type, array_type), array_type)
1551 selector := t.make_selector(t.make_ident(tmp_name), fn_node.value, '')
1552 mut children := []flat.NodeId{cap: int(node.children_count)}
1553 children << selector
1554 for i in 1 .. node.children_count {
1555 children << t.a.child(&node, i)
1556 }
1557 start := t.a.children.len
1558 for child in children {
1559 t.a.children << child
1560 }
1561 new_node := flat.Node{
1562 kind: .call
1563 children_start: start
1564 children_count: node.children_count
1565 pos: node.pos
1566 typ: node.typ
1567 }
1568 return t.try_lower_array_method_call(new_node)
1569 }
1570 if !clean_base_type.starts_with('[]') {
1571 return none
1572 }
1573 elem_type := clean_base_type[2..]
1574 if fn_node.value == 'prepend' {
1575 return t.lower_array_prepend_call(node, fn_node, base_type, elem_type)
1576 }
1577 if fn_node.value == 'insert' {
1578 return t.lower_array_insert_call(node, fn_node, base_type, elem_type)
1579 }
1580 if fn_node.value == 'push_many' {
1581 return t.lower_array_push_many_call(node, fn_node, base_type, elem_type)
1582 }
1583 if fn_node.value == 'contains' {
1584 method_name := t.resolve_receiver_method_name(base_id, fn_node.value)
1585 if method_name.len > 0 {
1586 args := t.transform_receiver_method_args(node, base_id, method_name)
1587 ret_type := t.receiver_method_return_type(method_name, node.typ)
1588 return t.make_call_typed(method_name, args, ret_type)
1589 }
1590 }
1591 match fn_node.value {
1592 'filter' {
1593 return t.lower_array_filter_call(node, fn_node, clean_base_type)
1594 }
1595 'map' {
1596 return t.lower_array_map_call(node, fn_node, clean_base_type)
1597 }
1598 'sort' {
1599 return t.lower_array_sort_call(node, fn_node, base_type)
1600 }
1601 'sorted' {
1602 return t.lower_array_sorted_call(node, fn_node, clean_base_type)
1603 }
1604 'sort_with_compare' {
1605 return t.lower_array_sort_with_compare_call(node, fn_node, base_type)
1606 }
1607 'sorted_with_compare' {
1608 return t.lower_array_sorted_with_compare_call(node, fn_node, clean_base_type)
1609 }
1610 'any', 'all' {
1611 return t.lower_array_any_all_call(node, fn_node, clean_base_type, fn_node.value)
1612 }
1613 'count' {
1614 return t.lower_array_count_call(node, fn_node, clean_base_type)
1615 }
1616 'equals' {
1617 if node.children_count < 2 {
1618 return none
1619 }
1620 method_name := t.resolve_receiver_method_name(base_id, 'equals')
1621 if method_name.len > 0 {
1622 args := t.transform_receiver_method_args(node, base_id, method_name)
1623 return t.make_call_typed(method_name, args, 'bool')
1624 }
1625 receiver := t.transform_expr(base_id)
1626 arg := t.transform_expr(t.a.children[node.children_start + 1])
1627 if elem_type == 'string' {
1628 return t.make_call_typed('array_eq_string', arr2(receiver, arg), 'bool')
1629 }
1630 return t.make_call_typed('array_eq_raw', arr3(receiver, arg,
1631 t.make_sizeof_type(elem_type)), 'bool')
1632 }
1633 else {}
1634 }
1635
1636 match fn_node.value {
1637 'clone' {
1638 receiver := t.transform_expr(base_id)
1639 return t.make_call_typed('array__clone', arr1(t.runtime_addr(receiver, base_type)),
1640 clean_base_type)
1641 }
1642 'reverse' {
1643 receiver := t.transform_expr(base_id)
1644 return t.make_call_typed('array__reverse', arr1(receiver), clean_base_type)
1645 }
1646 'contains' {
1647 if node.children_count < 2 {
1648 return none
1649 }
1650 arg_id := t.a.children[node.children_start + 1]
1651 if lowered := t.lower_array_membership_expr(base_id, arg_id, base_type, true, node) {
1652 return lowered
1653 }
1654 receiver := t.transform_expr(base_id)
1655 arg := t.transform_expr(arg_id)
1656 fn_name := if elem_type == 'string' {
1657 'array_contains_string'
1658 } else {
1659 'array_contains_int'
1660 }
1661 return t.make_call_typed(fn_name, arr2(receiver, arg), 'bool')
1662 }
1663 'index' {
1664 if node.children_count < 2 {
1665 return none
1666 }
1667 arg_id := t.a.children[node.children_start + 1]
1668 if lowered := t.lower_array_index_expr(base_id, arg_id, base_type, true, node) {
1669 return lowered
1670 }
1671 receiver := t.transform_expr(base_id)
1672 arg := t.transform_expr(arg_id)
1673 fn_name := if elem_type == 'string' { 'array_index_string' } else { 'array_index_int' }
1674 return t.make_call_typed(fn_name, arr2(receiver, arg), 'int')
1675 }
1676 'join' {
1677 if node.children_count < 2 {
1678 return none
1679 }
1680 receiver := t.transform_expr(base_id)
1681 arg := t.transform_expr(t.a.children[node.children_start + 1])
1682 return t.make_call_typed('Array_string__join', arr2(receiver, arg), 'string')
1683 }
1684 else {
1685 return none
1686 }
1687 }
1688}
1689
1690// try_lower_map_method_call supports try lower map method call handling for Transformer.
1691fn (mut t Transformer) try_lower_map_method_call(node flat.Node) ?flat.NodeId {
1692 if node.children_count == 0 {
1693 return none
1694 }
1695 fn_id := t.a.children[node.children_start]
1696 fn_node := t.a.nodes[int(fn_id)]
1697 if fn_node.kind != .selector || fn_node.children_count == 0
1698 || fn_node.value !in ['keys', 'values'] {
1699 return none
1700 }
1701 base_id := t.a.children[fn_node.children_start]
1702 base_type := t.node_type(base_id)
1703 clean_type := t.clean_map_type(base_type)
1704 if !clean_type.starts_with('map[') {
1705 return none
1706 }
1707 elem_type := if fn_node.value == 'keys' {
1708 t.map_key_type(clean_type)
1709 } else {
1710 t.map_value_type(clean_type)
1711 }
1712 if elem_type.len == 0 {
1713 return none
1714 }
1715 base := t.transform_expr(base_id)
1716 return t.make_call_typed('map__${fn_node.value}', arr1(t.runtime_addr(base, base_type)),
1717 '[]${elem_type}')
1718}
1719
1720// try_lower_move_method_call supports try lower move method call handling for Transformer.
1721fn (mut t Transformer) try_lower_move_method_call(node flat.Node) ?flat.NodeId {
1722 if node.children_count != 1 {
1723 return none
1724 }
1725 fn_id := t.a.children[node.children_start]
1726 fn_node := t.a.nodes[int(fn_id)]
1727 if fn_node.kind != .selector || fn_node.children_count == 0 || fn_node.value != 'move' {
1728 return none
1729 }
1730 base_id := t.a.child(&fn_node, 0)
1731 base_type := t.node_type(base_id)
1732 clean_array_type := t.membership_container_type(base_type)
1733 clean_map_type := t.clean_map_type(base_type)
1734 if clean_array_type.starts_with('[]') || clean_map_type.starts_with('map[') {
1735 return t.transform_expr(base_id)
1736 }
1737 return none
1738}
1739
1740// lift_fn_literal supports lift fn literal handling for Transformer.
1741fn (mut t Transformer) lift_fn_literal(_id flat.NodeId, node flat.Node) flat.NodeId {
1742 name := t.new_temp('anon_fn')
1743 mut param_types := []types.Type{}
1744 mut param_ids := []flat.NodeId{}
1745 mut param_names := []string{}
1746 mut capture_names := []string{}
1747 mut capture_types := map[string]string{}
1748 mut body_ids := []flat.NodeId{}
1749 for i in 0 .. node.children_count {
1750 child_id := t.a.child(&node, i)
1751 child := t.a.nodes[int(child_id)]
1752 if child.kind == .param {
1753 param_ids << child_id
1754 if child.value.len > 0 {
1755 param_names << child.value
1756 }
1757 if !isnil(t.tc) {
1758 param_types << t.tc.parse_type(child.typ)
1759 }
1760 } else if child.kind == .ident {
1761 if child.value.len > 0 && child.value !in capture_names {
1762 mut capture_type := t.var_type(child.value)
1763 if capture_type.len == 0 {
1764 capture_type = t.node_type(child_id)
1765 }
1766 if capture_type.len == 0 && !isnil(t.tc) {
1767 if typ := t.tc.expr_type(child_id) {
1768 capture_type = typ.name()
1769 }
1770 }
1771 if capture_type.len == 0 || capture_type == 'unknown' {
1772 capture_type = 'int'
1773 }
1774 capture_names << child.value
1775 capture_types[child.value] = capture_type
1776 }
1777 } else {
1778 body_ids << child_id
1779 }
1780 }
1781 ret_type := if node.typ.len > 0 { node.typ } else { 'void' }
1782 saved_fn_name := t.cur_fn_name
1783 saved_ret_type := t.cur_fn_ret_type
1784 saved_vars := t.var_types.clone()
1785 t.cur_fn_name = name
1786 t.cur_fn_ret_type = ret_type
1787 t.reset_var_types()
1788 for param_id in param_ids {
1789 param := t.a.nodes[int(param_id)]
1790 if param.value.len > 0 && param.typ.len > 0 {
1791 t.set_var_type(param.value, param.typ)
1792 }
1793 }
1794 mut lifted_body := []flat.NodeId{cap: capture_names.len + body_ids.len}
1795 for capture_name in capture_names {
1796 if capture_name in param_names {
1797 continue
1798 }
1799 capture_type := capture_types[capture_name] or { continue }
1800 t.set_var_type(capture_name, capture_type)
1801 lifted_body << t.make_decl_assign_typed(capture_name, t.zero_value_for_type(capture_type),
1802 capture_type)
1803 }
1804 for body_id in body_ids {
1805 lifted_body << body_id
1806 }
1807 new_body := t.transform_stmts(lifted_body)
1808 t.var_types = saved_vars
1809 t.cur_fn_name = saved_fn_name
1810 t.cur_fn_ret_type = saved_ret_type
1811 mut all_ids := []flat.NodeId{cap: param_ids.len + new_body.len}
1812 for param_id in param_ids {
1813 all_ids << param_id
1814 }
1815 for body_id in new_body {
1816 all_ids << body_id
1817 }
1818 if t.cur_module.len > 0 {
1819 t.a.add_node(flat.Node{
1820 kind: .module_decl
1821 value: t.cur_module
1822 })
1823 }
1824 start := t.a.children.len
1825 for child_id in all_ids {
1826 t.a.children << child_id
1827 }
1828 t.a.add_node(flat.Node{
1829 kind: .fn_decl
1830 value: name
1831 typ: ret_type
1832 children_start: start
1833 children_count: flat.child_count(all_ids.len)
1834 })
1835 if !isnil(t.tc) {
1836 ret := t.tc.parse_type(ret_type)
1837 t.tc.fn_ret_types[name] = ret
1838 t.tc.fn_param_types[name] = param_types.clone()
1839 t.tc.fn_variadic[name] = false
1840 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1841 qname := '${t.cur_module}.${name}'
1842 t.tc.fn_ret_types[qname] = ret
1843 t.tc.fn_param_types[qname] = param_types.clone()
1844 t.tc.fn_variadic[qname] = false
1845 }
1846 }
1847 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1848 return t.make_ident('${t.cur_module}.${name}')
1849 }
1850 return t.make_ident(name)
1851}
1852
1853// try_lower_builtin_call checks if a call is to a builtin that needs special lowering.
1854// Returns none for most calls so the caller falls through to generic call transform.
1855fn (mut t Transformer) try_lower_builtin_call(_id flat.NodeId, node flat.Node) ?flat.NodeId {
1856 if node.children_count == 0 {
1857 return none
1858 }
1859 if cast_call := t.try_lower_primitive_cast_call(node) {
1860 return cast_call
1861 }
1862 if flag_call := t.try_lower_flag_enum_call(node) {
1863 return flag_call
1864 }
1865 if flag_default := t.try_lower_flag_default_value_call(node) {
1866 return flag_default
1867 }
1868 if pool_call := t.try_lower_pool_generic_method_call(node) {
1869 return pool_call
1870 }
1871 if move_call := t.try_lower_move_method_call(node) {
1872 return move_call
1873 }
1874 if map_call := t.try_lower_map_method_call(node) {
1875 return map_call
1876 }
1877 if array_call := t.try_lower_array_method_call(node) {
1878 return array_call
1879 }
1880 if type_name_call := t.try_lower_sum_type_name_method_call(node) {
1881 return type_name_call
1882 }
1883 if receiver_call := t.try_lower_receiver_method_call(_id, node) {
1884 return receiver_call
1885 }
1886 if string_call := t.try_lower_string_method_call(node) {
1887 return string_call
1888 }
1889 fn_id := t.a.children[node.children_start]
1890 if int(fn_id) < 0 {
1891 return none
1892 }
1893 fn_node := t.a.nodes[int(fn_id)]
1894 if fn_node.kind != .ident {
1895 return none
1896 }
1897 name := fn_node.value
1898 match name {
1899 'copy' {
1900 return t.try_lower_copy_call(node)
1901 }
1902 'println', 'eprintln', 'print', 'eprint' {
1903 if node.children_count < 2 {
1904 return t.transform_call_args(_id, node)
1905 }
1906 arg := t.stringify_expr(t.a.child(&node, 1))
1907 return t.make_call(name, arr1(arg))
1908 }
1909 'panic' {
1910 if node.children_count == 2 {
1911 arg_id := t.a.child(&node, 1)
1912 arg_type := t.node_type(arg_id)
1913 if arg_type == 'IError' {
1914 arg := t.transform_expr(arg_id)
1915 return t.make_call('panic',
1916 arr1(t.make_method_call(arg, 'str', []flat.NodeId{})))
1917 }
1918 }
1919 return none
1920 }
1921 'sizeof' {
1922 return none
1923 }
1924 'typeof' {
1925 return none
1926 }
1927 else {
1928 return none
1929 }
1930 }
1931}
1932
1933fn (mut t Transformer) try_lower_copy_call(node flat.Node) ?flat.NodeId {
1934 if node.children_count != 3 {
1935 return none
1936 }
1937 dst_arg_id := t.a.child(&node, 1)
1938 src_arg_id := t.a.child(&node, 2)
1939 dst_id := t.copy_mut_arg_value(dst_arg_id)
1940 src := t.transform_expr_for_type(src_arg_id, '[]u8')
1941 if t.copy_destination_is_range(dst_id) {
1942 slice := t.transform_expr(dst_id)
1943 tmp_name := t.new_temp('copy_dst')
1944 t.pending_stmts << t.make_decl_assign_typed(tmp_name, slice, '[]u8')
1945 return t.make_call_typed('copy', arr2(t.make_prefix(.amp, t.make_ident(tmp_name)), src),
1946 'int')
1947 }
1948 dst := t.make_prefix(.amp, t.transform_expr(dst_id))
1949 return t.make_call_typed('copy', arr2(dst, src), 'int')
1950}
1951
1952fn (t &Transformer) copy_mut_arg_value(id flat.NodeId) flat.NodeId {
1953 if int(id) < 0 {
1954 return id
1955 }
1956 node := t.a.nodes[int(id)]
1957 if node.kind == .paren && node.children_count > 0 {
1958 return t.copy_mut_arg_value(t.a.child(&node, 0))
1959 }
1960 if node.kind == .prefix && node.op == .amp && node.children_count > 0 {
1961 return t.copy_mut_arg_value(t.a.child(&node, 0))
1962 }
1963 return id
1964}
1965
1966fn (t &Transformer) copy_destination_is_range(id flat.NodeId) bool {
1967 if int(id) < 0 {
1968 return false
1969 }
1970 node := t.a.nodes[int(id)]
1971 if node.kind != .index {
1972 return false
1973 }
1974 if node.value == 'range' {
1975 return true
1976 }
1977 if node.children_count > 1 {
1978 index := t.a.child_node(&node, 1)
1979 return index.kind == .range
1980 }
1981 return false
1982}
1983
1984const primitive_cast_type_names = ['bool', 'int', 'i8', 'i16', 'i32', 'i64', 'isize', 'u8', 'byte',
1985 'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'rune', 'char']
1986
1987// try_lower_primitive_cast_call supports try lower primitive cast call handling for Transformer.
1988fn (mut t Transformer) try_lower_primitive_cast_call(node flat.Node) ?flat.NodeId {
1989 if node.children_count != 2 {
1990 return none
1991 }
1992 fn_id := t.a.child(&node, 0)
1993 if int(fn_id) < 0 {
1994 return none
1995 }
1996 fn_node := t.a.nodes[int(fn_id)]
1997 if fn_node.kind != .ident || fn_node.value !in primitive_cast_type_names {
1998 return none
1999 }
2000 arg_id := t.a.child(&node, 1)
2001 return t.make_cast(fn_node.value, t.transform_expr(arg_id), fn_node.value)
2002}
2003
2004// try_lower_flag_default_value_call
2005// supports helper handling in transform.
2006fn (mut t Transformer) try_lower_flag_default_value_call(node flat.Node) ?flat.NodeId {
2007 if node.children_count != 2 {
2008 return none
2009 }
2010 fn_id := t.a.child(&node, 0)
2011 fn_node := t.a.nodes[int(fn_id)]
2012 mut name := ''
2013 if fn_node.kind == .ident {
2014 name = fn_node.value
2015 } else if fn_node.kind == .selector {
2016 name = fn_node.value
2017 }
2018 if name != 'flag_default_value' {
2019 return none
2020 }
2021 arg_id := t.a.child(&node, 1)
2022 arg := t.transform_expr(arg_id)
2023 mut arg_type := t.resolve_expr_type(arg_id)
2024 if arg_type.len == 0 {
2025 arg_type = t.reliable_stringify_type(arg_id)
2026 }
2027 if arg_type.len == 0 {
2028 arg_type = t.node_type(arg)
2029 }
2030 if t.normalize_type_alias(arg_type) == 'string' {
2031 escaped := t.make_call_typed('escape_default_string', arr1(arg), 'string')
2032 return t.string_plus(t.string_plus(t.make_string_literal('"'), escaped),
2033 t.make_string_literal('"'))
2034 }
2035 return t.wrap_string_conversion(arg, arg_type)
2036}
2037
2038// try_lower_sum_type_name_method_call supports try_lower_sum_type_name_method_call handling.
2039fn (mut t Transformer) try_lower_sum_type_name_method_call(node flat.Node) ?flat.NodeId {
2040 if node.children_count != 1 {
2041 return none
2042 }
2043 fn_id := t.a.child(&node, 0)
2044 fn_node := t.a.nodes[int(fn_id)]
2045 if fn_node.kind != .selector || fn_node.value != 'type_name' || fn_node.children_count == 0 {
2046 return none
2047 }
2048 base_id := t.a.child(&fn_node, 0)
2049 mut base_type := t.node_type(base_id)
2050 if base_type.len == 0 {
2051 base_type = t.lvalue_type(base_id)
2052 }
2053 clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2054 resolved_sum := t.resolve_sum_name(clean_type)
2055 variants := t.sum_types[resolved_sum] or { return none }
2056 base := t.stable_transformed_expr_for_reuse(t.transform_expr(base_id), base_type, 'sum_type')
2057 tag := t.make_selector_op(base, 'typ', 'int', if base_type.starts_with('&') {
2058 .arrow
2059 } else {
2060 .dot
2061 })
2062 return t.build_sum_type_name_chain(tag, resolved_sum, variants, 0)
2063}
2064
2065// build_sum_type_name_chain builds sum type name chain data for transform.
2066fn (mut t Transformer) build_sum_type_name_chain(tag flat.NodeId, sum_name string, variants []string, idx int) flat.NodeId {
2067 if idx >= variants.len {
2068 return t.make_string_literal('')
2069 }
2070 variant := variants[idx]
2071 display := if variant.contains('.') { variant.all_after_last('.') } else { variant }
2072 cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(sum_name, variant)))
2073 then_block := t.make_block(arr1(t.make_expr_stmt(t.make_string_literal(display))))
2074 else_expr := t.build_sum_type_name_chain(tag, sum_name, variants, idx + 1)
2075 else_block := t.make_block(arr1(t.make_expr_stmt(else_expr)))
2076 start := t.a.children.len
2077 t.a.children << cond
2078 t.a.children << then_block
2079 t.a.children << else_block
2080 return t.a.add_node(flat.Node{
2081 kind: .if_expr
2082 children_start: start
2083 children_count: 3
2084 typ: 'string'
2085 })
2086}
2087
2088// try_lower_pool_generic_method_call
2089// supports helper handling in transform.
2090fn (mut t Transformer) try_lower_pool_generic_method_call(node flat.Node) ?flat.NodeId {
2091 if node.children_count == 0 || node.value.len == 0 {
2092 return none
2093 }
2094 fn_id := t.a.child(&node, 0)
2095 fn_node := t.a.nodes[int(fn_id)]
2096 if fn_node.kind != .selector || fn_node.children_count == 0 {
2097 return none
2098 }
2099 method := fn_node.value
2100 if method !in ['get_item', 'get_results', 'get_results_ref'] {
2101 return none
2102 }
2103 base_id := t.a.child(&fn_node, 0)
2104 base_type := t.lvalue_type(base_id)
2105 if !is_pool_processor_type(base_type) {
2106 return none
2107 }
2108 elem_type := node.value
2109 base := t.stable_expr_for_reuse(base_id)
2110 mut prefix := []flat.NodeId{}
2111 t.drain_pending(mut prefix)
2112 if method == 'get_item' {
2113 if node.children_count < 2 {
2114 return none
2115 }
2116 idx := t.transform_expr(t.a.child(&node, 1))
2117 t.drain_pending(mut prefix)
2118 items := t.pool_processor_field(base, base_type, 'items', '[]voidptr')
2119 item := t.make_index(items, idx, 'voidptr')
2120 cast := t.make_cast('&${elem_type}', item, '&${elem_type}')
2121 value := t.make_prefix(.mul, cast)
2122 t.a.nodes[int(value)].typ = elem_type
2123 for stmt in prefix {
2124 t.pending_stmts << stmt
2125 }
2126 return value
2127 }
2128 result_name := t.new_temp('pool_results')
2129 idx_name := t.new_temp('pool_idx')
2130 results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2131 results_len := t.make_selector(results, 'len', 'int')
2132 is_ref_results := method == 'get_results_ref'
2133 out_elem_type := if is_ref_results { '&${elem_type}' } else { elem_type }
2134 out_type := '[]${out_elem_type}'
2135 prefix << t.make_decl_assign_typed(result_name, t.make_array_new_call(out_elem_type,
2136 t.make_int_literal(0), results_len), out_type)
2137 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
2138 cond_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2139 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(cond_results, 'len', 'int'))
2140 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
2141 body_results := t.pool_processor_field(base, base_type, 'results', '[]voidptr')
2142 item := t.make_index(body_results, t.make_ident(idx_name), 'voidptr')
2143 value := if is_ref_results {
2144 t.make_cast('&${elem_type}', item, '&${elem_type}')
2145 } else {
2146 t.make_prefix(.mul, t.make_cast('&${elem_type}', item, '&${elem_type}'))
2147 }
2148 t.a.nodes[int(value)].typ = out_elem_type
2149 value_name := t.new_temp('pool_result')
2150 value_decl := t.make_decl_assign_typed(value_name, value, out_elem_type)
2151 push_call := t.make_call_typed('array_push', arr2(t.make_prefix(.amp, t.make_ident(result_name)), t.make_prefix(.amp,
2152 t.make_ident(value_name))), 'void')
2153 loop_body := [value_decl, t.make_expr_stmt(push_call)]
2154 prefix << t.make_for_stmt(init, cond, post, loop_body, node)
2155 for stmt in prefix {
2156 t.pending_stmts << stmt
2157 }
2158 return t.make_ident(result_name)
2159}
2160
2161// is_pool_processor_type reports whether is pool processor type applies in transform.
2162fn is_pool_processor_type(typ string) bool {
2163 mut clean := typ
2164 if clean.starts_with('&') {
2165 clean = clean[1..]
2166 }
2167 if clean.starts_with('mut ') {
2168 clean = clean[4..]
2169 }
2170 return clean == 'PoolProcessor' || clean.ends_with('.PoolProcessor')
2171}
2172
2173// pool_processor_field supports pool processor field handling for Transformer.
2174fn (mut t Transformer) pool_processor_field(base flat.NodeId, base_type string, field string, typ string) flat.NodeId {
2175 op := if base_type.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
2176 return t.make_selector_op(base, field, typ, op)
2177}
2178
2179// try_lower_receiver_method_call supports try lower receiver method call handling for Transformer.
2180fn (mut t Transformer) try_lower_receiver_method_call(id flat.NodeId, node flat.Node) ?flat.NodeId {
2181 if node.children_count == 0 {
2182 return none
2183 }
2184 fn_id := t.a.children[node.children_start]
2185 fn_node := t.a.nodes[int(fn_id)]
2186 if fn_node.kind != .selector || fn_node.children_count == 0 {
2187 return none
2188 }
2189 method := fn_node.value
2190 base_id := t.a.child(&fn_node, 0)
2191 base_node := t.a.nodes[int(base_id)]
2192 // `Type.fn(...)` / `module.Type.fn(...)` is a static associated function, not a method:
2193 // the base names a type, so it must not be lowered into `fn(receiver, ...)`.
2194 if _ := t.static_assoc_fn_name(base_id, method) {
2195 return none
2196 }
2197 mut base_type := if base_node.kind in [.selector, .index] {
2198 t.lvalue_type(base_id)
2199 } else {
2200 t.node_type(base_id)
2201 }
2202 if base_type.len == 0 {
2203 base_type = t.lvalue_type(base_id)
2204 }
2205 if base_type.starts_with('&') {
2206 base_type = base_type[1..]
2207 }
2208 if base_type.len == 0 {
2209 return none
2210 }
2211 mut builtin_base_type := t.normalize_type_alias(base_type)
2212 if builtin_base_type == 'byte' {
2213 builtin_base_type = 'u8'
2214 }
2215 if base_type == '[]rune' && method == 'string' {
2216 return t.make_call_typed('Array_rune__string', arr1(t.transform_expr(base_id)), 'string')
2217 }
2218 if method == 'str' && t.is_builder_receiver(base_id, base_type) {
2219 method_name := 'strings.Builder.str'
2220 args := t.transform_receiver_method_args(node, base_id, method_name)
2221 return t.make_call_typed(method_name, args, 'string')
2222 }
2223 if base_type.starts_with('[]') && method == 'str' {
2224 return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
2225 }
2226 if method == 'str' {
2227 if t.is_enum_stringify_type(base_type) {
2228 return none
2229 }
2230 return t.wrap_string_conversion(t.transform_expr(base_id), base_type)
2231 }
2232 if base_type == '[]u8' || base_type == '[]byte' {
2233 if method == 'bytestr' {
2234 return t.make_call_typed('Array_u8__bytestr', arr1(t.transform_expr(base_id)), 'string')
2235 }
2236 if method == 'hex' {
2237 return t.make_call_typed('Array_u8__hex', arr1(t.transform_expr(base_id)), 'string')
2238 }
2239 }
2240 if t.is_builder_receiver(base_id, base_type) {
2241 for method_name in ['strings.Builder.${method}', 'Builder.${method}'] {
2242 if t.is_known_fn_name(method_name) {
2243 args := t.transform_receiver_method_args(node, base_id, method_name)
2244 ret_type := t.receiver_method_return_type(method_name, node.typ)
2245 return t.make_call_typed(method_name, args, ret_type)
2246 }
2247 }
2248 }
2249 if builtin_base_type == 'u8' && method in ['is_space', 'is_digit', 'is_hex_digit', 'is_letter'] {
2250 return t.make_call_typed('u8__${method}', arr1(t.transform_expr(base_id)), 'bool')
2251 }
2252 if builtin_base_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'int', 'u64', 'i64', 'rune']
2253 && method in ['hex', 'hex_full'] {
2254 return t.make_call_typed('${builtin_base_type}__${method}',
2255 arr1(t.transform_expr(base_id)), 'string')
2256 }
2257 if base_type.starts_with('[]') || base_type.starts_with('map[') {
2258 if base_type.starts_with('[]') {
2259 return none
2260 }
2261 }
2262 method_name := t.resolve_receiver_method_name(base_id, method)
2263 if method_name.len > 0 {
2264 args := t.transform_receiver_method_args(node, base_id, method_name)
2265 ret_type := t.receiver_method_return_type(method_name, node.typ)
2266 return t.make_call_typed(method_name, args, ret_type)
2267 }
2268 if !isnil(t.tc) {
2269 if resolved_method := t.tc.resolved_call_name(id) {
2270 if t.is_known_fn_name(resolved_method) {
2271 params := t.call_param_types(resolved_method)
2272 if !t.resolved_call_uses_receiver_type(base_id, base_type, params) {
2273 return none
2274 }
2275 args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
2276 resolved_method), resolved_method)
2277 ret_type := t.receiver_method_return_type(resolved_method, node.typ)
2278 return t.make_call_typed(resolved_method, args, ret_type)
2279 }
2280 }
2281 }
2282 if sum_method := t.resolve_smartcast_sum_receiver_method(base_id, method) {
2283 args := t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
2284 sum_method), sum_method)
2285 ret_type := t.receiver_method_return_type(sum_method, node.typ)
2286 return t.make_call_typed(sum_method, args, ret_type)
2287 }
2288 return none
2289}
2290
2291// is_builder_receiver reports whether is builder receiver applies in transform.
2292fn (t &Transformer) is_builder_receiver(base_id flat.NodeId, base_type string) bool {
2293 if is_builder_type_name(base_type) {
2294 return true
2295 }
2296 if raw_type := t.raw_var_type_for_expr(base_id) {
2297 return is_builder_type_name(raw_type)
2298 }
2299 if raw_field_type := t.raw_selector_field_type(base_id) {
2300 return is_builder_type_name(raw_field_type)
2301 }
2302 return false
2303}
2304
2305// raw_selector_field_type supports raw selector field type handling for Transformer.
2306fn (t &Transformer) raw_selector_field_type(id flat.NodeId) ?string {
2307 if int(id) < 0 {
2308 return none
2309 }
2310 node := t.a.nodes[int(id)]
2311 if node.kind != .selector || node.children_count == 0 {
2312 return none
2313 }
2314 base_id := t.a.child(&node, 0)
2315 mut base_type := t.original_expr_type(base_id)
2316 if base_type.len == 0 {
2317 base_type = t.node_type(base_id)
2318 }
2319 if base_type.starts_with('&') {
2320 base_type = base_type[1..]
2321 }
2322 return t.lookup_struct_field_raw_type(base_type, node.value)
2323}
2324
2325// is_builder_type_name reports whether is builder type name applies in transform.
2326fn is_builder_type_name(typ string) bool {
2327 mut clean := typ
2328 if clean.starts_with('&') {
2329 clean = clean[1..]
2330 }
2331 return clean == 'strings.Builder' || clean == 'Builder'
2332}
2333
2334// resolved_call_uses_receiver_type
2335// supports helper handling in transform.
2336fn (t &Transformer) resolved_call_uses_receiver_type(base_id flat.NodeId, receiver_type string, params []types.Type) bool {
2337 if params.len == 0 {
2338 return false
2339 }
2340 mut param_type := params[0].name()
2341 if param_type.starts_with('&') {
2342 param_type = param_type[1..]
2343 }
2344 if sc := t.find_smartcast(t.expr_key(base_id)) {
2345 sc_type := t.trim_pointer_type(t.smartcast_target_type(sc))
2346 if sc_type.len > 0 && t.normalize_type_alias(sc_type) == t.normalize_type_alias(param_type) {
2347 return true
2348 }
2349 sc_sum := t.trim_pointer_type(t.resolve_sum_name(sc.sum_type_name))
2350 if sc_sum.len > 0 && t.normalize_type_alias(sc_sum) == t.normalize_type_alias(param_type) {
2351 return true
2352 }
2353 if t.is_sum_type_name(param_type) {
2354 for parent in t.sum_type_parents_for_variant(sc.variant_name) {
2355 if t.normalize_type_alias(parent) == t.normalize_type_alias(param_type) {
2356 return true
2357 }
2358 }
2359 }
2360 }
2361 mut base_type := receiver_type
2362 if base_type.starts_with('&') {
2363 base_type = base_type[1..]
2364 }
2365 if base_type.len == 0 {
2366 return true
2367 }
2368 if t.normalize_type_alias(base_type) == t.normalize_type_alias(param_type) {
2369 return true
2370 }
2371 if _ := t.embedded_receiver_path(base_type, param_type) {
2372 return true
2373 }
2374 return false
2375}
2376
2377// receiver_base_for_resolved_method
2378// supports helper handling in transform.
2379fn (mut t Transformer) receiver_base_for_resolved_method(base_id flat.NodeId, method_name string) flat.NodeId {
2380 if embedded_base := t.embedded_receiver_base(base_id, method_name) {
2381 return embedded_base
2382 }
2383 key := t.expr_key(base_id)
2384 sc := t.find_smartcast(key) or { return t.transform_expr(base_id) }
2385 params := t.call_param_types(method_name)
2386 if params.len == 0 {
2387 return t.transform_expr(base_id)
2388 }
2389 mut param_type := params[0].name()
2390 if param_type.starts_with('&') {
2391 param_type = param_type[1..]
2392 }
2393 target_type := t.trim_pointer_type(t.smartcast_target_type(sc))
2394 if target_type.len > 0
2395 && t.normalize_type_alias(param_type) == t.normalize_type_alias(target_type) {
2396 return t.apply_smartcast_contexts(t.make_plain_expr_for_smartcast(base_id),
2397 t.original_expr_type(base_id), t.smartcasts_for(key))
2398 }
2399 method_receiver := method_name.all_before_last('.')
2400 if method_receiver.len > 0 && t.is_sum_type_name(method_receiver)
2401 && t.normalize_type_alias(param_type) == t.normalize_type_alias(method_receiver) {
2402 return t.make_plain_expr_for_smartcast(base_id)
2403 }
2404 mut sum_type := t.resolve_sum_name(sc.sum_type_name)
2405 if sum_type.starts_with('&') {
2406 sum_type = sum_type[1..]
2407 }
2408 if sum_type.len > 0 && t.normalize_type_alias(param_type) == t.normalize_type_alias(sum_type) {
2409 return t.make_plain_expr_for_smartcast(base_id)
2410 }
2411 original_type := t.trim_pointer_type(t.original_expr_type(base_id))
2412 if original_type.len > 0
2413 && t.normalize_type_alias(param_type) == t.normalize_type_alias(original_type) {
2414 return t.make_plain_expr_for_smartcast(base_id)
2415 }
2416 return t.transform_expr(base_id)
2417}
2418
2419fn (mut t Transformer) embedded_receiver_base(base_id flat.NodeId, method_name string) ?flat.NodeId {
2420 params := t.call_param_types(method_name)
2421 if params.len == 0 {
2422 return none
2423 }
2424 mut param_type := params[0].name()
2425 if param_type.starts_with('&') {
2426 param_type = param_type[1..]
2427 }
2428 mut base_type := t.node_type(base_id)
2429 if base_type.len == 0 {
2430 base_type = t.lvalue_type(base_id)
2431 }
2432 mut is_ptr := false
2433 if base_type.starts_with('&') {
2434 is_ptr = true
2435 base_type = base_type[1..]
2436 }
2437 path := t.embedded_receiver_path(base_type, param_type) or { return none }
2438 mut base := t.transform_expr(base_id)
2439 mut current_is_ptr := is_ptr
2440 for field in path {
2441 op := if current_is_ptr { flat.Op.arrow } else { flat.Op.dot }
2442 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
2443 base = t.make_selector_op(base, field.name, field_type, op)
2444 current_is_ptr = field_type.starts_with('&')
2445 }
2446 return base
2447}
2448
2449fn (t &Transformer) embedded_receiver_field(base_type string, receiver_type string) ?FieldInfo {
2450 path := t.embedded_receiver_path(base_type, receiver_type) or { return none }
2451 if path.len == 0 {
2452 return none
2453 }
2454 return path[0]
2455}
2456
2457fn (t &Transformer) embedded_receiver_path(base_type string, receiver_type string) ?[]FieldInfo {
2458 if base_type.len == 0 || receiver_type.len == 0 {
2459 return none
2460 }
2461 mut lookup_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2462 if lookup_type !in t.structs && lookup_type.contains('.') {
2463 short_type := lookup_type.all_after_last('.')
2464 if short_type in t.structs {
2465 lookup_type = short_type
2466 }
2467 }
2468 info := t.structs[lookup_type] or { return none }
2469 clean_receiver := t.normalize_type_alias(receiver_type)
2470 for field in info.fields {
2471 if !t.is_embedded_field(field) {
2472 continue
2473 }
2474 field_type := if field.raw_typ.len > 0 { field.raw_typ } else { field.typ }
2475 clean_field := t.normalize_type_alias(field_type.trim_left('&'))
2476 short_field := if clean_field.contains('.') {
2477 clean_field.all_after_last('.')
2478 } else {
2479 clean_field
2480 }
2481 if (field.name == clean_field || field.name == short_field) && clean_field == clean_receiver {
2482 return [field]
2483 }
2484 if sub_path := t.embedded_receiver_path(clean_field, receiver_type) {
2485 mut path := []FieldInfo{}
2486 path << field
2487 path << sub_path
2488 return path
2489 }
2490 }
2491 return none
2492}
2493
2494// receiver_method_return_type supports receiver method return type handling for Transformer.
2495fn (t &Transformer) receiver_method_return_type(method_name string, fallback string) string {
2496 if !isnil(t.tc) {
2497 if typ := t.tc.fn_ret_types[method_name] {
2498 return t.normalize_type_alias(typ.name())
2499 }
2500 }
2501 if ret := t.fn_ret_types[method_name] {
2502 return ret
2503 }
2504 return fallback
2505}
2506
2507// resolve_smartcast_sum_receiver_method supports resolve_smartcast_sum_receiver_method handling.
2508fn (t &Transformer) resolve_smartcast_sum_receiver_method(base_id flat.NodeId, method string) ?string {
2509 key := t.expr_key(base_id)
2510 sc := t.find_smartcast(key) or { return none }
2511 variant := t.resolve_variant(sc.sum_type_name, sc.variant_name)
2512 mut receiver_types := []string{}
2513 receiver_types << variant
2514 original_type := t.trim_pointer_type(t.original_expr_type(base_id))
2515 if original_type.len > 0 && original_type !in receiver_types {
2516 receiver_types << original_type
2517 }
2518 if sc.sum_type_name.len > 0 && sc.sum_type_name !in receiver_types {
2519 receiver_types << sc.sum_type_name
2520 }
2521 sum_type := t.resolve_sum_name(sc.sum_type_name)
2522 if sum_type.len > 0 && sum_type !in receiver_types {
2523 receiver_types << sum_type
2524 }
2525 for parent in t.sum_type_parents_for_variant(sc.variant_name) {
2526 if parent !in receiver_types {
2527 receiver_types << parent
2528 }
2529 }
2530 for receiver_type in receiver_types {
2531 for candidate in t.receiver_method_candidates(receiver_type, method) {
2532 if t.is_known_fn_name(candidate) {
2533 return candidate
2534 }
2535 }
2536 }
2537 return none
2538}
2539
2540// sum_type_parents_for_variant supports sum type parents for variant handling for Transformer.
2541fn (t &Transformer) sum_type_parents_for_variant(variant string) []string {
2542 if parents := t.sum_variant_parents[variant] {
2543 return parents.clone()
2544 }
2545 short := t.variant_short_name(variant)
2546 if short != variant {
2547 if parents := t.sum_variant_parents[short] {
2548 return parents.clone()
2549 }
2550 }
2551 mut result := []string{}
2552 for sum_name, variants in t.sum_types {
2553 for v in variants {
2554 short_v := t.variant_short_name(v)
2555 if v == variant || short_v == short {
2556 result << sum_name
2557 break
2558 }
2559 }
2560 }
2561 if !isnil(t.tc) {
2562 for sum_name, variants in t.tc.sum_types {
2563 if sum_name in result {
2564 continue
2565 }
2566 for v in variants {
2567 short_v := t.variant_short_name(v)
2568 if v == variant || short_v == short {
2569 result << sum_name
2570 break
2571 }
2572 }
2573 }
2574 }
2575 return result
2576}
2577
2578// receiver_method_candidates supports receiver method candidates handling for Transformer.
2579fn (t &Transformer) receiver_method_candidates(receiver_type string, method string) []string {
2580 mut clean_type := receiver_type
2581 if clean_type.starts_with('&') {
2582 clean_type = clean_type[1..]
2583 }
2584 if clean_type.starts_with('map[') {
2585 return t.map_receiver_method_candidates(clean_type, method)
2586 }
2587 mut candidates := []string{}
2588 candidates << '${clean_type}.${method}'
2589 if clean_type.starts_with('[]') {
2590 elem_type := clean_type[2..]
2591 short_elem := if elem_type.contains('.') { elem_type.all_after_last('.') } else { elem_type }
2592 candidates << '[]${short_elem}.${method}'
2593 if elem_type.contains('.') {
2594 candidates << '${elem_type.all_before_last('.')}.[]${short_elem}.${method}'
2595 }
2596 } else if clean_type.contains('.') {
2597 short_type := clean_type.all_after_last('.')
2598 candidates << '${short_type}.${method}'
2599 } else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2600 candidates << '${t.cur_module}.${clean_type}.${method}'
2601 }
2602 return candidates
2603}
2604
2605// map_receiver_method_candidates supports map receiver method candidates handling for Transformer.
2606fn (t &Transformer) map_receiver_method_candidates(receiver_type string, method string) []string {
2607 clean_type := t.clean_map_type(receiver_type)
2608 key_type := t.map_key_type(clean_type)
2609 value_type := t.map_value_type(clean_type)
2610 mut candidates := []string{}
2611 candidates << '${clean_type}.${method}'
2612 if key_type.len == 0 || value_type.len == 0 {
2613 return candidates
2614 }
2615 short_value := if value_type.contains('.') {
2616 value_type.all_after_last('.')
2617 } else {
2618 value_type
2619 }
2620 short_map := 'map[${key_type}]${short_value}'
2621 if short_map != clean_type {
2622 candidates << '${short_map}.${method}'
2623 }
2624 if value_type.contains('.') {
2625 mod_name := value_type.all_before_last('.')
2626 candidates << '${mod_name}.${short_map}.${method}'
2627 } else if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2628 candidates << '${t.cur_module}.${short_map}.${method}'
2629 }
2630 return candidates
2631}
2632
2633// transform_receiver_method_args transforms transform receiver method args data for transform.
2634fn (mut t Transformer) transform_receiver_method_args(node flat.Node, base_id flat.NodeId, method_name string) []flat.NodeId {
2635 return t.transform_receiver_method_args_with_base(node, t.receiver_base_for_resolved_method(base_id,
2636 method_name), method_name)
2637}
2638
2639// transform_receiver_method_args_with_base
2640// transforms helper data for transform.
2641fn (mut t Transformer) transform_receiver_method_args_with_base(node flat.Node, base flat.NodeId, method_name string) []flat.NodeId {
2642 mut args := []flat.NodeId{cap: int(node.children_count)}
2643 args << base
2644 params := t.call_param_types(method_name)
2645 param_offset := t.receiver_method_param_offset(base, node, params)
2646 explicit_args := int(node.children_count) - 1
2647 expected_explicit := params.len - param_offset
2648 is_variadic := t.call_is_variadic(method_name) || (params.len > 0
2649 && params[params.len - 1] is types.Array && explicit_args > expected_explicit)
2650 variadic_idx := if is_variadic && params.len > 0 && params[params.len - 1] is types.Array {
2651 params.len - 1
2652 } else {
2653 -1
2654 }
2655 mut i := 1
2656 for i < node.children_count {
2657 param_idx := (args.len - 1) + param_offset
2658 arg_id := t.a.child(&node, i)
2659 arg_node := t.a.nodes[int(arg_id)]
2660 param_type := if param_idx < params.len { params[param_idx].name() } else { '' }
2661 if arg_node.kind == .field_init {
2662 if packed_arg := t.transform_params_struct_call_arg(node, i, param_type) {
2663 args << packed_arg
2664 i = t.next_non_field_init_arg(node, i)
2665 continue
2666 }
2667 }
2668 if variadic_idx >= 0 && param_idx == variadic_idx {
2669 variadic_type := params[variadic_idx]
2670 if variadic_type is types.Array {
2671 remaining := int(node.children_count) - i
2672 if remaining == 1 {
2673 arg_type := t.node_type(arg_id)
2674 if arg_type.starts_with('[]') {
2675 args << t.transform_call_arg_for_param(arg_id, param_type)
2676 } else {
2677 args << t.pack_variadic_args(node, i, variadic_type.elem_type)
2678 }
2679 } else {
2680 args << t.pack_variadic_args(node, i, variadic_type.elem_type)
2681 }
2682 break
2683 }
2684 }
2685 args << t.transform_call_arg_for_param(arg_id, param_type)
2686 i++
2687 }
2688 if variadic_idx >= 0 && explicit_args == variadic_idx - param_offset {
2689 variadic_type := params[variadic_idx]
2690 if variadic_type is types.Array {
2691 args << t.pack_variadic_args(node, int(node.children_count), variadic_type.elem_type)
2692 }
2693 }
2694 t.append_missing_params_struct_args(mut args, params, param_offset)
2695 return args
2696}
2697
2698// receiver_method_param_offset supports receiver method param offset handling for Transformer.
2699fn (t &Transformer) receiver_method_param_offset(base_id flat.NodeId, node flat.Node, params []types.Type) int {
2700 if params.len == 0 {
2701 return 0
2702 }
2703 base_type := t.node_type(base_id)
2704 first_type := t.normalize_type_alias(params[0].name())
2705 clean_first := if first_type.starts_with('&') { first_type[1..] } else { first_type }
2706 clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2707 if clean_first == clean_base
2708 || t.normalize_type_alias(clean_first) == t.normalize_type_alias(clean_base)
2709 || clean_first.all_after_last('.') == clean_base.all_after_last('.') {
2710 return 1
2711 }
2712 if params.len >= int(node.children_count) {
2713 return 1
2714 }
2715 return 0
2716}
2717
2718// try_lower_string_method_call supports try lower string method call handling for Transformer.
2719fn (mut t Transformer) try_lower_string_method_call(node flat.Node) ?flat.NodeId {
2720 if node.children_count == 0 {
2721 return none
2722 }
2723 fn_id := t.a.children[node.children_start]
2724 fn_node := t.a.nodes[int(fn_id)]
2725 if fn_node.kind != .selector || fn_node.children_count == 0 {
2726 return none
2727 }
2728 method := fn_node.value
2729 if method == 'count' && node.children_count == 2 && t.expr_uses_ident(t.a.child(&node, 1), 'it') {
2730 return t.lower_string_count_call(node, fn_node)
2731 }
2732 if method !in ['replace', 'replace_once', 'trim', 'trim_left', 'trim_right', 'all_before',
2733 'all_after', 'all_before_last', 'all_after_last', 'contains', 'starts_with', 'ends_with',
2734 'bytes', 'substr', 'substr_unsafe', 'repeat', 'plus_two'] {
2735 return none
2736 }
2737 base_id := t.a.child(&fn_node, 0)
2738 base_type := t.node_type(base_id)
2739 if base_type != 'string' {
2740 return none
2741 }
2742 mut args := []flat.NodeId{cap: int(node.children_count)}
2743 args << t.transform_expr(base_id)
2744 for i in 1 .. node.children_count {
2745 args << t.transform_expr(t.a.child(&node, i))
2746 }
2747 ret_type := match method {
2748 'contains', 'starts_with', 'ends_with' { 'bool' }
2749 'bytes' { '[]u8' }
2750 else { 'string' }
2751 }
2752
2753 return t.make_call_typed('string__${method}', args, ret_type)
2754}
2755
2756// lower_string_count_call builds lower string count call data for transform.
2757fn (mut t Transformer) lower_string_count_call(node flat.Node, fn_node flat.Node) ?flat.NodeId {
2758 base_id := t.a.child(&fn_node, 0)
2759 base_type := t.node_type(base_id)
2760 if base_type != 'string' {
2761 return none
2762 }
2763 base := t.stable_expr_for_reuse(base_id)
2764 mut prefix := []flat.NodeId{}
2765 t.drain_pending(mut prefix)
2766 result_name := t.new_temp('count')
2767 idx_name := t.new_temp('count_idx')
2768 prefix << t.make_decl_assign_typed(result_name, t.make_int_literal(0), 'int')
2769 init := t.make_decl_assign_typed(idx_name, t.make_int_literal(0), 'int')
2770 cond := t.make_infix(.lt, t.make_ident(idx_name), t.make_selector(base, 'len', 'int'))
2771 post := t.make_expr_stmt(t.make_postfix(t.make_ident(idx_name), .inc))
2772 elem_expr := t.make_index(base, t.make_ident(idx_name), 'u8')
2773 elem_decl := t.make_decl_assign_typed('it', elem_expr, 'u8')
2774 predicate_id := t.a.child(&node, 1)
2775 old_it := t.var_type('it')
2776 t.set_var_type('it', 'u8')
2777 predicate := t.transform_expr(predicate_id)
2778 if old_it.len > 0 {
2779 t.set_var_type('it', old_it)
2780 } else {
2781 t.unset_var_type('it')
2782 }
2783 mut loop_body := []flat.NodeId{}
2784 loop_body << elem_decl
2785 t.drain_pending(mut loop_body)
2786 inc := t.make_assign_op(t.make_ident(result_name), t.make_int_literal(1), .plus_assign)
2787 loop_body << t.make_if(predicate, t.make_block(arr1(inc)), t.make_empty())
2788 prefix << t.make_for_stmt(init, cond, post, loop_body, node)
2789 for stmt in prefix {
2790 t.pending_stmts << stmt
2791 }
2792 return t.make_ident(result_name)
2793}
2794
2795// expr_uses_ident supports expr uses ident handling for Transformer.
2796fn (t &Transformer) expr_uses_ident(id flat.NodeId, name string) bool {
2797 if int(id) < 0 {
2798 return false
2799 }
2800 node := t.a.nodes[int(id)]
2801 if node.kind == .ident && node.value == name {
2802 return true
2803 }
2804 for i in 0 .. node.children_count {
2805 if t.expr_uses_ident(t.a.child(&node, i), name) {
2806 return true
2807 }
2808 }
2809 return false
2810}
2811
2812// is_method_call checks if a .call node is a method call (child[0] is .selector).
2813// Returns true for `obj.method(args)` patterns.
2814fn (mut t Transformer) is_method_call(node flat.Node) bool {
2815 if node.children_count == 0 {
2816 return false
2817 }
2818 fn_id := t.a.children[node.children_start]
2819 if int(fn_id) < 0 {
2820 return false
2821 }
2822 fn_node := t.a.nodes[int(fn_id)]
2823 return fn_node.kind == .selector
2824}
2825
2826// get_call_return_type looks up the return type for a resolved call.
2827// Handles both checker-resolved calls and transform-time name resolution.
2828fn (t &Transformer) get_call_return_type(id flat.NodeId, node flat.Node) string {
2829 if node.children_count > 0 {
2830 fn_node := t.a.child_node(&node, 0)
2831 if fn_node.kind == .selector && fn_node.value in ['clone', 'reverse']
2832 && fn_node.children_count > 0 {
2833 base_id := t.a.child(fn_node, 0)
2834 base_type := t.node_type(base_id)
2835 clean_type := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2836 if clean_type.starts_with('[]') {
2837 return clean_type
2838 }
2839 }
2840 if fn_node.kind == .selector && fn_node.value == 'keys' && fn_node.children_count > 0 {
2841 base_id := t.a.child(fn_node, 0)
2842 base_type := t.node_type(base_id)
2843 clean_type := t.clean_map_type(base_type)
2844 if clean_type.starts_with('map[') {
2845 key_type := t.map_key_type(clean_type)
2846 if key_type.len > 0 {
2847 return '[]${key_type}'
2848 }
2849 }
2850 }
2851 // A method on a concrete generic instance (`Box[int].clone`) is registered under
2852 // the open form (`Box[T].clone`), whose stored return type collapsed `Box[T]` to
2853 // the bare base. Resolve it through the checker, which re-substitutes the concrete
2854 // arguments from the signature text, so the inferred decl type is `Box[int]`.
2855 if !isnil(t.tc) && fn_node.kind == .selector && fn_node.children_count > 0 {
2856 base_type := t.node_type(t.a.child(fn_node, 0))
2857 clean_base := if base_type.starts_with('&') { base_type[1..] } else { base_type }
2858 if clean_base.contains('[') && clean_base.ends_with(']') {
2859 if ci := t.tc.resolve_generic_struct_method(clean_base, fn_node.value) {
2860 rn := ci.return_type.name()
2861 if rn.len > 0 && rn != 'void' && rn != 'unknown' {
2862 return t.normalize_type_alias(rn)
2863 }
2864 }
2865 }
2866 }
2867 }
2868 if !isnil(t.tc) {
2869 if name := t.tc.resolved_call_name(id) {
2870 if ret := t.tc.fn_ret_types[name] {
2871 return t.call_return_type_name(ret.name(), node)
2872 }
2873 }
2874 }
2875 name := t.resolve_call_name(node)
2876 if name.len == 0 {
2877 return ''
2878 }
2879 if !isnil(t.tc) {
2880 if ret := t.tc.fn_ret_types[name] {
2881 return t.call_return_type_name(ret.name(), node)
2882 }
2883 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2884 qname := '${t.cur_module}.${name}'
2885 if ret := t.tc.fn_ret_types[qname] {
2886 return t.call_return_type_name(ret.name(), node)
2887 }
2888 }
2889 }
2890 // Try exact name first
2891 if ret := t.fn_ret_types[name] {
2892 return t.call_return_type_name(ret, node)
2893 }
2894 // Try qualified with current module
2895 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
2896 qname := '${t.cur_module}.${name}'
2897 if ret := t.fn_ret_types[qname] {
2898 return t.call_return_type_name(ret, node)
2899 }
2900 }
2901 return ''
2902}
2903
2904// call_return_type_name updates call return type name state for Transformer.
2905fn (t &Transformer) call_return_type_name(ret_name string, node flat.Node) string {
2906 mut typ := ret_name
2907 if node.value.len > 0 {
2908 generic_arg := t.normalize_type_in_module(node.value, t.cur_module)
2909 if generic_arg.len > 0 {
2910 typ = t.specialize_generic_type_name(typ, generic_arg)
2911 }
2912 }
2913 if t.is_optional_type_name(typ) {
2914 return typ
2915 }
2916 return t.normalize_type_alias(typ)
2917}
2918
2919// specialize_generic_type_name supports specialize generic type name handling for Transformer.
2920fn (t &Transformer) specialize_generic_type_name(typ string, generic_arg string) string {
2921 clean := typ.trim_space()
2922 if clean.len == 0 || generic_arg.len == 0 {
2923 return typ
2924 }
2925 if clean == 'T' {
2926 return generic_arg
2927 }
2928 if clean.starts_with('&') {
2929 return '&' + t.specialize_generic_type_name(clean[1..], generic_arg)
2930 }
2931 if clean.starts_with('[]') {
2932 return '[]' + t.specialize_generic_type_name(clean[2..], generic_arg)
2933 }
2934 if clean.starts_with('?') {
2935 return '?' + t.specialize_generic_type_name(clean[1..], generic_arg)
2936 }
2937 if clean.starts_with('!') {
2938 return '!' + t.specialize_generic_type_name(clean[1..], generic_arg)
2939 }
2940 if clean.starts_with('...') {
2941 return '...' + t.specialize_generic_type_name(clean[3..], generic_arg)
2942 }
2943 if clean.starts_with('map[') {
2944 bracket_end := clean.index(']') or { return typ }
2945 key_type := t.specialize_generic_type_name(clean[4..bracket_end], generic_arg)
2946 value_type := t.specialize_generic_type_name(clean[bracket_end + 1..], generic_arg)
2947 return 'map[${key_type}]${value_type}'
2948 }
2949 if clean.starts_with('[') {
2950 bracket_end := clean.index(']') or { return typ }
2951 return clean[..bracket_end + 1] + t.specialize_generic_type_name(clean[bracket_end +
2952 1..], generic_arg)
2953 }
2954 return typ
2955}
2956