v / vlib / v2 / transformer / struct.v
2823 lines · 2730 sloc · 79.37 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module transformer
6
7import v2.ast
8import v2.types
9import v2.token
10
11fn is_numeric_literal_expr(expr ast.Expr) bool {
12 match expr {
13 ast.BasicLiteral {
14 return expr.kind == .number
15 }
16 ast.PrefixExpr {
17 return expr.op == .minus && expr.expr is ast.BasicLiteral && expr.expr.kind == .number
18 }
19 ast.ParenExpr {
20 return is_numeric_literal_expr(expr.expr)
21 }
22 else {
23 return false
24 }
25 }
26}
27
28fn (mut t Transformer) synth_selector(lhs ast.Expr, field_name string, typ types.Type) ast.Expr {
29 pos := t.next_synth_pos()
30 t.register_synth_type(pos, typ)
31 return ast.Expr(ast.SelectorExpr{
32 lhs: lhs
33 rhs: ast.Ident{
34 name: field_name
35 }
36 pos: pos
37 })
38}
39
40// synth_selector_from_struct creates a typed SelectorExpr by looking up the field type
41// from the named struct in the environment. Falls back to a pos-only synth node if
42// the field type cannot be resolved.
43fn (mut t Transformer) synth_selector_from_struct(lhs ast.Expr, field_name string, struct_name string) ast.Expr {
44 pos := t.next_synth_pos()
45 if field_typ := t.lookup_struct_field_type(struct_name, field_name) {
46 t.register_synth_type(pos, field_typ)
47 }
48 return ast.Expr(ast.SelectorExpr{
49 lhs: lhs
50 rhs: ast.Ident{
51 name: field_name
52 }
53 pos: pos
54 })
55}
56
57// lookup_struct_field_type returns the raw types.Type for a struct field.
58fn transformer_object_type(obj types.Object) ?types.Type {
59 match obj {
60 types.Type {
61 return types.Type(obj)
62 }
63 types.TypeObject {
64 return obj.typ
65 }
66 else {}
67 }
68
69 return none
70}
71
72fn (t &Transformer) lookup_struct_field_type(struct_name string, field_name string) ?types.Type {
73 if struct_name.len == 0 || field_name.len == 0 || struct_name.len > 1024
74 || field_name.len > 1024 || !transformer_string_has_valid_data(struct_name)
75 || !transformer_string_has_valid_data(field_name) {
76 return none
77 }
78 if field_type := t.lookup_struct_field_generic_decl_type(struct_name, field_name) {
79 return field_type
80 }
81 mut sname := struct_name
82 mut mod := ''
83 dunder := struct_name.last_index('__') or { -1 }
84 if dunder >= 0 {
85 mod = struct_name[..dunder]
86 sname = struct_name[dunder + 2..]
87 if field_type := t.cached_struct_field_types[struct_field_lookup_cache_key(mod, sname,
88 field_name)]
89 {
90 return field_type
91 }
92 if field_type := t.cached_struct_field_types[struct_field_generic_decl_key(struct_name,
93 field_name)]
94 {
95 return field_type
96 }
97 } else if dot := struct_name.last_index('.') {
98 mod = struct_name[..dot]
99 sname = struct_name[dot + 1..]
100 if field_type := t.cached_struct_field_types[struct_field_lookup_cache_key(mod, sname,
101 field_name)]
102 {
103 return field_type
104 }
105 dot_name := struct_name.replace('.', '__')
106 if field_type := t.cached_struct_field_types[struct_field_generic_decl_key(dot_name,
107 field_name)]
108 {
109 return field_type
110 }
111 } else if t.cur_module != '' {
112 if field_type := t.cached_struct_field_types[struct_field_lookup_cache_key(t.cur_module,
113 sname, field_name)]
114 {
115 return field_type
116 }
117 }
118 // Try module scope first if qualified
119 if mod != '' {
120 if scope := t.cached_scopes[mod] {
121 if obj := scope.objects[sname] {
122 if typ := transformer_object_type(obj) {
123 if typ is types.Struct {
124 if field_type := t.struct_field_type(typ, field_name) {
125 return field_type
126 }
127 }
128 }
129 }
130 }
131 }
132 // For unqualified names, try the current module first to avoid
133 // collisions (e.g., ast.OptionType vs types.OptionType).
134 if mod == '' && t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
135 if cur_scope := t.cached_scopes[t.cur_module] {
136 if obj := cur_scope.objects[sname] {
137 if typ := transformer_object_type(obj) {
138 if typ is types.Struct {
139 if field_type := t.struct_field_type(typ, field_name) {
140 return field_type
141 }
142 }
143 }
144 }
145 }
146 }
147 // Fallback: scan all scopes
148 for sk in t.cached_scope_keys {
149 scope := t.cached_scopes[sk] or { continue }
150 if obj := scope.objects[struct_name] {
151 if typ := transformer_object_type(obj) {
152 if typ is types.Struct {
153 if field_type := t.struct_field_type(typ, field_name) {
154 return field_type
155 }
156 }
157 }
158 }
159 if sname != struct_name {
160 if obj := scope.objects[sname] {
161 if typ := transformer_object_type(obj) {
162 if typ is types.Struct {
163 if field_type := t.struct_field_type(typ, field_name) {
164 return field_type
165 }
166 }
167 }
168 }
169 }
170 }
171 return none
172}
173
174fn (t &Transformer) lookup_struct_field_generic_decl_type(struct_name string, field_name string) ?types.Type {
175 if struct_name == '' || field_name == '' {
176 return none
177 }
178 if field_type := t.struct_field_generic_decl_type_for_candidate(struct_name, field_name) {
179 return field_type
180 }
181 if struct_name.contains('__') {
182 short_name := struct_name.all_after_last('__')
183 if short_name != '' && short_name != struct_name {
184 if field_type := t.struct_field_generic_decl_type_for_candidate(short_name, field_name) {
185 return field_type
186 }
187 }
188 }
189 if struct_name.index_u8(`.`) >= 0 {
190 dot_name := struct_name.replace('.', '__')
191 if dot_name != struct_name {
192 if field_type := t.struct_field_generic_decl_type_for_candidate(dot_name, field_name) {
193 return field_type
194 }
195 }
196 short_name := struct_name.all_after_last('.')
197 if short_name != '' && short_name != struct_name && short_name != dot_name {
198 if field_type := t.struct_field_generic_decl_type_for_candidate(short_name, field_name) {
199 return field_type
200 }
201 }
202 }
203 return none
204}
205
206fn (t &Transformer) struct_field_generic_decl_type_for_candidate(struct_name string, field_name string) ?types.Type {
207 key := struct_field_generic_decl_key(struct_name, field_name)
208 field_type := t.struct_field_generic_decl_types[key] or { return none }
209 return field_type
210}
211
212fn (t &Transformer) lookup_struct_field_generic_decl_bindings(struct_name string, field_name string) ?map[string]types.Type {
213 if struct_name == '' || field_name == '' {
214 return none
215 }
216 if bindings := t.struct_field_generic_decl_bindings_for_candidate(struct_name, field_name) {
217 return bindings
218 }
219 if struct_name.contains('__') {
220 short_name := struct_name.all_after_last('__')
221 if short_name != '' && short_name != struct_name {
222 if bindings := t.struct_field_generic_decl_bindings_for_candidate(short_name,
223 field_name)
224 {
225 return bindings
226 }
227 }
228 }
229 if struct_name.index_u8(`.`) >= 0 {
230 dot_name := struct_name.replace('.', '__')
231 if dot_name != struct_name {
232 if bindings := t.struct_field_generic_decl_bindings_for_candidate(dot_name, field_name) {
233 return bindings
234 }
235 }
236 short_name := struct_name.all_after_last('.')
237 if short_name != '' && short_name != struct_name && short_name != dot_name {
238 if bindings := t.struct_field_generic_decl_bindings_for_candidate(short_name,
239 field_name)
240 {
241 return bindings
242 }
243 }
244 }
245 return none
246}
247
248fn (t &Transformer) struct_field_generic_decl_bindings_for_candidate(struct_name string, field_name string) ?map[string]types.Type {
249 key := struct_field_generic_decl_key(struct_name, field_name)
250 if bindings := t.struct_field_generic_decl_bindings[key] {
251 return bindings.clone()
252 }
253 return none
254}
255
256fn struct_field_lookup_candidates(struct_name string) []string {
257 mut candidates := []string{}
258 if struct_name != '' {
259 candidates << struct_name
260 if struct_name.contains('__') {
261 short_name := struct_name.all_after_last('__')
262 if short_name != '' && short_name !in candidates {
263 candidates << short_name
264 }
265 }
266 if struct_name.contains('.') {
267 dot_name := struct_name.replace('.', '__')
268 if dot_name !in candidates {
269 candidates << dot_name
270 }
271 short_name := struct_name.all_after_last('.')
272 if short_name != '' && short_name !in candidates {
273 candidates << short_name
274 }
275 }
276 }
277 return candidates
278}
279
280fn embedded_type_name_matches(embedded_name string, field_name string) bool {
281 if embedded_name == field_name {
282 return true
283 }
284 if embedded_name.contains('__') && embedded_name.all_after_last('__') == field_name {
285 return true
286 }
287 if embedded_name.contains('.') && embedded_name.all_after_last('.') == field_name {
288 return true
289 }
290 return false
291}
292
293fn (t &Transformer) live_embedded_struct_type(embedded types.Struct) types.Struct {
294 if embedded.name != '' {
295 if live_type := t.lookup_struct_type_any_module(embedded.name) {
296 return live_type
297 }
298 if live_type := t.lookup_type(embedded.name) {
299 if live_type is types.Struct {
300 return live_type
301 }
302 }
303 if embedded.name.contains('__') {
304 short_name := embedded.name.all_after_last('__')
305 if live_type := t.lookup_struct_type_any_module(short_name) {
306 return live_type
307 }
308 if live_type := t.lookup_type(short_name) {
309 if live_type is types.Struct {
310 return live_type
311 }
312 }
313 }
314 }
315 return embedded
316}
317
318fn (t &Transformer) struct_field_type(struct_type types.Struct, field_name string) ?types.Type {
319 mut seen := map[string]bool{}
320 return t.struct_field_type_inner(struct_type, field_name, mut seen)
321}
322
323fn (t &Transformer) struct_field_type_inner(struct_type types.Struct, field_name string, mut seen map[string]bool) ?types.Type {
324 if struct_type.name != '' {
325 if struct_type.name in seen {
326 return none
327 }
328 seen[struct_type.name] = true
329 }
330 for field in struct_type.fields {
331 if !transformer_string_has_valid_data(field.name) {
332 continue
333 }
334 if field.name == field_name {
335 return field.typ
336 }
337 }
338 for embedded in struct_type.embedded {
339 live_embedded := t.live_embedded_struct_type(embedded)
340 if embedded_type_name_matches(live_embedded.name, field_name) {
341 return types.Type(live_embedded)
342 }
343 if field_type := t.struct_field_type_inner(live_embedded, field_name, mut seen) {
344 return field_type
345 }
346 }
347 return none
348}
349
350// open_scope creates a new nested scope
351fn (mut t Transformer) apply_smartcast_field_access_ctx(sumtype_expr ast.Expr, field_name string, ctx SmartcastContext) ast.Expr {
352 // variant (short name) is used for union member access
353 // variant_full (full name) is used for type cast
354 variant_short := ctx.variant
355 // Extract simple variant name for _data._ accessor
356 // Union fields use: _Null (same-module Ident), _time__Time (cross-module SelectorExpr),
357 // _Array_json2__Any (composite). Only strip module prefix for same-module types.
358 // Union fields use the name as seen from the declaring module:
359 // same-module Ident → _Null, cross-module SelectorExpr → _time__Time.
360 // Strip module prefix when the variant's module matches the sumtype's module,
361 // because those variants were declared as Idents (no module prefix in the union).
362 sumtype_module := if ctx.sumtype.contains('__') {
363 ctx.sumtype.all_before_last('__')
364 } else {
365 ''
366 }
367 variant_simple := if variant_short.starts_with('Array_') || variant_short.starts_with('Map_') {
368 // For composite types, use the short name to match union member
369 variant_short
370 } else if variant_short.contains('__') {
371 mod_prefix := variant_short.all_before_last('__')
372 if mod_prefix == sumtype_module {
373 variant_short.all_after_last('__')
374 } else {
375 variant_short
376 }
377 } else {
378 variant_short
379 }
380 // Use full variant name for type cast from context
381 mangled_variant := if ctx.variant_full != '' {
382 ctx.variant_full
383 } else if variant_short.contains('__') {
384 variant_short // Already has module prefix
385 } else if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
386 '${t.cur_module}__${variant_short}'
387 } else {
388 variant_short
389 }
390 // For nested smartcasts, we need to transform the base of sumtype_expr to apply outer smartcasts
391 // E.g., for stmt.receiver.typ with outer smartcast on stmt, we need to transform stmt.receiver first.
392 // Temporarily remove this exact context to avoid applying it recursively.
393 removed_ctxs := t.remove_matching_smartcasts(ctx)
394 transformed_base := t.transform_expr(sumtype_expr)
395 t.restore_smartcasts(removed_ctxs)
396 if t.expr_is_casted_to_type(transformed_base, '${mangled_variant}*') {
397 return t.synth_selector_from_struct(transformed_base, field_name, mangled_variant)
398 }
399 // Already concretely casted to this variant by an outer smartcast context.
400 if t.expr_is_casted_to_type(transformed_base, mangled_variant) {
401 return t.synth_selector_from_struct(transformed_base, field_name, mangled_variant)
402 }
403 // For interface smartcasts, use _object instead of _data
404 is_interface_ctx := ctx.sumtype.starts_with('__iface__')
405 if is_interface_ctx {
406 object_access := t.synth_selector(transformed_base, '_object', types.Type(types.voidptr_))
407 cast_expr := ast.CastExpr{
408 typ: ast.Ident{
409 name: '${mangled_variant}*'
410 }
411 expr: object_access
412 }
413 return t.synth_selector_from_struct(ast.ParenExpr{
414 expr: cast_expr
415 }, field_name, mangled_variant)
416 }
417 // Create data access.
418 // For native backends (arm64/x64): _data is a plain i64 (void pointer) in the SSA struct.
419 // No union variant sub-field exists, so just use _data directly.
420 // For C backends: _data is a union, so access _data._variant for the specific member.
421 is_native_backend := t.pref != unsafe { nil } && t.is_native_be
422 data_access := t.synth_selector(transformed_base, '_data', types.Type(types.voidptr_))
423 variant_access := if is_native_backend {
424 data_access
425 } else {
426 t.synth_selector(data_access, '_${variant_simple}', types.Type(types.voidptr_))
427 }
428 if t.is_eval_backend() {
429 return t.synth_selector_from_struct(variant_access, field_name, mangled_variant)
430 }
431 // Create: (mangled_variant*)variant_access
432 cast_expr := ast.CastExpr{
433 typ: ast.Ident{
434 name: '${mangled_variant}*'
435 }
436 expr: variant_access
437 }
438 // Create: cast_expr->field_name (cleanc will handle pointer arrow vs dot)
439 return t.synth_selector_from_struct(ast.Expr(cast_expr), field_name, mangled_variant)
440}
441
442fn (mut t Transformer) transform_array_init_expr(expr ast.ArrayInitExpr) ast.Expr {
443 if !expr_array_has_valid_data(expr.exprs) {
444 return ast.Expr(expr)
445 }
446 is_native_backend := t.pref != unsafe { nil } && t.is_native_be
447 native_interface_elem_type := if is_native_backend {
448 t.get_interface_array_init_concrete_type(&expr) or { '' }
449 } else {
450 ''
451 }
452 // Transform value expressions
453 mut exprs := []ast.Expr{cap: expr.exprs.len}
454 for e in expr.exprs {
455 if native_interface_elem_type != '' {
456 if inner := t.get_interface_cast_inner_expr(e) {
457 exprs << t.transform_expr(inner)
458 continue
459 }
460 }
461 exprs << t.transform_expr(e)
462 }
463
464 // Check if this is a fixed-size array
465 mut is_fixed := false
466 mut array_typ := expr.typ
467 mut elem_type_expr := ast.empty_expr
468 has_fixed_marker := array_init_has_fixed_len_marker(expr)
469 // Check for ArrayFixedType or ArrayType (expr.typ is ast.Type sum type)
470 if expr.typ is ast.Type {
471 if expr.typ is ast.ArrayFixedType {
472 is_fixed = true
473 } else if expr.typ is ast.ArrayType {
474 elem_type_expr = expr.typ.elem_type
475 }
476 }
477 // For untyped `[]` literals, use checker-inferred type from context (assign/call/return).
478 if array_typ is ast.EmptyExpr {
479 if !has_fixed_marker {
480 if literal_type := t.get_array_init_expr_type(expr) {
481 if literal_type is types.Array {
482 array_typ = t.type_to_ast_type_expr(literal_type)
483 elem_type_expr = t.type_to_ast_type_expr(literal_type.elem_type)
484 }
485 }
486 }
487 }
488 if array_typ is ast.EmptyExpr {
489 if inferred := t.get_expr_type(ast.Expr(expr)) {
490 inferred_base := t.unwrap_alias_and_pointer_type(inferred)
491 match inferred_base {
492 types.Array {
493 array_typ = t.type_to_ast_type_expr(inferred_base)
494 elem_type_expr = t.type_to_ast_type_expr(inferred_base.elem_type)
495 }
496 types.ArrayFixed {
497 array_typ = t.type_to_ast_type_expr(inferred_base)
498 elem_type_expr = t.type_to_ast_type_expr(inferred_base.elem_type)
499 is_fixed = true
500 }
501 else {}
502 }
503 }
504 }
505 if native_interface_elem_type != '' {
506 elem_type_expr = ast.Expr(ast.Ident{
507 name: native_interface_elem_type
508 })
509 array_typ = ast.Expr(ast.Type(ast.ArrayType{
510 elem_type: elem_type_expr
511 }))
512 }
513 // Also check for [x, y, z]! syntax - parser marks this with len: PostfixExpr{op: .not}
514 if has_fixed_marker {
515 is_fixed = true
516 if expr.typ is ast.EmptyExpr {
517 array_typ = ast.Expr(ast.empty_expr)
518 }
519 if array_typ is ast.Type && array_typ is ast.ArrayType {
520 array_typ = ast.Expr(ast.Type(ast.ArrayFixedType{
521 len: ast.BasicLiteral{
522 kind: .number
523 value: '${expr.exprs.len}'
524 }
525 elem_type: elem_type_expr
526 }))
527 } else if array_typ is ast.EmptyExpr {
528 if elem_type := t.get_array_init_elem_expr_type(if expr.exprs.len > 0 {
529 expr.exprs[0]
530 } else {
531 expr.init
532 })
533 {
534 array_typ = ast.Expr(ast.Type(ast.ArrayFixedType{
535 len: ast.BasicLiteral{
536 kind: .number
537 value: '${expr.exprs.len}'
538 }
539 elem_type: t.type_to_ast_type_expr(elem_type)
540 }))
541 } else if elem_type_expr !is ast.EmptyExpr {
542 array_typ = ast.Expr(ast.Type(ast.ArrayFixedType{
543 len: ast.BasicLiteral{
544 kind: .number
545 value: '${expr.exprs.len}'
546 }
547 elem_type: elem_type_expr
548 }))
549 }
550 }
551 }
552
553 if is_fixed {
554 // Fixed-size array: keep as ArrayInitExpr
555 return ast.ArrayInitExpr{
556 typ: array_typ
557 exprs: exprs
558 init: t.transform_expr(expr.init)
559 cap: if expr.cap !is ast.EmptyExpr { t.transform_expr(expr.cap) } else { expr.cap }
560 len: if expr.len !is ast.EmptyExpr { t.transform_expr(expr.len) } else { expr.len }
561 pos: expr.pos
562 }
563 }
564
565 if t.is_eval_backend() {
566 return ast.ArrayInitExpr{
567 typ: array_typ
568 exprs: exprs
569 init: if expr.init !is ast.EmptyExpr {
570 t.transform_expr(expr.init)
571 } else {
572 expr.init
573 }
574 cap: if expr.cap !is ast.EmptyExpr {
575 t.transform_expr(expr.cap)
576 } else {
577 expr.cap
578 }
579 len: if expr.len !is ast.EmptyExpr {
580 t.transform_expr(expr.len)
581 } else {
582 expr.len
583 }
584 update_expr: if expr.update_expr !is ast.EmptyExpr {
585 t.transform_expr(expr.update_expr)
586 } else {
587 expr.update_expr
588 }
589 pos: expr.pos
590 }
591 }
592
593 // Spread syntax `[...base, e1, e2]` — lower to
594 // builtin__new_array_from_array_and_c_array(array__clone_to_depth(&base, depth),
595 // n, sizeof(elem), {e1, e2}).
596 if expr.update_expr !is ast.EmptyExpr {
597 return t.transform_array_spread_expr(expr, exprs, elem_type_expr)
598 }
599
600 // Dynamic array: transform to builtin__new_array_from_c_array_noscan(len, cap, sizeof(elem), values)
601 arr_len := exprs.len
602
603 // Handle empty dynamic arrays: lower to __new_array_with_default_noscan(len, cap, sizeof(elem), init)
604 if arr_len == 0 {
605 sizeof_expr := if elem_type_expr !is ast.EmptyExpr {
606 elem_type_expr
607 } else {
608 ast.Expr(ast.Ident{
609 name: 'int'
610 })
611 }
612 len_expr := ast.Expr(if expr.len !is ast.EmptyExpr {
613 t.transform_expr(expr.len)
614 } else {
615 ast.Expr(ast.BasicLiteral{
616 kind: .number
617 value: '0'
618 })
619 })
620 cap_expr := ast.Expr(if expr.cap !is ast.EmptyExpr {
621 t.transform_expr(expr.cap)
622 } else {
623 ast.Expr(ast.BasicLiteral{
624 kind: .number
625 value: '0'
626 })
627 })
628 mut init_expr := ast.Expr(if expr.init !is ast.EmptyExpr {
629 t.transform_expr(expr.init)
630 } else {
631 ast.Expr(ast.Ident{
632 name: 'nil'
633 })
634 })
635 if expr.init !is ast.EmptyExpr && is_numeric_literal_expr(init_expr) {
636 init_expr = ast.Expr(ast.CastExpr{
637 typ: sizeof_expr
638 expr: init_expr
639 })
640 }
641 // If init expression uses `index`, expand to a for-loop that assigns each element.
642 if expr.init !is ast.EmptyExpr && t.expr_contains_ident_named(init_expr, 'index') {
643 return t.expand_array_init_with_index(len_expr, cap_expr, sizeof_expr, init_expr,
644 expr.pos)
645 }
646 // When element type is a reference type (array or map) and no explicit init,
647 // synthesize a proper default so inner elements get initialized correctly
648 // (not zero-filled via NULL, which leaves element_size=0 or hash_fn=null).
649 // sizeof_expr holds elem_type_expr before smartcasting, so use it to avoid issues.
650 elem_is_nested_array := elem_type_expr is ast.Type && elem_type_expr is ast.ArrayType
651 elem_is_map := elem_type_expr is ast.Type && elem_type_expr is ast.MapType
652 if expr.init is ast.EmptyExpr && elem_is_nested_array {
653 init_expr = ast.Expr(t.transform_array_init_expr(ast.ArrayInitExpr{
654 typ: sizeof_expr
655 }))
656 }
657 if expr.init is ast.EmptyExpr && elem_is_map {
658 init_expr = ast.Expr(t.transform_map_init_expr(ast.MapInitExpr{
659 typ: sizeof_expr
660 }))
661 }
662 // When element type is a struct with map fields and no explicit init,
663 // synthesize a struct default and use for-loop expansion so each element
664 // gets its own map allocation (memcpy would share internal pointers).
665 if expr.init is ast.EmptyExpr && !elem_is_nested_array && !elem_is_map {
666 if elem_type := t.get_expr_type(elem_type_expr) {
667 elem_base := t.unwrap_alias_and_pointer_type(elem_type)
668 if elem_base is types.Struct {
669 mut field_inits := []ast.FieldInit{}
670 for field in elem_base.fields {
671 if field.typ is types.Map {
672 map_type_expr := t.type_to_ast_type_expr(field.typ)
673 map_init := t.transform_map_init_expr(ast.MapInitExpr{
674 typ: map_type_expr
675 })
676 field_inits << ast.FieldInit{
677 name: field.name
678 value: map_init
679 }
680 }
681 }
682 if field_inits.len > 0 {
683 struct_init := ast.Expr(ast.InitExpr{
684 typ: elem_type_expr
685 fields: field_inits
686 })
687 return t.expand_array_init_with_index(len_expr, cap_expr, sizeof_expr,
688 struct_init, expr.pos)
689 }
690 }
691 }
692 }
693 // When init value is an array, use __new_array_with_array_default for deep cloning
694 // (shallow memcpy would share data pointers between all elements)
695 mut init_is_array := expr.init is ast.ArrayInitExpr
696 if !init_is_array && elem_is_nested_array && expr.init is ast.EmptyExpr {
697 init_is_array = true
698 }
699 if !init_is_array {
700 if init_type := t.get_expr_type(expr.init) {
701 init_base := t.unwrap_alias_and_pointer_type(init_type)
702 init_is_array = init_base is types.Array
703 }
704 }
705 if init_is_array {
706 return ast.CallExpr{
707 lhs: ast.Ident{
708 name: '__new_array_with_array_default'
709 }
710 args: [
711 len_expr,
712 cap_expr,
713 ast.Expr(ast.KeywordOperator{
714 op: .key_sizeof
715 exprs: [sizeof_expr]
716 }),
717 init_expr,
718 // depth parameter for clone_to_depth
719 ast.Expr(ast.BasicLiteral{
720 kind: .number
721 value: '3'
722 }),
723 ]
724 pos: expr.pos
725 }
726 }
727 return ast.CallExpr{
728 lhs: ast.Ident{
729 name: '__new_array_with_default_noscan'
730 }
731 args: [
732 len_expr,
733 cap_expr,
734 ast.Expr(ast.KeywordOperator{
735 op: .key_sizeof
736 exprs: [sizeof_expr]
737 }),
738 init_expr,
739 ]
740 pos: expr.pos
741 }
742 }
743
744 // Determine element type name and sizeof argument
745 // First, try to get the array type from the type checker's annotations
746 mut elem_type_name := 'int'
747 mut elem_type_expr_resolved := elem_type_expr
748 if elem_type_expr_resolved is ast.EmptyExpr && exprs.len > 0 {
749 if arr_type := t.env.get_expr_type(expr.pos.id) {
750 match arr_type {
751 types.Array {
752 tn := t.type_to_c_name(arr_type.elem_type)
753 if tn != '' {
754 elem_type_name = tn
755 elem_type_expr_resolved = ast.Expr(ast.Ident{
756 name: tn
757 })
758 }
759 }
760 types.ArrayFixed {
761 tn := t.type_to_c_name(arr_type.elem_type)
762 if tn != '' {
763 elem_type_name = tn
764 elem_type_expr_resolved = ast.Expr(ast.Ident{
765 name: tn
766 })
767 }
768 }
769 else {}
770 }
771 }
772 // If env lookup failed, try getting element type from the ORIGINAL (untransformed)
773 // first expression, which preserves CastExpr and other type-annotated nodes
774 if elem_type_expr_resolved is ast.EmptyExpr {
775 orig_first := expr.exprs[0]
776 if elem_type := t.get_expr_type(orig_first) {
777 tn := t.type_to_c_name(elem_type)
778 if tn != '' {
779 elem_type_name = tn
780 elem_type_expr_resolved = ast.Expr(ast.Ident{
781 name: tn
782 })
783 }
784 } else {
785 }
786 // If still not resolved, check if first expr is a CallExpr and look up its return type
787 if elem_type_expr_resolved is ast.EmptyExpr {
788 first := exprs[0]
789 if first is ast.CallExpr || first is ast.CallOrCastExpr {
790 if ret_type := t.get_method_return_type(first) {
791 tn := t.type_to_c_name(ret_type)
792 if tn != '' {
793 elem_type_name = tn
794 elem_type_expr_resolved = ast.Expr(ast.Ident{
795 name: tn
796 })
797 }
798 } else if first is ast.CallExpr {
799 // Try looking up by function name for plain function calls
800 fn_name := if first.lhs is ast.Ident {
801 first.lhs.name
802 } else {
803 ''
804 }
805 if fn_name != '' {
806 if ret_type2 := t.get_fn_return_type(fn_name) {
807 tn := t.type_to_c_name(ret_type2)
808 if tn != '' {
809 elem_type_name = tn
810 elem_type_expr_resolved = ast.Expr(ast.Ident{
811 name: tn
812 })
813 }
814 }
815 }
816 }
817 }
818 }
819 }
820 }
821 sizeof_arg := if elem_type_expr_resolved !is ast.EmptyExpr {
822 elem_type_name = t.expr_to_type_name(elem_type_expr_resolved)
823 elem_type_expr_resolved
824 } else if exprs.len > 0 {
825 // Infer from first element
826 first := exprs[0]
827 if first is ast.BasicLiteral {
828 if first.kind == .number {
829 if first.value.contains('.') || first.value.contains('e')
830 || first.value.contains('E') {
831 elem_type_name = 'f64'
832 } else {
833 elem_type_name = 'int'
834 }
835 } else if first.kind == .string {
836 elem_type_name = 'string'
837 }
838 ast.Expr(ast.Ident{
839 name: elem_type_name
840 })
841 } else if first is ast.StringLiteral {
842 elem_type_name = 'string'
843 ast.Expr(ast.Ident{
844 name: 'string'
845 })
846 } else if first is ast.SelectorExpr {
847 // For enum values like .trim_left, use int for sizeof
848 // Try to get actual enum type from environment
849 if enum_type := t.get_expr_type(first) {
850 type_name := t.type_to_c_name(enum_type)
851 if type_name != '' {
852 elem_type_name = type_name
853 ast.Expr(ast.Ident{
854 name: type_name
855 })
856 } else {
857 elem_type_name = 'int'
858 ast.Expr(ast.Ident{
859 name: 'int'
860 })
861 }
862 } else {
863 elem_type_name = 'int'
864 ast.Expr(ast.Ident{
865 name: 'int'
866 })
867 }
868 } else if first is ast.Ident {
869 // Try to get type from scope
870 var_type := t.get_var_type_name(first.name)
871 if var_type != '' {
872 elem_type_name = var_type
873 ast.Expr(ast.Ident{
874 name: var_type
875 })
876 } else {
877 // Default: use int
878 ast.Expr(ast.Ident{
879 name: 'int'
880 })
881 }
882 } else if first is ast.CallOrCastExpr {
883 // Handle cast expressions like u8(`0`) - infer element type from cast type
884 if first.lhs is ast.Ident {
885 cast_type := first.lhs.name
886 // Check if this is a primitive type cast
887 if cast_type in ['u8', 'i8', 'u16', 'i16', 'u32', 'i32', 'u64', 'i64', 'f32', 'f64',
888 'int', 'bool', 'byte', 'rune', 'voidptr', 'charptr', 'byteptr', 'usize', 'isize',
889 'string'] {
890 elem_type_name = cast_type
891 ast.Expr(ast.Ident{
892 name: cast_type
893 })
894 } else {
895 // Could be a struct cast - use the type name
896 elem_type_name = cast_type
897 ast.Expr(ast.Ident{
898 name: cast_type
899 })
900 }
901 } else {
902 // Default: use int
903 ast.Expr(ast.Ident{
904 name: 'int'
905 })
906 }
907 } else if first is ast.CastExpr {
908 // Handle explicit CastExpr nodes
909 elem_type_name = t.expr_to_type_name(first.typ)
910 ast.Expr(first.typ)
911 } else if first is ast.IndexExpr {
912 // Handle index expressions like s[i] - try to infer element type from the indexed container
913 // Also handle slice expressions like s[..i] which become IndexExpr with RangeExpr
914 // Extract first.lhs to avoid double smartcast in if-guard expansions
915 first_lhs := first.lhs
916 mut idx_sizeof := ast.Expr(ast.Ident{
917 name: 'int'
918 })
919 if first.expr is ast.RangeExpr {
920 // Slicing: s[a..b] returns the same type as s
921 if expr_type := t.get_expr_type(first_lhs) {
922 type_name := t.type_to_c_name(expr_type)
923 if type_name != '' {
924 elem_type_name = type_name
925 idx_sizeof = ast.Expr(ast.Ident{
926 name: type_name
927 })
928 }
929 }
930 } else if expr_type := t.get_expr_type(first_lhs) {
931 type_name := t.type_to_c_name(expr_type)
932 if type_name == 'string' {
933 // String indexing returns u8
934 elem_type_name = 'u8'
935 idx_sizeof = ast.Expr(ast.Ident{
936 name: 'u8'
937 })
938 } else if type_name.starts_with('Array_') {
939 // Array indexing returns element type
940 arr_elem := type_name[6..] // Remove 'Array_' prefix
941 elem_type_name = arr_elem
942 idx_sizeof = ast.Expr(ast.Ident{
943 name: arr_elem
944 })
945 }
946 }
947 idx_sizeof
948 } else if first is ast.CallExpr {
949 // Handle function calls - try to infer return type
950 if expr_type := t.get_expr_type(first) {
951 type_name := t.type_to_c_name(expr_type)
952 if type_name != '' {
953 elem_type_name = type_name
954 ast.Expr(ast.Ident{
955 name: type_name
956 })
957 } else {
958 ast.Expr(ast.Ident{
959 name: 'int'
960 })
961 }
962 } else {
963 // Try to infer from function name for common patterns
964 mut fn_name := ''
965 if first.lhs is ast.Ident {
966 fn_name = first.lhs.name
967 } else if first.lhs is ast.SelectorExpr {
968 fn_name = first.lhs.rhs.name
969 }
970 // Dynamic array construction functions return 'array' type
971 if fn_name in ['builtin__new_array_from_c_array_noscan',
972 'builtin__new_array_from_c_array', '__new_array_with_default_noscan',
973 'new_array_from_c_array'] {
974 elem_type_name = 'array'
975 ast.Expr(ast.Ident{
976 name: 'array'
977 })
978 } else if fn_name in ['substr', 'substr_unsafe', 'trim', 'trim_left', 'trim_right',
979 'to_upper', 'to_lower', 'replace', 'reverse', 'clone', 'repeat'] {
980 // String methods that return string
981 elem_type_name = 'string'
982 ast.Expr(ast.Ident{
983 name: 'string'
984 })
985 } else {
986 ast.Expr(ast.Ident{
987 name: 'int'
988 })
989 }
990 }
991 } else if first is ast.InitExpr {
992 // Struct literal - get the type name from the struct type
993 init_type_name := t.expr_to_type_name(first.typ)
994 if init_type_name != '' {
995 elem_type_name = init_type_name
996 ast.Expr(ast.Ident{
997 name: init_type_name
998 })
999 } else {
1000 ast.Expr(ast.Ident{
1001 name: 'int'
1002 })
1003 }
1004 } else {
1005 // Default: use int
1006 ast.Expr(ast.Ident{
1007 name: 'int'
1008 })
1009 }
1010 } else {
1011 ast.Expr(ast.Ident{
1012 name: 'int'
1013 })
1014 }
1015
1016 // Create proper array type for the inner ArrayInitExpr.
1017 // Use the resolved elem_type expression when available (preserves structured AST
1018 // type info like ArrayType, PrefixExpr for &T, etc.). Only fall back to the mangled
1019 // name string when no structured expression exists — the name-based path can lose
1020 // type structure (e.g., 'Array_int*' gets misinterpreted as ptr(array) in SSA).
1021 inner_elem_type := if elem_type_expr_resolved !is ast.EmptyExpr {
1022 elem_type_expr_resolved
1023 } else {
1024 ast.Expr(ast.Ident{
1025 name: elem_type_name
1026 })
1027 }
1028 inner_array_typ := ast.Type(ast.ArrayType{
1029 elem_type: inner_elem_type
1030 })
1031
1032 return ast.CallExpr{
1033 lhs: ast.Ident{
1034 name: 'builtin__new_array_from_c_array_noscan'
1035 }
1036 args: [
1037 ast.Expr(ast.BasicLiteral{
1038 kind: .number
1039 value: '${arr_len}'
1040 }),
1041 ast.Expr(ast.BasicLiteral{
1042 kind: .number
1043 value: '${arr_len}'
1044 }),
1045 ast.Expr(ast.KeywordOperator{
1046 op: .key_sizeof
1047 exprs: [sizeof_arg]
1048 }),
1049 ast.Expr(ast.ArrayInitExpr{
1050 typ: ast.Expr(inner_array_typ)
1051 exprs: exprs
1052 }),
1053 ]
1054 pos: expr.pos
1055 }
1056}
1057
1058fn (mut t Transformer) transform_array_spread_expr(expr ast.ArrayInitExpr, exprs []ast.Expr, hint_elem_type_expr ast.Expr) ast.Expr {
1059 update_expr := t.transform_expr(expr.update_expr)
1060 // Resolve element type from the base array.
1061 mut elem_type_expr := hint_elem_type_expr
1062 mut clone_depth := 0
1063 if base_type := t.get_expr_type(expr.update_expr) {
1064 base_unwrapped := t.unwrap_alias_and_pointer_type(base_type)
1065 if base_unwrapped is types.Array {
1066 if elem_type_expr is ast.EmptyExpr {
1067 elem_type_expr = t.type_to_ast_type_expr(base_unwrapped.elem_type)
1068 }
1069 // Match V2 `.clone()` semantics for nested arrays: deep-clone inner
1070 // arrays so `[...nested]` doesn't share inner storage with `nested`.
1071 nesting := t.get_array_nesting_depth(base_type)
1072 if nesting > 1 {
1073 clone_depth = nesting - 1
1074 }
1075 }
1076 }
1077 sizeof_arg := if elem_type_expr !is ast.EmptyExpr {
1078 elem_type_expr
1079 } else {
1080 ast.Expr(ast.Ident{
1081 name: 'int'
1082 })
1083 }
1084 cloned_base := ast.Expr(ast.CallExpr{
1085 lhs: ast.Ident{
1086 name: 'array__clone_to_depth'
1087 }
1088 args: [
1089 update_expr,
1090 ast.Expr(ast.BasicLiteral{
1091 kind: .number
1092 value: '${clone_depth}'
1093 }),
1094 ]
1095 pos: expr.pos
1096 })
1097 new_count := exprs.len
1098 mut data_arg := ast.Expr(ast.CastExpr{
1099 typ: ast.Expr(ast.Ident{
1100 name: 'voidptr'
1101 })
1102 expr: ast.Expr(ast.BasicLiteral{
1103 kind: .number
1104 value: '0'
1105 })
1106 })
1107 if new_count > 0 {
1108 inner_array_typ := ast.Type(ast.ArrayType{
1109 elem_type: sizeof_arg
1110 })
1111 data_arg = ast.Expr(ast.ArrayInitExpr{
1112 typ: ast.Expr(inner_array_typ)
1113 exprs: exprs
1114 })
1115 }
1116 return ast.CallExpr{
1117 lhs: ast.Ident{
1118 name: 'builtin__new_array_from_array_and_c_array'
1119 }
1120 args: [
1121 cloned_base,
1122 ast.Expr(ast.BasicLiteral{
1123 kind: .number
1124 value: '${new_count}'
1125 }),
1126 ast.Expr(ast.KeywordOperator{
1127 op: .key_sizeof
1128 exprs: [sizeof_arg]
1129 }),
1130 data_arg,
1131 ]
1132 pos: expr.pos
1133 }
1134}
1135
1136fn array_expr_with_expected_type(expr ast.Expr, expected_type ast.Expr) ast.Expr {
1137 if expr is ast.ArrayInitExpr {
1138 return ast.Expr(array_init_with_expected_type(expr, expected_type))
1139 }
1140 return expr
1141}
1142
1143fn array_init_with_expected_type(expr ast.ArrayInitExpr, expected_type ast.Expr) ast.ArrayInitExpr {
1144 if expected_type is ast.EmptyExpr {
1145 return expr
1146 }
1147 mut rewritten_exprs := []ast.Expr{cap: expr.exprs.len}
1148 mut elem_type := ast.Expr(ast.empty_expr)
1149 if expected_type is ast.Type {
1150 expected_ast_type := expected_type as ast.Type
1151 if expected_ast_type is ast.ArrayType {
1152 array_type := expected_ast_type as ast.ArrayType
1153 elem_type = array_type.elem_type
1154 }
1155 }
1156 for item in expr.exprs {
1157 if item is ast.ArrayInitExpr && elem_type !is ast.EmptyExpr {
1158 rewritten_exprs << ast.Expr(array_init_with_expected_type(item, elem_type))
1159 } else {
1160 rewritten_exprs << item
1161 }
1162 }
1163 return ast.ArrayInitExpr{
1164 typ: expected_type
1165 exprs: rewritten_exprs
1166 init: expr.init
1167 cap: expr.cap
1168 len: expr.len
1169 pos: expr.pos
1170 }
1171}
1172
1173fn (mut t Transformer) transform_map_init_expr(expr ast.MapInitExpr) ast.Expr {
1174 // Determine key/value types from the explicit map type when available.
1175 mut key_type_expr := ast.Expr(ast.Ident{
1176 name: 'int'
1177 })
1178 mut val_type_expr := ast.Expr(ast.Ident{
1179 name: 'int'
1180 })
1181 mut key_type_name := 'int'
1182 mut have_explicit_map_type := false
1183 match expr.typ {
1184 ast.Type {
1185 expr_typ := expr.typ as ast.Type
1186 if expr_typ is ast.MapType {
1187 mt := expr_typ as ast.MapType
1188 key_type_expr = mt.key_type
1189 val_type_expr = mt.value_type
1190 key_type_name = t.expr_to_type_name(mt.key_type)
1191 have_explicit_map_type = true
1192 }
1193 }
1194 ast.Ident {
1195 if explicit_map_typ := t.lookup_type(expr.typ.name) {
1196 if explicit_map := t.unwrap_map_type(explicit_map_typ) {
1197 key_type_expr = t.type_to_ast_type_expr(explicit_map.key_type)
1198 val_type_expr = t.type_to_ast_type_expr(explicit_map.value_type)
1199 key_type_name = t.type_to_c_name(explicit_map.key_type)
1200 have_explicit_map_type = true
1201 }
1202 }
1203 }
1204 ast.SelectorExpr {
1205 explicit_map_name := t.expr_to_type_name(expr.typ)
1206 if explicit_map_name != '' {
1207 if explicit_map_typ := t.lookup_type(explicit_map_name) {
1208 if explicit_map := t.unwrap_map_type(explicit_map_typ) {
1209 key_type_expr = t.type_to_ast_type_expr(explicit_map.key_type)
1210 val_type_expr = t.type_to_ast_type_expr(explicit_map.value_type)
1211 key_type_name = t.type_to_c_name(explicit_map.key_type)
1212 have_explicit_map_type = true
1213 }
1214 }
1215 }
1216 }
1217 else {}
1218 }
1219
1220 // Empty map literals `{}` rely on checker-provided expected type.
1221 // Use the inferred map type from the environment when the AST node doesn't carry one.
1222 if !have_explicit_map_type {
1223 if inferred := t.get_expr_type(ast.Expr(expr)) {
1224 if inferred_map := t.unwrap_map_type(inferred) {
1225 key_type_expr = t.type_to_ast_type_expr(inferred_map.key_type)
1226 val_type_expr = t.type_to_ast_type_expr(inferred_map.value_type)
1227 key_type_name = t.type_to_c_name(inferred_map.key_type)
1228 }
1229 }
1230 }
1231
1232 // Transform key and value expressions (if any).
1233 mut keys := []ast.Expr{cap: expr.keys.len}
1234 mut vals := []ast.Expr{cap: expr.vals.len}
1235 for k in expr.keys {
1236 keys << t.transform_expr(k)
1237 }
1238 for v in expr.vals {
1239 vals << t.transform_expr(array_expr_with_expected_type(v, val_type_expr))
1240 }
1241
1242 if keys.len > 0 && key_type_name != '' && key_type_name != 'int' {
1243 mut needs_enum_key_resolution := false
1244 for key_expr in keys {
1245 if key_expr is ast.SelectorExpr && key_expr.lhs is ast.EmptyExpr {
1246 needs_enum_key_resolution = true
1247 break
1248 }
1249 }
1250 if needs_enum_key_resolution {
1251 for i, key_expr in keys {
1252 keys[i] = t.transform_expr(t.resolve_enum_shorthand(key_expr, key_type_name))
1253 }
1254 }
1255 }
1256
1257 // Infer map type from first entry when the checker didn't provide one.
1258 if key_type_name == 'int' && keys.len > 0 {
1259 first_key := keys[0]
1260 first_val := vals[0]
1261 if first_key is ast.BasicLiteral && first_key.kind == .string {
1262 key_type_name = 'string'
1263 key_type_expr = ast.Expr(ast.Ident{
1264 name: 'string'
1265 })
1266 } else if first_key is ast.StringLiteral {
1267 key_type_name = 'string'
1268 key_type_expr = ast.Expr(ast.Ident{
1269 name: 'string'
1270 })
1271 }
1272 if first_val is ast.BasicLiteral && first_val.kind == .string {
1273 val_type_expr = ast.Expr(ast.Ident{
1274 name: 'string'
1275 })
1276 } else if first_val is ast.StringLiteral {
1277 val_type_expr = ast.Expr(ast.Ident{
1278 name: 'string'
1279 })
1280 }
1281 }
1282
1283 if t.is_eval_backend() {
1284 return ast.MapInitExpr{
1285 typ: if expr.typ !is ast.EmptyExpr { t.transform_expr(expr.typ) } else { expr.typ }
1286 keys: keys
1287 vals: vals
1288 pos: expr.pos
1289 }
1290 }
1291
1292 hash_fn, eq_fn, clone_fn, free_fn := map_runtime_key_fns_from_type_name(key_type_name)
1293
1294 // Empty map literal `{}`: lower to `new_map(sizeof(K), sizeof(V), &hash, &eq, &clone, &free)`.
1295 if keys.len == 0 {
1296 return ast.CallExpr{
1297 lhs: ast.Ident{
1298 name: 'new_map'
1299 }
1300 args: [
1301 ast.Expr(ast.KeywordOperator{
1302 op: .key_sizeof
1303 exprs: [key_type_expr]
1304 }),
1305 ast.Expr(ast.KeywordOperator{
1306 op: .key_sizeof
1307 exprs: [val_type_expr]
1308 }),
1309 ast.Expr(ast.PrefixExpr{
1310 op: .amp
1311 expr: ast.Ident{
1312 name: hash_fn
1313 }
1314 }),
1315 ast.Expr(ast.PrefixExpr{
1316 op: .amp
1317 expr: ast.Ident{
1318 name: eq_fn
1319 }
1320 }),
1321 ast.Expr(ast.PrefixExpr{
1322 op: .amp
1323 expr: ast.Ident{
1324 name: clone_fn
1325 }
1326 }),
1327 ast.Expr(ast.PrefixExpr{
1328 op: .amp
1329 expr: ast.Ident{
1330 name: free_fn
1331 }
1332 }),
1333 ]
1334 pos: expr.pos
1335 }
1336 }
1337
1338 n := keys.len
1339
1340 // Create array types for keys and values.
1341 key_array_typ := ast.Type(ast.ArrayType{
1342 elem_type: key_type_expr
1343 })
1344 val_array_typ := ast.Type(ast.ArrayType{
1345 elem_type: val_type_expr
1346 })
1347
1348 // new_map_init_noscan_value(hash_fn, eq_fn, clone_fn, free_fn, n, key_size, val_size, keys, vals)
1349 return ast.CallExpr{
1350 lhs: ast.Ident{
1351 name: 'new_map_init_noscan_value'
1352 }
1353 args: [
1354 ast.Expr(ast.PrefixExpr{
1355 op: .amp
1356 expr: ast.Ident{
1357 name: hash_fn
1358 }
1359 }),
1360 ast.Expr(ast.PrefixExpr{
1361 op: .amp
1362 expr: ast.Ident{
1363 name: eq_fn
1364 }
1365 }),
1366 ast.Expr(ast.PrefixExpr{
1367 op: .amp
1368 expr: ast.Ident{
1369 name: clone_fn
1370 }
1371 }),
1372 ast.Expr(ast.PrefixExpr{
1373 op: .amp
1374 expr: ast.Ident{
1375 name: free_fn
1376 }
1377 }),
1378 ast.Expr(ast.BasicLiteral{
1379 kind: .number
1380 value: '${n}'
1381 }),
1382 ast.Expr(ast.KeywordOperator{
1383 op: .key_sizeof
1384 exprs: [key_type_expr]
1385 }),
1386 ast.Expr(ast.KeywordOperator{
1387 op: .key_sizeof
1388 exprs: [val_type_expr]
1389 }),
1390 ast.Expr(ast.ArrayInitExpr{
1391 typ: ast.Expr(key_array_typ)
1392 exprs: keys
1393 }),
1394 ast.Expr(ast.ArrayInitExpr{
1395 typ: ast.Expr(val_array_typ)
1396 exprs: vals
1397 }),
1398 ]
1399 pos: expr.pos
1400 }
1401}
1402
1403fn (mut t Transformer) transform_init_expr(expr ast.InitExpr) ast.Expr {
1404 // Typed empty map init: `map[K]V{}`.
1405 // Lower here so backends do not need to special-case map InitExpr nodes.
1406 if expr.fields.len == 0 {
1407 match expr.typ {
1408 ast.Type {
1409 expr_typ := expr.typ as ast.Type
1410 if expr_typ is ast.MapType {
1411 if t.is_eval_backend() {
1412 return ast.Expr(ast.MapInitExpr{
1413 typ: ast.Expr(ast.Type(expr.typ))
1414 keys: []ast.Expr{}
1415 vals: []ast.Expr{}
1416 pos: expr.pos
1417 })
1418 }
1419 mt := expr_typ as ast.MapType
1420 key_type_name := t.expr_to_type_name(mt.key_type)
1421 hash_fn, eq_fn, clone_fn, free_fn :=
1422 map_runtime_key_fns_from_type_name(key_type_name)
1423 return ast.Expr(ast.CallExpr{
1424 lhs: ast.Ident{
1425 name: 'new_map'
1426 }
1427 args: [
1428 ast.Expr(ast.KeywordOperator{
1429 op: .key_sizeof
1430 exprs: [mt.key_type]
1431 }),
1432 ast.Expr(ast.KeywordOperator{
1433 op: .key_sizeof
1434 exprs: [mt.value_type]
1435 }),
1436 ast.Expr(ast.PrefixExpr{
1437 op: .amp
1438 expr: ast.Ident{
1439 name: hash_fn
1440 }
1441 }),
1442 ast.Expr(ast.PrefixExpr{
1443 op: .amp
1444 expr: ast.Ident{
1445 name: eq_fn
1446 }
1447 }),
1448 ast.Expr(ast.PrefixExpr{
1449 op: .amp
1450 expr: ast.Ident{
1451 name: clone_fn
1452 }
1453 }),
1454 ast.Expr(ast.PrefixExpr{
1455 op: .amp
1456 expr: ast.Ident{
1457 name: free_fn
1458 }
1459 }),
1460 ]
1461 pos: expr.pos
1462 })
1463 }
1464 }
1465 else {}
1466 }
1467 }
1468
1469 mut init_typ_expr := expr.typ
1470 if concrete_typ_expr := t.concrete_generic_struct_type_expr(expr.typ) {
1471 init_typ_expr = concrete_typ_expr
1472 }
1473 typed_expr := ast.InitExpr{
1474 typ: init_typ_expr
1475 pos: expr.pos
1476 }
1477 if expr.pos.id != 0 {
1478 if init_typ := t.type_from_init_expr(typed_expr) {
1479 t.synth_types[expr.pos.id] = init_typ
1480 }
1481 }
1482 // Get the struct type name for field type lookups
1483 struct_type_name := t.get_init_expr_type_name(init_typ_expr)
1484
1485 // Transform field values recursively
1486 // Note: ArrayInitExpr is NOT transformed here because cleanc uses field type info
1487 // to determine if it's a fixed-size array (which transformer doesn't have access to)
1488 mut fields := []ast.FieldInit{cap: expr.fields.len}
1489 positional_field_names := t.struct_positional_field_names(struct_type_name)
1490 mut positional_idx := 0
1491 for field in expr.fields {
1492 mut lookup_field_name := field.name
1493 if lookup_field_name == '' {
1494 if positional_idx < positional_field_names.len {
1495 lookup_field_name = positional_field_names[positional_idx]
1496 }
1497 positional_idx++
1498 }
1499 // Check if this field is a sum type and needs wrapping
1500 mut field_type_name := t.get_struct_field_type_name(struct_type_name, lookup_field_name)
1501 mut expected_field_type := types.Type(types.int_)
1502 mut has_expected_field_type := false
1503 mut pretransformed_value := ast.empty_expr
1504 mut has_pretransformed_value := false
1505 if direct_type := t.lookup_struct_field_type(struct_type_name, lookup_field_name) {
1506 expected_field_type = direct_type
1507 has_expected_field_type = true
1508 field_type_name = t.type_to_c_name(direct_type)
1509 } else if direct_type := t.get_init_expr_field_type(init_typ_expr, lookup_field_name) {
1510 expected_field_type = direct_type
1511 has_expected_field_type = true
1512 field_type_name = t.type_to_c_name(direct_type)
1513 } else if field_type_name != '' {
1514 if expected_typ := t.lookup_type(field_type_name) {
1515 expected_field_type = expected_typ
1516 has_expected_field_type = true
1517 }
1518 } else {
1519 // Fallback to direct type lookup from the init expression type.
1520 field_type_name = t.get_init_expr_field_type_name(expr.typ, lookup_field_name)
1521 if field_type_name != '' {
1522 if expected_typ := t.lookup_type(field_type_name) {
1523 expected_field_type = expected_typ
1524 has_expected_field_type = true
1525 }
1526 }
1527 }
1528 mut field_value := field.value
1529 is_sumtype_field := t.is_sum_type(field_type_name)
1530 mut use_direct_sumtype_value := false
1531 mut direct_sumtype_value := ast.empty_expr
1532 if has_expected_field_type {
1533 field_value = t.resolve_expr_with_expected_type(field_value, expected_field_type)
1534 fv_pos := field_value.pos()
1535 if !is_sumtype_field && fv_pos.id != 0 {
1536 t.synth_types[fv_pos.id] = expected_field_type
1537 }
1538 if t.is_eval_backend() {
1539 if expected_field_type is types.OptionType
1540 || expected_field_type is types.ResultType {
1541 base_type_name := t.type_to_c_name(expected_field_type.base_type())
1542 if t.is_sum_type(base_type_name) {
1543 if wrapped := t.wrap_sumtype_value(field_value, base_type_name) {
1544 pretransformed_value = wrapped
1545 has_pretransformed_value = true
1546 }
1547 }
1548 }
1549 }
1550 }
1551 if is_sumtype_field {
1552 if direct_value := t.transform_declared_sumtype_value(field_value, field_type_name) {
1553 direct_sumtype_value = direct_value
1554 use_direct_sumtype_value = true
1555 }
1556 // This is a sum type field - wrap the value in sum type initialization
1557 if !use_direct_sumtype_value {
1558 if wrapped := t.wrap_sumtype_value(field_value, field_type_name) {
1559 fields << ast.FieldInit{
1560 name: field.name
1561 value: wrapped
1562 }
1563 continue
1564 }
1565 }
1566 }
1567
1568 transformed_value := if has_pretransformed_value {
1569 pretransformed_value
1570 } else if use_direct_sumtype_value {
1571 direct_sumtype_value
1572 } else if field_value is ast.ArrayInitExpr {
1573 arr_value := field_value as ast.ArrayInitExpr
1574 // If the array has len/cap but no literal elements (e.g., []int{len: 4}),
1575 // use the normal transform_expr path which handles __new_array_with_default_noscan
1576 if arr_value.exprs.len == 0
1577 && (arr_value.len !is ast.EmptyExpr || arr_value.cap !is ast.EmptyExpr) {
1578 t.transform_expr(field_value)
1579 } else {
1580 // Transform array elements with sumtype wrapping if needed.
1581 elem_sumtype := t.get_field_array_elem_sumtype_name(struct_type_name,
1582 lookup_field_name)
1583 elem_interface_typ := t.get_field_array_elem_interface_type(struct_type_name,
1584 lookup_field_name)
1585 mut new_exprs := []ast.Expr{cap: arr_value.exprs.len}
1586 for e in arr_value.exprs {
1587 mut transformed := t.transform_expr(e)
1588 if elem_sumtype != '' {
1589 if wrapped := t.wrap_sumtype_value_transformed(transformed, elem_sumtype) {
1590 new_exprs << wrapped
1591 continue
1592 }
1593 }
1594 if iface_typ := elem_interface_typ {
1595 if !t.is_interface_cast(transformed) {
1596 transformed = ast.Expr(ast.CallOrCastExpr{
1597 lhs: t.type_to_ast_type_expr(iface_typ)
1598 expr: transformed
1599 })
1600 }
1601 }
1602 new_exprs << transformed
1603 }
1604 // Set elem type from struct field so
1605 // transform_array_init_with_exprs uses the correct element type.
1606 // This is critical when elements were wrapped in a sum type above,
1607 // as the C array type must match the wrapped (sum type) elements.
1608 mut arr_with_type := arr_value
1609 elem_c_name := t.get_field_array_elem_c_name(struct_type_name, lookup_field_name)
1610 if elem_c_name != '' {
1611 arr_with_type = ast.ArrayInitExpr{
1612 typ: ast.Expr(ast.Type(ast.ArrayType{
1613 elem_type: ast.Ident{
1614 name: elem_c_name
1615 }
1616 }))
1617 exprs: arr_value.exprs
1618 init: arr_value.init
1619 cap: arr_value.cap
1620 len: arr_value.len
1621 pos: arr_value.pos
1622 }
1623 }
1624 // Use transform_array_init_with_exprs which handles both fixed and dynamic:
1625 // - Fixed arrays stay as ArrayInitExpr for cleanc
1626 // - Dynamic arrays are lowered to builtin__new_array_from_c_array_noscan
1627 t.transform_array_init_with_exprs(arr_with_type, new_exprs)
1628 }
1629 } else {
1630 t.transform_expr(field_value)
1631 }
1632 final_value := if has_expected_field_type {
1633 t.deref_init_field_value_if_needed(transformed_value, expected_field_type)
1634 } else {
1635 transformed_value
1636 }
1637 field_name := t.rewrite_embedded_default_field_name(struct_type_name, field.name)
1638 fields << ast.FieldInit{
1639 name: field_name
1640 value: final_value
1641 }
1642 }
1643 fields = t.add_missing_struct_field_defaults(struct_type_name, fields)
1644
1645 return ast.InitExpr{
1646 typ: init_typ_expr
1647 fields: fields
1648 pos: expr.pos
1649 }
1650}
1651
1652fn (t &Transformer) struct_positional_field_names(struct_name string) []string {
1653 if struct_name == '' {
1654 return []string{}
1655 }
1656 struct_type := t.lookup_type(struct_name) or { return []string{} }
1657 base_type := t.unwrap_alias_and_pointer_type(struct_type)
1658 if base_type !is types.Struct {
1659 return []string{}
1660 }
1661 info := base_type as types.Struct
1662 mut names := []string{cap: info.fields.len}
1663 for field in info.fields {
1664 names << field.name
1665 }
1666 return names
1667}
1668
1669fn (t &Transformer) type_resolves_to_pointer(typ types.Type) bool {
1670 mut cur := typ
1671 for {
1672 match cur {
1673 types.Pointer {
1674 return true
1675 }
1676 types.Alias {
1677 cur = t.live_alias_base_type(cur) or { return false }
1678 }
1679 else {
1680 return false
1681 }
1682 }
1683 }
1684 return false
1685}
1686
1687fn (t &Transformer) deref_init_field_value_if_needed(value ast.Expr, expected types.Type) ast.Expr {
1688 if t.type_resolves_to_pointer(expected) {
1689 return value
1690 }
1691 expected_base := t.unwrap_alias_and_pointer_type(expected)
1692 expected_base_c := t.type_to_c_name(expected_base)
1693 value_typ := t.get_expr_type(value) or { return value }
1694 if value_typ is types.Pointer {
1695 value_base := t.unwrap_alias_and_pointer_type(value_typ.base_type)
1696 value_base_c := t.type_to_c_name(value_base)
1697 value_short := if value_base_c.contains('__') {
1698 value_base_c.all_after_last('__')
1699 } else {
1700 value_base_c
1701 }
1702 expected_short := if expected_base_c.contains('__') {
1703 expected_base_c.all_after_last('__')
1704 } else {
1705 expected_base_c
1706 }
1707 if expected_base_c == value_base_c || expected_short == value_short {
1708 return ast.PrefixExpr{
1709 op: .mul
1710 expr: value
1711 }
1712 }
1713 }
1714 return value
1715}
1716
1717fn (t &Transformer) get_init_expr_field_type(init_typ_expr ast.Expr, field_name string) ?types.Type {
1718 if !transformer_string_has_valid_data(field_name) {
1719 return none
1720 }
1721 if field_typ := t.generic_init_expr_field_type(init_typ_expr, field_name) {
1722 return field_typ
1723 }
1724 init_typ := t.get_expr_type(init_typ_expr) or { return none }
1725 base_typ := t.unwrap_alias_and_pointer_type(init_typ)
1726 if base_typ is types.Struct {
1727 if field_typ := t.lookup_struct_field_type(base_typ.name, field_name) {
1728 return field_typ
1729 }
1730 }
1731 return none
1732}
1733
1734fn (t &Transformer) get_init_expr_field_type_name(init_typ_expr ast.Expr, field_name string) string {
1735 if !transformer_string_has_valid_data(field_name) {
1736 return ''
1737 }
1738 if field_typ := t.generic_init_expr_field_type(init_typ_expr, field_name) {
1739 return t.type_to_name(field_typ)
1740 }
1741 init_typ := t.get_expr_type(init_typ_expr) or { return '' }
1742 base_typ := t.unwrap_alias_and_pointer_type(init_typ)
1743 if base_typ is types.Struct {
1744 if field_typ := t.lookup_struct_field_type(base_typ.name, field_name) {
1745 return t.type_to_name(field_typ)
1746 }
1747 }
1748 return ''
1749}
1750
1751fn (t &Transformer) generic_init_expr_field_type(init_typ_expr ast.Expr, field_name string) ?types.Type {
1752 mut lhs := ast.empty_expr
1753 mut args := []ast.Expr{}
1754 match init_typ_expr {
1755 ast.GenericArgs {
1756 lhs = init_typ_expr.lhs
1757 args = init_typ_expr.args.clone()
1758 }
1759 ast.GenericArgOrIndexExpr {
1760 lhs = init_typ_expr.lhs
1761 args = [init_typ_expr.expr]
1762 }
1763 ast.IndexExpr {
1764 lhs = init_typ_expr.lhs
1765 args = [init_typ_expr.expr]
1766 }
1767 else {
1768 return none
1769 }
1770 }
1771
1772 base := t.lookup_type_from_expr(lhs) or { return none }
1773 if base is types.Struct {
1774 field := struct_field_by_name(base, field_name) or { return none }
1775 bindings := t.generic_type_arg_bindings(base.generic_params, args) or { return field.typ }
1776 return substitute_type(field.typ, bindings)
1777 }
1778 return none
1779}
1780
1781fn (mut t Transformer) add_missing_struct_field_defaults(struct_name string, fields []ast.FieldInit) []ast.FieldInit {
1782 if struct_name == '' {
1783 return fields
1784 }
1785 struct_type := t.lookup_type(struct_name) or {
1786 if struct_name.contains('Scope') || struct_name.contains('DenseArray')
1787 || struct_name.contains('Env') {
1788 }
1789 return fields
1790 }
1791 base_type := t.unwrap_alias_and_pointer_type(struct_type)
1792 if base_type !is types.Struct {
1793 if struct_name.contains('Scope') || struct_name.contains('DenseArray')
1794 || struct_name.contains('Env') {
1795 }
1796 return fields
1797 }
1798 struct_info := base_type as types.Struct
1799 mut existing := map[string]bool{}
1800 mut positional_idx := 0
1801 for field in fields {
1802 if field.name == '' {
1803 // Positional field — map it to the corresponding struct field name
1804 if positional_idx < struct_info.fields.len {
1805 positional_name := struct_info.fields[positional_idx].name
1806 existing[positional_name] = true
1807 existing[t.rewrite_embedded_default_field_name(struct_name, positional_name)] = true
1808 }
1809 positional_idx++
1810 } else {
1811 existing[field.name] = true
1812 existing[t.rewrite_embedded_default_field_name(struct_name, field.name)] = true
1813 }
1814 }
1815 mut out := []ast.FieldInit{cap: fields.len}
1816 for field in fields {
1817 out << field
1818 }
1819 for struct_field in struct_info.fields {
1820 field_name := t.rewrite_embedded_default_field_name(struct_name, struct_field.name)
1821 if struct_field.name in existing || field_name in existing {
1822 continue
1823 }
1824 if struct_field.default_expr !is ast.EmptyExpr
1825 && t.is_supported_struct_default_expr(struct_field.default_expr) {
1826 out << ast.FieldInit{
1827 name: field_name
1828 value: t.transform_struct_field_default_value(struct_name,
1829 struct_field.default_expr, struct_field.typ)
1830 }
1831 continue
1832 }
1833 if t.is_pointer_type(struct_field.typ) {
1834 continue
1835 }
1836 field_type := t.unwrap_alias_and_pointer_type(struct_field.typ)
1837 if field_type is types.Map {
1838 if struct_name.contains('Scope') || struct_name.contains('Env') {
1839 }
1840 map_init := ast.Expr(ast.MapInitExpr{
1841 typ: t.type_to_ast_type_expr(field_type)
1842 })
1843 out << ast.FieldInit{
1844 name: field_name
1845 value: t.transform_expr(map_init)
1846 }
1847 continue
1848 }
1849 if field_type is types.Array {
1850 array_init := ast.Expr(ast.ArrayInitExpr{
1851 typ: t.type_to_ast_type_expr(field_type)
1852 })
1853 out << ast.FieldInit{
1854 name: field_name
1855 value: t.transform_expr(array_init)
1856 }
1857 continue
1858 }
1859 if field_type is types.OptionType {
1860 option_none := ast.Expr(ast.InitExpr{
1861 typ: t.type_to_ast_type_expr(field_type)
1862 fields: [
1863 ast.FieldInit{
1864 name: 'state'
1865 value: ast.BasicLiteral{
1866 kind: token.Token.number
1867 value: '2'
1868 }
1869 },
1870 ]
1871 })
1872 out << ast.FieldInit{
1873 name: field_name
1874 value: t.transform_expr(option_none)
1875 }
1876 continue
1877 }
1878 if field_type is types.String {
1879 out << ast.FieldInit{
1880 name: field_name
1881 value: ast.StringLiteral{
1882 kind: .v
1883 value: "''"
1884 }
1885 }
1886 }
1887 }
1888 // Also fill defaults for fields from embedded structs.
1889 for emb in struct_info.embedded {
1890 resolved_emb := t.resolve_embedded_struct_for_default_fields(struct_name, emb)
1891 for struct_field in resolved_emb.fields {
1892 raw_field_name := embedded_default_field_name_with_owner(emb.name, struct_field.name)
1893 field_name := t.embedded_default_field_name(struct_name, emb.name, struct_field.name)
1894 if struct_field.name in existing || raw_field_name in existing || field_name in existing {
1895 continue
1896 }
1897 if struct_field.default_expr !is ast.EmptyExpr
1898 && t.is_supported_struct_default_expr(struct_field.default_expr) {
1899 mut emb_struct_name := struct_name
1900 if resolved_emb.name != '' {
1901 emb_struct_name = t.type_to_c_name(types.Type(resolved_emb))
1902 }
1903 out << ast.FieldInit{
1904 name: field_name
1905 value: t.transform_struct_field_default_value(emb_struct_name,
1906 struct_field.default_expr, struct_field.typ)
1907 }
1908 continue
1909 }
1910 if t.is_pointer_type(struct_field.typ) {
1911 continue
1912 }
1913 field_type := t.unwrap_alias_and_pointer_type(struct_field.typ)
1914 if field_type is types.Map {
1915 map_init := ast.Expr(ast.MapInitExpr{
1916 typ: t.type_to_ast_type_expr(field_type)
1917 })
1918 out << ast.FieldInit{
1919 name: field_name
1920 value: t.transform_expr(map_init)
1921 }
1922 continue
1923 }
1924 if field_type is types.Array {
1925 array_init := ast.Expr(ast.ArrayInitExpr{
1926 typ: t.type_to_ast_type_expr(field_type)
1927 })
1928 out << ast.FieldInit{
1929 name: field_name
1930 value: t.transform_expr(array_init)
1931 }
1932 continue
1933 }
1934 if field_type is types.OptionType {
1935 option_none := ast.Expr(ast.InitExpr{
1936 typ: t.type_to_ast_type_expr(field_type)
1937 fields: [
1938 ast.FieldInit{
1939 name: 'state'
1940 value: ast.BasicLiteral{
1941 kind: token.Token.number
1942 value: '2'
1943 }
1944 },
1945 ]
1946 })
1947 out << ast.FieldInit{
1948 name: field_name
1949 value: t.transform_expr(option_none)
1950 }
1951 continue
1952 }
1953 if field_type is types.String {
1954 out << ast.FieldInit{
1955 name: field_name
1956 value: ast.StringLiteral{
1957 kind: .v
1958 value: "''"
1959 }
1960 }
1961 }
1962 }
1963 if resolved_emb.fields.len == 0 {
1964 if info := t.resolve_embedded_decl_for_default_fields(struct_name, emb) {
1965 out = t.add_missing_embedded_decl_defaults(struct_name, emb, info, out, existing)
1966 }
1967 }
1968 }
1969 return out
1970}
1971
1972fn embedded_default_field_name_with_owner(embedded_name string, field_name string) string {
1973 if field_name.contains('.') {
1974 return field_name
1975 }
1976 owner := embedded_concrete_owner_name_from_type_name(embedded_name)
1977 if owner == '' {
1978 return field_name
1979 }
1980 return '${owner}.${field_name}'
1981}
1982
1983fn (t &Transformer) embedded_default_field_name(parent_struct_name string, embedded_name string, field_name string) string {
1984 if field_name.contains('.') {
1985 return t.rewrite_embedded_default_field_name(parent_struct_name, field_name)
1986 }
1987 mut owner := embedded_concrete_owner_name_from_type_name(embedded_name)
1988 if concrete_owner := t.concrete_embedded_owner_name(parent_struct_name, owner) {
1989 owner = concrete_owner
1990 }
1991 if owner == '' {
1992 return field_name
1993 }
1994 return '${owner}.${field_name}'
1995}
1996
1997fn (t &Transformer) resolve_embedded_struct_for_default_fields(parent_struct_name string, embedded types.Struct) types.Struct {
1998 owner := embedded_concrete_owner_name_from_type_name(embedded.name)
1999 if concrete_owner := t.concrete_embedded_owner_name(parent_struct_name, owner) {
2000 if concrete_owner != owner {
2001 module_name := embedded_struct_module_name(embedded.name, owner)
2002 if resolved := t.lookup_struct_type_in_module(module_name, concrete_owner) {
2003 return resolved
2004 }
2005 }
2006 }
2007 return t.live_embedded_struct_type(embedded)
2008}
2009
2010fn (t &Transformer) resolve_embedded_decl_for_default_fields(parent_struct_name string, embedded types.Struct) ?StructDefaultDeclInfo {
2011 owner := embedded_concrete_owner_name_from_type_name(embedded.name)
2012 if concrete_owner := t.concrete_embedded_owner_name(parent_struct_name, owner) {
2013 if concrete_owner != owner {
2014 module_name := embedded_struct_module_name(embedded.name, owner)
2015 if module_name != '' {
2016 if info := t.lookup_struct_default_decl_info('${module_name}__${concrete_owner}') {
2017 return info
2018 }
2019 }
2020 if info := t.lookup_struct_default_decl_info(concrete_owner) {
2021 return info
2022 }
2023 }
2024 }
2025 if info := t.lookup_struct_default_decl_info(embedded.name) {
2026 return info
2027 }
2028 if embedded.name.contains('__') {
2029 return t.lookup_struct_default_decl_info(embedded.name.all_after_last('__'))
2030 }
2031 return none
2032}
2033
2034fn (t &Transformer) lookup_struct_default_decl_info(name string) ?StructDefaultDeclInfo {
2035 if name == '' {
2036 return none
2037 }
2038 return t.struct_default_decl_infos[name] or { return none }
2039}
2040
2041fn (mut t Transformer) add_missing_embedded_decl_defaults(parent_struct_name string, embedded types.Struct, info StructDefaultDeclInfo, fields []ast.FieldInit, existing map[string]bool) []ast.FieldInit {
2042 mut out := fields.clone()
2043 base_owner := embedded_concrete_owner_name_from_type_name(embedded.name)
2044 mut concrete_owner := base_owner
2045 if rewritten_owner := t.concrete_embedded_owner_name(parent_struct_name, base_owner) {
2046 concrete_owner = rewritten_owner
2047 }
2048 emb_struct_name := generic_struct_decl_c_name(info.decl, info.module_name)
2049 for field in info.decl.fields {
2050 if field.name == '' {
2051 continue
2052 }
2053 raw_field_name := '${base_owner}.${field.name}'
2054 field_name := '${concrete_owner}.${field.name}'
2055 if raw_field_name in existing || field_name in existing {
2056 continue
2057 }
2058 if field.value !is ast.EmptyExpr && t.is_supported_struct_default_expr(field.value) {
2059 field_type := t.struct_decl_field_type(info, field) or { continue }
2060 out << ast.FieldInit{
2061 name: field_name
2062 value: t.transform_struct_field_default_value(emb_struct_name, field.value,
2063 field_type)
2064 }
2065 continue
2066 }
2067 field_type := t.struct_decl_field_type(info, field) or { continue }
2068 if t.is_pointer_type(field_type) {
2069 continue
2070 }
2071 base_type := t.unwrap_alias_and_pointer_type(field_type)
2072 if base_type is types.Map {
2073 map_init := ast.Expr(ast.MapInitExpr{
2074 typ: t.type_to_ast_type_expr(base_type)
2075 })
2076 out << ast.FieldInit{
2077 name: field_name
2078 value: t.transform_expr(map_init)
2079 }
2080 continue
2081 }
2082 if base_type is types.Array {
2083 array_init := ast.Expr(ast.ArrayInitExpr{
2084 typ: t.type_to_ast_type_expr(base_type)
2085 })
2086 out << ast.FieldInit{
2087 name: field_name
2088 value: t.transform_expr(array_init)
2089 }
2090 continue
2091 }
2092 if base_type is types.OptionType {
2093 option_none := ast.Expr(ast.InitExpr{
2094 typ: t.type_to_ast_type_expr(base_type)
2095 fields: [
2096 ast.FieldInit{
2097 name: 'state'
2098 value: ast.BasicLiteral{
2099 kind: token.Token.number
2100 value: '2'
2101 }
2102 },
2103 ]
2104 })
2105 out << ast.FieldInit{
2106 name: field_name
2107 value: t.transform_expr(option_none)
2108 }
2109 continue
2110 }
2111 if base_type is types.String {
2112 out << ast.FieldInit{
2113 name: field_name
2114 value: ast.StringLiteral{
2115 kind: .v
2116 value: "''"
2117 }
2118 }
2119 }
2120 }
2121 return out
2122}
2123
2124fn (mut t Transformer) struct_decl_field_type(info StructDefaultDeclInfo, field ast.FieldDecl) ?types.Type {
2125 old_module := t.cur_module
2126 old_scope := t.scope
2127 t.cur_module = info.module_name
2128 if scope := t.get_module_scope(info.module_name) {
2129 t.scope = scope
2130 } else {
2131 t.scope = unsafe { nil }
2132 }
2133 field_type := t.lookup_type_from_expr(field.typ) or {
2134 t.cur_module = old_module
2135 t.scope = old_scope
2136 return none
2137 }
2138 t.cur_module = old_module
2139 t.scope = old_scope
2140 return field_type
2141}
2142
2143fn embedded_struct_module_name(type_name string, owner string) string {
2144 if type_name == '' || owner == '' {
2145 return ''
2146 }
2147 suffix := '__${owner}'
2148 if type_name.ends_with(suffix) {
2149 return type_name[..type_name.len - suffix.len]
2150 }
2151 if type_name.contains('__') {
2152 return type_name.all_before_last('__')
2153 }
2154 return ''
2155}
2156
2157fn (t &Transformer) lookup_struct_type_in_module(module_name string, type_name string) ?types.Struct {
2158 if module_name != '' {
2159 for candidate in [module_name, module_name.replace('__', '.')] {
2160 if scope := t.get_module_scope(candidate) {
2161 if typ := scope.lookup_type(type_name) {
2162 if typ is types.Struct {
2163 return typ
2164 }
2165 }
2166 }
2167 }
2168 }
2169 if typ := t.lookup_type(type_name) {
2170 if typ is types.Struct {
2171 return typ
2172 }
2173 }
2174 return none
2175}
2176
2177fn (mut t Transformer) transform_struct_field_default_value(struct_name string, expr ast.Expr, expected types.Type) ast.Expr {
2178 resolved := t.resolve_expr_with_expected_type(expr, expected)
2179 base := t.unwrap_alias_and_pointer_type(expected)
2180 if base is types.Interface && !t.is_interface_cast(resolved) {
2181 return t.transform_expr(ast.CallOrCastExpr{
2182 lhs: t.type_to_ast_type_expr(expected)
2183 expr: resolved
2184 pos: resolved.pos()
2185 })
2186 }
2187 return t.transform_struct_field_default_expr(struct_name, resolved)
2188}
2189
2190fn (mut t Transformer) transform_struct_field_default_expr(struct_name string, expr ast.Expr) ast.Expr {
2191 if struct_name.contains('__') {
2192 module_name := struct_name.all_before_last('__')
2193 if module_name != '' && module_name != t.cur_module {
2194 // Cross-module const defaults like `ast.empty_expr` must be qualified.
2195 if expr is ast.Ident {
2196 if expr.name.contains('__') {
2197 return expr
2198 }
2199 return ast.SelectorExpr{
2200 lhs: ast.Ident{
2201 name: module_name
2202 }
2203 rhs: expr
2204 }
2205 }
2206 old_module := t.cur_module
2207 t.cur_module = module_name
2208 transformed := t.transform_expr(expr)
2209 t.cur_module = old_module
2210 return transformed
2211 }
2212 }
2213 return t.transform_expr(expr)
2214}
2215
2216fn (t &Transformer) get_init_expr_type_name(typ ast.Expr) string {
2217 if generic_type_name := t.generic_init_type_name(typ) {
2218 return t.qualify_type_name(generic_type_name)
2219 }
2220 if typ is ast.Ident {
2221 if synth_typ := t.get_synth_type(typ.pos) {
2222 return t.type_to_c_name(synth_typ)
2223 }
2224 base_name := typ.name
2225 if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
2226 if scope := t.cached_scopes[t.cur_module] {
2227 if obj := scope.objects[base_name] {
2228 if obj is types.Type {
2229 return '${t.cur_module}__${base_name}'
2230 }
2231 }
2232 }
2233 if t.lookup_method_cached('${t.cur_module}__${base_name}', 'msg') != none {
2234 return '${t.cur_module}__${base_name}'
2235 }
2236 }
2237 return t.qualify_type_name(base_name)
2238 }
2239 if typ is ast.SelectorExpr {
2240 // Module-qualified: os.Eof -> os__Eof
2241 if typ.lhs is ast.Ident {
2242 return '${typ.lhs.name}__${typ.rhs.name}'
2243 }
2244 return typ.rhs.name
2245 }
2246 return ''
2247}
2248
2249// is_error_type_name checks if a type implements IError
2250// This includes types that embed Error OR types that have msg() method
2251fn (t &Transformer) get_struct_field_type_name(struct_name string, field_name string) string {
2252 // Look up the struct type in scopes.
2253 // For qualified names (e.g. "ast__CallExpr"), try the module scope directly first.
2254 dunder := struct_name.index('__') or { -1 }
2255 if dunder >= 0 {
2256 mod_name := struct_name[..dunder]
2257 last_dunder := struct_name.last_index('__') or { dunder }
2258 short_name := struct_name[last_dunder + 2..]
2259 if mod_scope := t.cached_scopes[mod_name] {
2260 if obj := mod_scope.objects[short_name] {
2261 if obj is types.Type {
2262 result := t.get_field_type_name(obj, field_name)
2263 if result != '' {
2264 return result
2265 }
2266 }
2267 }
2268 // Also try the full qualified name
2269 if obj := mod_scope.objects[struct_name] {
2270 if obj is types.Type {
2271 result := t.get_field_type_name(obj, field_name)
2272 if result != '' {
2273 return result
2274 }
2275 }
2276 }
2277 }
2278 }
2279 // Prefer the current module scope for non-qualified names
2280 // to avoid collisions (e.g., ast.FnType vs types.FnType both named "FnType").
2281 if dunder < 0 && t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
2282 if cur_scope := t.cached_scopes[t.cur_module] {
2283 if obj := cur_scope.objects[struct_name] {
2284 if obj is types.Type {
2285 result := t.get_field_type_name(obj, field_name)
2286 if result != '' {
2287 return result
2288 }
2289 }
2290 }
2291 }
2292 }
2293 // Fallback: scan all scopes
2294 scope_keys2 := t.cached_scopes.keys()
2295 for sk in scope_keys2 {
2296 scope := t.cached_scopes[sk] or { continue }
2297 if obj := scope.objects[struct_name] {
2298 if obj is types.Type {
2299 return t.get_field_type_name(obj, field_name)
2300 }
2301 }
2302 if dunder >= 0 {
2303 short_name := struct_name[struct_name.last_index('__') or { dunder } + 2..]
2304 if obj := scope.objects[short_name] {
2305 if obj is types.Type {
2306 return t.get_field_type_name(obj, field_name)
2307 }
2308 }
2309 } else if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
2310 mangled := '${t.cur_module}__${struct_name}'
2311 if obj := scope.objects[mangled] {
2312 if obj is types.Type {
2313 return t.get_field_type_name(obj, field_name)
2314 }
2315 }
2316 }
2317 }
2318 return ''
2319}
2320
2321// wrap_sumtype_value wraps a value in sum type initialization if needed
2322// Returns the wrapped expression or none if the value type couldn't be determined
2323fn (t &Transformer) resolve_field_type(var_name string, field_name string) string {
2324 // First, check if variable is already an enum type
2325 if _ := t.is_var_enum(var_name) {
2326 // Variable is already an enum, no field access needed
2327 return ''
2328 }
2329
2330 // Check if variable is smartcasted - use the smartcast variant type
2331 if ctx := t.find_smartcast_for_expr(var_name) {
2332 return t.resolve_struct_field_type(ctx.variant, field_name)
2333 }
2334
2335 // Look up variable type from scope
2336 var_type_name := t.get_var_type_name(var_name)
2337 if var_type_name != '' {
2338 // Strip pointer prefix/suffix for struct lookup
2339 mut clean_type := var_type_name
2340 if clean_type.starts_with('&') {
2341 clean_type = clean_type[1..]
2342 }
2343 if clean_type.ends_with('*') {
2344 clean_type = clean_type[..clean_type.len - 1]
2345 }
2346 return t.resolve_struct_field_type(clean_type, field_name)
2347 }
2348
2349 // Look up the variable in the current module's scope
2350 mut scope := t.get_current_scope() or { return '' }
2351 obj := scope.lookup_parent(var_name, 0) or { return '' }
2352
2353 // Get the variable's type
2354 var_type := obj.typ()
2355 return t.get_field_type_name(var_type, field_name)
2356}
2357
2358// resolve_struct_field_type looks up a field type given a struct type name
2359fn (t &Transformer) resolve_struct_field_type(struct_name string, field_name string) string {
2360 // Look up the struct type in scopes
2361 // Handle qualified names like "ast__SelectorExpr" - extract module and type name
2362 mut lookup_name := struct_name
2363 mut lookup_module := ''
2364 dunder := struct_name.index('__') or { -1 }
2365 if dunder >= 0 {
2366 lookup_module = struct_name[..dunder]
2367 last_dunder := struct_name.last_index('__') or { dunder }
2368 lookup_name = struct_name[last_dunder + 2..]
2369 }
2370 // Fast path: try the target module scope directly
2371 if lookup_module != '' {
2372 if mod_scope := t.cached_scopes[lookup_module] {
2373 if obj := mod_scope.objects[lookup_name] {
2374 if obj is types.Type {
2375 result := t.get_field_type_name(obj, field_name)
2376 if result != '' {
2377 return result
2378 }
2379 }
2380 }
2381 if obj := mod_scope.objects[struct_name] {
2382 if obj is types.Type {
2383 result := t.get_field_type_name(obj, field_name)
2384 if result != '' {
2385 return result
2386 }
2387 }
2388 }
2389 }
2390 }
2391 // Try current module scope
2392 if t.cur_module != '' {
2393 if cur_scope := t.cached_scopes[t.cur_module] {
2394 if obj := cur_scope.objects[struct_name] {
2395 if obj is types.Type {
2396 result := t.get_field_type_name(obj, field_name)
2397 if result != '' {
2398 return result
2399 }
2400 }
2401 }
2402 if obj := cur_scope.objects[lookup_name] {
2403 if obj is types.Type {
2404 result := t.get_field_type_name(obj, field_name)
2405 if result != '' {
2406 return result
2407 }
2408 }
2409 }
2410 }
2411 }
2412 // Fallback: scan all scopes
2413 scope_keys3 := t.cached_scopes.keys()
2414 for sk in scope_keys3 {
2415 scope := t.cached_scopes[sk] or { continue }
2416 if obj := scope.objects[struct_name] {
2417 if obj is types.Type {
2418 return t.get_field_type_name(obj, field_name)
2419 }
2420 }
2421 if obj := scope.objects[lookup_name] {
2422 if obj is types.Type {
2423 return t.get_field_type_name(obj, field_name)
2424 }
2425 }
2426 }
2427 return ''
2428}
2429
2430// get_field_type_name gets the type name of a field from a Type
2431fn (t &Transformer) get_field_type_name(typ types.Type, field_name string) string {
2432 if typ is types.Struct {
2433 if field_type := t.struct_field_type(typ, field_name) {
2434 return t.type_to_name(field_type)
2435 }
2436 }
2437 if typ is types.Pointer {
2438 // Dereference pointer and recurse
2439 return t.get_field_type_name(typ.base_type, field_name)
2440 }
2441 return ''
2442}
2443
2444// get_field_array_elem_sumtype_name returns the sum type name of the array element type
2445// for a struct field, if the field is an array of sum types. Returns '' otherwise.
2446fn (t &Transformer) get_field_array_elem_sumtype_name(struct_name string, field_name string) string {
2447 // Compute short name once outside the loop
2448 dunder := struct_name.index('__') or { -1 }
2449 short_name := if dunder >= 0 {
2450 last_dunder := struct_name.last_index('__') or { dunder }
2451 struct_name[last_dunder + 2..]
2452 } else {
2453 struct_name
2454 }
2455 // Try module scope directly first for qualified names
2456 if dunder >= 0 {
2457 mod_name := struct_name[..dunder]
2458 if mod_scope := t.cached_scopes[mod_name] {
2459 if obj := mod_scope.objects[short_name] {
2460 if obj is types.Type {
2461 if obj is types.Struct {
2462 for field in obj.fields {
2463 if field.name == field_name {
2464 if field.typ is types.Array {
2465 field_arr := field.typ as types.Array
2466 elem_name := t.type_to_name(field_arr.elem_type)
2467 if t.is_sum_type(elem_name) {
2468 return elem_name
2469 }
2470 }
2471 return ''
2472 }
2473 }
2474 }
2475 }
2476 }
2477 }
2478 }
2479 scope_keys4 := t.cached_scopes.keys()
2480 for sk in scope_keys4 {
2481 scope := t.cached_scopes[sk] or { continue }
2482 if obj := scope.objects[struct_name] {
2483 if obj is types.Type {
2484 if obj is types.Struct {
2485 for field in obj.fields {
2486 if field.name == field_name {
2487 if field.typ is types.Array {
2488 field_arr := field.typ as types.Array
2489 elem_name := t.type_to_name(field_arr.elem_type)
2490 if t.is_sum_type(elem_name) {
2491 return elem_name
2492 }
2493 }
2494 return ''
2495 }
2496 }
2497 }
2498 }
2499 }
2500 if dunder >= 0 {
2501 if obj := scope.objects[short_name] {
2502 if obj is types.Type {
2503 if obj is types.Struct {
2504 for field in obj.fields {
2505 if field.name == field_name {
2506 if field.typ is types.Array {
2507 field_arr := field.typ as types.Array
2508 elem_name := t.type_to_name(field_arr.elem_type)
2509 if t.is_sum_type(elem_name) {
2510 return elem_name
2511 }
2512 }
2513 return ''
2514 }
2515 }
2516 }
2517 }
2518 }
2519 }
2520 }
2521 return ''
2522}
2523
2524fn (t &Transformer) get_field_array_elem_interface_type(struct_name string, field_name string) ?types.Type {
2525 field_typ := t.lookup_struct_field_type(struct_name, field_name) or { return none }
2526 match field_typ {
2527 types.Array {
2528 et := field_typ.elem_type
2529 if et is types.Interface {
2530 return et
2531 }
2532 if et is types.Alias && et.base_type is types.Interface {
2533 return et
2534 }
2535 }
2536 types.Alias {
2537 if field_typ.base_type is types.Array {
2538 et := field_typ.base_type.elem_type
2539 if et is types.Interface {
2540 return et
2541 }
2542 if et is types.Alias && et.base_type is types.Interface {
2543 return et
2544 }
2545 }
2546 }
2547 else {}
2548 }
2549
2550 return none
2551}
2552
2553// get_field_array_elem_c_name returns the C type name for the element type of an array field
2554fn (t &Transformer) get_field_array_elem_c_name(struct_name string, field_name string) string {
2555 field_typ := t.lookup_struct_field_type(struct_name, field_name) or { return '' }
2556 if field_typ is types.Array {
2557 return t.type_to_c_name(field_typ.elem_type)
2558 }
2559 return ''
2560}
2561
2562// type_to_name converts a Type to its name string
2563fn (t &Transformer) get_struct_field_type(expr ast.SelectorExpr) ?types.Type {
2564 // Try to get the struct type from scope (for local variables and receivers)
2565 mut struct_type_name := ''
2566 if !transformer_string_has_valid_data(expr.rhs.name) {
2567 return none
2568 }
2569 if smartcast_type := t.smartcast_type_for_expr(expr.lhs) {
2570 if field_typ := t.field_type_from_receiver_type(smartcast_type, expr.rhs.name) {
2571 return field_typ
2572 }
2573 }
2574 if expr.lhs is ast.Ident {
2575 lhs_name := expr.lhs.name
2576 if !transformer_string_has_valid_data(lhs_name) {
2577 return none
2578 }
2579 if lhs_type := t.lookup_var_type(lhs_name) {
2580 if field_typ := t.field_type_from_receiver_type(lhs_type, expr.rhs.name) {
2581 return field_typ
2582 }
2583 base_type := lhs_type.base_type()
2584 if base_type is types.Struct {
2585 if field_typ := t.lookup_struct_field_type(base_type.name, expr.rhs.name) {
2586 return field_typ
2587 }
2588 }
2589 struct_type_name = t.type_to_name(base_type)
2590 }
2591 }
2592
2593 // If we have a type name, look it up in the environment
2594 if struct_type_name != '' {
2595 // Types are defined at module level, not function level
2596 // Use lookup_type which searches module scopes
2597 looked_up_type := t.lookup_type(struct_type_name) or { return none }
2598 base_type := if looked_up_type is types.Pointer {
2599 looked_up_type.base_type
2600 } else {
2601 looked_up_type
2602 }
2603 match base_type {
2604 types.Struct {
2605 if field_typ := t.lookup_struct_field_type(base_type.name, expr.rhs.name) {
2606 return field_typ
2607 }
2608 }
2609 else {}
2610 }
2611 }
2612
2613 // Fall back to get_expr_type for module-level lookups
2614 struct_type := t.get_expr_type(expr.lhs) or { return none }
2615
2616 // If it's a pointer, dereference to get the struct
2617 base_type := if struct_type is types.Pointer {
2618 struct_type.base_type
2619 } else {
2620 struct_type
2621 }
2622
2623 // Look up the field in the struct
2624 match base_type {
2625 types.Struct {
2626 if field_typ := t.lookup_struct_field_type(base_type.name, expr.rhs.name) {
2627 return field_typ
2628 }
2629 }
2630 else {}
2631 }
2632
2633 return none
2634}
2635
2636// expand_array_init_with_index expands `[]T{len: n, init: expr_using_index}` into:
2637// mut _awi_tN = __new_array_with_default_noscan(len, cap, sizeof(T), nil)
2638// for (_v_index = 0; _v_index < _awi_tN.len; _v_index++) {
2639// ((T*)_awi_tN.data)[_v_index] = init_expr (with `index` renamed to `_v_index`)
2640// }
2641// <returns _awi_tN ident>
2642fn (mut t Transformer) expand_array_init_with_index(len_expr ast.Expr, cap_expr ast.Expr, sizeof_expr ast.Expr, init_expr ast.Expr, _pos token.Pos) ast.Expr {
2643 t.temp_counter++
2644 arr_name := '_awi_t${t.temp_counter}'
2645 arr_ident := ast.Ident{
2646 name: arr_name
2647 }
2648 idx_ident := ast.Ident{
2649 name: '_v_index'
2650 }
2651
2652 // 1. mut _awi_tN = __new_array_with_default_noscan(len, cap, sizeof(T), nil)
2653 init_stmt := ast.Stmt(ast.AssignStmt{
2654 op: .decl_assign
2655 lhs: [
2656 ast.Expr(ast.ModifierExpr{
2657 kind: .key_mut
2658 expr: arr_ident
2659 }),
2660 ]
2661 rhs: [
2662 ast.Expr(ast.CallExpr{
2663 lhs: ast.Ident{
2664 name: '__new_array_with_default_noscan'
2665 }
2666 args: [
2667 len_expr,
2668 cap_expr,
2669 ast.Expr(ast.KeywordOperator{
2670 op: .key_sizeof
2671 exprs: [sizeof_expr]
2672 }),
2673 ast.Expr(ast.Ident{
2674 name: 'nil'
2675 }),
2676 ]
2677 }),
2678 ]
2679 })
2680
2681 // 2. Build assignment: ((T*)_awi_tN.data)[_v_index] = init_expr
2682 // with `index` renamed to `_v_index`
2683 renamed_init := t.replace_ident_named(init_expr, 'index', '_v_index')
2684 arr_data := t.synth_selector(arr_ident, 'data', types.Type(types.voidptr_))
2685 // Use a simple Ident for the cast type name so cleanc renders it as
2686 // ((ElemType*)data)[idx] without decomposing compound type expressions.
2687 cast_type_name := t.expr_to_type_name(sizeof_expr)
2688 elem_assign := ast.Stmt(ast.AssignStmt{
2689 op: .assign
2690 lhs: [
2691 ast.Expr(ast.IndexExpr{
2692 lhs: ast.CastExpr{
2693 typ: ast.Ident{
2694 name: '${cast_type_name}*'
2695 }
2696 expr: arr_data
2697 }
2698 expr: idx_ident
2699 }),
2700 ]
2701 rhs: [renamed_init]
2702 })
2703
2704 // 3. for (int _v_index = 0; _v_index < _awi_tN.len; _v_index++) { ... }
2705 for_stmt := ast.Stmt(ast.ForStmt{
2706 init: ast.AssignStmt{
2707 op: .decl_assign
2708 lhs: [ast.Expr(idx_ident)]
2709 rhs: [ast.Expr(ast.BasicLiteral{
2710 kind: .number
2711 value: '0'
2712 })]
2713 }
2714 cond: ast.InfixExpr{
2715 op: .lt
2716 lhs: idx_ident
2717 rhs: t.synth_selector(arr_ident, 'len', types.Type(types.int_))
2718 }
2719 post: ast.AssignStmt{
2720 op: .assign
2721 lhs: [ast.Expr(idx_ident)]
2722 rhs: [
2723 ast.Expr(ast.InfixExpr{
2724 op: .plus
2725 lhs: idx_ident
2726 rhs: ast.BasicLiteral{
2727 kind: .number
2728 value: '1'
2729 }
2730 }),
2731 ]
2732 }
2733 stmts: [elem_assign]
2734 })
2735
2736 // Emit init + for loop as pending statements
2737 t.pending_stmts << init_stmt
2738 t.pending_stmts << for_stmt
2739
2740 // Return the temp array ident as the expression value
2741 return arr_ident
2742}
2743
2744// replace_ident_named replaces all occurrences of an identifier named `old_name`
2745// with a new identifier named `new_name` in an expression tree.
2746fn (t &Transformer) replace_ident_named(expr ast.Expr, old_name string, new_name string) ast.Expr {
2747 match expr {
2748 ast.Ident {
2749 if expr.name == old_name {
2750 return ast.Ident{
2751 name: new_name
2752 pos: expr.pos
2753 }
2754 }
2755 return expr
2756 }
2757 ast.InfixExpr {
2758 return ast.InfixExpr{
2759 op: expr.op
2760 lhs: t.replace_ident_named(expr.lhs, old_name, new_name)
2761 rhs: t.replace_ident_named(expr.rhs, old_name, new_name)
2762 pos: expr.pos
2763 }
2764 }
2765 ast.PrefixExpr {
2766 return ast.PrefixExpr{
2767 op: expr.op
2768 expr: t.replace_ident_named(expr.expr, old_name, new_name)
2769 pos: expr.pos
2770 }
2771 }
2772 ast.ParenExpr {
2773 return ast.ParenExpr{
2774 expr: t.replace_ident_named(expr.expr, old_name, new_name)
2775 pos: expr.pos
2776 }
2777 }
2778 ast.CallExpr {
2779 mut new_args := []ast.Expr{cap: expr.args.len}
2780 for arg in expr.args {
2781 new_args << t.replace_ident_named(arg, old_name, new_name)
2782 }
2783 return ast.CallExpr{
2784 lhs: t.replace_ident_named(expr.lhs, old_name, new_name)
2785 args: new_args
2786 pos: expr.pos
2787 }
2788 }
2789 ast.CastExpr {
2790 return ast.CastExpr{
2791 typ: expr.typ
2792 expr: t.replace_ident_named(expr.expr, old_name, new_name)
2793 pos: expr.pos
2794 }
2795 }
2796 ast.IndexExpr {
2797 return ast.IndexExpr{
2798 lhs: t.replace_ident_named(expr.lhs, old_name, new_name)
2799 expr: t.replace_ident_named(expr.expr, old_name, new_name)
2800 pos: expr.pos
2801 }
2802 }
2803 ast.SelectorExpr {
2804 return ast.SelectorExpr{
2805 lhs: t.replace_ident_named(expr.lhs, old_name, new_name)
2806 rhs: expr.rhs
2807 pos: expr.pos
2808 }
2809 }
2810 ast.ModifierExpr {
2811 return ast.ModifierExpr{
2812 kind: expr.kind
2813 expr: t.replace_ident_named(expr.expr, old_name, new_name)
2814 pos: expr.pos
2815 }
2816 }
2817 else {
2818 return expr
2819 }
2820 }
2821}
2822
2823// get_array_type_str returns the Array_T type string for an array expression using checker type info.
2824