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