vxx / vlib / v3 / gen / c / cleanc.v
7171 lines · 6917 sloc · 247.68 KB · 2b81918e62db9ab38787518603a7f93bee80868c
Raw
1module c
2
3import os
4import strings
5import v3.flat
6import v3.types
7
8// FlatGen emits flat gen output used by c.
9pub struct FlatGen {
10mut:
11 sb strings.Builder
12 indent int
13 a &flat.FlatAst = unsafe { nil }
14 used_fns map[string]bool
15 used_fn_names []string
16 test_files map[string]bool
17 str_lits []string
18 str_lit_ids map[string]int
19 global_types map[string]types.Type
20 enum_vals map[string]int
21 defers []flat.NodeId
22 fn_defers []flat.NodeId
23 fn_defer_counts map[int]string
24 defer_capture_names []string
25 defer_capture_types map[string]types.Type
26 interfaces map[string][]string
27 const_vals map[string]flat.NodeId
28 const_modules map[string]string
29 const_init_order []string
30 global_modules map[string]string
31 global_inits map[string]flat.NodeId // qualified global name -> initializer value node
32 global_init_order []string // qualified global names, in declaration order
33 iface_impls map[string][]string // interface name -> implementing concrete type names
34 iface_type_ids map[string]int // "${iface}::${concrete}" -> 1-based type id
35 module_init_fns []string // C names of module-level `init()` fns, in source order
36 module_init_fn_modules map[string]string // C init fn name -> V module name
37 module_imports map[string][]string // module -> imported modules
38 c_directives []CDirective
39 inlined_c_structs map[string]bool
40 inlined_c_fns map[string]bool
41 inlined_c_declared_fns map[string]bool
42 c_flags []string
43 libc_compat_fns map[string]bool
44 tc &types.TypeChecker = unsafe { nil }
45 has_builtins bool
46 tmp_count int
47 line_start bool
48 field_name_set map[string]bool // every struct field's C name (lazy) — for const/field collision checks
49 modules map[string]string // alias -> full module name
50 fn_ptr_types map[string]string // fn_ptr:ret|params -> typedef name
51 fixed_array_ret_wrappers map[string]bool // bare fixed-array c_type name -> has a return wrapper struct
52 emitted_fixed_array_typedefs map[string]bool // bare fixed-array typedefs already written (shared across passes)
53 fn_decl_param_types map[string][]types.Type
54 fn_decl_ret_types map[string]types.Type // fn decl name (and qualified variants) -> return type
55 struct_decl_infos map[string]StructDeclInfo
56 struct_decl_short_infos map[string]StructDeclInfo
57 const_runtime_inits []string
58 const_runtime_init_modules []string
59 runtime_inits []string
60 runtime_init_modules []string
61 compiler_vroot string
62 c99_mode bool
63 cur_fn_name string
64 cur_param_names []string
65 cur_param_type_values []types.Type
66 cur_param_types map[string]types.Type
67 cur_mut_params map[string]bool
68 cur_fn_ret types.Type = types.Type(types.void_)
69 cur_fn_ret_is_optional bool
70 cur_fn_ret_base types.Type = types.Type(types.void_)
71 // in_return is true only while generating a `return` statement's value, so a bare
72 // generic literal (`return Box{...}`) may adopt `cur_fn_ret`'s concrete instance —
73 // but a literal in a local decl / argument elsewhere in the body does not.
74 in_return bool
75 expected_expr_type types.Type = types.Type(types.void_)
76 expected_enum string
77 needed_optional_types map[string]string
78 emitted_optional_types map[string]bool
79 emitted_fns map[string]bool
80 array_method_cache map[string]string
81 param_types_cache map[string][]types.Type // (name|fallback) -> resolved param types
82 embedded_fields_by_type map[string][]types.StructField // type name -> its embedded fields (usually empty)
83 param_types_by_short map[string][]types.Type // method short-name suffix -> param types (fallback index)
84 spawn_wrapper_names map[string]string
85 spawn_wrapper_defs []string
86 callback_wrapper_names map[string]string
87 callback_wrapper_defs []string
88 parallel_used bool
89}
90
91struct FixedArrayTypedefInfo {
92 arr types.ArrayFixed
93 module string
94}
95
96struct CDirective {
97 module string
98 text string
99 before_import bool
100}
101
102struct CInlineHeader {
103 text string
104 preserved_directives []string
105 preserved_c_fns []string
106 preserved_c_structs []string
107}
108
109// was_parallel reports whether the last fn codegen actually ran across threads.
110pub fn (g &FlatGen) was_parallel() bool {
111 return g.parallel_used
112}
113
114pub fn (g &FlatGen) c_flags() []string {
115 return g.c_flags.clone()
116}
117
118// set_c99_mode configures whether generated C should support strict C99 builds.
119pub fn (mut g FlatGen) set_c99_mode(enabled bool) {
120 g.c99_mode = enabled
121}
122
123// new creates a FlatGen value for c.
124pub fn FlatGen.new() FlatGen {
125 return FlatGen{
126 sb: strings.new_builder(4096)
127 used_fns: map[string]bool{}
128 test_files: map[string]bool{}
129 str_lit_ids: map[string]int{}
130 global_types: map[string]types.Type{}
131 enum_vals: map[string]int{}
132 interfaces: map[string][]string{}
133 const_vals: map[string]flat.NodeId{}
134 const_modules: map[string]string{}
135 const_init_order: []string{}
136 global_modules: map[string]string{}
137 global_inits: map[string]flat.NodeId{}
138 global_init_order: []string{}
139 iface_impls: map[string][]string{}
140 iface_type_ids: map[string]int{}
141 module_init_fns: []string{}
142 module_init_fn_modules: map[string]string{}
143 module_imports: map[string][]string{}
144 c_directives: []CDirective{}
145 inlined_c_structs: map[string]bool{}
146 inlined_c_fns: map[string]bool{}
147 inlined_c_declared_fns: map[string]bool{}
148 c_flags: []string{}
149 libc_compat_fns: map[string]bool{}
150 modules: map[string]string{}
151 fn_ptr_types: map[string]string{}
152 fixed_array_ret_wrappers: map[string]bool{}
153 emitted_fixed_array_typedefs: map[string]bool{}
154 fn_decl_param_types: map[string][]types.Type{}
155 fn_decl_ret_types: map[string]types.Type{}
156 struct_decl_infos: map[string]StructDeclInfo{}
157 struct_decl_short_infos: map[string]StructDeclInfo{}
158 cur_param_names: []string{}
159 cur_param_type_values: []types.Type{}
160 cur_param_types: map[string]types.Type{}
161 cur_mut_params: map[string]bool{}
162 needed_optional_types: map[string]string{}
163 emitted_optional_types: map[string]bool{}
164 emitted_fns: map[string]bool{}
165 array_method_cache: map[string]string{}
166 param_types_cache: map[string][]types.Type{}
167 embedded_fields_by_type: map[string][]types.StructField{}
168 param_types_by_short: map[string][]types.Type{}
169 spawn_wrapper_names: map[string]string{}
170 spawn_wrapper_defs: []string{}
171 callback_wrapper_names: map[string]string{}
172 callback_wrapper_defs: []string{}
173 str_lits: []string{}
174 defers: []flat.NodeId{}
175 fn_defers: []flat.NodeId{}
176 fn_defer_counts: map[int]string{}
177 defer_capture_names: []string{}
178 defer_capture_types: map[string]types.Type{}
179 const_runtime_inits: []string{}
180 const_runtime_init_modules: []string{}
181 runtime_inits: []string{}
182 runtime_init_modules: []string{}
183 compiler_vroot: ''
184 line_start: true
185 }
186}
187
188// gen supports gen handling for FlatGen.
189pub fn (mut g FlatGen) gen(a &flat.FlatAst) string {
190 tc := types.TypeChecker.new(a)
191 return g.gen_with_used(a, map[string]bool{}, &tc)
192}
193
194// gen_with_used emits with used output for c.
195pub fn (mut g FlatGen) gen_with_used(a &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker) string {
196 return g.gen_with_used_options(a, used_fns, tc, false)
197}
198
199pub fn (mut g FlatGen) gen_with_used_test_options(a &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker, no_parallel bool, test_files []string) string {
200 g.test_files = map[string]bool{}
201 for file in test_files {
202 g.test_files[file] = true
203 }
204 return g.gen_with_used_options(a, used_fns, tc, no_parallel)
205}
206
207// gen_with_used_options emits with used options output for c.
208pub fn (mut g FlatGen) gen_with_used_options(a &flat.FlatAst, used_fns map[string]bool, tc &types.TypeChecker, no_parallel bool) string {
209 g.a = a
210 g.used_fns = used_fns.clone()
211 g.used_fn_names = []string{}
212 g.str_lits = []string{}
213 g.defers = []flat.NodeId{}
214 g.fn_defers = []flat.NodeId{}
215 g.fn_defer_counts = map[int]string{}
216 g.defer_capture_names = []string{}
217 g.defer_capture_types = map[string]types.Type{}
218 g.const_runtime_inits = []string{}
219 g.const_runtime_init_modules = []string{}
220 g.runtime_inits = []string{}
221 g.runtime_init_modules = []string{}
222 g.compiler_vroot = ''
223 g.str_lit_ids = map[string]int{}
224 g.global_types = map[string]types.Type{}
225 g.enum_vals = map[string]int{}
226 g.interfaces = map[string][]string{}
227 g.const_vals = map[string]flat.NodeId{}
228 g.const_modules = map[string]string{}
229 g.const_init_order = []string{}
230 g.global_modules = map[string]string{}
231 g.global_inits = map[string]flat.NodeId{}
232 g.global_init_order = []string{}
233 g.iface_impls = map[string][]string{}
234 g.iface_type_ids = map[string]int{}
235 g.module_init_fns = []string{}
236 g.module_init_fn_modules = map[string]string{}
237 g.module_imports = map[string][]string{}
238 g.c_directives = []CDirective{}
239 g.inlined_c_structs = map[string]bool{}
240 g.inlined_c_fns = map[string]bool{}
241 g.inlined_c_declared_fns = map[string]bool{}
242 g.c_flags = []string{}
243 g.libc_compat_fns = map[string]bool{}
244 g.modules = map[string]string{}
245 g.fn_ptr_types = map[string]string{}
246 g.fixed_array_ret_wrappers = map[string]bool{}
247 g.emitted_fixed_array_typedefs = map[string]bool{}
248 g.fn_decl_param_types = map[string][]types.Type{}
249 g.fn_decl_ret_types = map[string]types.Type{}
250 g.struct_decl_infos = map[string]StructDeclInfo{}
251 g.struct_decl_short_infos = map[string]StructDeclInfo{}
252 g.cur_param_names = []string{}
253 g.cur_param_type_values = []types.Type{}
254 g.cur_param_types = map[string]types.Type{}
255 g.cur_mut_params = map[string]bool{}
256 g.needed_optional_types = map[string]string{}
257 g.emitted_optional_types = map[string]bool{}
258 g.emitted_fns = map[string]bool{}
259 g.array_method_cache = map[string]string{}
260 g.param_types_cache = map[string][]types.Type{}
261 g.embedded_fields_by_type = map[string][]types.StructField{}
262 g.param_types_by_short = map[string][]types.Type{}
263 g.spawn_wrapper_names = map[string]string{}
264 g.spawn_wrapper_defs = []string{}
265 g.callback_wrapper_names = map[string]string{}
266 g.callback_wrapper_defs = []string{}
267 g.parallel_used = false
268 g.tc = unsafe { tc }
269 if g.tc.a == unsafe { nil } {
270 g.tc.collect(a)
271 }
272 g.has_builtins = g.tc.has_builtins
273 g.collect_gen_info()
274 g.precompute_embedded_fields()
275 g.precompute_param_type_index()
276 g.collect_interface_impls()
277 g.preseed_struct_fn_ptr_types()
278 g.preseed_global_fn_ptr_types()
279 g.preseed_c_extern_fn_ptr_types()
280 // Decide fixed-array return wrappers before generating function bodies, so
281 // signatures, returns and call sites all agree on the wrapped types.
282 g.populate_fixed_array_ret_wrappers()
283 const_code := g.precompute_consts()
284 orig_sb := g.sb
285 orig_line_start := g.line_start
286 g.sb = strings.new_builder(4096)
287 g.line_start = true
288 g.gen_fns_dispatch(no_parallel)
289 fn_code := g.sb.str()
290 // `.str()` copies out of the builder; free the emptied backing array under -gc none.
291 unsafe { g.sb.free() }
292 g.sb = orig_sb
293 g.line_start = orig_line_start
294 g.c99_feature_test_macros()
295 g.emit_preserved_c_directives()
296 g.preamble()
297 g.emit_c_directives()
298 g.enum_decls()
299 g.type_alias_decls()
300 g.type_forward_decls()
301 // Forward-declare multi-return structs before fn-ptr typedefs, which may name a
302 // multi-return as a by-value return type (full bodies come after struct_decls).
303 g.multi_return_forward_decls()
304 // Bare typedefs for primitive-element fixed arrays and wrapper structs for
305 // fixed-array return types, before fn-ptr typedefs (which may name a fixed
306 // array in param or return position) and the function declarations.
307 g.fixed_array_early_typedefs()
308 g.fn_ptr_typedefs()
309 g.struct_decls()
310 g.fixed_array_typedefs()
311 g.multi_return_typedefs()
312 g.optional_typedefs()
313 g.c_extern_forward_decls()
314 g.builtin_abi_decls()
315 g.global_decls()
316 g.forward_decls()
317 g.enum_str_forward_decls()
318 g.callback_wrapper_decls()
319 g.spawn_wrapper_decls()
320 g.register_interface_strings()
321 g.string_literals()
322 g.interface_method_stubs()
323 g.enum_str_defs()
324 g.sb.write_string(const_code)
325 // The final builder now owns a copy of the const code.
326 unsafe { const_code.free() }
327 if g.const_runtime_inits.len > 0 || g.runtime_inits.len > 0 || g.module_init_fns.len > 0
328 || g.global_inits.len > 0 {
329 g.writeln('void _vinit() {')
330 mut emitted_const := []bool{len: g.const_runtime_inits.len}
331 mut emitted_runtime := []bool{len: g.runtime_inits.len}
332 init_fns := g.module_init_fn_map()
333 for mod in g.ordered_startup_modules(init_fns) {
334 g.emit_runtime_inits_for_module(mod, mut emitted_const, mut emitted_runtime)
335 if init_fn := init_fns[mod] {
336 g.writeln('\t${init_fn}();')
337 }
338 }
339 g.emit_remaining_runtime_inits(mut emitted_const, mut emitted_runtime)
340 g.writeln('}')
341 g.writeln('')
342 }
343 g.sb.write_string(fn_code)
344 // The final builder now owns a copy of the function code.
345 unsafe { fn_code.free() }
346 result := g.sb.str()
347 // Keep only the returned C string, not the builder's copied backing array.
348 unsafe { g.sb.free() }
349 return result
350}
351
352// node_kind_id supports node kind id handling for c.
353fn node_kind_id(node flat.Node) int {
354 mut kind_id := node.kind_id
355 if kind_id == 0 && int(node.kind) != 0 {
356 kind_id = int(node.kind)
357 }
358 return kind_id
359}
360
361// collect_gen_info updates collect gen info state for c.
362fn (mut g FlatGen) collect_gen_info() {
363 g.collect_c_flags_from_directives()
364 mut cur_module := 'main'
365 mut cur_file := ''
366 mut seen_import_in_file := false
367 for node_idx in 0 .. g.a.nodes.len {
368 node := g.a.nodes[node_idx]
369 kind_id := node_kind_id(node)
370 if kind_id == 77 {
371 cur_file = node.value
372 g.note_compiler_source_file(node.value)
373 cur_module = 'main'
374 g.tc.cur_module = cur_module
375 g.tc.cur_file = cur_file
376 seen_import_in_file = false
377 continue
378 }
379 if kind_id == 73 {
380 cur_module = node.value
381 g.tc.cur_file = cur_file
382 g.tc.cur_module = cur_module
383 continue
384 }
385 if kind_id == 61 {
386 full_name := qualify_name_in_module(cur_module, node.value)
387 mut ptypes := []types.Type{}
388 g.tc.cur_file = cur_file
389 g.tc.cur_module = cur_module
390 for i in 0 .. node.children_count {
391 child := g.a.child_node(&node, i)
392 if node_kind_id(child) == 75 {
393 raw_pt := g.tc.parse_type(child.typ)
394 pt := raw_pt
395 ptypes << raw_pt
396 if pt is types.FnType {
397 ct := g.tc.c_type(raw_pt)
398 g.resolve_fn_ptr_type(ct)
399 }
400 }
401 }
402 ptypes = g.fn_param_types_with_implicit_veb_ctx(node, ptypes)
403 g.register_fn_decl_param_types(node.value, full_name, ptypes)
404 g.register_fn_decl_ret_type(node.value, full_name, node.typ)
405 // Module-level `init()` functions run once at startup. Collect their C
406 // names so _vinit can invoke them (V semantics).
407 if node.value == 'init' && ptypes.len == 0 {
408 init_cname := qualified_fn_name_in_module(cur_module, 'init')
409 if init_cname !in g.module_init_fns {
410 g.module_init_fns << init_cname
411 }
412 g.module_init_fn_modules[init_cname] = cur_module
413 }
414 continue
415 }
416 if g.collect_c_directive(cur_module, node, cur_file, !seen_import_in_file) {
417 continue
418 }
419 if node.kind == .directive && node.value == 'flag' {
420 continue
421 }
422 if node.kind == .directive && node.value == 'pkgconfig' {
423 continue
424 }
425 if kind_id == 62 {
426 full_name := qualify_name_in_module(cur_module, node.value)
427 g.register_struct_decl_info(node.value, full_name, cur_module, node)
428 continue
429 }
430 if kind_id == 64 {
431 g.tc.cur_file = cur_file
432 g.tc.cur_module = cur_module
433 for i in 0 .. node.children_count {
434 f := g.a.child_node(&node, i)
435 if f.value.starts_with('C.') {
436 continue
437 }
438 mut ft := g.tc.parse_type(f.typ)
439 if ft is types.Void && f.children_count > 0 {
440 ft = g.tc.resolve_type(g.a.child(f, 0))
441 }
442 qname := qualify_name_in_module(cur_module, f.value)
443 g.global_types[qname] = ft
444 g.global_modules[f.value] = cur_module
445 g.global_modules[qname] = cur_module
446 if f.children_count > 0 {
447 val_id := g.a.child(f, 0)
448 if int(val_id) >= 0 {
449 g.global_inits[qname] = val_id
450 g.global_init_order << qname
451 }
452 }
453 g.tc.file_scope.insert(f.value, ft)
454 if qname != f.value {
455 g.tc.file_scope.insert(qname, ft)
456 }
457 }
458 continue
459 }
460 if kind_id == 67 {
461 is_flag := node.typ == 'flag'
462 mut val := 0
463 enum_name := qualify_name_in_module(cur_module, node.value)
464 for i in 0 .. node.children_count {
465 f := g.a.child_node(&node, i)
466 if f.children_count > 0 {
467 if enum_val := g.enum_field_expr_value(g.a.child(f, 0)) {
468 val = enum_val
469 }
470 }
471 if is_flag {
472 g.enum_vals['${enum_name}.${f.value}'] = 1 << val
473 val++
474 } else {
475 g.enum_vals['${enum_name}.${f.value}'] = val
476 val++
477 }
478 }
479 continue
480 }
481 if kind_id == 70 {
482 iface_name := qualify_name_in_module(cur_module, node.value)
483 g.interfaces[iface_name] = g.tc.interface_abstract_method_names(iface_name)
484 continue
485 }
486 if kind_id == 65 {
487 for i in 0 .. node.children_count {
488 f := g.a.child_node(&node, i)
489 if node_kind_id(f) == 66 && f.children_count > 0 {
490 qname := g.const_storage_name(cur_module, f.value)
491 g.const_vals[qname] = g.a.child(f, 0)
492 g.const_modules[qname] = cur_module
493 if (cur_module.len == 0 || cur_module == 'main' || cur_module == 'builtin')
494 && f.value !in g.const_vals {
495 g.const_vals[f.value] = g.a.child(f, 0)
496 g.const_modules[f.value] = cur_module
497 }
498 }
499 }
500 continue
501 }
502 if kind_id == 72 {
503 seen_import_in_file = true
504 alias := node.typ.clone()
505 mod_name := node.value.clone()
506 if alias.len > 0 && mod_name.len > 0 {
507 g.modules[alias] = mod_name
508 }
509 if cur_module.len > 0 && mod_name.len > 0 {
510 dep_module := mod_name
511 if cur_module !in g.module_imports {
512 g.module_imports[cur_module] = []string{}
513 }
514 if dep_module !in g.module_imports[cur_module] {
515 g.module_imports[cur_module] << dep_module
516 }
517 }
518 continue
519 }
520 }
521 g.modules['strings'] = 'strings'
522 g.collect_const_init_order_from_files()
523}
524
525fn (mut g FlatGen) collect_c_flags_from_directives() {
526 mut cur_file := ''
527 for node in g.a.nodes {
528 kind_id := node_kind_id(node)
529 if kind_id == 77 {
530 cur_file = node.value
531 g.note_compiler_source_file(node.value)
532 continue
533 }
534 if node.kind != .directive || node.typ.len == 0 {
535 continue
536 }
537 if node.value == 'flag' {
538 flag := c_flag_arg(node.typ, g.compiler_vroot, cur_file)
539 if flag.len > 0 && flag !in g.c_flags {
540 g.c_flags << flag
541 }
542 continue
543 }
544 if node.value == 'pkgconfig' {
545 for flag in c_pkgconfig_flags(node.typ) {
546 if flag.len > 0 && flag !in g.c_flags {
547 g.c_flags << flag
548 }
549 }
550 }
551 }
552}
553
554fn (mut g FlatGen) collect_c_directive(module_name string, node flat.Node, source_file string, before_import bool) bool {
555 if node.kind != .directive {
556 return false
557 }
558 if node.value in ['include', 'insert'] {
559 if node.typ.len == 0 {
560 return true
561 }
562 include_arg := c_include_arg(node.typ, g.compiler_vroot, source_file)
563 if include_arg.len == 0 {
564 return true
565 }
566 // These helper headers are superseded by the inline compiler helpers emitted in
567 // builtin_abi_decls(); also including them would redefine the helpers.
568 if include_arg.contains('prealloc_atomics.h') || include_arg.contains('filelock_helpers.h')
569 || include_arg.contains('stdatomic') {
570 return true
571 }
572 include_dirs := c_flag_include_dirs(g.c_flags)
573 if header := c_inline_header_text(include_arg, g.compiler_vroot, source_file, include_dirs) {
574 header_text := header.text
575 g.collect_inlined_c_structs(header_text)
576 g.collect_inlined_c_fns(header_text)
577 g.collect_inlined_c_declared_fns(header_text)
578 g.collect_preserved_c_fns(header.preserved_c_fns)
579 g.collect_preserved_c_structs(header.preserved_c_structs)
580 for directive in header.preserved_directives {
581 g.add_c_directive(module_name, directive, before_import)
582 }
583 if header_text.len > 0 {
584 g.add_c_directive(module_name, header_text, before_import)
585 }
586 } else if c_should_preserve_uninlined_include(include_arg) {
587 g.collect_preserved_c_fns(c_preserved_system_include_declared_fns(include_arg))
588 g.collect_preserved_c_structs(c_preserved_system_include_struct_names(include_arg))
589 g.add_c_directive(module_name, '#include ${include_arg}', before_import)
590 }
591 return true
592 }
593 if node.value in ['define', 'undef', 'ifdef', 'ifndef', 'if', 'elif', 'else', 'endif', 'pragma',
594 'error', 'warning'] {
595 g.add_c_directive(module_name, c_preprocessor_directive_line(node.value, node.typ),
596 before_import)
597 return true
598 }
599 return false
600}
601
602fn c_inline_header_text(include_arg string, vroot string, source_file string, include_dirs []string) ?CInlineHeader {
603 if replacement := c_system_include_replacement(include_arg) {
604 return CInlineHeader{
605 text: replacement
606 }
607 }
608 mut seen := map[string]bool{}
609 for path in c_include_file_paths(include_arg, vroot, source_file, include_dirs) {
610 if header := c_inline_header_file(path, vroot, include_dirs, mut seen) {
611 return header
612 }
613 }
614 return none
615}
616
617fn c_inline_header_file(path string, vroot string, include_dirs []string, mut seen map[string]bool) ?CInlineHeader {
618 if path.len == 0 || !os.exists(path) {
619 return none
620 }
621 real_path := os.real_path(path)
622 if seen[real_path] {
623 return CInlineHeader{}
624 }
625 seen[real_path] = true
626 text := os.read_file(real_path) or { return none }
627 return c_inline_header_file_text(text, vroot, real_path, include_dirs, mut seen)
628}
629
630fn c_inline_header_file_text(text string, vroot string, source_file string, include_dirs []string, mut seen map[string]bool) CInlineHeader {
631 mut lines := []string{}
632 mut preserved_directives := []string{}
633 mut preserved_c_fns := []string{}
634 mut preserved_c_structs := []string{}
635 mut include_context := []string{}
636 mut include_prefix := []string{}
637 for line in text.split_into_lines() {
638 clean := line.trim_space()
639 if c_directive_name(clean) == 'include' {
640 include_arg := c_include_arg(c_directive_arg(clean), vroot, source_file)
641 if replacement := c_system_include_replacement(include_arg) {
642 lines << replacement
643 continue
644 }
645 mut inlined := false
646 for path in c_include_file_paths(include_arg, vroot, source_file, include_dirs) {
647 if nested := c_inline_header_file(path, vroot, include_dirs, mut seen) {
648 if nested.text.len > 0 {
649 lines << nested.text
650 }
651 preserved_directives << nested.preserved_directives
652 preserved_c_fns << nested.preserved_c_fns
653 preserved_c_structs << nested.preserved_c_structs
654 inlined = true
655 break
656 }
657 }
658 if !inlined && c_should_preserve_uninlined_include(include_arg) {
659 preserved_directives << c_preserved_nested_include_directive(include_arg,
660 include_context, include_prefix)
661 preserved_c_fns << c_preserved_system_include_declared_fns(include_arg)
662 preserved_c_structs << c_preserved_system_include_struct_names(include_arg)
663 }
664 continue
665 }
666 lines << line
667 c_update_nested_include_context(clean, line, mut include_context)
668 c_update_nested_include_prefix(clean, line, mut include_prefix)
669 }
670 return CInlineHeader{
671 text: lines.join('\n')
672 preserved_directives: preserved_directives
673 preserved_c_fns: preserved_c_fns
674 preserved_c_structs: preserved_c_structs
675 }
676}
677
678fn c_update_nested_include_context(clean string, line string, mut context []string) {
679 match c_directive_name(clean) {
680 'if', 'ifdef', 'ifndef', 'elif', 'else' {
681 context << line
682 }
683 'endif' {
684 for context.len > 0 {
685 name := c_directive_name(context[context.len - 1].trim_space())
686 context.delete_last()
687 if name in ['if', 'ifdef', 'ifndef'] {
688 break
689 }
690 }
691 }
692 else {}
693 }
694}
695
696fn c_update_nested_include_prefix(clean string, line string, mut prefix []string) {
697 match c_directive_name(clean) {
698 'define', 'undef' {
699 prefix << line
700 }
701 else {
702 prefix.clear()
703 }
704 }
705}
706
707fn c_preserved_nested_include_directive(include_arg string, context []string, prefix []string) string {
708 if context.len == 0 && prefix.len == 0 {
709 return '#include ${include_arg}'
710 }
711 mut lines := context.clone()
712 lines << prefix
713 lines << '#include ${include_arg}'
714 for _ in 0 .. c_nested_include_context_depth(context) {
715 lines << '#endif'
716 }
717 return lines.join('\n')
718}
719
720fn c_nested_include_context_depth(context []string) int {
721 mut depth := 0
722 for line in context {
723 if c_directive_name(line.trim_space()) in ['if', 'ifdef', 'ifndef'] {
724 depth++
725 }
726 }
727 return depth
728}
729
730fn c_flag_include_dirs(flags []string) []string {
731 mut dirs := []string{}
732 for flag in flags {
733 tokens := flag.fields()
734 mut i := 0
735 for i < tokens.len {
736 tok := tokens[i]
737 mut dir := ''
738 if tok == '-I' {
739 if i + 1 < tokens.len {
740 dir = tokens[i + 1]
741 i++
742 }
743 } else if tok.starts_with('-I') && tok.len > 2 {
744 dir = tok[2..]
745 }
746 if dir.len > 0 && dir !in dirs {
747 dirs << dir
748 }
749 i++
750 }
751 }
752 return dirs
753}
754
755fn c_system_include_replacement(include_arg string) ?string {
756 match include_arg.trim_space() {
757 '<stdint.h>' {
758 return c_stdint_header_text()
759 }
760 else {
761 return none
762 }
763 }
764}
765
766fn c_should_preserve_uninlined_include(include_arg string) bool {
767 clean := include_arg.trim_space()
768 if clean.len == 0 {
769 return false
770 }
771 if clean[0] == `<` {
772 return !c_headerless_system_include_is_handled(clean)
773 }
774 return true
775}
776
777fn c_preserved_system_include_declared_fns(include_arg string) []string {
778 match include_arg.trim_space() {
779 '<bcrypt.h>' {
780 return ['BCryptGenRandom']
781 }
782 '<dlfcn.h>' {
783 return ['dlopen', 'dlsym', 'dlclose', 'dlerror']
784 }
785 '<mach/mach_time.h>' {
786 return ['mach_absolute_time', 'mach_timebase_info']
787 }
788 else {
789 return []string{}
790 }
791 }
792}
793
794fn c_preserved_system_include_struct_names(include_arg string) []string {
795 match include_arg.trim_space() {
796 '<mach/mach_time.h>' {
797 return ['mach_timebase_info_data_t']
798 }
799 '<X11/Xlib.h>', '<X11/Xutil.h>', '<X11/Xresource.h>', '<X11/XKBlib.h>',
800 '<X11/extensions/XInput2.h>', '<X11/Xcursor/Xcursor.h>' {
801 return c_x11_preserved_struct_names.clone()
802 }
803 else {
804 return []string{}
805 }
806 }
807}
808
809const c_x11_preserved_struct_names = [
810 'Display',
811 'Screen',
812 'Visual',
813 'XButtonEvent',
814 'XClientMessageData',
815 'XClientMessageEvent',
816 'XCrossingEvent',
817 'XcursorImage',
818 'XDestroyWindowEvent',
819 'XEvent',
820 'XFocusChangeEvent',
821 'XGenericEventCookie',
822 'XIEventMask',
823 'XIRawEvent',
824 'XIValuatorState',
825 'XkbDescRec',
826 'XkbKeyAliasRec',
827 'XkbKeyNameRec',
828 'XkbNamesRec',
829 'XKeyEvent',
830 'XMotionEvent',
831 'XPropertyEvent',
832 'XrmValue',
833 'XSelectionClearEvent',
834 'XSelectionEvent',
835 'XSelectionRequestEvent',
836 'XSetWindowAttributes',
837 'XSizeHints',
838 'XVisualInfo',
839 'XWindowAttributes',
840]
841
842fn c_headerless_system_include_is_handled(include_arg string) bool {
843 if include_arg.len < 3 || include_arg[0] != `<` || include_arg[include_arg.len - 1] != `>` {
844 return false
845 }
846 name := include_arg[1..include_arg.len - 1]
847 return name in c_headerless_handled_system_headers
848}
849
850const c_headerless_handled_system_headers = {
851 'arpa/inet.h': true
852 'conio.h': true
853 'dirent.h': true
854 'errno.h': true
855 'execinfo.h': true
856 'fcntl.h': true
857 'float.h': true
858 'io.h': true
859 'math.h': true
860 'netdb.h': true
861 'netinet/in.h': true
862 'netinet/tcp.h': true
863 'poll.h': true
864 'process.h': true
865 'pthread.h': true
866 'pthread_np.h': true
867 'semaphore.h': true
868 'signal.h': true
869 'stdarg.h': true
870 'stdatomic.h': true
871 'stdbool.h': true
872 'stddef.h': true
873 'stdint.h': true
874 'stdio.h': true
875 'stdlib.h': true
876 'string.h': true
877 'synchapi.h': true
878 'sys/epoll.h': true
879 'sys/event.h': true
880 'sys/file.h': true
881 'sys/ioctl.h': true
882 'sys/mman.h': true
883 'sys/ptrace.h': true
884 'sys/random.h': true
885 'sys/resource.h': true
886 'sys/select.h': true
887 'sys/sendfile.h': true
888 'sys/socket.h': true
889 'sys/stat.h': true
890 'sys/statvfs.h': true
891 'sys/syscall.h': true
892 'sys/sysctl.h': true
893 'sys/syslimits.h': true
894 'sys/time.h': true
895 'sys/types.h': true
896 'sys/uio.h': true
897 'sys/utime.h': true
898 'sys/utsname.h': true
899 'sys/wait.h': true
900 'termios.h': true
901 'time.h': true
902 'unistd.h': true
903 'utime.h': true
904 'wchar.h': true
905 'windows.h': true
906 'winsock2.h': true
907 'ws2tcpip.h': true
908}
909
910fn c_stdint_header_text() string {
911 return '#if !defined(__V_HEADERLESS_STDINT_H) && !defined(_STDINT_H) && !defined(_STDINT_H_) && !defined(_STDINT) && !defined(_STDINT_H_INCLUDED) && !defined(_GCC_STDINT_H) && !defined(_MSC_STDINT_H_)
912#define __V_HEADERLESS_STDINT_H
913typedef signed char int8_t;
914typedef short int16_t;
915typedef int int32_t;
916typedef long long int64_t;
917typedef unsigned char uint8_t;
918typedef unsigned short uint16_t;
919typedef unsigned int uint32_t;
920typedef unsigned long long uint64_t;
921#ifndef INT8_MIN
922#define INT8_MIN (-128)
923#endif
924#ifndef INT16_MIN
925#define INT16_MIN (-32767 - 1)
926#endif
927#ifndef INT32_MIN
928#define INT32_MIN (-2147483647 - 1)
929#endif
930#ifndef INT64_MIN
931#define INT64_MIN (-9223372036854775807LL - 1)
932#endif
933#ifndef INT8_MAX
934#define INT8_MAX 127
935#endif
936#ifndef INT16_MAX
937#define INT16_MAX 32767
938#endif
939#ifndef INT32_MAX
940#define INT32_MAX 2147483647
941#endif
942#ifndef INT64_MAX
943#define INT64_MAX 9223372036854775807LL
944#endif
945#ifndef UINT8_MAX
946#define UINT8_MAX 255U
947#endif
948#ifndef UINT16_MAX
949#define UINT16_MAX 65535U
950#endif
951#ifndef UINT32_MAX
952#define UINT32_MAX 4294967295U
953#endif
954#ifndef UINT64_MAX
955#define UINT64_MAX 18446744073709551615ULL
956#endif
957#ifndef INT32_C
958#define INT32_C(c) c
959#endif
960#ifndef UINT32_C
961#define UINT32_C(c) c ## U
962#endif
963#ifndef INT64_C
964#define INT64_C(c) c ## LL
965#endif
966#ifndef UINT64_C
967#define UINT64_C(c) c ## ULL
968#endif
969#endif'
970}
971
972fn (mut g FlatGen) collect_inlined_c_structs(text string) {
973 for line in text.split_into_lines() {
974 clean := line.trim_space()
975 mut rest := ''
976 if clean.starts_with('typedef struct ') {
977 rest = clean['typedef struct '.len..]
978 } else if clean.starts_with('typedef union ') {
979 rest = clean['typedef union '.len..]
980 } else if clean.starts_with('struct ') {
981 rest = clean['struct '.len..]
982 } else if clean.starts_with('union ') {
983 rest = clean['union '.len..]
984 } else {
985 continue
986 }
987 tag := c_header_struct_tag(rest)
988 if tag.len > 0 {
989 g.inlined_c_structs[tag] = true
990 }
991 }
992 for alias in c_typedef_struct_aliases(text) {
993 g.inlined_c_structs[alias] = true
994 }
995 for alias in c_typedef_union_aliases(text) {
996 g.inlined_c_structs[alias] = true
997 }
998}
999
1000fn (mut g FlatGen) collect_inlined_c_fns(text string) {
1001 mut pending_static := false
1002 for line in text.split_into_lines() {
1003 clean := line.trim_space()
1004 if clean.len == 0 {
1005 continue
1006 }
1007 if clean.starts_with('static ') {
1008 name := c_header_fn_name(clean)
1009 if name.len > 0 {
1010 g.inlined_c_fns[name] = true
1011 pending_static = false
1012 } else {
1013 pending_static = c_static_fn_prefix_can_continue(clean)
1014 }
1015 continue
1016 }
1017 if pending_static {
1018 name := c_header_fn_name(clean)
1019 if name.len > 0 {
1020 g.inlined_c_fns[name] = true
1021 pending_static = false
1022 continue
1023 }
1024 if clean.ends_with(';') || clean.contains('{') || clean.starts_with('#') {
1025 pending_static = false
1026 }
1027 }
1028 }
1029}
1030
1031fn (mut g FlatGen) collect_inlined_c_declared_fns(text string) {
1032 for line in text.split_into_lines() {
1033 name := c_header_declared_fn_name(line.trim_space())
1034 if name.len > 0 {
1035 g.inlined_c_declared_fns[name] = true
1036 }
1037 }
1038}
1039
1040fn (mut g FlatGen) collect_preserved_c_fns(names []string) {
1041 for name in names {
1042 g.inlined_c_declared_fns[name] = true
1043 }
1044}
1045
1046fn (mut g FlatGen) collect_preserved_c_structs(names []string) {
1047 for name in names {
1048 g.inlined_c_structs[name] = true
1049 }
1050}
1051
1052fn c_static_fn_prefix_can_continue(line string) bool {
1053 return line in ['static', 'static inline', 'static __inline', 'static __inline__']
1054 || line.starts_with('static inline ') || line.starts_with('static __inline ')
1055 || line.starts_with('static __inline__ ')
1056}
1057
1058fn c_header_struct_tag(rest string) string {
1059 mut end := 0
1060 for end < rest.len {
1061 c := rest[end]
1062 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`) || c == `_` {
1063 end++
1064 continue
1065 }
1066 break
1067 }
1068 return rest[..end]
1069}
1070
1071fn c_typedef_struct_aliases(text string) []string {
1072 return c_typedef_aggregate_aliases(text, 'struct')
1073}
1074
1075fn c_typedef_union_aliases(text string) []string {
1076 return c_typedef_aggregate_aliases(text, 'union')
1077}
1078
1079fn c_typedef_aggregate_aliases(text string, kind string) []string {
1080 mut aliases := []string{}
1081 prefix := 'typedef ${kind}'
1082 mut start := 0
1083 for start < text.len {
1084 rel_idx := text[start..].index(prefix) or { break }
1085 idx := start + rel_idx
1086 mut pos := idx + prefix.len
1087 if pos < text.len && c_ident_char(text[pos]) {
1088 start = pos + 1
1089 continue
1090 }
1091 for pos < text.len && text[pos].is_space() {
1092 pos++
1093 }
1094 if pos < text.len && text[pos] != `{` {
1095 tag := c_header_struct_tag(text[pos..])
1096 if tag.len == 0 {
1097 start = pos + 1
1098 continue
1099 }
1100 pos += tag.len
1101 for pos < text.len && text[pos].is_space() {
1102 pos++
1103 }
1104 }
1105 if pos >= text.len || text[pos] != `{` {
1106 start = pos + 1
1107 continue
1108 }
1109 close_idx := c_matching_brace_end(text, pos)
1110 if close_idx < 0 {
1111 break
1112 }
1113 semi_rel_idx := text[close_idx + 1..].index_u8(`;`)
1114 if semi_rel_idx < 0 {
1115 break
1116 }
1117 semi_idx := close_idx + 1 + semi_rel_idx
1118 for alias in c_typedef_declarator_aliases(text[close_idx + 1..semi_idx]) {
1119 aliases << alias
1120 }
1121 start = semi_idx + 1
1122 }
1123 return aliases
1124}
1125
1126fn c_matching_brace_end(text string, open_idx int) int {
1127 mut depth := 0
1128 for i in open_idx .. text.len {
1129 if text[i] == `{` {
1130 depth++
1131 } else if text[i] == `}` {
1132 depth--
1133 if depth == 0 {
1134 return i
1135 }
1136 }
1137 }
1138 return -1
1139}
1140
1141fn c_typedef_declarator_aliases(decl string) []string {
1142 mut aliases := []string{}
1143 for part in decl.split(',') {
1144 alias := c_last_ident(part)
1145 if alias.len > 0 {
1146 aliases << alias
1147 }
1148 }
1149 return aliases
1150}
1151
1152fn c_last_ident(text string) string {
1153 mut end := text.len
1154 for end > 0 && !c_ident_char(text[end - 1]) {
1155 end--
1156 }
1157 mut start := end
1158 for start > 0 && c_ident_char(text[start - 1]) {
1159 start--
1160 }
1161 if start == end {
1162 return ''
1163 }
1164 return text[start..end]
1165}
1166
1167fn c_header_fn_name(line string) string {
1168 paren := line.index_u8(`(`)
1169 if paren < 0 {
1170 return ''
1171 }
1172 mut end := paren
1173 for end > 0 && line[end - 1].is_space() {
1174 end--
1175 }
1176 mut start := end
1177 for start > 0 && c_ident_char(line[start - 1]) {
1178 start--
1179 }
1180 if start == end {
1181 return ''
1182 }
1183 name := line[start..end]
1184 if name in ['if', 'for', 'while', 'switch'] {
1185 return ''
1186 }
1187 return name
1188}
1189
1190fn c_header_declared_fn_name(line string) string {
1191 if line.len == 0 || line[0] == `#` || !line.ends_with(';') || !line.contains('(') {
1192 return ''
1193 }
1194 if line.starts_with('typedef ') || line.contains('(*') || line.contains('=')
1195 || line.contains('{') || line.contains('}') {
1196 return ''
1197 }
1198 for prefix in ['return ', 'if ', 'if(', 'for ', 'for(', 'while ', 'while(', 'switch ', 'switch(',
1199 'case ', 'do ', 'else '] {
1200 if line.starts_with(prefix) {
1201 return ''
1202 }
1203 }
1204 paren := line.index_u8(`(`)
1205 mut end := paren
1206 for end > 0 && line[end - 1].is_space() {
1207 end--
1208 }
1209 mut start := end
1210 for start > 0 && c_ident_char(line[start - 1]) {
1211 start--
1212 }
1213 if start == 0 {
1214 return ''
1215 }
1216 return c_header_fn_name(line)
1217}
1218
1219fn c_ident_char(ch u8) bool {
1220 return (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`)
1221 || (ch >= `0` && ch <= `9`) || ch == `_`
1222}
1223
1224fn c_include_file_path(include_arg string, vroot string, source_file string) string {
1225 clean := include_arg.trim_space()
1226 if clean.len < 2 {
1227 return ''
1228 }
1229 if clean[0] == `<` {
1230 return ''
1231 }
1232 mut path := ''
1233 if clean[0] == `"` && clean[clean.len - 1] == `"` {
1234 path = clean[1..clean.len - 1]
1235 } else {
1236 path = clean
1237 }
1238 path = c_resolve_pseudo_paths(path, vroot, source_file)
1239 if path.len == 0 || os.is_abs_path(path) {
1240 return path
1241 }
1242 if source_file.len == 0 {
1243 return path
1244 }
1245 return os.join_path_single(os.dir(source_file), path)
1246}
1247
1248fn c_include_file_paths(include_arg string, vroot string, source_file string, include_dirs []string) []string {
1249 clean := include_arg.trim_space()
1250 if clean.len < 2 {
1251 return []string{}
1252 }
1253 mut raw_path := clean
1254 mut search_source_dir := true
1255 if clean[0] == `"` && clean[clean.len - 1] == `"` {
1256 raw_path = clean[1..clean.len - 1]
1257 } else if clean[0] == `<` && clean[clean.len - 1] == `>` {
1258 raw_path = clean[1..clean.len - 1]
1259 search_source_dir = false
1260 }
1261 mut paths := []string{}
1262 if search_source_dir {
1263 first := c_include_file_path(include_arg, vroot, source_file)
1264 if first.len > 0 {
1265 paths << first
1266 }
1267 }
1268 resolved_path := c_resolve_pseudo_paths(raw_path, vroot, source_file)
1269 if os.is_abs_path(resolved_path) {
1270 if resolved_path !in paths {
1271 paths << resolved_path
1272 }
1273 return paths
1274 }
1275 for dir in include_dirs {
1276 if dir.len == 0 {
1277 continue
1278 }
1279 path := os.join_path_single(dir, resolved_path)
1280 if path !in paths {
1281 paths << path
1282 }
1283 }
1284 return paths
1285}
1286
1287fn (mut g FlatGen) add_c_directive(module_name string, text string, before_import bool) {
1288 if text.len == 0 {
1289 return
1290 }
1291 g.c_directives << CDirective{
1292 module: module_name
1293 text: text
1294 before_import: before_import
1295 }
1296}
1297
1298fn c_preprocessor_directive_line(name string, raw string) string {
1299 clean := raw.trim_space()
1300 if clean.len == 0 {
1301 return '#${name}'
1302 }
1303 return '#${name} ${clean}'
1304}
1305
1306// note_compiler_source_file supports note compiler source file handling for FlatGen.
1307fn (mut g FlatGen) note_compiler_source_file(path string) {
1308 if g.compiler_vroot.len > 0 || path.len == 0 {
1309 return
1310 }
1311 mut full_path := path
1312 if !os.is_abs_path(full_path) {
1313 full_path = os.abs_path(full_path)
1314 }
1315 full_path = os.real_path(full_path)
1316 normalized := full_path.replace('\\', '/')
1317 suffix := '/cmd/v/v.v'
1318 if normalized.ends_with(suffix) {
1319 g.compiler_vroot = normalized[..normalized.len - suffix.len]
1320 return
1321 }
1322 vlib_idx := normalized.index('/vlib/') or { return }
1323 if vlib_idx > 0 {
1324 g.compiler_vroot = normalized[..vlib_idx]
1325 }
1326}
1327
1328// collect_const_init_order_from_files converts collect const init order from files data for c.
1329fn (mut g FlatGen) collect_const_init_order_from_files() {
1330 mut seen := map[string]bool{}
1331 g.const_init_order = []string{}
1332 for node in g.a.nodes {
1333 if node_kind_id(node) != 77 || node.children_count == 0 {
1334 continue
1335 }
1336 mut cur_module := 'main'
1337 for i in 0 .. node.children_count {
1338 child := g.a.child_node(&node, i)
1339 kind_id := node_kind_id(child)
1340 if kind_id == 73 {
1341 cur_module = child.value
1342 continue
1343 }
1344 if kind_id != 65 {
1345 continue
1346 }
1347 for j in 0 .. child.children_count {
1348 field := g.a.child_node(child, j)
1349 if node_kind_id(field) != 66 || field.children_count == 0 {
1350 continue
1351 }
1352 qname := g.const_storage_name(cur_module, field.value)
1353 if qname in g.const_vals && !seen[qname] {
1354 seen[qname] = true
1355 g.const_init_order << qname
1356 }
1357 }
1358 }
1359 }
1360}
1361
1362// ordered_module_init_fns supports ordered module init fns handling for FlatGen.
1363fn (g &FlatGen) ordered_module_init_fns() []string {
1364 module_to_init := g.module_init_fn_map()
1365 mut result := []string{}
1366 mut visiting := map[string]bool{}
1367 mut visited := map[string]bool{}
1368 for init_fn in g.module_init_fns {
1369 mod := g.module_init_fn_modules[init_fn] or { '' }
1370 g.visit_module_init(mod, module_to_init, mut visiting, mut visited, mut result)
1371 }
1372 return result
1373}
1374
1375fn (g &FlatGen) module_init_fn_map() map[string]string {
1376 mut module_to_init := map[string]string{}
1377 for init_fn in g.module_init_fns {
1378 mod := g.module_init_fn_modules[init_fn] or { '' }
1379 module_to_init[mod] = init_fn
1380 }
1381 return module_to_init
1382}
1383
1384fn (g &FlatGen) ordered_startup_modules(module_to_init map[string]string) []string {
1385 mut module_order := []string{}
1386 for init_fn in g.module_init_fns {
1387 mod := g.module_init_fn_modules[init_fn] or { '' }
1388 if mod !in module_order {
1389 module_order << mod
1390 }
1391 }
1392 for mod in g.const_runtime_init_modules {
1393 if mod !in module_order {
1394 module_order << mod
1395 }
1396 }
1397 for mod in g.runtime_init_modules {
1398 if mod !in module_order {
1399 module_order << mod
1400 }
1401 }
1402 mut startup_modules := map[string]bool{}
1403 for mod in module_order {
1404 startup_modules[mod] = true
1405 }
1406 for mod, _ in module_to_init {
1407 startup_modules[mod] = true
1408 }
1409 mut result := []string{}
1410 mut visiting := map[string]bool{}
1411 mut visited := map[string]bool{}
1412 for mod in module_order {
1413 g.visit_startup_module(mod, startup_modules, mut visiting, mut visited, mut result)
1414 }
1415 return result
1416}
1417
1418fn (g &FlatGen) visit_startup_module(mod string, startup_modules map[string]bool, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
1419 if mod in visited || mod in visiting {
1420 return
1421 }
1422 visiting[mod] = true
1423 for dep in g.module_imports[mod] or { []string{} } {
1424 dep_module := g.startup_dependency_module(dep, startup_modules)
1425 g.visit_startup_module(dep_module, startup_modules, mut visiting, mut visited, mut result)
1426 }
1427 visiting.delete(mod)
1428 visited[mod] = true
1429 if mod in startup_modules {
1430 result << mod
1431 }
1432}
1433
1434fn (g &FlatGen) startup_dependency_module(dep string, startup_modules map[string]bool) string {
1435 if dep in startup_modules || dep in g.module_imports {
1436 return dep
1437 }
1438 short := startup_module_key(dep)
1439 if short in startup_modules || short in g.module_imports {
1440 return short
1441 }
1442 return dep
1443}
1444
1445fn (mut g FlatGen) emit_runtime_inits_for_module(mod string, mut emitted_const []bool, mut emitted_runtime []bool) {
1446 for i, ri in g.const_runtime_inits {
1447 if !emitted_const[i] && i < g.const_runtime_init_modules.len
1448 && g.const_runtime_init_modules[i] == mod {
1449 g.writeln(ri)
1450 emitted_const[i] = true
1451 }
1452 }
1453 for i, ri in g.runtime_inits {
1454 if !emitted_runtime[i] && i < g.runtime_init_modules.len && g.runtime_init_modules[i] == mod {
1455 g.writeln(ri)
1456 emitted_runtime[i] = true
1457 }
1458 }
1459}
1460
1461fn (mut g FlatGen) emit_remaining_runtime_inits(mut emitted_const []bool, mut emitted_runtime []bool) {
1462 for i, ri in g.const_runtime_inits {
1463 if !emitted_const[i] {
1464 g.writeln(ri)
1465 emitted_const[i] = true
1466 }
1467 }
1468 for i, ri in g.runtime_inits {
1469 if !emitted_runtime[i] {
1470 g.writeln(ri)
1471 emitted_runtime[i] = true
1472 }
1473 }
1474}
1475
1476fn (mut g FlatGen) queue_const_runtime_init(line string) {
1477 g.const_runtime_inits << line
1478 g.const_runtime_init_modules << g.tc.cur_module
1479}
1480
1481fn (mut g FlatGen) queue_runtime_init(line string) {
1482 g.runtime_inits << line
1483 g.runtime_init_modules << g.tc.cur_module
1484}
1485
1486// visit_module_init updates visit module init state for FlatGen.
1487fn (g &FlatGen) visit_module_init(mod string, module_to_init map[string]string, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
1488 if mod in visited || mod in visiting {
1489 return
1490 }
1491 visiting[mod] = true
1492 for dep in g.module_imports[mod] or { []string{} } {
1493 dep_module := startup_module_key(dep)
1494 g.visit_module_init(dep_module, module_to_init, mut visiting, mut visited, mut result)
1495 }
1496 visiting.delete(mod)
1497 visited[mod] = true
1498 if init_fn := module_to_init[mod] {
1499 result << init_fn
1500 }
1501}
1502
1503fn (g &FlatGen) ordered_c_directives() []string {
1504 mut directives_by_module := map[string][]CDirective{}
1505 mut module_order := []string{}
1506 for directive in g.c_directives {
1507 if directive.module !in directives_by_module {
1508 directives_by_module[directive.module] = []CDirective{}
1509 module_order << directive.module
1510 }
1511 directives_by_module[directive.module] << directive
1512 }
1513 mut result := []string{}
1514 mut visiting := map[string]bool{}
1515 mut visited := map[string]bool{}
1516 for mod in module_order {
1517 g.visit_c_directive_module(mod, directives_by_module, mut visiting, mut visited, mut result)
1518 }
1519 return dedupe_top_level_c_includes(result)
1520}
1521
1522fn (mut g FlatGen) emit_c_directives() {
1523 mut emitted := false
1524 for directive in g.ordered_c_directives() {
1525 if c_contains_preserved_system_include_directive(directive) {
1526 continue
1527 }
1528 g.writeln(directive)
1529 emitted = true
1530 }
1531 if emitted {
1532 g.writeln('')
1533 }
1534}
1535
1536fn (mut g FlatGen) emit_preserved_c_directives() {
1537 mut emitted := false
1538 directives := g.ordered_c_directives()
1539 for i, directive in directives {
1540 if !c_contains_preserved_system_include_directive(directive) {
1541 continue
1542 }
1543 if directive.contains('\n') {
1544 g.emit_preserved_c_directive(directive)
1545 emitted = true
1546 continue
1547 }
1548 prefix := c_lifted_include_context_prefix(directives, i)
1549 for line in prefix {
1550 g.writeln(line)
1551 emitted = true
1552 }
1553 g.emit_preserved_c_directive(directive)
1554 emitted = true
1555 for _ in 0 .. c_lifted_include_context_depth(prefix) {
1556 g.writeln('#endif')
1557 }
1558 }
1559 if emitted {
1560 g.writeln('')
1561 }
1562}
1563
1564fn (mut g FlatGen) emit_preserved_c_directive(directive string) {
1565 if c_preserved_directive_needs_mach_panic_alias(directive) {
1566 g.writeln('#define panic mach_panic')
1567 g.writeln(directive)
1568 g.writeln('#undef panic')
1569 return
1570 }
1571 g.writeln(directive)
1572}
1573
1574fn c_preserved_directive_needs_mach_panic_alias(directive string) bool {
1575 for line in directive.split_into_lines() {
1576 clean := line.trim_space()
1577 if clean == '#include <mach/mach.h>' || clean == '#include <mach/mach_time.h>' {
1578 return true
1579 }
1580 }
1581 return false
1582}
1583
1584fn c_lifted_include_context_prefix(directives []string, include_index int) []string {
1585 mut prefix := []string{}
1586 for i := include_index - 1; i >= 0; i-- {
1587 clean := directives[i].trim_space()
1588 if c_is_preserved_system_include_directive(clean) {
1589 continue
1590 }
1591 if !c_is_liftable_include_context_directive(clean) {
1592 break
1593 }
1594 prefix << directives[i]
1595 }
1596 prefix.reverse_in_place()
1597 return prefix
1598}
1599
1600fn c_lifted_include_context_depth(prefix []string) int {
1601 mut depth := 0
1602 for directive in prefix {
1603 clean := directive.trim_space()
1604 if clean.starts_with('#ifdef') || clean.starts_with('#ifndef') || clean.starts_with('#if ') {
1605 depth++
1606 }
1607 }
1608 return depth
1609}
1610
1611fn c_is_liftable_include_context_directive(directive string) bool {
1612 clean := directive.trim_space()
1613 if clean.len == 0 || clean.contains('\n') || clean.starts_with('#endif') {
1614 return false
1615 }
1616 return clean.starts_with('#define') || clean.starts_with('#undef')
1617 || clean.starts_with('#ifdef') || clean.starts_with('#ifndef') || clean.starts_with('#if ')
1618 || clean.starts_with('#elif') || clean.starts_with('#else')
1619}
1620
1621fn c_is_preserved_system_include_directive(directive string) bool {
1622 clean := directive.trim_space()
1623 return clean.starts_with('#include <') && clean.ends_with('>') && !clean.contains('\n')
1624}
1625
1626fn c_contains_preserved_system_include_directive(directive string) bool {
1627 if c_is_preserved_system_include_directive(directive) {
1628 return true
1629 }
1630 for line in directive.split_into_lines() {
1631 if c_is_preserved_system_include_directive(line) {
1632 return true
1633 }
1634 }
1635 return false
1636}
1637
1638fn (g &FlatGen) visit_c_directive_module(mod string, directives_by_module map[string][]CDirective, mut visiting map[string]bool, mut visited map[string]bool, mut result []string) {
1639 if mod in visited || mod in visiting {
1640 return
1641 }
1642 visiting[mod] = true
1643 directives := directives_by_module[mod] or { []CDirective{} }
1644 for directive in directives {
1645 if directive.before_import {
1646 result << directive.text
1647 }
1648 }
1649 for dep in g.module_imports[mod] or { []string{} } {
1650 if dep in directives_by_module {
1651 g.visit_c_directive_module(dep, directives_by_module, mut visiting, mut visited, mut
1652 result)
1653 }
1654 }
1655 visiting.delete(mod)
1656 visited[mod] = true
1657 for directive in directives {
1658 if !directive.before_import {
1659 result << directive.text
1660 }
1661 }
1662}
1663
1664fn dedupe_top_level_c_includes(directives []string) []string {
1665 mut result := []string{}
1666 mut seen_includes := map[string]bool{}
1667 mut depth := 0
1668 for directive in directives {
1669 clean := directive.trim_space()
1670 if depth == 0 && c_directive_name(clean) == 'include' {
1671 if clean in seen_includes {
1672 continue
1673 }
1674 seen_includes[clean] = true
1675 }
1676 result << directive
1677 name := c_directive_name(clean)
1678 if name in ['if', 'ifdef', 'ifndef'] {
1679 depth++
1680 } else if name == 'endif' && depth > 0 {
1681 depth--
1682 }
1683 }
1684 return result
1685}
1686
1687fn c_directive_name(text string) string {
1688 if text.len == 0 || text[0] != `#` {
1689 return ''
1690 }
1691 body := text[1..].trim_space()
1692 if body.len == 0 {
1693 return ''
1694 }
1695 idx := body.index_u8(` `)
1696 if idx < 0 {
1697 return body
1698 }
1699 return body[..idx]
1700}
1701
1702fn c_directive_arg(text string) string {
1703 if text.len == 0 || text[0] != `#` {
1704 return ''
1705 }
1706 body := text[1..].trim_space()
1707 mut idx := 0
1708 for idx < body.len && !body[idx].is_space() {
1709 idx++
1710 }
1711 if idx >= body.len {
1712 return ''
1713 }
1714 return body[idx..].trim_space()
1715}
1716
1717fn c_include_arg(raw string, vroot string, source_file string) string {
1718 mut clean := c_directive_arg_for_target(raw.trim_space()) or { return '' }
1719 clean = c_resolve_pseudo_paths(clean.trim_space(), vroot, source_file)
1720 if clean.len == 0 {
1721 return ''
1722 }
1723 if clean[0] == `<` {
1724 end := clean.index_u8(`>`)
1725 if end > 0 {
1726 return clean[..end + 1]
1727 }
1728 return clean
1729 }
1730 if clean[0] == `"` {
1731 mut i := 1
1732 for i < clean.len {
1733 if clean[i] == `"` {
1734 return clean[..i + 1]
1735 }
1736 i++
1737 }
1738 }
1739 hash := clean.index_u8(`#`)
1740 if hash > 0 {
1741 return clean[..hash].trim_space()
1742 }
1743 return clean
1744}
1745
1746fn c_flag_arg(raw string, vroot string, source_file string) string {
1747 clean := c_directive_arg_for_target(raw.trim_space()) or { return '' }
1748 if clean.len == 0 {
1749 return ''
1750 }
1751 resolved := c_resolve_pseudo_paths(clean, vroot, source_file)
1752 return c_resolve_relative_flag_paths(resolved, source_file)
1753}
1754
1755// c_resolve_relative_flag_paths rewrites relative path arguments in a `#flag`
1756// directive (e.g. `-I ./thirdparty`, or a bare `./foo.c`) to absolute paths,
1757// resolved against the directory of the source file that carried the directive.
1758// V1 does the same: a project's `#flag` paths are relative to its own module dir,
1759// not to the compiler's build/working directory.
1760fn c_resolve_relative_flag_paths(flag string, source_file string) string {
1761 if source_file.len == 0 || !flag.contains('/') {
1762 return flag
1763 }
1764 base_dir := os.dir(source_file)
1765 if base_dir.len == 0 {
1766 return flag
1767 }
1768 mut out := []string{}
1769 for tok in flag.fields() {
1770 out << c_resolve_flag_path_token(tok, base_dir)
1771 }
1772 return out.join(' ')
1773}
1774
1775fn c_resolve_flag_path_token(tok string, base_dir string) string {
1776 for prefix in ['-I', '-L'] {
1777 if tok.starts_with(prefix) && tok.len > prefix.len {
1778 path := tok[prefix.len..]
1779 if c_flag_path_is_relative(path) {
1780 return prefix + os.real_path(os.join_path_single(base_dir, path))
1781 }
1782 return tok
1783 }
1784 }
1785 if !tok.starts_with('-') && c_flag_path_is_relative(tok) {
1786 return os.real_path(os.join_path_single(base_dir, tok))
1787 }
1788 return tok
1789}
1790
1791fn c_flag_path_is_relative(p string) bool {
1792 if p.len == 0 || os.is_abs_path(p) {
1793 return false
1794 }
1795 return p.starts_with('./') || p.starts_with('../') || p.contains('/')
1796}
1797
1798fn c_directive_arg_for_target(raw string) ?string {
1799 parts := raw.fields()
1800 if parts.len == 0 {
1801 return none
1802 }
1803 if c_flag_has_target_prefix(parts[0]) {
1804 if !c_flag_target_enabled(parts[0]) || parts.len < 2 {
1805 return none
1806 }
1807 return parts[1..].join(' ')
1808 }
1809 return raw
1810}
1811
1812fn c_resolve_pseudo_paths(raw string, vroot string, source_file string) string {
1813 mut result := raw
1814 if result.contains('@VEXEROOT') && vroot.len > 0 {
1815 result = result.replace('@VEXEROOT', vroot)
1816 }
1817 if result.contains('@VROOT') {
1818 result = result.replace('@VROOT', '@VMODROOT')
1819 }
1820 if result.contains('@VMODROOT') {
1821 result = result.replace('@VMODROOT', c_vmod_root_for_file(source_file))
1822 }
1823 if result.contains('@DIR') {
1824 dir := if source_file.len > 0 { os.dir(source_file) } else { os.getwd() }
1825 result = result.replace('@DIR', os.real_path(dir))
1826 }
1827 return result
1828}
1829
1830fn c_vmod_root_for_file(source_file string) string {
1831 mut dir := if source_file.len > 0 { os.dir(source_file) } else { os.getwd() }
1832 if dir.len == 0 {
1833 dir = os.getwd()
1834 }
1835 for {
1836 if os.exists(os.join_path(dir, 'v.mod')) {
1837 return os.real_path(dir)
1838 }
1839 parent := os.dir(dir)
1840 if parent == dir || parent.len == 0 {
1841 return os.real_path(dir)
1842 }
1843 dir = parent
1844 }
1845 return os.real_path(dir)
1846}
1847
1848fn c_pkgconfig_flags(raw string) []string {
1849 name := raw.trim_space()
1850 if name.len == 0 {
1851 return []string{}
1852 }
1853 // The package name comes straight from source text and is interpolated into a
1854 // shell command, so reject anything that is not a plain pkg-config name/flag to
1855 // avoid command injection (e.g. `#pkgconfig foo; touch /tmp/pwned`).
1856 if !c_pkgconfig_arg_is_safe(name) {
1857 return []string{}
1858 }
1859 result := os.execute('pkg-config --cflags --libs ${name}')
1860 if result.exit_code != 0 {
1861 return []string{}
1862 }
1863 return result.output.trim_space().fields()
1864}
1865
1866fn c_pkgconfig_arg_is_safe(raw string) bool {
1867 for ch in raw {
1868 if (ch >= `a` && ch <= `z`) || (ch >= `A` && ch <= `Z`) || (ch >= `0` && ch <= `9`) {
1869 continue
1870 }
1871 if ch in [` `, `\t`, `_`, `-`, `.`, `+`, `:`, `/`] {
1872 continue
1873 }
1874 return false
1875 }
1876 return true
1877}
1878
1879fn c_flag_has_target_prefix(target string) bool {
1880 return target in ['darwin', 'macos', 'linux', 'windows', 'freebsd', 'openbsd', 'netbsd',
1881 'solaris', 'wasm32_emscripten']
1882}
1883
1884fn c_flag_target_enabled(target string) bool {
1885 match target {
1886 'darwin', 'macos' {
1887 $if macos {
1888 return true
1889 }
1890 return false
1891 }
1892 'linux' {
1893 $if linux {
1894 return true
1895 }
1896 return false
1897 }
1898 'windows' {
1899 $if windows {
1900 return true
1901 }
1902 return false
1903 }
1904 'freebsd' {
1905 $if freebsd {
1906 return true
1907 }
1908 return false
1909 }
1910 'openbsd' {
1911 $if openbsd {
1912 return true
1913 }
1914 return false
1915 }
1916 'netbsd' {
1917 $if netbsd {
1918 return true
1919 }
1920 return false
1921 }
1922 'solaris' {
1923 $if solaris {
1924 return true
1925 }
1926 return false
1927 }
1928 'wasm32_emscripten' {
1929 $if wasm32_emscripten {
1930 return true
1931 }
1932 return false
1933 }
1934 else {
1935 return true
1936 }
1937 }
1938}
1939
1940// register_fn_decl_param_types updates register fn decl param types state for c.
1941fn (mut g FlatGen) register_fn_decl_param_types(name string, full_name string, ptypes []types.Type) {
1942 if name !in g.fn_decl_param_types {
1943 g.fn_decl_param_types[name] = ptypes.clone()
1944 }
1945 if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
1946 dotted_name := '${g.tc.cur_module}.${name}'
1947 if dotted_name !in g.fn_decl_param_types {
1948 g.fn_decl_param_types[dotted_name] = ptypes.clone()
1949 }
1950 }
1951 if full_name !in g.fn_decl_param_types {
1952 g.fn_decl_param_types[full_name] = ptypes.clone()
1953 }
1954}
1955
1956// register_fn_decl_ret_type indexes a fn decl's return type by its name (and qualified
1957// variants), so the return type can be looked up in O(1) instead of scanning all AST
1958// nodes per call (see fn_decl_return_type_for_call_name).
1959fn (mut g FlatGen) register_fn_decl_ret_type(name string, full_name string, ret_typ string) {
1960 rt := g.tc.parse_type(ret_typ)
1961 if name !in g.fn_decl_ret_types {
1962 g.fn_decl_ret_types[name] = rt
1963 }
1964 if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
1965 dotted_name := '${g.tc.cur_module}.${name}'
1966 if dotted_name !in g.fn_decl_ret_types {
1967 g.fn_decl_ret_types[dotted_name] = rt
1968 }
1969 }
1970 if full_name !in g.fn_decl_ret_types {
1971 g.fn_decl_ret_types[full_name] = rt
1972 }
1973 cname := c_name(name)
1974 if cname != name && cname !in g.fn_decl_ret_types {
1975 g.fn_decl_ret_types[cname] = rt
1976 }
1977}
1978
1979// register_struct_decl_info updates register struct decl info state for c.
1980fn (mut g FlatGen) register_struct_decl_info(name string, full_name string, module_name string, node flat.Node) {
1981 info := StructDeclInfo{
1982 node: node
1983 module: module_name
1984 full_name: full_name
1985 }
1986 g.struct_decl_infos[full_name] = info
1987 if name !in g.struct_decl_short_infos {
1988 g.struct_decl_short_infos[name] = info
1989 }
1990}
1991
1992// enum_value_for_type supports enum value for type handling for FlatGen.
1993fn (g &FlatGen) enum_value_for_type(type_name string, field_name string) ?int {
1994 if type_name.len == 0 || field_name.len == 0 {
1995 return none
1996 }
1997 key := '${type_name}.${field_name}'
1998 if val := g.enum_vals[key] {
1999 return val
2000 }
2001 if !type_name.contains('.') && g.tc.cur_module.len > 0 && g.tc.cur_module != 'main'
2002 && g.tc.cur_module != 'builtin' {
2003 qkey := '${g.tc.cur_module}.${type_name}.${field_name}'
2004 if val := g.enum_vals[qkey] {
2005 return val
2006 }
2007 }
2008 if !type_name.contains('.') {
2009 mut found := 0
2010 mut ok := false
2011 for ename, val in g.enum_vals {
2012 if !ename.ends_with('.${type_name}.${field_name}') {
2013 continue
2014 }
2015 if ok {
2016 return none
2017 }
2018 found = val
2019 ok = true
2020 }
2021 if ok {
2022 return found
2023 }
2024 }
2025 return none
2026}
2027
2028fn (g &FlatGen) enum_selector_base_name(name string) ?string {
2029 if name in g.tc.enum_names || name in g.tc.flag_enums {
2030 return name
2031 }
2032 qname := g.tc.qualify_name(name)
2033 if qname in g.tc.enum_names || qname in g.tc.flag_enums {
2034 return qname
2035 }
2036 if name.contains('.') || g.tc.cur_file.len == 0 {
2037 return none
2038 }
2039 candidates := g.tc.file_selective_imports['${g.tc.cur_file}\n${name}'] or { return none }
2040 for candidate in candidates {
2041 if candidate in g.tc.enum_names || candidate in g.tc.flag_enums {
2042 return candidate
2043 }
2044 }
2045 return none
2046}
2047
2048// expr_to_string converts expr to string data for c.
2049fn (mut g FlatGen) expr_to_string(id flat.NodeId) string {
2050 orig := g.sb
2051 orig_line_start := g.line_start
2052 g.sb = strings.new_builder(64)
2053 g.line_start = true
2054 g.gen_expr(id)
2055 result := g.sb.str()
2056 g.sb = orig
2057 g.line_start = orig_line_start
2058 return result
2059}
2060
2061// interface_value_to_string captures, as a string, the boxed interface value the direct return
2062// path emits (`(Iface){._typ = N, ._object = ...}`) — so a deferred return can save it into a
2063// temp without dropping `_typ`/`_object`. Mirrors that path: box a concrete value, else (already
2064// boxed by the transform) emit it as-is.
2065fn (mut g FlatGen) interface_value_to_string(id flat.NodeId, expected types.Type) string {
2066 orig := g.sb
2067 orig_line_start := g.line_start
2068 g.sb = strings.new_builder(64)
2069 // Box mid-statement (no leading indent), matching the direct return path.
2070 g.line_start = false
2071 if !g.gen_interface_value_expr(id, expected) {
2072 g.gen_expr(id)
2073 }
2074 result := g.sb.str()
2075 g.sb = orig
2076 g.line_start = orig_line_start
2077 return result
2078}
2079
2080// fixed_array_copy_source_string captures gen_fixed_array_copy_source as a string, so a deferred
2081// optional/fixed-array return can embed the memcpy source when saving the value into a temp.
2082fn (mut g FlatGen) fixed_array_copy_source_string(value_id flat.NodeId, field_type types.Type) string {
2083 orig := g.sb
2084 orig_line_start := g.line_start
2085 g.sb = strings.new_builder(64)
2086 // Emit mid-statement (no leading indent), matching the direct return path.
2087 g.line_start = false
2088 g.gen_fixed_array_copy_source(value_id, field_type)
2089 result := g.sb.str()
2090 g.sb = orig
2091 g.line_start = orig_line_start
2092 return result
2093}
2094
2095// expr_to_string_with_expected_type converts expr to string with expected type data for c.
2096fn (mut g FlatGen) expr_to_string_with_expected_type(id flat.NodeId, expected types.Type) string {
2097 orig := g.sb
2098 orig_line_start := g.line_start
2099 g.sb = strings.new_builder(64)
2100 g.line_start = true
2101 g.gen_expr_with_expected_type(id, expected)
2102 result := g.sb.str()
2103 g.sb = orig
2104 g.line_start = orig_line_start
2105 return result
2106}
2107
2108fn (mut g FlatGen) gen_amp_c_string_literal(id flat.NodeId, node flat.Node) bool {
2109 if node.kind == .char_literal && node.value.starts_with('c:') {
2110 g.gen_expr(id)
2111 return true
2112 }
2113 if node.kind != .char_literal && node.kind != .string_literal {
2114 return false
2115 }
2116 expr := g.expr_to_string(id)
2117 if expr.len >= 2 && expr[0] == `"` && expr[expr.len - 1] == `"` {
2118 g.write(expr)
2119 return true
2120 }
2121 return false
2122}
2123
2124fn (mut g FlatGen) gen_expr_as_string(id flat.NodeId) {
2125 typ := g.usable_expr_type(id)
2126 if g.gen_map_str_expr(id, typ) {
2127 return
2128 }
2129 g.gen_expr(id)
2130}
2131
2132fn (mut g FlatGen) gen_map_str_expr(id flat.NodeId, typ types.Type) bool {
2133 clean := map_str_clean_type(typ)
2134 if clean !is types.Map {
2135 return false
2136 }
2137 node := g.a.nodes[int(id)]
2138 if node.kind == .map_init && typ !is types.Pointer {
2139 tmp := '__map_str_tmp_${g.tmp_count}'
2140 g.tmp_count++
2141 g.write('({ map ${tmp} = ')
2142 g.gen_expr_with_expected_type(id, clean)
2143 g.write(';')
2144 key_kind := map_str_kind(g.tc, clean.key_type)
2145 val_kind := map_str_kind(g.tc, clean.value_type)
2146 fixed_len := map_str_fixed_len(clean.value_type)
2147 g.write(' v3_map_str(${tmp}, ${key_kind}, ${val_kind}, ${fixed_len}); })')
2148 return true
2149 }
2150 g.write('v3_map_str(')
2151 if typ is types.Pointer {
2152 needs_paren := g.a.nodes[int(id)].kind !in [.ident, .selector, .call]
2153 g.write('*')
2154 if needs_paren {
2155 g.write('(')
2156 }
2157 g.gen_expr(id)
2158 if needs_paren {
2159 g.write(')')
2160 }
2161 } else {
2162 g.gen_expr(id)
2163 }
2164 key_kind := map_str_kind(g.tc, clean.key_type)
2165 val_kind := map_str_kind(g.tc, clean.value_type)
2166 fixed_len := map_str_fixed_len(clean.value_type)
2167 g.write(', ${key_kind}, ${val_kind}, ${fixed_len})')
2168 return true
2169}
2170
2171fn map_str_clean_type(typ types.Type) types.Type {
2172 clean := types.unwrap_pointer(typ)
2173 if clean is types.Alias {
2174 return clean.base_type
2175 }
2176 return clean
2177}
2178
2179fn map_str_kind(tc &types.TypeChecker, typ types.Type) int {
2180 clean := if typ is types.Alias { typ.base_type } else { typ }
2181 if clean is types.String {
2182 return 1
2183 }
2184 if clean is types.Rune {
2185 return 4
2186 }
2187 if clean is types.ISize || clean is types.Char {
2188 return 2
2189 }
2190 if clean is types.USize {
2191 return 3
2192 }
2193 if clean is types.Primitive {
2194 if clean.props.has(.float) {
2195 return if tc.c_type(types.Type(clean)) == 'float' { 8 } else { 5 }
2196 }
2197 name := types.Type(clean).name()
2198 if name in ['i8', 'i16', 'i32', 'i64', 'int'] {
2199 return 2
2200 }
2201 if name in ['u8', 'byte'] {
2202 return 3
2203 }
2204 if name in ['u16', 'u32', 'u64'] {
2205 return 3
2206 }
2207 if name == 'bool' {
2208 return 7
2209 }
2210 }
2211 if fixed := array_fixed_type(clean) {
2212 elem := if fixed.elem_type is types.Alias {
2213 fixed.elem_type.base_type
2214 } else {
2215 fixed.elem_type
2216 }
2217 if elem is types.Primitive && elem.props.has(.float) {
2218 return if tc.c_type(types.Type(elem)) == 'float' { 9 } else { 6 }
2219 }
2220 }
2221 return 0
2222}
2223
2224fn map_str_fixed_len(typ types.Type) int {
2225 if fixed := array_fixed_type(typ) {
2226 if fixed.len > 0 {
2227 return fixed.len
2228 }
2229 if fixed.len_expr.len > 0 && fixed.len_expr.int().str() == fixed.len_expr {
2230 return fixed.len_expr.int()
2231 }
2232 }
2233 return 0
2234}
2235
2236// gen_cast_from_mut_param_address emits pointer casts for `¶m` where `param`
2237// is a mutable V parameter already represented as a C pointer.
2238fn (mut g FlatGen) gen_cast_from_mut_param_address(id flat.NodeId, ct string) bool {
2239 node := g.a.nodes[int(id)]
2240 if node.kind != .prefix || node.op != .amp || node.children_count != 1 {
2241 return false
2242 }
2243 child_id := g.a.child(&node, 0)
2244 child := g.a.nodes[int(child_id)]
2245 if child.kind != .ident || !g.current_param_is_mut(child.value) {
2246 return false
2247 }
2248 param_type := g.current_param_type(child.value) or { return false }
2249 if param_type !is types.Pointer {
2250 return false
2251 }
2252 g.write('(${ct})(')
2253 g.gen_expr(child_id)
2254 g.write(')')
2255 return true
2256}
2257
2258// gen_expr_with_expected_type emits expr with expected type output for c.
2259fn (mut g FlatGen) gen_expr_with_expected_type(id flat.NodeId, expected types.Type) {
2260 old_expected := g.expected_expr_type
2261 old_expected_enum := g.expected_enum
2262 g.expected_expr_type = expected
2263 if expected is types.Enum {
2264 g.expected_enum = expected.name
2265 }
2266 node := g.a.nodes[int(id)]
2267 if node.kind == .dump_expr {
2268 if node.children_count > 0 {
2269 g.gen_expr_with_expected_type(g.a.child(&node, 0), expected)
2270 } else {
2271 g.write('0')
2272 }
2273 g.expected_expr_type = old_expected
2274 g.expected_enum = old_expected_enum
2275 return
2276 }
2277 mut actual := g.usable_expr_type(id)
2278 if node.kind == .ident {
2279 if param_type := g.current_param_type(node.value) {
2280 actual = param_type
2281 }
2282 }
2283 if expected is types.String && g.gen_map_str_expr(id, actual) {
2284 g.expected_expr_type = old_expected
2285 g.expected_enum = old_expected_enum
2286 return
2287 }
2288 if _ := fn_type_from(expected) {
2289 if g.gen_callback_fn_value_for_expected_type(id, expected) {
2290 g.expected_expr_type = old_expected
2291 g.expected_enum = old_expected_enum
2292 return
2293 }
2294 if call_name := g.callback_direct_fn_value_name(id, expected) {
2295 g.write(g.callback_c_fn_name(call_name))
2296 g.expected_expr_type = old_expected
2297 g.expected_enum = old_expected_enum
2298 return
2299 }
2300 }
2301 if expected is types.Array && node.kind == .array_literal {
2302 g.gen_array_literal_value(node, expected.elem_type)
2303 g.expected_expr_type = old_expected
2304 g.expected_enum = old_expected_enum
2305 return
2306 }
2307 if expected !is types.Pointer && expected !is types.Void && expected !is types.OptionType
2308 && expected !is types.ResultType && actual is types.Pointer
2309 && g.type_names_match(actual.base_type, expected) {
2310 needs_paren := node.kind !in [.ident, .selector, .call, .index]
2311 g.write('*')
2312 if needs_paren {
2313 g.write('(')
2314 }
2315 g.gen_expr(id)
2316 if needs_paren {
2317 g.write(')')
2318 }
2319 g.expected_expr_type = old_expected
2320 g.expected_enum = old_expected_enum
2321 return
2322 }
2323 if g.gen_interface_value_expr(id, expected) {
2324 g.expected_expr_type = old_expected
2325 g.expected_enum = old_expected_enum
2326 return
2327 }
2328 if g.gen_sum_value_expr(id, expected) {
2329 g.expected_expr_type = old_expected
2330 g.expected_enum = old_expected_enum
2331 return
2332 }
2333 g.gen_expr(id)
2334 g.expected_expr_type = old_expected
2335 g.expected_enum = old_expected_enum
2336}
2337
2338// gen_sum_value_expr emits sum value expr output for c.
2339fn (mut g FlatGen) gen_sum_value_expr(id flat.NodeId, expected types.Type) bool {
2340 sum_type0 := if expected is types.Alias { expected.base_type } else { expected }
2341 if sum_type0 !is types.SumType {
2342 return false
2343 }
2344 sum_type := sum_type0 as types.SumType
2345 raw_actual0 := g.sum_cast_actual_type(id)
2346 raw_actual_type := if raw_actual0 is types.Alias { raw_actual0.base_type } else { raw_actual0 }
2347 if raw_actual_type is types.SumType {
2348 return false
2349 }
2350 if declared := g.selector_declared_type(id) {
2351 declared0 := if declared is types.Alias { declared.base_type } else { declared }
2352 if declared0 is types.SumType && g.type_names_match(declared0, sum_type0) {
2353 return false
2354 }
2355 }
2356 actual0 := raw_actual0
2357 actual_type := if actual0 is types.Alias { actual0.base_type } else { actual0 }
2358 if actual_type is types.SumType {
2359 return false
2360 }
2361 sum_name := g.resolve_sum_name(sum_type.name)
2362 variant := g.resolve_variant(sum_name, actual_type.name())
2363 variants := g.tc.sum_types[sum_name] or { return false }
2364 if variant !in variants {
2365 return false
2366 }
2367 ct := g.tc.c_type(sum_type0)
2368 idx := g.sum_type_index(sum_name, variant)
2369 field := g.sum_field_name(variant)
2370 if g.variant_references_sum(variant, sum_name) {
2371 inner_ct := g.tc.c_type(g.tc.parse_type(variant))
2372 g.write('(${ct}){.typ = ${idx}, .${field} = (${inner_ct}*)memdup((${inner_ct}[]){')
2373 g.gen_expr(id)
2374 g.write('}, sizeof(${inner_ct}))}')
2375 return true
2376 }
2377 g.write('(${ct}){.typ = ${idx}, .${field} = ')
2378 g.gen_expr(id)
2379 g.write('}')
2380 return true
2381}
2382
2383fn (g &FlatGen) sum_cast_actual_type(id flat.NodeId) types.Type {
2384 mut actual_type := g.tc.resolve_type(id)
2385 if int(id) < 0 || int(id) >= g.a.nodes.len {
2386 return actual_type
2387 }
2388 node := g.a.nodes[int(id)]
2389 if node.kind == .ident {
2390 if param_type := g.current_param_type(node.value) {
2391 return param_type
2392 }
2393 if param_type := g.cur_param_types[node.value] {
2394 return param_type
2395 }
2396 }
2397 return actual_type
2398}
2399
2400// gen_sum_cast_expr emits sum cast expr output for c.
2401fn (mut g FlatGen) gen_sum_cast_expr(target_type types.SumType, inner_id flat.NodeId) {
2402 inner := g.a.nodes[int(inner_id)]
2403 actual_type := g.sum_cast_actual_type(inner_id)
2404 actual_clean := types.unwrap_pointer(actual_type)
2405 variant_name0 := if inner.kind == .struct_init {
2406 inner.value
2407 } else {
2408 actual_clean.name()
2409 }
2410 variant_name := g.resolve_variant(target_type.name, variant_name0)
2411 idx := g.sum_type_index(target_type.name, variant_name)
2412 field := g.sum_field_name(variant_name)
2413 ct := g.tc.c_type(target_type)
2414 variant_type := g.tc.parse_type(variant_name)
2415 variant_is_pointer_arg := actual_type is types.Pointer
2416 && g.type_names_match(actual_type.base_type, variant_type)
2417 if g.variant_references_sum(variant_name, target_type.name) {
2418 inner_ct := g.tc.c_type(variant_type)
2419 if variant_is_pointer_arg {
2420 g.write('(${ct}){.typ = ${idx}, .${field} = ')
2421 if g.pointer_variant_arg_needs_heap_copy(inner) {
2422 g.write('(${inner_ct}*)memdup(')
2423 g.gen_expr(inner_id)
2424 g.write(', sizeof(${inner_ct}))')
2425 } else {
2426 g.gen_expr(inner_id)
2427 }
2428 g.write('}')
2429 } else if inner.kind == .struct_init
2430 && g.resolve_sum_name(inner.value) == g.resolve_sum_name(target_type.name) {
2431 g.write('(${ct}){')
2432 for si in 0 .. inner.children_count {
2433 sf := g.a.child_node(&inner, si)
2434 if si > 0 {
2435 g.write(', ')
2436 }
2437 g.write('.${c_name(sf.value)} = ')
2438 g.gen_lowered_sum_field_value(target_type.name, sf)
2439 }
2440 g.write('}')
2441 } else if inner.kind == .struct_init {
2442 g.write('(${ct}){.typ = ${idx}, .${field} = (${inner_ct}*)memdup(&(${inner_ct}){')
2443 for si in 0 .. inner.children_count {
2444 sf := g.a.child_node(&inner, si)
2445 if si > 0 {
2446 g.write(', ')
2447 }
2448 g.write('.${c_name(sf.value)} = ')
2449 g.gen_expr(g.a.child(sf, 0))
2450 }
2451 g.write('}, sizeof(${inner_ct}))}')
2452 } else {
2453 g.write('(${ct}){.typ = ${idx}, .${field} = (${inner_ct}*)memdup((${inner_ct}[]){')
2454 g.gen_expr(inner_id)
2455 g.write('}, sizeof(${inner_ct}))}')
2456 }
2457 } else {
2458 g.write('(${ct}){.typ = ${idx}, .${field} = ')
2459 if variant_is_pointer_arg {
2460 g.write('*')
2461 }
2462 g.gen_expr(inner_id)
2463 g.write('}')
2464 }
2465}
2466
2467// pointer_variant_arg_needs_heap_copy supports pointer_variant_arg_needs_heap_copy handling in c.
2468fn (g &FlatGen) pointer_variant_arg_needs_heap_copy(node flat.Node) bool {
2469 if node.kind != .prefix || node.op != .amp || node.children_count == 0 {
2470 return false
2471 }
2472 child_id := g.a.child(&node, 0)
2473 child := g.a.nodes[int(child_id)]
2474 if child.kind != .ident {
2475 return false
2476 }
2477 if _ := g.current_param_type(child.value) {
2478 return true
2479 }
2480 if child.value in g.cur_param_types {
2481 return true
2482 }
2483 if _ := g.tc.cur_scope.lookup(child.value) {
2484 return true
2485 }
2486 return false
2487}
2488
2489// selector_declared_type supports selector declared type handling for FlatGen.
2490fn (g &FlatGen) selector_declared_type(id flat.NodeId) ?types.Type {
2491 if int(id) < 0 || int(id) >= g.a.nodes.len {
2492 return none
2493 }
2494 node := g.a.nodes[int(id)]
2495 if node.kind != .selector || node.children_count == 0 {
2496 return none
2497 }
2498 base_id := g.a.child(&node, 0)
2499 base_type0 := types.unwrap_pointer(g.tc.resolve_type(base_id))
2500 base_type := if base_type0 is types.Alias { base_type0.base_type } else { base_type0 }
2501 if base_type is types.Struct {
2502 return g.struct_field_type(base_type.name, node.value)
2503 }
2504 return none
2505}
2506
2507fn (g &FlatGen) c_typedef_cast_call_name(node flat.Node) string {
2508 if node.kind != .call || node.children_count == 0 {
2509 return ''
2510 }
2511 callee := g.a.child_node(&node, 0)
2512 match callee.kind {
2513 .ident {
2514 if callee.value.contains('__') {
2515 return callee.value
2516 }
2517 }
2518 .selector {
2519 if callee.children_count > 0 {
2520 base := g.a.child_node(callee, 0)
2521 if base.kind == .ident && base.value == 'C' {
2522 return callee.value
2523 }
2524 }
2525 }
2526 else {}
2527 }
2528
2529 return ''
2530}
2531
2532// gen_expr_with_possible_enum_type emits expr with possible enum type output for c.
2533fn (mut g FlatGen) gen_expr_with_possible_enum_type(id flat.NodeId, expected types.Type) {
2534 if expected is types.Enum {
2535 g.gen_expr_with_expected_type(id, expected)
2536 return
2537 }
2538 g.gen_expr(id)
2539}
2540
2541fn (g &FlatGen) expected_expr_is_optional_struct() bool {
2542 if g.expected_expr_type is types.Struct {
2543 return g.expected_expr_type.name.starts_with('Optional')
2544 }
2545 return false
2546}
2547
2548fn (mut g FlatGen) type_name_c_type(type_name string) string {
2549 if _ := g.tc.cur_scope.lookup(type_name) {
2550 return c_name(type_name)
2551 }
2552 if type_name.starts_with('fn_ptr:') {
2553 return g.resolve_fn_ptr_type(type_name)
2554 }
2555 t := g.tc.parse_type(type_name)
2556 return g.tc.c_type(t)
2557}
2558
2559fn (mut g FlatGen) sizeof_target(value string) string {
2560 if value.starts_with('fn_ptr:') {
2561 return g.resolve_fn_ptr_type(value)
2562 }
2563 if fixed_target := c_fixed_array_typedef_sizeof_target(value) {
2564 return fixed_target
2565 }
2566 if value.contains('.') {
2567 parts := value.split('.')
2568 if parts.len > 1 {
2569 if g.cur_scope_has_local_name(parts[0]) {
2570 return sizeof_selector_target(parts[0], parts[1..])
2571 }
2572 if global := g.sizeof_global_selector_base(parts[0]) {
2573 return sizeof_selector_target(global, parts[1..])
2574 }
2575 }
2576 }
2577 if fixed := array_fixed_type(g.tc.parse_type(value)) {
2578 c_elem, dims := g.fixed_array_decl_parts(fixed)
2579 return '${c_elem}${dims}'
2580 }
2581 return g.type_name_c_type(value)
2582}
2583
2584fn c_fixed_array_typedef_sizeof_target(value string) ?string {
2585 if !value.starts_with('Array_fixed_') {
2586 return none
2587 }
2588 payload := value['Array_fixed_'.len..]
2589 if !payload.contains('_') {
2590 return none
2591 }
2592 elem := payload.all_before_last('_')
2593 len := payload.all_after_last('_')
2594 if elem.len == 0 || len.len == 0 {
2595 return none
2596 }
2597 return '${elem}[${len}]'
2598}
2599
2600fn sizeof_selector_target(base string, fields []string) string {
2601 mut expr := c_name(base)
2602 for field in fields {
2603 expr += '.${c_field_name(field)}'
2604 }
2605 return expr
2606}
2607
2608fn (g &FlatGen) cur_scope_has_local_name(name string) bool {
2609 mut scope := g.tc.cur_scope
2610 for scope != unsafe { nil } && scope != g.tc.file_scope {
2611 for existing in scope.names {
2612 if existing == name {
2613 return true
2614 }
2615 }
2616 scope = scope.parent
2617 }
2618 return false
2619}
2620
2621fn (g &FlatGen) sizeof_global_selector_base(name string) ?string {
2622 if name.len == 0 || name.contains('.') {
2623 return none
2624 }
2625 current_qname := qualify_name_in_module(g.tc.cur_module, name)
2626 if current_qname in g.global_types {
2627 return current_qname
2628 }
2629 if mod := g.global_modules[name] {
2630 if mod.len == 0 || mod == 'main' || mod == 'builtin' || mod == g.tc.cur_module {
2631 return if mod.len > 0 && mod != 'main' && mod != 'builtin' {
2632 '${mod}.${name}'
2633 } else {
2634 name
2635 }
2636 }
2637 }
2638 return none
2639}
2640
2641// optional_none_type supports optional none type handling for FlatGen.
2642fn (mut g FlatGen) optional_none_type(id flat.NodeId) types.Type {
2643 if g.expected_expr_type is types.OptionType || g.expected_expr_type is types.ResultType {
2644 return g.expected_expr_type
2645 }
2646 if typ := g.tc.expr_type(id) {
2647 if typ is types.OptionType || typ is types.ResultType {
2648 return typ
2649 }
2650 }
2651 if g.cur_fn_ret_is_optional {
2652 return g.cur_fn_ret
2653 }
2654 return types.Type(types.OptionType{
2655 base_type: types.Type(types.void_)
2656 })
2657}
2658
2659// array_index_info supports array index info handling for c.
2660fn array_index_info(t types.Type) (bool, bool, types.Array) {
2661 if t is types.Array {
2662 return true, false, t
2663 }
2664 if t is types.Alias {
2665 base := t.base_type
2666 if base is types.Array {
2667 return true, false, base
2668 }
2669 }
2670 if t is types.Pointer {
2671 base := t.base_type
2672 if base is types.Array {
2673 return true, true, base
2674 }
2675 if base is types.Alias {
2676 alias_base := base.base_type
2677 if alias_base is types.Array {
2678 return true, true, alias_base
2679 }
2680 }
2681 }
2682 return false, false, types.Array{}
2683}
2684
2685// valid_node_id supports valid node id handling for FlatGen.
2686fn (g &FlatGen) valid_node_id(id flat.NodeId) bool {
2687 return g.a != unsafe { nil } && int(id) >= 0 && int(id) < g.a.nodes.len
2688}
2689
2690// const_storage_name supports const storage name handling for FlatGen.
2691fn (g &FlatGen) const_storage_name(module_name string, name string) string {
2692 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin'
2693 && !name.contains('.') {
2694 return '${module_name}.${name}'
2695 }
2696 return name
2697}
2698
2699// const_primary_name supports const primary name handling for FlatGen.
2700fn (g &FlatGen) const_primary_name(name string) string {
2701 mod := if name in g.const_modules { g.const_modules[name] } else { '' }
2702 qname := g.const_storage_name(mod, name)
2703 if qname != name && qname in g.const_vals {
2704 return qname
2705 }
2706 return name
2707}
2708
2709// is_const_alias_name reports whether is const alias name applies in c.
2710fn (g &FlatGen) is_const_alias_name(name string) bool {
2711 return g.const_primary_name(name) != name
2712}
2713
2714// const_ref_name supports const ref name handling for FlatGen.
2715fn (g &FlatGen) const_ref_name(name string) string {
2716 if !name.contains('.') && !name.contains('__') {
2717 cur_qname := g.const_storage_name(g.tc.cur_module, name)
2718 if cur_qname in g.const_vals {
2719 return cur_qname
2720 }
2721 if name in g.const_vals {
2722 mod := g.const_modules[name] or { '' }
2723 if mod.len == 0 || mod == g.tc.cur_module
2724 || (g.tc.cur_module in ['', 'main', 'builtin'] && mod in ['', 'main', 'builtin']) {
2725 return g.const_primary_name(name)
2726 }
2727 }
2728 return ''
2729 }
2730 if name in g.const_vals {
2731 return g.const_primary_name(name)
2732 }
2733 if name.contains('.') {
2734 if name in g.const_vals {
2735 return g.const_primary_name(name)
2736 }
2737 }
2738 sep := if name.contains('.') {
2739 '.'
2740 } else if name.contains('__') {
2741 '__'
2742 } else {
2743 return ''
2744 }
2745 short_name := name.all_after_last(sep)
2746 if short_name !in g.const_vals {
2747 return ''
2748 }
2749 resolved := g.const_primary_name(short_name)
2750 mod := if resolved in g.const_modules { g.const_modules[resolved] } else { '' }
2751 if mod.len == 0 {
2752 return resolved
2753 }
2754 ref_mod := name.all_before_last(sep)
2755 if ref_mod == mod || ref_mod == mod.all_after_last('.') {
2756 return resolved
2757 }
2758 return ''
2759}
2760
2761// const_ref_name_from_node converts const ref name from node data for c.
2762fn (g &FlatGen) const_ref_name_from_node(node flat.Node) string {
2763 if node.kind == .ident {
2764 return g.const_ref_name(node.value)
2765 }
2766 if node.kind == .selector && node.children_count > 0 {
2767 base := g.a.child_node(&node, 0)
2768 if base.kind == .ident {
2769 return g.const_ref_name('${base.value}.${node.value}')
2770 }
2771 }
2772 return ''
2773}
2774
2775// const_expr_to_string converts const expr to string data for c.
2776fn (mut g FlatGen) const_expr_to_string(id flat.NodeId, seen []string) string {
2777 if int(id) < 0 || int(id) >= g.a.nodes.len {
2778 return '0'
2779 }
2780 node := g.a.nodes[int(id)]
2781 return match node.kind {
2782 .ident, .selector {
2783 const_name := g.const_ref_name_from_node(node)
2784 if const_name.len > 0 && const_name !in seen {
2785 mut next_seen := seen.clone()
2786 next_seen << const_name
2787 old_module := g.tc.cur_module
2788 if mod := g.const_modules[const_name] {
2789 g.tc.cur_module = mod
2790 }
2791 dep_expr := g.const_expr_to_string(g.const_vals[const_name], next_seen)
2792 g.tc.cur_module = old_module
2793 if dep_expr.trim_space().len > 0 {
2794 return dep_expr
2795 }
2796 }
2797 g.expr_to_string(id)
2798 }
2799 .infix {
2800 lhs := g.const_expr_to_string(g.a.child(&node, 0), seen)
2801 rhs := g.const_expr_to_string(g.a.child(&node, 1), seen)
2802 '(${lhs}) ${g.op_str(node.op)} (${rhs})'
2803 }
2804 .prefix {
2805 child := g.const_expr_to_string(g.a.child(&node, 0), seen)
2806 '${g.op_str(node.op)}(${child})'
2807 }
2808 .paren {
2809 child := g.const_expr_to_string(g.a.child(&node, 0), seen)
2810 '(${child})'
2811 }
2812 .cast_expr {
2813 target_type := g.tc.parse_type(node.value)
2814 mut ct := g.tc.c_type(target_type)
2815 if ct.starts_with('fn_ptr:') {
2816 ct = g.resolve_fn_ptr_type(ct)
2817 }
2818 if node.value in g.interfaces || g.tc.qualify_name(node.value) in g.interfaces {
2819 return '(${ct}){0}'
2820 }
2821 if target_type is types.SumType {
2822 inner_id := g.a.child(&node, 0)
2823 inner := g.a.nodes[int(inner_id)]
2824 variant_name0 := if inner.kind == .struct_init {
2825 inner.value
2826 } else {
2827 g.tc.resolve_type(inner_id).name()
2828 }
2829 variant_name := g.resolve_variant(target_type.name, variant_name0)
2830 idx := g.sum_type_index(target_type.name, variant_name)
2831 field := g.sum_field_name(variant_name)
2832 inner_val := g.const_expr_to_string(inner_id, seen)
2833 inner_ct := g.tc.c_type(g.tc.parse_type(variant_name))
2834 payload := if inner_val.trim_space().len == 0 { '0' } else { inner_val }
2835 return '(${ct}){.typ = ${idx}, .${field} = (${inner_ct}[]){${payload}}}'
2836 }
2837 if target_type !is types.Primitive && target_type !is types.Char
2838 && target_type !is types.Rune && target_type !is types.ISize
2839 && target_type !is types.USize && target_type !is types.Pointer
2840 && target_type !is types.Enum {
2841 return g.expr_to_string(id)
2842 }
2843 child0 := g.const_expr_to_string(g.a.child(&node, 0), seen)
2844 child := if child0.trim_space().len == 0 { '0' } else { child0 }
2845 '(${ct})(${child})'
2846 }
2847 .array_literal {
2848 mut parts := []string{}
2849 for i in 0 .. node.children_count {
2850 parts << g.const_expr_to_string(g.a.child(&node, i), seen)
2851 }
2852 '{${parts.join(', ')}}'
2853 }
2854 .struct_init {
2855 ct := g.struct_init_c_type_name(node.value)
2856 sum_name := g.resolve_sum_name(node.value)
2857 is_sum_literal := sum_name in g.tc.sum_types
2858 mut parts := []string{}
2859 for i in 0 .. node.children_count {
2860 field := g.a.child_node(&node, i)
2861 if field.kind == .field_init && field.children_count > 0 {
2862 val_id := g.a.child(field, 0)
2863 val_node := g.a.nodes[int(val_id)]
2864 val := if field.value.len == 0 {
2865 const_val := g.const_expr_to_string(val_id, seen)
2866 if const_val.trim_space().len > 0 {
2867 const_val
2868 } else {
2869 if ftyp := g.struct_field_type_at(node.value, i) {
2870 g.expr_to_string_with_expected_type(val_id, ftyp)
2871 } else {
2872 g.expr_to_string(val_id)
2873 }
2874 }
2875 } else if is_sum_literal && field.value != 'typ' {
2876 mut variant := ''
2877 if field.typ.starts_with('&') {
2878 variant = field.typ[1..]
2879 } else if field.typ.len > 0 {
2880 variant = field.typ
2881 } else {
2882 for v in g.tc.sum_types[sum_name] {
2883 if g.sum_field_name(v) == field.value {
2884 variant = v
2885 break
2886 }
2887 }
2888 }
2889 variant = g.resolve_variant(sum_name, variant)
2890 inner_ct := g.tc.c_type(g.tc.parse_type(variant))
2891 const_val := g.const_expr_to_string(val_id, seen)
2892 payload := if const_val.trim_space().len > 0 {
2893 const_val
2894 } else {
2895 g.expr_to_string_with_expected_type(val_id, g.tc.parse_type(variant))
2896 }
2897 '(${inner_ct}[]){${payload}}'
2898 } else if ftyp := g.struct_field_type(node.value, field.value) {
2899 if val_node.kind == .enum_val {
2900 g.expr_to_string_with_expected_type(val_id, ftyp)
2901 } else {
2902 const_val := g.const_expr_to_string(val_id, seen)
2903 if const_val.trim_space().len > 0 {
2904 const_val
2905 } else {
2906 g.expr_to_string_with_expected_type(val_id, ftyp)
2907 }
2908 }
2909 } else {
2910 const_val := g.const_expr_to_string(val_id, seen)
2911 if const_val.trim_space().len > 0 {
2912 const_val
2913 } else {
2914 g.expr_to_string(val_id)
2915 }
2916 }
2917 if field.value.len == 0 {
2918 parts << val
2919 } else {
2920 parts << '.${c_name(field.value)} = ${val}'
2921 }
2922 } else {
2923 parts << g.const_expr_to_string(g.a.child(&node, i), seen)
2924 }
2925 }
2926 '(${ct}){${parts.join(', ')}}'
2927 }
2928 .string_literal {
2929 '{"${c_escape(node.value)}", ${node.value.len}, 1}'
2930 }
2931 .int_literal, .float_literal, .bool_literal, .char_literal, .enum_val, .sizeof_expr {
2932 g.expr_to_string(id)
2933 }
2934 .offsetof_expr {
2935 ct := g.sizeof_target(node.value)
2936 'offsetof(${ct}, ${c_name(node.typ)})'
2937 }
2938 else {
2939 g.expr_to_string(id)
2940 }
2941 }
2942}
2943
2944// const_ident_c_name converts const ident c name data for c.
2945fn (g &FlatGen) const_ident_c_name(name string) string {
2946 if name.contains('.') {
2947 return c_name(name)
2948 }
2949 mod := if name in g.const_modules { g.const_modules[name] } else { '' }
2950 if mod.len > 0 && mod != 'main' && mod != 'builtin' {
2951 return c_name('${mod}.${name}')
2952 }
2953 if (mod == '' || mod == 'main') && name in g.const_modules {
2954 return c_name('main.${name}')
2955 }
2956 return c_name(name)
2957}
2958
2959// fixed_array_len_expr supports fixed array len expr handling for FlatGen.
2960fn (mut g FlatGen) fixed_array_len_expr(type_name string, fallback int) string {
2961 if type_name.len > 0 {
2962 typ := g.tc.parse_type(type_name)
2963 if typ is types.ArrayFixed {
2964 return g.fixed_array_len_value(typ)
2965 }
2966 }
2967 mut raw_len := ''
2968 if type_name.starts_with('[') {
2969 idx := type_name.index_u8(`]`)
2970 if idx > 1 {
2971 raw_len = type_name[1..idx]
2972 }
2973 } else if type_name.contains('[') && type_name.ends_with(']') {
2974 idx := type_name.index_u8(`[`)
2975 if idx >= 0 && idx < type_name.len - 1 {
2976 raw_len = type_name[idx + 1..type_name.len - 1]
2977 }
2978 }
2979 return g.fixed_array_len_raw(raw_len, fallback)
2980}
2981
2982// fixed_array_len_value supports fixed array len value handling for FlatGen.
2983fn (mut g FlatGen) fixed_array_len_value(arr types.ArrayFixed) string {
2984 // Prefer the evaluated integer length: a const-expression size (`[segs + 1]f32`)
2985 // otherwise reaches the raw fallback and is c_name-mangled into garbage.
2986 if v := g.tc.fixed_array_len_value(arr) {
2987 return v.str()
2988 }
2989 return g.fixed_array_len_raw(arr.len_expr, arr.len)
2990}
2991
2992// fixed_array_len_is_zero supports fixed array len is zero handling for FlatGen.
2993fn (mut g FlatGen) fixed_array_len_is_zero(arr types.ArrayFixed) bool {
2994 if value := g.tc.fixed_array_len_value(arr) {
2995 return value == 0
2996 }
2997 return g.fixed_array_len_value(arr).trim_space() == '0'
2998}
2999
3000// fixed_array_len_raw supports fixed array len raw handling for FlatGen.
3001fn (mut g FlatGen) fixed_array_len_raw(raw_len string, fallback int) string {
3002 if raw_len.len == 0 {
3003 return '${fallback}'
3004 }
3005 // A literal or const-expression size (`8`, `SEGS + 1`, `1 << 2`, `8 >>> 1`) folds to an
3006 // integer; emit that literal so the C dimension is always valid — `>>>` has no C form,
3007 // so a digit-leading expression like `8 >>> 1` must not be passed through raw — and a
3008 // non-numeric expr isn't c_name-mangled (`SEGS_+_1`) into an undeclared identifier.
3009 if v := g.tc.const_int_value(raw_len, []string{}) {
3010 return v.str()
3011 }
3012 clean_len := raw_len.replace('_', '')
3013 if clean_len.len > 0 && clean_len[0] >= `0` && clean_len[0] <= `9` {
3014 return clean_len
3015 }
3016 const_name := g.const_ref_name(raw_len)
3017 if const_name.len > 0 {
3018 expr := g.const_expr_to_string(g.const_vals[const_name], []string{})
3019 if expr.trim_space().len > 0 {
3020 return expr
3021 }
3022 return g.const_ident_c_name(const_name)
3023 }
3024 return c_name(raw_len)
3025}
3026
3027fn (mut g FlatGen) fixed_array_decl_parts(arr types.ArrayFixed) (string, string) {
3028 len_expr := g.fixed_array_len_value(arr)
3029 if arr.elem_type is types.ArrayFixed {
3030 base_ct, suffix := g.fixed_array_decl_parts(arr.elem_type)
3031 return base_ct, '[${len_expr}]${suffix}'
3032 }
3033 return g.tc.c_type(arr.elem_type), '[${len_expr}]'
3034}
3035
3036// infix_can_skip_child_parens reports whether a child infix operand needs no
3037// surrounding parentheses. For associative logical chains (`||`, `&&`) a child of
3038// the same operator is safe unparenthesised; this keeps long lowered chains (e.g.
3039// a `match` over hundreds of enum values → `a || b || c || ...`) from nesting
3040// parentheses past the C compiler's bracket-depth limit.
3041fn infix_can_skip_child_parens(parent_op flat.Op, child_op flat.Op) bool {
3042 return (parent_op == .logical_or && child_op == .logical_or)
3043 || (parent_op == .logical_and && child_op == .logical_and)
3044}
3045
3046// assoc_infix_chain_len counts how many same-operator infix nodes hang off the left
3047// spine of `node` (its nesting depth). Capped early since only "very deep" matters.
3048fn (g &FlatGen) assoc_infix_chain_len(node flat.Node) int {
3049 op := node.op
3050 mut cur := node
3051 mut depth := 0
3052 for {
3053 if cur.children_count < 1 {
3054 break
3055 }
3056 lhs_id := g.a.child(&cur, 0)
3057 if !g.valid_node_id(lhs_id) {
3058 break
3059 }
3060 lhs := g.a.nodes[int(lhs_id)]
3061 if lhs.kind == .infix && lhs.op == op {
3062 depth++
3063 if depth > 101 {
3064 break
3065 }
3066 cur = lhs
3067 } else {
3068 break
3069 }
3070 }
3071 return depth
3072}
3073
3074// gen_assoc_infix_chain emits a left-nested `||`/`&&` chain iteratively, producing the
3075// same flat `a || b || c …` C as the recursive path but without growing the stack per
3076// link (a big match's condition chain can be hundreds deep).
3077fn (mut g FlatGen) gen_assoc_infix_chain(node flat.Node) {
3078 op := node.op
3079 op_s := g.op_str(op)
3080 mut operands := []flat.NodeId{cap: 256}
3081 mut cur := node
3082 for {
3083 operands << g.a.child(&cur, 1)
3084 lhs_id := g.a.child(&cur, 0)
3085 lhs := g.a.nodes[int(lhs_id)]
3086 if lhs.kind == .infix && lhs.op == op && g.valid_node_id(g.a.child(&lhs, 0)) {
3087 cur = lhs
3088 } else {
3089 operands << lhs_id
3090 break
3091 }
3092 }
3093 for i := operands.len - 1; i >= 0; i-- {
3094 if i != operands.len - 1 {
3095 g.write(' ${op_s} ')
3096 }
3097 oid := operands[i]
3098 onode := g.a.nodes[int(oid)]
3099 if onode.kind == .infix && !infix_can_skip_child_parens(op, onode.op) {
3100 g.write('(')
3101 g.gen_expr(oid)
3102 g.write(')')
3103 } else {
3104 g.gen_expr(oid)
3105 }
3106 }
3107}
3108
3109// gen_expr emits expr output for c.
3110fn (mut g FlatGen) gen_expr(id flat.NodeId) {
3111 if int(id) < 0 {
3112 g.write('0')
3113 return
3114 }
3115 node := g.a.nodes[int(id)]
3116 match node.kind {
3117 .int_literal {
3118 v := node.value.replace('_', '')
3119 if v.starts_with('0o') {
3120 g.write('0${v[2..]}')
3121 } else {
3122 g.write(v)
3123 }
3124 }
3125 .float_literal {
3126 g.write(node.value.replace('_', ''))
3127 }
3128 .bool_literal {
3129 g.write(node.value)
3130 }
3131 .char_literal {
3132 v := node.value
3133 if v.starts_with('c:') {
3134 cv := v[2..]
3135 g.write('"${cv}"')
3136 } else if v.len == 0 {
3137 g.write("' '")
3138 } else if v.len == 1 {
3139 if v[0] == `\\` {
3140 g.write("'\\\\'")
3141 } else if v[0] == `'` {
3142 g.write("'\\''")
3143 } else {
3144 g.write("'${v}'")
3145 }
3146 } else if v.starts_with('\\') {
3147 g.write("'${v}'")
3148 } else {
3149 runes := v.runes()
3150 if runes.len == 0 {
3151 g.write('0')
3152 } else {
3153 g.write(int(runes[0]).str())
3154 }
3155 }
3156 }
3157 .string_literal {
3158 sid := g.intern_string(node.value)
3159 g.write('_str_${sid}')
3160 }
3161 .string_interp {
3162 g.gen_string_interp(node)
3163 }
3164 .dump_expr {
3165 if node.children_count > 0 {
3166 g.gen_expr(g.a.child(&node, 0))
3167 } else {
3168 g.write('0')
3169 }
3170 }
3171 .ident {
3172 if c_fn_name := g.test_user_main_fn_value_c_name(id, node) {
3173 g.write(c_fn_name)
3174 return
3175 }
3176 looked_up := g.tc.cur_scope.lookup(node.value) or { types.Type(types.void_) }
3177 is_local := looked_up !is types.Void
3178 const_name := if !is_local { g.const_ref_name(node.value) } else { '' }
3179 if const_name.len > 0 {
3180 g.write(g.const_ident_c_name(const_name))
3181 } else if node.value in g.global_modules {
3182 mod := g.global_modules[node.value]
3183 if mod.len > 0 && mod != 'main' && mod != 'builtin' {
3184 g.write(c_name('${mod}.${node.value}'))
3185 } else {
3186 g.write(c_name(node.value))
3187 }
3188 } else {
3189 g.write(c_name(node.value))
3190 }
3191 }
3192 .enum_val {
3193 if node.value in g.enum_vals {
3194 eval := g.enum_vals[node.value]
3195 g.write('${eval}')
3196 return
3197 }
3198 if node.typ.len > 0 {
3199 short_name := node.value.trim_left('.').all_after_last('.')
3200 if eval := g.enum_value_for_type(node.typ, short_name) {
3201 g.write('${eval}')
3202 return
3203 }
3204 }
3205 if g.expected_enum.len > 0 {
3206 ekey := '${g.expected_enum}.${node.value}'
3207 if ekey in g.enum_vals {
3208 eval := g.enum_vals[ekey]
3209 g.write('${eval}')
3210 return
3211 }
3212 if !g.expected_enum.contains('.') && g.tc.cur_module.len > 0
3213 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin' {
3214 qkey := '${g.tc.cur_module}.${g.expected_enum}.${node.value}'
3215 if qkey in g.enum_vals {
3216 eval := g.enum_vals[qkey]
3217 g.write('${eval}')
3218 return
3219 }
3220 }
3221 }
3222 for ename, eval in g.enum_vals {
3223 if ename.ends_with('.${node.value}') {
3224 g.write('${eval}')
3225 return
3226 }
3227 }
3228 g.write('0')
3229 }
3230 .call {
3231 // A call to a fixed-array-returning function yields the wrapper struct;
3232 // unwrap `.ret_arr` so the result behaves as the array value everywhere
3233 // (indexing, arg passing, memcpy into a destination).
3234 ret_t := g.declared_call_return_type(id)
3235 if ret_t is types.ArrayFixed && g.tc.c_type(ret_t) in g.fixed_array_ret_wrappers {
3236 g.write('(')
3237 g.gen_call(id, node)
3238 g.write(').ret_arr')
3239 } else {
3240 g.gen_call(id, node)
3241 }
3242 }
3243 .spawn_expr {
3244 g.gen_spawn_expr(node)
3245 }
3246 .infix {
3247 // A very long left-nested `||`/`&&` chain (e.g. from a big match condition or
3248 // a `!in [...]` over many values) would recurse once per link and overflow the
3249 // stack; emit those iteratively. Only pathologically long chains take this path,
3250 // so ordinary code keeps the existing per-node generation unchanged.
3251 if (node.op == .logical_or || node.op == .logical_and)
3252 && g.assoc_infix_chain_len(node) > 100 {
3253 g.gen_assoc_infix_chain(node)
3254 return
3255 }
3256 lhs_id := g.a.child(&node, 0)
3257 rhs_id := g.a.child(&node, 1)
3258 old_expected_enum := g.expected_enum
3259 lhs_type := g.usable_expr_type(lhs_id)
3260 rhs_type := g.usable_expr_type(rhs_id)
3261 if node.op == .arrow && lhs_type is types.Channel {
3262 elem_ct := g.tc.c_type(lhs_type.elem_type)
3263 g.write('sync__Channel__push(')
3264 g.gen_expr(lhs_id)
3265 g.write(', &(${elem_ct}[]){')
3266 g.gen_expr_with_expected_type(rhs_id, lhs_type.elem_type)
3267 g.write('})')
3268 g.expected_enum = old_expected_enum
3269 return
3270 }
3271 if g.gen_array_infix_eq(node, lhs_id, rhs_id, lhs_type, rhs_type) {
3272 g.expected_enum = old_expected_enum
3273 return
3274 }
3275 if lhs_type is types.String || rhs_type is types.String {
3276 if g.gen_string_infix_fallback(node, lhs_id, rhs_id) {
3277 g.expected_enum = old_expected_enum
3278 return
3279 }
3280 }
3281 if lhs_type is types.Enum {
3282 g.expected_enum = lhs_type.name
3283 } else if rhs_type is types.Enum {
3284 g.expected_enum = rhs_type.name
3285 }
3286 if lhs_type is types.Struct {
3287 op_name := match node.op {
3288 .minus { '__minus' }
3289 .plus { '__plus' }
3290 .eq { '__eq' }
3291 .ne { '__ne' }
3292 .lt { '__lt' }
3293 .gt { '__gt' }
3294 .le { '__le' }
3295 .ge { '__ge' }
3296 else { '' }
3297 }
3298
3299 if op_name.len > 0 {
3300 method_name := '${lhs_type.name}${op_name}'
3301 if method_name in g.tc.fn_param_types {
3302 panic('internal error: struct operator overload reached C backend after transform: ${lhs_type.name} op=${node.op}')
3303 }
3304 }
3305 g.gen_expr(lhs_id)
3306 g.write(' ${g.op_str(node.op)} ')
3307 g.gen_expr_with_possible_enum_type(rhs_id, lhs_type)
3308 } else {
3309 lhs_node := g.a.nodes[int(lhs_id)]
3310 rhs_node := g.a.nodes[int(rhs_id)]
3311 if lhs_node.kind == .infix && !infix_can_skip_child_parens(node.op, lhs_node.op) {
3312 g.write('(')
3313 g.gen_expr_with_possible_enum_type(lhs_id, rhs_type)
3314 g.write(')')
3315 } else {
3316 g.gen_expr_with_possible_enum_type(lhs_id, rhs_type)
3317 }
3318 g.write(' ${g.op_str(node.op)} ')
3319 if rhs_node.kind == .infix && !infix_can_skip_child_parens(node.op, rhs_node.op) {
3320 g.write('(')
3321 g.gen_expr_with_possible_enum_type(rhs_id, lhs_type)
3322 g.write(')')
3323 } else {
3324 g.gen_expr_with_possible_enum_type(rhs_id, lhs_type)
3325 }
3326 }
3327 g.expected_enum = old_expected_enum
3328 }
3329 .prefix {
3330 child_id := g.a.child(&node, 0)
3331 child := g.a.nodes[int(child_id)]
3332 if node.op == .arrow {
3333 child_type := g.usable_expr_type(child_id)
3334 if child_type is types.Channel {
3335 elem_ct := g.tc.c_type(child_type.elem_type)
3336 tmp := g.tmp_name()
3337 g.write('({${elem_ct} ${tmp} = (${elem_ct}){0}; sync__Channel__pop(')
3338 g.gen_expr(child_id)
3339 g.write(', &${tmp}); ${tmp};})')
3340 return
3341 }
3342 }
3343 if node.op == .mul && child.kind == .ident {
3344 if typ := g.current_param_type(child.value) {
3345 if typ !is types.Pointer {
3346 g.gen_expr(child_id)
3347 return
3348 }
3349 } else if typ := g.cur_param_types[child.value] {
3350 if typ !is types.Pointer {
3351 g.gen_expr(child_id)
3352 return
3353 }
3354 }
3355 }
3356 if node.op == .amp && g.gen_amp_c_string_literal(child_id, child) {
3357 return
3358 } else if node.op == .amp && child.kind == .struct_init {
3359 g.gen_heap_struct_init(child)
3360 } else if node.op == .amp && child.kind == .assoc {
3361 g.gen_heap_assoc_expr(child)
3362 } else if node.op == .amp && child.kind == .cast_expr {
3363 target_type := g.tc.parse_type(child.value)
3364 ct := g.cast_c_type(target_type)
3365 cast_arg := g.a.child_node(&child, 0)
3366 if cast_arg.kind == .nil_literal {
3367 g.write('(${ct}*)NULL')
3368 return
3369 }
3370 if target_type is types.SumType {
3371 g.write('(${ct}*)memdup(&')
3372 g.gen_sum_cast_expr(target_type, g.a.child(&child, 0))
3373 g.write(', sizeof(${ct}))')
3374 return
3375 }
3376 g.write('(${ct}*)(')
3377 g.gen_expr(g.a.child(&child, 0))
3378 g.write(')')
3379 } else if node.op == .amp && child.kind == .call {
3380 fn_child := g.a.child_node(&child, 0)
3381 if fn_child.kind == .selector {
3382 base_child := g.a.child_node(fn_child, 0)
3383 if base_child.kind == .ident && base_child.value == 'C' {
3384 c_struct_prefix := if fn_child.value.len > 0 && fn_child.value[0] >= `a`
3385 && fn_child.value[0] <= `z` && !fn_child.value.ends_with('_t') {
3386 'struct '
3387 } else {
3388 ''
3389 }
3390 g.write('(${c_struct_prefix}${fn_child.value}*)(')
3391 if child.children_count > 1 {
3392 g.gen_expr(g.a.child(&child, 1))
3393 } else {
3394 g.write('0')
3395 }
3396 g.write(')')
3397 } else {
3398 g.write(g.op_str(node.op))
3399 g.gen_expr(child_id)
3400 }
3401 } else {
3402 g.write(g.op_str(node.op))
3403 g.gen_expr(child_id)
3404 }
3405 } else {
3406 g.write(g.op_str(node.op))
3407 g.gen_expr(child_id)
3408 }
3409 }
3410 .in_expr {
3411 // NOTE: range membership, inline-array-literal membership, dynamic- and
3412 // fixed-array membership, and `!in` negation are lowered by the
3413 // transformer (transform.transform_in_expr). Map membership stays as an
3414 // in_expr so each backend can lower it directly.
3415 lhs_id := g.a.child(&node, 0)
3416 rhs_id := g.a.child(&node, 1)
3417 rhs := g.a.nodes[int(rhs_id)]
3418 rhs_type := g.usable_expr_type(rhs_id)
3419 clean_rhs := types.unwrap_pointer(rhs_type)
3420 if clean_rhs is types.Map {
3421 c_key := g.tc.c_type(clean_rhs.key_type)
3422 is_ptr := rhs_type is types.Pointer
3423 if is_ptr {
3424 g.write('map__exists(')
3425 } else {
3426 g.write('map__exists(&')
3427 }
3428 g.gen_expr(rhs_id)
3429 g.write(', &(${c_key}[]){')
3430 g.gen_expr(lhs_id)
3431 g.write('})')
3432 } else if rhs.kind == .array_literal {
3433 if rhs.children_count == 0 {
3434 g.write('false')
3435 } else {
3436 lhs_type := g.usable_expr_type(lhs_id)
3437 g.write('(')
3438 for i in 0 .. rhs.children_count {
3439 if i > 0 {
3440 g.write(' || ')
3441 }
3442 elem_id := g.a.child(&rhs, i)
3443 elem_type := g.usable_expr_type(elem_id)
3444 if lhs_type is types.String || elem_type is types.String {
3445 g.write('string__eq(')
3446 g.gen_expr(lhs_id)
3447 g.write(', ')
3448 g.gen_expr(elem_id)
3449 g.write(')')
3450 } else {
3451 g.gen_expr(lhs_id)
3452 g.write(' == ')
3453 g.gen_expr(elem_id)
3454 }
3455 }
3456 g.write(')')
3457 }
3458 } else if clean_rhs is types.Array {
3459 fn_name := array_membership_fn_name(clean_rhs.elem_type, false)
3460 g.write('${fn_name}(')
3461 // A `mut []T` param (or any `&[]T`) is a pointer in C; the membership
3462 // helper takes the array by value, so dereference it first.
3463 if rhs_type is types.Pointer {
3464 g.write('*')
3465 }
3466 g.gen_expr(rhs_id)
3467 g.write(', ')
3468 g.gen_expr(lhs_id)
3469 g.write(')')
3470 } else if clean_rhs is types.ArrayFixed {
3471 fn_name := array_membership_fn_name(clean_rhs.elem_type, true)
3472 len_expr := g.fixed_array_len_value(clean_rhs)
3473 g.write('${fn_name}(')
3474 g.gen_expr(rhs_id)
3475 g.write(', ${len_expr}, ')
3476 g.gen_expr(lhs_id)
3477 g.write(')')
3478 } else if clean_rhs is types.Struct && clean_rhs.name == 'array' {
3479 lhs_type := g.usable_expr_type(lhs_id)
3480 fn_name := array_membership_fn_name(lhs_type, false)
3481 g.write('${fn_name}(')
3482 g.gen_expr(rhs_id)
3483 g.write(', ')
3484 g.gen_expr(lhs_id)
3485 g.write(')')
3486 } else {
3487 panic('internal error: non-map membership reached C backend in ${g.cur_fn_name}: rhs=${rhs_type.name()} kind=${rhs.kind} value=${rhs.value}')
3488 }
3489 }
3490 .postfix {
3491 g.gen_expr(g.a.child(&node, 0))
3492 g.write(g.op_str(node.op))
3493 }
3494 .paren {
3495 g.write('(')
3496 g.gen_expr(g.a.child(&node, 0))
3497 g.write(')')
3498 }
3499 .selector {
3500 base_id := g.a.child(&node, 0)
3501 base := g.a.nodes[int(base_id)]
3502 base_type0 := g.tc.resolve_type(base_id)
3503 if base_type0 is types.Channel && node.value in ['closed', 'len'] {
3504 if node.value == 'closed' {
3505 g.write('(atomic_load_u16(&')
3506 g.gen_expr(base_id)
3507 g.write('->closed) != 0)')
3508 } else {
3509 g.write('sync__Channel__len(')
3510 g.gen_expr(base_id)
3511 g.write(')')
3512 }
3513 return
3514 }
3515 base_is_local := if base.kind == .ident {
3516 (g.tc.cur_scope.lookup(base.value) or { types.Type(types.void_) }) !is types.Void
3517 } else {
3518 false
3519 }
3520 // A method used as a value (e.g. `game.draw` passed as a callback) rather
3521 // than a field access — bind the receiver and yield a wrapper function.
3522 if g.gen_method_value_closure(base_id, base_type0, node.value) {
3523 return
3524 }
3525 enum_selector_qbase := if base.kind == .ident && base.value != 'C' && !base_is_local {
3526 g.enum_selector_base_name(base.value) or { '' }
3527 } else {
3528 ''
3529 }
3530 if base.kind == .ident && base.value == 'C' {
3531 g.write(c_winapi_wide_export_name(node.value))
3532 } else if enum_selector_qbase.len > 0 {
3533 ekey := '${enum_selector_qbase}.${node.value}'
3534 if eval := g.enum_vals[ekey] {
3535 g.write('${eval}')
3536 } else {
3537 g.write('0')
3538 }
3539 } else if base_type0 is types.String && node.value == 'len' {
3540 g.gen_expr(base_id)
3541 g.write('.len')
3542 } else if types.unwrap_pointer(base_type0) is types.Array && node.value == 'len' {
3543 needs_paren := base.kind !in [.ident, .selector, .call]
3544 if needs_paren {
3545 g.write('(')
3546 }
3547 g.gen_expr(base_id)
3548 if needs_paren {
3549 g.write(')')
3550 }
3551 if base_type0 is types.Pointer {
3552 g.write('->len')
3553 } else {
3554 g.write('.len')
3555 }
3556 } else if base.kind == .call && base.children_count == 2
3557 && g.c_typedef_cast_call_name(base).len > 0 {
3558 cast_name := g.c_typedef_cast_call_name(base)
3559 cast_arg_id := g.a.child(&base, 1)
3560 g.write('((${c_name(cast_name)}*)')
3561 g.gen_expr(cast_arg_id)
3562 g.write(')->${c_name(node.value)}')
3563 } else if base.kind == .cast_expr && base.children_count > 0
3564 && (base.value.starts_with('C.') || base.value.contains('__')) {
3565 cast_child_id := g.a.child(&base, 0)
3566 cast_name := if base.value.starts_with('C.') { base.value[2..] } else { base.value }
3567 g.write('((${c_name(cast_name)}*)')
3568 g.gen_expr(cast_child_id)
3569 g.write(')->${c_name(node.value)}')
3570 } else if base.kind == .cast_expr && base.children_count > 0 {
3571 needs_paren := base.kind !in [.ident, .selector]
3572 if needs_paren {
3573 g.write('(')
3574 }
3575 g.gen_expr(base_id)
3576 if needs_paren {
3577 g.write(')')
3578 }
3579 if node.op == .arrow || base_type0 is types.Pointer {
3580 g.write('->')
3581 } else {
3582 g.write('.')
3583 }
3584 g.write(c_name(node.value))
3585 } else if node.value == 'len' && base.kind == .ident {
3586 base_type := g.tc.resolve_type(base_id)
3587 if base_type is types.ArrayFixed {
3588 g.write(g.fixed_array_len_value(base_type))
3589 } else {
3590 raw_type := g.tc.cur_scope.lookup(base.value) or { base_type }
3591 g.gen_expr(base_id)
3592 if raw_type is types.Pointer {
3593 g.write('->len')
3594 } else {
3595 g.write('.len')
3596 }
3597 }
3598 } else if base.kind == .ident && !base_is_local && g.has_import_alias(base.value) {
3599 mod := g.import_alias_module(base.value) or { '' }
3600 short_mod := if mod.contains('.') {
3601 mod.all_after_last('.')
3602 } else {
3603 mod
3604 }
3605 // A module-level const is stored under the importing module's full path
3606 // (e.g. `v3.gen.wasm`), matching its function naming. Reference it by that
3607 // exact storage name rather than the short alias, otherwise we'd emit an
3608 // undeclared `wasm__x` for a const defined as `v3__gen__wasm__x`.
3609 full_qname := g.const_storage_name(mod, node.value)
3610 if full_qname in g.const_vals {
3611 g.write(c_name(full_qname))
3612 } else {
3613 g.write(c_name('${short_mod}.${node.value}'))
3614 }
3615 } else if base.kind == .selector && base.children_count > 0
3616 && g.is_module_qualified_enum(base) {
3617 inner_base := g.a.child_node(&base, 0)
3618 mod := g.import_alias_module(inner_base.value) or { inner_base.value }
3619 short_mod := if mod.contains('.') {
3620 mod.all_after_last('.')
3621 } else {
3622 mod
3623 }
3624 qname := '${short_mod}.${base.value}'
3625 if qname in g.tc.enum_names || base.value in g.tc.enum_names {
3626 ekey := '${qname}.${node.value}'
3627 ekey2 := '${base.value}.${node.value}'
3628 if ekey in g.enum_vals {
3629 eval := g.enum_vals[ekey]
3630 g.write('${eval}')
3631 } else if ekey2 in g.enum_vals {
3632 eval := g.enum_vals[ekey2]
3633 g.write('${eval}')
3634 } else {
3635 g.write(c_name('${qname}.${node.value}'))
3636 }
3637 } else {
3638 g.write(c_name('${qname}.${node.value}'))
3639 }
3640 } else if embedded := g.direct_embedded_field_for_selector(base_type0, node.value) {
3641 needs_paren := base.kind !in [.ident, .selector]
3642 if needs_paren {
3643 g.write('(')
3644 }
3645 g.gen_expr(base_id)
3646 if needs_paren {
3647 g.write(')')
3648 }
3649 if node.op == .arrow || base_type0 is types.Pointer {
3650 g.write('->')
3651 } else {
3652 g.write('.')
3653 }
3654 g.write(c_name(embedded.name))
3655 } else if embedded_path := g.embedded_field_path_for_promoted_selector(base_type0,
3656 node.value)
3657 {
3658 needs_paren := base.kind !in [.ident, .selector]
3659 if needs_paren {
3660 g.write('(')
3661 }
3662 g.gen_expr(base_id)
3663 if needs_paren {
3664 g.write(')')
3665 }
3666 mut is_ptr := node.op == .arrow || base_type0 is types.Pointer
3667 for embedded in embedded_path {
3668 op := if is_ptr { '->' } else { '.' }
3669 g.write('${op}${c_name(embedded.name)}')
3670 is_ptr = embedded.typ is types.Pointer
3671 }
3672 final_op := if is_ptr { '->' } else { '.' }
3673 g.write('${final_op}${c_name(node.value)}')
3674 } else {
3675 needs_paren := base.kind !in [.ident, .selector]
3676 if needs_paren {
3677 g.write('(')
3678 }
3679 g.gen_expr(base_id)
3680 if needs_paren {
3681 g.write(')')
3682 }
3683 mut is_ptr := false
3684 if base.kind == .ident {
3685 if typ := g.tc.cur_scope.lookup(base.value) {
3686 is_ptr = typ is types.Pointer
3687 }
3688 } else if base.kind == .selector {
3689 if declared := g.selector_declared_type(base_id) {
3690 is_ptr = declared is types.Pointer
3691 } else {
3692 resolved := g.tc.resolve_type(base_id)
3693 is_ptr = resolved is types.Pointer
3694 }
3695 } else {
3696 resolved := g.tc.resolve_type(base_id)
3697 is_ptr = resolved is types.Pointer
3698 }
3699 if node.op == .arrow || is_ptr {
3700 g.write('->')
3701 } else {
3702 g.write('.')
3703 }
3704 g.write(c_name(node.value))
3705 }
3706 }
3707 .index {
3708 base_id := g.a.child(&node, 0)
3709 base_type := g.tc.resolve_type(base_id)
3710 if node.value == 'range' {
3711 g.gen_slice_expr(node, base_id, base_type)
3712 } else if base_type is types.Map {
3713 c_key := g.value_c_type(base_type.key_type)
3714 c_val := g.value_c_type(base_type.value_type)
3715 g.write('(*(${c_val}*)map__get(&')
3716 g.gen_expr(base_id)
3717 g.write(', &(${c_key}[]){')
3718 g.gen_expr(g.a.child(&node, 1))
3719 g.write('}, &(${c_val}[]){0}))')
3720 } else {
3721 is_fixed_array_index, fixed_is_ptr, _ := fixed_array_index_info(base_type)
3722 if is_fixed_array_index {
3723 if fixed_is_ptr {
3724 g.write('(*')
3725 g.gen_expr(base_id)
3726 g.write(')')
3727 } else {
3728 g.gen_expr(base_id)
3729 }
3730 g.write('[')
3731 g.gen_expr(g.a.child(&node, 1))
3732 g.write(']')
3733 } else {
3734 is_array_index, is_ptr, arr_type := array_index_info(base_type)
3735 if is_array_index {
3736 index_type := if g.expected_expr_type is types.OptionType
3737 || g.expected_expr_type is types.ResultType
3738 || g.expected_expr_is_optional_struct() {
3739 g.expected_expr_type
3740 } else if node.typ.starts_with('?') || node.typ.starts_with('!') {
3741 g.tc.parse_type(node.typ)
3742 } else {
3743 arr_type.elem_type
3744 }
3745 c_elem := g.value_c_type(index_type)
3746 g.write('(*(${c_elem}*)array_get(')
3747 if is_ptr {
3748 g.write('*')
3749 }
3750 g.gen_expr(base_id)
3751 g.write(', ')
3752 g.gen_expr(g.a.child(&node, 1))
3753 g.write('))')
3754 } else if base_type is types.String {
3755 // Parenthesize the base: a smartcast sum variant yields a deref
3756 // like `*v._string`, and `*v._string.str[i]` would bind as
3757 // `*(v._string.str[i])`. `(*v._string).str[i]` is what we want.
3758 g.write('(')
3759 g.gen_expr(base_id)
3760 g.write(').str[')
3761 g.gen_expr(g.a.child(&node, 1))
3762 g.write(']')
3763 } else if base_type is types.Pointer {
3764 ptr_type := base_type
3765 if ptr_type.base_type is types.Void {
3766 g.write('((u8*)')
3767 g.gen_expr(base_id)
3768 g.write(')[')
3769 g.gen_expr(g.a.child(&node, 1))
3770 g.write(']')
3771 } else {
3772 g.gen_expr(base_id)
3773 g.write('[')
3774 g.gen_expr(g.a.child(&node, 1))
3775 g.write(']')
3776 }
3777 } else {
3778 g.gen_expr(base_id)
3779 g.write('[')
3780 g.gen_expr(g.a.child(&node, 1))
3781 g.write(']')
3782 }
3783 }
3784 }
3785 }
3786 .array_init {
3787 raw_init_type := g.tc.parse_type(node.value)
3788 init_type := raw_init_type
3789 if init_type is types.ArrayFixed {
3790 ct := g.tc.c_type(raw_init_type)
3791 g.write('(${ct}){0}')
3792 } else {
3793 c_elem := g.sizeof_target(node.value)
3794 g.write('array_new(sizeof(${c_elem}), 0, 0)')
3795 }
3796 }
3797 .map_init {
3798 g.gen_map_init(id, node)
3799 }
3800 .sql_expr {
3801 panic('internal error: SQL expression reached C backend after transform')
3802 }
3803 .cast_expr {
3804 target_type := g.tc.parse_type(node.value)
3805 mut ct := g.cast_c_type(target_type)
3806 if ct.starts_with('fn_ptr:') {
3807 ct = g.resolve_fn_ptr_type(ct)
3808 }
3809 if node.value in g.interfaces || g.tc.qualify_name(node.value) in g.interfaces {
3810 g.write('(${ct}){0}')
3811 } else if target_type is types.SumType {
3812 g.gen_sum_cast_expr(target_type, g.a.child(&node, 0))
3813 } else if target_type is types.Pointer
3814 && g.gen_cast_from_mut_param_address(g.a.child(&node, 0), ct) {
3815 return
3816 } else {
3817 g.write('(${ct})(')
3818 g.gen_expr(g.a.child(&node, 0))
3819 g.write(')')
3820 }
3821 }
3822 .struct_init {
3823 g.gen_struct_init(node)
3824 }
3825 .if_expr {
3826 g.gen_if_expr(node)
3827 }
3828 .array_literal {
3829 g.write('{')
3830 for i in 0 .. node.children_count {
3831 if i > 0 {
3832 g.write(', ')