vq / vlib / v3 / transform / transform.v
7496 lines · 7194 sloc · 223.9 KB
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