vxx2 / vlib / v3 / transform / transform.v
7496 lines · 7194 sloc · 223.9 KB · 8739cbd7fc28231971854e7a7606cfeff0e26ea8
Raw
1module transform
2
3import v3.flat
4import v3.types
5
6// arr1 supports arr1 handling for transform.
7fn arr1(a flat.NodeId) []flat.NodeId {
8 mut r := []flat.NodeId{}
9 r << a
10 return r
11}
12
13// arr2 supports arr2 handling for transform.
14fn arr2(a flat.NodeId, b flat.NodeId) []flat.NodeId {
15 mut r := []flat.NodeId{}
16 r << a
17 r << b
18 return r
19}
20
21// arr3 supports arr3 handling for transform.
22fn arr3(a flat.NodeId, b flat.NodeId, c flat.NodeId) []flat.NodeId {
23 mut r := []flat.NodeId{}
24 r << a
25 r << b
26 r << c
27 return r
28}
29
30// arr4 supports arr4 handling for transform.
31fn arr4(a flat.NodeId, b flat.NodeId, c flat.NodeId, d flat.NodeId) []flat.NodeId {
32 mut r := []flat.NodeId{}
33 r << a
34 r << b
35 r << c
36 r << d
37 return r
38}
39
40// node_kind_id supports node kind id handling for transform.
41fn node_kind_id(node flat.Node) int {
42 mut kind_id := node.kind_id
43 if kind_id == 0 && int(node.kind) != 0 {
44 kind_id = int(node.kind)
45 }
46 return kind_id
47}
48
49// SmartcastContext stores smartcast context state used by transform.
50pub struct SmartcastContext {
51pub:
52 expr_name string // the expression being smartcast (e.g. "node")
53 variant_name string // the variant type name (e.g. "Ident")
54 sum_type_name string // the parent sum type name (e.g. "Expr")
55}
56
57// Transformer represents transformer data used by transform.
58pub struct Transformer {
59mut:
60 a &flat.FlatAst = unsafe { nil }
61 tc &types.TypeChecker = unsafe { nil }
62 structs map[string]StructInfo
63 unique_fields map[string]string
64 alias_methods map[string]string
65 globals map[string]string
66 sum_types map[string][]string
67 sum_variant_parents map[string][]string
68 sum_variant_fields map[string]string
69 qualified_types map[string]string
70 fn_ret_types map[string]string
71 const_suffixes map[string]string
72 enum_types map[string][]string
73 cur_file string
74 cur_module string
75 cur_fn_name string
76 cur_fn_ret_type string
77 cur_fn_is_generic bool
78 var_types []VarTypeBinding
79 pointer_value_lvalues map[string]bool
80 temp_counter int
81 pending_stmts []flat.NodeId
82 smartcast_stack []SmartcastContext
83 in_call_callee bool
84 in_const_init bool
85 in_return_expr bool
86 alias_cache &AliasCache = unsafe { nil }
87 sum_cache &AliasCache = unsafe { nil }
88 used_fns map[string]bool
89 // used_struct_operator_fns holds the callee names of direct calls seen during
90 // monomorphize. Infix operators on generic instances are lowered to direct calls
91 // (`Vec_int__plus(a, b)`) before this pass, so an operator overload is specialized for
92 // an instantiated generic struct only when its mangled name appears here — an instance
93 // whose type argument never has the operator applied is not emitted with a body that
94 // would fail C compilation.
95 used_struct_operator_fns map[string]bool
96 // active_generic_params holds the generic parameter names of the decl currently
97 // being specialized/rewritten, in the same order as the inferred type `args`.
98 // It lets type-text substitution map placeholders by name (so non-canonical
99 // params like `D`/`F` resolve to the right arg) instead of by the positional
100 // `generic_param_index` heuristic (which collapses anything outside the T/U/C
101 // sequences to index 0). Empty for struct-generic specialization, which keeps
102 // the legacy positional behaviour.
103 active_generic_params []string
104 // escaping_amp_ptrs holds the names of pointer locals `p` declared as `p := &v`
105 // (v a value local) whose pointer escapes the function (is returned). V semantics
106 // auto-heap such a `v`; v3 otherwise takes the address of a stack local that dies
107 // on return. Recomputed per function (structural pre-pass in transform_fn_body),
108 // consumed when the `p := &v` decl is transformed (RHS rewritten to a heap copy).
109 escaping_amp_ptrs map[string]bool
110 // escaping_amp_sources holds the source locals `v` of such `p := &v` escapes — the
111 // values whose address leaves the frame. The local itself is moved to the heap at its
112 // declaration (its type becomes `&T`) so a mutation between `p := &v` and `return p`
113 // is observed by the caller; copying eagerly at the alias would return stale data.
114 escaping_amp_sources map[string]bool
115 // heaped_amp_locals records which of those sources were actually moved to the heap, so
116 // the `p := &v` alias emits `p = v` (the heap pointer) instead of a fresh memdup copy.
117 heaped_amp_locals map[string]bool
118 generic_fn_decls_cache map[string]GenericFnDecl
119 generic_fn_decls_ready bool
120 node_module_map_cache map[int]string
121 node_module_map_nodes int = -1
122}
123
124// AliasCache memoizes normalize_type_alias results. It lives on the heap so the
125// many `&Transformer` (read-only) query methods can populate it through the
126// pointer. normalize_type_alias is a pure function of (cur_module, typ) plus the
127// collected type maps (which never change during transform), so the cache is
128// keyed by typ and cleared whenever cur_module changes.
129struct AliasCache {
130mut:
131 module string
132 entries map[string]string
133}
134
135// StructInfo stores struct info metadata used by transform.
136pub struct StructInfo {
137pub:
138 name string
139 module string
140 is_params bool
141 fields []FieldInfo
142}
143
144// FieldInfo stores field info metadata used by transform.
145pub struct FieldInfo {
146pub:
147 name string
148 typ string
149 raw_typ string
150 default_expr flat.NodeId
151}
152
153// TupleBlockParts represents tuple block parts data used by transform.
154struct TupleBlockParts {
155 prefix []flat.NodeId
156 values []flat.NodeId
157}
158
159// StructFieldLookup represents struct field lookup data used by transform.
160struct StructFieldLookup {
161 info StructInfo
162 owner_type string
163}
164
165// VarTypeBinding represents var type binding data used by transform.
166struct VarTypeBinding {
167 name string
168 typ string
169}
170
171struct GenericFnDecl {
172 id flat.NodeId
173 node flat.Node
174 file string
175 module string
176 key string
177}
178
179// --- entry point ---
180
181// transform supports transform handling for transform.
182pub fn transform(mut a flat.FlatAst, tc &types.TypeChecker) {
183 transform_with_used(mut a, tc, map[string]bool{})
184}
185
186// transform_with_used transforms transform with used data for transform.
187pub fn transform_with_used(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) map[string]bool {
188 augmented, _ := transform_with_used_opt(mut a, tc, used_fns, false)
189 return augmented
190}
191
192// transform_with_used_opt is transform_with_used with an opt-in for parallel
193// function-body transform. It returns the augmented used-fn set and whether the
194// function bodies were actually transformed across threads (false when parallel
195// was not requested, the build lacks thread support, or there was too little work).
196pub fn transform_with_used_opt(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool, want_parallel bool) (map[string]bool, bool) {
197 mut augmented_used_fns := used_fns.clone()
198 mut t := new_transformer(mut a, tc, augmented_used_fns)
199 t.prepare()
200 if want_parallel {
201 // Transform roughly grows the node/children arrays by ~75%. Reserve that capacity
202 // up front so they don't double past it (the parsed AST already overshoots to the
203 // next power of two, wasting ~40MB, and each doubling briefly holds both the old and
204 // new arrays — the dominant peak-RSS contributor under -gc none). The serial path is
205 // latency-sensitive and does not clone worker ASTs, so let it grow naturally.
206 reserve_nodes := a.nodes.len * 7 / 4 - a.nodes.cap
207 if reserve_nodes > 0 {
208 unsafe { a.nodes.grow_cap(reserve_nodes) }
209 }
210 reserve_children := a.children.len * 7 / 4 - a.children.cap
211 if reserve_children > 0 {
212 unsafe { a.children.grow_cap(reserve_children) }
213 }
214 }
215 was_parallel := t.transform_all_dispatch(want_parallel)
216 augmented_used_fns = t.used_fns.clone()
217 return augmented_used_fns, was_parallel
218}
219
220pub fn monomorphize_with_used(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) map[string]bool {
221 mut augmented_used_fns := used_fns.clone()
222 mut t := new_transformer(mut a, tc, augmented_used_fns)
223 t.prepare()
224 for name in t.monomorphize_pass() {
225 augmented_used_fns[name] = true
226 augmented_used_fns[c_name(name)] = true
227 t.used_fns[name] = true
228 t.used_fns[c_name(name)] = true
229 }
230 t.materialize_generic_structs()
231 return t.used_fns
232}
233
234fn new_transformer(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) Transformer {
235 return Transformer{
236 a: a
237 tc: unsafe { tc }
238 pointer_value_lvalues: map[string]bool{}
239 escaping_amp_ptrs: map[string]bool{}
240 escaping_amp_sources: map[string]bool{}
241 heaped_amp_locals: map[string]bool{}
242 used_fns: used_fns.clone()
243 }
244}
245
246fn (mut t Transformer) mark_fn_used_name(name string) {
247 if name.len == 0 {
248 return
249 }
250 t.used_fns[name] = true
251 t.used_fns[c_name(name)] = true
252 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
253 needs_module_prefix := !name.contains('.') || local_method_fn_name_needs_module_prefix(name)
254 if needs_module_prefix && !name.starts_with('${t.cur_module}.') {
255 qname := '${t.cur_module}.${name}'
256 t.used_fns[qname] = true
257 t.used_fns[c_name(qname)] = true
258 }
259 }
260}
261
262fn local_method_fn_name_needs_module_prefix(name string) bool {
263 if !name.contains('.') {
264 return false
265 }
266 receiver_name := name.all_before('.')
267 if receiver_name.len == 0 {
268 return false
269 }
270 first := receiver_name[0]
271 return first >= `A` && first <= `Z`
272}
273
274fn (mut t Transformer) prepare() {
275 t.collect_types()
276 t.collect_const_suffixes()
277 t.collect_alias_methods()
278 // Enable the alias cache only now that the type maps are fully populated.
279 // During collection those maps are incomplete, so caching there would poison
280 // entries with results computed against a partial view.
281 t.alias_cache = &AliasCache{}
282 t.sum_cache = &AliasCache{}
283}
284
285// reset_var_types updates reset var types state for transform.
286fn (mut t Transformer) reset_var_types() {
287 t.var_types.clear()
288}
289
290// set_var_type updates set var type state for transform.
291fn (mut t Transformer) set_var_type(name string, typ string) {
292 if name.len == 0 {
293 return
294 }
295 for i, binding in t.var_types {
296 if binding.name == name {
297 t.var_types[i] = VarTypeBinding{
298 name: name
299 typ: typ
300 }
301 return
302 }
303 }
304 t.var_types << VarTypeBinding{
305 name: name
306 typ: typ
307 }
308}
309
310// unset_var_type supports unset var type handling for Transformer.
311fn (mut t Transformer) unset_var_type(name string) {
312 for i, binding in t.var_types {
313 if binding.name == name {
314 t.var_types.delete(i)
315 return
316 }
317 }
318}
319
320// var_type supports var type handling for Transformer.
321fn (t &Transformer) var_type(name string) string {
322 for binding in t.var_types {
323 if binding.name == name {
324 return binding.typ
325 }
326 }
327 return ''
328}
329
330// --- type collection ---
331
332// collect_types updates collect types state for transform.
333fn (mut t Transformer) collect_types() {
334 mut cur_mod := ''
335 for node in t.a.nodes {
336 match node.kind {
337 .module_decl {
338 cur_mod = node.value
339 }
340 .struct_decl {
341 owner_type := if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
342 '${cur_mod}.${node.value}'
343 } else {
344 node.value
345 }
346 mut fields := []FieldInfo{}
347 for i in 0 .. node.children_count {
348 f := t.a.child_node(&node, i)
349 if f.kind != .field_decl {
350 continue
351 }
352 default_expr := if f.children_count > 0 {
353 t.a.child(f, 0)
354 } else {
355 flat.empty_node
356 }
357 fields << FieldInfo{
358 name: f.value
359 typ: t.normalize_field_type(f.typ, owner_type)
360 raw_typ: f.typ
361 default_expr: default_expr
362 }
363 }
364 info := StructInfo{
365 name: node.value
366 module: cur_mod
367 is_params: node.typ.contains('params')
368 fields: fields
369 }
370 t.structs[node.value] = info
371 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
372 qname := '${cur_mod}.${node.value}'
373 t.structs[qname] = info
374 if node.value !in t.qualified_types {
375 t.qualified_types[node.value] = qname
376 }
377 }
378 for f in fields {
379 t.add_unique_field_type(f.name, f.typ)
380 }
381 }
382 .type_decl {
383 if node.children_count > 0 {
384 mut variants := []string{}
385 for i in 0 .. node.children_count {
386 v := t.a.child_node(&node, i)
387 variants << t.normalize_sum_variant_type(v.value, cur_mod)
388 }
389 t.sum_types[node.value] = variants
390 for variant in variants {
391 t.add_sum_variant_parent(variant, node.value)
392 }
393 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
394 qname := '${cur_mod}.${node.value}'
395 t.sum_types[qname] = variants
396 if node.value !in t.qualified_types {
397 t.qualified_types[node.value] = qname
398 }
399 for variant in variants {
400 t.add_sum_variant_parent(variant, qname)
401 }
402 }
403 }
404 }
405 .enum_decl {
406 mut field_names := []string{}
407 for i in 0 .. node.children_count {
408 f := t.a.child_node(&node, i)
409 if f.kind == .enum_field {
410 field_names << f.value
411 }
412 }
413 t.enum_types[node.value] = field_names
414 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
415 t.enum_types['${cur_mod}.${node.value}'] = field_names
416 }
417 }
418 .global_decl {
419 for i in 0 .. node.children_count {
420 f := t.a.child_node(&node, i)
421 mut typ := t.normalize_type_in_module(f.typ, cur_mod)
422 if typ.len == 0 && f.children_count > 0 {
423 typ = t.normalize_type_in_module(t.node_type(t.a.child(f, 0)), cur_mod)
424 }
425 t.globals[f.value] = typ
426 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
427 t.globals['${cur_mod}.${f.value}'] = typ
428 }
429 }
430 }
431 .fn_decl {
432 if node.typ.len > 0 {
433 ret_typ := t.normalize_type_in_module(node.typ, cur_mod)
434 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
435 qname := '${cur_mod}.${node.value}'
436 t.fn_ret_types[qname] = ret_typ
437 qlowered := c_name(qname)
438 if qlowered != qname {
439 t.fn_ret_types[qlowered] = ret_typ
440 }
441 } else {
442 t.fn_ret_types[node.value] = ret_typ
443 lowered := c_name(node.value)
444 if lowered != node.value {
445 t.fn_ret_types[lowered] = ret_typ
446 }
447 }
448 }
449 }
450 .c_fn_decl {
451 if node.typ.len > 0 {
452 ret_typ := t.normalize_type_in_module(node.typ, cur_mod)
453 t.fn_ret_types[node.value] = ret_typ
454 if node.value.starts_with('C.') {
455 t.fn_ret_types[node.value[2..]] = ret_typ
456 } else {
457 t.fn_ret_types['C.${node.value}'] = ret_typ
458 }
459 }
460 }
461 else {}
462 }
463 }
464}
465
466// add_sum_variant_parent updates add sum variant parent state for Transformer.
467fn (mut t Transformer) add_sum_variant_parent(variant string, sum_name string) {
468 if variant.len == 0 || sum_name.len == 0 {
469 return
470 }
471 field_name := t.sum_field_name(variant)
472 if field_name.contains('__') && field_name !in t.sum_variant_fields {
473 t.sum_variant_fields[field_name] = variant
474 }
475 t.add_sum_variant_parent_key(variant, sum_name)
476 if variant.contains('.') {
477 t.add_sum_variant_parent_key(variant.all_after_last('.'), sum_name)
478 }
479}
480
481// add_sum_variant_parent_key updates add sum variant parent key state for Transformer.
482fn (mut t Transformer) add_sum_variant_parent_key(key string, sum_name string) {
483 mut parents := t.sum_variant_parents[key] or { []string{} }
484 if sum_name !in parents {
485 parents << sum_name
486 t.sum_variant_parents[key] = parents
487 }
488}
489
490// add_unique_field_type updates add unique field type state for Transformer.
491fn (mut t Transformer) add_unique_field_type(name string, typ string) {
492 if name.len == 0 || typ.len == 0 {
493 return
494 }
495 if existing := t.unique_fields[name] {
496 if existing != typ {
497 t.unique_fields[name] = ''
498 }
499 } else {
500 t.unique_fields[name] = typ
501 }
502}
503
504// collect_const_suffixes updates collect const suffixes state for transform.
505fn (mut t Transformer) collect_const_suffixes() {
506 if isnil(t.tc) {
507 return
508 }
509 // Register every dot-delimited suffix of each const key so that both
510 // unqualified (`foo`) and partially-qualified (`mod.foo`) lookups resolve
511 // in O(1) via const_type_key, instead of scanning all consts per ident.
512 for key, _ in t.tc.const_types {
513 if !key.contains('.') {
514 t.add_const_suffix(key, key)
515 continue
516 }
517 mut i := 0
518 for i < key.len {
519 if key[i] == `.` {
520 t.add_const_suffix(key[i + 1..], key)
521 }
522 i++
523 }
524 }
525}
526
527// add_const_suffix updates add const suffix state for Transformer.
528fn (mut t Transformer) add_const_suffix(suffix string, key string) {
529 if existing := t.const_suffixes[suffix] {
530 if existing != key {
531 t.const_suffixes[suffix] = ''
532 }
533 } else {
534 t.const_suffixes[suffix] = key
535 }
536}
537
538// collect_alias_methods converts collect alias methods data for transform.
539fn (mut t Transformer) collect_alias_methods() {
540 if isnil(t.tc) {
541 return
542 }
543 for name, params in t.tc.fn_param_types {
544 if params.len == 0 || !name.contains('.') {
545 continue
546 }
547 receiver_name := name.all_before_last('.')
548 if receiver_name.len == 0 || receiver_name !in t.tc.type_aliases {
549 continue
550 }
551 method := name.all_after_last('.')
552 param_name := params[0].name()
553 clean_alias := if param_name.starts_with('&') { param_name[1..] } else { param_name }
554 alias_target := t.normalize_type_alias(clean_alias)
555 if alias_target.len == 0 {
556 continue
557 }
558 key := '${alias_target}.${method}'
559 if key !in t.alias_methods {
560 t.alias_methods[key] = name
561 }
562 }
563}
564
565// normalize_sum_variant_type transforms normalize sum variant type data for transform.
566fn (t &Transformer) normalize_sum_variant_type(typ string, mod string) string {
567 clean := typ.trim_space()
568 if clean.len == 0 {
569 return clean
570 }
571 if clean.starts_with('&') {
572 return '&' + t.normalize_sum_variant_type(clean[1..], mod)
573 }
574 if clean.starts_with('mut ') {
575 return '&' + t.normalize_sum_variant_type(clean[4..], mod)
576 }
577 if clean.starts_with('?') {
578 return '?' + t.normalize_sum_variant_type(clean[1..], mod)
579 }
580 if clean.starts_with('!') {
581 return '!' + t.normalize_sum_variant_type(clean[1..], mod)
582 }
583 if clean.starts_with('[]') {
584 return '[]' + t.normalize_sum_variant_type(clean[2..], mod)
585 }
586 if clean.starts_with('map[') {
587 bracket_end := clean.index(']') or { return clean }
588 key := t.normalize_sum_variant_type(clean[4..bracket_end], mod)
589 value := t.normalize_sum_variant_type(clean[bracket_end + 1..], mod)
590 return 'map[${key}]${value}'
591 }
592 if clean.starts_with('[') {
593 bracket_end := clean.index(']') or { return clean }
594 return clean[..bracket_end + 1] + t.normalize_sum_variant_type(clean[bracket_end +
595 1..], mod)
596 }
597 if clean.contains('.') || mod.len == 0 || mod == 'main' || mod == 'builtin'
598 || types.is_builtin_type_name(clean) {
599 return clean
600 }
601 return '${mod}.${clean}'
602}
603
604// --- main transform pass ---
605
606// transform_all transforms transform all data for transform.
607fn (mut t Transformer) transform_all() {
608 has_entry_main := t.has_entry_main()
609 node_count := t.a.nodes.len
610 for i in 0 .. node_count {
611 node := t.a.nodes[i]
612 kind_id := node_kind_id(node)
613 if kind_id == 77 {
614 t.cur_file = node.value
615 }
616 if kind_id == 73 {
617 t.cur_module = node.value
618 }
619 if kind_id == 61 {
620 if !t.should_transform_fn(node) {
621 continue
622 }
623 t.transform_fn_body(i)
624 } else if kind_id == 65 {
625 t.transform_const_decl(node)
626 } else if kind_id == 64 {
627 t.transform_global_decl(node)
628 }
629 }
630 if !has_entry_main {
631 t.transform_top_level_user_stmts()
632 }
633}
634
635fn (t &Transformer) has_entry_main() bool {
636 mut cur_module := ''
637 for node in t.a.nodes {
638 kind_id := node_kind_id(node)
639 if kind_id == 77 {
640 cur_module = ''
641 continue
642 }
643 if kind_id == 73 {
644 cur_module = node.value
645 continue
646 }
647 if kind_id == 61 && node.value == 'main' && (cur_module.len == 0 || cur_module == 'main') {
648 return true
649 }
650 }
651 return false
652}
653
654fn (mut t Transformer) transform_top_level_user_stmts() {
655 node_count := t.a.nodes.len
656 for file_idx in 0 .. node_count {
657 file_node := t.a.nodes[file_idx]
658 if !t.should_transform_top_level_file(file_idx, file_node) {
659 continue
660 }
661 t.transform_top_level_file(file_idx, file_node)
662 }
663}
664
665fn (t &Transformer) should_transform_top_level_file(file_idx int, file_node flat.Node) bool {
666 if file_idx < t.a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
667 return false
668 }
669 module_name := t.file_module_name(file_node)
670 return module_name.len == 0 || module_name == 'main'
671}
672
673fn (t &Transformer) file_module_name(file_node flat.Node) string {
674 for i in 0 .. file_node.children_count {
675 child := t.a.child_node(&file_node, i)
676 if child.kind == .module_decl {
677 return child.value
678 }
679 }
680 return ''
681}
682
683fn transform_is_top_level_stmt(node flat.Node) bool {
684 return match node.kind {
685 .expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
686 .for_in_stmt, .if_expr, .match_stmt, .assert_stmt, .defer_stmt, .block {
687 true
688 }
689 else {
690 false
691 }
692 }
693}
694
695fn (mut t Transformer) transform_top_level_file(file_idx int, file_node flat.Node) {
696 old_file := t.cur_file
697 old_module := t.cur_module
698 old_fn_name := t.cur_fn_name
699 old_fn_ret_type := t.cur_fn_ret_type
700 old_var_types := t.var_types.clone()
701 old_smartcast_stack := t.smartcast_stack.clone()
702 old_pending_stmts := t.pending_stmts.clone()
703 module_name := t.file_module_name(file_node)
704 t.cur_file = file_node.value
705 t.cur_module = if module_name.len == 0 { 'main' } else { module_name }
706 t.cur_fn_name = 'main'
707 t.cur_fn_ret_type = 'void'
708 t.reset_var_types()
709 t.smartcast_stack.clear()
710 t.pending_stmts.clear()
711 mut new_children := []flat.NodeId{cap: int(file_node.children_count)}
712 mut pending_stmts := []flat.NodeId{}
713 for i in 0 .. file_node.children_count {
714 child_id := t.a.child(&file_node, i)
715 if int(child_id) >= t.a.user_code_start {
716 child := t.a.nodes[int(child_id)]
717 if transform_is_top_level_stmt(child) {
718 pending_stmts << child_id
719 continue
720 }
721 }
722 t.append_transformed_top_level_stmts(mut new_children, mut pending_stmts)
723 new_children << child_id
724 }
725 t.append_transformed_top_level_stmts(mut new_children, mut pending_stmts)
726 start := t.a.children.len
727 for child_id in new_children {
728 t.a.children << child_id
729 }
730 t.a.nodes[file_idx] = flat.Node{
731 kind: .file
732 kind_id: file_node.kind_id
733 op: file_node.op
734 children_start: start
735 children_count: flat.child_count(new_children.len)
736 pos: file_node.pos
737 value: file_node.value
738 typ: file_node.typ
739 generic_params: file_node.generic_params
740 }
741 t.cur_file = old_file
742 t.cur_module = old_module
743 t.cur_fn_name = old_fn_name
744 t.cur_fn_ret_type = old_fn_ret_type
745 t.var_types = old_var_types
746 t.smartcast_stack = old_smartcast_stack
747 t.pending_stmts = old_pending_stmts
748}
749
750fn (mut t Transformer) append_transformed_top_level_stmts(mut out []flat.NodeId, mut pending []flat.NodeId) {
751 if pending.len == 0 {
752 return
753 }
754 transformed := t.transform_stmts(pending)
755 out << transformed
756 pending.clear()
757}
758
759// FnWorkItem identifies one top-level function body to transform, together with
760// the file/module context active at its declaration and a rough cost estimate
761// (subtree node count) used to balance work across parallel workers.
762struct FnWorkItem {
763 fn_idx int
764 file string
765 module string
766 cost int
767}
768
769// transform_all_dispatch runs the main transform pass either serially (the
770// original single-threaded walk) or, when `want_parallel` is set and there is
771// enough work, with closure-free function bodies transformed across threads.
772// Returns whether function bodies were actually transformed in parallel.
773fn (mut t Transformer) transform_all_dispatch(want_parallel bool) bool {
774 if !want_parallel {
775 t.transform_all()
776 return false
777 }
778 has_entry_main := t.has_entry_main()
779 // Serial phase: transform consts/globals and every function whose body
780 // contains a function literal (the only construct that lifts new top-level
781 // declarations and mutates the shared TypeChecker). Collect the remaining,
782 // closure-free functions as parallelizable work items.
783 has_fn_literals := t.has_fn_literal_nodes()
784 pure_items := t.transform_serial_then_collect_pure(has_fn_literals)
785 base_nodes := t.a.nodes.len
786 base_children := t.a.children.len
787 was_parallel := t.run_parallel_transform(pure_items, base_nodes, base_children)
788 if !has_entry_main {
789 t.transform_top_level_user_stmts()
790 }
791 return was_parallel
792}
793
794fn (t &Transformer) has_fn_literal_nodes() bool {
795 for node in t.a.nodes {
796 if node.kind == .fn_literal || node.kind == .lambda_expr {
797 return true
798 }
799 }
800 return false
801}
802
803// transform_serial_then_collect_pure walks the top level once: it transforms
804// const/global declarations and closure-bearing functions in place (serially),
805// and returns work items for the closure-free functions left to transform.
806fn (mut t Transformer) transform_serial_then_collect_pure(scan_fn_literals bool) []FnWorkItem {
807 mut pure := []FnWorkItem{}
808 original_len := t.a.nodes.len
809 for i in 0 .. original_len {
810 node := t.a.nodes[i]
811 kind_id := node_kind_id(node)
812 if kind_id == 77 {
813 t.cur_file = node.value
814 } else if kind_id == 73 {
815 t.cur_module = node.value
816 } else if kind_id == 61 {
817 if !t.should_transform_fn(node) {
818 continue
819 }
820 mut has_literal := false
821 mut cost := int(node.children_count) + 1
822 if scan_fn_literals {
823 has_literal, cost = t.fn_subtree_scan(i)
824 }
825 if has_literal {
826 t.transform_fn_body(i)
827 } else {
828 pure << FnWorkItem{
829 fn_idx: i
830 file: t.cur_file
831 module: t.cur_module
832 cost: cost
833 }
834 }
835 } else if kind_id == 65 {
836 t.transform_const_decl(node)
837 } else if kind_id == 64 {
838 t.transform_global_decl(node)
839 }
840 }
841 return pure
842}
843
844// fn_subtree_scan walks the subtree rooted at the function declaration `fn_idx`
845// and reports (whether it contains a function literal, total node count). The
846// function-literal flag routes closure-bearing functions to the serial path; the
847// count is a cheap per-function cost used to balance parallel workers.
848fn (t &Transformer) fn_subtree_scan(fn_idx int) (bool, int) {
849 mut has_literal := false
850 mut count := 0
851 mut stack := [flat.NodeId(fn_idx)]
852 for stack.len > 0 {
853 id := stack.pop()
854 idx := int(id)
855 if idx < 0 || idx >= t.a.nodes.len {
856 continue
857 }
858 node := t.a.nodes[idx]
859 count++
860 // 21 = fn_literal, 32 = lambda_expr: both can lower to a lifted closure.
861 kid := node_kind_id(node)
862 if kid == 21 || kid == 32 {
863 has_literal = true
864 }
865 for ci in 0 .. node.children_count {
866 child_id := t.a.children[node.children_start + ci]
867 if int(child_id) >= 0 {
868 stack << child_id
869 }
870 }
871 }
872 return has_literal, count
873}
874
875// transform_pure_items_serial transforms a list of closure-free function bodies
876// on this Transformer, in order. Used both as the serial fallback and as the
877// per-worker body in the parallel path.
878fn (mut t Transformer) transform_pure_items_serial(items []FnWorkItem) {
879 for it in items {
880 t.cur_file = it.file
881 t.cur_module = it.module
882 t.transform_fn_body(it.fn_idx)
883 }
884}
885
886// clone_ast_base produces a private FlatAst holding an independent copy of the
887// first base_nodes nodes / base_children children, so a worker can append its own
888// transformed nodes without racing the master or other workers. Read-only metadata
889// (disabled_fns) is shared.
890fn (t &Transformer) clone_ast_base(base_nodes int, base_children int) &flat.FlatAst {
891 return &flat.FlatAst{
892 nodes: t.a.nodes[0..base_nodes].clone()
893 children: t.a.children[0..base_children].clone()
894 user_code_start: t.a.user_code_start
895 disabled_fns: t.a.disabled_fns
896 }
897}
898
899// fork_worker builds a worker Transformer that shares this transformer's
900// read-only collected maps (structs, sum types, fn return types, …) and
901// operates on its own cloned AST `ast` and forked TypeChecker `wtc`. All
902// per-function mutable state, helper-root tracking, used-fn additions, and
903// memoization caches are reset/private so the worker can run on its own thread.
904fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Transformer {
905 mut w := *t
906 w.a = ast
907 w.tc = wtc
908 w.used_fns = t.used_fns.clone()
909 w.alias_cache = &AliasCache{}
910 w.sum_cache = &AliasCache{}
911 w.generic_fn_decls_cache = map[string]GenericFnDecl{}
912 w.generic_fn_decls_ready = false
913 w.node_module_map_cache = map[int]string{}
914 w.node_module_map_nodes = -1
915 w.var_types = []VarTypeBinding{}
916 w.smartcast_stack = []SmartcastContext{}
917 w.pending_stmts = []flat.NodeId{}
918 w.pointer_value_lvalues = map[string]bool{}
919 w.escaping_amp_ptrs = map[string]bool{}
920 w.escaping_amp_sources = map[string]bool{}
921 w.heaped_amp_locals = map[string]bool{}
922 w.used_fns = t.used_fns.clone()
923 w.temp_counter = 0
924 w.cur_file = ''
925 w.cur_module = ''
926 w.cur_fn_name = ''
927 w.cur_fn_ret_type = ''
928 w.in_call_callee = false
929 w.in_const_init = false
930 w.in_return_expr = false
931 return &w
932}
933
934fn (mut t Transformer) merge_worker_used_fns(w &Transformer) {
935 for name, used in w.used_fns {
936 if used {
937 t.used_fns[name] = true
938 }
939 }
940}
941
942// merge_worker folds a finished worker's transformed output back into the master
943// AST. The worker created its new nodes/children at indices base_nodes/base_children
944// (matching the master at fork time); here they are appended to the master and every
945// reference to a worker-local new node or new children block is shifted by the
946// distance the block moved. `items` lists the function indices this worker owned, so
947// their rewritten top-level nodes can be copied into place.
948fn (mut t Transformer) merge_worker(w &Transformer, items []FnWorkItem, base_nodes int, base_children int) {
949 node_shift := i32(t.a.nodes.len - base_nodes)
950 child_shift := i32(t.a.children.len - base_children)
951 // New children: relocate references to worker-local new nodes.
952 for k in base_children .. w.a.children.len {
953 cid := w.a.children[k]
954 if int(cid) >= base_nodes {
955 t.a.children << flat.NodeId(int(cid) + int(node_shift))
956 } else {
957 t.a.children << cid
958 }
959 }
960 // New nodes: relocate children_start that points into the new children block.
961 for k in base_nodes .. w.a.nodes.len {
962 n := w.a.nodes[k]
963 if n.children_start >= base_children {
964 t.a.nodes << n.with_shifted_children(child_shift)
965 } else {
966 t.a.nodes << n
967 }
968 }
969 // Rewritten top-level function nodes keep their original index in the master.
970 for it in items {
971 n := w.a.nodes[it.fn_idx]
972 if n.children_start >= base_children {
973 t.a.nodes[it.fn_idx] = n.with_shifted_children(child_shift)
974 } else {
975 t.a.nodes[it.fn_idx] = n
976 }
977 }
978 for name, used in w.used_fns {
979 if used {
980 t.used_fns[name] = true
981 }
982 }
983}
984
985// split_work_items distributes items across `n` buckets using greedy
986// least-loaded-by-cost assignment, so heavy functions are spread evenly. The
987// assignment is deterministic for a given input (required for reproducible builds).
988fn split_work_items(items []FnWorkItem, n int) [][]FnWorkItem {
989 mut buckets := [][]FnWorkItem{len: n, init: []FnWorkItem{}}
990 mut loads := []i64{len: n}
991 for it in items {
992 mut best := 0
993 for b in 1 .. n {
994 if loads[b] < loads[best] {
995 best = b
996 }
997 }
998 buckets[best] << it
999 loads[best] += i64(it.cost) + 1
1000 }
1001 return buckets
1002}
1003
1004// should_transform_fn reports whether should transform fn applies in transform.
1005fn (t &Transformer) should_transform_fn(node flat.Node) bool {
1006 if t.used_fns.len == 0 {
1007 return true
1008 }
1009 if node.value in t.used_fns {
1010 return true
1011 }
1012 qname := transform_qualified_fn_name(t.cur_module, node.value)
1013 if qname in t.used_fns {
1014 return true
1015 }
1016 cname := c_name(qname)
1017 if cname != qname && cname in t.used_fns {
1018 return true
1019 }
1020 return false
1021}
1022
1023// transform_qualified_fn_name transforms transform qualified fn name data for transform.
1024fn transform_qualified_fn_name(mod string, name string) string {
1025 if mod.len == 0 || mod == 'main' || mod == 'builtin' {
1026 return name
1027 }
1028 return '${mod}.${name}'
1029}
1030
1031// transform_const_decl transforms the initializer expression of each const field
1032// so that const-level lowering (e.g. string concatenation in the prelude's
1033// embedded data tables) happens in the transformer rather than the backend.
1034fn (mut t Transformer) transform_const_decl(node flat.Node) {
1035 old_in_const_init := t.in_const_init
1036 t.in_const_init = true
1037 for ci in 0 .. node.children_count {
1038 cf_id := t.a.child(&node, ci)
1039 if int(cf_id) < 0 {
1040 continue
1041 }
1042 cf := t.a.nodes[int(cf_id)]
1043 if cf.kind == .const_field && cf.children_count >= 1 && cf.children_start >= 0 {
1044 val_id := t.a.child(&cf, 0)
1045 if int(val_id) < 0 {
1046 continue
1047 }
1048 val := t.a.nodes[int(val_id)]
1049 if block_val := t.const_block_value(val) {
1050 new_val := t.transform_const_value(block_val)
1051 t.a.children[cf.children_start] = new_val
1052 } else if val.kind == .string_interp {
1053 new_val := t.transform_const_string_interp(val_id, val)
1054 t.a.children[cf.children_start] = new_val
1055 } else if val.kind == .or_expr {
1056 const_typ := t.const_field_type_name(cf)
1057 new_val := t.transform_const_or_expr(val_id, val, const_typ)
1058 t.a.children[cf.children_start] = new_val
1059 } else if val.kind == .struct_init || val.kind == .cast_expr || val.kind == .call {
1060 new_val := t.transform_expr(val_id)
1061 t.a.children[cf.children_start] = new_val
1062 } else if val.kind == .infix && val.children_count >= 2 {
1063 new_val := t.transform_expr(val_id)
1064 // Overwrite the field's value slot in place (each const_field owns
1065 // its own single-element child range, so this is safe).
1066 t.a.children[cf.children_start] = new_val
1067 }
1068 }
1069 }
1070 t.in_const_init = old_in_const_init
1071}
1072
1073// const_field_type_name supports const field type name handling for Transformer.
1074fn (t &Transformer) const_field_type_name(field flat.Node) string {
1075 if field.value.len > 0 {
1076 if t.cur_module.len > 0 {
1077 if typ := t.const_type_name('${t.cur_module}.${field.value}') {
1078 return typ
1079 }
1080 }
1081 if typ := t.const_type_name(field.value) {
1082 return typ
1083 }
1084 }
1085 return field.typ
1086}
1087
1088// transform_const_or_expr transforms transform const or expr data for transform.
1089fn (mut t Transformer) transform_const_or_expr(_id flat.NodeId, node flat.Node, const_typ string) flat.NodeId {
1090 if node.children_count < 2 {
1091 return _id
1092 }
1093 mut children := []flat.NodeId{cap: int(node.children_count)}
1094 for i in 0 .. node.children_count {
1095 child_id := t.a.child(&node, i)
1096 if i == 0 {
1097 children << t.transform_expr(child_id)
1098 } else {
1099 children << child_id
1100 }
1101 }
1102 start := t.a.children.len
1103 for child in children {
1104 t.a.children << child
1105 }
1106 return t.a.add_node(flat.Node{
1107 kind: .or_expr
1108 op: node.op
1109 children_start: start
1110 children_count: node.children_count
1111 pos: node.pos
1112 value: node.value
1113 typ: if const_typ.len > 0 { const_typ } else { node.typ }
1114 })
1115}
1116
1117// transform_global_decl transforms transform global decl data for transform.
1118fn (mut t Transformer) transform_global_decl(node flat.Node) {
1119 for ci in 0 .. node.children_count {
1120 gf_id := t.a.child(&node, ci)
1121 if int(gf_id) < 0 {
1122 continue
1123 }
1124 gf := t.a.nodes[int(gf_id)]
1125 if gf.kind == .field_decl && gf.children_count >= 1 && gf.children_start >= 0 {
1126 val_id := t.a.child(&gf, 0)
1127 if int(val_id) < 0 {
1128 continue
1129 }
1130 val := t.a.nodes[int(val_id)]
1131 if preserved := t.transform_global_amp_initializer(val_id, val) {
1132 t.a.children[gf.children_start] = preserved
1133 continue
1134 }
1135 old_pending := t.pending_stmts.clone()
1136 t.pending_stmts.clear()
1137 new_val := t.transform_expr(val_id)
1138 has_pending := t.pending_stmts.len > 0
1139 t.pending_stmts.clear()
1140 t.pending_stmts = old_pending
1141 if !has_pending {
1142 t.a.children[gf.children_start] = new_val
1143 }
1144 }
1145 }
1146}
1147
1148// transform_global_amp_initializer transforms transform global amp initializer data for transform.
1149fn (mut t Transformer) transform_global_amp_initializer(val_id flat.NodeId, val flat.Node) ?flat.NodeId {
1150 if val.kind != .prefix || val.op != .amp || val.children_count != 1 {
1151 return none
1152 }
1153 child_id := t.a.child(&val, 0)
1154 child := t.a.nodes[int(child_id)]
1155 if child.kind == .assoc {
1156 return val_id
1157 }
1158 old_pending := t.pending_stmts.clone()
1159 t.pending_stmts.clear()
1160 mut result := flat.empty_node
1161 if child.kind == .struct_init {
1162 if preserved := t.transform_amp_struct_init_for_type(val_id, val, val.typ) {
1163 result = preserved
1164 }
1165 } else if child.kind == .cast_expr {
1166 if preserved := t.transform_global_amp_interface_cast(val, val.typ) {
1167 result = preserved
1168 }
1169 }
1170 has_pending := t.pending_stmts.len > 0
1171 t.pending_stmts.clear()
1172 t.pending_stmts = old_pending
1173 if has_pending || int(result) < 0 {
1174 return none
1175 }
1176 return result
1177}
1178
1179// transform_const_value transforms transform const value data for transform.
1180fn (mut t Transformer) transform_const_value(id flat.NodeId) flat.NodeId {
1181 if int(id) < 0 {
1182 return id
1183 }
1184 node := t.a.nodes[int(id)]
1185 if block_val := t.const_block_value(node) {
1186 return t.transform_const_value(block_val)
1187 }
1188 return t.transform_expr(id)
1189}
1190
1191// const_block_value supports const block value handling for Transformer.
1192fn (t &Transformer) const_block_value(node flat.Node) ?flat.NodeId {
1193 if node.kind != .block || node.children_count == 0 {
1194 return none
1195 }
1196 for i := int(node.children_count) - 1; i >= 0; i-- {
1197 stmt_id := t.a.child(&node, i)
1198 stmt := t.a.nodes[int(stmt_id)]
1199 if stmt.kind == .empty {
1200 continue
1201 }
1202 if stmt.kind == .expr_stmt && stmt.children_count == 1 {
1203 return t.a.child(&stmt, 0)
1204 }
1205 break
1206 }
1207 return none
1208}
1209
1210// transform_const_string_interp transforms transform const string interp data for transform.
1211fn (mut t Transformer) transform_const_string_interp(_id flat.NodeId, node flat.Node) flat.NodeId {
1212 if node.children_count == 0 {
1213 return t.make_string_literal('')
1214 }
1215 outer_pending := t.pending_stmts.clone()
1216 t.pending_stmts.clear()
1217 mut parts := []flat.NodeId{cap: int(node.children_count)}
1218 for i in 0 .. node.children_count {
1219 child_id := t.a.child(&node, i)
1220 parts << t.transform_string_interp_part(child_id)
1221 }
1222 mut expr := parts[0]
1223 for i in 1 .. parts.len {
1224 expr = t.make_call_typed('string__plus', arr2(expr, parts[i]), 'string')
1225 }
1226 mut stmts := []flat.NodeId{}
1227 t.drain_pending(mut stmts)
1228 t.pending_stmts = outer_pending
1229 if stmts.len == 0 {
1230 return expr
1231 }
1232 stmts << t.make_expr_stmt(expr)
1233 return t.make_block(stmts)
1234}
1235
1236fn (mut t Transformer) transform_string_interp_part(child_id flat.NodeId) flat.NodeId {
1237 mut expr_id := child_id
1238 mut format := ''
1239 child := t.a.nodes[int(child_id)]
1240 if child.kind == .directive && child.value == 'string_interp_format' && child.children_count > 0 {
1241 expr_id = t.a.child(&child, 0)
1242 format = child.typ
1243 }
1244 transformed := t.transform_expr(expr_id)
1245 mut typ := t.node_type(transformed)
1246 if typ.len == 0 {
1247 typ = t.reliable_stringify_type(transformed)
1248 }
1249 if typ.len == 0 {
1250 typ = t.reliable_stringify_type(expr_id)
1251 }
1252 if typ.len == 0 {
1253 typ = t.node_type(expr_id)
1254 }
1255 if typ.len == 0 {
1256 typ = 'string'
1257 }
1258 return t.wrap_formatted_string_conversion(transformed, typ, format)
1259}
1260
1261// transform_fn_body transforms transform fn body data for transform.
1262// try_heap_escaping_amp reports whether a `p := &v` decl is an escaping address of
1263// a value local that must be heap-copied: `p` is in escaping_amp_ptrs (returned)
1264// and `v` resolves to a non-reference value type.
1265fn (t &Transformer) try_heap_escaping_amp(node flat.Node, rhs_id flat.NodeId) bool {
1266 lhs := t.a.nodes[int(t.a.child(&node, 0))]
1267 if lhs.kind != .ident || lhs.value !in t.escaping_amp_ptrs {
1268 return false
1269 }
1270 rhs := t.a.nodes[int(rhs_id)]
1271 if rhs.kind != .prefix || rhs.op != .amp || rhs.children_count == 0 {
1272 return false
1273 }
1274 amp_child := t.a.child(&rhs, 0)
1275 amp_node := t.a.nodes[int(amp_child)]
1276 if amp_node.kind != .ident {
1277 return false
1278 }
1279 // The source local was moved to the heap at its declaration: the alias is now just that
1280 // `&T` pointer (handled below), regardless of its rewritten pointer type.
1281 if amp_node.value in t.heaped_amp_locals {
1282 return true
1283 }
1284 local_type := t.node_type(amp_child)
1285 return local_type.len > 0 && !local_type.starts_with('&') && !local_type.starts_with('[]')
1286 && !local_type.starts_with('map[') && !local_type.starts_with('?')
1287 && !local_type.starts_with('!')
1288}
1289
1290// heap_escaping_amp_rhs rewrites `&v` into `(&T)memdup(&v, sizeof(T))`, a heap copy
1291// of the value local `v` so the escaping pointer outlives the stack frame. When `v` was
1292// itself moved to the heap at its declaration, the alias is simply that pointer — copying
1293// would resurrect the stale-mutation bug the move avoids.
1294fn (mut t Transformer) heap_escaping_amp_rhs(rhs_id flat.NodeId) flat.NodeId {
1295 rhs := t.a.nodes[int(rhs_id)]
1296 amp_child := t.a.child(&rhs, 0)
1297 amp_node := t.a.nodes[int(amp_child)]
1298 if amp_node.kind == .ident && amp_node.value in t.heaped_amp_locals {
1299 return t.transform_expr(amp_child)
1300 }
1301 local_type := t.node_type(amp_child)
1302 addr := t.make_prefix(.amp, t.transform_expr(amp_child))
1303 size := t.make_sizeof_type(local_type)
1304 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
1305 return t.make_cast('&${local_type}', dup, '&${local_type}')
1306}
1307
1308// heapable_value_type reports whether a local of this declared type can be moved to the heap
1309// as a `&T` — a plain value type, not an already-reference / container / optional type (those
1310// either carry their own indirection or are not addressable as a single `T`).
1311fn (t &Transformer) heapable_value_type(typ string) bool {
1312 return typ.len > 0 && !typ.starts_with('&') && !typ.starts_with('[]')
1313 && !typ.starts_with('map[') && !typ.starts_with('?') && !typ.starts_with('!')
1314 && !typ.starts_with('[') && typ != 'unknown' && typ != 'void'
1315}
1316
1317// heap_escaping_source_decl rewrites `mut v := <init>` (where `&v` escapes) into a heap
1318// allocation so `v` is a `&T` to a heap object. A struct literal becomes `&T{..}` (the cgen
1319// memdup's it); any other initializer is copied into a stack temp and memdup'd. Subsequent
1320// `v.field = ..` writes then mutate the heap object the returned pointer alias also sees.
1321fn (mut t Transformer) heap_escaping_source_decl(node flat.Node, var_name string, elem_typ string) []flat.NodeId {
1322 rhs_id := t.a.child(&node, 1)
1323 rhs := t.a.nodes[int(rhs_id)]
1324 ptr_typ := '&${elem_typ}'
1325 mut stmts := []flat.NodeId{}
1326 transformed_init := t.transform_expr(rhs_id)
1327 // Statements lifted out while transforming the initializer must precede the heap decl.
1328 t.drain_pending(mut stmts)
1329 mut heap_rhs := flat.NodeId(0)
1330 if rhs.kind == .struct_init {
1331 heap_rhs = t.make_prefix(.amp, transformed_init)
1332 } else {
1333 tmp := t.new_temp('esc')
1334 stmts << t.make_decl_assign_typed(tmp, transformed_init, elem_typ)
1335 addr := t.make_prefix(.amp, t.make_ident(tmp))
1336 size := t.make_sizeof_type(elem_typ)
1337 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
1338 heap_rhs = t.make_cast(ptr_typ, dup, ptr_typ)
1339 }
1340 t.heaped_amp_locals[var_name] = true
1341 // The local is now a `&T`, so its compound/postfix mutations (`v += 1`, `v++`) must store
1342 // through the pointer (`*v += 1`); mark it as a pointer-value lvalue so that lowering fires.
1343 t.pointer_value_lvalues[var_name] = true
1344 stmts << t.make_decl_assign_typed(var_name, heap_rhs, ptr_typ)
1345 return stmts
1346}
1347
1348// mark_escaping_amp_ptrs runs a structural pre-pass over a function body to find
1349// `p := &v` declarations whose pointer `p` is later returned. Such a `v` is a local
1350// value whose address escapes, so it must be heap-copied (V auto-heaps it); the
1351// names are recorded in `escaping_amp_ptrs` and consumed by the decl-assign
1352// transform. Purely structural (no type info needed here): the type check happens
1353// at rewrite time when `v`'s type is known.
1354fn (mut t Transformer) mark_escaping_amp_ptrs(body_ids []flat.NodeId) {
1355 t.reset_escaping_amp_state()
1356 mut amp_ptrs := map[string]bool{}
1357 mut amp_sources := map[string]string{} // pointer `p` -> source local `v`
1358 mut ptr_aliases := map[string]string{} // copy `q := p` -> aliased pointer `p`
1359 mut returned := map[string]bool{}
1360 for id in body_ids {
1361 t.scan_escape_pass(id, mut amp_ptrs, mut amp_sources, mut ptr_aliases, mut returned)
1362 }
1363 // A pointer may be returned through a copy (`p := &v; q := p; return q`): `q` is collected
1364 // as returned but `p` is not, so propagate "returned" backward along the `q := p` aliases
1365 // until a fixpoint. Then `p` (and its source `v`) is recognised as escaping below.
1366 for _ in 0 .. ptr_aliases.len {
1367 mut changed := false
1368 for q, p in ptr_aliases {
1369 if q in returned && p !in returned {
1370 returned[p] = true
1371 changed = true
1372 }
1373 }
1374 if !changed {
1375 break
1376 }
1377 }
1378 for name, _ in amp_ptrs {
1379 if name in returned {
1380 t.escaping_amp_ptrs[name] = true
1381 if src := amp_sources[name] {
1382 t.escaping_amp_sources[src] = true
1383 }
1384 }
1385 }
1386}
1387
1388fn (mut t Transformer) reset_escaping_amp_state() {
1389 t.escaping_amp_ptrs.clear()
1390 t.escaping_amp_sources.clear()
1391 t.heaped_amp_locals.clear()
1392 // Cleared per function: heaped locals add their names below (in heap_escaping_source_decl);
1393 // for-loop element vars set and restore their own entries within the loop body.
1394 t.pointer_value_lvalues.clear()
1395}
1396
1397// scan_escape_pass recursively collects, in a function-body subtree, (a) the LHS
1398// names of `p := &ident` declarations into `amp_ptrs` (and the source `ident` into
1399// `amp_sources[p]`), (b) plain pointer copies `q := p` into `ptr_aliases[q] = p`, and
1400// (c) every ident name appearing inside a return statement into `returned`.
1401fn (mut t Transformer) scan_escape_pass(id flat.NodeId, mut amp_ptrs map[string]bool, mut amp_sources map[string]string, mut ptr_aliases map[string]string, mut returned map[string]bool) {
1402 if int(id) < 0 || int(id) >= t.a.nodes.len {
1403 return
1404 }
1405 node := t.a.nodes[int(id)]
1406 if node.kind == .decl_assign && node.children_count == 2 {
1407 lhs := t.a.nodes[int(t.a.child(&node, 0))]
1408 rhs := t.a.nodes[int(t.a.child(&node, 1))]
1409 if lhs.kind == .ident && lhs.value.len > 0 && rhs.kind == .prefix && rhs.op == .amp
1410 && rhs.children_count > 0 {
1411 amp_child := t.a.nodes[int(t.a.child(&rhs, 0))]
1412 if amp_child.kind == .ident {
1413 amp_ptrs[lhs.value] = true
1414 amp_sources[lhs.value] = amp_child.value
1415 }
1416 } else if lhs.kind == .ident && lhs.value.len > 0 && rhs.kind == .ident && rhs.value.len > 0 {
1417 // `q := p` aliases an existing pointer; recorded so a returned alias still marks the
1418 // underlying `p := &v` as escaping.
1419 ptr_aliases[lhs.value] = rhs.value
1420 }
1421 }
1422 if node.kind == .return_stmt {
1423 for i in 0 .. node.children_count {
1424 t.collect_return_escape_idents(t.a.child(&node, i), mut returned)
1425 }
1426 }
1427 for i in 0 .. node.children_count {
1428 t.scan_escape_pass(t.a.child(&node, i), mut amp_ptrs, mut amp_sources, mut ptr_aliases, mut
1429 returned)
1430 }
1431}
1432
1433// collect_return_escape_idents gathers the idents in a return-expression subtree that occupy an
1434// actual escape position — the returned value itself, or a member of a returned aggregate
1435// (struct/array/map literal, multi-return). It deliberately stops at operators that consume their
1436// operands into a fresh value: infix (`==`, `&&`, arithmetic, …), postfix, `is`/`in`, and any
1437// non-`&` prefix (deref `*p`, `!x`, `-x`). That way a pointer that is merely compared or
1438// dereferenced in the return expression — e.g. `return p == p && v == 1` — is not mistaken for a
1439// pointer that escapes, so its source local is not needlessly heap-moved (which would also make
1440// later non-pointer uses of that local read through an `int*`).
1441fn (mut t Transformer) collect_return_escape_idents(id flat.NodeId, mut names map[string]bool) {
1442 if int(id) < 0 || int(id) >= t.a.nodes.len {
1443 return
1444 }
1445 node := t.a.nodes[int(id)]
1446 match node.kind {
1447 .ident {
1448 if node.value.len > 0 {
1449 names[node.value] = true
1450 }
1451 return
1452 }
1453 .infix, .postfix, .is_expr, .in_expr {
1454 // These yield a new scalar/bool; their operands do not escape through the return.
1455 return
1456 }
1457 .prefix {
1458 // `&x` propagates an address (which may escape); any other prefix (`*x`, `!x`, `-x`)
1459 // produces a fresh value, so its operand does not escape.
1460 if node.op != .amp {
1461 return
1462 }
1463 }
1464 else {}
1465 }
1466
1467 for i in 0 .. node.children_count {
1468 t.collect_return_escape_idents(t.a.child(&node, i), mut names)
1469 }
1470}
1471
1472fn (mut t Transformer) transform_fn_body(fn_idx int) {
1473 fn_node := t.a.nodes[fn_idx]
1474 t.cur_fn_name = fn_node.value
1475 old_is_generic := t.cur_fn_is_generic
1476 t.cur_fn_is_generic = t.fn_decl_has_unresolved_generics(fn_node, t.cur_module)
1477 param_count := t.fn_body_param_count(fn_node)
1478 param_types := t.fn_body_param_types(fn_node, param_count)
1479 t.cur_fn_ret_type = t.fn_body_return_type(fn_node)
1480 t.reset_var_types()
1481 t.smartcast_stack.clear()
1482 // Collect param types
1483 mut param_idx := 0
1484 for i in 0 .. fn_node.children_count {
1485 child_id := t.a.children[fn_node.children_start + i]
1486 if int(child_id) < 0 {
1487 continue
1488 }
1489 child := t.a.nodes[int(child_id)]
1490 if node_kind_id(child) == 75 && child.value.len > 0 && child.typ.len > 0 {
1491 raw_typ := t.normalize_type_alias(child.typ)
1492 mut typ := if raw_typ.len > 0 {
1493 raw_typ
1494 } else if param_idx < param_types.len {
1495 t.normalize_type_alias(param_types[param_idx].name())
1496 } else {
1497 ''
1498 }
1499 if typ.starts_with('&') && raw_typ.len > 0 && !raw_typ.starts_with('&')
1500 && t.normalize_type_alias(typ[1..]) == raw_typ {
1501 typ = raw_typ
1502 }
1503 t.set_var_type(child.value, typ)
1504 param_idx++
1505 }
1506 }
1507 mut body_ids := []flat.NodeId{cap: int(fn_node.children_count)}
1508 for i in 0 .. fn_node.children_count {
1509 child_id := t.a.children[fn_node.children_start + i]
1510 if int(child_id) < 0 {
1511 continue
1512 }
1513 child := t.a.nodes[int(child_id)]
1514 if node_kind_id(child) != 75 {
1515 body_ids << child_id
1516 }
1517 }
1518 if t.cur_fn_ret_type == 'void' {
1519 t.reset_escaping_amp_state()
1520 } else {
1521 t.mark_escaping_amp_ptrs(body_ids)
1522 }
1523 new_body := t.transform_stmts(body_ids)
1524 // Rebuild function children: params then new body
1525 start := t.a.children.len
1526 for i in 0 .. fn_node.children_count {
1527 child_id := t.a.children[fn_node.children_start + i]
1528 if int(child_id) < 0 {
1529 continue
1530 }
1531 child := t.a.nodes[int(child_id)]
1532 if node_kind_id(child) == 75 {
1533 t.a.children << child_id
1534 }
1535 }
1536 for id in new_body {
1537 t.a.children << id
1538 }
1539 count := t.a.children.len - start
1540 t.a.nodes[fn_idx] = flat.Node{
1541 kind: .fn_decl
1542 kind_id: 61
1543 op: fn_node.op
1544 children_start: start
1545 children_count: flat.child_count(count)
1546 pos: fn_node.pos
1547 value: fn_node.value
1548 typ: fn_node.typ
1549 generic_params: fn_node.generic_params
1550 }
1551 t.smartcast_stack.clear()
1552 t.cur_fn_is_generic = old_is_generic
1553}
1554
1555// fn_body_param_types supports fn body param types handling for Transformer.
1556fn (t &Transformer) fn_body_param_types(fn_node flat.Node, expected int) []types.Type {
1557 if isnil(t.tc) {
1558 return []types.Type{}
1559 }
1560 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1561 qname := '${t.cur_module}.${fn_node.value}'
1562 if params := t.fn_param_types_for_name(qname, expected) {
1563 return params
1564 }
1565 cqname := c_name(qname)
1566 if cqname != qname {
1567 if params := t.fn_param_types_for_name(cqname, expected) {
1568 return params
1569 }
1570 }
1571 }
1572 if params := t.fn_param_types_for_name(fn_node.value, expected) {
1573 return params
1574 }
1575 cname := c_name(fn_node.value)
1576 if cname != fn_node.value {
1577 if params := t.fn_param_types_for_name(cname, expected) {
1578 return params
1579 }
1580 }
1581 return []types.Type{}
1582}
1583
1584// fn_param_types_for_name supports fn param types for name handling for Transformer.
1585fn (t &Transformer) fn_param_types_for_name(name string, expected int) ?[]types.Type {
1586 params := t.tc.fn_param_types[name] or { return none }
1587 if expected != 0 && params.len != expected {
1588 return none
1589 }
1590 return params
1591}
1592
1593// fn_body_param_count supports fn body param count handling for Transformer.
1594fn (t &Transformer) fn_body_param_count(fn_node flat.Node) int {
1595 mut n := 0
1596 for i in 0 .. fn_node.children_count {
1597 child := t.a.child_node(&fn_node, i)
1598 if child.kind == .param {
1599 n++
1600 }
1601 }
1602 return n
1603}
1604
1605// fn_body_return_type supports fn body return type handling for Transformer.
1606fn (t &Transformer) fn_body_return_type(fn_node flat.Node) string {
1607 if !isnil(t.tc) {
1608 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1609 qname := '${t.cur_module}.${fn_node.value}'
1610 if ret := t.fn_return_type_for_name(qname) {
1611 return ret
1612 }
1613 cqname := c_name(qname)
1614 if cqname != qname {
1615 if ret := t.fn_return_type_for_name(cqname) {
1616 return ret
1617 }
1618 }
1619 }
1620 if ret := t.fn_return_type_for_name(fn_node.value) {
1621 return ret
1622 }
1623 cname := c_name(fn_node.value)
1624 if cname != fn_node.value {
1625 if ret := t.fn_return_type_for_name(cname) {
1626 return ret
1627 }
1628 }
1629 }
1630 return t.normalize_type_alias(fn_node.typ)
1631}
1632
1633// fn_return_type_for_name supports fn return type for name handling for Transformer.
1634fn (t &Transformer) fn_return_type_for_name(name string) ?string {
1635 ret := t.tc.fn_ret_types[name] or { return none }
1636 return t.normalize_type_alias(ret.name())
1637}
1638
1639// --- statement list driver ---
1640
1641// transform_stmts transforms transform stmts data for transform.
1642pub fn (mut t Transformer) transform_stmts(ids []flat.NodeId) []flat.NodeId {
1643 mut result := []flat.NodeId{cap: ids.len}
1644 mut post_if_smartcasts := 0
1645 defer {
1646 for _ in 0 .. post_if_smartcasts {
1647 t.pop_smartcast()
1648 }
1649 }
1650 mut i := 0
1651 for i < ids.len {
1652 id := ids[i]
1653 if int(id) >= 0 && i + 1 < ids.len {
1654 node := t.a.nodes[int(id)]
1655 if node_kind_id(node) == 44 && node.children_count == 0 && t.cur_fn_ret_type.len > 0
1656 && t.cur_fn_ret_type != 'void' {
1657 next_id := ids[i + 1]
1658 next_node := t.a.nodes[int(next_id)]
1659 if node_kind_id(next_node) == 39 && next_node.children_count > 0 {
1660 expr_id := t.a.child(&next_node, 0)
1661 start := t.a.children.len
1662 t.a.children << expr_id
1663 merged_return := t.a.add_node(flat.Node{
1664 kind: .return_stmt
1665 children_start: start
1666 children_count: 1
1667 typ: node.typ
1668 })
1669 expanded := t.transform_stmt(merged_return)
1670 t.drain_pending(mut result)
1671 for eid in expanded {
1672 result << eid
1673 }
1674 i += 2
1675 continue
1676 }
1677 }
1678 if node.kind == .label_stmt {
1679 next_id := ids[i + 1]
1680 next_node := t.a.nodes[int(next_id)]
1681 if next_node.kind in [.for_stmt, .for_in_stmt] {
1682 expanded := t.transform_labeled_loop(node.value, next_id, next_node)
1683 t.drain_pending(mut result)
1684 for eid in expanded {
1685 result << eid
1686 }
1687 i += 2
1688 continue
1689 }
1690 }
1691 }
1692 expanded := t.transform_stmt(id)
1693 t.drain_pending(mut result)
1694 for eid in expanded {
1695 result << eid
1696 }
1697 for info in t.post_if_exit_smartcasts(id) {
1698 t.push_smartcast(info.expr_name, info.variant_name, info.sum_type_name)
1699 post_if_smartcasts++
1700 }
1701 i++
1702 }
1703 t.drain_pending(mut result)
1704 return result
1705}
1706
1707// transform_labeled_loop transforms transform labeled loop data for transform.
1708fn (mut t Transformer) transform_labeled_loop(label string, loop_id flat.NodeId, loop_node flat.Node) []flat.NodeId {
1709 if label.len == 0 {
1710 return t.transform_stmt(loop_id)
1711 }
1712 continue_label := '${label}_continue'
1713 break_label := '${label}_break'
1714 body_start := if loop_node.kind == .for_in_stmt { loop_node.value.int() } else { 3 }
1715 mut children := []flat.NodeId{cap: int(loop_node.children_count) + 1}
1716 for i in 0 .. loop_node.children_count {
1717 children << t.a.child(&loop_node, i)
1718 }
1719 if body_start <= children.len {
1720 children << t.a.add_val(.label_stmt, continue_label)
1721 }
1722 start := t.a.children.len
1723 for child in children {
1724 t.a.children << child
1725 }
1726 new_loop := t.a.add_node(flat.Node{
1727 kind: loop_node.kind
1728 op: loop_node.op
1729 children_start: start
1730 children_count: flat.child_count(children.len)
1731 pos: loop_node.pos
1732 value: loop_node.value
1733 typ: loop_node.typ
1734 })
1735 mut result := []flat.NodeId{}
1736 result << t.a.add_val(.label_stmt, label)
1737 result << t.transform_stmt(new_loop)
1738 result << t.a.add_val(.label_stmt, break_label)
1739 return result
1740}
1741
1742// transform_stmt transforms transform stmt data for transform.
1743pub fn (mut t Transformer) transform_stmt(id flat.NodeId) []flat.NodeId {
1744 if int(id) < 0 {
1745 return arr1(id)
1746 }
1747 node := t.a.nodes[int(id)]
1748 kind_id := node_kind_id(node)
1749 if kind_id == 44 {
1750 return t.transform_return_stmt(id, node)
1751 }
1752 if kind_id == 40 || kind_id == 42 || kind_id == 43 {
1753 return t.transform_assign_stmt(id, node)
1754 }
1755 if kind_id == 41 {
1756 return t.transform_decl_assign_stmt(id, node)
1757 }
1758 if kind_id == 39 {
1759 return t.transform_expr_stmt(id, node)
1760 }
1761 if kind_id == 46 {
1762 return t.transform_for_stmt(id, node)
1763 }
1764 if kind_id == 47 {
1765 return t.transform_for_in_stmt(id, node)
1766 }
1767 if kind_id == 45 {
1768 return t.transform_block_stmt(id, node)
1769 }
1770 if kind_id == 15 {
1771 return t.transform_if_stmt(id, node)
1772 }
1773 if kind_id == 50 {
1774 return arr1(t.lower_one_match(node))
1775 }
1776 if kind_id == 52 {
1777 return t.transform_defer_stmt(id, node)
1778 }
1779 if kind_id == 53 || kind_id == 56 {
1780 return t.transform_children_stmt(id, node)
1781 }
1782 match node.kind {
1783 .return_stmt {
1784 return t.transform_return_stmt(id, node)
1785 }
1786 .assign, .selector_assign, .index_assign {
1787 return t.transform_assign_stmt(id, node)
1788 }
1789 .decl_assign {
1790 return t.transform_decl_assign_stmt(id, node)
1791 }
1792 .expr_stmt {
1793 return t.transform_expr_stmt(id, node)
1794 }
1795 .for_stmt {
1796 return t.transform_for_stmt(id, node)
1797 }
1798 .for_in_stmt {
1799 return t.transform_for_in_stmt(id, node)
1800 }
1801 .block {
1802 return t.transform_block_stmt(id, node)
1803 }
1804 .comptime_if {
1805 return t.transform_comptime_if_stmt(id, node)
1806 }
1807 .if_expr {
1808 return t.transform_if_stmt(id, node)
1809 }
1810 .match_stmt {
1811 return arr1(t.lower_one_match(node))
1812 }
1813 .defer_stmt {
1814 return t.transform_defer_stmt(id, node)
1815 }
1816 .assert_stmt {
1817 return t.transform_children_stmt(id, node)
1818 }
1819 .select_stmt {
1820 return t.transform_children_stmt(id, node)
1821 }
1822 else {
1823 return arr1(id)
1824 }
1825 }
1826}
1827
1828// transform_expr transforms transform expr data for transform.
1829pub fn (mut t Transformer) transform_expr(id flat.NodeId) flat.NodeId {
1830 if int(id) < 0 {
1831 return id
1832 }
1833 node := t.a.nodes[int(id)]
1834 kind_id := node_kind_id(node)
1835 if kind_id == 8 {
1836 return t.transform_infix_expr(id, node)
1837 }
1838 if kind_id == 12 {
1839 return t.transform_call_expr(id, node)
1840 }
1841 if kind_id == 15 {
1842 return t.transform_if_expr(id, node)
1843 }
1844 if kind_id == 16 {
1845 return t.transform_struct_init(id, node)
1846 }
1847 if kind_id == 17 {
1848 return t.transform_field_init_expr(id, node)
1849 }
1850 if kind_id == 14 {
1851 return t.transform_index_expr(id, node)
1852 }
1853 if kind_id == 6 {
1854 return t.transform_string_interp(id, node)
1855 }
1856 if kind_id == 13 {
1857 return t.transform_selector_expr(id, node)
1858 }
1859 if kind_id == 22 {
1860 return t.transform_or_expr(id, node)
1861 }
1862 if kind_id == 24 {
1863 return t.transform_as_expr(id, node)
1864 }
1865 if kind_id == 9 {
1866 return t.transform_prefix_expr(id, node)
1867 }
1868 if kind_id == 11 {
1869 return t.transform_paren_expr(id, node)
1870 }
1871 if kind_id == 10 {
1872 return t.transform_postfix_expr(id, node)
1873 }
1874 if kind_id == 23 {
1875 return t.transform_cast_expr(id, node)
1876 }
1877 if kind_id == 18 {
1878 return t.transform_array_literal(id, node)
1879 }
1880 if kind_id == 19 {
1881 return t.transform_array_init_expr(id, node)
1882 }
1883 if kind_id == 20 {
1884 return t.transform_map_init(id, node)
1885 }
1886 if kind_id == 38 {
1887 return t.transform_in_expr(id, node)
1888 }
1889 if kind_id == 37 {
1890 return t.transform_is_expr(id, node)
1891 }
1892 if kind_id == 50 {
1893 return t.lower_one_match(node)
1894 }
1895 if kind_id == 45 {
1896 return t.transform_block_expr(id, node)
1897 }
1898 if kind_id == 31 {
1899 return t.transform_lock_expr(id, node)
1900 }
1901 if kind_id == 34 {
1902 return t.transform_typeof_expr(id, node)
1903 }
1904 if kind_id == 7 {
1905 return t.transform_ident_expr(id, node)
1906 }
1907 if kind_id == 26 {
1908 return t.transform_assoc_expr(id, node)
1909 }
1910 if kind_id == 21 {
1911 return t.lift_fn_literal(id, node)
1912 }
1913 if kind_id == 30 || kind_id == 35 || kind_id == 27 || kind_id == 56 || kind_id == 57 {
1914 return t.transform_children_expr(id, node)
1915 }
1916 if kind_id == 1 || kind_id == 2 || kind_id == 3 || kind_id == 4 || kind_id == 5 || kind_id == 28
1917 || kind_id == 29 || kind_id == 25 || kind_id == 33 || kind_id == 36 {
1918 return id
1919 }
1920 match node.kind {
1921 .infix {
1922 return t.transform_infix_expr(id, node)
1923 }
1924 .call {
1925 return t.transform_call_expr(id, node)
1926 }
1927 .if_expr {
1928 return t.transform_if_expr(id, node)
1929 }
1930 .struct_init {
1931 return t.transform_struct_init(id, node)
1932 }
1933 .field_init {
1934 return t.transform_field_init_expr(id, node)
1935 }
1936 .index {
1937 return t.transform_index_expr(id, node)
1938 }
1939 .string_interp {
1940 return t.transform_string_interp(id, node)
1941 }
1942 .selector {
1943 return t.transform_selector_expr(id, node)
1944 }
1945 .or_expr {
1946 return t.transform_or_expr(id, node)
1947 }
1948 .as_expr {
1949 return t.transform_as_expr(id, node)
1950 }
1951 .prefix {
1952 return t.transform_prefix_expr(id, node)
1953 }
1954 .paren {
1955 return t.transform_paren_expr(id, node)
1956 }
1957 .postfix {
1958 return t.transform_postfix_expr(id, node)
1959 }
1960 .cast_expr {
1961 return t.transform_cast_expr(id, node)
1962 }
1963 .array_literal {
1964 return t.transform_array_literal(id, node)
1965 }
1966 .array_init {
1967 return t.transform_array_init_expr(id, node)
1968 }
1969 .map_init {
1970 return t.transform_map_init(id, node)
1971 }
1972 .sql_expr {
1973 return t.transform_sql_expr(id, node)
1974 }
1975 .in_expr {
1976 return t.transform_in_expr(id, node)
1977 }
1978 .is_expr {
1979 return t.transform_is_expr(id, node)
1980 }
1981 .match_stmt {
1982 return t.lower_one_match(node)
1983 }
1984 .block {
1985 return t.transform_block_expr(id, node)
1986 }
1987 .lock_expr {
1988 return t.transform_lock_expr(id, node)
1989 }
1990 .typeof_expr {
1991 return t.transform_typeof_expr(id, node)
1992 }
1993 .ident {
1994 return t.transform_ident_expr(id, node)
1995 }
1996 .assoc {
1997 return t.transform_assoc_expr(id, node)
1998 }
1999 .fn_literal {
2000 return t.lift_fn_literal(id, node)
2001 }
2002 .lambda_expr, .spawn_expr, .dump_expr, .range, .select_stmt, .select_branch {
2003 return t.transform_children_expr(id, node)
2004 }
2005 .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal, .nil_literal,
2006 .none_expr, .enum_val, .sizeof_expr, .offsetof_expr {
2007 // leaf/simple nodes - pass through unchanged
2008 return id
2009 }
2010 else {
2011 return id
2012 }
2013 }
2014}
2015
2016// transform_lvalue transforms transform lvalue data for transform.
2017pub fn (mut t Transformer) transform_lvalue(id flat.NodeId) flat.NodeId {
2018 if int(id) < 0 {
2019 return id
2020 }
2021 node := t.a.nodes[int(id)]
2022 match node.kind {
2023 .ident {
2024 return id
2025 }
2026 .selector {
2027 if node.children_count == 0 {
2028 return id
2029 }
2030 if t.selector_chain_has_sum_shared_field(id) {
2031 value := t.transform_selector_expr(id, node)
2032 mut value_type := t.node_type(id)
2033 if value_type.len == 0 {
2034 value_type = t.node_type(value)
2035 }
2036 return t.stable_transformed_expr_for_reuse(value, value_type, 'lvalue')
2037 }
2038 full_key := t.expr_key(id)
2039 if t.has_smartcast(full_key) {
2040 return t.transform_selector_expr(id, node)
2041 }
2042 base_id := t.a.child(&node, 0)
2043 base_key := t.expr_key(base_id)
2044 if t.has_smartcast(base_key) {
2045 return t.transform_selector_expr(id, node)
2046 }
2047 base := t.transform_lvalue(t.a.child(&node, 0))
2048 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2049 new_children << base
2050 for i in 1 .. node.children_count {
2051 new_children << t.transform_expr(t.a.child(&node, i))
2052 }
2053 start := t.a.children.len
2054 for child in new_children {
2055 t.a.children << child
2056 }
2057 return t.a.add_node(flat.Node{
2058 kind: .selector
2059 op: node.op
2060 children_start: start
2061 children_count: flat.child_count(new_children.len)
2062 pos: node.pos
2063 value: node.value
2064 typ: node.typ
2065 })
2066 }
2067 .index {
2068 if node.children_count == 0 {
2069 return id
2070 }
2071 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2072 new_children << t.transform_expr(t.a.child(&node, 0))
2073 for i in 1 .. node.children_count {
2074 new_children << t.transform_expr(t.a.child(&node, i))
2075 }
2076 start := t.a.children.len
2077 for child in new_children {
2078 t.a.children << child
2079 }
2080 return t.a.add_node(flat.Node{
2081 kind: .index
2082 op: node.op
2083 children_start: start
2084 children_count: flat.child_count(new_children.len)
2085 pos: node.pos
2086 value: node.value
2087 typ: node.typ
2088 })
2089 }
2090 .prefix {
2091 if node.op == .mul && node.children_count > 0 {
2092 child := t.transform_expr(t.a.child(&node, 0))
2093 start := t.a.children.len
2094 t.a.children << child
2095 return t.a.add_node(flat.Node{
2096 kind: .prefix
2097 op: node.op
2098 children_start: start
2099 children_count: 1
2100 pos: node.pos
2101 value: node.value
2102 typ: node.typ
2103 })
2104 }
2105 return t.transform_expr(id)
2106 }
2107 .paren {
2108 if node.children_count == 0 {
2109 return id
2110 }
2111 child := t.transform_lvalue(t.a.child(&node, 0))
2112 start := t.a.children.len
2113 t.a.children << child
2114 return t.a.add_node(flat.Node{
2115 kind: .paren
2116 op: node.op
2117 children_start: start
2118 children_count: 1
2119 pos: node.pos
2120 value: node.value
2121 typ: node.typ
2122 })
2123 }
2124 else {
2125 return t.transform_expr(id)
2126 }
2127 }
2128}
2129
2130// --- stmt handlers (skeleton - identity transforms with child recursion) ---
2131
2132// transform_return_stmt transforms transform return stmt data for transform.
2133fn (mut t Transformer) transform_return_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
2134 if node.children_count == 0 {
2135 return arr1(id)
2136 }
2137 if expanded := t.try_expand_return_if(id, node) {
2138 return expanded
2139 }
2140 if expanded := t.try_expand_return_match(id, node) {
2141 return expanded
2142 }
2143 if direct := t.try_return_direct_optional_expr(node) {
2144 return direct
2145 }
2146 if expanded := t.try_expand_return_optional_expr(node) {
2147 return expanded
2148 }
2149 if node.children_count == 1 {
2150 child_id := t.a.child(&node, 0)
2151 if t.is_optional_type_name(t.cur_fn_ret_type) && t.return_expr_is_err(child_id) {
2152 return t.with_pending_before(t.make_none_return_stmt())
2153 }
2154 }
2155 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2156 for i in 0 .. node.children_count {
2157 child_id := t.a.child(&node, i)
2158 new_children << t.transform_return_child(child_id, i, int(node.children_count))
2159 }
2160 start := t.a.children.len
2161 for nc in new_children {
2162 t.a.children << nc
2163 }
2164 new_id := t.a.add_node(flat.Node{
2165 kind: .return_stmt
2166 op: node.op
2167 children_start: start
2168 children_count: node.children_count
2169 pos: node.pos
2170 value: node.value
2171 typ: node.typ
2172 })
2173 return t.with_pending_before(new_id)
2174}
2175
2176fn (mut t Transformer) return_values_with_extra(first_id flat.NodeId, extra_ids []flat.NodeId) []flat.NodeId {
2177 total := extra_ids.len + 1
2178 mut vals := []flat.NodeId{cap: total}
2179 vals << t.transform_return_child(first_id, 0, total)
2180 for i, extra_id in extra_ids {
2181 vals << t.transform_return_child(extra_id, i + 1, total)
2182 }
2183 return vals
2184}
2185
2186fn (mut t Transformer) transform_return_child(child_id flat.NodeId, child_index int, total_children int) flat.NodeId {
2187 if converted := t.fixed_array_return_value(child_id) {
2188 return converted
2189 }
2190 if copied := t.heap_copy_local_address_return(child_id) {
2191 return copied
2192 }
2193 target_type := t.return_child_target_type(child_index, total_children)
2194 if target_type.len > 0 && target_type !in t.sum_types && !t.is_optional_type_name(target_type) {
2195 return t.transform_expr_for_type(child_id, target_type)
2196 }
2197 return t.wrap_sum_return_expr(child_id)
2198}
2199
2200fn (t &Transformer) return_child_target_type(child_index int, total_children int) string {
2201 if total_children > 1 && !isnil(t.tc) && t.cur_fn_ret_type.len > 0 {
2202 if items := multi_return_types_from_type(t.tc.parse_type(t.cur_fn_ret_type), total_children) {
2203 if child_index >= 0 && child_index < items.len {
2204 return items[child_index].name()
2205 }
2206 }
2207 }
2208 return t.cur_fn_ret_type
2209}
2210
2211// heap_copy_local_address_return supports heap copy local address return handling for Transformer.
2212fn (mut t Transformer) heap_copy_local_address_return(child_id flat.NodeId) ?flat.NodeId {
2213 if !t.cur_fn_ret_type.starts_with('&') || int(child_id) < 0 {
2214 return none
2215 }
2216 node := t.a.nodes[int(child_id)]
2217 if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2218 return none
2219 }
2220 inner_id := t.a.child(&node, 0)
2221 inner := t.a.nodes[int(inner_id)]
2222 if inner.kind != .ident || inner.value.len == 0 {
2223 return none
2224 }
2225 local_type := t.var_type(inner.value)
2226 if local_type.len == 0 {
2227 return none
2228 }
2229 ret_base_type := t.cur_fn_ret_type[1..]
2230 if ret_base_type.len == 0 {
2231 return none
2232 }
2233 clean_local_type := t.normalize_type_alias(local_type)
2234 clean_ret_type := t.normalize_type_alias(ret_base_type)
2235 if clean_local_type != clean_ret_type && local_type != ret_base_type {
2236 return none
2237 }
2238 addr := t.make_prefix(.amp, t.make_ident(inner.value))
2239 size := t.make_sizeof_type(ret_base_type)
2240 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
2241 return t.make_cast(t.cur_fn_ret_type, dup, t.cur_fn_ret_type)
2242}
2243
2244// fixed_array_return_value supports fixed array return value handling for Transformer.
2245fn (mut t Transformer) fixed_array_return_value(child_id flat.NodeId) ?flat.NodeId {
2246 mut ret_type := t.cur_fn_ret_type
2247 if t.is_optional_type_name(ret_type) {
2248 ret_type = t.optional_base_type(ret_type)
2249 }
2250 // A function whose declared return type is itself a fixed array keeps
2251 // fixed-array (by-value) semantics; the C backend returns it via a wrapper
2252 // struct. Only a *dynamic* array return needs a fixed→dynamic conversion of a
2253 // fixed-array return value.
2254 if t.is_fixed_array_type(ret_type) {
2255 return none
2256 }
2257 return t.fixed_array_value_to_dynamic(child_id, ret_type)
2258}
2259
2260// fixed_array_value_to_dynamic converts a fixed-array *value* (e.g. a fixed-array
2261// const or variable, not a literal — those have their own lowering) to a dynamic
2262// array when `target_type` is `[]T` with a matching element type. Returns none
2263// when no conversion is needed/possible.
2264fn (mut t Transformer) fixed_array_value_to_dynamic(value_id flat.NodeId, target_type string) ?flat.NodeId {
2265 array_type := target_type
2266 if !array_type.starts_with('[]') {
2267 return none
2268 }
2269 child_type := t.node_type(value_id)
2270 if !t.is_fixed_array_type(child_type) || fixed_array_elem_type(child_type) != array_type[2..] {
2271 return none
2272 }
2273 return t.fixed_array_value_to_array(value_id, child_type, array_type)
2274}
2275
2276// transform_assign_stmt transforms transform assign stmt data for transform.
2277fn (mut t Transformer) transform_assign_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
2278 if node.children_count == 0 {
2279 return arr1(id)
2280 }
2281 if expanded := t.try_expand_multi_return_assign(node) {
2282 return expanded
2283 }
2284 if expanded := t.try_expand_plain_multi_assign(node) {
2285 return expanded
2286 }
2287 if lowered := t.try_lower_sum_shared_field_assign(node) {
2288 return lowered
2289 }
2290 if lowered := t.try_lower_optional_selector_lvalue_assign(node) {
2291 return lowered
2292 }
2293 if lowered := t.try_lower_pointer_value_assign(node) {
2294 return lowered
2295 }
2296 if lowered := t.try_lower_map_index_assign(node) {
2297 return lowered
2298 }
2299 // string `s += x` on a plain ident -> `s = string__plus(s, x)` (only when detectable as string)
2300 if expanded := t.try_lower_string_compound_assign(id, node) {
2301 return expanded
2302 }
2303 if expanded := t.try_lower_struct_compound_assign(node) {
2304 return expanded
2305 }
2306 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2307 for i in 0 .. node.children_count {
2308 child_id := t.a.child(&node, i)
2309 if i % 2 == 0 {
2310 new_children << t.transform_lvalue(child_id)
2311 } else {
2312 lhs_id := t.a.child(&node, i - 1)
2313 lhs := t.a.nodes[int(lhs_id)]
2314 mut lhs_type := if lhs.kind in [.selector, .index] {
2315 t.lvalue_type(lhs_id)
2316 } else {
2317 t.original_expr_type(lhs_id)
2318 }
2319 if lhs_type.len == 0 {
2320 lhs_type = t.lvalue_type(lhs_id)
2321 }
2322 // A value local moved to the heap (its type became `&T`) is assigned by storing a
2323 // value through the pointer (cgen emits `*v = ...`), so coerce the RHS to the value
2324 // type `T`, not `&T`. Otherwise a heaped-local RHS (`v = w`, both `&T`) is copied as a
2325 // pointer — aliasing `w`'s object — instead of dereferenced to its value.
2326 if lhs.kind == .ident && lhs.value in t.heaped_amp_locals && lhs_type.starts_with('&') {
2327 lhs_type = lhs_type[1..]
2328 }
2329 sum_target := t.assignment_sum_target(lhs_id, child_id, lhs_type)
2330 if node.op == .assign && sum_target.len > 0 {
2331 new_children << t.wrap_sum_value(child_id, sum_target)
2332 } else {
2333 new_children << t.transform_expr_for_type(child_id, lhs_type)
2334 }
2335 }
2336 }
2337 start := t.a.children.len
2338 for nc in new_children {
2339 t.a.children << nc
2340 }
2341 new_id := t.a.add_node(flat.Node{
2342 kind: node.kind
2343 op: node.op
2344 children_start: start
2345 children_count: node.children_count
2346 pos: node.pos
2347 value: node.value
2348 typ: node.typ
2349 })
2350 if node.kind == .assign && node.op == .left_shift_assign {
2351 t.annotate_left_shift_assign(new_id)
2352 }
2353 return t.with_pending_before(new_id)
2354}
2355
2356fn (mut t Transformer) try_lower_optional_selector_lvalue_assign(node flat.Node) ?[]flat.NodeId {
2357 if node.kind != .selector_assign || node.children_count != 2 {
2358 return none
2359 }
2360 lhs_id := t.a.child(&node, 0)
2361 rhs_id := t.a.child(&node, 1)
2362 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2363 return none
2364 }
2365 lowered_lhs, guard_source, guard_body, guard_mode := t.lower_optional_selector_lvalue(lhs_id) or {
2366 return none
2367 }
2368 mut result := []flat.NodeId{}
2369 t.drain_pending(mut result)
2370 not_ok := t.make_prefix(.not, t.make_selector(guard_source, 'ok', 'bool'))
2371 guard_stmts := t.optional_selector_lvalue_guard_stmts(guard_body, guard_mode, guard_source)
2372 result << t.make_if(not_ok, t.make_block(guard_stmts), t.make_empty())
2373 lhs_type := t.lvalue_type(lhs_id)
2374 sum_target := t.assignment_sum_target(lhs_id, rhs_id, lhs_type)
2375 rhs := if node.op == .assign && sum_target.len > 0 {
2376 t.wrap_sum_value(rhs_id, sum_target)
2377 } else {
2378 t.transform_expr_for_type(rhs_id, lhs_type)
2379 }
2380 t.drain_pending(mut result)
2381 result << t.make_assign_op(lowered_lhs, rhs, node.op)
2382 return result
2383}
2384
2385fn (mut t Transformer) optional_selector_lvalue_guard_stmts(body_id flat.NodeId, mode string, guard_source flat.NodeId) []flat.NodeId {
2386 err_expr := t.make_selector(guard_source, 'err', 'IError')
2387 if mode == '!' || mode == '?' {
2388 if t.is_optional_type_name(t.cur_fn_ret_type) {
2389 return arr1(t.make_return(t.make_optional_none_with_err(t.cur_fn_ret_type, err_expr),
2390 t.cur_fn_ret_type))
2391 }
2392 return arr1(t.make_panic_stmt('option/result propagation failed'))
2393 }
2394 return t.lower_or_body_to_stmts_with_err_expr(body_id, '', '', mode, err_expr)
2395}
2396
2397fn (mut t Transformer) lower_optional_selector_lvalue(id flat.NodeId) ?(flat.NodeId, flat.NodeId, flat.NodeId, string) {
2398 if int(id) < 0 {
2399 return none
2400 }
2401 node := t.a.nodes[int(id)]
2402 if node.kind != .selector || node.children_count == 0 || node.value.len == 0 {
2403 return none
2404 }
2405 base_id := t.a.child(&node, 0)
2406 base := t.a.nodes[int(base_id)]
2407 if base.kind == .or_expr && base.children_count >= 2 {
2408 return t.lower_optional_selector_lvalue_from_or(id, node, base)
2409 }
2410 if base.kind == .paren && base.children_count > 0 {
2411 inner_id := t.a.child(&base, 0)
2412 inner := t.a.nodes[int(inner_id)]
2413 if inner.kind == .or_expr && inner.children_count >= 2 {
2414 return t.lower_optional_selector_lvalue_from_or(id, node, inner)
2415 }
2416 }
2417 lowered_base, guard_source, guard_body, guard_mode := t.lower_optional_selector_lvalue(base_id) or {
2418 return none
2419 }
2420 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2421 new_children << lowered_base
2422 for i in 1 .. node.children_count {
2423 new_children << t.transform_expr(t.a.child(&node, i))
2424 }
2425 start := t.a.children.len
2426 for child in new_children {
2427 t.a.children << child
2428 }
2429 lowered := t.a.add_node(flat.Node{
2430 kind: .selector
2431 op: node.op
2432 children_start: start
2433 children_count: flat.child_count(new_children.len)
2434 pos: node.pos
2435 value: node.value
2436 typ: node.typ
2437 })
2438 return lowered, guard_source, guard_body, guard_mode
2439}
2440
2441fn (mut t Transformer) lower_optional_selector_lvalue_from_or(id flat.NodeId, node flat.Node, base flat.Node) ?(flat.NodeId, flat.NodeId, flat.NodeId, string) {
2442 source_id := t.a.child(&base, 0)
2443 if !t.optional_selector_lvalue_source(source_id) {
2444 return none
2445 }
2446 expr_type, value_type := t.or_expr_types(source_id, base.typ)
2447 if !t.is_optional_type_name(expr_type) || value_type.len == 0 || value_type == 'void' {
2448 return none
2449 }
2450 source := t.transform_lvalue(source_id)
2451 value_base := t.make_selector(source, 'value', value_type)
2452 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2453 new_children << value_base
2454 for i in 1 .. node.children_count {
2455 new_children << t.transform_expr(t.a.child(&node, i))
2456 }
2457 start := t.a.children.len
2458 for child in new_children {
2459 t.a.children << child
2460 }
2461 lhs_type := t.lvalue_type(id)
2462 lowered := t.a.add_node(flat.Node{
2463 kind: .selector
2464 op: if value_type.starts_with('&') { flat.Op.arrow } else { node.op }
2465 children_start: start
2466 children_count: flat.child_count(new_children.len)
2467 pos: node.pos
2468 value: node.value
2469 typ: if lhs_type.len > 0 { lhs_type } else { node.typ }
2470 })
2471 return lowered, source, t.a.child(&base, 1), base.value
2472}
2473
2474fn (t &Transformer) optional_selector_lvalue_source(id flat.NodeId) bool {
2475 if int(id) < 0 {
2476 return false
2477 }
2478 node := t.a.nodes[int(id)]
2479 match node.kind {
2480 .ident {
2481 return node.value.len > 0
2482 }
2483 .paren {
2484 if node.children_count == 0 {
2485 return false
2486 }
2487 return t.optional_selector_lvalue_source(t.a.child(&node, 0))
2488 }
2489 .selector {
2490 if node.children_count == 0 || node.value.len == 0 {
2491 return false
2492 }
2493 return t.optional_selector_lvalue_source(t.a.child(&node, 0))
2494 }
2495 else {
2496 return false
2497 }
2498 }
2499}
2500
2501fn (mut t Transformer) try_lower_struct_compound_assign(node flat.Node) ?[]flat.NodeId {
2502 if node.kind != .assign || node.children_count != 2 {
2503 return none
2504 }
2505 op_name := compound_assign_struct_operator_symbol(node.op) or { return none }
2506 lhs_id := t.a.child(&node, 0)
2507 rhs_id := t.a.child(&node, 1)
2508 lhs := t.a.nodes[int(lhs_id)]
2509 if lhs.kind != .ident || lhs.value.len == 0 {
2510 return none
2511 }
2512 mut lhs_type := t.var_type(lhs.value)
2513 if lhs_type.len == 0 {
2514 lhs_type = t.original_expr_type(lhs_id)
2515 }
2516 if lhs_type.starts_with('&') {
2517 return none
2518 }
2519 mut operator_type := t.struct_lookup_name(lhs_type)
2520 if operator_type.len == 0 {
2521 if _ := t.struct_operator_fn_name(lhs_type, op_name) {
2522 operator_type = lhs_type
2523 }
2524 }
2525 if operator_type.len == 0 {
2526 return none
2527 }
2528 method_name := t.struct_operator_fn_name(operator_type, op_name) or { return none }
2529 rhs := t.transform_expr_for_type(rhs_id, lhs_type)
2530 t.mark_fn_used_name(method_name)
2531 call := t.make_call_typed(method_name, arr2(t.make_ident(lhs.value), rhs), lhs_type)
2532 return arr1(t.make_assign(t.make_ident(lhs.value), call))
2533}
2534
2535fn compound_assign_struct_operator_symbol(op flat.Op) ?string {
2536 match op {
2537 .plus_assign { return '+' }
2538 .minus_assign { return '-' }
2539 .mul_assign { return '*' }
2540 .div_assign { return '/' }
2541 .mod_assign { return '%' }
2542 else {}
2543 }
2544
2545 return none
2546}
2547
2548// try_lower_sum_shared_field_assign
2549// supports helper handling in transform.
2550fn (mut t Transformer) try_lower_sum_shared_field_assign(node flat.Node) ?[]flat.NodeId {
2551 if node.kind !in [.assign, .selector_assign] || node.children_count != 2 {
2552 return none
2553 }
2554 lhs_id := t.a.child(&node, 0)
2555 rhs_id := t.a.child(&node, 1)
2556 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2557 return none
2558 }
2559 lhs := t.a.nodes[int(lhs_id)]
2560 if lhs.kind != .selector || lhs.children_count == 0 || lhs.value.len == 0 {
2561 return none
2562 }
2563 base_id := t.a.child(&lhs, 0)
2564 mut base_type := t.node_type(base_id)
2565 if base_type.len == 0 {
2566 base_type = t.original_expr_type(base_id)
2567 }
2568 field_type := t.sum_shared_field_type_name(base_type, lhs.value) or { return none }
2569 mut base := t.transform_lvalue(base_id)
2570 mut sum_type := base_type
2571 if !t.is_stable_expr_for_reuse(base) {
2572 clean_sum := t.trim_pointer_type(sum_type)
2573 ptr_type := if sum_type.starts_with('&') { sum_type } else { '&${clean_sum}' }
2574 addr := if sum_type.starts_with('&') {
2575 base
2576 } else {
2577 mut addr_expr := t.make_prefix(.amp, base)
2578 t.a.nodes[int(addr_expr)].typ = ptr_type
2579 addr_expr
2580 }
2581 tmp_name := t.new_temp('sum_lhs')
2582 t.pending_stmts << t.make_decl_assign_typed(tmp_name, addr, ptr_type)
2583 base = t.make_ident(tmp_name)
2584 sum_type = ptr_type
2585 }
2586 mut rhs := if node.op == .assign {
2587 t.transform_expr_for_type(rhs_id, field_type)
2588 } else {
2589 t.transform_expr(rhs_id)
2590 }
2591 mut rhs_type := t.node_type(rhs)
2592 if rhs_type.len == 0 {
2593 rhs_type = t.node_type(rhs_id)
2594 }
2595 if rhs_type.len == 0 {
2596 rhs_type = field_type
2597 }
2598 rhs = t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'sum_assign')
2599 resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_type))
2600 variants := t.sum_types[resolved_sum] or { return none }
2601 stmt := t.build_sum_shared_field_assign_chain(base, sum_type, resolved_sum, variants,
2602 lhs.value, field_type, rhs, node.op, 0)
2603 return t.with_pending_before(stmt)
2604}
2605
2606// build_sum_shared_field_assign_chain supports build_sum_shared_field_assign_chain handling.
2607fn (mut t Transformer) build_sum_shared_field_assign_chain(base flat.NodeId, sum_type string, resolved_sum string, variants []string, field string, field_type string, rhs flat.NodeId, op flat.Op, idx int) flat.NodeId {
2608 if idx >= variants.len {
2609 return t.make_empty()
2610 }
2611 variant := variants[idx]
2612 tag := t.make_selector_op(base, 'typ', 'int', if sum_type.starts_with('&') {
2613 .arrow
2614 } else {
2615 .dot
2616 })
2617 cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(resolved_sum, variant)))
2618 qv := t.resolve_variant(resolved_sum, variant)
2619 sum_field := t.sum_field_name(qv)
2620 use_ptr := t.variant_references_sum(qv, resolved_sum)
2621 variant_base := t.make_selector_op(base, sum_field, if use_ptr { '&${qv}' } else { qv }, if sum_type.starts_with('&') {
2622 .arrow
2623 } else {
2624 .dot
2625 })
2626 mut then_stmt := t.make_empty()
2627 if nested_field_type := t.sum_shared_field_type_name(qv, field) {
2628 nested_sum := t.resolve_sum_name(qv)
2629 if nested_variants := t.sum_types[nested_sum] {
2630 then_stmt = t.build_sum_shared_field_assign_chain(variant_base, qv, nested_sum,
2631 nested_variants, field, nested_field_type, rhs, op, 0)
2632 }
2633 } else {
2634 field_lhs := t.make_selector_op(variant_base, field, field_type, if use_ptr {
2635 .arrow
2636 } else {
2637 .dot
2638 })
2639 then_stmt = t.make_assign_op(field_lhs, rhs, op)
2640 }
2641 then_block := t.make_block(arr1(then_stmt))
2642 else_stmt := t.build_sum_shared_field_assign_chain(base, sum_type, resolved_sum, variants,
2643 field, field_type, rhs, op, idx + 1)
2644 return t.make_if(cond, then_block, else_stmt)
2645}
2646
2647// assignment_sum_target supports assignment sum target handling for Transformer.
2648fn (t &Transformer) assignment_sum_target(lhs_id flat.NodeId, rhs_id flat.NodeId, lhs_type string) string {
2649 if lhs_type.starts_with('&') {
2650 return ''
2651 }
2652 if lhs_type.starts_with('[]') || t.is_fixed_array_type(lhs_type) {
2653 return ''
2654 }
2655 if t.is_sum_type_name(lhs_type) {
2656 return lhs_type
2657 }
2658 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2659 return ''
2660 }
2661 lhs := t.a.nodes[int(lhs_id)]
2662 if lhs.kind != .selector || lhs.value.len == 0 {
2663 return ''
2664 }
2665 if lhs.value == 'obj' {
2666 sum_name := t.resolve_sum_name('ScopeObject')
2667 if t.is_sum_type_name(sum_name) {
2668 return sum_name
2669 }
2670 }
2671 rhs := t.a.nodes[int(rhs_id)]
2672 if inferred_sum := t.sum_type_for_field_variant(lhs.value, rhs_id, rhs) {
2673 return inferred_sum
2674 }
2675 if lhs.value == 'info' {
2676 if type_info_sum := t.type_info_sum_name() {
2677 return type_info_sum
2678 }
2679 }
2680 return ''
2681}
2682
2683// type_info_sum_name returns type info sum name data for Transformer.
2684fn (t &Transformer) type_info_sum_name() ?string {
2685 for sum_name, _ in t.sum_types {
2686 if sum_name == 'TypeInfo' || sum_name.ends_with('.TypeInfo') {
2687 return sum_name
2688 }
2689 }
2690 return none
2691}
2692
2693// try_lower_pointer_value_assign supports try lower pointer value assign handling for Transformer.
2694fn (mut t Transformer) try_lower_pointer_value_assign(node flat.Node) ?[]flat.NodeId {
2695 if node.kind != .assign || node.children_count != 2 {
2696 return none
2697 }
2698 lhs_id := t.a.child(&node, 0)
2699 lhs := t.a.nodes[int(lhs_id)]
2700 if lhs.kind != .ident || lhs.value.len == 0 {
2701 return none
2702 }
2703 mut lhs_type := t.var_type(lhs.value)
2704 if lhs_type.len == 0 {
2705 lhs_type = t.node_type(lhs_id)
2706 }
2707 if !lhs_type.starts_with('&') {
2708 return none
2709 }
2710 rhs_id := t.a.child(&node, 1)
2711 rhs_type := t.node_type(rhs_id)
2712 lhs_value_type := t.normalize_type_alias(lhs_type[1..])
2713 if node.op != .assign {
2714 if !t.pointer_value_lvalues[lhs.value] {
2715 return none
2716 }
2717 new_lhs := t.make_prefix(.mul, t.make_ident(lhs.value))
2718 return arr1(t.make_assign_op(new_lhs, t.transform_expr(rhs_id), node.op))
2719 }
2720 if rhs_type.len == 0
2721 || (rhs_type != lhs_value_type && !t.type_alias_targets_type(lhs_type[1..], rhs_type)) {
2722 return none
2723 }
2724 new_lhs := t.make_prefix(.mul, t.make_ident(lhs.value))
2725 return arr1(t.make_assign(new_lhs, t.transform_expr(rhs_id)))
2726}
2727
2728// transform_expr_for_type transforms transform expr for type data for transform.
2729fn (mut t Transformer) transform_expr_for_type(id flat.NodeId, target_type string) flat.NodeId {
2730 if int(id) >= 0 && target_type.len > 0 {
2731 node := t.a.nodes[int(id)]
2732 if node.kind == .none_expr && t.is_ierror_type(target_type) {
2733 return t.make_struct_init('IError')
2734 }
2735 if target_type.starts_with('&') {
2736 if expr := t.transform_amp_struct_init_for_type(id, node, target_type) {
2737 return expr
2738 }
2739 }
2740 if expr := t.transform_interface_value_for_type(id, target_type) {
2741 return expr
2742 }
2743 if node.kind == .block {
2744 if lowered := t.transform_block_expr_for_type(id, node, target_type) {
2745 return lowered
2746 }
2747 }
2748 if node.kind == .if_expr {
2749 if lowered := t.try_expand_if_expr_value_for_type(id, node, target_type) {
2750 return lowered
2751 }
2752 }
2753 if node.kind == .match_stmt {
2754 if lowered := t.transform_match_expr_for_type(id, node, target_type) {
2755 return lowered
2756 }
2757 }
2758 if node.kind == .or_expr && !t.is_optional_type_name(target_type) {
2759 old_typ := node.typ
2760 t.a.nodes[int(id)].typ = target_type
2761 expr := t.transform_expr(id)
2762 t.a.nodes[int(id)].typ = old_typ
2763 return t.coerce_transformed_expr_to_type(expr, id, target_type)
2764 }
2765 if node.kind == .array_literal {
2766 if lowered := t.transform_fixed_array_literal_for_type(id, node, target_type) {
2767 return lowered
2768 }
2769 if lowered := t.transform_array_literal_for_type(id, node, target_type) {
2770 return lowered
2771 }
2772 }
2773 if node.kind == .postfix && node.op == .not && node.children_count == 1 {
2774 child_id := t.a.child(&node, 0)
2775 child := t.a.nodes[int(child_id)]
2776 if child.kind == .array_literal {
2777 if lowered := t.transform_fixed_array_literal_for_type(child_id, child, target_type) {
2778 return lowered
2779 }
2780 }
2781 }
2782 if node.kind == .array_init {
2783 if lowered := t.transform_empty_array_init_for_type(node, target_type) {
2784 return lowered
2785 }
2786 }
2787 if node.kind == .map_init {
2788 clean_target := t.clean_map_type(target_type)
2789 if clean_target.starts_with('map[') {
2790 mut map_node := node
2791 map_node.value = clean_target
2792 map_node.typ = clean_target
2793 return t.lower_map_init_to_runtime(id, map_node)
2794 }
2795 }
2796 if target_type in ['f32', 'f64'] && node.kind == .infix
2797 && node.op in [.plus, .minus, .mul, .div] && node.children_count >= 2 {
2798 lhs := t.transform_expr_for_type(t.a.child(&node, 0), target_type)
2799 rhs := t.transform_expr_for_type(t.a.child(&node, 1), target_type)
2800 start := t.a.children.len
2801 t.a.children << lhs
2802 t.a.children << rhs
2803 return t.a.add_node(flat.Node{
2804 kind: .infix
2805 op: node.op
2806 children_start: start
2807 children_count: 2
2808 pos: node.pos
2809 value: node.value
2810 typ: target_type
2811 })
2812 }
2813 }
2814 expr := t.transform_expr(id)
2815 return t.coerce_transformed_expr_to_type(expr, id, target_type)
2816}
2817
2818// transform_block_expr_for_type transforms transform block expr for type data for transform.
2819fn (mut t Transformer) transform_block_expr_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2820 if node.kind != .block || node.children_count == 0 || target_type.len == 0 {
2821 return none
2822 }
2823 last_id := t.a.child(&node, node.children_count - 1)
2824 last := t.a.nodes[int(last_id)]
2825 tail_expr_id := if last.kind == .expr_stmt && last.children_count > 0 {
2826 t.a.child(&last, 0)
2827 } else if last.kind == .block && t.stmt_value_type(last_id).len > 0 {
2828 last_id
2829 } else if !t.is_stmt_kind(last.kind) {
2830 last_id
2831 } else {
2832 return none
2833 }
2834 mut prefix := []flat.NodeId{cap: int(node.children_count - 1)}
2835 for i in 0 .. node.children_count - 1 {
2836 prefix << t.a.child(&node, i)
2837 }
2838 mut new_children := t.transform_stmts(prefix)
2839 tail_expr := t.transform_expr_for_type(tail_expr_id, target_type)
2840 tail_stmt := t.make_expr_stmt(tail_expr)
2841 for stmt in t.with_pending_before(tail_stmt) {
2842 new_children << stmt
2843 }
2844 new_block := t.make_block(new_children)
2845 block_typ := t.stmt_value_type(new_block)
2846 t.a.nodes[int(new_block)].typ = if block_typ.len > 0 { block_typ } else { node.typ }
2847 return new_block
2848}
2849
2850// transform_match_expr_for_type transforms transform match expr for type data for transform.
2851fn (mut t Transformer) transform_match_expr_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2852 if target_type.len == 0 || node.kind != .match_stmt {
2853 return none
2854 }
2855 mut actual_result_type := t.match_expr_type(node)
2856 if actual_result_type.len == 0 || actual_result_type == 'void' {
2857 actual_result_type = target_type
2858 }
2859 tmp_name := t.new_temp('match_val')
2860 outer_pending := t.pending_stmts.clone()
2861 t.pending_stmts.clear()
2862
2863 mut prelude := []flat.NodeId{}
2864 prelude << t.make_decl_assign_typed(tmp_name, t.zero_value_for_type(actual_result_type),
2865 actual_result_type)
2866 for stmt in t.build_match_value_stmts(node, tmp_name, actual_result_type) {
2867 prelude << stmt
2868 }
2869
2870 t.pending_stmts = outer_pending
2871 for stmt in prelude {
2872 t.pending_stmts << stmt
2873 }
2874 tmp := t.make_ident(tmp_name)
2875 t.a.nodes[int(tmp)].typ = actual_result_type
2876 return tmp
2877}
2878
2879// transform_amp_struct_init_for_type supports transform_amp_struct_init_for_type handling.
2880fn (mut t Transformer) transform_amp_struct_init_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2881 if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2882 return none
2883 }
2884 child_id := t.a.child(&node, 0)
2885 child := t.a.nodes[int(child_id)]
2886 if child.kind != .struct_init {
2887 return none
2888 }
2889 new_child := t.transform_struct_init(child_id, child)
2890 start := t.a.children.len
2891 t.a.children << new_child
2892 return t.a.add_node(flat.Node{
2893 kind: .prefix
2894 op: node.op
2895 children_start: start
2896 children_count: 1
2897 pos: node.pos
2898 value: node.value
2899 typ: if target_type.len > 0 { target_type } else { node.typ }
2900 })
2901}
2902
2903// coerce_transformed_expr_to_type converts coerce transformed expr to type data for transform.
2904fn (mut t Transformer) coerce_transformed_expr_to_type(expr flat.NodeId, source_id flat.NodeId, target_type string) flat.NodeId {
2905 mut target := t.normalize_type_alias(target_type)
2906 if target.len == 0 || int(expr) < 0 {
2907 return expr
2908 }
2909 mut expr_type := t.node_type(expr)
2910 if expr_type.len == 0 {
2911 expr_type = t.node_type(source_id)
2912 }
2913 if expr_type.len == 0 {
2914 expr_type = t.resolve_expr_type(source_id)
2915 }
2916 expr_type = t.normalize_type_alias(expr_type)
2917 mut optional_target := if t.is_optional_type_name(target_type) {
2918 t.qualify_optional_type(target_type)
2919 } else {
2920 target
2921 }
2922 optional_target = t.infer_typed_optional_target(optional_target, expr_type)
2923 if t.is_optional_type_name(optional_target) && !t.is_optional_type_name(expr_type) {
2924 source := if int(source_id) >= 0 { t.a.nodes[int(source_id)] } else { flat.Node{} }
2925 if source.kind != .none_expr {
2926 return t.make_optional_some(expr, optional_target)
2927 }
2928 }
2929 if expr_type.len == 0 || expr_type == target {
2930 return expr
2931 }
2932 if target in ['f32', 'f64'] && t.is_integer_type_name(expr_type) {
2933 return t.make_cast(target, expr, target)
2934 }
2935 if target.starts_with('&') {
2936 if t.expr_is_nil_like(source_id) {
2937 t.a.nodes[int(expr)].typ = target
2938 return expr
2939 }
2940 target_value_type := t.normalize_type_alias(target[1..])
2941 expr_value_type := if expr_type.starts_with('&') {
2942 t.normalize_type_alias(expr_type[1..])
2943 } else {
2944 expr_type
2945 }
2946 if t.is_sum_type_name(target_value_type)
2947 && t.find_sum_type_for_variant(t.trim_pointer_type(expr_type)).len > 0 {
2948 if t.resolve_sum_name(t.trim_pointer_type(expr_type)) == t.resolve_sum_name(target_value_type) {
2949 if expr_type.starts_with('&') {
2950 return expr
2951 }
2952 if t.expr_can_take_address(expr) {
2953 addr := t.make_prefix(.amp, expr)
2954 t.a.nodes[int(addr)].typ = target
2955 return addr
2956 }
2957 tmp_name := t.new_temp('sum_ref')
2958 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, target_value_type)
2959 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2960 t.a.nodes[int(addr)].typ = target
2961 return addr
2962 }
2963 source := t.a.nodes[int(source_id)]
2964 wrap_source_id := if source.kind == .prefix && source.op == .amp
2965 && source.children_count > 0 {
2966 t.a.child(&source, 0)
2967 } else {
2968 source_id
2969 }
2970 wrapped := t.wrap_sum_value(wrap_source_id, target_value_type)
2971 tmp_name := t.new_temp('sum_ref')
2972 t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, target_value_type)
2973 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2974 t.a.nodes[int(addr)].typ = target
2975 return addr
2976 }
2977 if expr_value_type == target_value_type
2978 || t.type_alias_targets_type(target[1..], expr_value_type) {
2979 if expr_type.starts_with('&') {
2980 return expr
2981 }
2982 if t.expr_can_take_address(expr) {
2983 addr := t.make_prefix(.amp, expr)
2984 t.a.nodes[int(addr)].typ = target
2985 return addr
2986 }
2987 tmp_name := t.new_temp('addr')
2988 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, expr_value_type)
2989 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2990 t.a.nodes[int(addr)].typ = target
2991 return addr
2992 }
2993 return expr
2994 }
2995 if expr_type.starts_with('&') {
2996 expr_value_type := t.normalize_type_alias(expr_type[1..])
2997 if expr_value_type == target || t.type_alias_targets_type(expr_type[1..], target) {
2998 deref := t.make_prefix(.mul, expr)
2999 t.a.nodes[int(deref)].typ = target
3000 return deref
3001 }
3002 }
3003 return expr
3004}
3005
3006// is_ierror_type reports whether is ierror type applies in transform.
3007fn (t &Transformer) is_ierror_type(name string) bool {
3008 clean := t.trim_pointer_type(t.normalize_type_alias(name))
3009 return clean == 'IError' || clean.ends_with('.IError')
3010}
3011
3012// expr_is_nil_like supports expr is nil like handling for Transformer.
3013fn (t &Transformer) expr_is_nil_like(id flat.NodeId) bool {
3014 if int(id) < 0 {
3015 return false
3016 }
3017 node := t.a.nodes[int(id)]
3018 if node.kind == .nil_literal {
3019 return true
3020 }
3021 if node.kind != .block || node.children_count == 0 {
3022 return false
3023 }
3024 last_id := t.a.child(&node, node.children_count - 1)
3025 last := t.a.nodes[int(last_id)]
3026 if last.kind == .expr_stmt && last.children_count > 0 {
3027 return t.expr_is_nil_like(t.a.child(&last, 0))
3028 }
3029 return t.expr_is_nil_like(last_id)
3030}
3031
3032// infer_typed_optional_target resolves infer typed optional target information for transform.
3033fn (t &Transformer) infer_typed_optional_target(optional_target string, expr_type string) string {
3034 if expr_type.len == 0 {
3035 return optional_target
3036 }
3037 mut value_type := expr_type
3038 if !value_type.contains('.') {
3039 qualified := t.qualify_type(value_type)
3040 if qualified != value_type {
3041 value_type = qualified
3042 }
3043 }
3044 if !isnil(t.tc) {
3045 parsed := t.tc.parse_type(value_type)
3046 parsed_name := parsed.name()
3047 if parsed_name.len > 0 && parsed_name != 'unknown' {
3048 value_type = parsed_name
3049 }
3050 }
3051 if t.is_optional_type_name(optional_target) {
3052 base := t.optional_base_type(optional_target)
3053 if value_type.contains('.') && base == value_type.all_after_last('.') {
3054 return '?${value_type}'
3055 }
3056 return optional_target
3057 }
3058 if optional_target != 'Optional' || isnil(t.tc) {
3059 return optional_target
3060 }
3061 typ := t.tc.parse_type(value_type)
3062 if typ is types.Primitive || typ is types.Enum || typ is types.Void {
3063 return optional_target
3064 }
3065 return '?${value_type}'
3066}
3067
3068// make_optional_some builds make optional some data for transform.
3069fn (mut t Transformer) make_optional_some(value flat.NodeId, optional_type string) flat.NodeId {
3070 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(true), 'bool')
3071 base_type := t.optional_base_type(optional_type)
3072 mut fields := []flat.NodeId{cap: 2}
3073 fields << ok_field
3074 if base_type.len > 0 && base_type != 'void' {
3075 fields << t.make_sum_literal_field('value', value, base_type)
3076 }
3077 start := t.a.children.len
3078 for field in fields {
3079 t.a.children << field
3080 }
3081 return t.a.add_node(flat.Node{
3082 kind: .struct_init
3083 children_start: start
3084 children_count: flat.child_count(fields.len)
3085 value: optional_type
3086 typ: optional_type
3087 })
3088}
3089
3090// make_optional_none builds make optional none data for transform.
3091fn (mut t Transformer) make_optional_none(optional_type string) flat.NodeId {
3092 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(false), 'bool')
3093 start := t.a.children.len
3094 t.a.children << ok_field
3095 return t.a.add_node(flat.Node{
3096 kind: .struct_init
3097 children_start: start
3098 children_count: 1
3099 value: optional_type
3100 typ: optional_type
3101 })
3102}
3103
3104// make_optional_none_with_err builds make optional none with err data for transform.
3105fn (mut t Transformer) make_optional_none_with_err(optional_type string, err_expr flat.NodeId) flat.NodeId {
3106 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(false), 'bool')
3107 err_field := t.make_sum_literal_field('err', err_expr, 'IError')
3108 start := t.a.children.len
3109 t.a.children << ok_field
3110 t.a.children << err_field
3111 return t.a.add_node(flat.Node{
3112 kind: .struct_init
3113 children_start: start
3114 children_count: 2
3115 value: optional_type
3116 typ: optional_type
3117 })
3118}
3119
3120// expr_can_take_address supports expr can take address handling for Transformer.
3121fn (t &Transformer) expr_can_take_address(id flat.NodeId) bool {
3122 if int(id) < 0 {
3123 return false
3124 }
3125 node := t.a.nodes[int(id)]
3126 match node.kind {
3127 .ident {
3128 return true
3129 }
3130 .index {
3131 // `a[lo..hi]` (an index node tagged `range`) yields a fresh array value, not an
3132 // addressable element, so its address can't be taken in place — runtime_addr
3133 // must materialize it to a temp first. Only plain element indexing `a[i]` is an
3134 // addressable lvalue.
3135 if node.value == 'range' {
3136 return false
3137 }
3138 return true
3139 }
3140 .selector {
3141 if node.children_count == 0 {
3142 return false
3143 }
3144 if t.selector_chain_has_sum_variant_field(id) {
3145 return false
3146 }
3147 return t.expr_can_take_address(t.a.child(&node, 0))
3148 }
3149 .prefix {
3150 return node.op == .mul
3151 }
3152 .paren {
3153 if node.children_count == 0 {
3154 return false
3155 }
3156 return t.expr_can_take_address(t.a.child(&node, 0))
3157 }
3158 else {
3159 return false
3160 }
3161 }
3162}
3163
3164// type_alias_targets_type returns type alias targets type data for Transformer.
3165fn (t &Transformer) type_alias_targets_type(alias_name string, target_type string) bool {
3166 if alias_name.len == 0 || target_type.len == 0 || isnil(t.tc) {
3167 return false
3168 }
3169 for name, target in t.tc.type_aliases {
3170 if name == alias_name || name.all_after_last('.') == alias_name {
3171 if t.normalize_type_alias(target) == target_type {
3172 return true
3173 }
3174 }
3175 }
3176 return false
3177}
3178
3179// try_lower_string_compound_assign
3180// supports helper handling in transform.
3181fn (mut t Transformer) try_lower_string_compound_assign(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
3182 if node.kind != .assign || node.op != .plus_assign || node.children_count != 2 {
3183 return none
3184 }
3185 lhs_id := t.a.child(&node, 0)
3186 lhs := t.a.nodes[int(lhs_id)]
3187 if lhs.kind != .ident {
3188 return none
3189 }
3190 rhs_id := t.a.child(&node, 1)
3191 rhs := t.a.nodes[int(rhs_id)]
3192 is_string := t.resolve_expr_type(lhs_id) == 'string' || rhs.kind == .string_literal
3193 || rhs.kind == .string_interp || t.resolve_expr_type(rhs_id) == 'string'
3194 if !is_string {
3195 return none
3196 }
3197 new_rhs := t.transform_expr(rhs_id)
3198 lhs_copy := t.make_ident(lhs.value)
3199 concat := t.make_call('string__plus', arr2(lhs_copy, new_rhs))
3200 new_lhs := t.make_ident(lhs.value)
3201 return arr1(t.make_assign(new_lhs, concat))
3202}
3203
3204// transform_decl_assign_stmt transforms transform decl assign stmt data for transform.
3205fn (mut t Transformer) transform_decl_assign_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
3206 if node.children_count == 0 {
3207 return arr1(id)
3208 }
3209 mut has_empty_child := false
3210 for i in 0 .. node.children_count {
3211 if int(t.a.child(&node, i)) < 0 {
3212 has_empty_child = true
3213 }
3214 }
3215 if has_empty_child {
3216 mut parts := []string{}
3217 for i in 0 .. node.children_count {
3218 child_id := t.a.child(&node, i)
3219 if int(child_id) < 0 {
3220 parts << '${i}:empty'
3221 } else {
3222 child := t.a.nodes[int(child_id)]
3223 parts << '${i}:${child.kind}:${child.value}:${child.typ}'
3224 }
3225 }
3226 panic('internal error: empty decl_assign child in ${t.cur_fn_name}: count=${node.children_count} typ=${node.typ} value=${node.value} children=${parts.join('|')}')
3227 }
3228 mut inferred_typ := ''
3229 if node.children_count > 2 && !isnil(t.tc) {
3230 rhs_id := t.a.child(&node, 1)
3231 if rhs_types := t.multi_return_types_for_expr(rhs_id, node.children_count - 1) {
3232 for j, field_type in rhs_types {
3233 lhs_idx := if j == 0 { 0 } else { j + 1 }
3234 if lhs_idx >= node.children_count {
3235 continue
3236 }
3237 lhs := t.a.child_node(&node, lhs_idx)
3238 if lhs.kind == .ident && lhs.value.len > 0 && lhs.value != '_' {
3239 t.set_var_type(lhs.value, t.normalize_type_alias(field_type.name()))
3240 }
3241 }
3242 }
3243 }
3244 if expanded := t.try_expand_multi_return_decl(node) {
3245 return expanded
3246 }
3247 if expanded := t.try_expand_plain_multi_decl(node) {
3248 return expanded
3249 }
3250 // Track the variable type for the common 2-child case.
3251 if node.children_count == 2 {
3252 lhs := t.a.child_node(&node, 0)
3253 if lhs.kind == .ident && lhs.value.len > 0 {
3254 mut typ := t.infer_decl_type(node)
3255 rhs_id := t.a.child(&node, 1)
3256 rhs := t.a.nodes[int(rhs_id)]
3257 if rhs.kind == .call {
3258 call_typ := t.node_type(rhs_id)
3259 if decl_type_is_usable(call_typ) {
3260 typ = call_typ
3261 } else if !decl_type_is_usable(typ) {
3262 raw_call_typ := t.get_call_return_type(rhs_id, rhs)
3263 if raw_call_typ.len > 0 {
3264 typ = raw_call_typ
3265 }
3266 }
3267 generic_typ := t.concrete_generic_call_return_type(rhs_id, rhs)
3268 if generic_typ.len > 0 {
3269 typ = generic_typ
3270 }
3271 }
3272 if rhs.kind == .call && t.is_strings_builder_new_call(rhs_id, rhs) {
3273 typ = 'strings.Builder'
3274 } else if rhs.kind == .if_expr {
3275 if_typ := t.if_expr_result_type(rhs_id, rhs)
3276 if if_typ.len > 0 {
3277 typ = if_typ
3278 }
3279 } else if rhs.kind == .match_stmt {
3280 match_typ := t.match_expr_type(rhs)
3281 if match_typ.len > 0 {
3282 typ = match_typ
3283 }
3284 } else if rhs.kind == .block {
3285 block_typ := t.stmt_value_type(rhs_id)
3286 if block_typ.len > 0 {
3287 typ = block_typ
3288 }
3289 } else if rhs.kind == .or_expr && rhs.children_count > 0 {
3290 or_source_id := t.a.child(&rhs, 0)
3291 if info := t.map_index_info(or_source_id) {
3292 typ = info.value_type
3293 } else if info := t.array_index_info(or_source_id) {
3294 typ = info.value_type
3295 } else {
3296 or_body_id := if rhs.children_count > 1 {
3297 t.a.child(&rhs, 1)
3298 } else {
3299 flat.empty_node
3300 }
3301 fallback_type := if typ.len > 0 { typ } else { t.stmt_value_type(or_body_id) }
3302 expr_type, value_type := t.or_expr_types(or_source_id, fallback_type)
3303 if t.is_optional_type_name(expr_type) && value_type.len > 0
3304 && value_type != 'void' {
3305 typ = value_type
3306 }
3307 }
3308 }
3309 if node.typ.len == 0 {
3310 if rhs.kind == .array_literal && t.is_fixed_array_type(typ) {
3311 typ = '[]${fixed_array_elem_type(typ)}'
3312 t.a.nodes[int(rhs_id)].typ = typ
3313 }
3314 }
3315 if typ.len > 0 {
3316 t.set_var_type(lhs.value, typ)
3317 inferred_typ = typ
3318 }
3319 }
3320 }
3321 // A value local whose address escapes (`p := &v` with `p` returned) is moved to the heap
3322 // at its own declaration so writes after the alias are visible to the caller. Must run
3323 // before the `p := &v` alias is transformed (the source is declared first).
3324 if node.children_count == 2 {
3325 src := t.a.child_node(&node, 0)
3326 if src.kind == .ident && src.value in t.escaping_amp_sources
3327 && src.value !in t.heaped_amp_locals && t.heapable_value_type(inferred_typ) {
3328 return t.heap_escaping_source_decl(node, src.value, inferred_typ)
3329 }
3330 }
3331 mut new_children := []flat.NodeId{cap: int(node.children_count)}
3332 for i in 0 .. node.children_count {
3333 child_id := t.a.child(&node, i)
3334 if i == 0 || (node.children_count > 2 && i > 1) {
3335 new_children << t.transform_lvalue(child_id)
3336 } else if node.children_count == 2 && t.try_heap_escaping_amp(node, child_id) {
3337 new_children << t.heap_escaping_amp_rhs(child_id)
3338 // When `v` was heap-moved it is already a `&T`, so `p := &v` is really `p := v`
3339 // (a `&T`), not `&&T` as the literal `&v` would infer. Adopt the source's pointer
3340 // type for `p` so its declaration and later uses are consistent.
3341 amp := t.a.nodes[int(child_id)]
3342 if amp.children_count > 0 {
3343 amp_src := t.a.nodes[int(t.a.child(&, 0))]
3344 if amp_src.kind == .ident && amp_src.value in t.heaped_amp_locals {
3345 inferred_typ = t.var_type(amp_src.value)
3346 t.set_var_type(t.a.nodes[int(t.a.child(&node, 0))].value, inferred_typ)
3347 }
3348 }
3349 } else {
3350 lhs_id := t.a.child(&node, 0)
3351 lhs_type := if inferred_typ.len > 0 {
3352 inferred_typ
3353 } else if node.typ.len > 0 {
3354 node.typ
3355 } else {
3356 t.lvalue_type(lhs_id)
3357 }
3358 sum_target := t.assignment_sum_target(lhs_id, child_id, lhs_type)
3359 if sum_target.len > 0 && !t.expr_has_smartcast(child_id) {
3360 new_children << t.wrap_sum_value(child_id, sum_target)
3361 } else {
3362 new_children << t.transform_expr_for_type(child_id, lhs_type)
3363 }
3364 }
3365 }
3366 if node.children_count == 2 && !decl_type_is_usable(node.typ) {
3367 lhs := t.a.nodes[int(new_children[0])]
3368 if lhs.kind == .ident && lhs.value.len > 0 {
3369 rhs_typ := t.node_type(new_children[1])
3370 if decl_type_is_usable(rhs_typ)
3371 && (inferred_typ.len == 0 || inferred_typ in ['array', 'map', 'unknown']) {
3372 t.set_var_type(lhs.value, rhs_typ)
3373 inferred_typ = rhs_typ
3374 }
3375 }
3376 }
3377 start := t.a.children.len
3378 for nc in new_children {
3379 t.a.children << nc
3380 }
3381 new_id := t.a.add_node(flat.Node{
3382 kind: .decl_assign
3383 op: node.op
3384 children_start: start
3385 children_count: node.children_count
3386 pos: node.pos
3387 value: node.value
3388 typ: if inferred_typ.len > 0 { inferred_typ } else { node.typ }
3389 })
3390 return t.with_pending_before(new_id)
3391}
3392
3393fn (mut t Transformer) try_expand_plain_multi_decl(node flat.Node) ?[]flat.NodeId {
3394 if node.kind != .decl_assign || node.children_count < 4 || node.children_count % 2 != 0 {
3395 return none
3396 }
3397 mut result := []flat.NodeId{}
3398 for i := 0; i < node.children_count; i += 2 {
3399 lhs_id := t.a.child(&node, i)
3400 rhs_id := t.a.child(&node, i + 1)
3401 lhs := t.a.nodes[int(lhs_id)]
3402 rhs_node := t.a.nodes[int(rhs_id)]
3403 generic_rhs_typ := if rhs_node.kind == .call {
3404 t.concrete_generic_call_return_type(rhs_id, rhs_node)
3405 } else {
3406 ''
3407 }
3408 rhs := t.transform_expr(rhs_id)
3409 t.drain_pending(mut result)
3410 if lhs.kind != .ident || lhs.value == '_' {
3411 continue
3412 }
3413 rhs_authority := t.decl_rhs_type(rhs_id)
3414 mut typ := if t.is_fn_pointer_type_name(rhs_authority) { rhs_authority } else { '' }
3415 if typ.len == 0 {
3416 typ = generic_rhs_typ
3417 }
3418 if typ.len == 0 {
3419 typ = t.node_type(rhs)
3420 }
3421 if typ.len == 0 {
3422 typ = t.node_type(rhs_id)
3423 }
3424 if typ.len == 0 {
3425 typ = rhs_authority
3426 }
3427 if typ.len == 0 && lhs.typ.len > 0 {
3428 typ = lhs.typ
3429 }
3430 if typ.len > 0 {
3431 typ = t.normalize_type_alias(typ)
3432 t.set_var_type(lhs.value, typ)
3433 result << t.make_decl_assign_typed(lhs.value, rhs, typ)
3434 } else {
3435 result << t.make_decl_assign(lhs.value, rhs)
3436 }
3437 }
3438 return result
3439}
3440
3441// expr_has_smartcast converts expr has smartcast data for transform.
3442fn (t &Transformer) expr_has_smartcast(id flat.NodeId) bool {
3443 key := t.expr_key(id)
3444 return t.has_smartcast(key)
3445}
3446
3447// try_expand_multi_return_decl supports try expand multi return decl handling for Transformer.
3448fn (mut t Transformer) try_expand_multi_return_decl(node flat.Node) ?[]flat.NodeId {
3449 if node.kind != .decl_assign || node.children_count < 3 || isnil(t.tc) {
3450 return none
3451 }
3452 rhs_id := t.a.child(&node, 1)
3453 rhs := t.a.nodes[int(rhs_id)]
3454 lhs_ids := t.multi_assign_lhs_ids(node)
3455 if rhs.kind == .if_expr {
3456 return t.expand_multi_return_if_decl(rhs_id, rhs, lhs_ids)
3457 }
3458 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
3459 tmp_name := t.new_temp('multi_ret')
3460 mut result := []flat.NodeId{}
3461 new_rhs := t.transform_expr(rhs_id)
3462 t.drain_pending(mut result)
3463 result << t.make_decl_assign_typed(tmp_name, new_rhs, t.multi_return_type_name(rhs_types))
3464 for j, field_type in rhs_types {
3465 if j >= lhs_ids.len {
3466 continue
3467 }
3468 lhs_id := lhs_ids[j]
3469 lhs := t.a.nodes[int(lhs_id)]
3470 if lhs.kind != .ident || lhs.value == '_' {
3471 continue
3472 }
3473 field_name := 'arg${j}'
3474 field_type_name := field_type.name()
3475 field := t.make_selector(t.make_ident(tmp_name), field_name, field_type_name)
3476 t.set_var_type(lhs.value, t.normalize_type_alias(field_type_name))
3477 result << t.make_decl_assign_typed(lhs.value, field, field_type_name)
3478 }
3479 return result
3480 }
3481 return none
3482}
3483
3484// try_expand_multi_return_assign supports try expand multi return assign handling for Transformer.
3485fn (mut t Transformer) try_expand_multi_return_assign(node flat.Node) ?[]flat.NodeId {
3486 if node.kind != .assign || node.children_count < 3 || isnil(t.tc) {
3487 return none
3488 }
3489 rhs_id := t.a.child(&node, 1)
3490 rhs := t.a.nodes[int(rhs_id)]
3491 lhs_ids := t.multi_assign_lhs_ids(node)
3492 if rhs.kind == .if_expr {
3493 return t.expand_multi_return_if_assign(rhs_id, rhs, lhs_ids)
3494 }
3495 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
3496 tmp_name := t.new_temp('multi_ret')
3497 mut result := []flat.NodeId{}
3498 new_rhs := t.transform_expr(rhs_id)
3499 t.drain_pending(mut result)
3500 result << t.make_decl_assign_typed(tmp_name, new_rhs, t.multi_return_type_name(rhs_types))
3501 for j, field_type in rhs_types {
3502 if j >= lhs_ids.len {
3503 continue
3504 }
3505 lhs_id := lhs_ids[j]
3506 lhs := t.a.nodes[int(lhs_id)]
3507 if lhs.kind == .ident && lhs.value == '_' {
3508 continue
3509 }
3510 field_name := 'arg${j}'
3511 field_type_name := field_type.name()
3512 field := t.make_selector(t.make_ident(tmp_name), field_name, field_type_name)
3513 result << t.make_assign(t.transform_lvalue(lhs_id), field)
3514 }
3515 return result
3516 }
3517 return none
3518}
3519
3520// try_expand_plain_multi_assign supports try expand plain multi assign handling for Transformer.
3521fn (mut t Transformer) try_expand_plain_multi_assign(node flat.Node) ?[]flat.NodeId {
3522 if node.kind != .assign || node.op != .assign || node.children_count < 4
3523 || node.children_count % 2 != 0 {
3524 return none
3525 }
3526 mut result := []flat.NodeId{}
3527 mut lhs_ids := []flat.NodeId{}
3528 mut tmp_names := []string{}
3529 for i := 0; i < node.children_count; i += 2 {
3530 lhs_id := t.a.child(&node, i)
3531 rhs_id := t.a.child(&node, i + 1)
3532 lhs_ids << lhs_id
3533 lhs_type := t.lvalue_type(lhs_id)
3534 rhs := if lhs_type.len > 0 {
3535 t.transform_expr_for_type(rhs_id, lhs_type)
3536 } else {
3537 t.transform_expr(rhs_id)
3538 }
3539 t.drain_pending(mut result)
3540 tmp_name := t.new_temp('assign')
3541 tmp_type := if lhs_type.len > 0 { lhs_type } else { t.node_type(rhs_id) }
3542 result << t.make_decl_assign_typed(tmp_name, rhs, tmp_type)
3543 tmp_names << tmp_name
3544 }
3545 for i, lhs_id in lhs_ids {
3546 lhs := t.a.nodes[int(lhs_id)]
3547 if lhs.kind == .ident && lhs.value == '_' {
3548 continue
3549 }
3550 result << t.make_assign(t.transform_lvalue(lhs_id), t.make_ident(tmp_names[i]))
3551 }
3552 return result
3553}
3554
3555// multi_assign_lhs_ids supports multi assign lhs ids handling for Transformer.
3556fn (t &Transformer) multi_assign_lhs_ids(node flat.Node) []flat.NodeId {
3557 mut lhs_ids := []flat.NodeId{}
3558 if node.children_count > 0 {
3559 lhs_ids << t.a.child(&node, 0)
3560 }
3561 for i in 2 .. node.children_count {
3562 lhs_ids << t.a.child(&node, i)
3563 }
3564 return lhs_ids
3565}
3566
3567// multi_return_types_for_expr supports multi return types for expr handling for Transformer.
3568fn (t &Transformer) multi_return_types_for_expr(id flat.NodeId, expected_count int) ?[]types.Type {
3569 if int(id) < 0 || isnil(t.tc) {
3570 return none
3571 }
3572 if typ := t.tc.expr_type(id) {
3573 if items := multi_return_types_from_type(typ, expected_count) {
3574 return items
3575 }
3576 }
3577 node := t.a.nodes[int(id)]
3578 if node.kind == .or_expr && node.children_count > 0 {
3579 return t.multi_return_types_for_expr(t.a.child(&node, 0), expected_count)
3580 }
3581 if node.kind == .match_stmt {
3582 return t.match_multi_return_types(node, expected_count)
3583 }
3584 if node.kind == .expr_stmt {
3585 return t.expr_stmt_multi_return_types(node, expected_count)
3586 }
3587 if node.kind == .block {
3588 return t.block_multi_return_types(node, expected_count)
3589 }
3590 mut typ_name := node.typ
3591 if node.kind == .call {
3592 ret := t.get_call_return_type(id, node)
3593 if ret.len > 0 {
3594 typ_name = ret
3595 }
3596 } else if typ_name.len == 0 {
3597 typ_name = t.resolve_expr_type(id)
3598 }
3599 if typ_name.len == 0 {
3600 if node.kind == .call {
3601 return t.find_multi_return_call_types(node, expected_count)
3602 }
3603 return none
3604 }
3605 typ := t.tc.parse_type(typ_name)
3606 if items := multi_return_types_from_type(typ, expected_count) {
3607 return items
3608 }
3609 if node.kind == .call {
3610 return t.find_multi_return_call_types(node, expected_count)
3611 }
3612 return none
3613}
3614
3615fn (t &Transformer) match_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3616 if node.children_count < 2 {
3617 return none
3618 }
3619 for i in 1 .. node.children_count {
3620 branch := t.a.child_node(&node, i)
3621 if branch.kind != .match_branch {
3622 continue
3623 }
3624 body_start := if branch.value == 'else' { 0 } else { t.count_conds(*branch) }
3625 if branch.children_count <= body_start {
3626 continue
3627 }
3628 tail_id := t.a.child(branch, branch.children_count - 1)
3629 if items := t.multi_return_types_for_expr(tail_id, expected_count) {
3630 return items
3631 }
3632 }
3633 return none
3634}
3635
3636fn (t &Transformer) expr_stmt_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3637 if expected_count <= 0 || node.children_count != expected_count {
3638 return none
3639 }
3640 mut result := []types.Type{cap: expected_count}
3641 for i in 0 .. node.children_count {
3642 child_id := t.a.child(&node, i)
3643 mut typ_name := t.node_type(child_id)
3644 if typ_name.len == 0 {
3645 typ_name = t.resolve_expr_type(child_id)
3646 }
3647 if typ_name.len == 0 {
3648 return none
3649 }
3650 result << t.tc.parse_type(typ_name)
3651 }
3652 return result
3653}
3654
3655fn (t &Transformer) block_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3656 if expected_count <= 0 || node.children_count != expected_count {
3657 return none
3658 }
3659 mut result := []types.Type{cap: expected_count}
3660 for i in 0 .. node.children_count {
3661 stmt_id := t.a.child(&node, i)
3662 stmt := t.a.nodes[int(stmt_id)]
3663 if stmt.kind != .expr_stmt || stmt.children_count != 1 {
3664 return none
3665 }
3666 child_id := t.a.child(&stmt, 0)
3667 mut typ_name := t.node_type(child_id)
3668 if typ_name.len == 0 {
3669 typ_name = t.resolve_expr_type(child_id)
3670 }
3671 if typ_name.len == 0 {
3672 return none
3673 }
3674 result << t.tc.parse_type(typ_name)
3675 }
3676 return result
3677}
3678
3679// multi_return_types_from_type converts multi return types from type data for transform.
3680fn multi_return_types_from_type(typ types.Type, expected_count int) ?[]types.Type {
3681 if typ is types.MultiReturn {
3682 if expected_count <= 0 || typ.types.len == expected_count {
3683 items := typ.types.clone()
3684 return items
3685 }
3686 return none
3687 }
3688 if typ is types.OptionType {
3689 return multi_return_types_from_type(typ.base_type, expected_count)
3690 }
3691 if typ is types.ResultType {
3692 return multi_return_types_from_type(typ.base_type, expected_count)
3693 }
3694 return none
3695}
3696
3697// find_multi_return_call_types resolves find multi return call types information for transform.
3698fn (t &Transformer) find_multi_return_call_types(node flat.Node, expected_count int) ?[]types.Type {
3699 if node.kind != .call || node.children_count == 0 || isnil(t.tc) {
3700 return none
3701 }
3702 fn_node := t.a.child_node(&node, 0)
3703 mut candidates := []string{}
3704 if fn_node.kind == .ident {
3705 candidates << fn_node.value
3706 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
3707 candidates << '${t.cur_module}.${fn_node.value}'
3708 }
3709 } else if fn_node.kind == .selector {
3710 if fn_node.children_count > 0 {
3711 base_id := t.a.child(fn_node, 0)
3712 mut base_type := t.resolve_expr_type(base_id)
3713 if base_type.starts_with('&') {
3714 base_type = base_type[1..]
3715 }
3716 if base_type.len > 0 {
3717 candidates << '${base_type}.${fn_node.value}'
3718 if base_type.contains('.') {
3719 candidates << '${base_type.all_after_last('.')}.${fn_node.value}'
3720 }
3721 }
3722 }
3723 candidates << '.${fn_node.value}'
3724 }
3725 for candidate in candidates {
3726 if ret := t.tc.fn_ret_types[candidate] {
3727 if items := multi_return_types_from_type(ret, expected_count) {
3728 return items
3729 }
3730 }
3731 }
3732 for candidate in candidates {
3733 for key, ret in t.tc.fn_ret_types {
3734 matches := if candidate.starts_with('.') {
3735 key.ends_with(candidate)
3736 } else {
3737 key == candidate || key.ends_with('.${candidate}')
3738 }
3739 if matches {
3740 if items := multi_return_types_from_type(ret, expected_count) {
3741 return items
3742 }
3743 }
3744 }
3745 }
3746 return none
3747}
3748
3749// multi_return_type_name supports multi return type name handling for Transformer.
3750fn (t &Transformer) multi_return_type_name(items []types.Type) string {
3751 mut names := []string{cap: items.len}
3752 for item in items {
3753 names << item.name()
3754 }
3755 return '(${names.join(', ')})'
3756}
3757
3758// expand_multi_return_if_decl builds expand multi return if decl data for transform.
3759fn (mut t Transformer) expand_multi_return_if_decl(_rhs_id flat.NodeId, rhs flat.Node, lhs_ids []flat.NodeId) ?[]flat.NodeId {
3760 if lhs_ids.len == 0 {
3761 return none
3762 }
3763 value_types := t.infer_multi_if_value_types(rhs, lhs_ids.len)
3764 mut result := []flat.NodeId{}
3765 for i, lhs_id in lhs_ids {
3766 lhs := t.a.nodes[int(lhs_id)]
3767 if lhs.kind != .ident || lhs.value == '_' {
3768 continue
3769 }
3770 typ := if i < value_types.len { value_types[i] } else { 'int' }
3771 result << t.make_decl_assign_typed(lhs.value, t.zero_value_for_type(typ), typ)
3772 }
3773 if_stmts := t.expand_multi_return_if_assign(_rhs_id, rhs, lhs_ids) or { return none }
3774 for stmt in if_stmts {
3775 result << stmt
3776 }
3777 return result
3778}
3779
3780// expand_multi_return_if_assign builds expand multi return if assign data for transform.
3781fn (mut t Transformer) expand_multi_return_if_assign(_rhs_id flat.NodeId, rhs flat.Node, lhs_ids []flat.NodeId) ?[]flat.NodeId {
3782 if rhs.kind != .if_expr || lhs_ids.len == 0 {
3783 return none
3784 }
3785 return t.lower_multi_if_assign(rhs, lhs_ids)
3786}
3787
3788// lower_multi_if_assign builds lower multi if assign data for transform.
3789fn (mut t Transformer) lower_multi_if_assign(node flat.Node, lhs_ids []flat.NodeId) []flat.NodeId {
3790 if node.children_count < 2 {
3791 return []
3792 }
3793 cond_id := t.a.child(&node, 0)
3794 then_id := t.a.child(&node, 1)
3795 mut result := []flat.NodeId{}
3796 new_cond := t.transform_expr(cond_id)
3797 t.drain_pending(mut result)
3798 then_block := t.multi_if_assign_block(then_id, lhs_ids)
3799 mut else_block := t.make_empty()
3800 if node.children_count >= 3 {
3801 else_id := t.a.child(&node, 2)
3802 else_node := t.a.nodes[int(else_id)]
3803 if else_node.kind == .if_expr {
3804 else_stmts := t.lower_multi_if_assign(else_node, lhs_ids)
3805 else_block = t.make_block(else_stmts)
3806 } else {
3807 else_block = t.multi_if_assign_block(else_id, lhs_ids)
3808 }
3809 }
3810 result << t.make_if(new_cond, then_block, else_block)
3811 return result
3812}
3813
3814// multi_if_assign_block supports multi if assign block handling for Transformer.
3815fn (mut t Transformer) multi_if_assign_block(block_id flat.NodeId, lhs_ids []flat.NodeId) flat.NodeId {
3816 block := t.a.nodes[int(block_id)]
3817 parts := t.tuple_block_parts(block_id, lhs_ids.len) or { return t.transform_expr(block_id) }
3818 mut stmts := t.transform_stmts(parts.prefix)
3819 for i, value_id in parts.values {
3820 value := t.transform_expr(value_id)
3821 t.drain_pending(mut stmts)
3822 if i >= lhs_ids.len {
3823 stmts << t.make_expr_stmt(value)
3824 continue
3825 }
3826 lhs_id := lhs_ids[i]
3827 lhs := t.a.nodes[int(lhs_id)]
3828 if lhs.kind == .ident && lhs.value == '_' {
3829 stmts << t.make_expr_stmt(value)
3830 continue
3831 }
3832 stmts << t.make_assign(t.transform_lvalue(lhs_id), value)
3833 }
3834 if block.kind == .block {
3835 return t.make_block(stmts)
3836 }
3837 return t.make_block(stmts)
3838}
3839
3840// tuple_block_parts supports tuple block parts handling for Transformer.
3841fn (t &Transformer) tuple_block_parts(block_id flat.NodeId, count int) ?TupleBlockParts {
3842 if int(block_id) < 0 || count <= 0 {
3843 return none
3844 }
3845 block := t.a.nodes[int(block_id)]
3846 if block.kind != .block {
3847 return none
3848 }
3849 children := t.a.children_of(&block).clone()
3850 if children.len == 0 {
3851 return none
3852 }
3853 last_id := children[children.len - 1]
3854 last := t.a.nodes[int(last_id)]
3855 if last.kind == .block {
3856 if nested := t.tuple_block_parts(last_id, count) {
3857 if nested.prefix.len == 0 && nested.values.len == count {
3858 return TupleBlockParts{
3859 prefix: children[..children.len - 1].clone()
3860 values: nested.values.clone()
3861 }
3862 }
3863 }
3864 }
3865 mut values := []flat.NodeId{}
3866 mut prefix_end := children.len
3867 for i := children.len - 1; i >= 0; i-- {
3868 child_id := children[i]
3869 child := t.a.nodes[int(child_id)]
3870 if child.kind != .expr_stmt || child.children_count == 0 {
3871 break
3872 }
3873 values.prepend(t.a.child(&child, 0))
3874 prefix_end = i
3875 if values.len == count {
3876 return TupleBlockParts{
3877 prefix: children[..prefix_end].clone()
3878 values: values.clone()
3879 }
3880 }
3881 }
3882 return none
3883}
3884
3885// infer_multi_if_value_types resolves infer multi if value types information for transform.
3886fn (t &Transformer) infer_multi_if_value_types(node flat.Node, count int) []string {
3887 mut result := []string{cap: count}
3888 if node.kind != .if_expr || node.children_count < 2 {
3889 return result
3890 }
3891 then_id := t.a.child(&node, 1)
3892 if parts := t.tuple_block_parts(then_id, count) {
3893 for value_id in parts.values {
3894 mut typ := t.tuple_value_type(value_id)
3895 if typ.len == 0 {
3896 typ = 'int'
3897 }
3898 result << typ
3899 }
3900 }
3901 for result.len < count {
3902 result << 'int'
3903 }
3904 return result
3905}
3906
3907// tuple_value_type supports tuple value type handling for Transformer.
3908fn (t &Transformer) tuple_value_type(id flat.NodeId) string {
3909 if int(id) < 0 {
3910 return ''
3911 }
3912 node := t.a.nodes[int(id)]
3913 match node.kind {
3914 .cast_expr {
3915 return node.value
3916 }
3917 .prefix {
3918 if node.children_count > 0 {
3919 inner := t.tuple_value_type(t.a.child(&node, 0))
3920 if node.op == .amp && inner.len > 0 {
3921 return '&${inner}'
3922 }
3923 if node.op == .mul && inner.starts_with('&') {
3924 return inner[1..]
3925 }
3926 }
3927 return ''
3928 }
3929 .paren {
3930 if node.children_count > 0 {
3931 return t.tuple_value_type(t.a.child(&node, 0))
3932 }
3933 return ''
3934 }
3935 else {
3936 mut typ := t.resolve_expr_type(id)
3937 if typ.len == 0 {
3938 typ = t.node_type(id)
3939 }
3940 return typ
3941 }
3942 }
3943}
3944
3945// transform_expr_stmt transforms transform expr stmt data for transform.
3946fn (mut t Transformer) transform_expr_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
3947 if node.children_count == 0 {
3948 return arr1(id)
3949 }
3950 child_id := t.a.children[node.children_start]
3951 child := t.a.nodes[int(child_id)]
3952 if child.kind == .call && t.is_disabled_fn_call(child_id, child) {
3953 return []flat.NodeId{}
3954 }
3955 if child.kind == .or_expr && !t.is_map_index_or_expr(child) {
3956 _ = t.lower_or_expr_to_temp(child_id, child)
3957 mut result := []flat.NodeId{}
3958 t.drain_pending(mut result)
3959 return result
3960 }
3961 if child.kind == .lock_expr {
3962 return t.transform_lock_stmt(child_id, child)
3963 }
3964 if lowered := t.try_lower_map_index_append_stmt(child_id) {
3965 return lowered
3966 }
3967 if lowered := t.try_lower_map_index_postfix_stmt(child_id) {
3968 return lowered
3969 }
3970 if lowered := t.try_lower_array_append_stmt(child_id) {
3971 return lowered
3972 }
3973 if lowered := t.try_lower_flag_enum_stmt(child_id) {
3974 return arr1(lowered)
3975 }
3976 new_child := t.transform_expr(child_id)
3977 start := t.a.children.len
3978 t.a.children << new_child
3979 new_id := t.a.add_node(flat.Node{
3980 kind: .expr_stmt
3981 op: node.op
3982 children_start: start
3983 children_count: 1
3984 pos: node.pos
3985 value: node.value
3986 typ: node.typ
3987 })
3988 return t.with_pending_before(new_id)
3989}
3990
3991// transform_lock_stmt transforms transform lock stmt data for transform.
3992fn (mut t Transformer) transform_lock_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
3993 if node.children_count == 0 {
3994 return arr1(id)
3995 }
3996 body_id := t.a.child(&node, node.children_count - 1)
3997 if int(body_id) < 0 {
3998 return arr1(id)
3999 }
4000 body := t.a.nodes[int(body_id)]
4001 if body.kind == .block {
4002 return t.transform_stmts(t.a.children_of(&body))
4003 }
4004 if t.is_stmt_kind_id(node_kind_id(body)) {
4005 return t.transform_stmt(body_id)
4006 }
4007 new_child := t.transform_expr(body_id)
4008 return t.with_pending_before(t.make_expr_stmt(new_child))
4009}
4010
4011// transform_for_stmt transforms transform for stmt data for transform.
4012fn (mut t Transformer) transform_for_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
4013 return t.transform_for_body(id, node)
4014}
4015
4016// transform_for_in_stmt transforms transform for in stmt data for transform.
4017fn (mut t Transformer) transform_for_in_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
4018 return t.transform_for_in_body(id, node)
4019}
4020
4021// transform_block_stmt transforms transform block stmt data for transform.
4022fn (mut t Transformer) transform_block_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {
4023 mut child_ids := []flat.NodeId{cap: int(node.children_count)}
4024 for i in 0 .. node.children_count {
4025 child_ids << t.a.children[node.children_start + i]
4026 }
4027 new_children := t.transform_stmts(child_ids)
4028 new_block := t.make_block(new_children)
4029 return arr1(new_block)
4030}
4031
4032fn (mut t Transformer) transform_comptime_if_stmt(_id flat.NodeId, node flat.Node) []flat.NodeId {
4033 take_then := t.comptime_type_condition_value(node.value) or { return arr1(_id) }
4034 branch_index := if take_then { 0 } else { 1 }
4035 if branch_index >= node.children_count {
4036 return []flat.NodeId{}
4037 }
4038 branch_id := t.a.child(&node, branch_index)
4039 branch := t.a.nodes[int(branch_id)]
4040 if branch.kind == .block {
4041 return t.transform_stmts(t.a.children_of(&branch))
4042 }
4043 return t.transform_stmt(branch_id)
4044}
4045
4046fn comptime_condition_matching_paren(s string, start int) int {
4047 mut paren_depth := 0
4048 mut bracket_depth := 0
4049 for i in start .. s.len {
4050 match s[i] {
4051 `(` {
4052 paren_depth++
4053 }
4054 `)` {
4055 paren_depth--
4056 if paren_depth == 0 && bracket_depth == 0 {
4057 return i
4058 }
4059 }
4060 `[` {
4061 bracket_depth++
4062 }
4063 `]` {
4064 bracket_depth--
4065 }
4066 else {}
4067 }
4068 }
4069 return s.len
4070}
4071
4072fn comptime_condition_strip_outer_parens(cond string) string {
4073 mut clean := cond.trim_space()
4074 for clean.len >= 2 && clean.starts_with('(') {
4075 end := comptime_condition_matching_paren(clean, 0)
4076 if end != clean.len - 1 {
4077 break
4078 }
4079 clean = clean[1..clean.len - 1].trim_space()
4080 }
4081 return clean
4082}
4083
4084fn comptime_condition_top_level_index(s string, needle string) int {
4085 if needle.len == 0 || s.len < needle.len {
4086 return -1
4087 }
4088 mut paren_depth := 0
4089 mut bracket_depth := 0
4090 for i := 0; i <= s.len - needle.len; i++ {
4091 match s[i] {
4092 `(` {
4093 paren_depth++
4094 }
4095 `)` {
4096 if paren_depth > 0 {
4097 paren_depth--
4098 }
4099 }
4100 `[` {
4101 bracket_depth++
4102 }
4103 `]` {
4104 if bracket_depth > 0 {
4105 bracket_depth--
4106 }
4107 }
4108 else {}
4109 }
4110
4111 if paren_depth == 0 && bracket_depth == 0 && s[i..i + needle.len] == needle {
4112 return i
4113 }
4114 }
4115 return -1
4116}
4117
4118fn (mut t Transformer) comptime_type_condition_value(cond string) ?bool {
4119 clean := comptime_condition_strip_outer_parens(cond)
4120 if clean == 'true' {
4121 return true
4122 }
4123 if clean == 'false' {
4124 return false
4125 }
4126 or_idx := comptime_condition_top_level_index(clean, '||')
4127 if or_idx >= 0 {
4128 left := t.comptime_type_condition_value(clean[..or_idx]) or { return none }
4129 if left {
4130 return true
4131 }
4132 return t.comptime_type_condition_value(clean[or_idx + 2..])
4133 }
4134 and_idx := comptime_condition_top_level_index(clean, '&&')
4135 if and_idx >= 0 {
4136 left := t.comptime_type_condition_value(clean[..and_idx]) or { return none }
4137 if !left {
4138 return false
4139 }
4140 return t.comptime_type_condition_value(clean[and_idx + 2..])
4141 }
4142 for op in [' !is ', ' is '] {
4143 op_idx := comptime_condition_top_level_index(clean, op)
4144 if op_idx >= 0 {
4145 left := clean[..op_idx].trim_space()
4146 right := clean[op_idx + op.len..].trim_space()
4147 matches := t.comptime_type_matches(left, right) or { return none }
4148 return if op == ' is ' { matches } else { !matches }
4149 }
4150 }
4151 if clean.starts_with('!') {
4152 value := t.comptime_type_condition_value(clean[1..]) or { return none }
4153 return !value
4154 }
4155 return none
4156}
4157
4158fn (mut t Transformer) comptime_type_matches(actual string, expected string) ?bool {
4159 clean_actual := actual.trim_space()
4160 clean_expected := expected.trim_space()
4161 if clean_actual.len == 0 || clean_expected.len == 0
4162 || is_generic_fn_placeholder_name(clean_actual) {
4163 return none
4164 }
4165 normalized := t.normalize_type_alias(clean_actual)
4166 match clean_expected {
4167 '$array' {
4168 return normalized.starts_with('[]') || transform_type_text_is_fixed_array(normalized)
4169 }
4170 '$map' {
4171 return normalized.starts_with('map[')
4172 }
4173 '$function' {
4174 return normalized.starts_with('fn(') || normalized.starts_with('fn (')
4175 }
4176 '$option' {
4177 return normalized.starts_with('?')
4178 }
4179 '$int' {
4180 if typ := types.builtin_type(normalized) {
4181 return typ.is_integer()
4182 }
4183 return false
4184 }
4185 '$float' {
4186 if typ := types.builtin_type(normalized) {
4187 return typ.is_float()
4188 }
4189 return false
4190 }
4191 '$struct' {
4192 return !isnil(t.tc) && normalized in t.tc.structs
4193 }
4194 '$enum' {
4195 return !isnil(t.tc) && normalized in t.tc.enum_names
4196 }
4197 '$alias' {
4198 if isnil(t.tc) {
4199 return false
4200 }
4201 if clean_actual in t.tc.type_aliases {
4202 return true
4203 }
4204 if !clean_actual.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
4205 && t.cur_module != 'builtin' {
4206 return '${t.cur_module}.${clean_actual}' in t.tc.type_aliases
4207 }
4208 return false
4209 }
4210 '$sumtype' {
4211 return !isnil(t.tc) && normalized in t.tc.sum_types
4212 }
4213 '$interface' {
4214 return !isnil(t.tc) && normalized in t.tc.interface_names
4215 }
4216 else {}
4217 }
4218
4219 return normalized == t.normalize_type_alias(clean_expected)
4220}
4221
4222fn transform_type_text_is_fixed_array(typ string) bool {
4223 if typ.starts_with('[]') || typ.starts_with('[?') {
4224 return false
4225 }
4226 if typ.starts_with('[') {
4227 end := typ.index_u8(`]`)
4228 return end > 1
4229 }
4230 return typ.contains('[') && typ.ends_with(']') && is_decimal_text(fixed_array_len_text(typ))
4231}
4232
4233// transform_block_expr transforms transform block expr data for transform.
4234fn (mut t Transformer) transform_block_expr(_id flat.NodeId, node flat.Node) flat.NodeId {
4235 mut child_ids := []flat.NodeId{cap: int(node.children_count)}
4236 for i in 0 .. node.children_count {
4237 child_ids << t.a.children[node.children_start + i]
4238 }
4239 new_children := t.transform_stmts(child_ids)
4240 new_block := t.make_block(new_children)
4241 block_typ := t.stmt_value_type(new_block)
4242 t.a.nodes[int(new_block)].typ = if block_typ.len > 0 { block_typ } else { node.typ }
4243 return new_block
4244}
4245
4246// transform_lock_expr transforms transform lock expr data for transform.
4247fn (mut t Transformer) transform_lock_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4248 if node.children_count == 0 {
4249 return id
4250 }
4251 body_id := t.a.child(&node, node.children_count - 1)
4252 if int(body_id) < 0 {
4253 return id
4254 }
4255 body := t.a.nodes[int(body_id)]
4256 if body.kind == .block {
4257 mut new_block := t.transform_block_expr(body_id, body)
4258 block_typ := t.stmt_value_type(new_block)
4259 if node.typ == 'void' || block_typ == 'void' {
4260 mut children := t.a.children_of(&t.a.nodes[int(new_block)]).clone()
4261 children << t.make_expr_stmt(t.make_int_literal(0))
4262 new_block = t.make_block(children)
4263 t.a.nodes[int(new_block)].typ = 'int'
4264 } else {
4265 t.a.nodes[int(new_block)].typ = node.typ
4266 }
4267 return new_block
4268 }
4269 return t.transform_expr(body_id)
4270}
4271
4272// transform_if_stmt transforms transform if stmt data for transform.
4273fn (mut t Transformer) transform_if_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
4274 if expanded := t.try_expand_if_guard(id, node) {
4275 return expanded
4276 }
4277 new_id := t.transform_if_branches_with_smartcast(id, node)
4278 return t.with_pending_before(new_id)
4279}
4280
4281// transform_defer_stmt transforms transform defer stmt data for transform.
4282fn (mut t Transformer) transform_defer_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
4283 if node.children_count == 0 {
4284 return arr1(id)
4285 }
4286 body_id := t.a.child(&node, 0)
4287 if int(body_id) < 0 {
4288 return arr1(id)
4289 }
4290 body := t.a.nodes[int(body_id)]
4291 new_body := if body.kind == .block {
4292 t.transform_block_expr(body_id, body)
4293 } else if t.is_stmt_kind_id(node_kind_id(body)) {
4294 t.make_block(t.transform_stmt(body_id))
4295 } else {
4296 t.make_block(arr1(t.transform_expr(body_id)))
4297 }
4298 start := t.a.children.len
4299 t.a.children << new_body
4300 new_id := t.a.add_node(flat.Node{
4301 kind: .defer_stmt
4302 children_start: start
4303 children_count: 1
4304 pos: node.pos
4305 value: node.value
4306 typ: node.typ
4307 })
4308 return arr1(new_id)
4309}
4310
4311// Generic handler: rebuild a node with all children recursively transformed.
4312fn (mut t Transformer) transform_children_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
4313 if node.children_count == 0 {
4314 return arr1(id)
4315 }
4316 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4317 for i in 0 .. node.children_count {
4318 child_id := t.a.child(&node, i)
4319 child := t.a.nodes[int(child_id)]
4320 if t.is_stmt_kind_id(node_kind_id(child)) {
4321 expanded := t.transform_stmt(child_id)
4322 for eid in expanded {
4323 new_children << eid
4324 }
4325 } else {
4326 new_children << t.transform_expr(child_id)
4327 }
4328 }
4329 start := t.a.children.len
4330 for nc in new_children {
4331 t.a.children << nc
4332 }
4333 count := new_children.len
4334 new_id := t.a.add_node(flat.Node{
4335 kind: node.kind
4336 op: node.op
4337 children_start: start
4338 children_count: flat.child_count(count)
4339 pos: node.pos
4340 value: node.value
4341 typ: node.typ
4342 })
4343 return arr1(new_id)
4344}
4345
4346// --- expr handlers (skeleton - identity transforms with child recursion) ---
4347
4348// transform_children_expr transforms transform children expr data for transform.
4349fn (mut t Transformer) transform_children_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4350 if node.children_count == 0 {
4351 return id
4352 }
4353 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4354 mut changed := false
4355 for i in 0 .. node.children_count {
4356 child_id := t.a.child(&node, i)
4357 if int(child_id) < 0 {
4358 new_children << child_id
4359 continue
4360 }
4361 child := t.a.nodes[int(child_id)]
4362 if t.is_stmt_kind_id(node_kind_id(child)) {
4363 expanded := t.transform_stmt(child_id)
4364 if expanded.len == 1 {
4365 new_children << expanded[0]
4366 if expanded[0] != child_id {
4367 changed = true
4368 }
4369 } else {
4370 new_children << t.make_block(expanded)
4371 changed = true
4372 }
4373 } else {
4374 nc := t.transform_expr(child_id)
4375 new_children << nc
4376 if nc != child_id {
4377 changed = true
4378 }
4379 }
4380 }
4381 if !changed {
4382 return id
4383 }
4384 start := t.a.children.len
4385 for nc in new_children {
4386 t.a.children << nc
4387 }
4388 return t.a.add_node(flat.Node{
4389 kind: node.kind
4390 op: node.op
4391 children_start: start
4392 children_count: flat.child_count(new_children.len)
4393 pos: node.pos
4394 value: node.value
4395 typ: node.typ
4396 })
4397}
4398
4399// transform_infix_expr transforms transform infix expr data for transform.
4400fn (mut t Transformer) transform_infix_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4401 if node.children_count < 2 {
4402 return id
4403 }
4404 if node.op == .logical_and {
4405 return t.transform_and_chain_smartcasts(id)
4406 }
4407 if node.op == .left_shift {
4408 lhs_id := t.a.children[node.children_start]
4409 rhs_id := t.a.children[node.children_start + 1]
4410 new_lhs := t.transform_expr(lhs_id)
4411 new_rhs := t.transform_expr(rhs_id)
4412 start := t.a.children.len
4413 t.a.children << new_lhs
4414 t.a.children << new_rhs
4415 new_id := t.a.add_node(flat.Node{
4416 kind: .infix
4417 op: node.op
4418 children_start: start
4419 children_count: 2
4420 pos: node.pos
4421 value: node.value
4422 typ: node.typ
4423 })
4424 t.annotate_left_shift(new_id)
4425 return new_id
4426 }
4427 if str_result := t.transform_infix_string_ops(id, node) {
4428 return str_result
4429 }
4430 if array_result := t.transform_infix_array_ops(id, node) {
4431 return array_result
4432 }
4433 if map_result := t.transform_infix_map_ops(id, node) {
4434 return map_result
4435 }
4436 if optional_result := t.transform_infix_optional_none_ops(id, node) {
4437 return optional_result
4438 }
4439 if sum_result := t.transform_infix_sum_ops(id, node) {
4440 return sum_result
4441 }
4442 if struct_result := t.transform_infix_struct_ops(id, node) {
4443 return struct_result
4444 }
4445 lhs_id := t.a.children[node.children_start]
4446 rhs_id := t.a.children[node.children_start + 1]
4447 new_lhs := t.transform_expr(lhs_id)
4448 new_rhs := t.transform_expr(rhs_id)
4449 if struct_result := t.transform_transformed_struct_eq(node, new_lhs, new_rhs) {
4450 return struct_result
4451 }
4452 if new_lhs == lhs_id && new_rhs == rhs_id {
4453 // Nothing was lowered (the common case for plain arithmetic): reuse the original
4454 // node instead of allocating an identical copy. Under -gc none these copies are
4455 // never freed, so avoiding them cuts both transform time and peak RAM.
4456 return id
4457 }
4458 start := t.a.children.len
4459 t.a.children << new_lhs
4460 t.a.children << new_rhs
4461 return t.a.add_node(flat.Node{
4462 kind: .infix
4463 op: node.op
4464 children_start: start
4465 children_count: 2
4466 pos: node.pos
4467 value: node.value
4468 typ: node.typ
4469 })
4470}
4471
4472// transform_call_expr transforms transform call expr data for transform.
4473fn (mut t Transformer) transform_call_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4474 call_id := t.normalize_generic_call_expr(id, node)
4475 mut call_node := t.a.nodes[int(call_id)]
4476 mut resolved_typ := t.concrete_generic_call_return_type(call_id, call_node)
4477 if resolved_typ.len == 0 && call_node.typ.len > 0 {
4478 call_typ := t.normalize_type_alias(call_node.typ)
4479 if call_typ !in ['array', 'map', 'unknown'] {
4480 resolved_typ = call_typ
4481 }
4482 }
4483 if resolved_typ.len == 0 {
4484 resolved_typ = t.new_map_call_type(call_node)
4485 }
4486 if resolved_typ.len == 0 {
4487 resolved_typ = t.get_call_return_type(call_id, call_node)
4488 }
4489 if resolved_typ.len == 0 {
4490 resolved_typ = t.current_call_return_type(call_node)
4491 }
4492 if resolved_typ.len > 0 {
4493 t.a.nodes[int(call_id)].typ = resolved_typ
4494 call_node.typ = resolved_typ
4495 }
4496 if t.is_disabled_fn_call(call_id, call_node) {
4497 if resolved_typ.len == 0 || resolved_typ == 'void' {
4498 return t.make_empty()
4499 }
4500 return t.zero_value_for_type(resolved_typ)
4501 }
4502 if lowered := t.try_lower_builtin_call(call_id, call_node) {
4503 return lowered
4504 }
4505 if lowered := t.try_lower_array_repeat_call(call_id, call_node) {
4506 return lowered
4507 }
4508 if lowered := t.try_lower_join_path_call(call_id, call_node) {
4509 return lowered
4510 }
4511 return t.transform_call_args(call_id, call_node)
4512}
4513
4514// is_disabled_fn_name reports whether is disabled fn name applies in transform.
4515fn (t &Transformer) is_disabled_fn_name(name string) bool {
4516 if name in t.a.disabled_fns {
4517 return true
4518 }
4519 if !name.contains('.') && t.cur_module.len > 0 && t.cur_module != 'main'
4520 && t.cur_module != 'builtin' {
4521 return '${t.cur_module}.${name}' in t.a.disabled_fns
4522 }
4523 return false
4524}
4525
4526// is_disabled_fn_call reports whether is disabled fn call applies in transform.
4527fn (t &Transformer) is_disabled_fn_call(id flat.NodeId, node flat.Node) bool {
4528 name := t.call_name_for_node(id, node)
4529 return t.is_disabled_fn_name(name)
4530}
4531
4532// is_strings_builder_new_call reports whether is strings builder new call applies in transform.
4533fn (t &Transformer) is_strings_builder_new_call(id flat.NodeId, node flat.Node) bool {
4534 // Only `strings.new_builder` returns a `strings.Builder` (an alias for `[]u8`).
4535 // A bare `new_builder` must NOT be assumed to be the strings one: other modules
4536 // (e.g. `builder.new_builder`) and user code define their own `new_builder` that
4537 // return unrelated struct types. Resolve the call to its qualified name and only
4538 // match when it is genuinely the strings module's function.
4539 call_name := t.call_name_for_node(id, node)
4540 if call_name == 'strings.new_builder' {
4541 return true
4542 }
4543 if node.children_count == 0 {
4544 return false
4545 }
4546 fn_id := t.a.child(&node, 0)
4547 if int(fn_id) < 0 {
4548 return false
4549 }
4550 fn_node := t.a.nodes[int(fn_id)]
4551 if fn_node.kind == .ident {
4552 return fn_node.value == 'strings.new_builder'
4553 }
4554 if fn_node.kind == .selector && fn_node.value == 'new_builder' && fn_node.children_count > 0 {
4555 base := t.a.child_node(&fn_node, 0)
4556 return base.kind == .ident && base.value == 'strings'
4557 }
4558 return false
4559}
4560
4561// transform_if_expr transforms transform if expr data for transform.
4562fn (mut t Transformer) transform_if_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4563 if lowered := t.try_expand_if_expr_value(id, node) {
4564 return lowered
4565 }
4566 return t.transform_if_branches_with_smartcast(id, node)
4567}
4568
4569// transform_struct_init transforms transform struct init data for transform.
4570fn (mut t Transformer) transform_struct_init(id flat.NodeId, node flat.Node) flat.NodeId {
4571 return t.transform_struct_fields(id, node)
4572}
4573
4574// transform_index_expr transforms transform index expr data for transform.
4575fn (mut t Transformer) transform_index_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4576 if node.children_count == 0 {
4577 return id
4578 }
4579 if lowered := t.try_lower_map_index_expr(id, node) {
4580 return lowered
4581 }
4582 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4583 mut changed := false
4584 for i in 0 .. node.children_count {
4585 child_id := t.a.child(&node, i)
4586 mut new_child := t.transform_expr(child_id)
4587 if i == 0 {
4588 base := t.a.nodes[int(new_child)]
4589 if base.kind == .cast_expr {
4590 base_type := t.node_type(new_child)
4591 new_child = t.make_paren(new_child)
4592 if base_type.len > 0 {
4593 t.a.nodes[int(new_child)].typ = base_type
4594 }
4595 }
4596 }
4597 if new_child != child_id {
4598 changed = true
4599 }
4600 new_children << new_child
4601 }
4602 // Children unchanged: update the type annotation in place (applying the same
4603 // `typ = node.value when empty` fixup the rebuild would) instead of copying the node.
4604 mut index_typ := node.typ
4605 elem_type := t.index_expr_type(id, node)
4606 if elem_type == 'u8' || (index_typ.len == 0 && elem_type.len > 0) {
4607 index_typ = elem_type
4608 } else if index_typ.len == 0 && node.value.len > 0 {
4609 index_typ = node.value
4610 }
4611 if !changed {
4612 if index_typ.len > 0 {
4613 t.a.nodes[int(id)].typ = index_typ
4614 }
4615 return id
4616 }
4617 start := t.a.children.len
4618 for nc in new_children {
4619 t.a.children << nc
4620 }
4621 new_id := t.a.add_node(flat.Node{
4622 kind: .index
4623 op: node.op
4624 children_start: start
4625 children_count: node.children_count
4626 pos: node.pos
4627 value: node.value
4628 typ: index_typ
4629 })
4630 return new_id
4631}
4632
4633// transform_string_interp transforms transform string interp data for transform.
4634fn (mut t Transformer) transform_string_interp(_id flat.NodeId, node flat.Node) flat.NodeId {
4635 if node.children_count == 0 {
4636 return t.make_string_literal('')
4637 }
4638 // Some parts (arrays, optionals) lower into a prelude pushed onto pending_stmts, which
4639 // runs before the containing statement. If such a part follows a part with side effects,
4640 // keeping the earlier part inline in the string__plus chain would evaluate it after the
4641 // hoisted prelude, reversing source order. So once any part hoists statements, bind every
4642 // part to a temp in source order; while nothing hoists, keep the cheap inline form.
4643 outer_pending := t.pending_stmts.clone()
4644 t.pending_stmts.clear()
4645 mut inline_parts := []flat.NodeId{cap: int(node.children_count)}
4646 mut temps := []flat.NodeId{cap: int(node.children_count)}
4647 mut hoisting := false
4648 for i in 0 .. node.children_count {
4649 child_id := t.a.child(&node, i)
4650 part := t.transform_string_interp_part(child_id)
4651 mut part_stmts := []flat.NodeId{}
4652 t.drain_pending(mut part_stmts)
4653 if !hoisting && part_stmts.len == 0 {
4654 inline_parts << part
4655 continue
4656 }
4657 if !hoisting {
4658 hoisting = true
4659 // Earlier parts had no prelude; bind them to temps first so their side effects
4660 // still happen before this part's hoisted statements.
4661 for earlier in inline_parts {
4662 earlier_name := t.new_temp('interp_part')
4663 t.pending_stmts << t.make_decl_assign_typed(earlier_name, earlier, 'string')
4664 temps << t.make_ident(earlier_name)
4665 }
4666 }
4667 for st in part_stmts {
4668 t.pending_stmts << st
4669 }
4670 name := t.new_temp('interp_part')
4671 t.pending_stmts << t.make_decl_assign_typed(name, part, 'string')
4672 temps << t.make_ident(name)
4673 }
4674 // The interp's own statements must run after any pending the surrounding context queued.
4675 mut interp_stmts := []flat.NodeId{}
4676 t.drain_pending(mut interp_stmts)
4677 for st in outer_pending {
4678 t.pending_stmts << st
4679 }
4680 for st in interp_stmts {
4681 t.pending_stmts << st
4682 }
4683 parts := if hoisting { temps } else { inline_parts }
4684 mut result := if parts.len == 0 { t.make_string_literal('') } else { parts[0] }
4685 for i in 1 .. parts.len {
4686 result = t.string_plus(result, parts[i])
4687 }
4688 t.a.nodes[int(result)].typ = 'string'
4689 return result
4690}
4691
4692// transform_selector_expr transforms transform selector expr data for transform.
4693fn (mut t Transformer) transform_selector_expr(id flat.NodeId, node flat.Node) flat.NodeId {
4694 if node.children_count == 0 {
4695 return id
4696 }
4697 if node.value in t.sum_variant_fields {
4698 return id
4699 }
4700 base_id0 := t.a.child(&node, 0)
4701 if variant_type := t.generated_variant_access_type(base_id0) {
4702 new_base := t.transform_expr(base_id0)
4703 clean_variant_type := t.trim_pointer_type(variant_type)
4704 sel_typ := if node.typ.len > 0 {
4705 node.typ
4706 } else if ftyp := t.lookup_struct_field_type(clean_variant_type, node.value) {
4707 ftyp
4708 } else {
4709 t.resolve_selector_type(node)
4710 }
4711 return t.make_selector_op(new_base, node.value, sel_typ, if variant_type.starts_with('&') {
4712 .arrow
4713 } else {
4714 node.op
4715 })
4716 }
4717 base_node0 := t.a.nodes[int(base_id0)]
4718 if base_node0.kind == .typeof_expr {
4719 if node.value == 'name' {
4720 return t.transform_typeof_expr(base_id0, base_node0)
4721 }
4722 if node.value == 'idx' {
4723 return t.transform_typeof_idx_expr(base_node0)
4724 }
4725 }
4726 if fixed_len := t.transform_fixed_array_len(id, node) {
4727 return fixed_len
4728 }
4729 full_key := t.expr_key(id)
4730 if full_key.len > 0 {
4731 contexts := t.smartcasts_for(full_key)
4732 if contexts.len > 0 {
4733 plain := t.make_plain_selector_expr(id, node)
4734 return t.apply_smartcast_contexts(plain, t.original_expr_type(id), contexts)
4735 }
4736 }
4737 base_id := base_id0
4738 sc_key := t.expr_key(base_id)
4739 if sc_key.len > 0 {
4740 contexts := t.smartcasts_for(sc_key)
4741 if contexts.len > 0 {
4742 plain_base := t.make_plain_expr_for_smartcast(base_id)
4743 variant_sel := t.apply_smartcast_contexts(plain_base, t.original_expr_type(base_id),
4744 contexts)
4745 variant_type := t.node_type(variant_sel)
4746 if shared_typ := t.sum_shared_field_type_name(variant_type, node.value) {
4747 return t.lower_sum_shared_field_selector(variant_sel, variant_type, node.value,
4748 shared_typ)
4749 }
4750 sel_start := t.a.children.len
4751 t.a.children << variant_sel
4752 sel_typ := if node.typ.len > 0 { node.typ } else { t.resolve_selector_type(node) }
4753 return t.a.add_node(flat.Node{
4754 kind: .selector
4755 op: if node.op == .arrow || variant_type.starts_with('&') {
4756 flat.Op.arrow
4757 } else {
4758 flat.Op.dot
4759 }
4760 children_start: sel_start
4761 children_count: 1
4762 pos: node.pos
4763 value: node.value
4764 typ: sel_typ
4765 })
4766 }
4767 }
4768 base_type0 := t.node_type(base_id)
4769 base_clean := if base_type0.starts_with('&') { base_type0[1..] } else { base_type0 }
4770 if info := t.lookup_struct_info(base_clean) {
4771 has_direct_field := (t.struct_field_type(info, node.value) or { '' }).len > 0
4772 if !has_direct_field {
4773 if embedded := t.embedded_field_for_promoted_field(info, node.value) {
4774 new_base := t.transform_expr(base_id)
4775 embedded_op := if base_type0.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
4776 embedded_sel := t.make_selector_op(new_base, embedded.name, embedded.typ,
4777 embedded_op)
4778 sel_typ := if node.typ.len > 0 { node.typ } else { t.resolve_selector_type(node) }
4779 final_op := if embedded.typ.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
4780 return t.make_selector_op(embedded_sel, node.value, sel_typ, final_op)
4781 }
4782 }
4783 }
4784 if shared_typ := t.sum_shared_field_type_name(base_type0, node.value) {
4785 transformed_base := t.transform_expr(base_id)
4786 transformed_base_type := t.node_type(transformed_base)
4787 clean_transformed_base_type := if transformed_base_type.starts_with('&') {
4788 transformed_base_type[1..]
4789 } else {
4790 transformed_base_type
4791 }
4792 if clean_transformed_base_type.len > 0
4793 && t.normalize_type_alias(clean_transformed_base_type) != t.normalize_type_alias(base_type0) {
4794 if ftyp := t.lookup_struct_field_type(clean_transformed_base_type, node.value) {
4795 new_base := t.selector_base_for_field(transformed_base, transformed_base_type)
4796 return t.make_selector_op(new_base, node.value, if node.typ.len > 0 {
4797 node.typ
4798 } else {
4799 ftyp
4800 }, if transformed_base_type.starts_with('&') {
4801 .arrow
4802 } else {
4803 .dot
4804 })
4805 }
4806 if new_shared_typ := t.sum_shared_field_type_name(transformed_base_type, node.value) {
4807 new_base := t.selector_base_for_field(transformed_base, transformed_base_type)
4808 return t.lower_sum_shared_field_selector(new_base, transformed_base_type,
4809 node.value, new_shared_typ)
4810 }
4811 }
4812 new_base := t.selector_base_for_field(transformed_base, base_type0)
4813 return t.lower_sum_shared_field_selector(new_base, base_type0, node.value, shared_typ)
4814 }
4815 new_base := t.transform_expr(base_id)
4816 mut changed := new_base != base_id
4817 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4818 new_children << new_base
4819 for i in 1 .. node.children_count {
4820 child_id := t.a.child(&node, i)
4821 nc := t.transform_expr(child_id)
4822 if nc != child_id {
4823 changed = true
4824 }
4825 new_children << nc
4826 }
4827 sel_typ := if node.typ.len > 0 { node.typ } else { t.resolve_selector_type(node) }
4828 base_type := t.node_type(base_id)
4829 sel_op := if node.op == .arrow || base_type.starts_with('&') { flat.Op.arrow } else { node.op }
4830 if !changed && sel_op == node.op {
4831 // Children and op unchanged; only the type annotation may differ. Update it in
4832 // place rather than allocating an identical copy (cuts -gc none peak RAM). (`op`
4833 // is an immutable Node field, so a differing op still needs a fresh node below.)
4834 t.a.nodes[int(id)].typ = sel_typ
4835 return id
4836 }
4837 start := t.a.children.len
4838 for nc in new_children {
4839 t.a.children << nc
4840 }
4841 return t.a.add_node(flat.Node{
4842 kind: .selector
4843 op: sel_op
4844 children_start: start
4845 children_count: node.children_count
4846 pos: node.pos
4847 value: node.value
4848 typ: sel_typ
4849 })
4850}
4851
4852// make_plain_selector_expr builds make plain selector expr data for transform.
4853fn (mut t Transformer) make_plain_selector_expr(_id flat.NodeId, node flat.Node) flat.NodeId {
4854 base_id := t.a.child(&node, 0)
4855 base_type := t.node_type(base_id)
4856 new_base := t.selector_base_for_field(t.transform_expr(base_id), base_type)
4857 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4858 new_children << new_base
4859 for i in 1 .. node.children_count {
4860 child_id := t.a.child(&node, i)
4861 new_children << t.transform_expr(child_id)
4862 }
4863 start := t.a.children.len
4864 for nc in new_children {
4865 t.a.children << nc
4866 }
4867 sel_typ := if node.typ.len > 0 { node.typ } else { t.resolve_selector_type(node) }
4868 sel_op := if node.op == .arrow || base_type.starts_with('&') { flat.Op.arrow } else { node.op }
4869 return t.a.add_node(flat.Node{
4870 kind: .selector
4871 op: sel_op
4872 children_start: start
4873 children_count: node.children_count
4874 pos: node.pos
4875 value: node.value
4876 typ: sel_typ
4877 })
4878}
4879
4880// make_plain_expr_for_smartcast builds make plain expr for smartcast data for transform.
4881fn (mut t Transformer) make_plain_expr_for_smartcast(id flat.NodeId) flat.NodeId {
4882 if int(id) < 0 {
4883 return id
4884 }
4885 node := t.a.nodes[int(id)]
4886 match node.kind {
4887 .ident {
4888 expr := t.make_ident(node.value)
4889 typ := t.original_expr_type(id)
4890 if typ.len > 0 {
4891 t.a.nodes[int(expr)].typ = typ
4892 }
4893 return expr
4894 }
4895 .selector {
4896 return t.make_plain_selector_expr(id, node)
4897 }
4898 .index {
4899 mut new_children := []flat.NodeId{cap: int(node.children_count)}
4900 for i in 0 .. node.children_count {
4901 new_children << t.transform_expr(t.a.child(&node, i))
4902 }
4903 start := t.a.children.len
4904 for nc in new_children {
4905 t.a.children << nc
4906 }
4907 return t.a.add_node(flat.Node{
4908 kind: .index
4909 op: node.op
4910 children_start: start
4911 children_count: node.children_count
4912 pos: node.pos
4913 value: node.value
4914 typ: if node.typ.len > 0 { node.typ } else { node.value }
4915 })
4916 }
4917 else {
4918 return t.transform_expr(id)
4919 }
4920 }
4921}
4922
4923// selector_base_for_field supports selector base for field handling for Transformer.
4924fn (mut t Transformer) selector_base_for_field(base flat.NodeId, typ string) flat.NodeId {
4925 if int(base) < 0 {
4926 return base
4927 }
4928 node := t.a.nodes[int(base)]
4929 if node.kind in [.if_expr, .block] {
4930 return t.stable_transformed_expr_for_reuse(base, typ, 'sel_base')
4931 }
4932 return base
4933}
4934
4935// sum_shared_field_type_name supports sum shared field type name handling for Transformer.
4936fn (t &Transformer) sum_shared_field_type_name(sum_type string, field string) ?string {
4937 clean_sum := if sum_type.starts_with('&') { sum_type[1..] } else { sum_type }
4938 resolved_sum := t.resolve_sum_name(clean_sum)
4939 variants := t.sum_types[resolved_sum] or { return none }
4940 mut common := ''
4941 for variant in variants {
4942 ftyp := t.sum_variant_field_type_name(variant, field) or { return none }
4943 if common.len == 0 {
4944 common = ftyp
4945 continue
4946 }
4947 if t.normalize_type_alias(common) != t.normalize_type_alias(ftyp) {
4948 return none
4949 }
4950 }
4951 if common.len == 0 {
4952 return none
4953 }
4954 return common
4955}
4956
4957// sum_variant_field_type_name supports sum variant field type name handling for Transformer.
4958fn (t &Transformer) sum_variant_field_type_name(variant string, field string) ?string {
4959 if ftyp := t.lookup_struct_field_type(variant, field) {
4960 return ftyp
4961 }
4962 if ftyp := t.sum_shared_field_type_name(variant, field) {
4963 return ftyp
4964 }
4965 return none
4966}
4967
4968// lower_sum_shared_field_selector builds lower sum shared field selector data for transform.
4969fn (mut t Transformer) lower_sum_shared_field_selector(base flat.NodeId, sum_type string, field string, field_type string) flat.NodeId {
4970 clean_sum := if sum_type.starts_with('&') { sum_type[1..] } else { sum_type }
4971 resolved_sum := t.resolve_sum_name(clean_sum)
4972 variants := t.sum_types[resolved_sum] or { return base }
4973 return t.build_sum_shared_field_chain(base, sum_type, resolved_sum, variants, field,
4974 field_type, 0)
4975}
4976
4977// build_sum_shared_field_chain builds sum shared field chain data for transform.
4978fn (mut t Transformer) build_sum_shared_field_chain(base flat.NodeId, sum_type string, resolved_sum string, variants []string, field string, field_type string, idx int) flat.NodeId {
4979 if idx >= variants.len {
4980 return t.zero_value_for_type(field_type)
4981 }
4982 variant := variants[idx]
4983 tag := t.make_selector_op(base, 'typ', 'int', if sum_type.starts_with('&') {
4984 .arrow
4985 } else {
4986 .dot
4987 })
4988 cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(resolved_sum, variant)))
4989 qv := t.resolve_variant(resolved_sum, variant)
4990 sum_field := t.sum_field_name(qv)
4991 use_ptr := t.variant_references_sum(qv, resolved_sum)
4992 variant_base := t.make_selector_op(base, sum_field, if use_ptr { '&${qv}' } else { qv }, if sum_type.starts_with('&') {
4993 .arrow
4994 } else {
4995 .dot
4996 })
4997 value := if _ := t.sum_shared_field_type_name(qv, field) {
4998 t.lower_sum_shared_field_selector(variant_base, qv, field, field_type)
4999 } else {
5000 t.make_selector_op(variant_base, field, field_type, if use_ptr { .arrow } else { .dot })
5001 }
5002 then_block := t.make_block(arr1(t.make_expr_stmt(value)))
5003 else_expr := t.build_sum_shared_field_chain(base, sum_type, resolved_sum, variants, field,
5004 field_type, idx + 1)
5005 else_block := t.make_block(arr1(t.make_expr_stmt(else_expr)))
5006 start := t.a.children.len
5007 t.a.children << cond
5008 t.a.children << then_block
5009 t.a.children << else_block
5010 return t.a.add_node(flat.Node{
5011 kind: .if_expr
5012 children_start: start
5013 children_count: 3
5014 typ: field_type
5015 })
5016}
5017
5018// transform_or_expr transforms transform or expr data for transform.
5019fn (mut t Transformer) transform_or_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5020 if node.children_count < 2 {
5021 return id
5022 }
5023 if t.is_map_index_or_expr(node) {
5024 return t.transform_map_index_or_expr(id, node)
5025 }
5026 if t.is_array_index_or_expr(node) {
5027 return t.transform_array_index_or_expr(id, node)
5028 }
5029 if t.is_string_slice_or_expr(node) {
5030 return t.transform_string_slice_or_expr(id, node)
5031 }
5032 if t.is_enum_from_string_or_expr(node) {
5033 return t.transform_enum_from_string_or_expr(id, node)
5034 }
5035 return t.lower_or_expr_to_temp(id, node)
5036}
5037
5038// transform_prefix_expr transforms transform prefix expr data for transform.
5039fn (mut t Transformer) transform_prefix_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5040 if node.children_count == 0 {
5041 return id
5042 }
5043 if node.op == .mul && node.children_count == 1 {
5044 child_id := t.a.child(&node, 0)
5045 child := t.a.nodes[int(child_id)]
5046 mut child_type := t.node_type(child_id)
5047 if child_type.len == 0 {
5048 child_type = t.original_expr_type(child_id)
5049 }
5050 if child.kind != .cast_expr && child_type.len > 0 && !child_type.starts_with('&') {
5051 return t.transform_expr(child_id)
5052 }
5053 }
5054 if node.op == .amp && node.children_count == 1 {
5055 child_id := t.a.child(&node, 0)
5056 child := t.a.nodes[int(child_id)]
5057 if child.kind == .struct_init {
5058 // `&T{...}` (address of a struct literal) is ALWAYS a heap allocation in V,
5059 // in any context — not just in a return. Keeping it as a `.prefix .amp`
5060 // struct_init routes it through cgen's gen_heap_struct_init; otherwise the
5061 // generic fall-through lowers it to `&<stack temp>`, which dangles once the
5062 // frame dies (e.g. `arr << &T{...}` storing a stack pointer in the array).
5063 if expr := t.transform_amp_struct_init_for_type(id, node, node.typ) {
5064 return expr
5065 }
5066 }
5067 if expr := t.transform_amp_assoc_expr_for_type(id, node, node.typ) {
5068 return expr
5069 }
5070 if child.kind == .cast_expr && child.children_count > 0 {
5071 cast_arg_id := t.a.child(&child, 0)
5072 target_sum := t.resolve_sum_name(t.normalize_type_alias(child.value))
5073 if target_sum.len > 0 && target_sum in t.sum_types {
5074 cast_arg := t.a.nodes[int(cast_arg_id)]
5075 if cast_arg.kind == .nil_literal {
5076 return t.make_cast('&${child.value}', t.transform_expr(cast_arg_id),
5077 '&${child.value}')
5078 }
5079 wrapped := t.wrap_sum_value(cast_arg_id, target_sum)
5080 addr := t.make_prefix(.amp, wrapped)
5081 t.a.nodes[int(addr)].typ = if node.typ.len > 0 { node.typ } else { '&${target_sum}' }
5082 return addr
5083 }
5084 // `&InterfaceType(x)` (e.g. `&PRNG(rng)`): box the concrete into a
5085 // heap-allocated interface so the resulting pointer stays valid, rather
5086 // than emitting a plain `(Interface*)x` reinterpret cast.
5087 iface := t.resolve_interface_type_name(child.value)
5088 if iface.len > 0 && iface.all_after_last('.') != 'IError' {
5089 if boxed := t.transform_interface_value_for_type(cast_arg_id, '&${child.value}') {
5090 return boxed
5091 }
5092 }
5093 cast_arg := t.a.nodes[int(cast_arg_id)]
5094 if cast_arg.kind == .nil_literal {
5095 return t.make_cast('&${child.value}', t.transform_expr(cast_arg_id),
5096 '&${child.value}')
5097 }
5098 return t.make_cast('&${child.value}', t.transform_expr(cast_arg_id), '&${child.value}')
5099 }
5100 if child.kind == .or_expr && child.children_count >= 2
5101 && t.or_body_is_nil(t.a.child(&child, 1)) {
5102 index_id := t.a.child(&child, 0)
5103 if info := t.map_index_info(index_id) {
5104 map_expr := t.stable_expr_for_reuse(info.base_id)
5105 key_name := t.new_temp('map_key')
5106 t.pending_stmts << t.make_decl_assign_typed(key_name,
5107 t.transform_expr(info.key_id), info.key_type)
5108 ptr := t.make_map_get_check_expr(map_expr, info.base_type, key_name)
5109 return t.make_cast('&${info.value_type}', ptr, '&${info.value_type}')
5110 }
5111 }
5112 if child.kind == .call && child.children_count == 2 {
5113 callee := t.a.child_node(&child, 0)
5114 arg_id := t.a.child(&child, 1)
5115 arg := t.a.nodes[int(arg_id)]
5116 if callee.kind == .selector && callee.children_count > 0
5117 && (arg.kind == .nil_literal || callee.value.len > 0) {
5118 base := t.a.child_node(callee, 0)
5119 if base.kind == .ident && callee.value.len > 0
5120 && (base.value == 'C' || (callee.value[0] >= `A` && callee.value[0] <= `Z`)) {
5121 target_type := '${base.value}.${callee.value}'
5122 return t.make_cast('&${target_type}', t.transform_expr(arg_id),
5123 '&${target_type}')
5124 }
5125 }
5126 }
5127 if child.kind == .selector && (t.selector_chain_has_sum_shared_field(child_id)
5128 || t.selector_chain_has_sum_variant_field(child_id)) {
5129 value := t.transform_expr(child_id)
5130 mut value_type := t.node_type(child_id)
5131 if value_type.len == 0 {
5132 value_type = t.node_type(value)
5133 }
5134 stable := t.stable_transformed_expr_for_reuse(value, value_type, 'addr')
5135 addr := t.make_prefix(.amp, stable)
5136 if value_type.len > 0 {
5137 t.a.nodes[int(addr)].typ = '&${value_type}'
5138 }
5139 return addr
5140 }
5141 if child.kind == .ident && child.value.len > 0 && t.has_smartcast(child.value)
5142 && node.typ.starts_with('&') && t.is_sum_type_name(node.typ[1..]) {
5143 sum_type := node.typ[1..]
5144 wrapped := t.wrap_sum_value(child_id, sum_type)
5145 tmp_name := t.new_temp('sum_ref')
5146 t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, sum_type)
5147 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
5148 t.a.nodes[int(addr)].typ = node.typ
5149 return addr
5150 }
5151 value := t.transform_expr(child_id)
5152 if !t.expr_can_take_address(value) {
5153 mut value_type := t.node_type(child_id)
5154 if value_type.len == 0 {
5155 value_type = t.node_type(value)
5156 }
5157 stable := t.stable_transformed_expr_for_reuse(value, value_type, 'addr')
5158 addr := t.make_prefix(.amp, stable)
5159 if value_type.len > 0 {
5160 t.a.nodes[int(addr)].typ = '&${value_type}'
5161 }
5162 return addr
5163 }
5164 }
5165 mut new_children := []flat.NodeId{cap: int(node.children_count)}
5166 for i in 0 .. node.children_count {
5167 child_id := t.a.child(&node, i)
5168 mut new_child := t.transform_expr(child_id)
5169 if node.op == .not {
5170 child := t.a.nodes[int(new_child)]
5171 if child.kind == .infix {
5172 new_child = t.make_paren(new_child)
5173 }
5174 }
5175 new_children << new_child
5176 }
5177 start := t.a.children.len
5178 for nc in new_children {
5179 t.a.children << nc
5180 }
5181 new_id := t.a.add_node(flat.Node{
5182 kind: .prefix
5183 op: node.op
5184 children_start: start
5185 children_count: node.children_count
5186 pos: node.pos
5187 value: node.value
5188 typ: node.typ
5189 })
5190 if node.children_count == 1 {
5191 child_type := t.node_type(new_children[0])
5192 if node.op == .amp && child_type.len > 0 {
5193 t.a.nodes[int(new_id)].typ = '&${child_type}'
5194 } else if node.op == .mul && child_type.starts_with('&') {
5195 t.a.nodes[int(new_id)].typ = child_type[1..]
5196 }
5197 }
5198 return new_id
5199}
5200
5201// transform_amp_sum_cast_from_as_expr supports transform_amp_sum_cast_from_as_expr handling.
5202fn (mut t Transformer) transform_amp_sum_cast_from_as_expr(cast_node flat.Node, cast_arg_id flat.NodeId) ?flat.NodeId {
5203 target_sum := t.resolve_sum_name(cast_node.value)
5204 if target_sum.len == 0 || target_sum !in t.sum_types || int(cast_arg_id) < 0 {
5205 return none
5206 }
5207 mut arg_id := cast_arg_id
5208 for {
5209 arg0 := t.a.nodes[int(arg_id)]
5210 if arg0.kind != .paren || arg0.children_count == 0 {
5211 break
5212 }
5213 arg_id = t.a.child(&arg0, 0)
5214 }
5215 arg := t.a.nodes[int(arg_id)]
5216 if arg.kind != .as_expr || arg.children_count == 0 || arg.value.len == 0 {
5217 return none
5218 }
5219 source_id := t.a.child(&arg, 0)
5220 mut source_type := t.node_type(source_id)
5221 if source_type.len == 0 {
5222 source_type = t.original_expr_type(source_id)
5223 }
5224 mut source_sum := t.resolve_sum_name(t.trim_pointer_type(source_type))
5225 mut use_plain_source := false
5226 if source_sum.len == 0 || source_sum !in t.sum_types {
5227 raw_source_type := t.raw_expr_type_without_smartcast(source_id)
5228 raw_source_sum := t.resolve_sum_name(t.trim_pointer_type(raw_source_type))
5229 if raw_source_sum.len > 0 && raw_source_sum in t.sum_types {
5230 source_type = raw_source_type
5231 source_sum = raw_source_sum
5232 use_plain_source = true
5233 }
5234 }
5235 if source_sum.len == 0 || source_sum !in t.sum_types {
5236 return none
5237 }
5238 variant := t.resolve_variant(source_sum, arg.value)
5239 if variant.len == 0 || !t.variant_references_sum(variant, source_sum) {
5240 return none
5241 }
5242 source := if use_plain_source {
5243 t.make_plain_expr_for_smartcast(source_id)
5244 } else {
5245 t.transform_expr(source_id)
5246 }
5247 field_name := t.sum_field_name(variant)
5248 field_sel := t.make_selector_op(source, field_name, '&${variant}', if source_type.starts_with('&') {
5249 .arrow
5250 } else {
5251 .dot
5252 })
5253 return t.make_cast('&${cast_node.value}', field_sel, '&${cast_node.value}')
5254}
5255
5256// raw_expr_type_without_smartcast
5257// supports helper handling in transform.
5258fn (t &Transformer) raw_expr_type_without_smartcast(id flat.NodeId) string {
5259 if int(id) < 0 {
5260 return ''
5261 }
5262 node := t.a.nodes[int(id)]
5263 match node.kind {
5264 .ident {
5265 typ := t.normalize_type_alias(t.var_type(node.value))
5266 if typ.len > 0 {
5267 return typ
5268 }
5269 return t.normalize_type_alias(node.typ)
5270 }
5271 .selector {
5272 return t.raw_selector_type_without_smartcast(id)
5273 }
5274 else {
5275 return t.normalize_type_alias(node.typ)
5276 }
5277 }
5278}
5279
5280// raw_selector_type_without_smartcast supports raw_selector_type_without_smartcast handling.
5281fn (t &Transformer) raw_selector_type_without_smartcast(id flat.NodeId) string {
5282 if int(id) < 0 {
5283 return ''
5284 }
5285 node := t.a.nodes[int(id)]
5286 if node.kind != .selector || node.children_count == 0 {
5287 return ''
5288 }
5289 base_id := t.a.child(&node, 0)
5290 mut base_type := t.raw_expr_type_without_smartcast(base_id)
5291 if base_type.len == 0 {
5292 base_type = t.original_expr_type(base_id)
5293 }
5294 if ftyp := t.lookup_struct_field_type(base_type, node.value) {
5295 return ftyp
5296 }
5297 return t.normalize_type_alias(node.typ)
5298}
5299
5300// selector_chain_has_sum_shared_field supports selector_chain_has_sum_shared_field handling.
5301fn (t &Transformer) selector_chain_has_sum_shared_field(id flat.NodeId) bool {
5302 if int(id) < 0 {
5303 return false
5304 }
5305 node := t.a.nodes[int(id)]
5306 if node.kind != .selector || node.children_count == 0 {
5307 return false
5308 }
5309 base_id := t.a.child(&node, 0)
5310 base_type := t.node_type(base_id)
5311 if _ := t.sum_shared_field_type_name(base_type, node.value) {
5312 return true
5313 }
5314 return t.selector_chain_has_sum_shared_field(base_id)
5315}
5316
5317// selector_chain_has_sum_variant_field supports selector_chain_has_sum_variant_field handling.
5318fn (t &Transformer) selector_chain_has_sum_variant_field(id flat.NodeId) bool {
5319 if int(id) < 0 {
5320 return false
5321 }
5322 node := t.a.nodes[int(id)]
5323 if node.kind != .selector || node.children_count == 0 {
5324 return false
5325 }
5326 base_id := t.a.child(&node, 0)
5327 base_type := t.node_type(base_id)
5328 if t.sum_has_variant_field(base_type, node.value) {
5329 return true
5330 }
5331 return t.selector_chain_has_sum_variant_field(base_id)
5332}
5333
5334// sum_has_variant_field converts sum has variant field data for transform.
5335fn (t &Transformer) sum_has_variant_field(sum_type string, field string) bool {
5336 clean_sum := if sum_type.starts_with('&') { sum_type[1..] } else { sum_type }
5337 resolved_sum := t.resolve_sum_name(clean_sum)
5338 variants := t.sum_types[resolved_sum] or { return false }
5339 for variant in variants {
5340 if _ := t.sum_variant_field_type_name(variant, field) {
5341 return true
5342 }
5343 }
5344 return false
5345}
5346
5347// transform_paren_expr transforms transform paren expr data for transform.
5348fn (mut t Transformer) transform_paren_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5349 if node.children_count == 0 {
5350 return id
5351 }
5352 child_id := t.a.child(&node, 0)
5353 new_child := t.transform_expr(child_id)
5354 start := t.a.children.len
5355 t.a.children << new_child
5356 return t.a.add_node(flat.Node{
5357 kind: .paren
5358 op: node.op
5359 children_start: start
5360 children_count: 1
5361 pos: node.pos
5362 value: node.value
5363 typ: node.typ
5364 })
5365}
5366
5367// transform_postfix_expr transforms transform postfix expr data for transform.
5368fn (mut t Transformer) transform_postfix_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5369 if node.children_count == 0 {
5370 return id
5371 }
5372 child_id := t.a.child(&node, 0)
5373 child := t.a.nodes[int(child_id)]
5374 if node.op == .not && child.kind == .array_literal {
5375 node_type := t.node_type(id)
5376 if lowered := t.transform_fixed_array_literal_for_type(child_id, child, node_type) {
5377 return lowered
5378 }
5379 }
5380 new_child := if child.kind == .ident && t.pointer_value_lvalues[child.value] {
5381 t.make_paren(t.make_prefix(.mul, t.make_ident(child.value)))
5382 } else {
5383 t.transform_expr(child_id)
5384 }
5385 start := t.a.children.len
5386 t.a.children << new_child
5387 return t.a.add_node(flat.Node{
5388 kind: .postfix
5389 op: node.op
5390 children_start: start
5391 children_count: 1
5392 pos: node.pos
5393 value: node.value
5394 typ: node.typ
5395 })
5396}
5397
5398// transform_cast_expr transforms transform cast expr data for transform.
5399fn (mut t Transformer) transform_cast_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5400 if node.children_count == 0 {
5401 return id
5402 }
5403 target_type := t.normalize_type_alias(node.value)
5404 if target_type.starts_with('&') && !t.is_interface_type(target_type) {
5405 mut new_children := []flat.NodeId{cap: int(node.children_count)}
5406 for i in 0 .. node.children_count {
5407 child_id := t.a.child(&node, i)
5408 new_children << t.transform_expr(child_id)
5409 }
5410 start := t.a.children.len
5411 for nc in new_children {
5412 t.a.children << nc
5413 }
5414 return t.a.add_node(flat.Node{
5415 kind: .cast_expr
5416 op: node.op
5417 children_start: start
5418 children_count: node.children_count
5419 pos: node.pos
5420 value: node.value
5421 typ: node.typ
5422 })
5423 }
5424 if t.is_optional_type_name(node.value) {
5425 child_id := t.a.child(&node, 0)
5426 expr := t.transform_expr(child_id)
5427 mut expr_type := t.node_type(expr)
5428 if expr_type.len == 0 {
5429 expr_type = t.resolve_expr_type(child_id)
5430 }
5431 if t.is_optional_type_name(expr_type) {
5432 return t.coerce_transformed_expr_to_type(expr, child_id, node.value)
5433 }
5434 return t.make_optional_some(expr, t.qualify_optional_type(node.value))
5435 }
5436 if t.is_sum_type_name(target_type) {
5437 return t.wrap_sum_value(t.a.child(&node, 0), target_type)
5438 }
5439 // An explicit cast to an interface (`Animal(dog)`, `&PRNG(rng)`) boxes the
5440 // concrete value into the interface representation, just like an implicit
5441 // conversion does.
5442 if t.is_interface_type(target_type) {
5443 if boxed := t.transform_interface_value_for_type(t.a.child(&node, 0), node.value) {
5444 return boxed
5445 }
5446 }
5447 mut new_children := []flat.NodeId{cap: int(node.children_count)}
5448 for i in 0 .. node.children_count {
5449 child_id := t.a.child(&node, i)
5450 if target_type in ['f32', 'f64'] {
5451 new_children << t.transform_expr_for_type(child_id, target_type)
5452 } else {
5453 new_children << t.transform_expr(child_id)
5454 }
5455 }
5456 start := t.a.children.len
5457 for nc in new_children {
5458 t.a.children << nc
5459 }
5460 return t.a.add_node(flat.Node{
5461 kind: .cast_expr
5462 op: node.op
5463 children_start: start
5464 children_count: node.children_count
5465 pos: node.pos
5466 value: node.value
5467 typ: node.typ
5468 })
5469}
5470
5471// transform_array_literal transforms transform array literal data for transform.
5472fn (mut t Transformer) transform_array_literal(id flat.NodeId, node flat.Node) flat.NodeId {
5473 lowered := t.lower_array_literal_to_runtime(id, node)
5474 if lowered != id {
5475 return lowered
5476 }
5477 if node.children_count == 0 {
5478 return id
5479 }
5480 mut new_children := []flat.NodeId{cap: int(node.children_count)}
5481 for i in 0 .. node.children_count {
5482 child_id := t.a.child(&node, i)
5483 new_children << t.transform_expr(child_id)
5484 }
5485 start := t.a.children.len
5486 for nc in new_children {
5487 t.a.children << nc
5488 }
5489 return t.a.add_node(flat.Node{
5490 kind: .array_literal
5491 op: node.op
5492 children_start: start
5493 children_count: node.children_count
5494 pos: node.pos
5495 value: node.value
5496 typ: node.typ
5497 })
5498}
5499
5500// transform_map_init transforms transform map init data for transform.
5501fn (mut t Transformer) transform_map_init(id flat.NodeId, node flat.Node) flat.NodeId {
5502 return t.transform_map_init_expr(id, node)
5503}
5504
5505// transform_typeof_expr transforms transform typeof expr data for transform.
5506fn (mut t Transformer) transform_typeof_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5507 if node.value.len > 0 {
5508 return t.make_string_literal(node.value)
5509 }
5510 if node.children_count == 0 {
5511 return id
5512 }
5513 expr_id := t.a.child(&node, 0)
5514 expr := t.a.nodes[int(expr_id)]
5515 if expr.kind == .int_literal {
5516 return t.make_string_literal('int literal')
5517 }
5518 mut typ := t.node_type(expr_id)
5519 if typ.len == 0 {
5520 typ = t.reliable_stringify_type(expr_id)
5521 }
5522 if typ.len == 0 {
5523 typ = t.resolve_expr_type(expr_id)
5524 }
5525 if typ.len == 0 {
5526 typ = 'unknown'
5527 }
5528 return t.make_string_literal(typ)
5529}
5530
5531fn (mut t Transformer) transform_typeof_idx_expr(node flat.Node) flat.NodeId {
5532 type_name := t.typeof_type_name(node)
5533 return t.make_int_literal(t.type_index_for_type_name(type_name))
5534}
5535
5536fn (t &Transformer) typeof_type_name(node flat.Node) string {
5537 if node.value.len > 0 {
5538 return node.value
5539 }
5540 if node.children_count == 0 {
5541 return ''
5542 }
5543 expr_id := t.a.child(&node, 0)
5544 mut typ := t.node_type(expr_id)
5545 if typ.len == 0 {
5546 typ = t.reliable_stringify_type(expr_id)
5547 }
5548 if typ.len == 0 {
5549 typ = t.resolve_expr_type(expr_id)
5550 }
5551 return typ
5552}
5553
5554fn (t &Transformer) type_index_for_type_name(type_name string) int {
5555 if type_name.len == 0 {
5556 return 0
5557 }
5558 mut variants := []string{cap: 2}
5559 variants << type_name
5560 normalized := t.normalize_type_in_module(type_name, t.cur_module)
5561 if normalized.len > 0 && normalized !in variants {
5562 variants << normalized
5563 }
5564 mut sum_names := []string{}
5565 if t.cur_module.len > 0 {
5566 sum_names << '${t.cur_module}.Primitive'
5567 }
5568 sum_names << 'orm.Primitive'
5569 sum_names << 'Primitive'
5570 for sum_name in sum_names {
5571 if sum_name !in t.sum_types {
5572 continue
5573 }
5574 for variant in variants {
5575 idx := t.sum_type_index(sum_name, variant)
5576 if idx != 0 {
5577 return idx
5578 }
5579 }
5580 }
5581 for variant in variants {
5582 sum_name := t.find_sum_type_for_variant(variant)
5583 if sum_name.len > 0 {
5584 idx := t.sum_type_index(sum_name, variant)
5585 if idx != 0 {
5586 return idx
5587 }
5588 }
5589 }
5590 return 0
5591}
5592
5593// transform_ident_expr transforms transform ident expr data for transform.
5594fn (mut t Transformer) transform_ident_expr(id flat.NodeId, node flat.Node) flat.NodeId {
5595 match node.value {
5596 '@VMODROOT' {
5597 return t.make_string_literal(t.vmod_root())
5598 }
5599 else {
5600 if smartcasted := t.smartcast_ident_value(node.value) {
5601 return smartcasted
5602 }
5603 // Idents are the most common node; re-annotating them in place (rather than
5604 // allocating a fresh node) avoids cascading rebuilds of every enclosing
5605 // expression and the associated allocations (critical under -gc none).
5606 if !t.in_call_callee {
5607 if fn_name := t.resolve_fn_value_ident(node.value) {
5608 t.a.nodes[int(id)].value = fn_name
5609 return id
5610 }
5611 }
5612 typ := t.var_type(node.value)
5613 if typ.len > 0 && typ != node.typ {
5614 t.a.nodes[int(id)].typ = typ
5615 }
5616 return id
5617 }
5618 }
5619}
5620
5621// smartcast_ident_value supports smartcast ident value handling for Transformer.
5622fn (mut t Transformer) smartcast_ident_value(name string) ?flat.NodeId {
5623 if t.smartcast_stack.len == 0 {
5624 return none
5625 }
5626 contexts := t.smartcasts_for(name)
5627 if contexts.len == 0 {
5628 return none
5629 }
5630 return t.apply_smartcast_contexts(t.make_ident(name), t.var_type(name), contexts)
5631}
5632
5633// apply_smartcast_contexts supports apply smartcast contexts handling for Transformer.
5634fn (mut t Transformer) apply_smartcast_contexts(base flat.NodeId, typ string, contexts []SmartcastContext) flat.NodeId {
5635 mut current := base
5636 mut current_type := typ
5637 for i, sc in contexts {
5638 if t.is_interface_type_name(sc.sum_type_name) {
5639 qv := t.interface_variant_type(sc.variant_name)
5640 field_op := if current_type.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
5641 object := t.make_selector_op(current, '_object', 'voidptr', field_op)
5642 cast := t.make_cast('&${qv}', object, '&${qv}')
5643 current = t.make_prefix(.mul, cast)
5644 t.a.nodes[int(current)].typ = qv
5645 current_type = qv
5646 continue
5647 }
5648 qv := t.resolve_variant(sc.sum_type_name, sc.variant_name)
5649 if t.expr_is_variant_access(current, qv) {
5650 current_type = qv
5651 continue
5652 }
5653 field := t.sum_field_name(qv)
5654 use_ptr := t.variant_references_sum(qv, sc.sum_type_name)
5655 field_typ := if use_ptr { '&${qv}' } else { qv }
5656 field_op := if current_type.starts_with('&') { flat.Op.arrow } else { flat.Op.dot }
5657 field_sel := t.make_selector_op(current, field, field_typ, field_op)
5658 if use_ptr && i == contexts.len - 1 {
5659 current = t.make_prefix(.mul, field_sel)
5660 t.a.nodes[int(current)].typ = qv
5661 current_type = qv
5662 } else {
5663 current = field_sel
5664 current_type = field_typ
5665 }
5666 }
5667 return current
5668}
5669
5670// expr_is_variant_access supports expr is variant access handling for Transformer.
5671fn (t &Transformer) expr_is_variant_access(id flat.NodeId, variant string) bool {
5672 if int(id) < 0 || variant.len == 0 {
5673 return false
5674 }
5675 node := t.a.nodes[int(id)]
5676 field := t.sum_field_name(variant)
5677 match node.kind {
5678 .selector {
5679 return node.value == field
5680 }
5681 .prefix {
5682 if node.op == .mul && node.children_count > 0 {
5683 return t.expr_is_variant_access(t.a.child(&node, 0), variant)
5684 }
5685 return false
5686 }
5687 .paren {
5688 if node.children_count > 0 {
5689 return t.expr_is_variant_access(t.a.child(&node, 0), variant)
5690 }
5691 return false
5692 }
5693 else {
5694 return false
5695 }
5696 }
5697}
5698
5699// is_sum_variant_field_name reports whether is sum variant field name applies in transform.
5700fn (t &Transformer) is_sum_variant_field_name(name string) bool {
5701 return name in t.sum_variant_fields
5702}
5703
5704// variant_type_from_sum_field_name converts variant type from sum field name data for transform.
5705fn (t &Transformer) variant_type_from_sum_field_name(name string) ?string {
5706 if variant := t.sum_variant_fields[name] {
5707 return variant
5708 }
5709 return none
5710}
5711
5712// generated_variant_access_type supports generated variant access type handling for Transformer.
5713fn (t &Transformer) generated_variant_access_type(id flat.NodeId) ?string {
5714 if int(id) < 0 {
5715 return none
5716 }
5717 node := t.a.nodes[int(id)]
5718 match node.kind {
5719 .selector {
5720 variant := t.variant_type_from_sum_field_name(node.value) or { return none }
5721 if node.typ.starts_with('&') {
5722 return node.typ
5723 }
5724 return variant
5725 }
5726 .prefix {
5727 if node.op == .mul && node.children_count > 0 {
5728 variant := t.generated_variant_access_type(t.a.child(&node, 0)) or { return none }
5729 return t.trim_pointer_type(variant)
5730 }
5731 return none
5732 }
5733 .paren {
5734 if node.children_count > 0 {
5735 return t.generated_variant_access_type(t.a.child(&node, 0))
5736 }
5737 return none
5738 }
5739 else {
5740 return none
5741 }
5742 }
5743}
5744
5745// original_expr_type supports original expr type handling for Transformer.
5746fn (t &Transformer) original_expr_type(id flat.NodeId) string {
5747 if int(id) < 0 {
5748 return ''
5749 }
5750 node := t.a.nodes[int(id)]
5751 match node.kind {
5752 .ident {
5753 typ := t.normalize_type_alias(t.var_type(node.value))
5754 if typ.len > 0 {
5755 return typ
5756 }
5757 if node.typ.len > 0 {
5758 return t.normalize_type_alias(node.typ)
5759 }
5760 return ''
5761 }
5762 .selector {
5763 if node.typ.len > 0 {
5764 return t.normalize_type_alias(node.typ)
5765 }
5766 return t.resolve_selector_type(node)
5767 }
5768 else {
5769 if node.typ.len > 0 {
5770 return t.normalize_type_alias(node.typ)
5771 }
5772 return t.resolve_expr_type(id)
5773 }
5774 }
5775}
5776
5777// smartcasts_for supports smartcasts for handling for Transformer.
5778fn (t &Transformer) smartcasts_for(expr_name string) []SmartcastContext {
5779 if expr_name.len == 0 || t.smartcast_stack.len == 0 {
5780 return []SmartcastContext{}
5781 }
5782 mut result := []SmartcastContext{cap: 1}
5783 for sc in t.smartcast_stack {
5784 if sc.expr_name == expr_name {
5785 result << sc
5786 }
5787 }
5788 return result
5789}
5790
5791// has_smartcast reports whether has smartcast applies in transform.
5792fn (t &Transformer) has_smartcast(expr_name string) bool {
5793 if expr_name.len == 0 || t.smartcast_stack.len == 0 {
5794 return false
5795 }
5796 for sc in t.smartcast_stack {
5797 if sc.expr_name == expr_name {
5798 return true
5799 }
5800 }
5801 return false
5802}
5803
5804// resolve_fn_value_ident resolves resolve fn value ident information for transform.
5805fn (t &Transformer) resolve_fn_value_ident(name string) ?string {
5806 if name.len == 0 || name.contains('.') || t.var_type(name).len > 0 {
5807 return none
5808 }
5809 mut candidates := []string{}
5810 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
5811 candidates << '${t.cur_module}.${name}'
5812 }
5813 candidates << name
5814 for candidate in candidates {
5815 if candidate in t.fn_ret_types {
5816 return candidate
5817 }
5818 if !isnil(t.tc) && (candidate in t.tc.fn_ret_types || candidate in t.tc.fn_param_types) {
5819 return candidate
5820 }
5821 }
5822 return none
5823}
5824
5825// --- helper methods ---
5826
5827// new_temp supports new temp handling for Transformer.
5828pub fn (mut t Transformer) new_temp(prefix string) string {
5829 name := '__${prefix}_${t.temp_counter}'
5830 t.temp_counter++
5831 return name
5832}
5833
5834// make_ident builds make ident data for transform.
5835pub fn (mut t Transformer) make_ident(name string) flat.NodeId {
5836 id := t.a.add_val(.ident, name)
5837 typ := t.var_type(name)
5838 if typ.len > 0 {
5839 t.a.nodes[int(id)].typ = typ
5840 }
5841 return id
5842}
5843
5844// make_decl_assign builds make decl assign data for transform.
5845pub fn (mut t Transformer) make_decl_assign(name string, rhs flat.NodeId) flat.NodeId {
5846 lhs := t.make_ident(name)
5847 start := t.a.children.len
5848 t.a.children << lhs
5849 t.a.children << rhs
5850 return t.a.add_node(flat.Node{
5851 kind: .decl_assign
5852 children_start: start
5853 children_count: 2
5854 })
5855}
5856
5857// make_expr_stmt builds make expr stmt data for transform.
5858pub fn (mut t Transformer) make_expr_stmt(expr flat.NodeId) flat.NodeId {
5859 start := t.a.children.len
5860 t.a.children << expr
5861 return t.a.add_node(flat.Node{
5862 kind: .expr_stmt
5863 children_start: start
5864 children_count: 1
5865 })
5866}
5867
5868// make_assign builds make assign data for transform.
5869pub fn (mut t Transformer) make_assign(lhs flat.NodeId, rhs flat.NodeId) flat.NodeId {
5870 return t.make_assign_op(lhs, rhs, .assign)
5871}
5872
5873// make_assign_op builds make assign op data for transform.
5874pub fn (mut t Transformer) make_assign_op(lhs flat.NodeId, rhs flat.NodeId, op flat.Op) flat.NodeId {
5875 start := t.a.children.len
5876 t.a.children << lhs
5877 t.a.children << rhs
5878 return t.a.add_node(flat.Node{
5879 kind: .assign
5880 op: op
5881 children_start: start
5882 children_count: 2
5883 })
5884}
5885
5886// make_block builds make block data for transform.
5887pub fn (mut t Transformer) make_block(stmts []flat.NodeId) flat.NodeId {
5888 start := t.a.children.len
5889 for id in stmts {
5890 t.a.children << id
5891 }
5892 return t.a.add_node(flat.Node{
5893 kind: .block
5894 children_start: start
5895 children_count: flat.child_count(stmts.len)
5896 })
5897}
5898
5899// make_infix builds make infix data for transform.
5900pub fn (mut t Transformer) make_infix(op flat.Op, lhs flat.NodeId, rhs flat.NodeId) flat.NodeId {
5901 start := t.a.children.len
5902 t.a.children << lhs
5903 t.a.children << rhs
5904 return t.a.add_node(flat.Node{
5905 kind: .infix
5906 op: op
5907 children_start: start
5908 children_count: 2
5909 })
5910}
5911
5912// make_prefix builds make prefix data for transform.
5913pub fn (mut t Transformer) make_prefix(op flat.Op, expr flat.NodeId) flat.NodeId {
5914 start := t.a.children.len
5915 t.a.children << expr
5916 return t.a.add_node(flat.Node{
5917 kind: .prefix
5918 op: op
5919 children_start: start
5920 children_count: 1
5921 })
5922}
5923
5924// make_paren builds make paren data for transform.
5925pub fn (mut t Transformer) make_paren(expr flat.NodeId) flat.NodeId {
5926 start := t.a.children.len
5927 t.a.children << expr
5928 return t.a.add_node(flat.Node{
5929 kind: .paren
5930 children_start: start
5931 children_count: 1
5932 })
5933}
5934
5935// make_if builds make if data for transform.
5936pub fn (mut t Transformer) make_if(cond flat.NodeId, then_block flat.NodeId, else_block flat.NodeId) flat.NodeId {
5937 start := t.a.children.len
5938 t.a.children << cond
5939 t.a.children << then_block
5940 if int(else_block) >= 0 {
5941 t.a.children << else_block
5942 return t.a.add_node(flat.Node{
5943 kind: .if_expr
5944 children_start: start
5945 children_count: 3
5946 })
5947 }
5948 return t.a.add_node(flat.Node{
5949 kind: .if_expr
5950 children_start: start
5951 children_count: 2
5952 })
5953}
5954
5955// push_smartcast updates push smartcast state for Transformer.
5956pub fn (mut t Transformer) push_smartcast(expr_name string, variant string, sum_type string) {
5957 t.smartcast_stack << SmartcastContext{
5958 expr_name: expr_name
5959 variant_name: variant
5960 sum_type_name: sum_type
5961 }
5962}
5963
5964// pop_smartcast updates pop smartcast state for Transformer.
5965pub fn (mut t Transformer) pop_smartcast() {
5966 if t.smartcast_stack.len > 0 {
5967 t.smartcast_stack.delete_last()
5968 }
5969}
5970
5971// find_smartcast resolves find smartcast information for transform.
5972pub fn (t &Transformer) find_smartcast(expr_name string) ?SmartcastContext {
5973 // Search from top of stack (most recent) to bottom
5974 mut i := t.smartcast_stack.len - 1
5975 for i >= 0 {
5976 if t.smartcast_stack[i].expr_name == expr_name {
5977 return t.smartcast_stack[i]
5978 }
5979 i--
5980 }
5981 return none
5982}
5983
5984// expr_key supports expr key handling for Transformer.
5985fn (t &Transformer) expr_key(id flat.NodeId) string {
5986 if int(id) < 0 {
5987 return ''
5988 }
5989 node := t.a.nodes[int(id)]
5990 if node.kind == .ident {
5991 return node.value
5992 }
5993 if node.kind == .selector && node.children_count >= 1 {
5994 base_id := t.a.child(&node, 0)
5995 base_key := t.expr_key(base_id)
5996 if base_key.len > 0 {
5997 return '${base_key}.${node.value}'
5998 }
5999 }
6000 if node.kind == .index && node.children_count >= 2 {
6001 base_key := t.expr_key(t.a.child(&node, 0))
6002 index_key := t.expr_key_part(t.a.child(&node, 1))
6003 if base_key.len > 0 && index_key.len > 0 {
6004 return '${base_key}[${index_key}]'
6005 }
6006 }
6007 if node.kind in [.as_expr, .paren] && node.children_count >= 1 {
6008 return t.expr_key(t.a.child(&node, 0))
6009 }
6010 return ''
6011}
6012
6013// expr_key_part supports expr key part handling for Transformer.
6014fn (t &Transformer) expr_key_part(id flat.NodeId) string {
6015 if int(id) < 0 {
6016 return ''
6017 }
6018 node := t.a.nodes[int(id)]
6019 match node.kind {
6020 .ident {
6021 return node.value
6022 }
6023 .int_literal, .string_literal, .char_literal, .enum_val {
6024 return node.value
6025 }
6026 else {
6027 return t.expr_key(id)
6028 }
6029 }
6030}
6031
6032// qualify_variant supports qualify variant handling for Transformer.
6033fn (t &Transformer) qualify_variant(variant string, sum_type_name string) string {
6034 if variant.contains('.') {
6035 return variant
6036 }
6037 resolved_sum := t.resolve_sum_name(sum_type_name)
6038 if resolved_variant := t.sum_variant_name(resolved_sum, variant) {
6039 return resolved_variant
6040 }
6041 if sum_type_name.contains('.') {
6042 mod := sum_type_name.all_before_last('.')
6043 return '${mod}.${variant}'
6044 }
6045 return variant
6046}
6047
6048// sum_variant_name supports sum variant name handling for Transformer.
6049fn (t &Transformer) sum_variant_name(sum_name string, variant string) ?string {
6050 resolved_sum := t.resolve_sum_name(sum_name)
6051 variants := t.sum_types[resolved_sum] or { return none }
6052 for v in variants {
6053 if t.variant_names_match(v, variant) {
6054 return v
6055 }
6056 }
6057 return none
6058}
6059
6060fn (t &Transformer) variant_names_match(a string, b string) bool {
6061 return a == b || t.variant_short_name(a) == t.variant_short_name(b)
6062}
6063
6064fn (t &Transformer) variant_short_name(name string) string {
6065 return variant_short_name_text(name)
6066}
6067
6068fn variant_short_name_text(name string) string {
6069 if name.starts_with('&') {
6070 return '&' + variant_short_name_text(name[1..])
6071 }
6072 if name.starts_with('[]') {
6073 return '[]' + variant_short_name_text(name[2..])
6074 }
6075 if name.starts_with('map[') {
6076 bracket_end := name.index(']') or { return name }
6077 key := name[4..bracket_end]
6078 value := name[bracket_end + 1..]
6079 return 'map[${variant_short_name_text(key)}]${variant_short_name_text(value)}'
6080 }
6081 return if name.contains('.') { name.all_after_last('.') } else { name }
6082}
6083
6084// sum_field_name supports sum field name handling for Transformer.
6085fn (t &Transformer) sum_field_name(variant string) string {
6086 if variant.starts_with('&') {
6087 return t.sum_field_name(variant[1..])
6088 }
6089 if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('.') {
6090 return t.sum_field_name(variant[3..])
6091 }
6092 if variant.starts_with('ptr') && variant.len > 3 && variant[3..].contains('__') {
6093 return t.sum_field_name(variant[3..].replace('__', '.'))
6094 }
6095 if variant.starts_with('[]') {
6096 return '_Array_${c_name(variant[2..])}'
6097 }
6098 if variant.starts_with('map[') {
6099 return '_Map_${c_name(variant[4..].replace(']', '_'))}'
6100 }
6101 return match variant {
6102 'int' { '_int' }
6103 'i8' { '_i8' }
6104 'i16' { '_i16' }
6105 'i64' { '_i64' }
6106 'u8', 'byte' { '_u8' }
6107 'u16' { '_u16' }
6108 'u32' { '_u32' }
6109 'u64' { '_u64' }
6110 'f32' { '_f32' }
6111 'f64' { '_f64' }
6112 'bool' { '_bool' }
6113 'string' { '_string' }
6114 else { c_name(variant) }
6115 }
6116}
6117
6118// variant_references_sum supports variant references sum handling for Transformer.
6119fn (t &Transformer) variant_references_sum(variant string, sum_name string) bool {
6120 _ = t
6121 _ = variant
6122 _ = sum_name
6123 return true
6124}
6125
6126// tc_variant_refs_sum_inner supports tc variant refs sum inner handling for Transformer.
6127fn (t &Transformer) tc_variant_refs_sum_inner(variant string, sum_name string, mut visited map[string]bool) bool {
6128 if variant == sum_name || variant.all_after_last('.') == sum_name.all_after_last('.') {
6129 return true
6130 }
6131 if variant in visited {
6132 return false
6133 }
6134 visited[variant] = true
6135 mut lookup := variant
6136 if lookup !in t.tc.structs && !lookup.contains('.') && sum_name.contains('.') {
6137 qlookup := '${sum_name.all_before_last('.')}.${lookup}'
6138 if qlookup in t.tc.structs {
6139 lookup = qlookup
6140 }
6141 }
6142 if lookup !in t.tc.structs && lookup.contains('.') {
6143 short := lookup.all_after_last('.')
6144 if short in t.tc.structs {
6145 lookup = short
6146 }
6147 }
6148 if lookup in t.tc.structs {
6149 for f in t.tc.structs[lookup] {
6150 if t.tc_type_references_sum(f.typ, sum_name, mut visited) {
6151 return true
6152 }
6153 }
6154 }
6155 return false
6156}
6157
6158// tc_type_references_sum supports tc type references sum handling for Transformer.
6159fn (t &Transformer) tc_type_references_sum(typ types.Type, sum_name string, mut visited map[string]bool) bool {
6160 clean := types.unwrap_pointer(typ)
6161 if clean is types.Struct && clean.name == sum_name {
6162 return true
6163 }
6164 if clean is types.SumType && clean.name == sum_name {
6165 return true
6166 }
6167 if clean is types.SumType {
6168 return true
6169 }
6170 if clean is types.Struct {
6171 if t.tc_variant_refs_sum_inner(clean.name, sum_name, mut visited) {
6172 return true
6173 }
6174 }
6175 if clean is types.Array {
6176 return t.tc_type_references_sum(clean.elem_type, sum_name, mut visited)
6177 }
6178 return false
6179}
6180
6181// variant_refs_sum_inner supports variant refs sum inner handling for Transformer.
6182fn (t &Transformer) variant_refs_sum_inner(variant string, sum_name string, mut visited map[string]bool) bool {
6183 short_v := if variant.contains('.') { variant.all_after_last('.') } else { variant }
6184 short_s := if sum_name.contains('.') { sum_name.all_after_last('.') } else { sum_name }
6185 if short_v == short_s {
6186 return true
6187 }
6188 if variant in visited {
6189 return false
6190 }
6191 visited[variant] = true
6192 qualified := if sum_name.contains('.') && !variant.contains('.') {
6193 '${sum_name.all_before_last('.')}.${variant}'
6194 } else {
6195 variant
6196 }
6197 lookup := if qualified in t.structs {
6198 qualified
6199 } else if variant in t.structs {
6200 variant
6201 } else {
6202 short_v
6203 }
6204 if lookup in t.structs {
6205 for f in t.structs[lookup].fields {
6206 if f.typ.starts_with('&') || f.typ.starts_with('[]') {
6207 continue
6208 }
6209 ftyp := f.typ
6210 short_f := if ftyp.contains('.') { ftyp.all_after_last('.') } else { ftyp }
6211 if ftyp == sum_name || short_f == short_s {
6212 return true
6213 }
6214 qftyp := if sum_name.contains('.') && !ftyp.contains('.') {
6215 '${sum_name.all_before_last('.')}.${ftyp}'
6216 } else {
6217 ftyp
6218 }
6219 if qftyp in t.sum_types {
6220 return true
6221 }
6222 if ftyp in t.structs || short_f in t.structs || qftyp in t.structs {
6223 inner_lookup := if ftyp in t.structs {
6224 ftyp
6225 } else if short_f in t.structs {
6226 short_f
6227 } else {
6228 qftyp
6229 }
6230 if t.variant_refs_sum_inner(inner_lookup, sum_name, mut visited) {
6231 return true
6232 }
6233 }
6234 }
6235 }
6236 return false
6237}
6238
6239// drain_pending supports drain pending handling for Transformer.
6240pub fn (mut t Transformer) drain_pending(mut result []flat.NodeId) {
6241 for id in t.pending_stmts {
6242 result << id
6243 }
6244 t.pending_stmts.clear()
6245}
6246
6247// with_pending_before supports with pending before handling for Transformer.
6248fn (mut t Transformer) with_pending_before(stmt flat.NodeId) []flat.NodeId {
6249 mut result := []flat.NodeId{}
6250 t.drain_pending(mut result)
6251 result << stmt
6252 return result
6253}
6254
6255// is_stmt_kind_id reports whether is stmt kind id applies in transform.
6256fn (t &Transformer) is_stmt_kind_id(kind_id int) bool {
6257 return kind_id == 39 || kind_id == 40 || kind_id == 41 || kind_id == 42 || kind_id == 43
6258 || kind_id == 44 || kind_id == 45 || kind_id == 46 || kind_id == 47 || kind_id == 48
6259 || kind_id == 49 || kind_id == 50 || kind_id == 52 || kind_id == 53 || kind_id == 54
6260 || kind_id == 55 || kind_id == 15 || kind_id == 56 || kind_id == 57 || kind_id == 60
6261}
6262
6263// is_stmt_kind reports whether is stmt kind applies in transform.
6264fn (t &Transformer) is_stmt_kind(kind flat.NodeKind) bool {
6265 return t.is_stmt_kind_id(int(kind))
6266}
6267
6268// --- type resolution helpers (will move to types.v later) ---
6269
6270// infer_decl_type resolves infer decl type information for transform.
6271fn (t &Transformer) infer_decl_type(node &flat.Node) string {
6272 if node.typ.len > 0 {
6273 return node.typ
6274 }
6275 if node.children_count >= 2 {
6276 rhs_id := t.a.child(node, 1)
6277 return t.decl_rhs_type(rhs_id)
6278 }
6279 return ''
6280}
6281
6282// resolve_expr_type resolves resolve expr type information for transform.
6283fn (t &Transformer) resolve_expr_type(id flat.NodeId) string {
6284 if int(id) < 0 {
6285 return ''
6286 }
6287 node := t.a.nodes[int(id)]
6288 match node.kind {
6289 .ident {
6290 if sc := t.find_smartcast(node.value) {
6291 return t.smartcast_target_type(sc)
6292 }
6293 local_type := t.normalize_type_alias(t.var_type(node.value))
6294 if local_type.len > 0 {
6295 return local_type
6296 }
6297 if global_type := t.globals[node.value] {
6298 return t.normalize_type_alias(global_type)
6299 }
6300 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
6301 qglobal := '${t.cur_module}.${node.value}'
6302 if global_type := t.globals[qglobal] {
6303 return t.normalize_type_alias(global_type)
6304 }
6305 }
6306 if !isnil(t.tc) {
6307 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
6308 qname := '${t.cur_module}.${node.value}'
6309 if name := t.const_type_name(qname) {
6310 return name
6311 }
6312 }
6313 if name := t.const_type_name(node.value) {
6314 return name
6315 }
6316 }
6317 return ''
6318 }
6319 .call {
6320 concrete_typ := t.concrete_node_type_name(node)
6321 if concrete_typ.len > 0 {
6322 return concrete_typ
6323 }
6324 new_map_typ := t.new_map_call_type(node)
6325 if new_map_typ.len > 0 {
6326 return new_map_typ
6327 }
6328 if node.typ.len > 0 {
6329 typ := t.normalize_type_alias(node.typ)
6330 if typ !in ['array', 'map', 'unknown'] {
6331 return typ
6332 }
6333 }
6334 mut ret := t.get_call_return_type(id, node)
6335 if ret.len == 0 {
6336 ret = t.current_call_return_type(node)
6337 }
6338 if ret.len > 0 {
6339 return ret
6340 }
6341 return ''
6342 }
6343 .cast_expr {
6344 if node.value.len > 0 {
6345 return node.value
6346 }
6347 return node.typ
6348 }
6349 .array_literal {
6350 if node.typ.len > 0 {
6351 typ := t.normalize_type_alias(node.typ)
6352 if typ != 'array' {
6353 return typ
6354 }
6355 }
6356 if node.children_count > 0 {
6357 elem_type := t.node_type(t.a.child(&node, 0))
6358 if elem_type.len > 0 {
6359 return '[]${elem_type}'
6360 }
6361 }
6362 return '[]int'
6363 }
6364 .array_init {
6365 if node.value.starts_with('[]') {
6366 return '[]${node.value}'
6367 }
6368 if node.typ.len > 0 {
6369 typ := t.normalize_type_alias(node.typ)
6370 if typ != 'array' {
6371 return typ
6372 }
6373 }
6374 if t.is_fixed_array_type(node.value) {
6375 return node.value
6376 }
6377 if node.value.len > 0 {
6378 return '[]${node.value}'
6379 }
6380 return '[]int'
6381 }
6382 .map_init {
6383 if node.value.len > 0 {
6384 return node.value
6385 }
6386 if node.children_count >= 2 {
6387 key_type := t.node_type(t.a.child(&node, 0))
6388 value_type := t.node_type(t.a.child(&node, 1))
6389 if key_type.len > 0 && value_type.len > 0 {
6390 return 'map[${key_type}]${value_type}'
6391 }
6392 }
6393 return ''
6394 }
6395 .selector {
6396 if t.smartcast_stack.len == 0 {
6397 typ := t.concrete_node_type_name(node)
6398 if typ.len > 0 {
6399 return typ
6400 }
6401 }
6402 if !isnil(t.tc) && node.children_count > 0 {
6403 base := t.a.child_node(&node, 0)
6404 if base.kind == .ident {
6405 qname := '${base.value}.${node.value}'
6406 if name := t.const_type_name(qname) {
6407 return name
6408 }
6409 }
6410 }
6411 return t.resolve_selector_type(node)
6412 }
6413 .index {
6414 return t.index_expr_type(id, node)
6415 }
6416 .paren {
6417 if node.children_count > 0 {
6418 return t.node_type(t.a.child(&node, 0))
6419 }
6420 return ''
6421 }
6422 .prefix {
6423 if node.children_count > 0 {
6424 child_type := t.node_type(t.a.child(&node, 0))
6425 if node.op == .amp && child_type.len > 0 {
6426 return '&${child_type}'
6427 }
6428 if node.op == .mul && child_type.starts_with('&') {
6429 return child_type[1..]
6430 }
6431 if node.op == .not {
6432 return 'bool'
6433 }
6434 if node.op in [.plus, .minus, .bit_not] {
6435 return child_type
6436 }
6437 }
6438 return ''
6439 }
6440 .block {
6441 return t.stmt_value_type(id)
6442 }
6443 .bool_literal {
6444 return 'bool'
6445 }
6446 .float_literal {
6447 return 'f64'
6448 }
6449 .char_literal {
6450 return 'rune'
6451 }
6452 .string_literal, .string_interp {
6453 return 'string'
6454 }
6455 .int_literal {
6456 return 'int'
6457 }
6458 .nil_literal {
6459 return 'voidptr'
6460 }
6461 .none_expr {
6462 return '?void'
6463 }
6464 .infix {
6465 if node.children_count >= 2 {
6466 if node.op in [.eq, .ne, .lt, .gt, .le, .ge, .logical_and, .logical_or] {
6467 return 'bool'
6468 }
6469 lhs_type := t.node_type(t.a.child(&node, 0))
6470 ret_type := t.infix_struct_operator_result_type(node, lhs_type)
6471 if ret_type.len > 0 {
6472 return ret_type
6473 }
6474 if node.op == .plus && lhs_type == 'string' {
6475 return 'string'
6476 }
6477 rhs_type := t.node_type(t.a.child(&node, 1))
6478 if node.op == .plus && rhs_type == 'string' {
6479 return 'string'
6480 }
6481 if node.op in [.plus, .minus] && lhs_type.starts_with('&')
6482 && t.is_integer_type_name(rhs_type) {
6483 return lhs_type
6484 }
6485 if node.op == .plus && rhs_type.starts_with('&') && t.is_integer_type_name(lhs_type) {
6486 return rhs_type
6487 }
6488 if node.op in [.plus, .minus, .mul, .div, .mod, .amp, .pipe, .xor] {
6489 if lhs_type.len > 0 && rhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type)
6490 && t.is_numeric_stringify_type(rhs_type) {
6491 return promote_numeric_stringify_type(lhs_type, rhs_type)
6492 }
6493 if lhs_type.len > 0 && t.is_numeric_stringify_type(lhs_type) {
6494 return lhs_type
6495 }
6496 if rhs_type.len > 0 && t.is_numeric_stringify_type(rhs_type) {
6497 return rhs_type
6498 }
6499 }
6500 }
6501 return ''
6502 }
6503 .or_expr {
6504 if node.children_count > 0 {
6505 inner_type := t.resolve_expr_type(t.a.child(&node, 0))
6506 if inner_type.starts_with('!') {
6507 return inner_type[1..]
6508 }
6509 if inner_type.starts_with('?') {
6510 return inner_type[1..]
6511 }
6512 return inner_type
6513 }
6514 return ''
6515 }
6516 .if_expr {
6517 return t.if_expr_result_type(id, node)
6518 }
6519 .match_stmt {
6520 return t.match_expr_type(node)
6521 }
6522 else {
6523 return ''
6524 }
6525 }
6526}
6527
6528fn (t &Transformer) current_call_return_type(node flat.Node) string {
6529 if node.children_count > 0 {
6530 fn_node := t.a.child_node(&node, 0)
6531 if fn_node.kind == .ident {
6532 local_type := t.var_type(fn_node.value)
6533 if local_type.len > 0 {
6534 if ret := t.local_fn_value_return_type_from_type(local_type) {
6535 return t.call_return_type_name(ret, node)
6536 }
6537 } else {
6538 if ret := t.local_fn_decl_return_type(fn_node.value) {
6539 return t.call_return_type_name(ret, node)
6540 }
6541 }
6542 }
6543 }
6544 name := t.resolve_call_name(node)
6545 if name.len == 0 {
6546 return ''
6547 }
6548 if ret := t.fn_ret_types[name] {
6549 return t.call_return_type_name(ret, node)
6550 }
6551 if !isnil(t.tc) {
6552 if ret := t.tc.fn_ret_types[name] {
6553 return t.call_return_type_name(ret.name(), node)
6554 }
6555 }
6556 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
6557 qname := '${t.cur_module}.${name}'
6558 if ret := t.fn_ret_types[qname] {
6559 return t.call_return_type_name(ret, node)
6560 }
6561 if !isnil(t.tc) {
6562 if ret := t.tc.fn_ret_types[qname] {
6563 return t.call_return_type_name(ret.name(), node)
6564 }
6565 }
6566 }
6567 return ''
6568}
6569
6570fn (t &Transformer) is_local_fn_value_call(node flat.Node) bool {
6571 if node.kind != .call || node.children_count == 0 {
6572 return false
6573 }
6574 fn_id := t.a.child(&node, 0)
6575 if int(fn_id) < 0 {
6576 return false
6577 }
6578 fn_node := t.a.nodes[int(fn_id)]
6579 if fn_node.kind != .ident || fn_node.value.len == 0 {
6580 return false
6581 }
6582 local_type := t.var_type(fn_node.value)
6583 return local_type.starts_with('fn ') || t.is_fn_pointer_type_name(local_type)
6584}
6585
6586// const_type_name supports const type name handling for Transformer.
6587fn (t &Transformer) const_type_name(name string) ?string {
6588 if isnil(t.tc) || name.len == 0 {
6589 return none
6590 }
6591 key := t.const_type_key(name) or { return none }
6592 typ := t.tc.const_types[key] or { return none }
6593 if tname := t.const_entry_type_name(key, typ) {
6594 return tname
6595 }
6596 return none
6597}
6598
6599// const_type_key supports const type key handling for Transformer.
6600fn (t &Transformer) const_type_key(name string) ?string {
6601 if name.len == 0 || isnil(t.tc) {
6602 return none
6603 }
6604 if name in t.tc.const_types {
6605 return name
6606 }
6607 if key := t.const_suffixes[name] {
6608 if key.len > 0 {
6609 return key
6610 }
6611 }
6612 return none
6613}
6614
6615// const_entry_type_name supports const entry type name handling for Transformer.
6616fn (t &Transformer) const_entry_type_name(name string, typ types.Type) ?string {
6617 tname := t.normalize_type_alias(typ.name())
6618 if tname.len > 0 && tname != 'unknown' {
6619 if t.is_fixed_array_type(tname) {
6620 if expr_id := t.tc.const_exprs[name] {
6621 expr := t.a.nodes[int(expr_id)]
6622 if expr.kind == .call {
6623 return '[]${fixed_array_elem_type(tname)}'
6624 }
6625 }
6626 }
6627 return tname
6628 }
6629 if name.ends_with('.scanner_matcher') {
6630 mod_name := name.all_before_last('.')
6631 return '${mod_name}.KeywordsMatcherTrie'
6632 }
6633 if expr_id := t.tc.const_exprs[name] {
6634 if etyp := t.tc.expr_type(expr_id) {
6635 ename := t.normalize_type_alias(etyp.name())
6636 if ename.len > 0 && ename != 'unknown' {
6637 return ename
6638 }
6639 }
6640 ename := t.resolve_expr_type(expr_id)
6641 if ename.len > 0 && ename != 'unknown' {
6642 return ename
6643 }
6644 }
6645 return none
6646}
6647
6648// match_expr_type supports match expr type handling for Transformer.
6649fn (t &Transformer) match_expr_type(node flat.Node) string {
6650 if node.kind != .match_stmt || node.children_count < 2 {
6651 return ''
6652 }
6653 match_expr_id := t.a.child(&node, 0)
6654 for i in 1 .. node.children_count {
6655 branch := t.a.child_node(&node, i)
6656 if branch.kind != .match_branch {
6657 continue
6658 }
6659 body_start := if branch.value == 'else' { 0 } else { t.count_conds(*branch) }
6660 if branch.children_count <= body_start {
6661 continue
6662 }
6663 contexts := t.match_branch_type_contexts(match_expr_id, *branch)
6664 for j := branch.children_count - 1; j >= body_start; j-- {
6665 stmt_id := t.a.child(branch, j)
6666 typ := if contexts.len > 0 {
6667 t.stmt_value_type_with_smartcasts(stmt_id, contexts)
6668 } else {
6669 t.stmt_value_type(stmt_id)
6670 }
6671 if typ.len > 0 {
6672 return typ
6673 }
6674 }
6675 }
6676 return ''
6677}
6678
6679// match_branch_type_contexts supports match branch type contexts handling for Transformer.
6680fn (t &Transformer) match_branch_type_contexts(match_expr_id flat.NodeId, branch flat.Node) []SmartcastContext {
6681 if branch.value == 'else' {
6682 return []SmartcastContext{}
6683 }
6684 n_conds := t.count_conds(branch)
6685 if n_conds != 1 {
6686 return []SmartcastContext{}
6687 }
6688 cond_val_id := t.a.child(&branch, 0)
6689 variant_name := t.match_type_pattern(cond_val_id) or { return []SmartcastContext{} }
6690 subj := t.expr_key(match_expr_id)
6691 sum_name := t.sum_type_for_is_expr(t.original_expr_type(match_expr_id), variant_name)
6692 if subj.len == 0 || sum_name.len == 0 {
6693 return []SmartcastContext{}
6694 }
6695 return [
6696 SmartcastContext{
6697 expr_name: subj
6698 variant_name: variant_name
6699 sum_type_name: sum_name
6700 },
6701 ]
6702}
6703
6704// stmt_value_type supports stmt value type handling for Transformer.
6705fn (t &Transformer) stmt_value_type(id flat.NodeId) string {
6706 if int(id) < 0 {
6707 return ''
6708 }
6709 node := t.a.nodes[int(id)]
6710 match node.kind {
6711 .expr_stmt {
6712 if node.children_count > 0 {
6713 return t.node_type(t.a.child(&node, node.children_count - 1))
6714 }
6715 return ''
6716 }
6717 .block {
6718 for i := node.children_count - 1; i >= 0; i-- {
6719 typ := t.stmt_value_type(t.a.child(&node, i))
6720 if typ.len > 0 {
6721 return typ
6722 }
6723 }
6724 return ''
6725 }
6726 else {
6727 return t.node_type(id)
6728 }
6729 }
6730}
6731
6732// --- match lowering (existing, will move to expr.v later) ---
6733
6734// lower_match_stmts builds lower match stmts data for transform.
6735fn (mut t Transformer) lower_match_stmts() {
6736 for i, node in t.a.nodes {
6737 if node.kind == .match_stmt {
6738 if_id := t.lower_one_match(node)
6739 t.a.nodes[i] = t.a.nodes[int(if_id)]
6740 } else if node.kind == .expr_stmt && node.children_count == 1 {
6741 child_id := t.a.child(&node, 0)
6742 child := t.a.nodes[int(child_id)]
6743 if child.kind == .match_stmt {
6744 if_id := t.lower_one_match(child)
6745 t.a.nodes[i] = flat.Node{
6746 kind: .expr_stmt
6747 children_start: t.a.children.len
6748 children_count: 1
6749 }
6750 t.a.children << if_id
6751 }
6752 }
6753 }
6754}
6755
6756// lower_one_match builds lower one match data for transform.
6757fn (mut t Transformer) lower_one_match(node flat.Node) flat.NodeId {
6758 match_expr_id := t.a.child(&node, 0)
6759 match_expr := t.a.nodes[int(match_expr_id)]
6760 result_type := t.match_expr_type(node)
6761
6762 needs_temp := match_expr.kind !in [.ident, .int_literal, .bool_literal, .string_literal,
6763 .char_literal]
6764
6765 mut actual_expr_id := match_expr_id
6766 mut prefix_id := flat.empty_node
6767
6768 if needs_temp {
6769 tmp_name := '__match_tmp_${int(match_expr_id)}'
6770 match_type := t.node_type(match_expr_id)
6771 transformed_match_expr := t.transform_expr(match_expr_id)
6772 tmp_ident := t.a.add_val(.ident, tmp_name)
6773 t.a.nodes[int(tmp_ident)].typ = match_type
6774 decl_start := t.a.children.len
6775 t.a.children << tmp_ident
6776 t.a.children << transformed_match_expr
6777 prefix_id = t.a.add_node(flat.Node{
6778 kind: .decl_assign
6779 children_start: decl_start
6780 children_count: 2
6781 typ: match_type
6782 })
6783 actual_expr_id = t.a.add_val(.ident, tmp_name)
6784 t.a.nodes[int(actual_expr_id)].typ = match_type
6785 }
6786
6787 mut branches := []flat.NodeId{}
6788 for i in 1 .. node.children_count {
6789 branches << t.a.child(&node, i)
6790 }
6791 if_id := t.build_match_chain(actual_expr_id, match_expr_id, branches, 0)
6792
6793 if needs_temp {
6794 block_start := t.a.children.len
6795 t.a.children << prefix_id
6796 t.a.children << if_id
6797 block_id := t.a.add_node(flat.Node{
6798 kind: .block
6799 children_start: block_start
6800 children_count: 2
6801 typ: result_type
6802 })
6803 return block_id
6804 }
6805 if result_type.len > 0 {
6806 t.a.nodes[int(if_id)].typ = result_type
6807 }
6808 return if_id
6809}
6810
6811// build_match_chain builds match chain data for transform.
6812fn (mut t Transformer) build_match_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branches []flat.NodeId, idx int) flat.NodeId {
6813 if idx >= branches.len {
6814 return t.a.add(flat.NodeKind.empty)
6815 }
6816 branch := t.a.nodes[int(branches[idx])]
6817 is_else := branch.value == 'else'
6818
6819 // count_conds scans the branch's condition children; compute it once and reuse
6820 // (build_match_chain runs per branch, and the compiler has very large matches).
6821 n_conds := if is_else { 0 } else { t.count_conds(branch) }
6822 body_start_idx := n_conds
6823 if !is_else && n_conds > 1 && t.match_branch_all_type_patterns(branch) {
6824 return t.build_match_type_branch_chain(match_expr_id, orig_expr_id, branch, branches, idx,
6825 0)
6826 }
6827 // Push a smartcast around the body transform when this branch matches a
6828 // single sum-type variant, so selectors inside the body get narrowed.
6829 mut sc_pushed := 0
6830 if !is_else {
6831 if n_conds == 1 {
6832 cond_val_id := t.a.child(&branch, 0)
6833 if variant_name := t.match_type_pattern(cond_val_id) {
6834 subj := t.expr_key(match_expr_id)
6835 sum_name := t.sum_type_for_is_expr(t.original_expr_type(match_expr_id),
6836 variant_name)
6837 if subj.len > 0 && sum_name.len > 0 {
6838 t.push_smartcast(subj, variant_name, sum_name)
6839 sc_pushed++
6840 }
6841 orig_subj := t.expr_key(orig_expr_id)
6842 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
6843 t.push_smartcast(orig_subj, variant_name, sum_name)
6844 sc_pushed++
6845 }
6846 }
6847 }
6848 }
6849 mut body_ids := []flat.NodeId{}
6850 for i in body_start_idx .. branch.children_count {
6851 body_ids << t.a.child(&branch, i)
6852 }
6853 new_body := t.transform_stmts(body_ids)
6854 for _ in 0 .. sc_pushed {
6855 t.pop_smartcast()
6856 }
6857 body_block := t.make_block(new_body)
6858
6859 if is_else {
6860 return body_block
6861 }
6862
6863 outer_pending := t.pending_stmts.clone()
6864 t.pending_stmts.clear()
6865 cond_id := t.build_match_cond(match_expr_id, branch)
6866 mut cond_prelude := []flat.NodeId{}
6867 t.drain_pending(mut cond_prelude)
6868 t.pending_stmts = outer_pending
6869
6870 mut if_ids := []flat.NodeId{}
6871 if_ids << cond_id
6872 if_ids << body_block
6873 if idx + 1 < branches.len {
6874 else_part := t.build_match_chain(match_expr_id, orig_expr_id, branches, idx + 1)
6875 if_ids << else_part
6876 }
6877
6878 if_start := t.a.children.len
6879 for id in if_ids {
6880 t.a.children << id
6881 }
6882 if_id := t.a.add_node(flat.Node{
6883 kind: .if_expr
6884 children_start: if_start
6885 children_count: flat.child_count(if_ids.len)
6886 })
6887 if cond_prelude.len > 0 {
6888 cond_prelude << if_id
6889 return t.make_block(cond_prelude)
6890 }
6891 return if_id
6892}
6893
6894// build_match_value_stmts builds match value stmts data for transform.
6895fn (mut t Transformer) build_match_value_stmts(node flat.Node, target_name string, target_type string) []flat.NodeId {
6896 match_expr_id := t.a.child(&node, 0)
6897 match_expr := t.a.nodes[int(match_expr_id)]
6898 needs_temp := match_expr.kind !in [.ident, .int_literal, .bool_literal, .string_literal,
6899 .char_literal]
6900
6901 mut actual_expr_id := match_expr_id
6902 mut result := []flat.NodeId{}
6903 if needs_temp {
6904 tmp_name := '__match_tmp_${int(match_expr_id)}'
6905 match_type := t.node_type(match_expr_id)
6906 transformed_match_expr := t.transform_expr(match_expr_id)
6907 t.drain_pending(mut result)
6908 tmp_ident := t.a.add_val(.ident, tmp_name)
6909 t.a.nodes[int(tmp_ident)].typ = match_type
6910 result << t.make_decl_assign_typed(tmp_name, transformed_match_expr, match_type)
6911 actual_expr_id = t.a.add_val(.ident, tmp_name)
6912 t.a.nodes[int(actual_expr_id)].typ = match_type
6913 }
6914
6915 mut branches := []flat.NodeId{}
6916 for i in 1 .. node.children_count {
6917 branches << t.a.child(&node, i)
6918 }
6919 result << t.build_match_value_chain(actual_expr_id, match_expr_id, branches, 0, target_name,
6920 target_type)
6921 return result
6922}
6923
6924// build_match_value_chain builds match value chain data for transform.
6925fn (mut t Transformer) build_match_value_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branches []flat.NodeId, idx int, target_name string, target_type string) flat.NodeId {
6926 if idx >= branches.len {
6927 return t.a.add(flat.NodeKind.empty)
6928 }
6929 branch := t.a.nodes[int(branches[idx])]
6930 is_else := branch.value == 'else'
6931 body_start_idx := if is_else { 0 } else { t.count_conds(branch) }
6932 if !is_else && t.match_branch_all_type_patterns(branch) && t.count_conds(branch) > 1 {
6933 return t.build_match_value_type_branch_chain(match_expr_id, orig_expr_id, branch, branches,
6934 idx, 0, target_name, target_type)
6935 }
6936
6937 mut sc_pushed := 0
6938 if !is_else {
6939 n_conds := t.count_conds(branch)
6940 if n_conds == 1 {
6941 cond_val_id := t.a.child(&branch, 0)
6942 if variant_name := t.match_type_pattern(cond_val_id) {
6943 subj := t.expr_key(match_expr_id)
6944 sum_name := t.sum_type_for_is_expr(t.original_expr_type(match_expr_id),
6945 variant_name)
6946 if subj.len > 0 && sum_name.len > 0 {
6947 t.push_smartcast(subj, variant_name, sum_name)
6948 sc_pushed++
6949 }
6950 orig_subj := t.expr_key(orig_expr_id)
6951 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
6952 t.push_smartcast(orig_subj, variant_name, sum_name)
6953 sc_pushed++
6954 }
6955 }
6956 }
6957 }
6958
6959 mut body_ids := []flat.NodeId{}
6960 for i in body_start_idx .. branch.children_count {
6961 body_ids << t.a.child(&branch, i)
6962 }
6963 raw_body := t.make_block(body_ids)
6964 body_block := t.if_value_branch_block(raw_body, target_name, target_type)
6965 for _ in 0 .. sc_pushed {
6966 t.pop_smartcast()
6967 }
6968
6969 if is_else {
6970 return body_block
6971 }
6972 outer_pending := t.pending_stmts.clone()
6973 t.pending_stmts.clear()
6974 cond_id := t.build_match_cond(match_expr_id, branch)
6975 mut cond_prelude := []flat.NodeId{}
6976 t.drain_pending(mut cond_prelude)
6977 t.pending_stmts = outer_pending
6978 mut if_ids := []flat.NodeId{}
6979 if_ids << cond_id
6980 if_ids << body_block
6981 if idx + 1 < branches.len {
6982 else_part := t.build_match_value_chain(match_expr_id, orig_expr_id, branches, idx + 1,
6983 target_name, target_type)
6984 if_ids << else_part
6985 }
6986 if_start := t.a.children.len
6987 for id in if_ids {
6988 t.a.children << id
6989 }
6990 if_id := t.a.add_node(flat.Node{
6991 kind: .if_expr
6992 children_start: if_start
6993 children_count: flat.child_count(if_ids.len)
6994 })
6995 if cond_prelude.len > 0 {
6996 cond_prelude << if_id
6997 return t.make_block(cond_prelude)
6998 }
6999 return if_id
7000}
7001
7002// build_match_value_type_branch_chain supports build_match_value_type_branch_chain handling.
7003fn (mut t Transformer) build_match_value_type_branch_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branch flat.Node, branches []flat.NodeId, idx int, cond_idx int, target_name string, target_type string) flat.NodeId {
7004 n_conds := t.count_conds(branch)
7005 if cond_idx >= n_conds {
7006 return if idx + 1 < branches.len {
7007 t.build_match_value_chain(match_expr_id, orig_expr_id, branches, idx + 1, target_name,
7008 target_type)
7009 } else {
7010 t.a.add(flat.NodeKind.empty)
7011 }
7012 }
7013 cond_val_id := t.a.child(&branch, cond_idx)
7014 variant_name := t.match_type_pattern(cond_val_id) or {
7015 return t.build_match_value_chain(match_expr_id, orig_expr_id, branches, idx + 1,
7016 target_name, target_type)
7017 }
7018 is_start := t.a.children.len
7019 t.a.children << match_expr_id
7020 is_id := t.a.add_node(flat.Node{
7021 kind: .is_expr
7022 value: variant_name
7023 children_start: is_start
7024 children_count: 1
7025 })
7026 cond_id := t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
7027
7028 mut sc_pushed := 0
7029 subj := t.expr_key(match_expr_id)
7030 sum_name := t.sum_type_for_is_expr(t.original_expr_type(match_expr_id), variant_name)
7031 if subj.len > 0 && sum_name.len > 0 {
7032 t.push_smartcast(subj, variant_name, sum_name)
7033 sc_pushed++
7034 }
7035 orig_subj := t.expr_key(orig_expr_id)
7036 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
7037 t.push_smartcast(orig_subj, variant_name, sum_name)
7038 sc_pushed++
7039 }
7040
7041 mut body_ids := []flat.NodeId{}
7042 for i in n_conds .. branch.children_count {
7043 body_ids << t.a.child(&branch, i)
7044 }
7045 raw_body := t.make_block(body_ids)
7046 body_block := t.if_value_branch_block(raw_body, target_name, target_type)
7047 for _ in 0 .. sc_pushed {
7048 t.pop_smartcast()
7049 }
7050
7051 else_part := t.build_match_value_type_branch_chain(match_expr_id, orig_expr_id, branch,
7052 branches, idx, cond_idx + 1, target_name, target_type)
7053 start := t.a.children.len
7054 t.a.children << cond_id
7055 t.a.children << body_block
7056 t.a.children << else_part
7057 return t.a.add_node(flat.Node{
7058 kind: .if_expr
7059 children_start: start
7060 children_count: 3
7061 })
7062}
7063
7064// build_match_type_branch_chain builds match type branch chain data for transform.
7065fn (mut t Transformer) build_match_type_branch_chain(match_expr_id flat.NodeId, orig_expr_id flat.NodeId, branch flat.Node, branches []flat.NodeId, idx int, cond_idx int) flat.NodeId {
7066 n_conds := t.count_conds(branch)
7067 if cond_idx >= n_conds {
7068 return if idx + 1 < branches.len {
7069 t.build_match_chain(match_expr_id, orig_expr_id, branches, idx + 1)
7070 } else {
7071 t.a.add(flat.NodeKind.empty)
7072 }
7073 }
7074 cond_val_id := t.a.child(&branch, cond_idx)
7075 variant_name := t.match_type_pattern(cond_val_id) or {
7076 return t.build_match_chain(match_expr_id, orig_expr_id, branches, idx + 1)
7077 }
7078 is_start := t.a.children.len
7079 t.a.children << match_expr_id
7080 is_id := t.a.add_node(flat.Node{
7081 kind: .is_expr
7082 value: variant_name
7083 children_start: is_start
7084 children_count: 1
7085 })
7086 cond_id := t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
7087
7088 mut sc_pushed := 0
7089 subj := t.expr_key(match_expr_id)
7090 sum_name := t.sum_type_for_is_expr(t.original_expr_type(match_expr_id), variant_name)
7091 if subj.len > 0 && sum_name.len > 0 {
7092 t.push_smartcast(subj, variant_name, sum_name)
7093 sc_pushed++
7094 }
7095 orig_subj := t.expr_key(orig_expr_id)
7096 if orig_subj.len > 0 && orig_subj != subj && sum_name.len > 0 {
7097 t.push_smartcast(orig_subj, variant_name, sum_name)
7098 sc_pushed++
7099 }
7100
7101 mut body_ids := []flat.NodeId{}
7102 for i in n_conds .. branch.children_count {
7103 body_ids << t.a.child(&branch, i)
7104 }
7105 body_block := t.make_block(t.transform_stmts(body_ids))
7106 for _ in 0 .. sc_pushed {
7107 t.pop_smartcast()
7108 }
7109
7110 else_part := t.build_match_type_branch_chain(match_expr_id, orig_expr_id, branch, branches,
7111 idx, cond_idx + 1)
7112 start := t.a.children.len
7113 t.a.children << cond_id
7114 t.a.children << body_block
7115 t.a.children << else_part
7116 return t.a.add_node(flat.Node{
7117 kind: .if_expr
7118 children_start: start
7119 children_count: 3
7120 })
7121}
7122
7123// make_match_eq builds the equality test between a match subject and a branch
7124// value, lowering string comparisons to string__eq (the transformer owns string
7125// lowering; the backend no longer special-cases it).
7126fn (mut t Transformer) make_match_eq(lhs flat.NodeId, rhs flat.NodeId) flat.NodeId {
7127 if t.is_string_type(lhs) || t.is_string_type(rhs) {
7128 return t.make_call('string__eq', arr2(lhs, rhs))
7129 }
7130 start := t.a.children.len
7131 t.a.children << lhs
7132 t.a.children << rhs
7133 return t.a.add_node(flat.Node{
7134 kind: .infix
7135 op: .eq
7136 children_start: start
7137 children_count: 2
7138 })
7139}
7140
7141// make_match_range builds make match range data for transform.
7142fn (mut t Transformer) make_match_range(lhs flat.NodeId, range_id flat.NodeId) flat.NodeId {
7143 range := t.a.nodes[int(range_id)]
7144 if range.children_count < 2 {
7145 return t.make_bool_literal(false)
7146 }
7147 low_id := t.a.children[range.children_start]
7148 high_id := t.a.children[range.children_start + 1]
7149 low := t.match_cond_value(lhs, low_id)
7150 high := t.match_cond_value(lhs, high_id)
7151 ge_cmp := t.make_infix(.ge, lhs, low)
7152 le_cmp := t.make_infix(.le, lhs, high)
7153 return t.make_infix(.logical_and, ge_cmp, le_cmp)
7154}
7155
7156// match_cond_value supports match cond value handling for Transformer.
7157fn (mut t Transformer) match_cond_value(match_expr_id flat.NodeId, cond_val_id flat.NodeId) flat.NodeId {
7158 cond_val := t.a.nodes[int(cond_val_id)]
7159 if cond_val.kind == .enum_val {
7160 return t.transform_enum_shorthand(cond_val_id, cond_val, t.node_type(match_expr_id))
7161 }
7162 return t.transform_expr(cond_val_id)
7163}
7164
7165// build_match_cond builds match cond data for transform.
7166fn (mut t Transformer) build_match_cond(match_expr_id flat.NodeId, branch flat.Node) flat.NodeId {
7167 n_conds := t.count_conds(branch)
7168 if n_conds == 1 {
7169 cond_val_id := t.a.child(&branch, 0)
7170 cond_val := t.a.nodes[int(cond_val_id)]
7171 if variant_name := t.match_type_pattern(cond_val_id) {
7172 is_start := t.a.children.len
7173 t.a.children << match_expr_id
7174 is_id := t.a.add_node(flat.Node{
7175 kind: .is_expr
7176 value: variant_name
7177 children_start: is_start
7178 children_count: 1
7179 })
7180 return t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
7181 }
7182 if cond_val.kind == .range {
7183 return t.make_match_range(match_expr_id, cond_val_id)
7184 }
7185 return t.make_match_eq(match_expr_id, t.match_cond_value(match_expr_id, cond_val_id))
7186 }
7187 mut result := flat.empty_node
7188 for i in 0 .. n_conds {
7189 cond_val_id := t.a.child(&branch, i)
7190 cond_val := t.a.nodes[int(cond_val_id)]
7191 variant_name := t.match_type_pattern(cond_val_id) or { '' }
7192 cmp := if variant_name.len > 0 {
7193 is_start := t.a.children.len
7194 t.a.children << match_expr_id
7195 is_id := t.a.add_node(flat.Node{
7196 kind: .is_expr
7197 value: variant_name
7198 children_start: is_start
7199 children_count: 1
7200 })
7201 t.transform_is_expr(is_id, t.a.nodes[int(is_id)])
7202 } else if cond_val.kind == .range {
7203 t.make_match_range(match_expr_id, cond_val_id)
7204 } else {
7205 t.make_match_eq(match_expr_id, t.match_cond_value(match_expr_id, cond_val_id))
7206 }
7207 if int(result) < 0 {
7208 result = cmp
7209 } else {
7210 or_start := t.a.children.len
7211 t.a.children << result
7212 t.a.children << cmp
7213 result = t.a.add_node(flat.Node{
7214 kind: .infix
7215 op: .logical_or
7216 children_start: or_start
7217 children_count: 2
7218 })
7219 }
7220 }
7221 return result
7222}
7223
7224// match_type_pattern supports match type pattern handling for Transformer.
7225fn (t &Transformer) match_type_pattern(cond_val_id flat.NodeId) ?string {
7226 if int(cond_val_id) < 0 {
7227 return none
7228 }
7229 pattern := t.type_pattern_name(cond_val_id)
7230 if pattern.len > 0 && t.is_sum_variant(pattern) {
7231 return pattern
7232 }
7233 return none
7234}
7235
7236// match_branch_all_type_patterns supports match branch all type patterns handling for Transformer.
7237fn (t &Transformer) match_branch_all_type_patterns(branch flat.Node) bool {
7238 n_conds := t.count_conds(branch)
7239 if n_conds == 0 {
7240 return false
7241 }
7242 for i in 0 .. n_conds {
7243 cond_val_id := t.a.child(&branch, i)
7244 if _ := t.match_type_pattern(cond_val_id) {
7245 continue
7246 }
7247 return false
7248 }
7249 return true
7250}
7251
7252// count_conds supports count conds handling for Transformer.
7253fn (t &Transformer) count_conds(branch flat.Node) int {
7254 if branch.value.len > 0 && branch.value != 'else' {
7255 if branch.value[0] >= `0` && branch.value[0] <= `9` {
7256 return branch.value.int()
7257 }
7258 }
7259 mut count := 0
7260 for i in 0 .. branch.children_count {
7261 child := t.a.child_node(&branch, i)
7262 if child.kind == .int_literal || child.kind == .ident || child.kind == .string_literal
7263 || child.kind == .enum_val || child.kind == .bool_literal || child.kind == .char_literal
7264 || child.kind == .selector || child.kind == .range || child.kind == .prefix {
7265 count++
7266 } else {
7267 break
7268 }
7269 }
7270 return count
7271}
7272
7273// is_sum_variant reports whether is sum variant applies in transform.
7274pub fn (t &Transformer) is_sum_variant(name string) bool {
7275 short_name := t.variant_short_name(name)
7276 for _, variants in t.sum_types {
7277 for v in variants {
7278 short_v := t.variant_short_name(v)
7279 if v == name || short_v == short_name {
7280 return true
7281 }
7282 }
7283 }
7284 return false
7285}
7286
7287// --- array append lowering (existing, will move to expr.v later) ---
7288
7289// lower_array_appends builds lower array appends data for transform.
7290fn (mut t Transformer) lower_array_appends() {
7291 for i, node in t.a.nodes {
7292 if node.kind == .module_decl {
7293 t.cur_module = node.value
7294 continue
7295 }
7296 if node.kind == .fn_decl {
7297 t.reset_var_types()
7298 t.annotate_fn_body(node)
7299 continue
7300 }
7301 if node.kind == .decl_assign && node.children_count >= 2 {
7302 lhs := t.a.child_node(&node, 0)
7303 if lhs.kind == .ident && lhs.value.len > 0 {
7304 typ := t.infer_decl_type(node)
7305 if typ.len > 0 {
7306 t.set_var_type(lhs.value, typ)
7307 }
7308 }
7309 }
7310 if node.kind == .expr_stmt && node.children_count == 1 {
7311 child_id := t.a.child(&node, 0)
7312 mut child := &t.a.nodes[int(child_id)]
7313 if child.kind == .infix && child.op == .left_shift {
7314 t.annotate_left_shift(child_id)
7315 }
7316 }
7317 if node.kind == .assign && node.op == .left_shift_assign && node.children_count >= 2 {
7318 lhs_id := t.a.child(&node, 0)
7319 lhs_type := t.lvalue_type(lhs_id)
7320 clean_lhs_type := t.clean_array_append_lhs_type(lhs_type)
7321 if clean_lhs_type.starts_with('[]') {
7322 rhs_id := t.a.child(&node, 1)
7323 rhs_type := t.lvalue_type(rhs_id)
7324 elem_type := clean_lhs_type[2..]
7325 val := if t.array_append_rhs_is_push_many(lhs_id, rhs_id, rhs_type, elem_type) {
7326 'push_many'
7327 } else {
7328 'push'
7329 }
7330 t.a.nodes[i] = flat.Node{
7331 kind: node.kind
7332 op: node.op
7333 children_start: node.children_start
7334 children_count: node.children_count
7335 value: val
7336 typ: elem_type
7337 }
7338 }
7339 }
7340 }
7341}
7342
7343// annotate_fn_body supports annotate fn body handling for Transformer.
7344fn (mut t Transformer) annotate_fn_body(fn_node flat.Node) {
7345 for i in 0 .. fn_node.children_count {
7346 child_id := t.a.child(&fn_node, i)
7347 if int(child_id) < 0 {
7348 continue
7349 }
7350 child := t.a.nodes[int(child_id)]
7351 if child.kind == .param && child.value.len > 0 && child.typ.len > 0 {
7352 t.set_var_type(child.value, t.normalize_type_alias(child.typ))
7353 }
7354 if child.kind == .decl_assign && child.children_count >= 2 {
7355 lhs := t.a.child_node(&child, 0)
7356 if lhs.kind == .ident && lhs.value.len > 0 {
7357 typ := t.infer_decl_type(child)
7358 if typ.len > 0 {
7359 t.set_var_type(lhs.value, typ)
7360 }
7361 }
7362 }
7363 if child.kind == .expr_stmt && child.children_count == 1 {
7364 inner_id := t.a.child(&child, 0)
7365 inner := t.a.nodes[int(inner_id)]
7366 if inner.kind == .infix && inner.op == .left_shift {
7367 t.annotate_left_shift(inner_id)
7368 }
7369 }
7370 t.annotate_block_stmts(child_id)
7371 }
7372}
7373
7374// annotate_block_stmts supports annotate block stmts handling for Transformer.
7375fn (mut t Transformer) annotate_block_stmts(node_id flat.NodeId) {
7376 if int(node_id) < 0 {
7377 return
7378 }
7379 node := t.a.nodes[int(node_id)]
7380 for i in 0 .. node.children_count {
7381 child_id := t.a.child(&node, i)
7382 if int(child_id) < 0 {
7383 continue
7384 }
7385 child := t.a.nodes[int(child_id)]
7386 if child.kind == .decl_assign && child.children_count >= 2 {
7387 lhs := t.a.child_node(&child, 0)
7388 if lhs.kind == .ident && lhs.value.len > 0 {
7389 typ := t.infer_decl_type(child)
7390 if typ.len > 0 {
7391 t.set_var_type(lhs.value, typ)
7392 }
7393 }
7394 }
7395 if child.kind == .expr_stmt && child.children_count == 1 {
7396 inner_id := t.a.child(&child, 0)
7397 inner := t.a.nodes[int(inner_id)]
7398 if inner.kind == .infix && inner.op == .left_shift {
7399 t.annotate_left_shift(inner_id)
7400 }
7401 }
7402 t.annotate_block_stmts(child_id)
7403 }
7404}
7405
7406// annotate_left_shift supports annotate left shift handling for Transformer.
7407fn (mut t Transformer) annotate_left_shift(node_id flat.NodeId) {
7408 node := t.a.nodes[int(node_id)]
7409 if node.children_count < 2 {
7410 return
7411 }
7412 lhs_id := t.a.child(&node, 0)
7413 mut lhs_type := t.lvalue_type(lhs_id)
7414 if lhs_type == 'strings.Builder' || lhs_type == '&strings.Builder' || lhs_type == 'Builder'
7415 || lhs_type == '&Builder' {
7416 lhs_type = '[]u8'
7417 }
7418 clean_lhs_type := t.clean_array_append_lhs_type(lhs_type)
7419 if !clean_lhs_type.starts_with('[]') {
7420 return
7421 }
7422 rhs_id := t.a.child(&node, 1)
7423 rhs_type := t.lvalue_type(rhs_id)
7424 elem_type := clean_lhs_type[2..]
7425 if t.array_append_rhs_is_push_many(lhs_id, rhs_id, rhs_type, elem_type) {
7426 t.a.nodes[int(node_id)] = flat.Node{
7427 kind: .infix
7428 op: .left_shift
7429 children_start: node.children_start
7430 children_count: node.children_count
7431 value: 'push_many'
7432 typ: elem_type
7433 }
7434 } else {
7435 t.a.nodes[int(node_id)] = flat.Node{
7436 kind: .infix
7437 op: .left_shift
7438 children_start: node.children_start
7439 children_count: node.children_count
7440 value: 'push'
7441 typ: elem_type
7442 }
7443 }
7444}
7445
7446// annotate_left_shift_assign supports annotate left shift assign handling for Transformer.
7447fn (mut t Transformer) annotate_left_shift_assign(node_id flat.NodeId) {
7448 node := t.a.nodes[int(node_id)]
7449 if node.kind != .assign || node.op != .left_shift_assign || node.children_count < 2 {
7450 return
7451 }
7452 lhs_id := t.a.child(&node, 0)
7453 lhs := t.a.nodes[int(lhs_id)]
7454 if lhs.kind != .ident {
7455 return
7456 }
7457 lhs_type := t.lvalue_type(lhs_id)
7458 clean_lhs_type := t.clean_array_append_lhs_type(lhs_type)
7459 if !clean_lhs_type.starts_with('[]') {
7460 return
7461 }
7462 rhs_id := t.a.child(&node, 1)
7463 rhs_type := t.lvalue_type(rhs_id)
7464 elem_type := clean_lhs_type[2..]
7465 val := if t.array_append_rhs_is_push_many(lhs_id, rhs_id, rhs_type, elem_type) {
7466 'push_many'
7467 } else {
7468 'push'
7469 }
7470 t.a.nodes[int(node_id)] = flat.Node{
7471 kind: node.kind
7472 op: node.op
7473 children_start: node.children_start
7474 children_count: node.children_count
7475 value: val
7476 typ: elem_type
7477 }
7478}
7479
7480// --- public query helpers ---
7481
7482// get_struct_info returns get struct info data for Transformer.
7483pub fn (t &Transformer) get_struct_info(name string) ?StructInfo {
7484 if info := t.structs[name] {
7485 return info
7486 }
7487 return none
7488}
7489
7490// get_global_type returns get global type data for Transformer.
7491pub fn (t &Transformer) get_global_type(name string) ?string {
7492 if typ := t.globals[name] {
7493 return typ
7494 }
7495 return none
7496}
7497