v / vlib / v2 / ssa / builder.v
22162 lines · 21115 sloc · 689.85 KB
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 ssa
6
7import v2.ast
8import v2.markused
9import v2.token
10import v2.types
11
12const enum_field_c_keywords = ['auto', 'break', 'case', 'char', 'const', 'continue', 'default',
13 'do', 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
14 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
15 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while', '_Bool', '_Complex', '_Imaginary',
16 'unix', 'linux']
17
18const fixed_array_empty_literal_element_store_threshold = 16
19
20fn enum_field_symbol_name(name string) string {
21 n := if name.len > 0 && name[0] == `@` { 'at_${name[1..]}' } else { name }
22 mut escaped := n.replace('*', 'ptr')
23 escaped = escaped.replace('&', 'ref')
24 escaped = escaped.replace('@', 'at_')
25 escaped = escaped.replace(' ', '_')
26 escaped = escaped.replace('.', '__')
27 if escaped in enum_field_c_keywords {
28 return '_${escaped}'
29 }
30 return escaped
31}
32
33fn normalize_target_os_name(target_os string) string {
34 return match target_os.to_lower() {
35 'macos', 'darwin' { 'macos' }
36 else { target_os.to_lower() }
37 }
38}
39
40fn (b &Builder) is_macos_target() bool {
41 return normalize_target_os_name(b.target_os) == 'macos'
42}
43
44fn (b &Builder) is_linux_target() bool {
45 return normalize_target_os_name(b.target_os) == 'linux'
46}
47
48fn (b &Builder) is_windows_target() bool {
49 return normalize_target_os_name(b.target_os) == 'windows'
50}
51
52fn (mut b Builder) build_c_errno_addr() ?ValueID {
53 if !b.is_macos_target() && !b.is_linux_target() && !b.is_windows_target() {
54 return none
55 }
56 i32_t := b.mod.type_store.get_int(32)
57 ptr_i32 := b.mod.type_store.get_ptr(i32_t)
58 err_fn_name := if b.is_macos_target() {
59 '__error'
60 } else if b.is_windows_target() {
61 '_errno'
62 } else {
63 '__errno_location'
64 }
65 err_fn := b.get_or_create_fn_ref(err_fn_name, ptr_i32)
66 return b.mod.add_instr(.call, b.cur_block, ptr_i32, [err_fn])
67}
68
69fn (mut b Builder) build_windows_c_macro_const(name string) ?ValueID {
70 if !b.is_windows_target() {
71 return none
72 }
73 // WinAPI GetStdHandle constants are DWORD macros, not linkable symbols.
74 win_std_handle_val := match name {
75 'STD_INPUT_HANDLE' { '4294967286' }
76 'STD_OUTPUT_HANDLE' { '4294967285' }
77 'STD_ERROR_HANDLE' { '4294967284' }
78 else { '' }
79 }
80
81 if win_std_handle_val.len > 0 {
82 return b.mod.get_or_add_const(b.mod.type_store.get_uint(32), win_std_handle_val)
83 }
84 if name == 'INVALID_HANDLE_VALUE' {
85 i8_t := b.mod.type_store.get_int(8)
86 voidptr_t := b.mod.type_store.get_ptr(i8_t)
87 return b.mod.get_or_add_const(voidptr_t, '-1')
88 }
89 return none
90}
91
92fn (mut b Builder) build_raw_c_global_addr(name string, typ TypeID) ValueID {
93 if glob_id := b.find_global(name) {
94 return glob_id
95 }
96 glob := b.mod.add_external_global(name, typ)
97 b.global_refs[name] = glob
98 return glob
99}
100
101fn (mut b Builder) build_c_errno_storage_addr() ValueID {
102 if errno_addr := b.build_c_errno_addr() {
103 return errno_addr
104 }
105 i32_t := b.mod.type_store.get_int(32)
106 return b.build_raw_c_global_addr('errno', i32_t)
107}
108
109fn ssa_module_storage_name(module_name string, name string) string {
110 if name == '' || name.starts_with('C.') {
111 return name
112 }
113 if module_name == 'C' {
114 return 'C.${name}'
115 }
116 if module_name != '' && module_name != 'main' {
117 return '${module_name}__${name}'
118 }
119 return name
120}
121
122fn ssa_module_storage_field_is_c_extern(node ast.GlobalDecl, field ast.FieldDecl) bool {
123 return field.name.starts_with('C.') || node.attributes.has('c_extern')
124 || field.attributes.has('c_extern')
125}
126
127// cursor_attrs_has (s237, fixed s246) reports whether an attribute list (a
128// CursorList of .aux_attribute nodes) contains an attribute named `name`.
129// Cursor mirror of `[]Attribute.has(name)`: an attribute matches when its
130// name_id equals `name`, OR (the `@[flag]`/`@[c_extern]` shape) when name_id is
131// empty and its value (edge 0) is an Ident whose name equals `name`. The earlier
132// name_id-only version silently missed `@[flag]`/`@[c_extern]`, since the parser
133// stores those as `Attribute{name:'', value: Ident{name:...}}`.
134fn cursor_attrs_has(attrs ast.CursorList, name string) bool {
135 for i in 0 .. attrs.len() {
136 attr := attrs.at(i)
137 if attr.name() == name {
138 return true
139 }
140 if attr.name() == '' {
141 val := attr.edge(0)
142 if val.kind() == .expr_ident && val.name() == name {
143 return true
144 }
145 }
146 }
147 return false
148}
149
150struct DynConstArray {
151 arr_global_name string // V array struct global name
152 data_global_name string // raw data global name
153 elem_count int
154 elem_size int
155}
156
157struct SelectorAddr {
158 addr ValueID
159 base_type TypeID
160}
161
162struct EmbeddedFieldSpan {
163 source_type TypeID
164 start int
165 len int
166}
167
168struct StructInitFieldPlan {
169 source_idx int
170 target_idx int
171 expand_len int
172 source_type TypeID
173}
174
175struct MatchExprIncoming {
176 value ValueID
177 block BlockID
178}
179
180struct MatchSumtypeBranchInfo {
181 tag int
182 variant_type TypeID
183}
184
185const unsupported_captured_fn_literal_symbol = 'v2_unsupported_captured_fn_literal'
186
187pub struct Builder {
188pub mut:
189 mod &Module
190 cur_module string = 'main'
191 // When set, build_fn_bodies only builds this function (hot code reload optimization)
192 hot_fn string
193 // When set, build_all skips Phase 4 (function bodies) — caller handles it.
194 skip_fn_bodies bool
195 // When set, only build functions whose decl key is in this map (dead code elimination).
196 used_fn_keys map[string]bool
197 // When set, skip all functions from these modules (dead code elimination for unused backends).
198 skip_modules map[string]bool
199 // Optional path fragments for skip_modules. When populated for a module,
200 // the skip only applies to files whose path matches the fragment.
201 skip_module_file_fragments map[string]string
202 // Native self-hosted builds use the SSA sumtype layout directly; guard
203 // against null large-variant payloads before matching types.Type.
204 guard_invalid_type_payloads bool
205 // Target OS for lowering target-specific C globals.
206 target_os string
207 // When set, native Windows PE builds rely on markused instead of retaining
208 // broad builtin runtime modules before linking.
209 minimal_runtime_roots bool
210 // When set, the backend zeroes large fixed-array allocas as a bulk operation,
211 // so SSA can keep IR size bounded by skipping per-element zero stores.
212 native_backend_bulk_zero_alloca bool
213mut:
214 env &types.Environment = unsafe { nil }
215 cur_func int = -1
216 cur_block BlockID = -1
217 // Checker function-scope key for the function currently being lowered.
218 cur_fn_scope_key string
219 // Variable name -> SSA ValueID (alloca pointer)
220 vars map[string]ValueID
221 // Loop break/continue targets
222 loop_stack []LoopInfo
223 // Struct name -> SSA TypeID
224 struct_types map[string]TypeID
225 // Flattened embedded field ranges by owner struct TypeID.
226 struct_embedded_spans map[int][]EmbeddedFieldSpan
227 // Type alias name -> resolved SSA base TypeID
228 type_aliases map[string]TypeID
229 // Enum name -> field values
230 enum_values map[string]int
231 // Function name -> SSA function index
232 fn_index map[string]int
233 module_fn_names map[string]string
234 // Function name -> dynamic array element type by argument index.
235 fn_param_array_elem_types map[string][]TypeID
236 // Function name -> SSA func_ref value
237 fn_refs map[string]ValueID
238 // Current file's selective imports: short symbol name -> primary mangled function name.
239 selective_import_fn_names map[string]string
240 // Current file's selective imports: short symbol name -> ordered mangled function candidates.
241 selective_import_fn_candidates map[string][]string
242 // Current file's module imports: local alias/tail name -> mangled module name.
243 module_import_aliases map[string]string
244 // Global variable name -> SSA global value
245 global_refs map[string]ValueID
246 // Constant name -> evaluated integer value (for inlining)
247 const_values map[string]i64
248 const_value_types map[string]TypeID // SSA type for the constant (e.g., u64 vs i64)
249 // String constant name -> string literal value (for inlining)
250 string_const_values map[string]string
251 // Float constant name -> float literal string (for inlining as f64)
252 float_const_values map[string]string
253 // Label name -> SSA BlockID (for goto/label support)
254 label_blocks map[string]BlockID
255 // Track mut pointer params (e.g., mut buf &u8) that need extra dereference
256 // when used in expressions (buf is ptr(ptr(i8)), but user sees buf as &u8)
257 mut_ptr_params map[string]bool
258 // Branch-local smartcasts from `if x is T` while lowering SSA.
259 local_smartcasts map[string]TypeID
260 // Set during sum type init _data field building to trigger heap allocation
261 // for &struct_local (prevents dangling stack pointers in returned sum types)
262 in_sumtype_data bool
263 // Constant array globals: names of globals that store raw element data
264 // (not V array structs). build_ident returns the pointer directly.
265 const_array_globals map[string]bool
266 const_array_elem_count map[string]int
267 // Dynamic const arrays: array struct globals that need _vinit initialization.
268 // Key: array struct global name, Value: data global name + metadata.
269 dyn_const_arrays []DynConstArray
270 // Synthetic native wrapper types for ?T / !T.
271 option_wrapper_types map[string]TypeID
272 result_wrapper_types map[string]TypeID
273 // Counter for generating unique anonymous function names
274 anon_fn_counter int
275 // Array element types by variable name (for transformer-generated functions
276 // where checker position info is unavailable). Maps param/var name to element SSA type.
277 array_elem_types map[string]TypeID
278 // Array element types by SSA value id for dynamic array values that came from
279 // typed fields/selectors but have the erased builtin `array` SSA layout.
280 array_value_elem_types map[int]TypeID
281 // Struct field dynamic-array element types keyed by `struct_type_id:field_name`.
282 struct_field_array_elem_types map[string]TypeID
283 // Expected element type for the array literal currently being built.
284 array_literal_elem_type_hint TypeID
285 // Current array literal is a temporary element buffer for a voidptr argument
286 // such as array.push_noscan, not a dynamic array value.
287 array_literal_as_element_buffer bool
288}
289
290struct LoopInfo {
291 cond_block BlockID
292 exit_block BlockID
293}
294
295pub fn Builder.new(mod &Module) &Builder {
296 return Builder.new_with_env(mod, unsafe { nil })
297}
298
299pub fn Builder.new_with_env(mod &Module, env &types.Environment) &Builder {
300 mut b := &Builder{
301 mod: mod
302 vars: map[string]ValueID{}
303 loop_stack: []LoopInfo{}
304 struct_types: map[string]TypeID{}
305 struct_embedded_spans: map[int][]EmbeddedFieldSpan{}
306 type_aliases: map[string]TypeID{}
307 enum_values: map[string]int{}
308 fn_index: map[string]int{}
309 module_fn_names: map[string]string{}
310 fn_param_array_elem_types: map[string][]TypeID{}
311 fn_refs: map[string]ValueID{}
312 selective_import_fn_names: map[string]string{}
313 selective_import_fn_candidates: map[string][]string{}
314 module_import_aliases: map[string]string{}
315 global_refs: map[string]ValueID{}
316 skip_modules: map[string]bool{}
317 skip_module_file_fragments: map[string]string{}
318 option_wrapper_types: map[string]TypeID{}
319 result_wrapper_types: map[string]TypeID{}
320 array_value_elem_types: map[int]TypeID{}
321 struct_field_array_elem_types: map[string]TypeID{}
322 local_smartcasts: map[string]TypeID{}
323 }
324 unsafe {
325 b.env = env
326 mod.env = env
327 }
328 return b
329}
330
331// new_worker_clone creates a Builder for parallel SSA building.
332// Shares read-only maps from the main builder, uses a separate worker Module.
333// worker_idx offsets anon_fn_counter so workers don't generate conflicting names.
334pub fn (mut b Builder) new_worker_clone(worker_mod &Module, worker_idx int) &Builder {
335 // Clone all maps to avoid COW races between threads.
336 // Maps that are read-only in Phase 4 (struct_types, enum_values, const_values, etc.)
337 // still need cloning because V's map read operations can trigger internal COW writes.
338 // Maps that are written in Phase 4 (fn_index, option_wrapper_types, etc.)
339 // obviously need per-worker copies.
340 return &Builder{
341 mod: worker_mod
342 env: b.env
343 target_os: b.target_os
344 minimal_runtime_roots: b.minimal_runtime_roots
345 native_backend_bulk_zero_alloca: b.native_backend_bulk_zero_alloca
346 struct_types: b.struct_types.clone()
347 struct_embedded_spans: b.struct_embedded_spans.clone()
348 type_aliases: b.type_aliases.clone()
349 enum_values: b.enum_values.clone()
350 fn_index: b.fn_index.clone()
351 module_fn_names: b.module_fn_names.clone()
352 selective_import_fn_names: b.selective_import_fn_names.clone()
353 selective_import_fn_candidates: b.selective_import_fn_candidates.clone()
354 module_import_aliases: b.module_import_aliases.clone()
355 fn_param_array_elem_types: b.fn_param_array_elem_types.clone()
356 global_refs: b.global_refs.clone()
357 const_values: b.const_values.clone()
358 const_value_types: b.const_value_types.clone()
359 string_const_values: b.string_const_values.clone()
360 float_const_values: b.float_const_values.clone()
361 const_array_globals: b.const_array_globals.clone()
362 const_array_elem_count: b.const_array_elem_count.clone()
363 option_wrapper_types: b.option_wrapper_types.clone()
364 result_wrapper_types: b.result_wrapper_types.clone()
365 struct_field_array_elem_types: b.struct_field_array_elem_types.clone()
366 // Offset anon_fn_counter so each worker generates unique names.
367 // Stride of 100_000 per worker avoids collisions.
368 anon_fn_counter: (worker_idx + 1) * 100_000
369 // Per-function state is reset at start of each build_fn, so empty init is fine
370 fn_refs: map[string]ValueID{}
371 vars: map[string]ValueID{}
372 loop_stack: []LoopInfo{}
373 label_blocks: map[string]BlockID{}
374 mut_ptr_params: map[string]bool{}
375 local_smartcasts: map[string]TypeID{}
376 }
377}
378
379pub fn imported_symbol_fn_name(module_name string, name string) string {
380 normalized_module_name := module_name_to_ssa_name(module_name)
381 if normalized_module_name == '' || normalized_module_name == 'main' {
382 return name
383 }
384 return '${normalized_module_name}__${name}'
385}
386
387fn (b &Builder) resolve_module_call_fn_name(module_name string, name string) string {
388 full_name := imported_symbol_fn_name(module_name, name)
389 if full_name in b.fn_index {
390 return full_name
391 }
392 if module_name.contains('.') {
393 leaf_name := imported_symbol_fn_name(module_name.all_after_last('.'), name)
394 if leaf_name in b.fn_index {
395 return leaf_name
396 }
397 }
398 return full_name
399}
400
401fn module_name_to_ssa_name(module_name string) string {
402 return module_name.replace('.', '_')
403}
404
405fn checked_type_name_to_ssa_name(type_name string) string {
406 if type_name == '' {
407 return ''
408 }
409 if type_name.contains('__') {
410 separator_idx := type_name.last_index('__') or { -1 }
411 module_name := type_name[..separator_idx]
412 name := type_name[separator_idx + 2..]
413 return imported_symbol_fn_name(module_name, name)
414 }
415 if type_name.contains('.') {
416 dot_idx := type_name.last_index('.') or { -1 }
417 module_name := type_name[..dot_idx]
418 name := type_name[dot_idx + 1..]
419 return imported_symbol_fn_name(module_name, name)
420 }
421 return type_name
422}
423
424fn generic_token_part_name(name string) string {
425 if name == '' {
426 return 'Type'
427 }
428 mut out := []u8{cap: name.len}
429 mut wrote_sep := false
430 for i in 0 .. name.len {
431 ch := name[i]
432 if (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`) || (ch >= `0` && ch <= `9`) {
433 out << ch
434 wrote_sep = false
435 } else if !wrote_sep {
436 out << `_`
437 wrote_sep = true
438 }
439 }
440 mut tok := out.bytestr().trim('_')
441 if tok == '' {
442 tok = 'Type'
443 }
444 return tok
445}
446
447fn import_module_name(imp ast.ImportStmt) string {
448 return imp.name
449}
450
451fn selective_import_module_name(imp ast.ImportStmt) string {
452 if imp.is_aliased {
453 return imp.name.all_after_last('.')
454 }
455 if imp.alias != '' {
456 return imp.alias
457 }
458 return imp.name.all_after_last('.')
459}
460
461pub fn module_import_aliases_from_imports(imports []ast.ImportStmt) map[string]string {
462 mut aliases := map[string]string{}
463 for imp in imports {
464 alias := if imp.alias != '' { imp.alias } else { imp.name.all_after_last('.') }
465 if !ssa_string_ok(alias) || imp.name.len == 0 {
466 continue
467 }
468 module_name := import_module_name(imp)
469 if !ssa_string_ok(module_name) {
470 continue
471 }
472 aliases[alias] = module_name
473 }
474 return aliases
475}
476
477pub fn selective_import_fn_names_from_imports(imports []ast.ImportStmt) map[string]string {
478 mut names := map[string]string{}
479 for imp in imports {
480 if imp.symbols.len == 0 {
481 continue
482 }
483 module_name := import_module_name(imp)
484 for symbol in imp.symbols {
485 if symbol is ast.Ident {
486 names[symbol.name] = imported_symbol_fn_name(module_name, symbol.name)
487 }
488 }
489 }
490 return names
491}
492
493pub fn selective_import_fn_candidates_from_imports(imports []ast.ImportStmt) map[string][]string {
494 mut names := map[string][]string{}
495 for imp in imports {
496 if imp.symbols.len == 0 {
497 continue
498 }
499 full_module_name := import_module_name(imp)
500 leaf_module_name := selective_import_module_name(imp)
501 for symbol in imp.symbols {
502 if symbol is ast.Ident {
503 full_name := imported_symbol_fn_name(full_module_name, symbol.name)
504 leaf_name := imported_symbol_fn_name(leaf_module_name, symbol.name)
505 mut candidates := []string{cap: 2}
506 candidates << full_name
507 if leaf_name != full_name {
508 candidates << leaf_name
509 }
510 names[symbol.name] = candidates
511 }
512 }
513 }
514 return names
515}
516
517fn (b &Builder) selective_import_fn_name(name string) ?string {
518 if candidates := b.selective_import_fn_candidates[name] {
519 for candidate in candidates {
520 if candidate in b.fn_index {
521 return candidate
522 }
523 }
524 return none
525 }
526 if imported := b.selective_import_fn_names[name] {
527 if imported in b.fn_index {
528 return imported
529 }
530 }
531 return none
532}
533
534fn module_fn_key(module_name string, name string) string {
535 return '${module_name}:${name}'
536}
537
538fn (b &Builder) current_module_fn_name(name string) ?string {
539 if name == '' {
540 return none
541 }
542 if target := b.module_fn_names[module_fn_key(b.cur_module, name)] {
543 if target in b.fn_index {
544 return target
545 }
546 }
547 return none
548}
549
550pub fn (mut b Builder) set_selective_import_fn_names(names map[string]string) {
551 b.selective_import_fn_names = names.clone()
552 mut candidates := map[string][]string{}
553 for name, imported in names {
554 candidates[name] = [imported]
555 }
556 b.selective_import_fn_candidates = candidates.clone()
557}
558
559pub fn (mut b Builder) set_selective_import_fn_candidates(candidates map[string][]string) {
560 b.selective_import_fn_candidates = candidates.clone()
561}
562
563pub fn (mut b Builder) set_module_import_aliases(aliases map[string]string) {
564 b.module_import_aliases = aliases.clone()
565}
566
567pub fn (mut b Builder) build_all(files []ast.File) {
568 // Register builtin globals needed by all backends
569 i32_t := b.mod.type_store.get_int(32)
570 i8_t := b.mod.type_store.get_int(8)
571 ptr_t := b.mod.type_store.get_ptr(i8_t)
572 ptr_ptr_t := b.mod.type_store.get_ptr(ptr_t)
573 b.mod.add_global('g_main_argc', i32_t, false)
574 b.mod.add_global('g_main_argv', ptr_ptr_t, false)
575
576 // Pre-register libSystem stdio externs on the main module so that worker
577 // modules inherit them and the arm64 backend emits GOT-indirect loads when
578 // `C.stdout` / `C.stdin` / `C.stderr` are referenced.
579 for stdio_sym in ['__stdoutp', '__stdinp', '__stderrp'] {
580 glob := b.mod.add_external_global(stdio_sym, ptr_t)
581 b.global_refs[stdio_sym] = glob
582 }
583
584 // Phase 1a: Register core builtin types first (string, array) since other structs depend on them.
585 // First, register builtin enums (e.g., ArrayFlags) so their types resolve correctly
586 // when registering struct fields for array/string.
587 for file in files {
588 b.cur_module = file_module_name(file)
589 if b.cur_module == 'builtin' {
590 for stmt in file.stmts {
591 if stmt is ast.EnumDecl {
592 b.register_enum(stmt)
593 }
594 }
595 }
596 }
597 for file in files {
598 b.cur_module = file_module_name(file)
599 if b.cur_module == 'builtin' {
600 for stmt in file.stmts {
601 if stmt is ast.StructDecl {
602 if stmt.name in ['string', 'array'] {
603 b.register_struct(stmt)
604 }
605 }
606 }
607 }
608 }
609 // Phase 1b: Register all struct type names (forward declarations) and enums
610 for file in files {
611 b.cur_module = file_module_name(file)
612 b.register_types_pass1(file)
613 }
614 // Phase 1c: Register type aliases after all pass-1 type names are known.
615 for file in files {
616 b.cur_module = file_module_name(file)
617 b.register_type_aliases(file)
618 }
619 // Phase 1d: Fill in struct field types (now all struct names are known)
620 for file in files {
621 b.cur_module = file_module_name(file)
622 b.register_types_pass2(file)
623 }
624 // Embedded structs from imported modules can appear earlier in file order
625 // than the embedded type's own field pass. A second idempotent pass lets
626 // those now-filled embedded fields flatten without expression lowering.
627 for file in files {
628 b.cur_module = file_module_name(file)
629 b.register_types_pass2(file)
630 }
631 // Phase 2: Register consts and globals
632 for file in files {
633 b.cur_module = file_module_name(file)
634 b.register_consts_and_globals(file)
635 }
636 // Phase 2b: Re-evaluate constants with forward references
637 // Constants that referenced other constants from later files got value 0.
638 // Now that all constants are collected, re-evaluate them.
639 b.resolve_forward_const_refs(files)
640 // Build global lookup cache once before expression lowering.
641 b.index_global_values()
642 // Phase 3: Register function signatures
643 for file in files {
644 b.cur_module = file_module_name(file)
645 b.register_fn_signatures(file)
646 }
647 // Phase 3.1: Remove globals that collide with function names.
648 // e.g. sgl has both `const default_context = ...` and `fn default_context() ...`.
649 // The function takes precedence; the global would cause the init_consts function
650 // to write to the function's TEXT address (read-only), causing a bus error.
651 for mut gvar in b.mod.globals {
652 if gvar.name in b.fn_index {
653 gvar.linkage = .external // Mark as external so codegen skips data symbol
654 b.global_refs.delete(gvar.name) // Remove from lookup so stores are not generated
655 }
656 }
657
658 // Phase 3.5: Generate synthetic stubs for transformer-generated functions
659 if b.hot_fn.len == 0 && !b.minimal_runtime_roots {
660 b.generate_array_eq_stub()
661 b.generate_wymix_stub()
662 b.generate_wyhash64_stub()
663 b.generate_wyhash_stub()
664 b.generate_ierror_stubs()
665 b.generate_fd_macro_stubs()
666 }
667
668 // Phase 4: Build function bodies
669 if !b.skip_fn_bodies {
670 b.build_all_fn_bodies(files)
671 }
672 b.generate_referenced_synthetic_runtime_stubs()
673
674 // Phase 5: Generate _vinit for dynamic array constant initialization
675 // Always generate _vinit (even if empty) so the symbol is always resolvable
676 if b.hot_fn.len == 0 && !b.skip_fn_bodies {
677 b.generate_vinit()
678 }
679}
680
681// build_all_from_flat is the flat-input counterpart of `build_all`. Phases
682// 1a–3.5 (everything before fn-body building) now walk FileCursors directly
683// and only rehydrate the decls each phase actually consumes — non-decl stmts
684// (ModuleStmt, ImportStmt, attributes, etc.) are never decoded.
685//
686// Phase 4 also consumes cursors directly: function signatures are read from
687// `.stmt_fn_decl` nodes and function bodies are lowered from body stmt cursors.
688pub fn (mut b Builder) build_all_from_flat(flat &ast.FlatAst) {
689 // Register builtin globals needed by all backends
690 i32_t := b.mod.type_store.get_int(32)
691 i8_t := b.mod.type_store.get_int(8)
692 ptr_t := b.mod.type_store.get_ptr(i8_t)
693 ptr_ptr_t := b.mod.type_store.get_ptr(ptr_t)
694 b.mod.add_global('g_main_argc', i32_t, false)
695 b.mod.add_global('g_main_argv', ptr_ptr_t, false)
696
697 // Pre-register libSystem stdio externs on the main module so that worker
698 // modules inherit them and the arm64 backend emits GOT-indirect loads when
699 // `C.stdout` / `C.stdin` / `C.stderr` are referenced.
700 for stdio_sym in ['__stdoutp', '__stdinp', '__stderrp'] {
701 glob := b.mod.add_external_global(stdio_sym, ptr_t)
702 b.global_refs[stdio_sym] = glob
703 }
704
705 // Phase 1a: Register core builtin types first (string, array) since other
706 // structs depend on them. First, register builtin enums (e.g., ArrayFlags)
707 // so their types resolve correctly when registering struct fields for
708 // array/string.
709 for fi in 0 .. flat.files.len {
710 fc := flat.file_cursor(fi)
711 b.cur_module = fc.mod().replace('.', '_')
712 if b.cur_module != 'builtin' {
713 continue
714 }
715 stmts := fc.stmts()
716 for si in 0 .. stmts.len() {
717 c := stmts.at(si)
718 if c.kind() != .stmt_enum_decl {
719 continue
720 }
721 // s238: cursor-native enum registration — no decode_stmt.
722 b.register_enum_from_flat(c)
723 }
724 }
725 for fi in 0 .. flat.files.len {
726 fc := flat.file_cursor(fi)
727 b.cur_module = fc.mod().replace('.', '_')
728 if b.cur_module != 'builtin' {
729 continue
730 }
731 stmts := fc.stmts()
732 for si in 0 .. stmts.len() {
733 c := stmts.at(si)
734 if c.kind() != .stmt_struct_decl {
735 continue
736 }
737 // s239: cursor-native struct registration — no decode_stmt. The struct
738 // name is the cursor's name_id.
739 if c.name() in ['string', 'array'] {
740 b.register_struct_from_flat(c)
741 }
742 }
743 }
744 // Phase 1b: Register all struct type names (forward declarations) and enums
745 for fi in 0 .. flat.files.len {
746 fc := flat.file_cursor(fi)
747 b.cur_module = fc.mod().replace('.', '_')
748 b.register_types_pass1_from_flat(fc)
749 }
750 // Phase 1c: Register type aliases after all pass-1 type names are known.
751 for fi in 0 .. flat.files.len {
752 fc := flat.file_cursor(fi)
753 b.cur_module = fc.mod().replace('.', '_')
754 b.register_type_aliases_from_flat(fc)
755 }
756 // Phase 1d: Fill in struct field types (now all struct names are known)
757 for fi in 0 .. flat.files.len {
758 fc := flat.file_cursor(fi)
759 b.cur_module = fc.mod().replace('.', '_')
760 b.register_types_pass2_from_flat(fc)
761 }
762 // See the AST pass2 note above: imported embedded field layouts may only
763 // become available after the first pass over all files.
764 for fi in 0 .. flat.files.len {
765 fc := flat.file_cursor(fi)
766 b.cur_module = fc.mod().replace('.', '_')
767 b.register_types_pass2_from_flat(fc)
768 }
769 // Phase 2: Register consts and globals
770 for fi in 0 .. flat.files.len {
771 fc := flat.file_cursor(fi)
772 b.cur_module = fc.mod().replace('.', '_')
773 b.register_consts_and_globals_from_flat(fc)
774 }
775 // Phase 2b: Re-evaluate constants with forward references
776 b.resolve_forward_const_refs_from_flat(flat)
777 // Build global lookup cache once before expression lowering.
778 b.index_global_values()
779 // Phase 3: Register function signatures
780 for fi in 0 .. flat.files.len {
781 fc := flat.file_cursor(fi)
782 b.cur_module = fc.mod().replace('.', '_')
783 b.register_fn_signatures_from_flat(fc)
784 }
785 // Phase 3.1: Remove globals that collide with function names.
786 for mut gvar in b.mod.globals {
787 if gvar.name in b.fn_index {
788 gvar.linkage = .external
789 b.global_refs.delete(gvar.name)
790 }
791 }
792
793 // Phase 3.5: Generate synthetic stubs for transformer-generated functions
794 if b.hot_fn.len == 0 && !b.minimal_runtime_roots {
795 b.generate_array_eq_stub()
796 b.generate_wymix_stub()
797 b.generate_wyhash64_stub()
798 b.generate_wyhash_stub()
799 b.generate_ierror_stubs()
800 b.generate_fd_macro_stubs()
801 }
802
803 // Phase 4: build function bodies from `.stmt_fn_decl` cursors. Signatures,
804 // filters, params, and body statements all stay on the flat path.
805 if !b.skip_fn_bodies {
806 for fi in 0 .. flat.files.len {
807 fc := flat.file_cursor(fi)
808 b.cur_module = fc.mod().replace('.', '_')
809 b.build_fn_bodies_from_flat(fc)
810 }
811 }
812 b.generate_referenced_synthetic_runtime_stubs()
813
814 // Phase 5: Generate _vinit for dynamic array constant initialization
815 if b.hot_fn.len == 0 && !b.skip_fn_bodies {
816 b.generate_vinit()
817 }
818}
819
820// generate_referenced_synthetic_runtime_stubs materializes native-only helpers
821// that are referenced after transformation or minimal-runtime pruning.
822pub fn (mut b Builder) generate_referenced_synthetic_runtime_stubs() {
823 if b.hot_fn.len != 0 || b.skip_fn_bodies {
824 return
825 }
826 referenced := b.referenced_func_ref_names()
827 if referenced['builtin__string__plus_two'] {
828 b.generate_string_plus_two_stub()
829 }
830 if !b.minimal_runtime_roots {
831 return
832 }
833 if referenced['array__eq'] {
834 b.generate_array_eq_stub()
835 }
836 if referenced['_wymix'] {
837 b.generate_wymix_stub()
838 }
839 if referenced['wyhash64'] {
840 b.generate_wyhash64_stub()
841 }
842 if referenced['wyhash'] {
843 b.generate_wyhash_stub()
844 }
845 if referenced['IError__msg'] || referenced['IError__code'] || referenced['IError__type_name'] {
846 b.generate_ierror_stubs()
847 }
848 if referenced['FD_ZERO'] || referenced['FD_SET'] || referenced['FD_ISSET'] {
849 b.generate_fd_macro_stubs()
850 }
851}
852
853fn (b &Builder) referenced_func_ref_names() map[string]bool {
854 mut names := map[string]bool{}
855 for val in b.mod.values {
856 if val.kind != .instruction {
857 continue
858 }
859 instr := b.mod.instrs[val.index]
860 for operand in instr.operands {
861 if operand <= 0 || operand >= b.mod.values.len {
862 continue
863 }
864 ref := b.mod.values[operand]
865 if ref.kind == .func_ref && ref.name != '' {
866 names[ref.name] = true
867 }
868 }
869 }
870 return names
871}
872
873// build_all_fn_bodies builds SSA for all function bodies (Phase 4).
874// Separated from build_all to allow the parallel builder to replace this step.
875pub fn (mut b Builder) build_all_fn_bodies(files []ast.File) {
876 for file in files {
877 b.cur_module = file_module_name(file)
878 b.build_fn_bodies(file)
879 }
880}
881
882pub fn file_module_name(file ast.File) string {
883 for stmt in file.stmts {
884 if stmt is ast.ModuleStmt {
885 return stmt.name.replace('.', '_')
886 }
887 }
888 return 'main'
889}
890
891// --- Type resolution using types.Environment ---
892
893fn ssa_string_ok(s string) bool {
894 if s.len == 0 {
895 return true
896 }
897 if s.len < 0 || s.len > 1024 {
898 return false
899 }
900 ptr := unsafe { u64(s.str) }
901 return ptr >= 0x10000 && ptr < 0x0000800000000000
902}
903
904fn ssa_module_tail_name(name string) string {
905 if name.contains('.') {
906 return name.all_after_last('.')
907 }
908 if name.contains('__') {
909 return name.all_after_last('__')
910 }
911 if name.starts_with('v2_') && name.contains('_') {
912 return name.all_after_last('_')
913 }
914 return name
915}
916
917fn ssa_is_string_struct_name(name string) bool {
918 return name == 'string' || name == 'builtin__string'
919}
920
921fn ssa_is_string_str_fn_name(name string) bool {
922 return name == 'string__str' || name == 'builtin__string__str'
923}
924
925fn (b &Builder) valid_value_id(val ValueID) bool {
926 return val > 0 && val < b.mod.values.len
927}
928
929fn (b &Builder) valid_type_id(typ TypeID) bool {
930 return typ > 0 && typ < b.mod.type_store.types.len
931}
932
933fn (mut b Builder) type_to_ssa(t types.Type) TypeID {
934 if b.guard_invalid_type_payloads && !types.type_has_valid_payload(t) {
935 return b.mod.type_store.get_int(64)
936 }
937 match t {
938 types.Primitive {
939 if t.props.has(.boolean) {
940 return b.mod.type_store.get_int(1)
941 }
942 if t.props.has(.float) {
943 width := if t.size == 32 { 32 } else { 64 }
944 return b.mod.type_store.get_float(width)
945 }
946 if t.props.has(.integer) {
947 size := if t.size == 0 { 32 } else { int(t.size) }
948 if t.props.has(.unsigned) {
949 return b.mod.type_store.get_uint(size)
950 }
951 return b.mod.type_store.get_int(size)
952 }
953 return b.mod.type_store.get_int(32)
954 }
955 types.Pointer {
956 if t.base_type is types.Struct {
957 st := t.base_type as types.Struct
958 if !ssa_string_ok(st.name) {
959 return b.mod.type_store.get_int(64)
960 }
961 if base := b.struct_name_to_ssa(st.name) {
962 return b.mod.type_store.get_ptr(base)
963 }
964 }
965 base := b.type_to_ssa(t.base_type)
966 return b.mod.type_store.get_ptr(base)
967 }
968 types.String {
969 return b.get_string_type()
970 }
971 types.Struct {
972 if !ssa_string_ok(t.name) {
973 return b.mod.type_store.get_int(64)
974 }
975 if id := b.struct_name_to_ssa(t.name) {
976 return id
977 }
978 return b.mod.type_store.get_int(64) // fallback
979 }
980 types.Enum {
981 return b.mod.type_store.get_int(32)
982 }
983 types.Void {
984 return 0 // void
985 }
986 types.Char {
987 return b.mod.type_store.get_int(8)
988 }
989 types.Rune {
990 return b.mod.type_store.get_int(32)
991 }
992 types.ISize {
993 return b.mod.type_store.get_int(64)
994 }
995 types.USize {
996 return b.mod.type_store.get_uint(64)
997 }
998 types.Alias {
999 base := b.resolve_alias_base_type(t) or { return b.mod.type_store.get_int(64) }
1000 return b.type_to_ssa(base)
1001 }
1002 types.Array {
1003 // Dynamic arrays are struct-like: {data*, len, cap, element_size}
1004 return b.get_array_type()
1005 }
1006 types.ArrayFixed {
1007 // Fixed-size arrays: [N]T → SSA array type
1008 elem_type := b.type_to_ssa(t.elem_type)
1009 if t.len > 0 && elem_type != 0 {
1010 return b.mod.type_store.get_array(elem_type, t.len)
1011 }
1012 return b.mod.type_store.get_int(64) // fallback
1013 }
1014 types.Nil {
1015 i8_t := b.mod.type_store.get_int(8)
1016 return b.mod.type_store.get_ptr(i8_t)
1017 }
1018 types.None {
1019 return 0
1020 }
1021 types.Tuple {
1022 tt := t.get_types()
1023 mut elem_types := []TypeID{cap: tt.len}
1024 for et in tt {
1025 elem_types << b.type_to_ssa(et)
1026 }
1027 return b.mod.type_store.get_tuple(elem_types)
1028 }
1029 types.SumType {
1030 if !ssa_string_ok(t.name) {
1031 return b.mod.type_store.get_int(32)
1032 }
1033 if id := b.struct_types[t.name] {
1034 return id
1035 }
1036 // Try module-qualified name
1037 qualified_st := '${b.cur_module}__${t.name}'
1038 if id := b.struct_types[qualified_st] {
1039 return id
1040 }
1041 // Search all known module prefixes
1042 for sname, sid in b.struct_types {
1043 if sname.ends_with('__${t.name}') {
1044 return sid
1045 }
1046 }
1047 return b.mod.type_store.get_int(64) // fallback
1048 }
1049 types.Map {
1050 return b.struct_types['map'] or { b.mod.type_store.get_int(64) }
1051 }
1052 types.OptionType {
1053 return b.get_option_wrapper_type(b.type_to_ssa(t.base_type))
1054 }
1055 types.ResultType {
1056 return b.get_result_wrapper_type(b.type_to_ssa(t.base_type))
1057 }
1058 types.FnType {
1059 i8_t := b.mod.type_store.get_int(8)
1060 return b.mod.type_store.get_ptr(i8_t) // fn pointers
1061 }
1062 types.Interface {
1063 return b.mod.type_store.get_int(64) // interfaces lowered to i64
1064 }
1065 else {
1066 name := types.type_name(t)
1067 if name != '' {
1068 return b.named_type_to_ssa(name)
1069 }
1070 return b.mod.type_store.get_int(64) // fallback for unhandled
1071 }
1072 }
1073}
1074
1075fn (mut b Builder) struct_name_to_ssa(name string) ?TypeID {
1076 if !ssa_string_ok(name) {
1077 return none
1078 }
1079 if id := b.struct_types[name] {
1080 return id
1081 }
1082 // Try module-qualified name: C structs are registered as "os__dirent"
1083 // but the type checker stores them as just "dirent".
1084 qualified := '${b.cur_module}__${name}'
1085 if id := b.struct_types[qualified] {
1086 return id
1087 }
1088 // Try all known module prefixes for cross-module struct access.
1089 for sname, sid in b.struct_types {
1090 if sname.ends_with('__${name}') {
1091 return sid
1092 }
1093 }
1094 return none
1095}
1096
1097fn pointer_type_part_name(base string) string {
1098 if base == '' {
1099 return ''
1100 }
1101 return '${base}ptr'
1102}
1103
1104fn (b &Builder) generic_ident_type_part_name(name string) string {
1105 if name == '' {
1106 return ''
1107 }
1108 normalized := name.replace('.', '__')
1109 if normalized.contains('__') {
1110 return normalized
1111 }
1112 if b.env == unsafe { nil } {
1113 return normalized
1114 }
1115 typ := b.lookup_checked_type_by_name(name) or { return normalized }
1116 match typ {
1117 types.Struct, types.SumType {
1118 typ_name := types.type_name(typ)
1119 if typ_name == '' {
1120 return normalized
1121 }
1122 return checked_type_name_to_ssa_name(typ_name)
1123 }
1124 else {
1125 return normalized
1126 }
1127 }
1128}
1129
1130fn (b &Builder) generic_type_part_name(expr ast.Expr) string {
1131 match expr {
1132 ast.Ident {
1133 return b.generic_ident_type_part_name(expr.name)
1134 }
1135 ast.SelectorExpr {
1136 return b.generic_selector_type_part_name(expr)
1137 }
1138 ast.PrefixExpr {
1139 if expr.op == .amp {
1140 return pointer_type_part_name(b.generic_type_part_name(expr.expr))
1141 }
1142 return ''
1143 }
1144 ast.Type {
1145 match expr {
1146 ast.ArrayType {
1147 elem_name := b.generic_type_part_name(expr.elem_type)
1148 if elem_name == '' {
1149 return ''
1150 }
1151 return 'Array_${elem_name}'
1152 }
1153 ast.ArrayFixedType {
1154 elem_name := b.generic_type_part_name(expr.elem_type)
1155 len_name := if expr.len is ast.BasicLiteral {
1156 (expr.len as ast.BasicLiteral).value
1157 } else {
1158 ''
1159 }
1160 if elem_name == '' || len_name == '' {
1161 return ''
1162 }
1163 return 'Array_fixed_${elem_name}_${len_name}'
1164 }
1165 ast.MapType {
1166 key_name := b.generic_type_part_name(expr.key_type)
1167 value_name := b.generic_type_part_name(expr.value_type)
1168 if key_name == '' || value_name == '' {
1169 return ''
1170 }
1171 return 'Map_${key_name}_${value_name}'
1172 }
1173 ast.PointerType {
1174 return pointer_type_part_name(b.generic_type_part_name(expr.base_type))
1175 }
1176 ast.GenericType {
1177 return b.generic_type_name(expr.name, expr.params)
1178 }
1179 ast.OptionType {
1180 base_name := b.generic_type_part_name(expr.base_type)
1181 if base_name == '' {
1182 return ''
1183 }
1184 return 'Option_${base_name}'
1185 }
1186 ast.ResultType {
1187 base_name := b.generic_type_part_name(expr.base_type)
1188 if base_name == '' {
1189 return ''
1190 }
1191 return 'Result_${base_name}'
1192 }
1193 else {
1194 return ''
1195 }
1196 }
1197 }
1198 else {
1199 return ''
1200 }
1201 }
1202}
1203
1204fn (b &Builder) generic_ident_type_arg_part_name(name string) string {
1205 if name == '' {
1206 return ''
1207 }
1208 if b.env == unsafe { nil } {
1209 return generic_token_part_name(name)
1210 }
1211 typ := b.lookup_checked_type_by_name(name) or { return generic_token_part_name(name) }
1212 match typ {
1213 types.Struct, types.SumType {
1214 typ_name := types.type_name(typ)
1215 if typ_name == '' {
1216 return generic_token_part_name(name)
1217 }
1218 return generic_token_part_name(typ_name)
1219 }
1220 else {
1221 return generic_token_part_name(name)
1222 }
1223 }
1224}
1225
1226fn (b &Builder) generic_selector_type_arg_part_name(expr ast.SelectorExpr) string {
1227 full_name := b.generic_selector_type_part_name(expr)
1228 if full_name == '' {
1229 return ''
1230 }
1231 return generic_token_part_name(full_name)
1232}
1233
1234fn (b &Builder) generic_type_arg_part_name(expr ast.Expr) string {
1235 match expr {
1236 ast.Ident {
1237 return b.generic_ident_type_arg_part_name(expr.name)
1238 }
1239 ast.SelectorExpr {
1240 return b.generic_selector_type_arg_part_name(expr)
1241 }
1242 ast.PrefixExpr {
1243 if expr.op == .amp {
1244 return pointer_type_part_name(b.generic_type_arg_part_name(expr.expr))
1245 }
1246 return ''
1247 }
1248 ast.Type {
1249 match expr {
1250 ast.ArrayType {
1251 elem_name := b.generic_type_arg_part_name(expr.elem_type)
1252 if elem_name == '' {
1253 return ''
1254 }
1255 return 'Array_${elem_name}'
1256 }
1257 ast.ArrayFixedType {
1258 elem_name := b.generic_type_arg_part_name(expr.elem_type)
1259 len_name := if expr.len is ast.BasicLiteral {
1260 (expr.len as ast.BasicLiteral).value
1261 } else {
1262 ''
1263 }
1264 if elem_name == '' || len_name == '' {
1265 return ''
1266 }
1267 return 'Array_fixed_${elem_name}_${len_name}'
1268 }
1269 ast.MapType {
1270 key_name := b.generic_type_arg_part_name(expr.key_type)
1271 value_name := b.generic_type_arg_part_name(expr.value_type)
1272 if key_name == '' || value_name == '' {
1273 return ''
1274 }
1275 return 'Map_${key_name}_${value_name}'
1276 }
1277 ast.PointerType {
1278 return pointer_type_part_name(b.generic_type_arg_part_name(expr.base_type))
1279 }
1280 ast.GenericType {
1281 concrete_name := b.generic_type_name(expr.name, expr.params)
1282 if concrete_name == '' {
1283 return ''
1284 }
1285 return generic_token_part_name(concrete_name)
1286 }
1287 ast.OptionType {
1288 base_name := b.generic_type_arg_part_name(expr.base_type)
1289 if base_name == '' {
1290 return ''
1291 }
1292 return 'Option_${base_name}'
1293 }
1294 ast.ResultType {
1295 base_name := b.generic_type_arg_part_name(expr.base_type)
1296 if base_name == '' {
1297 return ''
1298 }
1299 return 'Result_${base_name}'
1300 }
1301 else {
1302 return ''
1303 }
1304 }
1305 }
1306 else {
1307 return ''
1308 }
1309 }
1310}
1311
1312fn (b &Builder) generic_selector_type_part_name(expr ast.SelectorExpr) string {
1313 if expr.lhs is ast.Ident {
1314 lhs_name := (expr.lhs as ast.Ident).name
1315 rhs_name := expr.rhs.name
1316 if lhs_name == '' || rhs_name == '' {
1317 return ''
1318 }
1319 if resolved_mod := b.module_import_aliases[lhs_name] {
1320 return imported_symbol_fn_name(resolved_mod, rhs_name)
1321 }
1322 if resolved_mod := b.selector_module_name_for_ident(lhs_name) {
1323 return imported_symbol_fn_name(resolved_mod, rhs_name)
1324 }
1325 return '${lhs_name.replace('.', '__')}__${rhs_name}'
1326 }
1327 return expr.name().replace('.', '__')
1328}
1329
1330fn generic_type_name_from_parts(base string, parts []string) string {
1331 if base == '' || parts.len == 0 {
1332 return ''
1333 }
1334 return '${base}_T_${parts.join('_')}'
1335}
1336
1337fn concrete_generic_type_suffix_name(concrete_name string) string {
1338 if !concrete_name.contains('_T_') {
1339 return ''
1340 }
1341 base := concrete_name.all_before('_T_')
1342 args := concrete_name.all_after('_T_')
1343 if base == '' || args == '' || !base.contains('__') {
1344 return ''
1345 }
1346 return '${base.all_after_last('__')}_T_${args}'
1347}
1348
1349fn (b &Builder) registered_type_by_unique_concrete_suffix(concrete_name string) ?TypeID {
1350 suffix_name := concrete_generic_type_suffix_name(concrete_name)
1351 if suffix_name == '' {
1352 return none
1353 }
1354 mut found := TypeID(0)
1355 mut found_count := 0
1356 for registered_name, type_id in b.struct_types {
1357 if registered_name == suffix_name || registered_name.ends_with('__${suffix_name}') {
1358 found = type_id
1359 found_count++
1360 }
1361 }
1362 if found_count == 1 {
1363 return found
1364 }
1365 return none
1366}
1367
1368fn (b &Builder) generic_type_name(name ast.Expr, params []ast.Expr) string {
1369 base := b.generic_type_part_name(name)
1370 mut parts := []string{cap: params.len}
1371 for param in params {
1372 part := b.generic_type_arg_part_name(param)
1373 if part == '' {
1374 return ''
1375 }
1376 parts << part
1377 }
1378 return generic_type_name_from_parts(base, parts)
1379}
1380
1381fn (mut b Builder) checked_sumtype_for_ssa_name(name string) ?types.SumType {
1382 typ := b.lookup_checked_type_by_name(name) or {
1383 if name.contains('_T_') {
1384 base_name := name.all_before('_T_')
1385 b.lookup_checked_type_by_name(base_name) or { return none }
1386 } else {
1387 return none
1388 }
1389 }
1390 if typ is types.SumType {
1391 return typ
1392 }
1393 return none
1394}
1395
1396fn (mut b Builder) source_base_is_sumtype(name string) bool {
1397 _ := b.checked_sumtype_for_ssa_name(name) or { return false }
1398 return true
1399}
1400
1401fn (mut b Builder) generic_sumtype_storage_to_ssa(base_name string, concrete_name string) ?TypeID {
1402 if concrete_name == '' || !ssa_string_ok(concrete_name) || !b.source_base_is_sumtype(base_name) {
1403 return none
1404 }
1405 if id := b.struct_name_to_ssa(concrete_name) {
1406 return id
1407 }
1408 i64_t := b.mod.type_store.get_int(64)
1409 type_id := b.mod.type_store.register(Type{
1410 kind: .struct_t
1411 fields: [i64_t, i64_t]
1412 field_names: ['_tag', '_data']
1413 })
1414 b.struct_types[concrete_name] = type_id
1415 b.mod.c_struct_names[type_id] = concrete_name
1416 return type_id
1417}
1418
1419fn (mut b Builder) generic_type_to_ssa(name ast.Expr, params []ast.Expr) TypeID {
1420 concrete_name := b.generic_type_name(name, params)
1421 if concrete_name == '' || !ssa_string_ok(concrete_name) {
1422 return b.mod.type_store.get_int(64)
1423 }
1424 if id := b.struct_name_to_ssa(concrete_name) {
1425 return id
1426 }
1427 if id := b.registered_type_by_unique_concrete_suffix(concrete_name) {
1428 return id
1429 }
1430 base_name := b.generic_type_part_name(name)
1431 if id := b.generic_sumtype_storage_to_ssa(base_name, concrete_name) {
1432 return id
1433 }
1434 return b.mod.type_store.get_int(64)
1435}
1436
1437fn (b &Builder) generic_type_part_name_from_flat(c ast.Cursor) string {
1438 match c.kind() {
1439 .expr_ident {
1440 return b.generic_ident_type_part_name(c.name())
1441 }
1442 .expr_selector {
1443 lhs := c.edge(0)
1444 rhs := c.edge(1)
1445 if lhs.kind() == .expr_ident && rhs.kind() == .expr_ident {
1446 return b.generic_selector_type_part_name_from_flat(lhs.name(), rhs.name())
1447 }
1448 return ''
1449 }
1450 .expr_prefix {
1451 op := unsafe { token.Token(int(c.aux())) }
1452 if op == .amp {
1453 return pointer_type_part_name(b.generic_type_part_name_from_flat(c.edge(0)))
1454 }
1455 return ''
1456 }
1457 .typ_array {
1458 elem_name := b.generic_type_part_name_from_flat(c.edge(0))
1459 if elem_name == '' {
1460 return ''
1461 }
1462 return 'Array_${elem_name}'
1463 }
1464 .typ_array_fixed {
1465 elem_name := b.generic_type_part_name_from_flat(c.edge(1))
1466 len_c := c.edge(0)
1467 len_name := if len_c.kind() == .expr_basic_literal { len_c.name() } else { '' }
1468 if elem_name == '' || len_name == '' {
1469 return ''
1470 }
1471 return 'Array_fixed_${elem_name}_${len_name}'
1472 }
1473 .typ_map {
1474 key_name := b.generic_type_part_name_from_flat(c.edge(0))
1475 value_name := b.generic_type_part_name_from_flat(c.edge(1))
1476 if key_name == '' || value_name == '' {
1477 return ''
1478 }
1479 return 'Map_${key_name}_${value_name}'
1480 }
1481 .typ_pointer {
1482 return pointer_type_part_name(b.generic_type_part_name_from_flat(c.edge(0)))
1483 }
1484 .typ_generic {
1485 return b.generic_type_name_from_flat(c)
1486 }
1487 .typ_option {
1488 base_name := b.generic_type_part_name_from_flat(c.edge(0))
1489 if base_name == '' {
1490 return ''
1491 }
1492 return 'Option_${base_name}'
1493 }
1494 .typ_result {
1495 base_name := b.generic_type_part_name_from_flat(c.edge(0))
1496 if base_name == '' {
1497 return ''
1498 }
1499 return 'Result_${base_name}'
1500 }
1501 else {
1502 return ''
1503 }
1504 }
1505}
1506
1507fn (b &Builder) generic_selector_type_part_name_from_flat(lhs_name string, rhs_name string) string {
1508 if lhs_name == '' || rhs_name == '' {
1509 return ''
1510 }
1511 if resolved_mod := b.module_import_aliases[lhs_name] {
1512 return imported_symbol_fn_name(resolved_mod, rhs_name)
1513 }
1514 if resolved_mod := b.selector_module_name_for_ident(lhs_name) {
1515 return imported_symbol_fn_name(resolved_mod, rhs_name)
1516 }
1517 return '${lhs_name.replace('.', '__')}__${rhs_name}'
1518}
1519
1520fn (b &Builder) generic_ident_type_arg_part_name_from_flat(name string) string {
1521 return b.generic_ident_type_arg_part_name(name)
1522}
1523
1524fn (b &Builder) generic_selector_type_arg_part_name_from_flat(lhs_name string, rhs_name string) string {
1525 full_name := b.generic_selector_type_part_name_from_flat(lhs_name, rhs_name)
1526 if full_name == '' {
1527 return ''
1528 }
1529 return generic_token_part_name(full_name)
1530}
1531
1532fn (b &Builder) generic_type_arg_part_name_from_flat(c ast.Cursor) string {
1533 match c.kind() {
1534 .expr_ident {
1535 return b.generic_ident_type_arg_part_name_from_flat(c.name())
1536 }
1537 .expr_selector {
1538 lhs := c.edge(0)
1539 rhs := c.edge(1)
1540 if lhs.kind() == .expr_ident && rhs.kind() == .expr_ident {
1541 return b.generic_selector_type_arg_part_name_from_flat(lhs.name(), rhs.name())
1542 }
1543 return ''
1544 }
1545 .expr_prefix {
1546 op := unsafe { token.Token(int(c.aux())) }
1547 if op == .amp {
1548 return pointer_type_part_name(b.generic_type_arg_part_name_from_flat(c.edge(0)))
1549 }
1550 return ''
1551 }
1552 .typ_array {
1553 elem_name := b.generic_type_arg_part_name_from_flat(c.edge(0))
1554 if elem_name == '' {
1555 return ''
1556 }
1557 return 'Array_${elem_name}'
1558 }
1559 .typ_array_fixed {
1560 if c.edge_count() < 2 {
1561 return ''
1562 }
1563 elem_name := b.generic_type_arg_part_name_from_flat(c.edge(1))
1564 len_c := c.edge(0)
1565 len_name := if len_c.kind() == .expr_basic_literal { len_c.name() } else { '' }
1566 if elem_name == '' || len_name == '' {
1567 return ''
1568 }
1569 return 'Array_fixed_${elem_name}_${len_name}'
1570 }
1571 .typ_map {
1572 if c.edge_count() < 2 {
1573 return ''
1574 }
1575 key_name := b.generic_type_arg_part_name_from_flat(c.edge(0))
1576 value_name := b.generic_type_arg_part_name_from_flat(c.edge(1))
1577 if key_name == '' || value_name == '' {
1578 return ''
1579 }
1580 return 'Map_${key_name}_${value_name}'
1581 }
1582 .typ_pointer {
1583 return pointer_type_part_name(b.generic_type_arg_part_name_from_flat(c.edge(0)))
1584 }
1585 .typ_generic {
1586 concrete_name := b.generic_type_name_from_flat(c)
1587 if concrete_name == '' {
1588 return ''
1589 }
1590 return generic_token_part_name(concrete_name)
1591 }
1592 .typ_option {
1593 base_name := b.generic_type_arg_part_name_from_flat(c.edge(0))
1594 if base_name == '' {
1595 return ''
1596 }
1597 return 'Option_${base_name}'
1598 }
1599 .typ_result {
1600 base_name := b.generic_type_arg_part_name_from_flat(c.edge(0))
1601 if base_name == '' {
1602 return ''
1603 }
1604 return 'Result_${base_name}'
1605 }
1606 else {
1607 return ''
1608 }
1609 }
1610}
1611
1612fn (b &Builder) generic_type_name_from_flat(c ast.Cursor) string {
1613 if c.kind() != .typ_generic || c.edge_count() <= 1 {
1614 return ''
1615 }
1616 base := b.generic_type_part_name_from_flat(c.edge(0))
1617 mut parts := []string{cap: c.edge_count() - 1}
1618 for i in 1 .. c.edge_count() {
1619 part := b.generic_type_arg_part_name_from_flat(c.edge(i))
1620 if part == '' {
1621 return ''
1622 }
1623 parts << part
1624 }
1625 return generic_type_name_from_parts(base, parts)
1626}
1627
1628fn (mut b Builder) generic_type_to_ssa_from_flat(c ast.Cursor) TypeID {
1629 concrete_name := b.generic_type_name_from_flat(c)
1630 if concrete_name == '' || !ssa_string_ok(concrete_name) {
1631 return b.mod.type_store.get_int(64)
1632 }
1633 if id := b.struct_name_to_ssa(concrete_name) {
1634 return id
1635 }
1636 if id := b.registered_type_by_unique_concrete_suffix(concrete_name) {
1637 return id
1638 }
1639 base_name := b.generic_type_part_name_from_flat(c.edge(0))
1640 if id := b.generic_sumtype_storage_to_ssa(base_name, concrete_name) {
1641 return id
1642 }
1643 return b.mod.type_store.get_int(64)
1644}
1645
1646fn (mut b Builder) primitive_type_name_to_ssa(name string) ?TypeID {
1647 if !ssa_string_ok(name) {
1648 return none
1649 }
1650 return match name {
1651 'int' {
1652 b.mod.type_store.get_int(32)
1653 }
1654 'i8' {
1655 b.mod.type_store.get_int(8)
1656 }
1657 'i16' {
1658 b.mod.type_store.get_int(16)
1659 }
1660 'i32' {
1661 b.mod.type_store.get_int(32)
1662 }
1663 'i64' {
1664 b.mod.type_store.get_int(64)
1665 }
1666 'u8', 'byte' {
1667 b.mod.type_store.get_uint(8)
1668 }
1669 'u16' {
1670 b.mod.type_store.get_uint(16)
1671 }
1672 'u32' {
1673 b.mod.type_store.get_uint(32)
1674 }
1675 'u64' {
1676 b.mod.type_store.get_uint(64)
1677 }
1678 'f32' {
1679 b.mod.type_store.get_float(32)
1680 }
1681 'f64' {
1682 b.mod.type_store.get_float(64)
1683 }
1684 'bool' {
1685 b.mod.type_store.get_int(1)
1686 }
1687 'string' {
1688 b.get_string_type()
1689 }
1690 'voidptr' {
1691 i8_t := b.mod.type_store.get_int(8)
1692 b.mod.type_store.get_ptr(i8_t)
1693 }
1694 'rune' {
1695 b.mod.type_store.get_int(32)
1696 }
1697 'char' {
1698 b.mod.type_store.get_int(8)
1699 }
1700 'isize' {
1701 b.mod.type_store.get_int(64)
1702 }
1703 'usize' {
1704 b.mod.type_store.get_uint(64)
1705 }
1706 else {
1707 none
1708 }
1709 }
1710}
1711
1712fn (mut b Builder) named_type_to_ssa(name string) TypeID {
1713 if !ssa_string_ok(name) {
1714 return b.mod.type_store.get_int(64)
1715 }
1716 if type_id := b.primitive_type_name_to_ssa(name) {
1717 return type_id
1718 }
1719 normalized := name.replace('.', '__')
1720 if type_id := b.lookup_type_alias_ssa(name) {
1721 return type_id
1722 }
1723 if normalized != name {
1724 if type_id := b.lookup_type_alias_ssa(normalized) {
1725 return type_id
1726 }
1727 }
1728 if normalized in b.struct_types {
1729 return b.struct_types[normalized]
1730 }
1731 if b.is_enum_type(name) || b.is_enum_type(normalized) {
1732 return b.mod.type_store.get_int(32)
1733 }
1734 if typ := b.lookup_checked_type_by_name(name) {
1735 if typ is types.Alias {
1736 return b.type_to_ssa(typ)
1737 } else {
1738 typ_name := types.type_name(typ)
1739 if typ_name != name {
1740 return b.type_to_ssa(typ)
1741 }
1742 }
1743 }
1744 if normalized != name {
1745 if typ := b.lookup_checked_type_by_name(normalized) {
1746 if typ is types.Alias {
1747 return b.type_to_ssa(typ)
1748 } else {
1749 typ_name := types.type_name(typ)
1750 if typ_name != name && typ_name != normalized {
1751 return b.type_to_ssa(typ)
1752 }
1753 }
1754 }
1755 }
1756 return b.mod.type_store.get_int(64)
1757}
1758
1759fn (b &Builder) selector_module_name_for_ident(mod_name string) ?string {
1760 if mod_name == 'C' {
1761 return 'C'
1762 }
1763 if resolved_mod := b.module_import_aliases[mod_name] {
1764 return resolved_mod
1765 }
1766 if b.env != unsafe { nil } {
1767 if scope := b.env.get_scope(b.cur_module) {
1768 if obj := scope.lookup_parent(mod_name, 0) {
1769 if obj is types.Module {
1770 return obj.name
1771 }
1772 }
1773 }
1774 }
1775 return none
1776}
1777
1778fn (b &Builder) selector_type_alias_to_ssa(mod_name string, type_name string) ?TypeID {
1779 if mod_name == '' || type_name == '' || !ssa_string_ok(mod_name) || !ssa_string_ok(type_name) {
1780 return none
1781 }
1782 normalized_mod := mod_name.replace('.', '_')
1783 qualified := ssa_module_storage_name(normalized_mod, type_name)
1784 if type_id := b.lookup_type_alias_ssa(qualified) {
1785 return type_id
1786 }
1787 if normalized_mod != mod_name {
1788 dot_qualified := ssa_module_storage_name(mod_name, type_name)
1789 if type_id := b.lookup_type_alias_ssa(dot_qualified) {
1790 return type_id
1791 }
1792 }
1793 short_mod := ssa_module_tail_name(normalized_mod)
1794 if short_mod != '' && short_mod != normalized_mod {
1795 short_qualified := ssa_module_storage_name(short_mod, type_name)
1796 if type_id := b.lookup_type_alias_ssa(short_qualified) {
1797 return type_id
1798 }
1799 }
1800 return none
1801}
1802
1803fn (b &Builder) lookup_type_alias_ssa(name string) ?TypeID {
1804 if name == '' || !ssa_string_ok(name) {
1805 return none
1806 }
1807 if type_id := b.type_aliases[name] {
1808 return type_id
1809 }
1810 if b.cur_module != '' && b.cur_module != 'main' && !name.contains('__') {
1811 if type_id := b.type_aliases['${b.cur_module}__${name}'] {
1812 return type_id
1813 }
1814 short_mod := ssa_module_tail_name(b.cur_module)
1815 if short_mod != '' && short_mod != b.cur_module {
1816 if type_id := b.type_aliases['${short_mod}__${name}'] {
1817 return type_id
1818 }
1819 }
1820 }
1821 if name.contains('__') {
1822 mod_name := name.all_before_last('__')
1823 type_name := name.all_after_last('__')
1824 short_mod := ssa_module_tail_name(mod_name)
1825 if short_mod != '' && short_mod != mod_name {
1826 if type_id := b.type_aliases['${short_mod}__${type_name}'] {
1827 return type_id
1828 }
1829 }
1830 qualified_suffix := '__${name}'
1831 for alias_name, type_id in b.type_aliases {
1832 if alias_name.ends_with(qualified_suffix) {
1833 return type_id
1834 }
1835 }
1836 }
1837 return none
1838}
1839
1840fn (b &Builder) sizeof_type_alias_name(name string) ?int {
1841 if type_id := b.lookup_type_alias_ssa(name) {
1842 return b.type_byte_size(type_id)
1843 }
1844 normalized := name.replace('.', '__')
1845 if normalized != name {
1846 if type_id := b.lookup_type_alias_ssa(normalized) {
1847 return b.type_byte_size(type_id)
1848 }
1849 }
1850 return none
1851}
1852
1853fn struct_field_array_elem_key(type_id TypeID, field_name string) string {
1854 return '${int(type_id)}:${field_name}'
1855}
1856
1857fn (mut b Builder) record_struct_field_array_elem_type(type_id TypeID, field_name string, field_type ast.Expr, field_ssa_type TypeID) {
1858 if field_name == '' || field_ssa_type != b.get_array_type() {
1859 return
1860 }
1861 elem_type := b.array_elem_type_from_ast_type(field_type)
1862 if elem_type != 0 {
1863 b.struct_field_array_elem_types[struct_field_array_elem_key(type_id, field_name)] = elem_type
1864 }
1865}
1866
1867// record_struct_field_array_elem_type_from_flat (s239) is the cursor mirror of
1868// record_struct_field_array_elem_type. `field_type_c` is the field's typ cursor;
1869// elem-type inference reuses array_elem_type_from_ast_type_from_flat (s233).
1870fn (mut b Builder) record_struct_field_array_elem_type_from_flat(type_id TypeID, field_name string, field_type_c ast.Cursor, field_ssa_type TypeID) {
1871 if field_name == '' || field_ssa_type != b.get_array_type() {
1872 return
1873 }
1874 elem_type := b.array_elem_type_from_ast_type_from_flat(field_type_c)
1875 if elem_type != 0 {
1876 b.struct_field_array_elem_types[struct_field_array_elem_key(type_id, field_name)] = elem_type
1877 }
1878}
1879
1880fn (b &Builder) struct_field_array_elem_type(type_id TypeID, field_name string) TypeID {
1881 if field_name == '' {
1882 return 0
1883 }
1884 mut cur_type := type_id
1885 for cur_type > 0 && int(cur_type) < b.mod.type_store.types.len {
1886 if elem_type := b.struct_field_array_elem_types[struct_field_array_elem_key(cur_type,
1887 field_name)]
1888 {
1889 return elem_type
1890 }
1891 typ := b.mod.type_store.types[cur_type]
1892 if typ.kind == .ptr_t && typ.elem_type > 0 {
1893 cur_type = typ.elem_type
1894 continue
1895 }
1896 break
1897 }
1898 return 0
1899}
1900
1901fn (mut b Builder) get_string_type() TypeID {
1902 return b.struct_types['string'] or { 0 }
1903}
1904
1905fn (mut b Builder) get_array_type() TypeID {
1906 return b.struct_types['array'] or { 0 }
1907}
1908
1909fn (b &Builder) is_string_like_ssa_type(typ_id TypeID) bool {
1910 if typ_id == 0 || int(typ_id) >= b.mod.type_store.types.len {
1911 return false
1912 }
1913 str_type := b.struct_types['string'] or { TypeID(0) }
1914 if str_type != 0 && typ_id == str_type {
1915 return true
1916 }
1917 typ := b.mod.type_store.types[typ_id]
1918 return typ.kind == .ptr_t && typ.elem_type == str_type
1919}
1920
1921fn (mut b Builder) load_string_like_value(val_id ValueID) ValueID {
1922 if val_id <= 0 || int(val_id) >= b.mod.values.len {
1923 return val_id
1924 }
1925 str_type := b.get_string_type()
1926 if str_type == 0 {
1927 return val_id
1928 }
1929 typ_id := b.mod.values[val_id].typ
1930 if typ_id == str_type {
1931 return val_id
1932 }
1933 if typ_id > 0 && int(typ_id) < b.mod.type_store.types.len {
1934 typ := b.mod.type_store.types[typ_id]
1935 if typ.kind == .ptr_t && typ.elem_type == str_type {
1936 return b.mod.add_instr(.load, b.cur_block, str_type, [val_id])
1937 }
1938 }
1939 return val_id
1940}
1941
1942fn (mut b Builder) get_ierror_storage_type() TypeID {
1943 if 'IError' in b.struct_types {
1944 return b.struct_types['IError']
1945 }
1946 return b.mod.type_store.get_int(64)
1947}
1948
1949fn (mut b Builder) get_option_wrapper_type(base_type TypeID) TypeID {
1950 key := base_type.str()
1951 if type_id := b.option_wrapper_types[key] {
1952 return type_id
1953 }
1954 state_type := b.mod.type_store.get_int(8)
1955 err_type := b.get_ierror_storage_type()
1956 mut field_types := []TypeID{cap: 3}
1957 mut field_names := []string{cap: 3}
1958 field_types << state_type
1959 field_names << 'state'
1960 field_types << err_type
1961 field_names << 'err'
1962 if base_type != 0 {
1963 field_types << base_type
1964 field_names << 'data'
1965 }
1966 type_id := b.mod.type_store.register(Type{
1967 kind: .struct_t
1968 fields: field_types
1969 field_names: field_names
1970 })
1971 b.option_wrapper_types[key] = type_id
1972 return type_id
1973}
1974
1975fn (mut b Builder) get_result_wrapper_type(base_type TypeID) TypeID {
1976 key := base_type.str()
1977 if type_id := b.result_wrapper_types[key] {
1978 return type_id
1979 }
1980 bool_type := b.mod.type_store.get_int(1)
1981 err_type := b.get_ierror_storage_type()
1982 mut field_types := []TypeID{cap: 3}
1983 mut field_names := []string{cap: 3}
1984 field_types << bool_type
1985 field_names << 'is_error'
1986 field_types << err_type
1987 field_names << 'err'
1988 if base_type != 0 {
1989 field_types << base_type
1990 field_names << 'data'
1991 }
1992 type_id := b.mod.type_store.register(Type{
1993 kind: .struct_t
1994 fields: field_types
1995 field_names: field_names
1996 })
1997 b.result_wrapper_types[key] = type_id
1998 return type_id
1999}
2000
2001fn (b &Builder) is_option_wrapper_type(type_id TypeID) bool {
2002 if type_id <= 0 || type_id >= b.mod.type_store.types.len {
2003 return false
2004 }
2005 typ := b.mod.type_store.types[type_id]
2006 return typ.kind == .struct_t && typ.field_names.len >= 2 && typ.field_names[0] == 'state'
2007 && typ.field_names[1] == 'err'
2008}
2009
2010fn (b &Builder) is_result_wrapper_type(type_id TypeID) bool {
2011 if type_id <= 0 || type_id >= b.mod.type_store.types.len {
2012 return false
2013 }
2014 typ := b.mod.type_store.types[type_id]
2015 return typ.kind == .struct_t && typ.field_names.len >= 2 && typ.field_names[0] == 'is_error'
2016 && typ.field_names[1] == 'err'
2017}
2018
2019fn (b &Builder) is_wrapper_type(type_id TypeID) bool {
2020 return b.is_option_wrapper_type(type_id) || b.is_result_wrapper_type(type_id)
2021}
2022
2023fn (b &Builder) wrapper_has_data(type_id TypeID) bool {
2024 if type_id <= 0 || type_id >= b.mod.type_store.types.len {
2025 return false
2026 }
2027 typ := b.mod.type_store.types[type_id]
2028 return typ.kind == .struct_t && typ.field_names.len >= 3 && typ.field_names[2] == 'data'
2029}
2030
2031fn (b &Builder) wrapper_data_type(type_id TypeID) TypeID {
2032 if !b.wrapper_has_data(type_id) {
2033 return 0
2034 }
2035 return b.mod.type_store.types[type_id].fields[2]
2036}
2037
2038fn (b &Builder) current_fn_return_type() TypeID {
2039 if b.cur_func >= 0 && b.cur_func < b.mod.funcs.len {
2040 return b.mod.funcs[b.cur_func].typ
2041 }
2042 return 0
2043}
2044
2045fn (mut b Builder) build_unwrapped_postfix(expr ast.PostfixExpr, wrapped_val ValueID) ValueID {
2046 if wrapped_val <= 0 || wrapped_val >= b.mod.values.len {
2047 return wrapped_val
2048 }
2049 wrapped_type := b.mod.values[wrapped_val].typ
2050 if !b.is_wrapper_type(wrapped_type) {
2051 return wrapped_val
2052 }
2053 wrapper_info := b.mod.type_store.types[wrapped_type]
2054 i32_t := b.mod.type_store.get_int(32)
2055 bool_t := b.mod.type_store.get_int(1)
2056 flag_idx := b.mod.get_or_add_const(i32_t, '0')
2057 err_idx := b.mod.get_or_add_const(i32_t, '1')
2058 flag_type := wrapper_info.fields[0]
2059 flag_val := b.mod.add_instr(.extractvalue, b.cur_block, flag_type, [wrapped_val, flag_idx])
2060 mut fail_cond := flag_val
2061 if b.is_option_wrapper_type(wrapped_type) {
2062 zero_flag := b.mod.get_or_add_const(flag_type, '0')
2063 fail_cond = b.mod.add_instr(.ne, b.cur_block, bool_t, [flag_val, zero_flag])
2064 }
2065 fail_block := b.mod.add_block(b.cur_func, 'postfix_fail')
2066 ok_block := b.mod.add_block(b.cur_func, 'postfix_ok')
2067 b.mod.add_instr(.br, b.cur_block, 0,
2068 [fail_cond, b.mod.blocks[fail_block].val_id, b.mod.blocks[ok_block].val_id])
2069 b.add_edge(b.cur_block, fail_block)
2070 b.add_edge(b.cur_block, ok_block)
2071
2072 b.cur_block = fail_block
2073 fn_ret_type := b.current_fn_return_type()
2074 if fn_ret_type != 0 && b.is_wrapper_type(fn_ret_type) {
2075 if fn_ret_type == wrapped_type {
2076 b.mod.add_instr(.ret, b.cur_block, 0, [wrapped_val])
2077 } else {
2078 err_type := if wrapper_info.fields.len > 1 {
2079 wrapper_info.fields[1]
2080 } else {
2081 b.get_ierror_storage_type()
2082 }
2083 err_val := b.mod.add_instr(.extractvalue, b.cur_block, err_type, [
2084 wrapped_val,
2085 err_idx,
2086 ])
2087 propagated := b.build_wrapper_value(fn_ret_type, false, err_val, false)
2088 b.mod.add_instr(.ret, b.cur_block, 0, [propagated])
2089 }
2090 } else {
2091 panic_name := if 'builtin__panic' in b.fn_index { 'builtin__panic' } else { 'panic' }
2092 if panic_name in b.fn_index {
2093 panic_ref := b.get_or_create_fn_ref(panic_name, 0)
2094 panic_msg := b.build_string_literal(ast.StringLiteral{
2095 kind: .v
2096 value: if expr.op == .not {
2097 "'postfix ! unwrap failed'"
2098 } else {
2099 "'postfix ? unwrap failed'"
2100 }
2101 })
2102 b.mod.add_instr(.call, b.cur_block, 0, [panic_ref, panic_msg])
2103 }
2104 b.mod.add_instr(.unreachable, b.cur_block, 0, []ValueID{})
2105 }
2106
2107 b.cur_block = ok_block
2108 if !b.wrapper_has_data(wrapped_type) {
2109 return b.mod.get_or_add_const(b.mod.type_store.get_int(32), '0')
2110 }
2111 data_type := b.wrapper_data_type(wrapped_type)
2112 data_idx := b.mod.get_or_add_const(i32_t, '2')
2113 return b.mod.add_instr(.extractvalue, b.cur_block, data_type, [wrapped_val, data_idx])
2114}
2115
2116fn (b &Builder) is_none_expr(expr ast.Expr) bool {
2117 match expr {
2118 ast.Ident {
2119 return expr.name == 'none'
2120 }
2121 ast.Keyword {
2122 return expr.tok == .key_none
2123 }
2124 ast.Type {
2125 return expr is ast.NoneType
2126 }
2127 else {
2128 return false
2129 }
2130 }
2131}
2132
2133fn (b &Builder) is_error_expr(expr ast.Expr) bool {
2134 error_fn_names := ['error', 'error_posix', 'error_with_code', 'error_win32']
2135 match expr {
2136 ast.Ident {
2137 return expr.name == 'err'
2138 }
2139 ast.CallExpr {
2140 return expr.lhs is ast.Ident && expr.lhs.name in error_fn_names
2141 }
2142 ast.CallOrCastExpr {
2143 return expr.lhs is ast.Ident && expr.lhs.name in error_fn_names
2144 }
2145 else {
2146 return false
2147 }
2148 }
2149}
2150
2151fn (b &Builder) module_scope_has_direct_type(module_name string, type_name string) bool {
2152 if b.env == unsafe { nil } {
2153 return false
2154 }
2155 if scope := b.env.get_scope(module_name) {
2156 if _ := scope.lookup_type(type_name) {
2157 return true
2158 }
2159 }
2160 return false
2161}
2162
2163fn (b &Builder) short_type_name_is_unshadowed_builtin(name string) bool {
2164 if name == '' || !b.module_scope_has_direct_type('builtin', name) {
2165 return false
2166 }
2167 if b.cur_module != '' && b.cur_module != 'builtin'
2168 && b.module_scope_has_direct_type(b.cur_module, name) {
2169 return false
2170 }
2171 return true
2172}
2173
2174fn (b &Builder) type_name_is_builtin_ierror(name string) bool {
2175 if name == 'builtin__IError' || name == 'builtin.IError' {
2176 return b.module_scope_has_direct_type('builtin', 'IError')
2177 }
2178 if name == 'IError' {
2179 return b.short_type_name_is_unshadowed_builtin('IError')
2180 }
2181 return false
2182}
2183
2184fn (b &Builder) type_name_is_builtin_error(name string) bool {
2185 if name == 'builtin__Error' || name == 'builtin.Error' {
2186 return b.module_scope_has_direct_type('builtin', 'Error')
2187 }
2188 if name == 'Error' {
2189 return b.short_type_name_is_unshadowed_builtin('Error')
2190 }
2191 return false
2192}
2193
2194fn (mut b Builder) type_is_ierror_like(typ types.Type) bool {
2195 unwrapped := b.unwrap_alias_type(typ)
2196 match unwrapped {
2197 types.Interface {
2198 return b.type_name_is_builtin_ierror(unwrapped.name)
2199 }
2200 types.Pointer {
2201 return b.type_is_ierror_like(unwrapped.base_type)
2202 }
2203 types.Struct {
2204 if b.type_name_is_builtin_error(unwrapped.name) {
2205 return true
2206 }
2207 for embedded in unwrapped.embedded {
2208 if b.type_name_is_builtin_error(embedded.name) {
2209 return true
2210 }
2211 }
2212 return false
2213 }
2214 else {
2215 return false
2216 }
2217 }
2218}
2219
2220fn (mut b Builder) expr_is_ierror_like(expr ast.Expr) bool {
2221 typ := b.get_checked_expr_type(expr) or { return false }
2222 return b.type_is_ierror_like(typ)
2223}
2224
2225fn (mut b Builder) cursor_is_ierror_like(c ast.Cursor) bool {
2226 typ := b.get_checked_expr_type_from_flat(c) or { return false }
2227 return b.type_is_ierror_like(typ)
2228}
2229
2230fn (mut b Builder) type_is_ierror_interface(typ types.Type) bool {
2231 unwrapped := b.unwrap_alias_type(typ)
2232 match unwrapped {
2233 types.Interface {
2234 return b.type_name_is_builtin_ierror(unwrapped.name)
2235 }
2236 else {
2237 return false
2238 }
2239 }
2240}
2241
2242fn (mut b Builder) type_is_concrete_ierror(typ types.Type) bool {
2243 unwrapped := b.unwrap_alias_type(typ)
2244 match unwrapped {
2245 types.Pointer {
2246 return b.type_is_concrete_ierror(unwrapped.base_type)
2247 }
2248 types.Struct {
2249 if b.type_name_is_builtin_error(unwrapped.name) {
2250 return true
2251 }
2252 for embedded in unwrapped.embedded {
2253 if b.type_name_is_builtin_error(embedded.name) {
2254 return true
2255 }
2256 }
2257 return false
2258 }
2259 else {
2260 return false
2261 }
2262 }
2263}
2264
2265fn (mut b Builder) checked_type_for_ssa_type(type_id TypeID) ?types.Type {
2266 if !b.valid_type_id(type_id) {
2267 return none
2268 }
2269 type_name := b.mod.c_struct_names[int(type_id)] or { return none }
2270 return b.lookup_checked_type_by_name(type_name)
2271}
2272
2273fn (mut b Builder) ierror_tag_for_checked_type(typ types.Type) ?ValueID {
2274 if !b.type_is_concrete_ierror(typ) {
2275 return none
2276 }
2277 unwrapped := b.unwrap_alias_type(typ)
2278 concrete_type := match unwrapped {
2279 types.Pointer {
2280 b.unwrap_alias_type(unwrapped.base_type)
2281 }
2282 else {
2283 unwrapped
2284 }
2285 }
2286
2287 type_id := b.type_to_ssa(concrete_type)
2288 if !b.valid_type_id(type_id) || int(type_id) == 0 {
2289 return none
2290 }
2291 i64_t := b.mod.type_store.get_int(64)
2292 return b.mod.get_or_add_const(i64_t, int(type_id).str())
2293}
2294
2295fn (mut b Builder) ierror_tag_for_ssa_type(type_id TypeID) ?ValueID {
2296 typ := b.checked_type_for_ssa_type(type_id) or { return none }
2297 return b.ierror_tag_for_checked_type(typ)
2298}
2299
2300fn (mut b Builder) ierror_tag_for_type_expr(expr ast.Expr) ?ValueID {
2301 type_id := b.ast_type_to_ssa(expr)
2302 return b.ierror_tag_for_ssa_type(type_id)
2303}
2304
2305fn (mut b Builder) ierror_tag_for_type_cursor(c ast.Cursor) ?ValueID {
2306 type_id := b.ast_type_to_ssa_from_flat(c)
2307 return b.ierror_tag_for_ssa_type(type_id)
2308}
2309
2310fn (mut b Builder) type_expr_is_ierror_interface(expr ast.Expr) bool {
2311 name := match expr {
2312 ast.Ident {
2313 expr.name
2314 }
2315 ast.SelectorExpr {
2316 expr.name().replace('.', '__')
2317 }
2318 else {
2319 ''
2320 }
2321 }
2322
2323 if b.type_name_is_builtin_ierror(name) {
2324 return true
2325 }
2326 if name != '' {
2327 return false
2328 }
2329 typ := b.get_checked_expr_type(expr) or { return false }
2330 return b.type_is_ierror_interface(typ)
2331}
2332
2333fn (mut b Builder) type_cursor_is_ierror_interface(c ast.Cursor) bool {
2334 name := match c.kind() {
2335 .expr_ident {
2336 c.name()
2337 }
2338 .expr_selector {
2339 lhs := c.edge(0)
2340 rhs := c.edge(1)
2341 if lhs.kind() == .expr_ident {
2342 '${lhs.name()}__${rhs.name()}'
2343 } else {
2344 rhs.name()
2345 }
2346 }
2347 else {
2348 ''
2349 }
2350 }
2351
2352 if b.type_name_is_builtin_ierror(name) {
2353 return true
2354 }
2355 if name != '' {
2356 return false
2357 }
2358 typ := b.get_checked_expr_type_from_flat(c) or { return false }
2359 return b.type_is_ierror_interface(typ)
2360}
2361
2362fn (mut b Builder) ierror_tag_for_expr(expr ast.Expr) ?ValueID {
2363 match expr {
2364 ast.CallOrCastExpr {
2365 if b.type_expr_is_ierror_interface(expr.lhs) {
2366 return b.ierror_tag_for_expr(expr.expr)
2367 }
2368 }
2369 ast.CastExpr {
2370 if b.type_expr_is_ierror_interface(expr.typ) {
2371 return b.ierror_tag_for_expr(expr.expr)
2372 }
2373 }
2374 ast.ParenExpr {
2375 return b.ierror_tag_for_expr(expr.expr)
2376 }
2377 ast.ModifierExpr {
2378 return b.ierror_tag_for_expr(expr.expr)
2379 }
2380 else {}
2381 }
2382
2383 typ := b.get_checked_expr_type(expr) or { return none }
2384 return b.ierror_tag_for_checked_type(typ)
2385}
2386
2387fn (mut b Builder) ierror_tag_for_cursor(c ast.Cursor) ?ValueID {
2388 match c.kind() {
2389 .expr_call_or_cast {
2390 if b.type_cursor_is_ierror_interface(c.edge(0)) {
2391 return b.ierror_tag_for_cursor(c.edge(1))
2392 }
2393 }
2394 .expr_cast {
2395 if b.type_cursor_is_ierror_interface(c.edge(0)) {
2396 return b.ierror_tag_for_cursor(c.edge(1))
2397 }
2398 }
2399 .expr_paren, .expr_modifier {
2400 return b.ierror_tag_for_cursor(c.edge(0))
2401 }
2402 else {}
2403 }
2404
2405 typ := b.get_checked_expr_type_from_flat(c) or { return none }
2406 return b.ierror_tag_for_checked_type(typ)
2407}
2408
2409fn (mut b Builder) build_ierror_concrete_compare(subject ValueID, subject_expr ast.Expr, variant_expr ast.Expr, op token.Token) ?ValueID {
2410 if op !in [.eq, .ne, .key_is, .not_is] {
2411 return none
2412 }
2413 subject_type := b.get_checked_expr_type(subject_expr) or { return none }
2414 if !b.type_is_ierror_interface(subject_type) {
2415 return none
2416 }
2417 expected := b.ierror_tag_for_type_expr(variant_expr) or { return none }
2418 bool_type := b.mod.type_store.get_int(1)
2419 cmp_op := if op in [.eq, .key_is] { OpCode.eq } else { OpCode.ne }
2420 return b.mod.add_instr(cmp_op, b.cur_block, bool_type, [subject, expected])
2421}
2422
2423fn (mut b Builder) build_ierror_concrete_compare_from_flat(subject ValueID, subject_c ast.Cursor, variant_c ast.Cursor, op token.Token) ?ValueID {
2424 if op !in [.eq, .ne, .key_is, .not_is] {
2425 return none
2426 }
2427 subject_type := b.get_checked_expr_type_from_flat(subject_c) or { return none }
2428 if !b.type_is_ierror_interface(subject_type) {
2429 return none
2430 }
2431 expected := b.ierror_tag_for_type_cursor(variant_c) or { return none }
2432 bool_type := b.mod.type_store.get_int(1)
2433 cmp_op := if op in [.eq, .key_is] { OpCode.eq } else { OpCode.ne }
2434 return b.mod.add_instr(cmp_op, b.cur_block, bool_type, [subject, expected])
2435}
2436
2437fn (mut b Builder) wrapper_value_is_valid_payload(val ValueID, wrapper_type TypeID) bool {
2438 if !b.wrapper_has_data(wrapper_type) {
2439 return false
2440 }
2441 if val <= 0 || val >= b.mod.values.len {
2442 return false
2443 }
2444 return b.mod.values[val].typ == b.wrapper_data_type(wrapper_type)
2445}
2446
2447fn (mut b Builder) build_wrapper_value(wrapper_type TypeID, success bool, payload ValueID, has_payload bool) ValueID {
2448 if wrapper_type <= 0 || wrapper_type >= b.mod.type_store.types.len {
2449 return payload
2450 }
2451 wrapper_info := b.mod.type_store.types[wrapper_type]
2452 if wrapper_info.kind != .struct_t || wrapper_info.field_names.len < 2 {
2453 return payload
2454 }
2455 mut wrapper := b.mod.get_or_add_const(wrapper_type, '0')
2456 i32_t := b.mod.type_store.get_int(32)
2457 flag_idx := b.mod.get_or_add_const(i32_t, '0')
2458 err_idx := b.mod.get_or_add_const(i32_t, '1')
2459 flag_type := wrapper_info.fields[0]
2460 flag_val := if b.is_option_wrapper_type(wrapper_type) {
2461 // V options use state==0 for success and state==2 for none/error.
2462 b.mod.get_or_add_const(flag_type, if success { '0' } else { '2' })
2463 } else {
2464 b.mod.get_or_add_const(flag_type, if success { '0' } else { '1' })
2465 }
2466 wrapper = b.mod.add_instr(.insertvalue, b.cur_block, wrapper_type,
2467 [wrapper, flag_val, flag_idx])
2468 if !success {
2469 err_type := wrapper_info.fields[1]
2470 mut err_val := payload
2471 if err_val == 0 {
2472 err_val = b.mod.get_or_add_const(err_type, '0')
2473 } else if b.mod.values[err_val].typ != err_type {
2474 err_val = b.cast_value_to_type(err_val, err_type)
2475 }
2476 wrapper = b.mod.add_instr(.insertvalue, b.cur_block, wrapper_type,
2477 [wrapper, err_val, err_idx])
2478 }
2479 if has_payload && b.wrapper_has_data(wrapper_type) {
2480 data_idx := b.mod.get_or_add_const(i32_t, '2')
2481 data_type := wrapper_info.fields[2]
2482 mut data_val := payload
2483 if data_val == 0 {
2484 data_val = b.mod.get_or_add_const(data_type, '0')
2485 } else if b.mod.values[data_val].typ != data_type {
2486 data_val = b.cast_value_to_type(data_val, data_type)
2487 }
2488 wrapper = b.mod.add_instr(.insertvalue, b.cur_block, wrapper_type, [wrapper, data_val,
2489 data_idx])
2490 }
2491 return wrapper
2492}
2493
2494fn (mut b Builder) coerce_wrapper_value(expr ast.Expr, val ValueID, wrapper_type TypeID) ValueID {
2495 if !b.is_wrapper_type(wrapper_type) {
2496 return val
2497 }
2498 if b.is_none_expr(expr) {
2499 return b.build_wrapper_value(wrapper_type, false, 0, false)
2500 }
2501 if b.is_error_expr(expr) {
2502 return b.build_wrapper_value(wrapper_type, false, val, false)
2503 }
2504 if b.is_result_wrapper_type(wrapper_type) && b.expr_is_ierror_like(expr)
2505 && !b.wrapper_value_is_valid_payload(val, wrapper_type) {
2506 err_payload := b.ierror_tag_for_expr(expr) or { val }
2507 return b.build_wrapper_value(wrapper_type, false, err_payload, false)
2508 }
2509 if val > 0 && val < b.mod.values.len && b.mod.values[val].typ == wrapper_type {
2510 if payload := b.wrapper_payload_bitcast_source(val, wrapper_type) {
2511 return b.build_wrapper_value(wrapper_type, true, payload, true)
2512 }
2513 match b.mod.values[val].kind {
2514 .argument, .global, .instruction {
2515 return val
2516 }
2517 else {}
2518 }
2519 }
2520 return b.build_wrapper_value(wrapper_type, true, val, true)
2521}
2522
2523fn (b &Builder) wrapper_payload_bitcast_source(val ValueID, wrapper_type TypeID) ?ValueID {
2524 if val <= 0 || val >= b.mod.values.len || !b.wrapper_has_data(wrapper_type) {
2525 return none
2526 }
2527 value := b.mod.values[val]
2528 if value.kind != .instruction || value.typ != wrapper_type {
2529 return none
2530 }
2531 instr := b.mod.instrs[value.index]
2532 if instr.op != .bitcast || instr.operands.len != 1 {
2533 return none
2534 }
2535 payload := instr.operands[0]
2536 if payload <= 0 || payload >= b.mod.values.len {
2537 return none
2538 }
2539 payload_type := b.mod.values[payload].typ
2540 if payload_type == wrapper_type || b.is_wrapper_type(payload_type) {
2541 return none
2542 }
2543 return payload
2544}
2545
2546fn (mut b Builder) expr_type(e ast.Expr) TypeID {
2547 if !builder_expr_ok(e) {
2548 return b.mod.type_store.get_int(64)
2549 }
2550 if typ := b.get_checked_expr_type(e) {
2551 return b.type_to_ssa(typ)
2552 }
2553 // Fallback for literals
2554 match e {
2555 ast.BasicLiteral {
2556 if e.kind == .key_true || e.kind == .key_false {
2557 return b.mod.type_store.get_int(1)
2558 }
2559 if e.kind == .number && (e.value.contains('.')
2560 || (!e.value.starts_with('0x') && !e.value.starts_with('0X')
2561 && (e.value.contains('e') || e.value.contains('E')))) {
2562 return b.mod.type_store.get_float(64)
2563 }
2564 return b.mod.type_store.get_int(64)
2565 }
2566 ast.StringLiteral {
2567 return b.get_string_type()
2568 }
2569 else {
2570 return b.mod.type_store.get_int(64)
2571 }
2572 }
2573}
2574
2575fn (mut b Builder) const_field_type(field_name string, value ast.Expr) TypeID {
2576 if b.env != unsafe { nil } {
2577 if scope := b.env.get_scope(b.cur_module) {
2578 if obj := scope.lookup_parent(field_name, 0) {
2579 obj_typ := obj.typ()
2580 obj_type := b.type_to_ssa(obj_typ)
2581 if obj_type != 0 {
2582 return obj_type
2583 }
2584 }
2585 }
2586 }
2587 return b.expr_type(value)
2588}
2589
2590// const_field_type_from_flat (s235) is the cursor mirror of const_field_type:
2591// the scope lookup is name-only, the fallback uses expr_type_from_flat (pos-only).
2592fn (mut b Builder) const_field_type_from_flat(field_name string, value_c ast.Cursor) TypeID {
2593 if b.env != unsafe { nil } {
2594 if scope := b.env.get_scope(b.cur_module) {
2595 if obj := scope.lookup_parent(field_name, 0) {
2596 obj_typ := obj.typ()
2597 obj_type := b.type_to_ssa(obj_typ)
2598 if obj_type != 0 {
2599 return obj_type
2600 }
2601 }
2602 }
2603 }
2604 return b.expr_type_from_flat(value_c)
2605}
2606
2607fn (mut b Builder) scope_field_type(field_name string) TypeID {
2608 if b.env != unsafe { nil } {
2609 if scope := b.env.get_scope(b.cur_module) {
2610 if obj := scope.lookup_parent(field_name, 0) {
2611 obj_typ := obj.typ()
2612 obj_type := b.type_to_ssa(obj_typ)
2613 if obj_type != 0 {
2614 return obj_type
2615 }
2616 }
2617 }
2618 }
2619 return 0
2620}
2621
2622fn (mut b Builder) global_field_type(field ast.FieldDecl) TypeID {
2623 scope_type := b.scope_field_type(field.name)
2624 if scope_type != 0 {
2625 return scope_type
2626 }
2627 if builder_expr_ok(field.typ) && field.typ !is ast.EmptyExpr {
2628 return b.ast_type_to_ssa(field.typ)
2629 }
2630 if field.value is ast.ArrayInitExpr {
2631 arr := field.value as ast.ArrayInitExpr
2632 if builder_expr_ok(arr.typ) && arr.typ !is ast.EmptyExpr {
2633 return b.ast_type_to_ssa(arr.typ)
2634 }
2635 }
2636 if builder_expr_ok(field.value) && field.value !is ast.EmptyExpr {
2637 return b.expr_type(field.value)
2638 }
2639 return b.mod.type_store.get_int(64)
2640}
2641
2642// global_field_type_from_flat (s237) is the cursor mirror of global_field_type.
2643// field_c is an .aux_field_decl: name in name_id, typ at edge(0), value at edge(1).
2644// `field.typ !is ast.EmptyExpr` maps to `edge(0).kind() != .expr_empty`; the
2645// ArrayInitExpr.typ check uses value_c.edge(0). scope/ssa/expr lookups identical.
2646fn (mut b Builder) global_field_type_from_flat(field_c ast.Cursor) TypeID {
2647 scope_type := b.scope_field_type(field_c.name())
2648 if scope_type != 0 {
2649 return scope_type
2650 }
2651 typ_c := field_c.edge(0)
2652 if typ_c.kind() != .expr_empty {
2653 return b.ast_type_to_ssa_from_flat(typ_c)
2654 }
2655 value_c := field_c.edge(1)
2656 if value_c.kind() == .expr_array_init {
2657 arr_typ_c := value_c.edge(0)
2658 if arr_typ_c.kind() != .expr_empty {
2659 return b.ast_type_to_ssa_from_flat(arr_typ_c)
2660 }
2661 }
2662 if value_c.kind() != .expr_empty {
2663 return b.expr_type_from_flat(value_c)
2664 }
2665 return b.mod.type_store.get_int(64)
2666}
2667
2668fn (mut b Builder) types_type_c_name(t types.Type) string {
2669 if !types.type_has_valid_payload(t) {
2670 return 'unknown'
2671 }
2672 match t {
2673 types.Primitive {
2674 if t.props.has(.boolean) {
2675 return 'bool'
2676 }
2677 if t.props.has(.float) {
2678 return if t.size == 32 { 'f32' } else { 'f64' }
2679 }
2680 if t.props.has(.integer) {
2681 if t.props.has(.untyped) {
2682 return 'int'
2683 }
2684 size := if t.size == 0 { 32 } else { int(t.size) }
2685 is_signed := !t.props.has(.unsigned)
2686 return if is_signed {
2687 match size {
2688 8 { 'i8' }
2689 16 { 'i16' }
2690 64 { 'i64' }
2691 else { 'int' }
2692 }
2693 } else {
2694 match size {
2695 8 { 'u8' }
2696 16 { 'u16' }
2697 32 { 'u32' }
2698 else { 'u64' }
2699 }
2700 }
2701 }
2702 return 'int'
2703 }
2704 types.Pointer {
2705 return b.types_type_c_name(t.base_type) + '*'
2706 }
2707 types.String {
2708 return 'string'
2709 }
2710 types.Struct {
2711 return t.name
2712 }
2713 types.Enum {
2714 return t.name
2715 }
2716 types.Void {
2717 return 'void'
2718 }
2719 types.Char {
2720 return 'char'
2721 }
2722 types.Alias {
2723 base := b.resolve_alias_base_type(t) or {
2724 return if t.name != '' { t.name } else { 'unknown' }
2725 }
2726 return b.types_type_c_name(base)
2727 }
2728 types.Array {
2729 // []rune → Array_rune, []int → Array_int, etc.
2730 return 'Array_${b.types_type_c_name(t.elem_type)}'
2731 }
2732 types.Rune {
2733 return 'rune'
2734 }
2735 types.SumType {
2736 return t.name
2737 }
2738 types.Interface {
2739 return t.name
2740 }
2741 types.FnType {
2742 return 'FnType'
2743 }
2744 types.Map {
2745 return 'map'
2746 }
2747 types.OptionType {
2748 // Unwrap Option to base type for method resolution
2749 // (e.g., r.str() where r was unwrapped from ?int should resolve to int__str)
2750 return b.types_type_c_name(t.base_type)
2751 }
2752 types.ResultType {
2753 // Unwrap Result to base type for method resolution
2754 return b.types_type_c_name(t.base_type)
2755 }
2756 types.ArrayFixed {
2757 return 'ArrayFixed'
2758 }
2759 else {
2760 return 'int'
2761 }
2762 }
2763}
2764
2765// --- Phase 1: Register types ---
2766
2767// Pass 1: Register struct names as forward declarations (empty structs),
2768// enums, and sumtypes. This ensures all struct names are in struct_types
2769// before any field types are resolved.
2770fn (mut b Builder) register_types_pass1(file ast.File) {
2771 for stmt in file.stmts {
2772 match stmt {
2773 ast.StructDecl {
2774 b.register_struct_name(stmt)
2775 }
2776 ast.EnumDecl {
2777 b.register_enum(stmt)
2778 }
2779 ast.TypeDecl {
2780 if stmt.variants.len > 0 {
2781 b.register_sumtype(stmt)
2782 }
2783 }
2784 else {}
2785 }
2786 }
2787}
2788
2789// register_types_pass1_from_flat is the flat-cursor counterpart of
2790// `register_types_pass1`. It walks one file's top-level stmts via FileCursor
2791// without rehydrating the file, and rehydrates only the StructDecl /
2792// EnumDecl / TypeDecl nodes via `flat.decode_stmt`. ModuleStmt, FnDecl,
2793// ConstDecl, GlobalDecl, ImportStmt, etc. are skipped entirely — never
2794// decoded — which is the actual allocation reduction over the legacy walker.
2795fn (mut b Builder) register_types_pass1_from_flat(file_cursor ast.FileCursor) {
2796 stmts := file_cursor.stmts()
2797 for si in 0 .. stmts.len() {
2798 c := stmts.at(si)
2799 match c.kind() {
2800 .stmt_struct_decl {
2801 // s239: cursor-native struct-name registration — no decode_stmt.
2802 b.register_struct_name_from_flat(c)
2803 }
2804 .stmt_enum_decl {
2805 // s238: cursor-native enum registration — no decode_stmt.
2806 b.register_enum_from_flat(c)
2807 }
2808 .stmt_type_decl {
2809 // s240: cursor-native sumtype registration — no decode_stmt.
2810 // TypeDecl variants list is edge3 (list_at(3)).
2811 if c.list_at(3).len() > 0 {
2812 b.register_sumtype_from_flat(c)
2813 }
2814 }
2815 else {}
2816 }
2817 }
2818}
2819
2820// Pass 2: Fill in struct field types. All struct names are now registered,
2821// so cross-module struct references (e.g., &scanner.Scanner in Parser)
2822// resolve correctly to the struct type instead of falling back to i64.
2823fn (mut b Builder) register_types_pass2(file ast.File) {
2824 b.module_import_aliases = module_import_aliases_from_imports(file.imports)
2825 nstmts := file.stmts.len
2826 for si in 0 .. nstmts {
2827 stmt := file.stmts[si]
2828 if stmt is ast.StructDecl {
2829 b.register_struct_fields(stmt)
2830 }
2831 }
2832}
2833
2834// register_types_pass2_from_flat is the flat-cursor counterpart of
2835// `register_types_pass2`. Same shape as `register_types_pass1_from_flat`
2836// (s169) but filtered to `.stmt_struct_decl` only — pass2 only fills in
2837// struct field types. Non-StructDecl stmts (ModuleStmt, FnDecl, ConstDecl,
2838// EnumDecl, TypeDecl, etc.) are never decoded.
2839fn (mut b Builder) register_types_pass2_from_flat(file_cursor ast.FileCursor) {
2840 b.module_import_aliases =
2841 module_import_aliases_from_imports(file_cursor.imports().import_stmts())
2842 stmts := file_cursor.stmts()
2843 for si in 0 .. stmts.len() {
2844 c := stmts.at(si)
2845 if c.kind() == .stmt_struct_decl {
2846 // s239: cursor-native struct-fields registration — no decode_stmt.
2847 b.register_struct_fields_from_flat(c)
2848 }
2849 }
2850}
2851
2852fn (mut b Builder) struct_mangled_name(decl ast.StructDecl) string {
2853 if !ssa_string_ok(decl.name) || decl.name == '' {
2854 return ''
2855 }
2856 return if b.cur_module == 'builtin'
2857 && decl.name in ['array', 'string', 'map', 'DenseArray', 'IError', 'Error', 'MessageError', 'None__', '_option', '_result', 'Option'] {
2858 decl.name
2859 } else if b.cur_module != '' && b.cur_module != 'main' {
2860 '${b.cur_module}__${decl.name}'
2861 } else {
2862 decl.name
2863 }
2864}
2865
2866// struct_mangled_name_from_flat (s239) is the cursor mirror of struct_mangled_name.
2867// The struct name is the cursor's name_id.
2868fn (mut b Builder) struct_mangled_name_from_flat(c ast.Cursor) string {
2869 name_str := c.name()
2870 if !ssa_string_ok(name_str) || name_str == '' {
2871 return ''
2872 }
2873 return if b.cur_module == 'builtin'
2874 && name_str in ['array', 'string', 'map', 'DenseArray', 'IError', 'Error', 'MessageError', 'None__', '_option', '_result', 'Option'] {
2875 name_str
2876 } else if b.cur_module != '' && b.cur_module != 'main' {
2877 '${b.cur_module}__${name_str}'
2878 } else {
2879 name_str
2880 }
2881}
2882
2883// register_struct_name registers a struct name with an empty struct type.
2884// The fields will be filled in by register_struct_fields in pass 2.
2885fn (mut b Builder) register_struct_name(decl ast.StructDecl) {
2886 name := b.struct_mangled_name(decl)
2887 if name == '' || !ssa_string_ok(name) {
2888 return
2889 }
2890
2891 if name in b.struct_types {
2892 return
2893 }
2894
2895 type_id := b.mod.type_store.register(Type{
2896 kind: .struct_t
2897 is_union: decl.is_union
2898 })
2899 b.struct_types[name] = type_id
2900 b.mod.c_struct_names[type_id] = name
2901}
2902
2903// register_struct_name_from_flat (s239) is the cursor mirror of register_struct_name.
2904// StructDecl flat = (.stmt_struct_decl, name in name_id, is_union in flags,
2905// [edge0=attrs, edge1=implements, edge2=embedded, edge3=generic_params, edge4=fields]).
2906fn (mut b Builder) register_struct_name_from_flat(c ast.Cursor) {
2907 name := b.struct_mangled_name_from_flat(c)
2908 if name == '' || !ssa_string_ok(name) {
2909 return
2910 }
2911 if name in b.struct_types {
2912 return
2913 }
2914 type_id := b.mod.type_store.register(Type{
2915 kind: .struct_t
2916 is_union: c.flag(ast.flag_is_union)
2917 })
2918 b.struct_types[name] = type_id
2919 b.mod.c_struct_names[type_id] = name
2920}
2921
2922// register_struct_fields fills in the field types for a previously forward-declared struct.
2923fn (mut b Builder) register_struct_fields(decl ast.StructDecl) {
2924 name := b.struct_mangled_name(decl)
2925 if name == '' || !ssa_string_ok(name) {
2926 return
2927 }
2928
2929 type_id := b.struct_types[name] or { return }
2930 n_embedded := decl.embedded.len
2931
2932 // Skip if fields are already populated (e.g., builtin types registered in Phase 1a)
2933 if b.mod.type_store.types[type_id].fields.len > 0 && n_embedded == 0 {
2934 return
2935 }
2936
2937 mut field_types := []TypeID{}
2938 mut field_names := []string{}
2939 n_fields := decl.fields.len
2940 b.struct_embedded_spans.delete(int(type_id))
2941
2942 // Flatten embedded struct fields first (e.g., ObjectCommon in Const)
2943 if n_embedded > 0 {
2944 b.record_embedded_field_spans(type_id, decl.embedded, field_names.len)
2945 b.collect_embedded_fields(decl.embedded, mut field_names, mut field_types)
2946 }
2947
2948 for fi in 0 .. n_fields {
2949 ft := b.ast_type_to_ssa(decl.fields[fi].typ)
2950 field_types << ft
2951 field_name := decl.fields[fi].name
2952 field_names << if ssa_string_ok(field_name) { field_name } else { '' }
2953 b.record_struct_field_array_elem_type(type_id, field_name, decl.fields[fi].typ, ft)
2954 }
2955 b.mod.type_store.types[type_id] = Type{
2956 kind: .struct_t
2957 fields: field_types
2958 field_names: field_names
2959 is_union: decl.is_union
2960 }
2961}
2962
2963// register_struct_fields_from_flat (s239) is the cursor mirror of
2964// register_struct_fields. embedded = c.list_at(2); fields = c.list_at(4)
2965// (.aux_field_decl: name in name_id, typ at edge0).
2966fn (mut b Builder) register_struct_fields_from_flat(c ast.Cursor) {
2967 name := b.struct_mangled_name_from_flat(c)
2968 if name == '' || !ssa_string_ok(name) {
2969 return
2970 }
2971
2972 type_id := b.struct_types[name] or { return }
2973 embedded := c.list_at(2)
2974
2975 // Skip if fields are already populated (e.g., builtin types registered in Phase 1a)
2976 if b.mod.type_store.types[type_id].fields.len > 0 && embedded.len() == 0 {
2977 return
2978 }
2979
2980 mut field_types := []TypeID{}
2981 mut field_names := []string{}
2982 fields := c.list_at(4)
2983 b.struct_embedded_spans.delete(int(type_id))
2984
2985 // Flatten embedded struct fields first (e.g., ObjectCommon in Const)
2986 if embedded.len() > 0 {
2987 b.record_embedded_field_spans_from_flat(type_id, embedded, field_names.len)
2988 b.collect_embedded_fields_from_flat(embedded, mut field_names, mut field_types)
2989 }
2990
2991 for fi in 0 .. fields.len() {
2992 field_c := fields.at(fi)
2993 typ_c := field_c.edge(0)
2994 ft := b.ast_type_to_ssa_from_flat(typ_c)
2995 field_types << ft
2996 field_name := field_c.name()
2997 field_names << if ssa_string_ok(field_name) { field_name } else { '' }
2998 b.record_struct_field_array_elem_type_from_flat(type_id, field_name, typ_c, ft)
2999 }
3000 b.mod.type_store.types[type_id] = Type{
3001 kind: .struct_t
3002 fields: field_types
3003 field_names: field_names
3004 is_union: c.flag(ast.flag_is_union)
3005 }
3006}
3007
3008// register_struct is the legacy combined registration (used for Phase 1a core types).
3009fn (mut b Builder) register_struct(decl ast.StructDecl) {
3010 name := b.struct_mangled_name(decl)
3011 if name == '' || !ssa_string_ok(name) {
3012 return
3013 }
3014
3015 if name in b.struct_types {
3016 return
3017 }
3018
3019 mut field_types := []TypeID{}
3020 mut field_names := []string{}
3021
3022 // Flatten embedded struct fields first
3023 b.collect_embedded_fields(decl.embedded, mut field_names, mut field_types)
3024
3025 for fi in 0 .. decl.fields.len {
3026 ft := b.ast_type_to_ssa(decl.fields[fi].typ)
3027 field_types << ft
3028 field_name := decl.fields[fi].name
3029 field_names << if ssa_string_ok(field_name) { field_name } else { '' }
3030 // This combined path registers the struct after fields are collected,
3031 // so array element metadata is recorded below once type_id exists.
3032 }
3033
3034 type_id := b.mod.type_store.register(Type{
3035 kind: .struct_t
3036 fields: field_types
3037 field_names: field_names
3038 is_union: decl.is_union
3039 })
3040 b.struct_types[name] = type_id
3041 b.mod.c_struct_names[type_id] = name
3042 for fi in 0 .. decl.fields.len {
3043 field_name := decl.fields[fi].name
3044 if ssa_string_ok(field_name) {
3045 b.record_struct_field_array_elem_type(type_id, field_name, decl.fields[fi].typ,
3046 field_types[fi])
3047 }
3048 }
3049}
3050
3051// register_struct_from_flat (s239) is the cursor mirror of register_struct
3052// (the combined name+fields path). embedded = c.list_at(2); fields = c.list_at(4).
3053// The second loop's `field_types[fi]` indexing matches the AST exactly (fi indexes
3054// the decl fields but field_types has the embedded fields prepended).
3055fn (mut b Builder) register_struct_from_flat(c ast.Cursor) {
3056 name := b.struct_mangled_name_from_flat(c)
3057 if name == '' || !ssa_string_ok(name) {
3058 return
3059 }
3060 if name in b.struct_types {
3061 return
3062 }
3063
3064 mut field_types := []TypeID{}
3065 mut field_names := []string{}
3066 embedded := c.list_at(2)
3067 fields := c.list_at(4)
3068
3069 // Flatten embedded struct fields first
3070 b.collect_embedded_fields_from_flat(embedded, mut field_names, mut field_types)
3071
3072 for fi in 0 .. fields.len() {
3073 typ_c := fields.at(fi).edge(0)
3074 ft := b.ast_type_to_ssa_from_flat(typ_c)
3075 field_types << ft
3076 field_name := fields.at(fi).name()
3077 field_names << if ssa_string_ok(field_name) { field_name } else { '' }
3078 }
3079
3080 type_id := b.mod.type_store.register(Type{
3081 kind: .struct_t
3082 fields: field_types
3083 field_names: field_names
3084 is_union: c.flag(ast.flag_is_union)
3085 })
3086 b.struct_types[name] = type_id
3087 b.mod.c_struct_names[type_id] = name
3088 for fi in 0 .. fields.len() {
3089 field_c := fields.at(fi)
3090 field_name := field_c.name()
3091 if ssa_string_ok(field_name) {
3092 b.record_struct_field_array_elem_type_from_flat(type_id, field_name, field_c.edge(0),
3093 field_types[fi])
3094 }
3095 }
3096}
3097
3098fn (mut b Builder) embedded_type_id_from_expr(emb ast.Expr) ?TypeID {
3099 if emb is ast.Ident {
3100 if !ssa_string_ok(emb.name) {
3101 return none
3102 }
3103 return b.lookup_embedded_type_id_by_name(emb.name)
3104 }
3105 if emb is ast.SelectorExpr && emb.lhs is ast.Ident {
3106 lhs_name := (emb.lhs as ast.Ident).name
3107 if !ssa_string_ok(lhs_name) || !ssa_string_ok(emb.rhs.name) {
3108 return none
3109 }
3110 if type_id := b.selector_type_name_to_ssa(lhs_name, emb.rhs.name) {
3111 if b.valid_type_id(type_id) {
3112 return type_id
3113 }
3114 }
3115 if resolved_mod := b.module_import_aliases[lhs_name] or {
3116 b.selector_module_name_for_ident(lhs_name) or { '' }
3117 }
3118 {
3119 if resolved_mod == '' {
3120 return none
3121 }
3122 qualified := imported_symbol_fn_name(resolved_mod, emb.rhs.name)
3123 if type_id := b.struct_types[qualified] {
3124 return type_id
3125 }
3126 if type_id := b.selector_type_name_to_ssa(resolved_mod, emb.rhs.name) {
3127 if b.valid_type_id(type_id) {
3128 return type_id
3129 }
3130 }
3131 }
3132 }
3133 return none
3134}
3135
3136fn (mut b Builder) embedded_type_id_from_flat(emb ast.Cursor) ?TypeID {
3137 if emb.kind() == .expr_ident {
3138 n := emb.name()
3139 if !ssa_string_ok(n) {
3140 return none
3141 }
3142 return b.lookup_embedded_type_id_by_name(n)
3143 }
3144 if emb.kind() == .expr_selector && emb.edge(0).kind() == .expr_ident {
3145 lhs_name := emb.edge(0).name()
3146 rhs_name := emb.edge(1).name()
3147 if !ssa_string_ok(lhs_name) || !ssa_string_ok(rhs_name) {
3148 return none
3149 }
3150 if type_id := b.selector_type_name_to_ssa(lhs_name, rhs_name) {
3151 if b.valid_type_id(type_id) {
3152 return type_id
3153 }
3154 }
3155 if resolved_mod := b.module_import_aliases[lhs_name] or {
3156 b.selector_module_name_for_ident(lhs_name) or { '' }
3157 }
3158 {
3159 if resolved_mod == '' {
3160 return none
3161 }
3162 qualified := imported_symbol_fn_name(resolved_mod, rhs_name)
3163 if type_id := b.struct_types[qualified] {
3164 return type_id
3165 }
3166 if type_id := b.selector_type_name_to_ssa(resolved_mod, rhs_name) {
3167 if b.valid_type_id(type_id) {
3168 return type_id
3169 }
3170 }
3171 }
3172 }
3173 return none
3174}
3175
3176fn (mut b Builder) lookup_embedded_type_id_by_name(emb_name string) ?TypeID {
3177 if b.cur_module != '' && b.cur_module != 'main' {
3178 qualified := '${b.cur_module}__${emb_name}'
3179 if qualified in b.struct_types {
3180 return b.struct_types[qualified]
3181 }
3182 }
3183 if emb_name in b.struct_types {
3184 return b.struct_types[emb_name]
3185 }
3186 return none
3187}
3188
3189fn (mut b Builder) record_embedded_field_spans(owner_type TypeID, embedded []ast.Expr, start_idx int) {
3190 mut offset := start_idx
3191 for emb in embedded {
3192 emb_type_id := b.embedded_type_id_from_expr(emb) or { continue }
3193 if emb_type_id <= 0 || emb_type_id >= b.mod.type_store.types.len {
3194 continue
3195 }
3196 emb_typ := b.mod.type_store.types[emb_type_id]
3197 if emb_typ.kind != .struct_t || emb_typ.field_names.len == 0 {
3198 continue
3199 }
3200 mut spans := b.struct_embedded_spans[int(owner_type)] or { []EmbeddedFieldSpan{} }
3201 spans << EmbeddedFieldSpan{
3202 source_type: emb_type_id
3203 start: offset
3204 len: emb_typ.field_names.len
3205 }
3206 b.struct_embedded_spans[int(owner_type)] = spans
3207 offset += emb_typ.field_names.len
3208 }
3209}
3210
3211fn (mut b Builder) record_embedded_field_spans_from_flat(owner_type TypeID, embedded ast.CursorList, start_idx int) {
3212 mut offset := start_idx
3213 for ei in 0 .. embedded.len() {
3214 emb_type_id := b.embedded_type_id_from_flat(embedded.at(ei)) or { continue }
3215 if emb_type_id <= 0 || emb_type_id >= b.mod.type_store.types.len {
3216 continue
3217 }
3218 emb_typ := b.mod.type_store.types[emb_type_id]
3219 if emb_typ.kind != .struct_t || emb_typ.field_names.len == 0 {
3220 continue
3221 }
3222 mut spans := b.struct_embedded_spans[int(owner_type)] or { []EmbeddedFieldSpan{} }
3223 spans << EmbeddedFieldSpan{
3224 source_type: emb_type_id
3225 start: offset
3226 len: emb_typ.field_names.len
3227 }
3228 b.struct_embedded_spans[int(owner_type)] = spans
3229 offset += emb_typ.field_names.len
3230 }
3231}
3232
3233// collect_embedded_fields resolves embedded type expressions and adds their
3234// flattened fields to the field_names and field_types lists.
3235// Embedded structs (e.g., `ObjectCommon` in `struct Const { ObjectCommon; int_val int }`)
3236// have their fields in a separate `embedded` list in the AST StructDecl.
3237// This function looks up each embedded type in struct_types and prepends its fields.
3238fn (mut b Builder) collect_embedded_fields(embedded []ast.Expr, mut field_names []string, mut field_types []TypeID) {
3239 for emb in embedded {
3240 emb_type_id := b.embedded_type_id_from_expr(emb) or { continue }
3241 if emb_type_id < b.mod.type_store.types.len {
3242 emb_typ := b.mod.type_store.types[emb_type_id]
3243 if emb_typ.kind == .struct_t && emb_typ.field_names.len > 0 {
3244 for i, fname in emb_typ.field_names {
3245 field_names << fname
3246 if i < emb_typ.fields.len {
3247 field_types << emb_typ.fields[i]
3248 } else {
3249 field_types << b.mod.type_store.get_int(64)
3250 }
3251 }
3252 }
3253 }
3254 }
3255}
3256
3257// collect_embedded_fields_from_flat (s239) is the cursor mirror of
3258// collect_embedded_fields. Each embedded entry is an `.expr_ident` (`emb.name()`)
3259// or an `.expr_selector` with an Ident lhs (`edge(0).name()` / rhs `edge(1).name()`).
3260// The struct_types lookup + field-copy logic is identical (type-store reads only).
3261fn (mut b Builder) collect_embedded_fields_from_flat(embedded ast.CursorList, mut field_names []string, mut field_types []TypeID) {
3262 for ei in 0 .. embedded.len() {
3263 emb := embedded.at(ei)
3264 emb_type_id := b.embedded_type_id_from_flat(emb) or { continue }
3265 if emb_type_id < b.mod.type_store.types.len {
3266 emb_typ := b.mod.type_store.types[emb_type_id]
3267 if emb_typ.kind == .struct_t && emb_typ.field_names.len > 0 {
3268 for i, fname in emb_typ.field_names {
3269 field_names << fname
3270 if i < emb_typ.fields.len {
3271 field_types << emb_typ.fields[i]
3272 } else {
3273 field_types << b.mod.type_store.get_int(64)
3274 }
3275 }
3276 }
3277 }
3278 }
3279}
3280
3281fn (mut b Builder) register_enum(decl ast.EnumDecl) {
3282 if !ssa_string_ok(decl.name) || decl.name == '' {
3283 return
3284 }
3285 name := if b.cur_module != '' && b.cur_module != 'main' {
3286 '${b.cur_module}__${decl.name}'
3287 } else {
3288 decl.name
3289 }
3290 if !ssa_string_ok(name) {
3291 return
3292 }
3293
3294 is_flag := decl.attributes.has('flag')
3295 for i, field in decl.fields {
3296 if !ssa_string_ok(field.name) || field.name == '' {
3297 continue
3298 }
3299 key := '${name}__${enum_field_symbol_name(field.name)}'
3300 if is_flag {
3301 // @[flag] enums use power-of-2 values: 1, 2, 4, 8, ...
3302 b.enum_values[key] = 1 << i
3303 } else {
3304 b.enum_values[key] = i
3305 }
3306 }
3307}
3308
3309// register_enum_from_flat (s238) is the cursor mirror of register_enum. EnumDecl
3310// flat = (.stmt_enum_decl, name in name_id, [edge0=as_type, edge1=attrs,
3311// edge2=fields]). Values come from the field INDEX (matching the AST, which
3312// ignores field.value), so the `flag` power-of-2 numbering and the skip-on-bad-
3313// name behaviour are index-identical. `flag` attr via cursor_attrs_has (s237).
3314fn (mut b Builder) register_enum_from_flat(c ast.Cursor) {
3315 name_str := c.name()
3316 if !ssa_string_ok(name_str) || name_str == '' {
3317 return
3318 }
3319 name := if b.cur_module != '' && b.cur_module != 'main' {
3320 '${b.cur_module}__${name_str}'
3321 } else {
3322 name_str
3323 }
3324 if !ssa_string_ok(name) {
3325 return
3326 }
3327
3328 is_flag := cursor_attrs_has(c.list_at(1), 'flag')
3329 fields := c.list_at(2)
3330 for i in 0 .. fields.len() {
3331 field_name := fields.at(i).name()
3332 if !ssa_string_ok(field_name) || field_name == '' {
3333 continue
3334 }
3335 key := '${name}__${enum_field_symbol_name(field_name)}'
3336 if is_flag {
3337 b.enum_values[key] = 1 << i
3338 } else {
3339 b.enum_values[key] = i
3340 }
3341 }
3342}
3343
3344// is_enum_type checks if a type name corresponds to a registered enum
3345// by looking for any enum_values key that starts with the name followed by '__'.
3346fn (b &Builder) is_enum_type(name string) bool {
3347 prefix := '${name}__'
3348 for key, _ in b.enum_values {
3349 if key.starts_with(prefix) {
3350 return true
3351 }
3352 }
3353 return false
3354}
3355
3356fn (mut b Builder) register_type_aliases(file ast.File) {
3357 for stmt in file.stmts {
3358 if stmt is ast.TypeDecl && stmt.variants.len == 0 {
3359 b.register_type_alias(stmt)
3360 }
3361 }
3362}
3363
3364fn (mut b Builder) register_type_aliases_from_flat(file_cursor ast.FileCursor) {
3365 stmts := file_cursor.stmts()
3366 for si in 0 .. stmts.len() {
3367 c := stmts.at(si)
3368 if c.kind() == .stmt_type_decl {
3369 // s236: TypeDecl flat = (.stmt_type_decl, name in name_id,
3370 // [edge0=base_type, edge1=attrs, edge2=generic_params, edge3=variants]).
3371 // Aliases have no variants — dispatch cursor-natively, no decode_stmt.
3372 if c.list_at(3).len() == 0 {
3373 b.register_type_alias_from_flat(c)
3374 }
3375 }
3376 }
3377}
3378
3379fn (mut b Builder) register_type_alias(decl ast.TypeDecl) {
3380 if !ssa_string_ok(decl.name) || decl.name == '' || !builder_expr_ok(decl.base_type) {
3381 return
3382 }
3383 base_type := b.ast_type_to_ssa(decl.base_type)
3384 if base_type == 0 {
3385 return
3386 }
3387 name := if b.cur_module != '' && b.cur_module != 'main' {
3388 '${b.cur_module}__${decl.name}'
3389 } else {
3390 decl.name
3391 }
3392 if ssa_string_ok(name) {
3393 b.type_aliases[name] = base_type
3394 }
3395 short_mod := ssa_module_tail_name(b.cur_module)
3396 if short_mod != '' && short_mod != b.cur_module && b.cur_module != 'main' {
3397 short_name := '${short_mod}__${decl.name}'
3398 if ssa_string_ok(short_name) {
3399 b.type_aliases[short_name] = base_type
3400 }
3401 }
3402 if b.cur_module == '' || b.cur_module == 'main' {
3403 b.type_aliases[decl.name] = base_type
3404 }
3405}
3406
3407// register_type_alias_from_flat (s236) is the cursor mirror of register_type_alias.
3408// `c` is the .stmt_type_decl cursor: name in name_id, base_type at edge(0). The
3409// builder_expr_ok corruption guard is unnecessary for a valid cursor; the
3410// ssa_string_ok / base_type==0 guards and the map writes are identical.
3411fn (mut b Builder) register_type_alias_from_flat(c ast.Cursor) {
3412 name_str := c.name()
3413 if !ssa_string_ok(name_str) || name_str == '' {
3414 return
3415 }
3416 base_type := b.ast_type_to_ssa_from_flat(c.edge(0))
3417 if base_type == 0 {
3418 return
3419 }
3420 name := if b.cur_module != '' && b.cur_module != 'main' {
3421 '${b.cur_module}__${name_str}'
3422 } else {
3423 name_str
3424 }
3425 if ssa_string_ok(name) {
3426 b.type_aliases[name] = base_type
3427 }
3428 short_mod := ssa_module_tail_name(b.cur_module)
3429 if short_mod != '' && short_mod != b.cur_module && b.cur_module != 'main' {
3430 short_name := '${short_mod}__${name_str}'
3431 if ssa_string_ok(short_name) {
3432 b.type_aliases[short_name] = base_type
3433 }
3434 }
3435 if b.cur_module == '' || b.cur_module == 'main' {
3436 b.type_aliases[name_str] = base_type
3437 }
3438}
3439
3440fn (mut b Builder) register_sumtype(decl ast.TypeDecl) {
3441 if decl.variants.len == 0 {
3442 return
3443 }
3444 if !ssa_string_ok(decl.name) || decl.name == '' {
3445 return
3446 }
3447 name := if b.cur_module != '' && b.cur_module != 'main' {
3448 '${b.cur_module}__${decl.name}'
3449 } else {
3450 decl.name
3451 }
3452 if !ssa_string_ok(name) {
3453 return
3454 }
3455
3456 if name in b.struct_types {
3457 return
3458 }
3459
3460 i64_t := b.mod.type_store.get_int(64)
3461 type_id := b.mod.type_store.register(Type{
3462 kind: .struct_t
3463 fields: [i64_t, i64_t]
3464 field_names: ['_tag', '_data']
3465 })
3466 b.struct_types[name] = type_id
3467 b.mod.c_struct_names[type_id] = name
3468}
3469
3470// register_sumtype_from_flat (s240) is the cursor mirror of register_sumtype.
3471// `c` is a .stmt_type_decl with variants (variants list = edge3). The variant
3472// types are not read (the AST also ignores them) — a sum type lowers to a
3473// {_tag, _data} two-i64 struct. Caller already gates on variants present.
3474fn (mut b Builder) register_sumtype_from_flat(c ast.Cursor) {
3475 if c.list_at(3).len() == 0 {
3476 return
3477 }
3478 name_str := c.name()
3479 if !ssa_string_ok(name_str) || name_str == '' {
3480 return
3481 }
3482 name := if b.cur_module != '' && b.cur_module != 'main' {
3483 '${b.cur_module}__${name_str}'
3484 } else {
3485 name_str
3486 }
3487 if !ssa_string_ok(name) {
3488 return
3489 }
3490
3491 if name in b.struct_types {
3492 return
3493 }
3494
3495 i64_t := b.mod.type_store.get_int(64)
3496 type_id := b.mod.type_store.register(Type{
3497 kind: .struct_t
3498 fields: [i64_t, i64_t]
3499 field_names: ['_tag', '_data']
3500 })
3501 b.struct_types[name] = type_id
3502 b.mod.c_struct_names[type_id] = name
3503}
3504
3505fn (mut b Builder) register_consts_and_globals(file ast.File) {
3506 for stmt in file.stmts {
3507 match stmt {
3508 ast.ConstDecl {
3509 b.register_const_decl(stmt)
3510 }
3511 ast.GlobalDecl {
3512 b.register_global_decl(stmt)
3513 }
3514 else {}
3515 }
3516 }
3517}
3518
3519// register_consts_and_globals_from_flat is the flat-cursor counterpart of
3520// `register_consts_and_globals`. Walks one file's top-level stmts via
3521// FileCursor and only rehydrates `.stmt_const_decl` / `.stmt_global_decl`
3522// nodes via `flat.decode_stmt`. ModuleStmt, FnDecl, StructDecl, EnumDecl,
3523// TypeDecl, ImportStmt etc. are never decoded.
3524fn (mut b Builder) register_consts_and_globals_from_flat(file_cursor ast.FileCursor) {
3525 stmts := file_cursor.stmts()
3526 for si in 0 .. stmts.len() {
3527 c := stmts.at(si)
3528 match c.kind() {
3529 .stmt_const_decl {
3530 // s242: cursor-native const registration — no decode_stmt.
3531 b.register_const_decl_from_flat(c)
3532 }
3533 .stmt_global_decl {
3534 // s237: cursor-native global registration — no decode_stmt.
3535 b.register_global_decl_from_flat(c)
3536 }
3537 else {}
3538 }
3539 }
3540}
3541
3542// register_const_decl extracts the per-ConstDecl body from
3543// `register_consts_and_globals` so both the legacy walker and the
3544// flat-cursor port can dispatch through a single implementation.
3545fn (mut b Builder) register_const_decl(stmt ast.ConstDecl) {
3546 for field in stmt.fields {
3547 const_name := if b.cur_module != '' && b.cur_module != 'main' {
3548 '${b.cur_module}__${field.name}'
3549 } else {
3550 field.name
3551 }
3552 mut const_type := b.const_field_type(field.name, field.value)
3553 // Check if this is a string constant - store for inline resolution
3554 str_val := b.try_eval_const_string(field.value)
3555 if str_val.len > 0 {
3556 b.string_const_values[const_name] = str_val
3557 b.string_const_values[field.name] = str_val
3558 }
3559 // Check if this is a float constant - store for inline resolution
3560 if field.value is ast.BasicLiteral && field.value.kind == .number
3561 && (field.value.value.contains('.')
3562 || (!field.value.value.starts_with('0x') && !field.value.value.starts_with('0X')
3563 && (field.value.value.contains('e') || field.value.value.contains('E')))) {
3564 b.float_const_values[const_name] = field.value.value
3565 b.float_const_values[field.name] = field.value.value
3566 } else if b.is_float_cast_expr(field.value) {
3567 if fval := b.try_eval_computed_float(field.value) {
3568 fval_str := fval.str()
3569 b.float_const_values[const_name] = fval_str
3570 b.float_const_values[field.name] = fval_str
3571 }
3572 }
3573 initial_value := b.try_eval_const_int(field.value)
3574 // Detect sum type constants: these are multi-word values that
3575 // cannot be inlined as a single i64.
3576 // Case 1: Transformer succeeded → InitExpr{_tag: N, _data: ...}
3577 // Case 2: Transformer failed → CastExpr{typ: SumType, expr: ...}
3578 mut is_sumtype_const := false
3579 if field.value is ast.InitExpr {
3580 for init_field in field.value.fields {
3581 if init_field.name == '_tag' {
3582 is_sumtype_const = true
3583 break
3584 }
3585 }
3586 }
3587 if !is_sumtype_const {
3588 mut cast_type_name := ''
3589 if field.value is ast.CastExpr {
3590 if field.value.typ is ast.Ident {
3591 cast_type_name = field.value.typ.name
3592 }
3593 } else if field.value is ast.CallOrCastExpr {
3594 if field.value.lhs is ast.Ident {
3595 cast_type_name = field.value.lhs.name
3596 }
3597 }
3598 if cast_type_name != '' {
3599 qualified_cast := if b.cur_module != '' && b.cur_module != 'main' {
3600 '${b.cur_module}__${cast_type_name}'
3601 } else {
3602 cast_type_name
3603 }
3604 for _, check_name in [cast_type_name, qualified_cast] {
3605 if st_type := b.struct_types[check_name] {
3606 if int(st_type) < b.mod.type_store.types.len {
3607 st := b.mod.type_store.types[st_type]
3608 if st.kind == .struct_t && st.field_names.len >= 2
3609 && st.field_names[0] == '_tag' {
3610 is_sumtype_const = true
3611 // Fix the global type: use the sum type struct
3612 // instead of the i64 fallback from expr_type()
3613 const_type = st_type
3614 break
3615 }
3616 }
3617 }
3618 }
3619 }
3620 }
3621 // For sum type constants detected via InitExpr, also fix const_type
3622 if is_sumtype_const && field.value is ast.InitExpr {
3623 if field.value.typ is ast.Ident {
3624 type_name := field.value.typ.name
3625 if st_type := b.struct_types[type_name] {
3626 const_type = st_type
3627 } else {
3628 // Try with module prefix
3629 qualified_st := '${b.cur_module}__${type_name}'
3630 if st_type2 := b.struct_types[qualified_st] {
3631 const_type = st_type2
3632 }
3633 }
3634 }
3635 }
3636 // Detect constant FIXED arrays with all-literal elements.
3637 if field.value is ast.ArrayInitExpr && field.value.exprs.len > 0 {
3638 mut is_fixed_array := true
3639 // Check type environment to see if this is a dynamic array
3640 if b.env != unsafe { nil } {
3641 fpos := field.value.pos
3642 if fpos.id != 0 {
3643 if ct := b.env.get_expr_type(fpos.id) {
3644 if ct is types.Array {
3645 is_fixed_array = false
3646 }
3647 }
3648 }
3649 }
3650 arr_data := b.try_serialize_const_array(field.value)
3651 if arr_data.len > 0 {
3652 elem_size := arr_data.len / field.value.exprs.len
3653 is_float_arr := b.is_float_array(field.value)
3654 elem_type := if is_float_arr {
3655 b.mod.type_store.get_float(elem_size * 8)
3656 } else if elem_size == 8 {
3657 b.mod.type_store.get_int(64)
3658 } else if elem_size == 4 {
3659 b.mod.type_store.get_int(32)
3660 } else if elem_size == 2 {
3661 b.mod.type_store.get_int(16)
3662 } else {
3663 b.mod.type_store.get_int(8)
3664 }
3665 if is_fixed_array {
3666 b.mod.add_global_with_data(const_name, elem_type, true, arr_data)
3667 b.const_array_globals[const_name] = true
3668 b.const_array_globals[field.name] = true
3669 b.const_array_elem_count[const_name] = field.value.exprs.len
3670 b.const_array_elem_count[field.name] = field.value.exprs.len
3671 continue
3672 } else {
3673 // Dynamic array constant: serialize data, create array struct global
3674 data_name := '${const_name}__data'
3675 b.mod.add_global_with_data(data_name, elem_type, true, arr_data)
3676 b.const_array_globals[data_name] = true
3677 // Add array struct global (initialized in _vinit)
3678 arr_struct_type := b.get_array_type()
3679 b.mod.add_global(const_name, arr_struct_type, false)
3680 b.dyn_const_arrays << DynConstArray{
3681 arr_global_name: const_name
3682 data_global_name: data_name
3683 elem_count: field.value.exprs.len
3684 elem_size: elem_size
3685 }
3686 continue
3687 }
3688 }
3689 }
3690 // For float constants, store bit pattern as initial_value
3691 mut actual_init := initial_value
3692 if const_name in b.float_const_values {
3693 f_val := b.float_const_values[const_name].f64()
3694 actual_init = i64(unsafe { *(&i64(&f_val)) })
3695 const_type = b.mod.type_store.get_float(64)
3696 }
3697 b.mod.add_global_with_value(const_name, const_type, true, actual_init)
3698 if !is_sumtype_const && (initial_value != 0 || b.is_zero_literal(field.value)) {
3699 b.const_values[const_name] = initial_value
3700 b.const_value_types[const_name] = const_type
3701 // Also store without module prefix for transformer-generated references
3702 b.const_values[field.name] = initial_value
3703 b.const_value_types[field.name] = const_type
3704 }
3705 }
3706}
3707
3708// register_const_decl_from_flat (s242) is the cursor mirror of register_const_decl
3709// (the last decode_stmt in builder.v). ConstDecl flat = one edge -> aux_list of
3710// `.aux_field_init` (field name in name_id, value at edge(0)). Uses the s235
3711// (try_eval_const_int / const_field_type / is_zero_literal) and s241
3712// (try_eval_const_string / is_float_cast_expr / try_eval_computed_float /
3713// is_float_array / try_serialize_const_array) cursor helpers. Every branch
3714// (string / float / int / sumtype-const / fixed+dynamic const-array) mirrors the
3715// AST exactly; ArrayInitExpr exprs are edges 5..n and InitExpr fields edges 1..n.
3716fn (mut b Builder) register_const_decl_from_flat(c ast.Cursor) {
3717 fields := c.list_at(0)
3718 for fidx in 0 .. fields.len() {
3719 field_c := fields.at(fidx)
3720 field_name := field_c.name()
3721 value_c := field_c.edge(0)
3722 value_kind := value_c.kind()
3723 const_name := if b.cur_module != '' && b.cur_module != 'main' {
3724 '${b.cur_module}__${field_name}'
3725 } else {
3726 field_name
3727 }
3728 mut const_type := b.const_field_type_from_flat(field_name, value_c)
3729 // Check if this is a string constant - store for inline resolution
3730 str_val := b.try_eval_const_string_from_flat(value_c)
3731 if str_val.len > 0 {
3732 b.string_const_values[const_name] = str_val
3733 b.string_const_values[field_name] = str_val
3734 }
3735 // Check if this is a float constant - store for inline resolution
3736 if value_kind == .expr_basic_literal
3737 && unsafe { token.Token(int(value_c.aux())) } == .number
3738 && (value_c.name().contains('.') || (!value_c.name().starts_with('0x')
3739 && !value_c.name().starts_with('0X')
3740 && (value_c.name().contains('e') || value_c.name().contains('E')))) {
3741 b.float_const_values[const_name] = value_c.name()
3742 b.float_const_values[field_name] = value_c.name()
3743 } else if b.is_float_cast_expr_from_flat(value_c) {
3744 if fval := b.try_eval_computed_float_from_flat(value_c) {
3745 fval_str := fval.str()
3746 b.float_const_values[const_name] = fval_str
3747 b.float_const_values[field_name] = fval_str
3748 }
3749 }
3750 initial_value := b.try_eval_const_int_from_flat(value_c)
3751 // Detect sum type constants (Case 1: InitExpr{_tag,...}; Case 2: cast to sum type).
3752 mut is_sumtype_const := false
3753 if value_kind == .expr_init {
3754 n_init_fields := value_c.edge_count() - 1
3755 for ifi := 0; ifi < n_init_fields; ifi++ {
3756 if value_c.edge(1 + ifi).name() == '_tag' {
3757 is_sumtype_const = true
3758 break
3759 }
3760 }
3761 }
3762 if !is_sumtype_const {
3763 mut cast_type_name := ''
3764 if value_kind == .expr_cast {
3765 if value_c.edge(0).kind() == .expr_ident {
3766 cast_type_name = value_c.edge(0).name()
3767 }
3768 } else if value_kind == .expr_call_or_cast {
3769 if value_c.edge(0).kind() == .expr_ident {
3770 cast_type_name = value_c.edge(0).name()
3771 }
3772 }
3773 if cast_type_name != '' {
3774 qualified_cast := if b.cur_module != '' && b.cur_module != 'main' {
3775 '${b.cur_module}__${cast_type_name}'
3776 } else {
3777 cast_type_name
3778 }
3779 for _, check_name in [cast_type_name, qualified_cast] {
3780 if st_type := b.struct_types[check_name] {
3781 if int(st_type) < b.mod.type_store.types.len {
3782 st := b.mod.type_store.types[st_type]
3783 if st.kind == .struct_t && st.field_names.len >= 2
3784 && st.field_names[0] == '_tag' {
3785 is_sumtype_const = true
3786 const_type = st_type
3787 break
3788 }