v4 / vlib / v3 / transform / transform.v
7046 lines · 6762 sloc · 211.82 KB · 1498e02bf64c6e33255fc0d3055b6e72900af9a5
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// node_kind_id supports node kind id handling for transform.
31fn node_kind_id(node flat.Node) int {
32 mut kind_id := node.kind_id
33 if kind_id == 0 && int(node.kind) != 0 {
34 kind_id = int(node.kind)
35 }
36 return kind_id
37}
38
39// SmartcastContext stores smartcast context state used by transform.
40pub struct SmartcastContext {
41pub:
42 expr_name string // the expression being smartcast (e.g. "node")
43 variant_name string // the variant type name (e.g. "Ident")
44 sum_type_name string // the parent sum type name (e.g. "Expr")
45}
46
47// Transformer represents transformer data used by transform.
48pub struct Transformer {
49mut:
50 a &flat.FlatAst = unsafe { nil }
51 tc &types.TypeChecker = unsafe { nil }
52 structs map[string]StructInfo
53 unique_fields map[string]string
54 alias_methods map[string]string
55 globals map[string]string
56 sum_types map[string][]string
57 sum_variant_parents map[string][]string
58 sum_variant_fields map[string]string
59 qualified_types map[string]string
60 fn_ret_types map[string]string
61 const_suffixes map[string]string
62 enum_types map[string][]string
63 cur_file string
64 cur_module string
65 cur_fn_name string
66 cur_fn_ret_type string
67 var_types []VarTypeBinding
68 pointer_value_lvalues map[string]bool
69 temp_counter int
70 pending_stmts []flat.NodeId
71 smartcast_stack []SmartcastContext
72 in_call_callee bool
73 in_const_init bool
74 in_return_expr bool
75 alias_cache &AliasCache = unsafe { nil }
76 sum_cache &AliasCache = unsafe { nil }
77 used_fns map[string]bool
78 // used_struct_operator_fns holds the callee names of direct calls seen during
79 // monomorphize. Infix operators on generic instances are lowered to direct calls
80 // (`Vec_int__plus(a, b)`) before this pass, so an operator overload is specialized for
81 // an instantiated generic struct only when its mangled name appears here — an instance
82 // whose type argument never has the operator applied is not emitted with a body that
83 // would fail C compilation.
84 used_struct_operator_fns map[string]bool
85 // active_generic_params holds the generic parameter names of the decl currently
86 // being specialized/rewritten, in the same order as the inferred type `args`.
87 // It lets type-text substitution map placeholders by name (so non-canonical
88 // params like `D`/`F` resolve to the right arg) instead of by the positional
89 // `generic_param_index` heuristic (which collapses anything outside the T/U/C
90 // sequences to index 0). Empty for struct-generic specialization, which keeps
91 // the legacy positional behaviour.
92 active_generic_params []string
93 // escaping_amp_ptrs holds the names of pointer locals `p` declared as `p := &v`
94 // (v a value local) whose pointer escapes the function (is returned). V semantics
95 // auto-heap such a `v`; v3 otherwise takes the address of a stack local that dies
96 // on return. Recomputed per function (structural pre-pass in transform_fn_body),
97 // consumed when the `p := &v` decl is transformed (RHS rewritten to a heap copy).
98 escaping_amp_ptrs map[string]bool
99 // escaping_amp_sources holds the source locals `v` of such `p := &v` escapes — the
100 // values whose address leaves the frame. The local itself is moved to the heap at its
101 // declaration (its type becomes `&T`) so a mutation between `p := &v` and `return p`
102 // is observed by the caller; copying eagerly at the alias would return stale data.
103 escaping_amp_sources map[string]bool
104 // heaped_amp_locals records which of those sources were actually moved to the heap, so
105 // the `p := &v` alias emits `p = v` (the heap pointer) instead of a fresh memdup copy.
106 heaped_amp_locals map[string]bool
107 generic_fn_decls_cache map[string]GenericFnDecl
108 generic_fn_decls_ready bool
109}
110
111// AliasCache memoizes normalize_type_alias results. It lives on the heap so the
112// many `&Transformer` (read-only) query methods can populate it through the
113// pointer. normalize_type_alias is a pure function of (cur_module, typ) plus the
114// collected type maps (which never change during transform), so the cache is
115// keyed by typ and cleared whenever cur_module changes.
116struct AliasCache {
117mut:
118 module string
119 entries map[string]string
120}
121
122// StructInfo stores struct info metadata used by transform.
123pub struct StructInfo {
124pub:
125 name string
126 module string
127 is_params bool
128 fields []FieldInfo
129}
130
131// FieldInfo stores field info metadata used by transform.
132pub struct FieldInfo {
133pub:
134 name string
135 typ string
136 raw_typ string
137 default_expr flat.NodeId
138}
139
140// TupleBlockParts represents tuple block parts data used by transform.
141struct TupleBlockParts {
142 prefix []flat.NodeId
143 values []flat.NodeId
144}
145
146// StructFieldLookup represents struct field lookup data used by transform.
147struct StructFieldLookup {
148 info StructInfo
149 owner_type string
150}
151
152// VarTypeBinding represents var type binding data used by transform.
153struct VarTypeBinding {
154 name string
155 typ string
156}
157
158struct GenericFnDecl {
159 id flat.NodeId
160 node flat.Node
161 file string
162 module string
163 key string
164}
165
166// --- entry point ---
167
168// transform supports transform handling for transform.
169pub fn transform(mut a flat.FlatAst, tc &types.TypeChecker) {
170 transform_with_used(mut a, tc, map[string]bool{})
171}
172
173// transform_with_used transforms transform with used data for transform.
174pub fn transform_with_used(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) map[string]bool {
175 augmented, _ := transform_with_used_opt(mut a, tc, used_fns, false)
176 return augmented
177}
178
179// transform_with_used_opt is transform_with_used with an opt-in for parallel
180// function-body transform. It returns the augmented used-fn set and whether the
181// function bodies were actually transformed across threads (false when parallel
182// was not requested, the build lacks thread support, or there was too little work).
183pub fn transform_with_used_opt(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool, want_parallel bool) (map[string]bool, bool) {
184 mut augmented_used_fns := used_fns.clone()
185 mut t := new_transformer(mut a, tc, augmented_used_fns)
186 t.prepare()
187 if want_parallel {
188 // Transform roughly grows the node/children arrays by ~75%. Reserve that capacity
189 // up front so they don't double past it (the parsed AST already overshoots to the
190 // next power of two, wasting ~40MB, and each doubling briefly holds both the old and
191 // new arrays — the dominant peak-RSS contributor under -gc none). The serial path is
192 // latency-sensitive and does not clone worker ASTs, so let it grow naturally.
193 reserve_nodes := a.nodes.len * 7 / 4 - a.nodes.cap
194 if reserve_nodes > 0 {
195 unsafe { a.nodes.grow_cap(reserve_nodes) }
196 }
197 reserve_children := a.children.len * 7 / 4 - a.children.cap
198 if reserve_children > 0 {
199 unsafe { a.children.grow_cap(reserve_children) }
200 }
201 }
202 was_parallel := t.transform_all_dispatch(want_parallel)
203 return augmented_used_fns, was_parallel
204}
205
206pub fn monomorphize_with_used(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) map[string]bool {
207 mut augmented_used_fns := used_fns.clone()
208 mut t := new_transformer(mut a, tc, augmented_used_fns)
209 t.prepare()
210 for name in t.monomorphize_pass() {
211 augmented_used_fns[name] = true
212 augmented_used_fns[c_name(name)] = true
213 t.used_fns[name] = true
214 t.used_fns[c_name(name)] = true
215 }
216 t.materialize_generic_structs()
217 return augmented_used_fns
218}
219
220fn new_transformer(mut a flat.FlatAst, tc &types.TypeChecker, used_fns map[string]bool) Transformer {
221 return Transformer{
222 a: a
223 tc: unsafe { tc }
224 pointer_value_lvalues: map[string]bool{}
225 escaping_amp_ptrs: map[string]bool{}
226 escaping_amp_sources: map[string]bool{}
227 heaped_amp_locals: map[string]bool{}
228 used_fns: used_fns.clone()
229 }
230}
231
232fn (mut t Transformer) prepare() {
233 t.collect_types()
234 t.collect_const_suffixes()
235 t.collect_alias_methods()
236 // Enable the alias cache only now that the type maps are fully populated.
237 // During collection those maps are incomplete, so caching there would poison
238 // entries with results computed against a partial view.
239 t.alias_cache = &AliasCache{}
240 t.sum_cache = &AliasCache{}
241}
242
243// reset_var_types updates reset var types state for transform.
244fn (mut t Transformer) reset_var_types() {
245 t.var_types.clear()
246}
247
248// set_var_type updates set var type state for transform.
249fn (mut t Transformer) set_var_type(name string, typ string) {
250 if name.len == 0 {
251 return
252 }
253 for i, binding in t.var_types {
254 if binding.name == name {
255 t.var_types[i] = VarTypeBinding{
256 name: name
257 typ: typ
258 }
259 return
260 }
261 }
262 t.var_types << VarTypeBinding{
263 name: name
264 typ: typ
265 }
266}
267
268// unset_var_type supports unset var type handling for Transformer.
269fn (mut t Transformer) unset_var_type(name string) {
270 for i, binding in t.var_types {
271 if binding.name == name {
272 t.var_types.delete(i)
273 return
274 }
275 }
276}
277
278// var_type supports var type handling for Transformer.
279fn (t &Transformer) var_type(name string) string {
280 for binding in t.var_types {
281 if binding.name == name {
282 return binding.typ
283 }
284 }
285 return ''
286}
287
288// --- type collection ---
289
290// collect_types updates collect types state for transform.
291fn (mut t Transformer) collect_types() {
292 mut cur_mod := ''
293 for node in t.a.nodes {
294 match node.kind {
295 .module_decl {
296 cur_mod = node.value
297 }
298 .struct_decl {
299 owner_type := if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
300 '${cur_mod}.${node.value}'
301 } else {
302 node.value
303 }
304 mut fields := []FieldInfo{}
305 for i in 0 .. node.children_count {
306 f := t.a.child_node(&node, i)
307 if f.kind != .field_decl {
308 continue
309 }
310 default_expr := if f.children_count > 0 {
311 t.a.child(f, 0)
312 } else {
313 flat.empty_node
314 }
315 fields << FieldInfo{
316 name: f.value
317 typ: t.normalize_field_type(f.typ, owner_type)
318 raw_typ: f.typ
319 default_expr: default_expr
320 }
321 }
322 info := StructInfo{
323 name: node.value
324 module: cur_mod
325 is_params: node.typ.contains('params')
326 fields: fields
327 }
328 t.structs[node.value] = info
329 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
330 qname := '${cur_mod}.${node.value}'
331 t.structs[qname] = info
332 if node.value !in t.qualified_types {
333 t.qualified_types[node.value] = qname
334 }
335 }
336 for f in fields {
337 t.add_unique_field_type(f.name, f.typ)
338 }
339 }
340 .type_decl {
341 if node.children_count > 0 {
342 mut variants := []string{}
343 for i in 0 .. node.children_count {
344 v := t.a.child_node(&node, i)
345 variants << t.normalize_sum_variant_type(v.value, cur_mod)
346 }
347 t.sum_types[node.value] = variants
348 for variant in variants {
349 t.add_sum_variant_parent(variant, node.value)
350 }
351 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
352 qname := '${cur_mod}.${node.value}'
353 t.sum_types[qname] = variants
354 if node.value !in t.qualified_types {
355 t.qualified_types[node.value] = qname
356 }
357 for variant in variants {
358 t.add_sum_variant_parent(variant, qname)
359 }
360 }
361 }
362 }
363 .enum_decl {
364 mut field_names := []string{}
365 for i in 0 .. node.children_count {
366 f := t.a.child_node(&node, i)
367 if f.kind == .enum_field {
368 field_names << f.value
369 }
370 }
371 t.enum_types[node.value] = field_names
372 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
373 t.enum_types['${cur_mod}.${node.value}'] = field_names
374 }
375 }
376 .global_decl {
377 for i in 0 .. node.children_count {
378 f := t.a.child_node(&node, i)
379 mut typ := t.normalize_type_in_module(f.typ, cur_mod)
380 if typ.len == 0 && f.children_count > 0 {
381 typ = t.normalize_type_in_module(t.node_type(t.a.child(f, 0)), cur_mod)
382 }
383 t.globals[f.value] = typ
384 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
385 t.globals['${cur_mod}.${f.value}'] = typ
386 }
387 }
388 }
389 .fn_decl {
390 if node.typ.len > 0 {
391 ret_typ := t.normalize_type_in_module(node.typ, cur_mod)
392 if cur_mod.len > 0 && cur_mod != 'main' && cur_mod != 'builtin' {
393 qname := '${cur_mod}.${node.value}'
394 t.fn_ret_types[qname] = ret_typ
395 qlowered := c_name(qname)
396 if qlowered != qname {
397 t.fn_ret_types[qlowered] = ret_typ
398 }
399 } else {
400 t.fn_ret_types[node.value] = ret_typ
401 lowered := c_name(node.value)
402 if lowered != node.value {
403 t.fn_ret_types[lowered] = ret_typ
404 }
405 }
406 }
407 }
408 .c_fn_decl {
409 if node.typ.len > 0 {
410 ret_typ := t.normalize_type_in_module(node.typ, cur_mod)
411 t.fn_ret_types[node.value] = ret_typ
412 if node.value.starts_with('C.') {
413 t.fn_ret_types[node.value[2..]] = ret_typ
414 } else {
415 t.fn_ret_types['C.${node.value}'] = ret_typ
416 }
417 }
418 }
419 else {}
420 }
421 }
422}
423
424// add_sum_variant_parent updates add sum variant parent state for Transformer.
425fn (mut t Transformer) add_sum_variant_parent(variant string, sum_name string) {
426 if variant.len == 0 || sum_name.len == 0 {
427 return
428 }
429 field_name := t.sum_field_name(variant)
430 if field_name.contains('__') && field_name !in t.sum_variant_fields {
431 t.sum_variant_fields[field_name] = variant
432 }
433 t.add_sum_variant_parent_key(variant, sum_name)
434 if variant.contains('.') {
435 t.add_sum_variant_parent_key(variant.all_after_last('.'), sum_name)
436 }
437}
438
439// add_sum_variant_parent_key updates add sum variant parent key state for Transformer.
440fn (mut t Transformer) add_sum_variant_parent_key(key string, sum_name string) {
441 mut parents := t.sum_variant_parents[key] or { []string{} }
442 if sum_name !in parents {
443 parents << sum_name
444 t.sum_variant_parents[key] = parents
445 }
446}
447
448// add_unique_field_type updates add unique field type state for Transformer.
449fn (mut t Transformer) add_unique_field_type(name string, typ string) {
450 if name.len == 0 || typ.len == 0 {
451 return
452 }
453 if existing := t.unique_fields[name] {
454 if existing != typ {
455 t.unique_fields[name] = ''
456 }
457 } else {
458 t.unique_fields[name] = typ
459 }
460}
461
462// collect_const_suffixes updates collect const suffixes state for transform.
463fn (mut t Transformer) collect_const_suffixes() {
464 if isnil(t.tc) {
465 return
466 }
467 // Register every dot-delimited suffix of each const key so that both
468 // unqualified (`foo`) and partially-qualified (`mod.foo`) lookups resolve
469 // in O(1) via const_type_key, instead of scanning all consts per ident.
470 for key, _ in t.tc.const_types {
471 if !key.contains('.') {
472 t.add_const_suffix(key, key)
473 continue
474 }
475 mut i := 0
476 for i < key.len {
477 if key[i] == `.` {
478 t.add_const_suffix(key[i + 1..], key)
479 }
480 i++
481 }
482 }
483}
484
485// add_const_suffix updates add const suffix state for Transformer.
486fn (mut t Transformer) add_const_suffix(suffix string, key string) {
487 if existing := t.const_suffixes[suffix] {
488 if existing != key {
489 t.const_suffixes[suffix] = ''
490 }
491 } else {
492 t.const_suffixes[suffix] = key
493 }
494}
495
496// collect_alias_methods converts collect alias methods data for transform.
497fn (mut t Transformer) collect_alias_methods() {
498 if isnil(t.tc) {
499 return
500 }
501 for name, params in t.tc.fn_param_types {
502 if params.len == 0 || !name.contains('.') {
503 continue
504 }
505 receiver_name := name.all_before_last('.')
506 if receiver_name.len == 0 || receiver_name !in t.tc.type_aliases {
507 continue
508 }
509 method := name.all_after_last('.')
510 param_name := params[0].name()
511 clean_alias := if param_name.starts_with('&') { param_name[1..] } else { param_name }
512 alias_target := t.normalize_type_alias(clean_alias)
513 if alias_target.len == 0 {
514 continue
515 }
516 key := '${alias_target}.${method}'
517 if key !in t.alias_methods {
518 t.alias_methods[key] = name
519 }
520 }
521}
522
523// normalize_sum_variant_type transforms normalize sum variant type data for transform.
524fn (t &Transformer) normalize_sum_variant_type(typ string, mod string) string {
525 clean := typ.trim_space()
526 if clean.len == 0 {
527 return clean
528 }
529 if clean.starts_with('&') {
530 return '&' + t.normalize_sum_variant_type(clean[1..], mod)
531 }
532 if clean.starts_with('mut ') {
533 return '&' + t.normalize_sum_variant_type(clean[4..], mod)
534 }
535 if clean.starts_with('?') {
536 return '?' + t.normalize_sum_variant_type(clean[1..], mod)
537 }
538 if clean.starts_with('!') {
539 return '!' + t.normalize_sum_variant_type(clean[1..], mod)
540 }
541 if clean.starts_with('[]') {
542 return '[]' + t.normalize_sum_variant_type(clean[2..], mod)
543 }
544 if clean.starts_with('map[') {
545 bracket_end := clean.index(']') or { return clean }
546 key := t.normalize_sum_variant_type(clean[4..bracket_end], mod)
547 value := t.normalize_sum_variant_type(clean[bracket_end + 1..], mod)
548 return 'map[${key}]${value}'
549 }
550 if clean.starts_with('[') {
551 bracket_end := clean.index(']') or { return clean }
552 return clean[..bracket_end + 1] + t.normalize_sum_variant_type(clean[bracket_end +
553 1..], mod)
554 }
555 if clean.contains('.') || mod.len == 0 || mod == 'main' || mod == 'builtin'
556 || types.is_builtin_type_name(clean) {
557 return clean
558 }
559 return '${mod}.${clean}'
560}
561
562// --- main transform pass ---
563
564// transform_all transforms transform all data for transform.
565fn (mut t Transformer) transform_all() {
566 has_entry_main := t.has_entry_main()
567 node_count := t.a.nodes.len
568 for i in 0 .. node_count {
569 node := t.a.nodes[i]
570 kind_id := node_kind_id(node)
571 if kind_id == 77 {
572 t.cur_file = node.value
573 }
574 if kind_id == 73 {
575 t.cur_module = node.value
576 }
577 if kind_id == 61 {
578 if !t.should_transform_fn(node) {
579 continue
580 }
581 t.transform_fn_body(i)
582 } else if kind_id == 65 {
583 t.transform_const_decl(node)
584 } else if kind_id == 64 {
585 t.transform_global_decl(node)
586 }
587 }
588 if !has_entry_main {
589 t.transform_top_level_user_stmts()
590 }
591}
592
593fn (t &Transformer) has_entry_main() bool {
594 mut cur_module := ''
595 for node in t.a.nodes {
596 kind_id := node_kind_id(node)
597 if kind_id == 77 {
598 cur_module = ''
599 continue
600 }
601 if kind_id == 73 {
602 cur_module = node.value
603 continue
604 }
605 if kind_id == 61 && node.value == 'main' && (cur_module.len == 0 || cur_module == 'main') {
606 return true
607 }
608 }
609 return false
610}
611
612fn (mut t Transformer) transform_top_level_user_stmts() {
613 node_count := t.a.nodes.len
614 for file_idx in 0 .. node_count {
615 file_node := t.a.nodes[file_idx]
616 if !t.should_transform_top_level_file(file_idx, file_node) {
617 continue
618 }
619 t.transform_top_level_file(file_idx, file_node)
620 }
621}
622
623fn (t &Transformer) should_transform_top_level_file(file_idx int, file_node flat.Node) bool {
624 if file_idx < t.a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
625 return false
626 }
627 module_name := t.file_module_name(file_node)
628 return module_name.len == 0 || module_name == 'main'
629}
630
631fn (t &Transformer) file_module_name(file_node flat.Node) string {
632 for i in 0 .. file_node.children_count {
633 child := t.a.child_node(&file_node, i)
634 if child.kind == .module_decl {
635 return child.value
636 }
637 }
638 return ''
639}
640
641fn transform_is_top_level_stmt(node flat.Node) bool {
642 return match node.kind {
643 .expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
644 .for_in_stmt, .if_expr, .match_stmt, .assert_stmt, .defer_stmt, .block {
645 true
646 }
647 else {
648 false
649 }
650 }
651}
652
653fn (mut t Transformer) transform_top_level_file(file_idx int, file_node flat.Node) {
654 old_file := t.cur_file
655 old_module := t.cur_module
656 old_fn_name := t.cur_fn_name
657 old_fn_ret_type := t.cur_fn_ret_type
658 old_var_types := t.var_types.clone()
659 old_smartcast_stack := t.smartcast_stack.clone()
660 old_pending_stmts := t.pending_stmts.clone()
661 module_name := t.file_module_name(file_node)
662 t.cur_file = file_node.value
663 t.cur_module = if module_name.len == 0 { 'main' } else { module_name }
664 t.cur_fn_name = 'main'
665 t.cur_fn_ret_type = 'void'
666 t.reset_var_types()
667 t.smartcast_stack.clear()
668 t.pending_stmts.clear()
669 mut new_children := []flat.NodeId{cap: int(file_node.children_count)}
670 mut pending_stmts := []flat.NodeId{}
671 for i in 0 .. file_node.children_count {
672 child_id := t.a.child(&file_node, i)
673 if int(child_id) >= t.a.user_code_start {
674 child := t.a.nodes[int(child_id)]
675 if transform_is_top_level_stmt(child) {
676 pending_stmts << child_id
677 continue
678 }
679 }
680 t.append_transformed_top_level_stmts(mut new_children, mut pending_stmts)
681 new_children << child_id
682 }
683 t.append_transformed_top_level_stmts(mut new_children, mut pending_stmts)
684 start := t.a.children.len
685 for child_id in new_children {
686 t.a.children << child_id
687 }
688 t.a.nodes[file_idx] = flat.Node{
689 kind: .file
690 kind_id: file_node.kind_id
691 op: file_node.op
692 children_start: start
693 children_count: flat.child_count(new_children.len)
694 pos: file_node.pos
695 value: file_node.value
696 typ: file_node.typ
697 generic_params: file_node.generic_params
698 }
699 t.cur_file = old_file
700 t.cur_module = old_module
701 t.cur_fn_name = old_fn_name
702 t.cur_fn_ret_type = old_fn_ret_type
703 t.var_types = old_var_types
704 t.smartcast_stack = old_smartcast_stack
705 t.pending_stmts = old_pending_stmts
706}
707
708fn (mut t Transformer) append_transformed_top_level_stmts(mut out []flat.NodeId, mut pending []flat.NodeId) {
709 if pending.len == 0 {
710 return
711 }
712 transformed := t.transform_stmts(pending)
713 out << transformed
714 pending.clear()
715}
716
717// FnWorkItem identifies one top-level function body to transform, together with
718// the file/module context active at its declaration and a rough cost estimate
719// (subtree node count) used to balance work across parallel workers.
720struct FnWorkItem {
721 fn_idx int
722 file string
723 module string
724 cost int
725}
726
727// transform_all_dispatch runs the main transform pass either serially (the
728// original single-threaded walk) or, when `want_parallel` is set and there is
729// enough work, with closure-free function bodies transformed across threads.
730// Returns whether function bodies were actually transformed in parallel.
731fn (mut t Transformer) transform_all_dispatch(want_parallel bool) bool {
732 if !want_parallel {
733 t.transform_all()
734 return false
735 }
736 has_entry_main := t.has_entry_main()
737 // Serial phase: transform consts/globals and every function whose body
738 // contains a function literal (the only construct that lifts new top-level
739 // declarations and mutates the shared TypeChecker). Collect the remaining,
740 // closure-free functions as parallelizable work items.
741 has_fn_literals := t.has_fn_literal_nodes()
742 pure_items := t.transform_serial_then_collect_pure(has_fn_literals)
743 base_nodes := t.a.nodes.len
744 base_children := t.a.children.len
745 was_parallel := t.run_parallel_transform(pure_items, base_nodes, base_children)
746 if !has_entry_main {
747 t.transform_top_level_user_stmts()
748 }
749 return was_parallel
750}
751
752fn (t &Transformer) has_fn_literal_nodes() bool {
753 for node in t.a.nodes {
754 if node.kind == .fn_literal || node.kind == .lambda_expr {
755 return true
756 }
757 }
758 return false
759}
760
761// transform_serial_then_collect_pure walks the top level once: it transforms
762// const/global declarations and closure-bearing functions in place (serially),
763// and returns work items for the closure-free functions left to transform.
764fn (mut t Transformer) transform_serial_then_collect_pure(scan_fn_literals bool) []FnWorkItem {
765 mut pure := []FnWorkItem{}
766 original_len := t.a.nodes.len
767 for i in 0 .. original_len {
768 node := t.a.nodes[i]
769 kind_id := node_kind_id(node)
770 if kind_id == 77 {
771 t.cur_file = node.value
772 } else if kind_id == 73 {
773 t.cur_module = node.value
774 } else if kind_id == 61 {
775 if !t.should_transform_fn(node) {
776 continue
777 }
778 mut has_literal := false
779 mut cost := int(node.children_count) + 1
780 if scan_fn_literals {
781 has_literal, cost = t.fn_subtree_scan(i)
782 }
783 if has_literal {
784 t.transform_fn_body(i)
785 } else {
786 pure << FnWorkItem{
787 fn_idx: i
788 file: t.cur_file
789 module: t.cur_module
790 cost: cost
791 }
792 }
793 } else if kind_id == 65 {
794 t.transform_const_decl(node)
795 } else if kind_id == 64 {
796 t.transform_global_decl(node)
797 }
798 }
799 return pure
800}
801
802// fn_subtree_scan walks the subtree rooted at the function declaration `fn_idx`
803// and reports (whether it contains a function literal, total node count). The
804// function-literal flag routes closure-bearing functions to the serial path; the
805// count is a cheap per-function cost used to balance parallel workers.
806fn (t &Transformer) fn_subtree_scan(fn_idx int) (bool, int) {
807 mut has_literal := false
808 mut count := 0
809 mut stack := [flat.NodeId(fn_idx)]
810 for stack.len > 0 {
811 id := stack.pop()
812 idx := int(id)
813 if idx < 0 || idx >= t.a.nodes.len {
814 continue
815 }
816 node := t.a.nodes[idx]
817 count++
818 // 21 = fn_literal, 32 = lambda_expr: both can lower to a lifted closure.
819 kid := node_kind_id(node)
820 if kid == 21 || kid == 32 {
821 has_literal = true
822 }
823 for ci in 0 .. node.children_count {
824 child_id := t.a.children[node.children_start + ci]
825 if int(child_id) >= 0 {
826 stack << child_id
827 }
828 }
829 }
830 return has_literal, count
831}
832
833// transform_pure_items_serial transforms a list of closure-free function bodies
834// on this Transformer, in order. Used both as the serial fallback and as the
835// per-worker body in the parallel path.
836fn (mut t Transformer) transform_pure_items_serial(items []FnWorkItem) {
837 for it in items {
838 t.cur_file = it.file
839 t.cur_module = it.module
840 t.transform_fn_body(it.fn_idx)
841 }
842}
843
844// clone_ast_base produces a private FlatAst holding an independent copy of the
845// first base_nodes nodes / base_children children, so a worker can append its own
846// transformed nodes without racing the master or other workers. Read-only metadata
847// (disabled_fns) is shared.
848fn (t &Transformer) clone_ast_base(base_nodes int, base_children int) &flat.FlatAst {
849 return &flat.FlatAst{
850 nodes: t.a.nodes[0..base_nodes].clone()
851 children: t.a.children[0..base_children].clone()
852 user_code_start: t.a.user_code_start
853 disabled_fns: t.a.disabled_fns
854 }
855}
856
857// fork_worker builds a worker Transformer that shares this transformer's
858// read-only collected maps (structs, sum types, fn return types, used_fns, …)
859// and operates on its own cloned AST `ast` and forked TypeChecker `wtc`. All
860// per-function mutable state and memoization caches are reset/private so the
861// worker can run on its own thread.
862fn (t &Transformer) fork_worker(ast &flat.FlatAst, wtc &types.TypeChecker) &Transformer {
863 mut w := *t
864 w.a = ast
865 w.tc = wtc
866 w.alias_cache = &AliasCache{}
867 w.sum_cache = &AliasCache{}
868 w.generic_fn_decls_cache = map[string]GenericFnDecl{}
869 w.generic_fn_decls_ready = false
870 w.var_types = []VarTypeBinding{}
871 w.smartcast_stack = []SmartcastContext{}
872 w.pending_stmts = []flat.NodeId{}
873 w.pointer_value_lvalues = map[string]bool{}
874 w.escaping_amp_ptrs = map[string]bool{}
875 w.escaping_amp_sources = map[string]bool{}
876 w.heaped_amp_locals = map[string]bool{}
877 w.temp_counter = 0
878 w.cur_file = ''
879 w.cur_module = ''
880 w.cur_fn_name = ''
881 w.cur_fn_ret_type = ''
882 w.in_call_callee = false
883 w.in_const_init = false
884 w.in_return_expr = false
885 return &w
886}
887
888// merge_worker folds a finished worker's transformed output back into the master
889// AST. The worker created its new nodes/children at indices base_nodes/base_children
890// (matching the master at fork time); here they are appended to the master and every
891// reference to a worker-local new node or new children block is shifted by the
892// distance the block moved. `items` lists the function indices this worker owned, so
893// their rewritten top-level nodes can be copied into place.
894fn (mut t Transformer) merge_worker(w &Transformer, items []FnWorkItem, base_nodes int, base_children int) {
895 node_shift := i32(t.a.nodes.len - base_nodes)
896 child_shift := i32(t.a.children.len - base_children)
897 // New children: relocate references to worker-local new nodes.
898 for k in base_children .. w.a.children.len {
899 cid := w.a.children[k]
900 if int(cid) >= base_nodes {
901 t.a.children << flat.NodeId(int(cid) + int(node_shift))
902 } else {
903 t.a.children << cid
904 }
905 }
906 // New nodes: relocate children_start that points into the new children block.
907 for k in base_nodes .. w.a.nodes.len {
908 n := w.a.nodes[k]
909 if n.children_start >= base_children {
910 t.a.nodes << n.with_shifted_children(child_shift)
911 } else {
912 t.a.nodes << n
913 }
914 }
915 // Rewritten top-level function nodes keep their original index in the master.
916 for it in items {
917 n := w.a.nodes[it.fn_idx]
918 if n.children_start >= base_children {
919 t.a.nodes[it.fn_idx] = n.with_shifted_children(child_shift)
920 } else {
921 t.a.nodes[it.fn_idx] = n
922 }
923 }
924}
925
926// split_work_items distributes items across `n` buckets using greedy
927// least-loaded-by-cost assignment, so heavy functions are spread evenly. The
928// assignment is deterministic for a given input (required for reproducible builds).
929fn split_work_items(items []FnWorkItem, n int) [][]FnWorkItem {
930 mut buckets := [][]FnWorkItem{len: n, init: []FnWorkItem{}}
931 mut loads := []i64{len: n}
932 for it in items {
933 mut best := 0
934 for b in 1 .. n {
935 if loads[b] < loads[best] {
936 best = b
937 }
938 }
939 buckets[best] << it
940 loads[best] += i64(it.cost) + 1
941 }
942 return buckets
943}
944
945// should_transform_fn reports whether should transform fn applies in transform.
946fn (t &Transformer) should_transform_fn(node flat.Node) bool {
947 if t.used_fns.len == 0 {
948 return true
949 }
950 if node.value in t.used_fns {
951 return true
952 }
953 qname := transform_qualified_fn_name(t.cur_module, node.value)
954 if qname in t.used_fns {
955 return true
956 }
957 cname := c_name(qname)
958 if cname != qname && cname in t.used_fns {
959 return true
960 }
961 return false
962}
963
964// transform_qualified_fn_name transforms transform qualified fn name data for transform.
965fn transform_qualified_fn_name(mod string, name string) string {
966 if mod.len == 0 || mod == 'main' || mod == 'builtin' {
967 return name
968 }
969 return '${mod}.${name}'
970}
971
972// transform_const_decl transforms the initializer expression of each const field
973// so that const-level lowering (e.g. string concatenation in the prelude's
974// embedded data tables) happens in the transformer rather than the backend.
975fn (mut t Transformer) transform_const_decl(node flat.Node) {
976 old_in_const_init := t.in_const_init
977 t.in_const_init = true
978 for ci in 0 .. node.children_count {
979 cf_id := t.a.child(&node, ci)
980 if int(cf_id) < 0 {
981 continue
982 }
983 cf := t.a.nodes[int(cf_id)]
984 if cf.kind == .const_field && cf.children_count >= 1 && cf.children_start >= 0 {
985 val_id := t.a.child(&cf, 0)
986 if int(val_id) < 0 {
987 continue
988 }
989 val := t.a.nodes[int(val_id)]
990 if block_val := t.const_block_value(val) {
991 new_val := t.transform_const_value(block_val)
992 t.a.children[cf.children_start] = new_val
993 } else if val.kind == .string_interp {
994 new_val := t.transform_const_string_interp(val_id, val)
995 t.a.children[cf.children_start] = new_val
996 } else if val.kind == .or_expr {
997 const_typ := t.const_field_type_name(cf)
998 new_val := t.transform_const_or_expr(val_id, val, const_typ)
999 t.a.children[cf.children_start] = new_val
1000 } else if val.kind == .struct_init || val.kind == .cast_expr || val.kind == .call {
1001 new_val := t.transform_expr(val_id)
1002 t.a.children[cf.children_start] = new_val
1003 } else if val.kind == .infix && val.children_count >= 2 {
1004 new_val := t.transform_expr(val_id)
1005 // Overwrite the field's value slot in place (each const_field owns
1006 // its own single-element child range, so this is safe).
1007 t.a.children[cf.children_start] = new_val
1008 }
1009 }
1010 }
1011 t.in_const_init = old_in_const_init
1012}
1013
1014// const_field_type_name supports const field type name handling for Transformer.
1015fn (t &Transformer) const_field_type_name(field flat.Node) string {
1016 if field.value.len > 0 {
1017 if t.cur_module.len > 0 {
1018 if typ := t.const_type_name('${t.cur_module}.${field.value}') {
1019 return typ
1020 }
1021 }
1022 if typ := t.const_type_name(field.value) {
1023 return typ
1024 }
1025 }
1026 return field.typ
1027}
1028
1029// transform_const_or_expr transforms transform const or expr data for transform.
1030fn (mut t Transformer) transform_const_or_expr(_id flat.NodeId, node flat.Node, const_typ string) flat.NodeId {
1031 if node.children_count < 2 {
1032 return _id
1033 }
1034 mut children := []flat.NodeId{cap: int(node.children_count)}
1035 for i in 0 .. node.children_count {
1036 child_id := t.a.child(&node, i)
1037 if i == 0 {
1038 children << t.transform_expr(child_id)
1039 } else {
1040 children << child_id
1041 }
1042 }
1043 start := t.a.children.len
1044 for child in children {
1045 t.a.children << child
1046 }
1047 return t.a.add_node(flat.Node{
1048 kind: .or_expr
1049 op: node.op
1050 children_start: start
1051 children_count: node.children_count
1052 pos: node.pos
1053 value: node.value
1054 typ: if const_typ.len > 0 { const_typ } else { node.typ }
1055 })
1056}
1057
1058// transform_global_decl transforms transform global decl data for transform.
1059fn (mut t Transformer) transform_global_decl(node flat.Node) {
1060 for ci in 0 .. node.children_count {
1061 gf_id := t.a.child(&node, ci)
1062 if int(gf_id) < 0 {
1063 continue
1064 }
1065 gf := t.a.nodes[int(gf_id)]
1066 if gf.kind == .field_decl && gf.children_count >= 1 && gf.children_start >= 0 {
1067 val_id := t.a.child(&gf, 0)
1068 if int(val_id) < 0 {
1069 continue
1070 }
1071 val := t.a.nodes[int(val_id)]
1072 if preserved := t.transform_global_amp_initializer(val_id, val) {
1073 t.a.children[gf.children_start] = preserved
1074 continue
1075 }
1076 old_pending := t.pending_stmts.clone()
1077 t.pending_stmts.clear()
1078 new_val := t.transform_expr(val_id)
1079 has_pending := t.pending_stmts.len > 0
1080 t.pending_stmts.clear()
1081 t.pending_stmts = old_pending
1082 if !has_pending {
1083 t.a.children[gf.children_start] = new_val
1084 }
1085 }
1086 }
1087}
1088
1089// transform_global_amp_initializer transforms transform global amp initializer data for transform.
1090fn (mut t Transformer) transform_global_amp_initializer(val_id flat.NodeId, val flat.Node) ?flat.NodeId {
1091 if val.kind != .prefix || val.op != .amp || val.children_count != 1 {
1092 return none
1093 }
1094 child_id := t.a.child(&val, 0)
1095 child := t.a.nodes[int(child_id)]
1096 if child.kind == .assoc {
1097 return val_id
1098 }
1099 old_pending := t.pending_stmts.clone()
1100 t.pending_stmts.clear()
1101 mut result := flat.empty_node
1102 if child.kind == .struct_init {
1103 if preserved := t.transform_amp_struct_init_for_type(val_id, val, val.typ) {
1104 result = preserved
1105 }
1106 } else if child.kind == .cast_expr {
1107 if preserved := t.transform_global_amp_interface_cast(val, val.typ) {
1108 result = preserved
1109 }
1110 }
1111 has_pending := t.pending_stmts.len > 0
1112 t.pending_stmts.clear()
1113 t.pending_stmts = old_pending
1114 if has_pending || int(result) < 0 {
1115 return none
1116 }
1117 return result
1118}
1119
1120// transform_const_value transforms transform const value data for transform.
1121fn (mut t Transformer) transform_const_value(id flat.NodeId) flat.NodeId {
1122 if int(id) < 0 {
1123 return id
1124 }
1125 node := t.a.nodes[int(id)]
1126 if block_val := t.const_block_value(node) {
1127 return t.transform_const_value(block_val)
1128 }
1129 return t.transform_expr(id)
1130}
1131
1132// const_block_value supports const block value handling for Transformer.
1133fn (t &Transformer) const_block_value(node flat.Node) ?flat.NodeId {
1134 if node.kind != .block || node.children_count == 0 {
1135 return none
1136 }
1137 for i := int(node.children_count) - 1; i >= 0; i-- {
1138 stmt_id := t.a.child(&node, i)
1139 stmt := t.a.nodes[int(stmt_id)]
1140 if stmt.kind == .empty {
1141 continue
1142 }
1143 if stmt.kind == .expr_stmt && stmt.children_count == 1 {
1144 return t.a.child(&stmt, 0)
1145 }
1146 break
1147 }
1148 return none
1149}
1150
1151// transform_const_string_interp transforms transform const string interp data for transform.
1152fn (mut t Transformer) transform_const_string_interp(_id flat.NodeId, node flat.Node) flat.NodeId {
1153 if node.children_count == 0 {
1154 return t.make_string_literal('')
1155 }
1156 outer_pending := t.pending_stmts.clone()
1157 t.pending_stmts.clear()
1158 mut parts := []flat.NodeId{cap: int(node.children_count)}
1159 for i in 0 .. node.children_count {
1160 child_id := t.a.child(&node, i)
1161 transformed := t.transform_expr(child_id)
1162 mut typ := t.reliable_stringify_type(child_id)
1163 if typ.len == 0 {
1164 typ = t.node_type(transformed)
1165 }
1166 if typ.len == 0 {
1167 typ = 'string'
1168 }
1169 parts << t.wrap_string_conversion(transformed, typ)
1170 }
1171 mut expr := parts[0]
1172 for i in 1 .. parts.len {
1173 expr = t.make_call_typed('string__plus', arr2(expr, parts[i]), 'string')
1174 }
1175 mut stmts := []flat.NodeId{}
1176 t.drain_pending(mut stmts)
1177 t.pending_stmts = outer_pending
1178 if stmts.len == 0 {
1179 return expr
1180 }
1181 stmts << t.make_expr_stmt(expr)
1182 return t.make_block(stmts)
1183}
1184
1185// transform_fn_body transforms transform fn body data for transform.
1186// try_heap_escaping_amp reports whether a `p := &v` decl is an escaping address of
1187// a value local that must be heap-copied: `p` is in escaping_amp_ptrs (returned)
1188// and `v` resolves to a non-reference value type.
1189fn (t &Transformer) try_heap_escaping_amp(node flat.Node, rhs_id flat.NodeId) bool {
1190 lhs := t.a.nodes[int(t.a.child(&node, 0))]
1191 if lhs.kind != .ident || lhs.value !in t.escaping_amp_ptrs {
1192 return false
1193 }
1194 rhs := t.a.nodes[int(rhs_id)]
1195 if rhs.kind != .prefix || rhs.op != .amp || rhs.children_count == 0 {
1196 return false
1197 }
1198 amp_child := t.a.child(&rhs, 0)
1199 amp_node := t.a.nodes[int(amp_child)]
1200 if amp_node.kind != .ident {
1201 return false
1202 }
1203 // The source local was moved to the heap at its declaration: the alias is now just that
1204 // `&T` pointer (handled below), regardless of its rewritten pointer type.
1205 if amp_node.value in t.heaped_amp_locals {
1206 return true
1207 }
1208 local_type := t.node_type(amp_child)
1209 return local_type.len > 0 && !local_type.starts_with('&') && !local_type.starts_with('[]')
1210 && !local_type.starts_with('map[') && !local_type.starts_with('?')
1211 && !local_type.starts_with('!')
1212}
1213
1214// heap_escaping_amp_rhs rewrites `&v` into `(&T)memdup(&v, sizeof(T))`, a heap copy
1215// of the value local `v` so the escaping pointer outlives the stack frame. When `v` was
1216// itself moved to the heap at its declaration, the alias is simply that pointer — copying
1217// would resurrect the stale-mutation bug the move avoids.
1218fn (mut t Transformer) heap_escaping_amp_rhs(rhs_id flat.NodeId) flat.NodeId {
1219 rhs := t.a.nodes[int(rhs_id)]
1220 amp_child := t.a.child(&rhs, 0)
1221 amp_node := t.a.nodes[int(amp_child)]
1222 if amp_node.kind == .ident && amp_node.value in t.heaped_amp_locals {
1223 return t.transform_expr(amp_child)
1224 }
1225 local_type := t.node_type(amp_child)
1226 addr := t.make_prefix(.amp, t.transform_expr(amp_child))
1227 size := t.make_sizeof_type(local_type)
1228 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
1229 return t.make_cast('&${local_type}', dup, '&${local_type}')
1230}
1231
1232// heapable_value_type reports whether a local of this declared type can be moved to the heap
1233// as a `&T` — a plain value type, not an already-reference / container / optional type (those
1234// either carry their own indirection or are not addressable as a single `T`).
1235fn (t &Transformer) heapable_value_type(typ string) bool {
1236 return typ.len > 0 && !typ.starts_with('&') && !typ.starts_with('[]')
1237 && !typ.starts_with('map[') && !typ.starts_with('?') && !typ.starts_with('!')
1238 && !typ.starts_with('[') && typ != 'unknown' && typ != 'void'
1239}
1240
1241// heap_escaping_source_decl rewrites `mut v := <init>` (where `&v` escapes) into a heap
1242// allocation so `v` is a `&T` to a heap object. A struct literal becomes `&T{..}` (the cgen
1243// memdup's it); any other initializer is copied into a stack temp and memdup'd. Subsequent
1244// `v.field = ..` writes then mutate the heap object the returned pointer alias also sees.
1245fn (mut t Transformer) heap_escaping_source_decl(node flat.Node, var_name string, elem_typ string) []flat.NodeId {
1246 rhs_id := t.a.child(&node, 1)
1247 rhs := t.a.nodes[int(rhs_id)]
1248 ptr_typ := '&${elem_typ}'
1249 mut stmts := []flat.NodeId{}
1250 transformed_init := t.transform_expr(rhs_id)
1251 // Statements lifted out while transforming the initializer must precede the heap decl.
1252 t.drain_pending(mut stmts)
1253 mut heap_rhs := flat.NodeId(0)
1254 if rhs.kind == .struct_init {
1255 heap_rhs = t.make_prefix(.amp, transformed_init)
1256 } else {
1257 tmp := t.new_temp('esc')
1258 stmts << t.make_decl_assign_typed(tmp, transformed_init, elem_typ)
1259 addr := t.make_prefix(.amp, t.make_ident(tmp))
1260 size := t.make_sizeof_type(elem_typ)
1261 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
1262 heap_rhs = t.make_cast(ptr_typ, dup, ptr_typ)
1263 }
1264 t.heaped_amp_locals[var_name] = true
1265 // The local is now a `&T`, so its compound/postfix mutations (`v += 1`, `v++`) must store
1266 // through the pointer (`*v += 1`); mark it as a pointer-value lvalue so that lowering fires.
1267 t.pointer_value_lvalues[var_name] = true
1268 stmts << t.make_decl_assign_typed(var_name, heap_rhs, ptr_typ)
1269 return stmts
1270}
1271
1272// mark_escaping_amp_ptrs runs a structural pre-pass over a function body to find
1273// `p := &v` declarations whose pointer `p` is later returned. Such a `v` is a local
1274// value whose address escapes, so it must be heap-copied (V auto-heaps it); the
1275// names are recorded in `escaping_amp_ptrs` and consumed by the decl-assign
1276// transform. Purely structural (no type info needed here): the type check happens
1277// at rewrite time when `v`'s type is known.
1278fn (mut t Transformer) mark_escaping_amp_ptrs(body_ids []flat.NodeId) {
1279 t.reset_escaping_amp_state()
1280 mut amp_ptrs := map[string]bool{}
1281 mut amp_sources := map[string]string{} // pointer `p` -> source local `v`
1282 mut ptr_aliases := map[string]string{} // copy `q := p` -> aliased pointer `p`
1283 mut returned := map[string]bool{}
1284 for id in body_ids {
1285 t.scan_escape_pass(id, mut amp_ptrs, mut amp_sources, mut ptr_aliases, mut returned)
1286 }
1287 // A pointer may be returned through a copy (`p := &v; q := p; return q`): `q` is collected
1288 // as returned but `p` is not, so propagate "returned" backward along the `q := p` aliases
1289 // until a fixpoint. Then `p` (and its source `v`) is recognised as escaping below.
1290 for _ in 0 .. ptr_aliases.len {
1291 mut changed := false
1292 for q, p in ptr_aliases {
1293 if q in returned && p !in returned {
1294 returned[p] = true
1295 changed = true
1296 }
1297 }
1298 if !changed {
1299 break
1300 }
1301 }
1302 for name, _ in amp_ptrs {
1303 if name in returned {
1304 t.escaping_amp_ptrs[name] = true
1305 if src := amp_sources[name] {
1306 t.escaping_amp_sources[src] = true
1307 }
1308 }
1309 }
1310}
1311
1312fn (mut t Transformer) reset_escaping_amp_state() {
1313 t.escaping_amp_ptrs.clear()
1314 t.escaping_amp_sources.clear()
1315 t.heaped_amp_locals.clear()
1316 // Cleared per function: heaped locals add their names below (in heap_escaping_source_decl);
1317 // for-loop element vars set and restore their own entries within the loop body.
1318 t.pointer_value_lvalues.clear()
1319}
1320
1321// scan_escape_pass recursively collects, in a function-body subtree, (a) the LHS
1322// names of `p := &ident` declarations into `amp_ptrs` (and the source `ident` into
1323// `amp_sources[p]`), (b) plain pointer copies `q := p` into `ptr_aliases[q] = p`, and
1324// (c) every ident name appearing inside a return statement into `returned`.
1325fn (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) {
1326 if int(id) < 0 || int(id) >= t.a.nodes.len {
1327 return
1328 }
1329 node := t.a.nodes[int(id)]
1330 if node.kind == .decl_assign && node.children_count == 2 {
1331 lhs := t.a.nodes[int(t.a.child(&node, 0))]
1332 rhs := t.a.nodes[int(t.a.child(&node, 1))]
1333 if lhs.kind == .ident && lhs.value.len > 0 && rhs.kind == .prefix && rhs.op == .amp
1334 && rhs.children_count > 0 {
1335 amp_child := t.a.nodes[int(t.a.child(&rhs, 0))]
1336 if amp_child.kind == .ident {
1337 amp_ptrs[lhs.value] = true
1338 amp_sources[lhs.value] = amp_child.value
1339 }
1340 } else if lhs.kind == .ident && lhs.value.len > 0 && rhs.kind == .ident && rhs.value.len > 0 {
1341 // `q := p` aliases an existing pointer; recorded so a returned alias still marks the
1342 // underlying `p := &v` as escaping.
1343 ptr_aliases[lhs.value] = rhs.value
1344 }
1345 }
1346 if node.kind == .return_stmt {
1347 for i in 0 .. node.children_count {
1348 t.collect_return_escape_idents(t.a.child(&node, i), mut returned)
1349 }
1350 }
1351 for i in 0 .. node.children_count {
1352 t.scan_escape_pass(t.a.child(&node, i), mut amp_ptrs, mut amp_sources, mut ptr_aliases, mut
1353 returned)
1354 }
1355}
1356
1357// collect_return_escape_idents gathers the idents in a return-expression subtree that occupy an
1358// actual escape position — the returned value itself, or a member of a returned aggregate
1359// (struct/array/map literal, multi-return). It deliberately stops at operators that consume their
1360// operands into a fresh value: infix (`==`, `&&`, arithmetic, …), postfix, `is`/`in`, and any
1361// non-`&` prefix (deref `*p`, `!x`, `-x`). That way a pointer that is merely compared or
1362// dereferenced in the return expression — e.g. `return p == p && v == 1` — is not mistaken for a
1363// pointer that escapes, so its source local is not needlessly heap-moved (which would also make
1364// later non-pointer uses of that local read through an `int*`).
1365fn (mut t Transformer) collect_return_escape_idents(id flat.NodeId, mut names map[string]bool) {
1366 if int(id) < 0 || int(id) >= t.a.nodes.len {
1367 return
1368 }
1369 node := t.a.nodes[int(id)]
1370 match node.kind {
1371 .ident {
1372 if node.value.len > 0 {
1373 names[node.value] = true
1374 }
1375 return
1376 }
1377 .infix, .postfix, .is_expr, .in_expr {
1378 // These yield a new scalar/bool; their operands do not escape through the return.
1379 return
1380 }
1381 .prefix {
1382 // `&x` propagates an address (which may escape); any other prefix (`*x`, `!x`, `-x`)
1383 // produces a fresh value, so its operand does not escape.
1384 if node.op != .amp {
1385 return
1386 }
1387 }
1388 else {}
1389 }
1390
1391 for i in 0 .. node.children_count {
1392 t.collect_return_escape_idents(t.a.child(&node, i), mut names)
1393 }
1394}
1395
1396fn (mut t Transformer) transform_fn_body(fn_idx int) {
1397 fn_node := t.a.nodes[fn_idx]
1398 t.cur_fn_name = fn_node.value
1399 param_count := t.fn_body_param_count(fn_node)
1400 param_types := t.fn_body_param_types(fn_node, param_count)
1401 t.cur_fn_ret_type = t.fn_body_return_type(fn_node)
1402 t.reset_var_types()
1403 t.smartcast_stack.clear()
1404 // Collect param types
1405 mut param_idx := 0
1406 for i in 0 .. fn_node.children_count {
1407 child_id := t.a.children[fn_node.children_start + i]
1408 if int(child_id) < 0 {
1409 continue
1410 }
1411 child := t.a.nodes[int(child_id)]
1412 if node_kind_id(child) == 75 && child.value.len > 0 && child.typ.len > 0 {
1413 raw_typ := t.normalize_type_alias(child.typ)
1414 mut typ := if raw_typ.len > 0 {
1415 raw_typ
1416 } else if param_idx < param_types.len {
1417 t.normalize_type_alias(param_types[param_idx].name())
1418 } else {
1419 ''
1420 }
1421 if typ.starts_with('&') && raw_typ.len > 0 && !raw_typ.starts_with('&')
1422 && t.normalize_type_alias(typ[1..]) == raw_typ {
1423 typ = raw_typ
1424 }
1425 t.set_var_type(child.value, typ)
1426 param_idx++
1427 }
1428 }
1429 mut body_ids := []flat.NodeId{cap: int(fn_node.children_count)}
1430 for i in 0 .. fn_node.children_count {
1431 child_id := t.a.children[fn_node.children_start + i]
1432 if int(child_id) < 0 {
1433 continue
1434 }
1435 child := t.a.nodes[int(child_id)]
1436 if node_kind_id(child) != 75 {
1437 body_ids << child_id
1438 }
1439 }
1440 if t.cur_fn_ret_type == 'void' {
1441 t.reset_escaping_amp_state()
1442 } else {
1443 t.mark_escaping_amp_ptrs(body_ids)
1444 }
1445 new_body := t.transform_stmts(body_ids)
1446 // Rebuild function children: params then new body
1447 start := t.a.children.len
1448 for i in 0 .. fn_node.children_count {
1449 child_id := t.a.children[fn_node.children_start + i]
1450 if int(child_id) < 0 {
1451 continue
1452 }
1453 child := t.a.nodes[int(child_id)]
1454 if node_kind_id(child) == 75 {
1455 t.a.children << child_id
1456 }
1457 }
1458 for id in new_body {
1459 t.a.children << id
1460 }
1461 count := t.a.children.len - start
1462 t.a.nodes[fn_idx] = flat.Node{
1463 kind: .fn_decl
1464 kind_id: 61
1465 op: fn_node.op
1466 children_start: start
1467 children_count: flat.child_count(count)
1468 pos: fn_node.pos
1469 value: fn_node.value
1470 typ: fn_node.typ
1471 generic_params: fn_node.generic_params
1472 }
1473 t.smartcast_stack.clear()
1474}
1475
1476// fn_body_param_types supports fn body param types handling for Transformer.
1477fn (t &Transformer) fn_body_param_types(fn_node flat.Node, expected int) []types.Type {
1478 if isnil(t.tc) {
1479 return []types.Type{}
1480 }
1481 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1482 qname := '${t.cur_module}.${fn_node.value}'
1483 if params := t.fn_param_types_for_name(qname, expected) {
1484 return params
1485 }
1486 cqname := c_name(qname)
1487 if cqname != qname {
1488 if params := t.fn_param_types_for_name(cqname, expected) {
1489 return params
1490 }
1491 }
1492 }
1493 if params := t.fn_param_types_for_name(fn_node.value, expected) {
1494 return params
1495 }
1496 cname := c_name(fn_node.value)
1497 if cname != fn_node.value {
1498 if params := t.fn_param_types_for_name(cname, expected) {
1499 return params
1500 }
1501 }
1502 return []types.Type{}
1503}
1504
1505// fn_param_types_for_name supports fn param types for name handling for Transformer.
1506fn (t &Transformer) fn_param_types_for_name(name string, expected int) ?[]types.Type {
1507 params := t.tc.fn_param_types[name] or { return none }
1508 if expected != 0 && params.len != expected {
1509 return none
1510 }
1511 return params
1512}
1513
1514// fn_body_param_count supports fn body param count handling for Transformer.
1515fn (t &Transformer) fn_body_param_count(fn_node flat.Node) int {
1516 mut n := 0
1517 for i in 0 .. fn_node.children_count {
1518 child := t.a.child_node(&fn_node, i)
1519 if child.kind == .param {
1520 n++
1521 }
1522 }
1523 return n
1524}
1525
1526// fn_body_return_type supports fn body return type handling for Transformer.
1527fn (t &Transformer) fn_body_return_type(fn_node flat.Node) string {
1528 if !isnil(t.tc) {
1529 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
1530 qname := '${t.cur_module}.${fn_node.value}'
1531 if ret := t.fn_return_type_for_name(qname) {
1532 return ret
1533 }
1534 cqname := c_name(qname)
1535 if cqname != qname {
1536 if ret := t.fn_return_type_for_name(cqname) {
1537 return ret
1538 }
1539 }
1540 }
1541 if ret := t.fn_return_type_for_name(fn_node.value) {
1542 return ret
1543 }
1544 cname := c_name(fn_node.value)
1545 if cname != fn_node.value {
1546 if ret := t.fn_return_type_for_name(cname) {
1547 return ret
1548 }
1549 }
1550 }
1551 return t.normalize_type_alias(fn_node.typ)
1552}
1553
1554// fn_return_type_for_name supports fn return type for name handling for Transformer.
1555fn (t &Transformer) fn_return_type_for_name(name string) ?string {
1556 ret := t.tc.fn_ret_types[name] or { return none }
1557 return t.normalize_type_alias(ret.name())
1558}
1559
1560// --- statement list driver ---
1561
1562// transform_stmts transforms transform stmts data for transform.
1563pub fn (mut t Transformer) transform_stmts(ids []flat.NodeId) []flat.NodeId {
1564 mut result := []flat.NodeId{cap: ids.len}
1565 mut i := 0
1566 for i < ids.len {
1567 id := ids[i]
1568 if int(id) >= 0 && i + 1 < ids.len {
1569 node := t.a.nodes[int(id)]
1570 if node_kind_id(node) == 44 && node.children_count == 0 && t.cur_fn_ret_type.len > 0
1571 && t.cur_fn_ret_type != 'void' {
1572 next_id := ids[i + 1]
1573 next_node := t.a.nodes[int(next_id)]
1574 if node_kind_id(next_node) == 39 && next_node.children_count > 0 {
1575 expr_id := t.a.child(&next_node, 0)
1576 start := t.a.children.len
1577 t.a.children << expr_id
1578 merged_return := t.a.add_node(flat.Node{
1579 kind: .return_stmt
1580 children_start: start
1581 children_count: 1
1582 typ: node.typ
1583 })
1584 expanded := t.transform_stmt(merged_return)
1585 t.drain_pending(mut result)
1586 for eid in expanded {
1587 result << eid
1588 }
1589 i += 2
1590 continue
1591 }
1592 }
1593 if node.kind == .label_stmt {
1594 next_id := ids[i + 1]
1595 next_node := t.a.nodes[int(next_id)]
1596 if next_node.kind in [.for_stmt, .for_in_stmt] {
1597 expanded := t.transform_labeled_loop(node.value, next_id, next_node)
1598 t.drain_pending(mut result)
1599 for eid in expanded {
1600 result << eid
1601 }
1602 i += 2
1603 continue
1604 }
1605 }
1606 }
1607 expanded := t.transform_stmt(id)
1608 t.drain_pending(mut result)
1609 for eid in expanded {
1610 result << eid
1611 }
1612 i++
1613 }
1614 t.drain_pending(mut result)
1615 return result
1616}
1617
1618// transform_labeled_loop transforms transform labeled loop data for transform.
1619fn (mut t Transformer) transform_labeled_loop(label string, loop_id flat.NodeId, loop_node flat.Node) []flat.NodeId {
1620 if label.len == 0 {
1621 return t.transform_stmt(loop_id)
1622 }
1623 continue_label := '${label}_continue'
1624 break_label := '${label}_break'
1625 body_start := if loop_node.kind == .for_in_stmt { loop_node.value.int() } else { 3 }
1626 mut children := []flat.NodeId{cap: int(loop_node.children_count) + 1}
1627 for i in 0 .. loop_node.children_count {
1628 children << t.a.child(&loop_node, i)
1629 }
1630 if body_start <= children.len {
1631 children << t.a.add_val(.label_stmt, continue_label)
1632 }
1633 start := t.a.children.len
1634 for child in children {
1635 t.a.children << child
1636 }
1637 new_loop := t.a.add_node(flat.Node{
1638 kind: loop_node.kind
1639 op: loop_node.op
1640 children_start: start
1641 children_count: flat.child_count(children.len)
1642 pos: loop_node.pos
1643 value: loop_node.value
1644 typ: loop_node.typ
1645 })
1646 mut result := []flat.NodeId{}
1647 result << t.a.add_val(.label_stmt, label)
1648 result << t.transform_stmt(new_loop)
1649 result << t.a.add_val(.label_stmt, break_label)
1650 return result
1651}
1652
1653// transform_stmt transforms transform stmt data for transform.
1654pub fn (mut t Transformer) transform_stmt(id flat.NodeId) []flat.NodeId {
1655 if int(id) < 0 {
1656 return arr1(id)
1657 }
1658 node := t.a.nodes[int(id)]
1659 kind_id := node_kind_id(node)
1660 if kind_id == 44 {
1661 return t.transform_return_stmt(id, node)
1662 }
1663 if kind_id == 40 || kind_id == 42 || kind_id == 43 {
1664 return t.transform_assign_stmt(id, node)
1665 }
1666 if kind_id == 41 {
1667 return t.transform_decl_assign_stmt(id, node)
1668 }
1669 if kind_id == 39 {
1670 return t.transform_expr_stmt(id, node)
1671 }
1672 if kind_id == 46 {
1673 return t.transform_for_stmt(id, node)
1674 }
1675 if kind_id == 47 {
1676 return t.transform_for_in_stmt(id, node)
1677 }
1678 if kind_id == 45 {
1679 return t.transform_block_stmt(id, node)
1680 }
1681 if kind_id == 15 {
1682 return t.transform_if_stmt(id, node)
1683 }
1684 if kind_id == 50 {
1685 return arr1(t.lower_one_match(node))
1686 }
1687 if kind_id == 52 {
1688 return t.transform_defer_stmt(id, node)
1689 }
1690 if kind_id == 53 || kind_id == 56 {
1691 return t.transform_children_stmt(id, node)
1692 }
1693 match node.kind {
1694 .return_stmt {
1695 return t.transform_return_stmt(id, node)
1696 }
1697 .assign, .selector_assign, .index_assign {
1698 return t.transform_assign_stmt(id, node)
1699 }
1700 .decl_assign {
1701 return t.transform_decl_assign_stmt(id, node)
1702 }
1703 .expr_stmt {
1704 return t.transform_expr_stmt(id, node)
1705 }
1706 .for_stmt {
1707 return t.transform_for_stmt(id, node)
1708 }
1709 .for_in_stmt {
1710 return t.transform_for_in_stmt(id, node)
1711 }
1712 .block {
1713 return t.transform_block_stmt(id, node)
1714 }
1715 .if_expr {
1716 return t.transform_if_stmt(id, node)
1717 }
1718 .match_stmt {
1719 return arr1(t.lower_one_match(node))
1720 }
1721 .defer_stmt {
1722 return t.transform_defer_stmt(id, node)
1723 }
1724 .assert_stmt {
1725 return t.transform_children_stmt(id, node)
1726 }
1727 .select_stmt {
1728 return t.transform_children_stmt(id, node)
1729 }
1730 else {
1731 return arr1(id)
1732 }
1733 }
1734}
1735
1736// transform_expr transforms transform expr data for transform.
1737pub fn (mut t Transformer) transform_expr(id flat.NodeId) flat.NodeId {
1738 if int(id) < 0 {
1739 return id
1740 }
1741 node := t.a.nodes[int(id)]
1742 kind_id := node_kind_id(node)
1743 if kind_id == 8 {
1744 return t.transform_infix_expr(id, node)
1745 }
1746 if kind_id == 12 {
1747 return t.transform_call_expr(id, node)
1748 }
1749 if kind_id == 15 {
1750 return t.transform_if_expr(id, node)
1751 }
1752 if kind_id == 16 {
1753 return t.transform_struct_init(id, node)
1754 }
1755 if kind_id == 17 {
1756 return t.transform_field_init_expr(id, node)
1757 }
1758 if kind_id == 14 {
1759 return t.transform_index_expr(id, node)
1760 }
1761 if kind_id == 6 {
1762 return t.transform_string_interp(id, node)
1763 }
1764 if kind_id == 13 {
1765 return t.transform_selector_expr(id, node)
1766 }
1767 if kind_id == 22 {
1768 return t.transform_or_expr(id, node)
1769 }
1770 if kind_id == 24 {
1771 return t.transform_as_expr(id, node)
1772 }
1773 if kind_id == 9 {
1774 return t.transform_prefix_expr(id, node)
1775 }
1776 if kind_id == 11 {
1777 return t.transform_paren_expr(id, node)
1778 }
1779 if kind_id == 10 {
1780 return t.transform_postfix_expr(id, node)
1781 }
1782 if kind_id == 23 {
1783 return t.transform_cast_expr(id, node)
1784 }
1785 if kind_id == 18 {
1786 return t.transform_array_literal(id, node)
1787 }
1788 if kind_id == 19 {
1789 return t.transform_array_init_expr(id, node)
1790 }
1791 if kind_id == 20 {
1792 return t.transform_map_init(id, node)
1793 }
1794 if kind_id == 38 {
1795 return t.transform_in_expr(id, node)
1796 }
1797 if kind_id == 37 {
1798 return t.transform_is_expr(id, node)
1799 }
1800 if kind_id == 50 {
1801 return t.lower_one_match(node)
1802 }
1803 if kind_id == 45 {
1804 return t.transform_block_expr(id, node)
1805 }
1806 if kind_id == 31 {
1807 return t.transform_lock_expr(id, node)
1808 }
1809 if kind_id == 34 {
1810 return t.transform_typeof_expr(id, node)
1811 }
1812 if kind_id == 7 {
1813 return t.transform_ident_expr(id, node)
1814 }
1815 if kind_id == 26 {
1816 return t.transform_assoc_expr(id, node)
1817 }
1818 if kind_id == 21 {
1819 return t.lift_fn_literal(id, node)
1820 }
1821 if kind_id == 30 || kind_id == 35 || kind_id == 27 || kind_id == 56 || kind_id == 57 {
1822 return t.transform_children_expr(id, node)
1823 }
1824 if kind_id == 1 || kind_id == 2 || kind_id == 3 || kind_id == 4 || kind_id == 5 || kind_id == 28
1825 || kind_id == 29 || kind_id == 25 || kind_id == 33 || kind_id == 36 {
1826 return id
1827 }
1828 match node.kind {
1829 .infix {
1830 return t.transform_infix_expr(id, node)
1831 }
1832 .call {
1833 return t.transform_call_expr(id, node)
1834 }
1835 .if_expr {
1836 return t.transform_if_expr(id, node)
1837 }
1838 .struct_init {
1839 return t.transform_struct_init(id, node)
1840 }
1841 .field_init {
1842 return t.transform_field_init_expr(id, node)
1843 }
1844 .index {
1845 return t.transform_index_expr(id, node)
1846 }
1847 .string_interp {
1848 return t.transform_string_interp(id, node)
1849 }
1850 .selector {
1851 return t.transform_selector_expr(id, node)
1852 }
1853 .or_expr {
1854 return t.transform_or_expr(id, node)
1855 }
1856 .as_expr {
1857 return t.transform_as_expr(id, node)
1858 }
1859 .prefix {
1860 return t.transform_prefix_expr(id, node)
1861 }
1862 .paren {
1863 return t.transform_paren_expr(id, node)
1864 }
1865 .postfix {
1866 return t.transform_postfix_expr(id, node)
1867 }
1868 .cast_expr {
1869 return t.transform_cast_expr(id, node)
1870 }
1871 .array_literal {
1872 return t.transform_array_literal(id, node)
1873 }
1874 .array_init {
1875 return t.transform_array_init_expr(id, node)
1876 }
1877 .map_init {
1878 return t.transform_map_init(id, node)
1879 }
1880 .sql_expr {
1881 return t.transform_sql_expr(id, node)
1882 }
1883 .in_expr {
1884 return t.transform_in_expr(id, node)
1885 }
1886 .is_expr {
1887 return t.transform_is_expr(id, node)
1888 }
1889 .match_stmt {
1890 return t.lower_one_match(node)
1891 }
1892 .block {
1893 return t.transform_block_expr(id, node)
1894 }
1895 .lock_expr {
1896 return t.transform_lock_expr(id, node)
1897 }
1898 .typeof_expr {
1899 return t.transform_typeof_expr(id, node)
1900 }
1901 .ident {
1902 return t.transform_ident_expr(id, node)
1903 }
1904 .assoc {
1905 return t.transform_assoc_expr(id, node)
1906 }
1907 .fn_literal {
1908 return t.lift_fn_literal(id, node)
1909 }
1910 .lambda_expr, .spawn_expr, .dump_expr, .range, .select_stmt, .select_branch {
1911 return t.transform_children_expr(id, node)
1912 }
1913 .int_literal, .float_literal, .bool_literal, .char_literal, .string_literal, .nil_literal,
1914 .none_expr, .enum_val, .sizeof_expr, .offsetof_expr {
1915 // leaf/simple nodes - pass through unchanged
1916 return id
1917 }
1918 else {
1919 return id
1920 }
1921 }
1922}
1923
1924// transform_lvalue transforms transform lvalue data for transform.
1925pub fn (mut t Transformer) transform_lvalue(id flat.NodeId) flat.NodeId {
1926 if int(id) < 0 {
1927 return id
1928 }
1929 node := t.a.nodes[int(id)]
1930 match node.kind {
1931 .ident {
1932 return id
1933 }
1934 .selector {
1935 if node.children_count == 0 {
1936 return id
1937 }
1938 if t.selector_chain_has_sum_shared_field(id) {
1939 value := t.transform_selector_expr(id, node)
1940 mut value_type := t.node_type(id)
1941 if value_type.len == 0 {
1942 value_type = t.node_type(value)
1943 }
1944 return t.stable_transformed_expr_for_reuse(value, value_type, 'lvalue')
1945 }
1946 full_key := t.expr_key(id)
1947 if t.has_smartcast(full_key) {
1948 return t.transform_selector_expr(id, node)
1949 }
1950 base_id := t.a.child(&node, 0)
1951 base_key := t.expr_key(base_id)
1952 if t.has_smartcast(base_key) {
1953 return t.transform_selector_expr(id, node)
1954 }
1955 base := t.transform_lvalue(t.a.child(&node, 0))
1956 mut new_children := []flat.NodeId{cap: int(node.children_count)}
1957 new_children << base
1958 for i in 1 .. node.children_count {
1959 new_children << t.transform_expr(t.a.child(&node, i))
1960 }
1961 start := t.a.children.len
1962 for child in new_children {
1963 t.a.children << child
1964 }
1965 return t.a.add_node(flat.Node{
1966 kind: .selector
1967 op: node.op
1968 children_start: start
1969 children_count: flat.child_count(new_children.len)
1970 pos: node.pos
1971 value: node.value
1972 typ: node.typ
1973 })
1974 }
1975 .index {
1976 if node.children_count == 0 {
1977 return id
1978 }
1979 mut new_children := []flat.NodeId{cap: int(node.children_count)}
1980 new_children << t.transform_expr(t.a.child(&node, 0))
1981 for i in 1 .. node.children_count {
1982 new_children << t.transform_expr(t.a.child(&node, i))
1983 }
1984 start := t.a.children.len
1985 for child in new_children {
1986 t.a.children << child
1987 }
1988 return t.a.add_node(flat.Node{
1989 kind: .index
1990 op: node.op
1991 children_start: start
1992 children_count: flat.child_count(new_children.len)
1993 pos: node.pos
1994 value: node.value
1995 typ: node.typ
1996 })
1997 }
1998 .prefix {
1999 if node.op == .mul && node.children_count > 0 {
2000 child := t.transform_expr(t.a.child(&node, 0))
2001 start := t.a.children.len
2002 t.a.children << child
2003 return t.a.add_node(flat.Node{
2004 kind: .prefix
2005 op: node.op
2006 children_start: start
2007 children_count: 1
2008 pos: node.pos
2009 value: node.value
2010 typ: node.typ
2011 })
2012 }
2013 return t.transform_expr(id)
2014 }
2015 .paren {
2016 if node.children_count == 0 {
2017 return id
2018 }
2019 child := t.transform_lvalue(t.a.child(&node, 0))
2020 start := t.a.children.len
2021 t.a.children << child
2022 return t.a.add_node(flat.Node{
2023 kind: .paren
2024 op: node.op
2025 children_start: start
2026 children_count: 1
2027 pos: node.pos
2028 value: node.value
2029 typ: node.typ
2030 })
2031 }
2032 else {
2033 return t.transform_expr(id)
2034 }
2035 }
2036}
2037
2038// --- stmt handlers (skeleton - identity transforms with child recursion) ---
2039
2040// transform_return_stmt transforms transform return stmt data for transform.
2041fn (mut t Transformer) transform_return_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
2042 if node.children_count == 0 {
2043 return arr1(id)
2044 }
2045 if expanded := t.try_expand_return_if(id, node) {
2046 return expanded
2047 }
2048 if expanded := t.try_expand_return_match(id, node) {
2049 return expanded
2050 }
2051 if direct := t.try_return_direct_optional_expr(node) {
2052 return direct
2053 }
2054 if expanded := t.try_expand_return_optional_expr(node) {
2055 return expanded
2056 }
2057 if node.children_count == 1 {
2058 child_id := t.a.child(&node, 0)
2059 if t.is_optional_type_name(t.cur_fn_ret_type) && t.return_expr_is_err(child_id) {
2060 return t.with_pending_before(t.make_none_return_stmt())
2061 }
2062 }
2063 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2064 for i in 0 .. node.children_count {
2065 child_id := t.a.child(&node, i)
2066 if converted := t.fixed_array_return_value(child_id) {
2067 new_children << converted
2068 } else if copied := t.heap_copy_local_address_return(child_id) {
2069 new_children << copied
2070 } else if t.cur_fn_ret_type.len > 0 && t.cur_fn_ret_type !in t.sum_types
2071 && !t.is_optional_type_name(t.cur_fn_ret_type) {
2072 new_children << t.transform_expr_for_type(child_id, t.cur_fn_ret_type)
2073 } else {
2074 new_children << t.wrap_sum_return_expr(child_id)
2075 }
2076 }
2077 start := t.a.children.len
2078 for nc in new_children {
2079 t.a.children << nc
2080 }
2081 new_id := t.a.add_node(flat.Node{
2082 kind: .return_stmt
2083 op: node.op
2084 children_start: start
2085 children_count: node.children_count
2086 pos: node.pos
2087 value: node.value
2088 typ: node.typ
2089 })
2090 return t.with_pending_before(new_id)
2091}
2092
2093// heap_copy_local_address_return supports heap copy local address return handling for Transformer.
2094fn (mut t Transformer) heap_copy_local_address_return(child_id flat.NodeId) ?flat.NodeId {
2095 if !t.cur_fn_ret_type.starts_with('&') || int(child_id) < 0 {
2096 return none
2097 }
2098 node := t.a.nodes[int(child_id)]
2099 if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2100 return none
2101 }
2102 inner_id := t.a.child(&node, 0)
2103 inner := t.a.nodes[int(inner_id)]
2104 if inner.kind != .ident || inner.value.len == 0 {
2105 return none
2106 }
2107 local_type := t.var_type(inner.value)
2108 if local_type.len == 0 {
2109 return none
2110 }
2111 ret_base_type := t.cur_fn_ret_type[1..]
2112 if ret_base_type.len == 0 {
2113 return none
2114 }
2115 clean_local_type := t.normalize_type_alias(local_type)
2116 clean_ret_type := t.normalize_type_alias(ret_base_type)
2117 if clean_local_type != clean_ret_type && local_type != ret_base_type {
2118 return none
2119 }
2120 addr := t.make_prefix(.amp, t.make_ident(inner.value))
2121 size := t.make_sizeof_type(ret_base_type)
2122 dup := t.make_call_typed('memdup', arr2(addr, size), 'voidptr')
2123 return t.make_cast(t.cur_fn_ret_type, dup, t.cur_fn_ret_type)
2124}
2125
2126// fixed_array_return_value supports fixed array return value handling for Transformer.
2127fn (mut t Transformer) fixed_array_return_value(child_id flat.NodeId) ?flat.NodeId {
2128 mut ret_type := t.cur_fn_ret_type
2129 if t.is_optional_type_name(ret_type) {
2130 ret_type = t.optional_base_type(ret_type)
2131 }
2132 // A function whose declared return type is itself a fixed array keeps
2133 // fixed-array (by-value) semantics; the C backend returns it via a wrapper
2134 // struct. Only a *dynamic* array return needs a fixed→dynamic conversion of a
2135 // fixed-array return value.
2136 if t.is_fixed_array_type(ret_type) {
2137 return none
2138 }
2139 return t.fixed_array_value_to_dynamic(child_id, ret_type)
2140}
2141
2142// fixed_array_value_to_dynamic converts a fixed-array *value* (e.g. a fixed-array
2143// const or variable, not a literal — those have their own lowering) to a dynamic
2144// array when `target_type` is `[]T` with a matching element type. Returns none
2145// when no conversion is needed/possible.
2146fn (mut t Transformer) fixed_array_value_to_dynamic(value_id flat.NodeId, target_type string) ?flat.NodeId {
2147 array_type := target_type
2148 if !array_type.starts_with('[]') {
2149 return none
2150 }
2151 child_type := t.node_type(value_id)
2152 if !t.is_fixed_array_type(child_type) || fixed_array_elem_type(child_type) != array_type[2..] {
2153 return none
2154 }
2155 return t.fixed_array_value_to_array(value_id, child_type, array_type)
2156}
2157
2158// transform_assign_stmt transforms transform assign stmt data for transform.
2159fn (mut t Transformer) transform_assign_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
2160 if node.children_count == 0 {
2161 return arr1(id)
2162 }
2163 if expanded := t.try_expand_multi_return_assign(node) {
2164 return expanded
2165 }
2166 if expanded := t.try_expand_plain_multi_assign(node) {
2167 return expanded
2168 }
2169 if lowered := t.try_lower_sum_shared_field_assign(node) {
2170 return lowered
2171 }
2172 if lowered := t.try_lower_optional_selector_lvalue_assign(node) {
2173 return lowered
2174 }
2175 if lowered := t.try_lower_pointer_value_assign(node) {
2176 return lowered
2177 }
2178 if lowered := t.try_lower_map_index_assign(node) {
2179 return lowered
2180 }
2181 // string `s += x` on a plain ident -> `s = string__plus(s, x)` (only when detectable as string)
2182 if expanded := t.try_lower_string_compound_assign(id, node) {
2183 return expanded
2184 }
2185 if expanded := t.try_lower_struct_compound_assign(node) {
2186 return expanded
2187 }
2188 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2189 for i in 0 .. node.children_count {
2190 child_id := t.a.child(&node, i)
2191 if i % 2 == 0 {
2192 new_children << t.transform_lvalue(child_id)
2193 } else {
2194 lhs_id := t.a.child(&node, i - 1)
2195 lhs := t.a.nodes[int(lhs_id)]
2196 mut lhs_type := if lhs.kind in [.selector, .index] {
2197 t.lvalue_type(lhs_id)
2198 } else {
2199 t.original_expr_type(lhs_id)
2200 }
2201 if lhs_type.len == 0 {
2202 lhs_type = t.lvalue_type(lhs_id)
2203 }
2204 // A value local moved to the heap (its type became `&T`) is assigned by storing a
2205 // value through the pointer (cgen emits `*v = ...`), so coerce the RHS to the value
2206 // type `T`, not `&T`. Otherwise a heaped-local RHS (`v = w`, both `&T`) is copied as a
2207 // pointer — aliasing `w`'s object — instead of dereferenced to its value.
2208 if lhs.kind == .ident && lhs.value in t.heaped_amp_locals && lhs_type.starts_with('&') {
2209 lhs_type = lhs_type[1..]
2210 }
2211 sum_target := t.assignment_sum_target(lhs_id, child_id, lhs_type)
2212 if node.op == .assign && sum_target.len > 0 {
2213 new_children << t.wrap_sum_value(child_id, sum_target)
2214 } else {
2215 new_children << t.transform_expr_for_type(child_id, lhs_type)
2216 }
2217 }
2218 }
2219 start := t.a.children.len
2220 for nc in new_children {
2221 t.a.children << nc
2222 }
2223 new_id := t.a.add_node(flat.Node{
2224 kind: node.kind
2225 op: node.op
2226 children_start: start
2227 children_count: node.children_count
2228 pos: node.pos
2229 value: node.value
2230 typ: node.typ
2231 })
2232 if node.kind == .assign && node.op == .left_shift_assign {
2233 t.annotate_left_shift_assign(new_id)
2234 }
2235 return t.with_pending_before(new_id)
2236}
2237
2238fn (mut t Transformer) try_lower_optional_selector_lvalue_assign(node flat.Node) ?[]flat.NodeId {
2239 if node.kind != .selector_assign || node.children_count != 2 {
2240 return none
2241 }
2242 lhs_id := t.a.child(&node, 0)
2243 rhs_id := t.a.child(&node, 1)
2244 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2245 return none
2246 }
2247 lowered_lhs, guard_source, guard_body, guard_mode := t.lower_optional_selector_lvalue(lhs_id) or {
2248 return none
2249 }
2250 mut result := []flat.NodeId{}
2251 t.drain_pending(mut result)
2252 not_ok := t.make_prefix(.not, t.make_selector(guard_source, 'ok', 'bool'))
2253 guard_stmts := t.optional_selector_lvalue_guard_stmts(guard_body, guard_mode, guard_source)
2254 result << t.make_if(not_ok, t.make_block(guard_stmts), t.make_empty())
2255 lhs_type := t.lvalue_type(lhs_id)
2256 sum_target := t.assignment_sum_target(lhs_id, rhs_id, lhs_type)
2257 rhs := if node.op == .assign && sum_target.len > 0 {
2258 t.wrap_sum_value(rhs_id, sum_target)
2259 } else {
2260 t.transform_expr_for_type(rhs_id, lhs_type)
2261 }
2262 t.drain_pending(mut result)
2263 result << t.make_assign_op(lowered_lhs, rhs, node.op)
2264 return result
2265}
2266
2267fn (mut t Transformer) optional_selector_lvalue_guard_stmts(body_id flat.NodeId, mode string, guard_source flat.NodeId) []flat.NodeId {
2268 err_expr := t.make_selector(guard_source, 'err', 'IError')
2269 if mode == '!' || mode == '?' {
2270 if t.is_optional_type_name(t.cur_fn_ret_type) {
2271 return arr1(t.make_return(t.make_optional_none_with_err(t.cur_fn_ret_type, err_expr),
2272 t.cur_fn_ret_type))
2273 }
2274 return arr1(t.make_panic_stmt('option/result propagation failed'))
2275 }
2276 return t.lower_or_body_to_stmts_with_err_expr(body_id, '', '', mode, err_expr)
2277}
2278
2279fn (mut t Transformer) lower_optional_selector_lvalue(id flat.NodeId) ?(flat.NodeId, flat.NodeId, flat.NodeId, string) {
2280 if int(id) < 0 {
2281 return none
2282 }
2283 node := t.a.nodes[int(id)]
2284 if node.kind != .selector || node.children_count == 0 || node.value.len == 0 {
2285 return none
2286 }
2287 base_id := t.a.child(&node, 0)
2288 base := t.a.nodes[int(base_id)]
2289 if base.kind == .or_expr && base.children_count >= 2 {
2290 return t.lower_optional_selector_lvalue_from_or(id, node, base)
2291 }
2292 if base.kind == .paren && base.children_count > 0 {
2293 inner_id := t.a.child(&base, 0)
2294 inner := t.a.nodes[int(inner_id)]
2295 if inner.kind == .or_expr && inner.children_count >= 2 {
2296 return t.lower_optional_selector_lvalue_from_or(id, node, inner)
2297 }
2298 }
2299 lowered_base, guard_source, guard_body, guard_mode := t.lower_optional_selector_lvalue(base_id) or {
2300 return none
2301 }
2302 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2303 new_children << lowered_base
2304 for i in 1 .. node.children_count {
2305 new_children << t.transform_expr(t.a.child(&node, i))
2306 }
2307 start := t.a.children.len
2308 for child in new_children {
2309 t.a.children << child
2310 }
2311 lowered := t.a.add_node(flat.Node{
2312 kind: .selector
2313 op: node.op
2314 children_start: start
2315 children_count: flat.child_count(new_children.len)
2316 pos: node.pos
2317 value: node.value
2318 typ: node.typ
2319 })
2320 return lowered, guard_source, guard_body, guard_mode
2321}
2322
2323fn (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) {
2324 source_id := t.a.child(&base, 0)
2325 if !t.optional_selector_lvalue_source(source_id) {
2326 return none
2327 }
2328 expr_type, value_type := t.or_expr_types(source_id, base.typ)
2329 if !t.is_optional_type_name(expr_type) || value_type.len == 0 || value_type == 'void' {
2330 return none
2331 }
2332 source := t.transform_lvalue(source_id)
2333 value_base := t.make_selector(source, 'value', value_type)
2334 mut new_children := []flat.NodeId{cap: int(node.children_count)}
2335 new_children << value_base
2336 for i in 1 .. node.children_count {
2337 new_children << t.transform_expr(t.a.child(&node, i))
2338 }
2339 start := t.a.children.len
2340 for child in new_children {
2341 t.a.children << child
2342 }
2343 lhs_type := t.lvalue_type(id)
2344 lowered := t.a.add_node(flat.Node{
2345 kind: .selector
2346 op: if value_type.starts_with('&') { flat.Op.arrow } else { node.op }
2347 children_start: start
2348 children_count: flat.child_count(new_children.len)
2349 pos: node.pos
2350 value: node.value
2351 typ: if lhs_type.len > 0 { lhs_type } else { node.typ }
2352 })
2353 return lowered, source, t.a.child(&base, 1), base.value
2354}
2355
2356fn (t &Transformer) optional_selector_lvalue_source(id flat.NodeId) bool {
2357 if int(id) < 0 {
2358 return false
2359 }
2360 node := t.a.nodes[int(id)]
2361 match node.kind {
2362 .ident {
2363 return node.value.len > 0
2364 }
2365 .paren {
2366 if node.children_count == 0 {
2367 return false
2368 }
2369 return t.optional_selector_lvalue_source(t.a.child(&node, 0))
2370 }
2371 .selector {
2372 if node.children_count == 0 || node.value.len == 0 {
2373 return false
2374 }
2375 return t.optional_selector_lvalue_source(t.a.child(&node, 0))
2376 }
2377 else {
2378 return false
2379 }
2380 }
2381}
2382
2383fn (mut t Transformer) try_lower_struct_compound_assign(node flat.Node) ?[]flat.NodeId {
2384 if node.kind != .assign || node.children_count != 2 {
2385 return none
2386 }
2387 op_name := compound_assign_struct_operator_symbol(node.op) or { return none }
2388 lhs_id := t.a.child(&node, 0)
2389 rhs_id := t.a.child(&node, 1)
2390 lhs := t.a.nodes[int(lhs_id)]
2391 if lhs.kind != .ident || lhs.value.len == 0 {
2392 return none
2393 }
2394 mut lhs_type := t.original_expr_type(lhs_id)
2395 if lhs_type.starts_with('&') {
2396 return none
2397 }
2398 struct_type := t.struct_lookup_name(lhs_type)
2399 if struct_type.len == 0 {
2400 return none
2401 }
2402 method_name := t.struct_operator_fn_name(struct_type, op_name) or { return none }
2403 rhs := t.transform_expr_for_type(rhs_id, lhs_type)
2404 call := t.make_call_typed(method_name, arr2(t.make_ident(lhs.value), rhs), lhs_type)
2405 return arr1(t.make_assign(t.make_ident(lhs.value), call))
2406}
2407
2408fn compound_assign_struct_operator_symbol(op flat.Op) ?string {
2409 match op {
2410 .plus_assign { return '+' }
2411 .minus_assign { return '-' }
2412 .mul_assign { return '*' }
2413 .div_assign { return '/' }
2414 .mod_assign { return '%' }
2415 else {}
2416 }
2417
2418 return none
2419}
2420
2421// try_lower_sum_shared_field_assign
2422// supports helper handling in transform.
2423fn (mut t Transformer) try_lower_sum_shared_field_assign(node flat.Node) ?[]flat.NodeId {
2424 if node.kind !in [.assign, .selector_assign] || node.children_count != 2 {
2425 return none
2426 }
2427 lhs_id := t.a.child(&node, 0)
2428 rhs_id := t.a.child(&node, 1)
2429 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2430 return none
2431 }
2432 lhs := t.a.nodes[int(lhs_id)]
2433 if lhs.kind != .selector || lhs.children_count == 0 || lhs.value.len == 0 {
2434 return none
2435 }
2436 base_id := t.a.child(&lhs, 0)
2437 mut base_type := t.node_type(base_id)
2438 if base_type.len == 0 {
2439 base_type = t.original_expr_type(base_id)
2440 }
2441 field_type := t.sum_shared_field_type_name(base_type, lhs.value) or { return none }
2442 mut base := t.transform_lvalue(base_id)
2443 mut sum_type := base_type
2444 if !t.is_stable_expr_for_reuse(base) {
2445 clean_sum := t.trim_pointer_type(sum_type)
2446 ptr_type := if sum_type.starts_with('&') { sum_type } else { '&${clean_sum}' }
2447 addr := if sum_type.starts_with('&') {
2448 base
2449 } else {
2450 mut addr_expr := t.make_prefix(.amp, base)
2451 t.a.nodes[int(addr_expr)].typ = ptr_type
2452 addr_expr
2453 }
2454 tmp_name := t.new_temp('sum_lhs')
2455 t.pending_stmts << t.make_decl_assign_typed(tmp_name, addr, ptr_type)
2456 base = t.make_ident(tmp_name)
2457 sum_type = ptr_type
2458 }
2459 mut rhs := if node.op == .assign {
2460 t.transform_expr_for_type(rhs_id, field_type)
2461 } else {
2462 t.transform_expr(rhs_id)
2463 }
2464 mut rhs_type := t.node_type(rhs)
2465 if rhs_type.len == 0 {
2466 rhs_type = t.node_type(rhs_id)
2467 }
2468 if rhs_type.len == 0 {
2469 rhs_type = field_type
2470 }
2471 rhs = t.stable_transformed_expr_for_reuse(rhs, rhs_type, 'sum_assign')
2472 resolved_sum := t.resolve_sum_name(t.trim_pointer_type(sum_type))
2473 variants := t.sum_types[resolved_sum] or { return none }
2474 stmt := t.build_sum_shared_field_assign_chain(base, sum_type, resolved_sum, variants,
2475 lhs.value, field_type, rhs, node.op, 0)
2476 return t.with_pending_before(stmt)
2477}
2478
2479// build_sum_shared_field_assign_chain supports build_sum_shared_field_assign_chain handling.
2480fn (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 {
2481 if idx >= variants.len {
2482 return t.make_empty()
2483 }
2484 variant := variants[idx]
2485 tag := t.make_selector_op(base, 'typ', 'int', if sum_type.starts_with('&') {
2486 .arrow
2487 } else {
2488 .dot
2489 })
2490 cond := t.make_infix(.eq, tag, t.make_int_literal(t.sum_type_index(resolved_sum, variant)))
2491 qv := t.resolve_variant(resolved_sum, variant)
2492 sum_field := t.sum_field_name(qv)
2493 use_ptr := t.variant_references_sum(qv, resolved_sum)
2494 variant_base := t.make_selector_op(base, sum_field, if use_ptr { '&${qv}' } else { qv }, if sum_type.starts_with('&') {
2495 .arrow
2496 } else {
2497 .dot
2498 })
2499 mut then_stmt := t.make_empty()
2500 if nested_field_type := t.sum_shared_field_type_name(qv, field) {
2501 nested_sum := t.resolve_sum_name(qv)
2502 if nested_variants := t.sum_types[nested_sum] {
2503 then_stmt = t.build_sum_shared_field_assign_chain(variant_base, qv, nested_sum,
2504 nested_variants, field, nested_field_type, rhs, op, 0)
2505 }
2506 } else {
2507 field_lhs := t.make_selector_op(variant_base, field, field_type, if use_ptr {
2508 .arrow
2509 } else {
2510 .dot
2511 })
2512 then_stmt = t.make_assign_op(field_lhs, rhs, op)
2513 }
2514 then_block := t.make_block(arr1(then_stmt))
2515 else_stmt := t.build_sum_shared_field_assign_chain(base, sum_type, resolved_sum, variants,
2516 field, field_type, rhs, op, idx + 1)
2517 return t.make_if(cond, then_block, else_stmt)
2518}
2519
2520// assignment_sum_target supports assignment sum target handling for Transformer.
2521fn (t &Transformer) assignment_sum_target(lhs_id flat.NodeId, rhs_id flat.NodeId, lhs_type string) string {
2522 if lhs_type.starts_with('&') {
2523 return ''
2524 }
2525 if lhs_type.starts_with('[]') || t.is_fixed_array_type(lhs_type) {
2526 return ''
2527 }
2528 if t.is_sum_type_name(lhs_type) {
2529 return lhs_type
2530 }
2531 if int(lhs_id) < 0 || int(rhs_id) < 0 {
2532 return ''
2533 }
2534 lhs := t.a.nodes[int(lhs_id)]
2535 if lhs.kind != .selector || lhs.value.len == 0 {
2536 return ''
2537 }
2538 if lhs.value == 'obj' {
2539 sum_name := t.resolve_sum_name('ScopeObject')
2540 if t.is_sum_type_name(sum_name) {
2541 return sum_name
2542 }
2543 }
2544 rhs := t.a.nodes[int(rhs_id)]
2545 if inferred_sum := t.sum_type_for_field_variant(lhs.value, rhs_id, rhs) {
2546 return inferred_sum
2547 }
2548 if lhs.value == 'info' {
2549 if type_info_sum := t.type_info_sum_name() {
2550 return type_info_sum
2551 }
2552 }
2553 return ''
2554}
2555
2556// type_info_sum_name returns type info sum name data for Transformer.
2557fn (t &Transformer) type_info_sum_name() ?string {
2558 for sum_name, _ in t.sum_types {
2559 if sum_name == 'TypeInfo' || sum_name.ends_with('.TypeInfo') {
2560 return sum_name
2561 }
2562 }
2563 return none
2564}
2565
2566// try_lower_pointer_value_assign supports try lower pointer value assign handling for Transformer.
2567fn (mut t Transformer) try_lower_pointer_value_assign(node flat.Node) ?[]flat.NodeId {
2568 if node.kind != .assign || node.children_count != 2 {
2569 return none
2570 }
2571 lhs_id := t.a.child(&node, 0)
2572 lhs := t.a.nodes[int(lhs_id)]
2573 if lhs.kind != .ident || lhs.value.len == 0 {
2574 return none
2575 }
2576 mut lhs_type := t.var_type(lhs.value)
2577 if lhs_type.len == 0 {
2578 lhs_type = t.node_type(lhs_id)
2579 }
2580 if !lhs_type.starts_with('&') {
2581 return none
2582 }
2583 rhs_id := t.a.child(&node, 1)
2584 rhs_type := t.node_type(rhs_id)
2585 lhs_value_type := t.normalize_type_alias(lhs_type[1..])
2586 if node.op != .assign {
2587 if !t.pointer_value_lvalues[lhs.value] {
2588 return none
2589 }
2590 new_lhs := t.make_prefix(.mul, t.make_ident(lhs.value))
2591 return arr1(t.make_assign_op(new_lhs, t.transform_expr(rhs_id), node.op))
2592 }
2593 if rhs_type.len == 0
2594 || (rhs_type != lhs_value_type && !t.type_alias_targets_type(lhs_type[1..], rhs_type)) {
2595 return none
2596 }
2597 new_lhs := t.make_prefix(.mul, t.make_ident(lhs.value))
2598 return arr1(t.make_assign(new_lhs, t.transform_expr(rhs_id)))
2599}
2600
2601// transform_expr_for_type transforms transform expr for type data for transform.
2602fn (mut t Transformer) transform_expr_for_type(id flat.NodeId, target_type string) flat.NodeId {
2603 if int(id) >= 0 && target_type.len > 0 {
2604 node := t.a.nodes[int(id)]
2605 if node.kind == .none_expr && t.is_ierror_type(target_type) {
2606 return t.make_struct_init('IError')
2607 }
2608 if target_type.starts_with('&') {
2609 if expr := t.transform_amp_struct_init_for_type(id, node, target_type) {
2610 return expr
2611 }
2612 }
2613 if expr := t.transform_interface_value_for_type(id, target_type) {
2614 return expr
2615 }
2616 if node.kind == .block {
2617 if lowered := t.transform_block_expr_for_type(id, node, target_type) {
2618 return lowered
2619 }
2620 }
2621 if node.kind == .if_expr {
2622 if lowered := t.try_expand_if_expr_value_for_type(id, node, target_type) {
2623 return lowered
2624 }
2625 }
2626 if node.kind == .match_stmt {
2627 if lowered := t.transform_match_expr_for_type(id, node, target_type) {
2628 return lowered
2629 }
2630 }
2631 if node.kind == .or_expr && !t.is_optional_type_name(target_type) {
2632 old_typ := node.typ
2633 t.a.nodes[int(id)].typ = target_type
2634 expr := t.transform_expr(id)
2635 t.a.nodes[int(id)].typ = old_typ
2636 return t.coerce_transformed_expr_to_type(expr, id, target_type)
2637 }
2638 if node.kind == .array_literal {
2639 if lowered := t.transform_fixed_array_literal_for_type(id, node, target_type) {
2640 return lowered
2641 }
2642 if lowered := t.transform_array_literal_for_type(id, node, target_type) {
2643 return lowered
2644 }
2645 }
2646 if node.kind == .array_init {
2647 if lowered := t.transform_empty_array_init_for_type(node, target_type) {
2648 return lowered
2649 }
2650 }
2651 }
2652 expr := t.transform_expr(id)
2653 return t.coerce_transformed_expr_to_type(expr, id, target_type)
2654}
2655
2656// transform_block_expr_for_type transforms transform block expr for type data for transform.
2657fn (mut t Transformer) transform_block_expr_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2658 if node.kind != .block || node.children_count == 0 || target_type.len == 0 {
2659 return none
2660 }
2661 last_id := t.a.child(&node, node.children_count - 1)
2662 last := t.a.nodes[int(last_id)]
2663 tail_expr_id := if last.kind == .expr_stmt && last.children_count > 0 {
2664 t.a.child(&last, 0)
2665 } else if last.kind == .block && t.stmt_value_type(last_id).len > 0 {
2666 last_id
2667 } else if !t.is_stmt_kind(last.kind) {
2668 last_id
2669 } else {
2670 return none
2671 }
2672 mut prefix := []flat.NodeId{cap: int(node.children_count - 1)}
2673 for i in 0 .. node.children_count - 1 {
2674 prefix << t.a.child(&node, i)
2675 }
2676 mut new_children := t.transform_stmts(prefix)
2677 tail_expr := t.transform_expr_for_type(tail_expr_id, target_type)
2678 tail_stmt := t.make_expr_stmt(tail_expr)
2679 for stmt in t.with_pending_before(tail_stmt) {
2680 new_children << stmt
2681 }
2682 new_block := t.make_block(new_children)
2683 block_typ := t.stmt_value_type(new_block)
2684 t.a.nodes[int(new_block)].typ = if block_typ.len > 0 { block_typ } else { node.typ }
2685 return new_block
2686}
2687
2688// transform_match_expr_for_type transforms transform match expr for type data for transform.
2689fn (mut t Transformer) transform_match_expr_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2690 if target_type.len == 0 || node.kind != .match_stmt {
2691 return none
2692 }
2693 mut actual_result_type := t.match_expr_type(node)
2694 if actual_result_type.len == 0 || actual_result_type == 'void' {
2695 actual_result_type = target_type
2696 }
2697 tmp_name := t.new_temp('match_val')
2698 outer_pending := t.pending_stmts.clone()
2699 t.pending_stmts.clear()
2700
2701 mut prelude := []flat.NodeId{}
2702 prelude << t.make_decl_assign_typed(tmp_name, t.zero_value_for_type(actual_result_type),
2703 actual_result_type)
2704 for stmt in t.build_match_value_stmts(node, tmp_name, actual_result_type) {
2705 prelude << stmt
2706 }
2707
2708 t.pending_stmts = outer_pending
2709 for stmt in prelude {
2710 t.pending_stmts << stmt
2711 }
2712 tmp := t.make_ident(tmp_name)
2713 t.a.nodes[int(tmp)].typ = actual_result_type
2714 return tmp
2715}
2716
2717// transform_amp_struct_init_for_type supports transform_amp_struct_init_for_type handling.
2718fn (mut t Transformer) transform_amp_struct_init_for_type(_id flat.NodeId, node flat.Node, target_type string) ?flat.NodeId {
2719 if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2720 return none
2721 }
2722 child_id := t.a.child(&node, 0)
2723 child := t.a.nodes[int(child_id)]
2724 if child.kind != .struct_init {
2725 return none
2726 }
2727 new_child := t.transform_struct_init(child_id, child)
2728 start := t.a.children.len
2729 t.a.children << new_child
2730 return t.a.add_node(flat.Node{
2731 kind: .prefix
2732 op: node.op
2733 children_start: start
2734 children_count: 1
2735 pos: node.pos
2736 value: node.value
2737 typ: if target_type.len > 0 { target_type } else { node.typ }
2738 })
2739}
2740
2741// coerce_transformed_expr_to_type converts coerce transformed expr to type data for transform.
2742fn (mut t Transformer) coerce_transformed_expr_to_type(expr flat.NodeId, source_id flat.NodeId, target_type string) flat.NodeId {
2743 mut target := t.normalize_type_alias(target_type)
2744 if target.len == 0 || int(expr) < 0 {
2745 return expr
2746 }
2747 mut expr_type := t.node_type(expr)
2748 if expr_type.len == 0 {
2749 expr_type = t.node_type(source_id)
2750 }
2751 if expr_type.len == 0 {
2752 expr_type = t.resolve_expr_type(source_id)
2753 }
2754 expr_type = t.normalize_type_alias(expr_type)
2755 mut optional_target := if t.is_optional_type_name(target_type) {
2756 t.qualify_optional_type(target_type)
2757 } else {
2758 target
2759 }
2760 optional_target = t.infer_typed_optional_target(optional_target, expr_type)
2761 if t.is_optional_type_name(optional_target) && !t.is_optional_type_name(expr_type) {
2762 source := if int(source_id) >= 0 { t.a.nodes[int(source_id)] } else { flat.Node{} }
2763 if source.kind != .none_expr {
2764 return t.make_optional_some(expr, optional_target)
2765 }
2766 }
2767 if expr_type.len == 0 || expr_type == target {
2768 return expr
2769 }
2770 if target.starts_with('&') {
2771 if t.expr_is_nil_like(source_id) {
2772 t.a.nodes[int(expr)].typ = target
2773 return expr
2774 }
2775 target_value_type := t.normalize_type_alias(target[1..])
2776 expr_value_type := if expr_type.starts_with('&') {
2777 t.normalize_type_alias(expr_type[1..])
2778 } else {
2779 expr_type
2780 }
2781 if t.is_sum_type_name(target_value_type)
2782 && t.find_sum_type_for_variant(t.trim_pointer_type(expr_type)).len > 0 {
2783 if t.resolve_sum_name(t.trim_pointer_type(expr_type)) == t.resolve_sum_name(target_value_type) {
2784 if expr_type.starts_with('&') {
2785 return expr
2786 }
2787 if t.expr_can_take_address(expr) {
2788 addr := t.make_prefix(.amp, expr)
2789 t.a.nodes[int(addr)].typ = target
2790 return addr
2791 }
2792 tmp_name := t.new_temp('sum_ref')
2793 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, target_value_type)
2794 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2795 t.a.nodes[int(addr)].typ = target
2796 return addr
2797 }
2798 source := t.a.nodes[int(source_id)]
2799 wrap_source_id := if source.kind == .prefix && source.op == .amp
2800 && source.children_count > 0 {
2801 t.a.child(&source, 0)
2802 } else {
2803 source_id
2804 }
2805 wrapped := t.wrap_sum_value(wrap_source_id, target_value_type)
2806 tmp_name := t.new_temp('sum_ref')
2807 t.pending_stmts << t.make_decl_assign_typed(tmp_name, wrapped, target_value_type)
2808 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2809 t.a.nodes[int(addr)].typ = target
2810 return addr
2811 }
2812 if expr_value_type == target_value_type
2813 || t.type_alias_targets_type(target[1..], expr_value_type) {
2814 if expr_type.starts_with('&') {
2815 return expr
2816 }
2817 if t.expr_can_take_address(expr) {
2818 addr := t.make_prefix(.amp, expr)
2819 t.a.nodes[int(addr)].typ = target
2820 return addr
2821 }
2822 tmp_name := t.new_temp('addr')
2823 t.pending_stmts << t.make_decl_assign_typed(tmp_name, expr, expr_value_type)
2824 addr := t.make_prefix(.amp, t.make_ident(tmp_name))
2825 t.a.nodes[int(addr)].typ = target
2826 return addr
2827 }
2828 return expr
2829 }
2830 if expr_type.starts_with('&') {
2831 expr_value_type := t.normalize_type_alias(expr_type[1..])
2832 if expr_value_type == target || t.type_alias_targets_type(expr_type[1..], target) {
2833 deref := t.make_prefix(.mul, expr)
2834 t.a.nodes[int(deref)].typ = target
2835 return deref
2836 }
2837 }
2838 return expr
2839}
2840
2841// is_ierror_type reports whether is ierror type applies in transform.
2842fn (t &Transformer) is_ierror_type(name string) bool {
2843 clean := t.trim_pointer_type(t.normalize_type_alias(name))
2844 return clean == 'IError' || clean.ends_with('.IError')
2845}
2846
2847// expr_is_nil_like supports expr is nil like handling for Transformer.
2848fn (t &Transformer) expr_is_nil_like(id flat.NodeId) bool {
2849 if int(id) < 0 {
2850 return false
2851 }
2852 node := t.a.nodes[int(id)]
2853 if node.kind == .nil_literal {
2854 return true
2855 }
2856 if node.kind != .block || node.children_count == 0 {
2857 return false
2858 }
2859 last_id := t.a.child(&node, node.children_count - 1)
2860 last := t.a.nodes[int(last_id)]
2861 if last.kind == .expr_stmt && last.children_count > 0 {
2862 return t.expr_is_nil_like(t.a.child(&last, 0))
2863 }
2864 return t.expr_is_nil_like(last_id)
2865}
2866
2867// infer_typed_optional_target resolves infer typed optional target information for transform.
2868fn (t &Transformer) infer_typed_optional_target(optional_target string, expr_type string) string {
2869 if expr_type.len == 0 {
2870 return optional_target
2871 }
2872 mut value_type := expr_type
2873 if !value_type.contains('.') {
2874 qualified := t.qualify_type(value_type)
2875 if qualified != value_type {
2876 value_type = qualified
2877 }
2878 }
2879 if !isnil(t.tc) {
2880 parsed := t.tc.parse_type(value_type)
2881 parsed_name := parsed.name()
2882 if parsed_name.len > 0 && parsed_name != 'unknown' {
2883 value_type = parsed_name
2884 }
2885 }
2886 if t.is_optional_type_name(optional_target) {
2887 base := t.optional_base_type(optional_target)
2888 if value_type.contains('.') && base == value_type.all_after_last('.') {
2889 return '?${value_type}'
2890 }
2891 return optional_target
2892 }
2893 if optional_target != 'Optional' || isnil(t.tc) {
2894 return optional_target
2895 }
2896 typ := t.tc.parse_type(value_type)
2897 if typ is types.Primitive || typ is types.Enum || typ is types.Void {
2898 return optional_target
2899 }
2900 return '?${value_type}'
2901}
2902
2903// make_optional_some builds make optional some data for transform.
2904fn (mut t Transformer) make_optional_some(value flat.NodeId, optional_type string) flat.NodeId {
2905 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(true), 'bool')
2906 base_type := t.optional_base_type(optional_type)
2907 mut fields := []flat.NodeId{cap: 2}
2908 fields << ok_field
2909 if base_type.len > 0 && base_type != 'void' {
2910 fields << t.make_sum_literal_field('value', value, base_type)
2911 }
2912 start := t.a.children.len
2913 for field in fields {
2914 t.a.children << field
2915 }
2916 return t.a.add_node(flat.Node{
2917 kind: .struct_init
2918 children_start: start
2919 children_count: flat.child_count(fields.len)
2920 value: optional_type
2921 typ: optional_type
2922 })
2923}
2924
2925// make_optional_none builds make optional none data for transform.
2926fn (mut t Transformer) make_optional_none(optional_type string) flat.NodeId {
2927 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(false), 'bool')
2928 start := t.a.children.len
2929 t.a.children << ok_field
2930 return t.a.add_node(flat.Node{
2931 kind: .struct_init
2932 children_start: start
2933 children_count: 1
2934 value: optional_type
2935 typ: optional_type
2936 })
2937}
2938
2939// make_optional_none_with_err builds make optional none with err data for transform.
2940fn (mut t Transformer) make_optional_none_with_err(optional_type string, err_expr flat.NodeId) flat.NodeId {
2941 ok_field := t.make_sum_literal_field('ok', t.make_bool_literal(false), 'bool')
2942 err_field := t.make_sum_literal_field('err', err_expr, 'IError')
2943 start := t.a.children.len
2944 t.a.children << ok_field
2945 t.a.children << err_field
2946 return t.a.add_node(flat.Node{
2947 kind: .struct_init
2948 children_start: start
2949 children_count: 2
2950 value: optional_type
2951 typ: optional_type
2952 })
2953}
2954
2955// expr_can_take_address supports expr can take address handling for Transformer.
2956fn (t &Transformer) expr_can_take_address(id flat.NodeId) bool {
2957 if int(id) < 0 {
2958 return false
2959 }
2960 node := t.a.nodes[int(id)]
2961 match node.kind {
2962 .ident {
2963 return true
2964 }
2965 .index {
2966 // `a[lo..hi]` (an index node tagged `range`) yields a fresh array value, not an
2967 // addressable element, so its address can't be taken in place — runtime_addr
2968 // must materialize it to a temp first. Only plain element indexing `a[i]` is an
2969 // addressable lvalue.
2970 if node.value == 'range' {
2971 return false
2972 }
2973 return true
2974 }
2975 .selector {
2976 if node.children_count == 0 {
2977 return false
2978 }
2979 if t.selector_chain_has_sum_variant_field(id) {
2980 return false
2981 }
2982 return t.expr_can_take_address(t.a.child(&node, 0))
2983 }
2984 .prefix {
2985 return node.op == .mul
2986 }
2987 .paren {
2988 if node.children_count == 0 {
2989 return false
2990 }
2991 return t.expr_can_take_address(t.a.child(&node, 0))
2992 }
2993 else {
2994 return false
2995 }
2996 }
2997}
2998
2999// type_alias_targets_type returns type alias targets type data for Transformer.
3000fn (t &Transformer) type_alias_targets_type(alias_name string, target_type string) bool {
3001 if alias_name.len == 0 || target_type.len == 0 || isnil(t.tc) {
3002 return false
3003 }
3004 for name, target in t.tc.type_aliases {
3005 if name == alias_name || name.all_after_last('.') == alias_name {
3006 if t.normalize_type_alias(target) == target_type {
3007 return true
3008 }
3009 }
3010 }
3011 return false
3012}
3013
3014// try_lower_string_compound_assign
3015// supports helper handling in transform.
3016fn (mut t Transformer) try_lower_string_compound_assign(_id flat.NodeId, node flat.Node) ?[]flat.NodeId {
3017 if node.kind != .assign || node.op != .plus_assign || node.children_count != 2 {
3018 return none
3019 }
3020 lhs_id := t.a.child(&node, 0)
3021 lhs := t.a.nodes[int(lhs_id)]
3022 if lhs.kind != .ident {
3023 return none
3024 }
3025 rhs_id := t.a.child(&node, 1)
3026 rhs := t.a.nodes[int(rhs_id)]
3027 is_string := t.resolve_expr_type(lhs_id) == 'string' || rhs.kind == .string_literal
3028 || rhs.kind == .string_interp || t.resolve_expr_type(rhs_id) == 'string'
3029 if !is_string {
3030 return none
3031 }
3032 new_rhs := t.transform_expr(rhs_id)
3033 lhs_copy := t.make_ident(lhs.value)
3034 concat := t.make_call('string__plus', arr2(lhs_copy, new_rhs))
3035 new_lhs := t.make_ident(lhs.value)
3036 return arr1(t.make_assign(new_lhs, concat))
3037}
3038
3039// transform_decl_assign_stmt transforms transform decl assign stmt data for transform.
3040fn (mut t Transformer) transform_decl_assign_stmt(id flat.NodeId, node flat.Node) []flat.NodeId {
3041 if node.children_count == 0 {
3042 return arr1(id)
3043 }
3044 mut has_empty_child := false
3045 for i in 0 .. node.children_count {
3046 if int(t.a.child(&node, i)) < 0 {
3047 has_empty_child = true
3048 }
3049 }
3050 if has_empty_child {
3051 mut parts := []string{}
3052 for i in 0 .. node.children_count {
3053 child_id := t.a.child(&node, i)
3054 if int(child_id) < 0 {
3055 parts << '${i}:empty'
3056 } else {
3057 child := t.a.nodes[int(child_id)]
3058 parts << '${i}:${child.kind}:${child.value}:${child.typ}'
3059 }
3060 }
3061 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('|')}')
3062 }
3063 mut inferred_typ := ''
3064 if node.children_count > 2 && !isnil(t.tc) {
3065 rhs_id := t.a.child(&node, 1)
3066 if rhs_types := t.multi_return_types_for_expr(rhs_id, node.children_count - 1) {
3067 for j, field_type in rhs_types {
3068 lhs_idx := if j == 0 { 0 } else { j + 1 }
3069 if lhs_idx >= node.children_count {
3070 continue
3071 }
3072 lhs := t.a.child_node(&node, lhs_idx)
3073 if lhs.kind == .ident && lhs.value.len > 0 && lhs.value != '_' {
3074 t.set_var_type(lhs.value, t.normalize_type_alias(field_type.name()))
3075 }
3076 }
3077 }
3078 }
3079 if expanded := t.try_expand_multi_return_decl(node) {
3080 return expanded
3081 }
3082 if expanded := t.try_expand_plain_multi_decl(node) {
3083 return expanded
3084 }
3085 // Track the variable type for the common 2-child case.
3086 if node.children_count == 2 {
3087 lhs := t.a.child_node(&node, 0)
3088 if lhs.kind == .ident && lhs.value.len > 0 {
3089 mut typ := t.infer_decl_type(node)
3090 rhs_id := t.a.child(&node, 1)
3091 rhs := t.a.nodes[int(rhs_id)]
3092 if rhs.kind == .call {
3093 generic_typ := t.concrete_generic_call_return_type(rhs_id, rhs)
3094 if generic_typ.len > 0 {
3095 typ = generic_typ
3096 }
3097 }
3098 if rhs.kind == .call && t.is_strings_builder_new_call(rhs_id, rhs) {
3099 typ = 'strings.Builder'
3100 } else if rhs.kind == .if_expr {
3101 if_typ := t.if_expr_result_type(rhs_id, rhs)
3102 if if_typ.len > 0 {
3103 typ = if_typ
3104 }
3105 } else if rhs.kind == .match_stmt {
3106 match_typ := t.match_expr_type(rhs)
3107 if match_typ.len > 0 {
3108 typ = match_typ
3109 }
3110 } else if rhs.kind == .block {
3111 block_typ := t.stmt_value_type(rhs_id)
3112 if block_typ.len > 0 {
3113 typ = block_typ
3114 }
3115 } else if rhs.kind == .or_expr && rhs.children_count > 0 {
3116 or_source_id := t.a.child(&rhs, 0)
3117 if info := t.map_index_info(or_source_id) {
3118 typ = info.value_type
3119 } else if info := t.array_index_info(or_source_id) {
3120 typ = info.value_type
3121 } else {
3122 or_body_id := if rhs.children_count > 1 {
3123 t.a.child(&rhs, 1)
3124 } else {
3125 flat.empty_node
3126 }
3127 fallback_type := if typ.len > 0 { typ } else { t.stmt_value_type(or_body_id) }
3128 expr_type, value_type := t.or_expr_types(or_source_id, fallback_type)
3129 if t.is_optional_type_name(expr_type) && value_type.len > 0
3130 && value_type != 'void' {
3131 typ = value_type
3132 }
3133 }
3134 }
3135 if node.typ.len == 0 {
3136 if rhs.kind == .array_literal && t.is_fixed_array_type(typ) {
3137 typ = '[]${fixed_array_elem_type(typ)}'
3138 t.a.nodes[int(rhs_id)].typ = typ
3139 }
3140 }
3141 if typ.len > 0 {
3142 t.set_var_type(lhs.value, typ)
3143 inferred_typ = typ
3144 }
3145 }
3146 }
3147 // A value local whose address escapes (`p := &v` with `p` returned) is moved to the heap
3148 // at its own declaration so writes after the alias are visible to the caller. Must run
3149 // before the `p := &v` alias is transformed (the source is declared first).
3150 if node.children_count == 2 {
3151 src := t.a.child_node(&node, 0)
3152 if src.kind == .ident && src.value in t.escaping_amp_sources
3153 && src.value !in t.heaped_amp_locals && t.heapable_value_type(inferred_typ) {
3154 return t.heap_escaping_source_decl(node, src.value, inferred_typ)
3155 }
3156 }
3157 mut new_children := []flat.NodeId{cap: int(node.children_count)}
3158 for i in 0 .. node.children_count {
3159 child_id := t.a.child(&node, i)
3160 if i == 0 || (node.children_count > 2 && i > 1) {
3161 new_children << t.transform_lvalue(child_id)
3162 } else if node.children_count == 2 && t.try_heap_escaping_amp(node, child_id) {
3163 new_children << t.heap_escaping_amp_rhs(child_id)
3164 // When `v` was heap-moved it is already a `&T`, so `p := &v` is really `p := v`
3165 // (a `&T`), not `&&T` as the literal `&v` would infer. Adopt the source's pointer
3166 // type for `p` so its declaration and later uses are consistent.
3167 amp := t.a.nodes[int(child_id)]
3168 if amp.children_count > 0 {
3169 amp_src := t.a.nodes[int(t.a.child(&, 0))]
3170 if amp_src.kind == .ident && amp_src.value in t.heaped_amp_locals {
3171 inferred_typ = t.var_type(amp_src.value)
3172 t.set_var_type(t.a.nodes[int(t.a.child(&node, 0))].value, inferred_typ)
3173 }
3174 }
3175 } else {
3176 lhs_id := t.a.child(&node, 0)
3177 lhs_type := if inferred_typ.len > 0 {
3178 inferred_typ
3179 } else if node.typ.len > 0 {
3180 node.typ
3181 } else {
3182 t.lvalue_type(lhs_id)
3183 }
3184 sum_target := t.assignment_sum_target(lhs_id, child_id, lhs_type)
3185 if sum_target.len > 0 && !t.expr_has_smartcast(child_id) {
3186 new_children << t.wrap_sum_value(child_id, sum_target)
3187 } else {
3188 new_children << t.transform_expr_for_type(child_id, lhs_type)
3189 }
3190 }
3191 }
3192 if node.children_count == 2 && node.typ.len == 0 {
3193 lhs := t.a.nodes[int(new_children[0])]
3194 if lhs.kind == .ident && lhs.value.len > 0 {
3195 rhs_typ := t.node_type(new_children[1])
3196 if decl_type_is_usable(rhs_typ)
3197 && (inferred_typ.len == 0 || inferred_typ in ['array', 'map', 'unknown']) {
3198 t.set_var_type(lhs.value, rhs_typ)
3199 inferred_typ = rhs_typ
3200 }
3201 }
3202 }
3203 start := t.a.children.len
3204 for nc in new_children {
3205 t.a.children << nc
3206 }
3207 new_id := t.a.add_node(flat.Node{
3208 kind: .decl_assign
3209 op: node.op
3210 children_start: start
3211 children_count: node.children_count
3212 pos: node.pos
3213 value: node.value
3214 typ: if inferred_typ.len > 0 { inferred_typ } else { node.typ }
3215 })
3216 return t.with_pending_before(new_id)
3217}
3218
3219fn (mut t Transformer) try_expand_plain_multi_decl(node flat.Node) ?[]flat.NodeId {
3220 if node.kind != .decl_assign || node.children_count < 4 || node.children_count % 2 != 0 {
3221 return none
3222 }
3223 mut result := []flat.NodeId{}
3224 for i := 0; i < node.children_count; i += 2 {
3225 lhs_id := t.a.child(&node, i)
3226 rhs_id := t.a.child(&node, i + 1)
3227 lhs := t.a.nodes[int(lhs_id)]
3228 rhs_node := t.a.nodes[int(rhs_id)]
3229 generic_rhs_typ := if rhs_node.kind == .call {
3230 t.concrete_generic_call_return_type(rhs_id, rhs_node)
3231 } else {
3232 ''
3233 }
3234 rhs := t.transform_expr(rhs_id)
3235 t.drain_pending(mut result)
3236 if lhs.kind != .ident || lhs.value == '_' {
3237 continue
3238 }
3239 rhs_authority := t.decl_rhs_type(rhs_id)
3240 mut typ := if t.is_fn_pointer_type_name(rhs_authority) { rhs_authority } else { '' }
3241 if typ.len == 0 {
3242 typ = generic_rhs_typ
3243 }
3244 if typ.len == 0 {
3245 typ = t.node_type(rhs)
3246 }
3247 if typ.len == 0 {
3248 typ = t.node_type(rhs_id)
3249 }
3250 if typ.len == 0 {
3251 typ = rhs_authority
3252 }
3253 if typ.len == 0 && lhs.typ.len > 0 {
3254 typ = lhs.typ
3255 }
3256 if typ.len > 0 {
3257 typ = t.normalize_type_alias(typ)
3258 t.set_var_type(lhs.value, typ)
3259 result << t.make_decl_assign_typed(lhs.value, rhs, typ)
3260 } else {
3261 result << t.make_decl_assign(lhs.value, rhs)
3262 }
3263 }
3264 return result
3265}
3266
3267// expr_has_smartcast converts expr has smartcast data for transform.
3268fn (t &Transformer) expr_has_smartcast(id flat.NodeId) bool {
3269 key := t.expr_key(id)
3270 return t.has_smartcast(key)
3271}
3272
3273// try_expand_multi_return_decl supports try expand multi return decl handling for Transformer.
3274fn (mut t Transformer) try_expand_multi_return_decl(node flat.Node) ?[]flat.NodeId {
3275 if node.kind != .decl_assign || node.children_count < 3 || isnil(t.tc) {
3276 return none
3277 }
3278 rhs_id := t.a.child(&node, 1)
3279 rhs := t.a.nodes[int(rhs_id)]
3280 lhs_ids := t.multi_assign_lhs_ids(node)
3281 if rhs.kind == .if_expr {
3282 return t.expand_multi_return_if_decl(rhs_id, rhs, lhs_ids)
3283 }
3284 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
3285 tmp_name := t.new_temp('multi_ret')
3286 mut result := []flat.NodeId{}
3287 new_rhs := t.transform_expr(rhs_id)
3288 t.drain_pending(mut result)
3289 result << t.make_decl_assign_typed(tmp_name, new_rhs, t.multi_return_type_name(rhs_types))
3290 for j, field_type in rhs_types {
3291 if j >= lhs_ids.len {
3292 continue
3293 }
3294 lhs_id := lhs_ids[j]
3295 lhs := t.a.nodes[int(lhs_id)]
3296 if lhs.kind != .ident || lhs.value == '_' {
3297 continue
3298 }
3299 field_name := 'arg${j}'
3300 field_type_name := field_type.name()
3301 field := t.make_selector(t.make_ident(tmp_name), field_name, field_type_name)
3302 t.set_var_type(lhs.value, t.normalize_type_alias(field_type_name))
3303 result << t.make_decl_assign_typed(lhs.value, field, field_type_name)
3304 }
3305 return result
3306 }
3307 return none
3308}
3309
3310// try_expand_multi_return_assign supports try expand multi return assign handling for Transformer.
3311fn (mut t Transformer) try_expand_multi_return_assign(node flat.Node) ?[]flat.NodeId {
3312 if node.kind != .assign || node.children_count < 3 || isnil(t.tc) {
3313 return none
3314 }
3315 rhs_id := t.a.child(&node, 1)
3316 rhs := t.a.nodes[int(rhs_id)]
3317 lhs_ids := t.multi_assign_lhs_ids(node)
3318 if rhs.kind == .if_expr {
3319 return t.expand_multi_return_if_assign(rhs_id, rhs, lhs_ids)
3320 }
3321 if rhs_types := t.multi_return_types_for_expr(rhs_id, lhs_ids.len) {
3322 tmp_name := t.new_temp('multi_ret')
3323 mut result := []flat.NodeId{}
3324 new_rhs := t.transform_expr(rhs_id)
3325 t.drain_pending(mut result)
3326 result << t.make_decl_assign_typed(tmp_name, new_rhs, t.multi_return_type_name(rhs_types))
3327 for j, field_type in rhs_types {
3328 if j >= lhs_ids.len {
3329 continue
3330 }
3331 lhs_id := lhs_ids[j]
3332 lhs := t.a.nodes[int(lhs_id)]
3333 if lhs.kind == .ident && lhs.value == '_' {
3334 continue
3335 }
3336 field_name := 'arg${j}'
3337 field_type_name := field_type.name()
3338 field := t.make_selector(t.make_ident(tmp_name), field_name, field_type_name)
3339 result << t.make_assign(t.transform_lvalue(lhs_id), field)
3340 }
3341 return result
3342 }
3343 return none
3344}
3345
3346// try_expand_plain_multi_assign supports try expand plain multi assign handling for Transformer.
3347fn (mut t Transformer) try_expand_plain_multi_assign(node flat.Node) ?[]flat.NodeId {
3348 if node.kind != .assign || node.op != .assign || node.children_count < 4
3349 || node.children_count % 2 != 0 {
3350 return none
3351 }
3352 mut result := []flat.NodeId{}
3353 mut lhs_ids := []flat.NodeId{}
3354 mut tmp_names := []string{}
3355 for i := 0; i < node.children_count; i += 2 {
3356 lhs_id := t.a.child(&node, i)
3357 rhs_id := t.a.child(&node, i + 1)
3358 lhs_ids << lhs_id
3359 lhs_type := t.lvalue_type(lhs_id)
3360 rhs := if lhs_type.len > 0 {
3361 t.transform_expr_for_type(rhs_id, lhs_type)
3362 } else {
3363 t.transform_expr(rhs_id)
3364 }
3365 t.drain_pending(mut result)
3366 tmp_name := t.new_temp('assign')
3367 tmp_type := if lhs_type.len > 0 { lhs_type } else { t.node_type(rhs_id) }
3368 result << t.make_decl_assign_typed(tmp_name, rhs, tmp_type)
3369 tmp_names << tmp_name
3370 }
3371 for i, lhs_id in lhs_ids {
3372 lhs := t.a.nodes[int(lhs_id)]
3373 if lhs.kind == .ident && lhs.value == '_' {
3374 continue
3375 }
3376 result << t.make_assign(t.transform_lvalue(lhs_id), t.make_ident(tmp_names[i]))
3377 }
3378 return result
3379}
3380
3381// multi_assign_lhs_ids supports multi assign lhs ids handling for Transformer.
3382fn (t &Transformer) multi_assign_lhs_ids(node flat.Node) []flat.NodeId {
3383 mut lhs_ids := []flat.NodeId{}
3384 if node.children_count > 0 {
3385 lhs_ids << t.a.child(&node, 0)
3386 }
3387 for i in 2 .. node.children_count {
3388 lhs_ids << t.a.child(&node, i)
3389 }
3390 return lhs_ids
3391}
3392
3393// multi_return_types_for_expr supports multi return types for expr handling for Transformer.
3394fn (t &Transformer) multi_return_types_for_expr(id flat.NodeId, expected_count int) ?[]types.Type {
3395 if int(id) < 0 || isnil(t.tc) {
3396 return none
3397 }
3398 if typ := t.tc.expr_type(id) {
3399 if items := multi_return_types_from_type(typ, expected_count) {
3400 return items
3401 }
3402 }
3403 node := t.a.nodes[int(id)]
3404 if node.kind == .or_expr && node.children_count > 0 {
3405 return t.multi_return_types_for_expr(t.a.child(&node, 0), expected_count)
3406 }
3407 if node.kind == .match_stmt {
3408 return t.match_multi_return_types(node, expected_count)
3409 }
3410 if node.kind == .expr_stmt {
3411 return t.expr_stmt_multi_return_types(node, expected_count)
3412 }
3413 if node.kind == .block {
3414 return t.block_multi_return_types(node, expected_count)
3415 }
3416 mut typ_name := node.typ
3417 if node.kind == .call {
3418 ret := t.get_call_return_type(id, node)
3419 if ret.len > 0 {
3420 typ_name = ret
3421 }
3422 } else if typ_name.len == 0 {
3423 typ_name = t.resolve_expr_type(id)
3424 }
3425 if typ_name.len == 0 {
3426 if node.kind == .call {
3427 return t.find_multi_return_call_types(node, expected_count)
3428 }
3429 return none
3430 }
3431 typ := t.tc.parse_type(typ_name)
3432 if items := multi_return_types_from_type(typ, expected_count) {
3433 return items
3434 }
3435 if node.kind == .call {
3436 return t.find_multi_return_call_types(node, expected_count)
3437 }
3438 return none
3439}
3440
3441fn (t &Transformer) match_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3442 if node.children_count < 2 {
3443 return none
3444 }
3445 for i in 1 .. node.children_count {
3446 branch := t.a.child_node(&node, i)
3447 if branch.kind != .match_branch {
3448 continue
3449 }
3450 body_start := if branch.value == 'else' { 0 } else { t.count_conds(*branch) }
3451 if branch.children_count <= body_start {
3452 continue
3453 }
3454 tail_id := t.a.child(branch, branch.children_count - 1)
3455 if items := t.multi_return_types_for_expr(tail_id, expected_count) {
3456 return items
3457 }
3458 }
3459 return none
3460}
3461
3462fn (t &Transformer) expr_stmt_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3463 if expected_count <= 0 || node.children_count != expected_count {
3464 return none
3465 }
3466 mut result := []types.Type{cap: expected_count}
3467 for i in 0 .. node.children_count {
3468 child_id := t.a.child(&node, i)
3469 mut typ_name := t.node_type(child_id)
3470 if typ_name.len == 0 {
3471 typ_name = t.resolve_expr_type(child_id)
3472 }
3473 if typ_name.len == 0 {
3474 return none
3475 }
3476 result << t.tc.parse_type(typ_name)
3477 }
3478 return result
3479}
3480
3481fn (t &Transformer) block_multi_return_types(node flat.Node, expected_count int) ?[]types.Type {
3482 if expected_count <= 0 || node.children_count != expected_count {
3483 return none
3484 }
3485 mut result := []types.Type{cap: expected_count}
3486 for i in 0 .. node.children_count {
3487 stmt_id := t.a.child(&node, i)
3488 stmt := t.a.nodes[int(stmt_id)]
3489 if stmt.kind != .expr_stmt || stmt.children_count != 1 {
3490 return none
3491 }
3492 child_id := t.a.child(&stmt, 0)
3493 mut typ_name := t.node_type(child_id)
3494 if typ_name.len == 0 {
3495 typ_name = t.resolve_expr_type(child_id)
3496 }
3497 if typ_name.len == 0 {
3498 return none
3499 }
3500 result << t.tc.parse_type(typ_name)
3501 }
3502 return result
3503}
3504
3505// multi_return_types_from_type converts multi return types from type data for transform.
3506fn multi_return_types_from_type(typ types.Type, expected_count int) ?[]types.Type {
3507 if typ is types.MultiReturn {
3508 if expected_count <= 0 || typ.types.len == expected_count {
3509 items := typ.types.clone()
3510 return items
3511 }
3512 return none
3513 }
3514 if typ is types.OptionType {
3515 return multi_return_types_from_type(typ.base_type, expected_count)
3516 }
3517 if typ is types.ResultType {
3518 return multi_return_types_from_type(typ.base_type, expected_count)
3519 }
3520 return none
3521}
3522
3523// find_multi_return_call_types resolves find multi return call types information for transform.
3524fn (t &Transformer) find_multi_return_call_types(node flat.Node, expected_count int) ?[]types.Type {
3525 if node.kind != .call || node.children_count == 0 || isnil(t.tc) {
3526 return none
3527 }
3528 fn_node := t.a.child_node(&node, 0)
3529 mut candidates := []string{}
3530 if fn_node.kind == .ident {
3531 candidates << fn_node.value
3532 if t.cur_module.len > 0 && t.cur_module != 'main' && t.cur_module != 'builtin' {
3533 candidates << '${t.cur_module}.${fn_node.value}'
3534 }
3535 } else if fn_node.kind == .selector {
3536 if fn_node.children_count > 0 {
3537 base_id := t.a.child(fn_node, 0)
3538 mut base_type := t.resolve_expr_type(base_id)
3539 if base_type.starts_with('&') {
3540 base_type = base_type[1..]
3541 }
3542 if base_type.len > 0 {
3543 candidates << '${base_type}.${fn_node.value}'
3544 if base_type.contains('.') {
3545 candidates << '${base_type.all_after_last('.')}.${fn_node.value}'
3546 }
3547 }
3548 }
3549 candidates << '.${fn_node.value}'
3550 }
3551 for candidate in candidates {
3552 if ret := t.tc.fn_ret_types[candidate] {
3553 if items := multi_return_types_from_type(ret, expected_count) {
3554 return items
3555 }
3556 }
3557 }
3558 for candidate in candidates {
3559 for key, ret in t.tc.fn_ret_types {
3560 matches := if candidate.starts_with('.') {
3561 key.ends_with(candidate)
3562 } else {
3563 key == candidate || key.ends_with('.${candidate}')
3564 }
3565 if matches {
3566 if items := multi_return_types_from_type(ret, expected_count) {
3567 return items
3568 }
3569 }
3570 }
3571 }
3572 return none
3573}
3574
3575// multi_return_type_name supports multi return type name handling for Transformer.
3576fn (t &Transformer) multi_return_type_name(items []types.Type) string {
3577 mut names := []string{cap: items.len}
3578 for item in items {
3579 names << item.name()
3580 }
3581 return '(${names.join(', ')})'
3582}
3583
3584// expand_multi_return_if_decl builds expand multi return if decl data for transform.
3585fn (mut t Transformer) expand_multi_return_if_decl(_rhs_id flat.NodeId, rhs flat.Node, lhs_ids []flat.NodeId) ?[]flat.NodeId {
3586 if lhs_ids.len == 0 {
3587 return none
3588 }
3589 value_types := t.infer_multi_if_value_types(rhs, lhs_ids.len)
3590 mut result := []flat.NodeId{}
3591 for i, lhs_id in lhs_ids {
3592 lhs := t.a.nodes[int(lhs_id)]
3593 if lhs.kind != .ident || lhs.value == '_' {
3594 continue
3595 }
3596 typ := if i < value_types.len { value_types[i] } else { 'int' }
3597 result << t.make_decl_assign_typed(lhs.value, t.zero_value_for_type(typ), typ)
3598 }
3599 if_stmts := t.expand_multi_return_if_assign(_rhs_id, rhs, lhs_ids) or { return none }
3600 for stmt in if_stmts {
3601 result << stmt
3602 }
3603 return result
3604}
3605
3606// expand_multi_return_if_assign builds expand multi return if assign data for transform.
3607fn (mut t Transformer) expand_multi_return_if_assign(_rhs_id flat.NodeId, rhs flat.Node, lhs_ids []flat.NodeId) ?[]flat.NodeId {
3608 if rhs.kind != .if_expr || lhs_ids.len == 0 {
3609 return none
3610 }
3611 return t.lower_multi_if_assign(rhs, lhs_ids)
3612}
3613
3614// lower_multi_if_assign builds lower multi if assign data for transform.
3615fn (mut t Transformer) lower_multi_if_assign(node flat.Node, lhs_ids []flat.NodeId) []flat.NodeId {
3616 if node.children_count < 2 {
3617 return []
3618 }
3619 cond_id := t.a.child(&node, 0)
3620 then_id := t.a.child(&node, 1)
3621 mut result := []flat.NodeId{}
3622 new_cond := t.transform_expr(cond_id)
3623 t.drain_pending(mut result)
3624 then_block := t.multi_if_assign_block(then_id, lhs_ids)
3625 mut else_block := t.make_empty()
3626 if node.children_count >= 3 {
3627 else_id := t.a.child(&node, 2)
3628 else_node := t.a.nodes[int(else_id)]
3629 if else_node.kind == .if_expr {
3630 else_stmts := t.lower_multi_if_assign(else_node, lhs_ids)
3631 else_block = t.make_block(else_stmts)
3632 } else {
3633 else_block = t.multi_if_assign_block(else_id, lhs_ids)
3634 }
3635 }
3636 result << t.make_if(new_cond, then_block, else_block)
3637 return result
3638}
3639
3640// multi_if_assign_block supports multi if assign block handling for Transformer.
3641fn (mut t Transformer) multi_if_assign_block(block_id flat.NodeId, lhs_ids []flat.NodeId) flat.NodeId {