v4 / vlib / v3 / gen / c / fn.v
4402 lines · 4210 sloc · 133.07 KB · 288feee4702b35beadb4037d02b96b5c8674156b
Raw
1module c
2
3import v3.flat
4import v3.types
5
6struct TestHarnessFn {
7 name string
8 c_name string
9 ret types.Type
10}
11
12struct TestHarnessHooks {
13mut:
14 testsuite_begin string
15 testsuite_end string
16 before_each string
17 after_each string
18}
19
20struct TopLevelStmt {
21 id flat.NodeId
22 file string
23 module string
24}
25
26// gen_fns emits fns output for c.
27fn (mut g FlatGen) gen_fns() {
28 mut cur_module := ''
29 mut cur_file := ''
30 for i in 0 .. g.a.nodes.len {
31 node := g.a.nodes[i]
32 kind_id := node_kind_id(node)
33 if kind_id == 77 {
34 cur_file = node.value
35 g.tc.cur_file = cur_file
36 cur_module = ''
37 g.tc.cur_module = cur_module
38 continue
39 }
40 if kind_id == 73 {
41 cur_module = node.value
42 g.tc.cur_file = cur_file
43 g.tc.cur_module = cur_module
44 continue
45 }
46
47 if kind_id == 61 {
48 if !g.should_emit_fn_node_in_module(node, i, cur_module) {
49 continue
50 }
51 qfn := g.fn_c_name_in_module(cur_module, node.value)
52 if g.emitted_fn_contains(qfn) {
53 continue
54 }
55 g.emitted_fns[qfn] = true
56 g.tc.cur_file = cur_file
57 g.tc.cur_module = cur_module
58 g.gen_fn_in_module(node, cur_module)
59 }
60 }
61}
62
63fn (mut g FlatGen) gen_synthetic_main_after_fns() {
64 if g.test_files.len > 0 {
65 g.gen_test_main()
66 return
67 }
68 if g.has_entry_main() {
69 return
70 }
71 top_level_stmts := g.top_level_stmts()
72 if top_level_stmts.len > 0 {
73 g.gen_top_level_main(top_level_stmts)
74 }
75}
76
77fn (g &FlatGen) has_entry_main() bool {
78 mut cur_module := ''
79 for node in g.a.nodes {
80 kind_id := node_kind_id(node)
81 if kind_id == 77 {
82 cur_module = ''
83 continue
84 }
85 if kind_id == 73 {
86 cur_module = node.value
87 continue
88 }
89 if kind_id == 61 && node.value == 'main' && (cur_module.len == 0 || cur_module == 'main') {
90 return true
91 }
92 }
93 return false
94}
95
96fn (g &FlatGen) top_level_stmts() []TopLevelStmt {
97 mut stmts := []TopLevelStmt{}
98 for file_idx, file_node in g.a.nodes {
99 if !g.should_emit_top_level_file(file_idx, file_node) {
100 continue
101 }
102 module_name := g.top_level_file_module_name(file_node)
103 for i in 0 .. file_node.children_count {
104 child_id := g.a.child(&file_node, i)
105 if int(child_id) < g.a.user_code_start {
106 continue
107 }
108 if g.cgen_is_top_level_stmt(child_id) {
109 stmts << TopLevelStmt{
110 id: child_id
111 file: file_node.value
112 module: if module_name.len == 0 { 'main' } else { module_name }
113 }
114 }
115 }
116 }
117 return stmts
118}
119
120fn (g &FlatGen) should_emit_top_level_file(file_idx int, file_node flat.Node) bool {
121 if file_idx < g.a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
122 return false
123 }
124 module_name := g.top_level_file_module_name(file_node)
125 return module_name.len == 0 || module_name == 'main'
126}
127
128fn (g &FlatGen) top_level_file_module_name(file_node flat.Node) string {
129 for i in 0 .. file_node.children_count {
130 child := g.a.child_node(&file_node, i)
131 if child.kind == .module_decl {
132 return child.value
133 }
134 }
135 return ''
136}
137
138fn (g &FlatGen) cgen_is_top_level_stmt(id flat.NodeId) bool {
139 if int(id) < 0 {
140 return false
141 }
142 node := g.a.nodes[int(id)]
143 return match node.kind {
144 .expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
145 .for_in_stmt, .if_expr, .assert_stmt, .defer_stmt {
146 true
147 }
148 .block, .comptime_if {
149 for i in 0 .. node.children_count {
150 if g.cgen_is_top_level_stmt(g.a.child(&node, i)) {
151 return true
152 }
153 }
154 false
155 }
156 else {
157 false
158 }
159 }
160}
161
162// should_emit_fn_node reports whether should emit fn node applies in c.
163fn (mut g FlatGen) should_emit_fn_node(node flat.Node, node_index int) bool {
164 return g.should_emit_fn_node_in_module(node, node_index, g.tc.cur_module)
165}
166
167// should_emit_fn_node_in_module reports whether should emit fn node in module applies in c.
168fn (mut g FlatGen) should_emit_fn_node_in_module(node flat.Node, node_index int, module_name string) bool {
169 _ = node_index
170 qfn := qualified_fn_name_in_module(module_name, node.value)
171 if g.should_rename_user_main_for_tests(module_name, node.value) {
172 return true
173 }
174 if module_name == 'builtin' && node.value == 'exit' {
175 return true
176 }
177 if node.value.starts_with('__anon_fn_') || qfn.contains('__anon_fn_') {
178 return true
179 }
180 if g.has_used_fn_filter() && !g.used_fn_contains_in_module(node.value, module_name) {
181 return false
182 }
183 return true
184}
185
186fn is_generated_fn_after_markused(name string) bool {
187 return name.starts_with('__anon_fn_')
188}
189
190// used_fn_contains reports whether used fn contains applies in c.
191fn (g &FlatGen) used_fn_contains(name string) bool {
192 if name.len == 0 {
193 return false
194 }
195 return g.used_fns[name]
196}
197
198fn (g &FlatGen) used_fn_contains_in_module(name string, module_name string) bool {
199 dfn := dotted_fn_name_in_module(module_name, name)
200 qfn := qualified_fn_name_in_module(module_name, name)
201 if g.used_fn_contains(dfn) || g.used_fn_contains(qfn) {
202 return true
203 }
204 if module_name.len == 0 || module_name == 'main' || module_name == 'builtin' {
205 cfn := c_name(name)
206 return g.used_fn_contains(name) || g.used_fn_contains(cfn)
207 }
208 return false
209}
210
211// has_used_fn_filter reports whether has used fn filter applies in c.
212fn (g &FlatGen) has_used_fn_filter() bool {
213 return g.used_fns.len > 0 && g.used_fn_contains('main')
214}
215
216// emitted_fn_contains reports whether emitted fn contains applies in c.
217fn (g &FlatGen) emitted_fn_contains(name string) bool {
218 return name.len > 0 && g.emitted_fns[name]
219}
220
221// qualified_fn_name supports qualified fn name handling for FlatGen.
222fn (g &FlatGen) qualified_fn_name(name string) string {
223 return qualified_fn_name_in_module(g.tc.cur_module, name)
224}
225
226fn (g &FlatGen) export_fn_name_in_module(module_name string, name string) ?string {
227 qname := dotted_fn_name_in_module(module_name, name)
228 if export_name := g.a.export_fn_names[qname] {
229 return export_name
230 }
231 if (module_name.len == 0 || module_name == 'main' || module_name == 'builtin')
232 && name in g.a.export_fn_names {
233 return g.a.export_fn_names[name]
234 }
235 return none
236}
237
238// qualified_fn_name_in_module supports qualified fn name in module handling for c.
239fn qualified_fn_name_in_module(module_name string, name string) string {
240 if module_name == 'builtin' && name == 'free' {
241 return 'v_free'
242 }
243 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
244 return c_name('${module_name}.${name}')
245 }
246 if name == 'free' {
247 return 'v_free'
248 }
249 return c_name(name)
250}
251
252fn is_main_fn_in_main_module(module_name string, name string) bool {
253 return name == 'main' && (module_name.len == 0 || module_name == 'main')
254}
255
256fn (g &FlatGen) should_rename_user_main_for_tests(module_name string, name string) bool {
257 return g.test_files.len > 0 && is_main_fn_in_main_module(module_name, name)
258}
259
260fn (g &FlatGen) fn_c_name_in_module(module_name string, name string) string {
261 if g.should_rename_user_main_for_tests(module_name, name) {
262 return g.test_user_main_c_name()
263 }
264 return qualified_fn_name_in_module(module_name, name)
265}
266
267fn (g &FlatGen) test_user_main_c_name() string {
268 base := 'main__user_main'
269 if !g.c_fn_symbol_exists(base) {
270 return base
271 }
272 limit := g.a.nodes.len + 2
273 for idx in 1 .. limit {
274 candidate := '${base}_${idx}'
275 if !g.c_fn_symbol_exists(candidate) {
276 return candidate
277 }
278 }
279 return '${base}_${limit}'
280}
281
282fn (g &FlatGen) c_fn_symbol_exists(candidate string) bool {
283 mut cur_module := ''
284 for node in g.a.nodes {
285 kind_id := node_kind_id(node)
286 if kind_id == 77 {
287 cur_module = ''
288 continue
289 }
290 if kind_id == 73 {
291 cur_module = node.value
292 continue
293 }
294 if kind_id != 61 {
295 continue
296 }
297 if qualified_fn_name_in_module(cur_module, node.value) == candidate {
298 return true
299 }
300 if export_name := g.export_fn_name_in_module(cur_module, node.value) {
301 if export_name == candidate {
302 return true
303 }
304 }
305 }
306 return false
307}
308
309// direct_call_name supports direct call name handling for FlatGen.
310fn (mut g FlatGen) direct_call_name(name string) string {
311 if compat_name := g.libc_compat_call_name(name) {
312 return compat_name
313 }
314 if g.test_files.len > 0 && (name == 'main' || name == 'main.main') {
315 return g.test_user_main_c_name()
316 }
317 if name == 'free' {
318 return 'v_free'
319 }
320 if name == 'int_str' {
321 return 'int__str'
322 }
323 if name == 'bool_str' {
324 return 'bool__str'
325 }
326 return c_name(name)
327}
328
329fn (mut g FlatGen) direct_call_name_for_call(id flat.NodeId, name string) string {
330 if g.test_files.len > 0 && (name == 'main' || name == 'main.main') {
331 if resolved := g.tc.resolved_call_name(id) {
332 if resolved == 'main' || resolved == 'main.main' {
333 return g.test_user_main_c_name()
334 }
335 }
336 return c_name(name)
337 }
338 return g.direct_call_name(name)
339}
340
341fn (mut g FlatGen) libc_compat_call_name(name string) ?string {
342 // `builtin.v_gettid()` reaches libc through `C.gettid()` on Linux/glibc, but
343 // that symbol is not declared by all usable C header sets. Route it through a
344 // tiny runtime compatibility helper instead of emitting an undeclared call.
345 if name == 'C.gettid' {
346 g.libc_compat_fns['gettid'] = true
347 return 'v3_gettid'
348 }
349 return none
350}
351
352fn (g &FlatGen) test_user_main_fn_value_c_name(id flat.NodeId, node flat.Node) ?string {
353 if g.test_files.len == 0 || node.kind != .ident || node.value != 'main' {
354 return none
355 }
356 looked_up := g.tc.cur_scope.lookup(node.value) or { types.Type(types.void_) }
357 if looked_up !is types.Void {
358 return none
359 }
360 if resolved := g.tc.resolved_fn_value_name(id) {
361 if resolved == 'main' || resolved == 'main.main' {
362 return g.test_user_main_c_name()
363 }
364 return none
365 }
366 if g.usable_expr_type(id) is types.FnType {
367 return g.test_user_main_c_name()
368 }
369 return none
370}
371
372fn (g &FlatGen) import_alias_module(alias string) ?string {
373 if alias.len == 0 {
374 return none
375 }
376 if g.tc != unsafe { nil } && g.tc.cur_file.len > 0 {
377 key := g.tc.cur_file + '\n' + alias
378 if mod := g.tc.file_imports[key] {
379 return mod
380 }
381 }
382 if mod := g.modules[alias] {
383 return mod
384 }
385 return none
386}
387
388fn (g &FlatGen) has_import_alias(alias string) bool {
389 if _ := g.import_alias_module(alias) {
390 return true
391 }
392 return false
393}
394
395// dotted_fn_name supports dotted fn name handling for FlatGen.
396fn (g &FlatGen) dotted_fn_name(name string) string {
397 return dotted_fn_name_in_module(g.tc.cur_module, name)
398}
399
400// dotted_fn_name_in_module supports dotted fn name in module handling for c.
401fn dotted_fn_name_in_module(module_name string, name string) string {
402 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
403 return '${module_name}.${name}'
404 }
405 return name
406}
407
408// qualify_name_in_module supports qualify name in module handling for c.
409fn qualify_name_in_module(module_name string, name string) string {
410 if module_name.len == 0 || module_name == 'main' || module_name == 'builtin' {
411 return name
412 }
413 if name.contains('.') {
414 return name
415 }
416 return '${module_name}.${name}'
417}
418
419// gen_fn emits fn output for c.
420fn (mut g FlatGen) gen_fn(node flat.Node) {
421 g.gen_fn_in_module(node, g.tc.cur_module)
422}
423
424fn (mut g FlatGen) write_method_c_name(id flat.NodeId, node flat.Node, method_name string) {
425 _ = id
426 _ = node
427 g.write(c_name(method_name))
428}
429
430// static_method_fn_name resolves `Type.method(...)` static calls where `Type` is a
431// named type, struct, enum, sum type or type alias (e.g. `SimdFloat4.new` for
432// `type SimdFloat4 = vec.Vec4[f32]`). Returns the fn key, or none if `type_ident`
433// is not a type or has no such static method.
434fn (g &FlatGen) static_method_fn_name(type_ident string, method string) ?string {
435 qtype := g.tc.qualify_name(type_ident)
436 is_type := type_ident in g.tc.type_aliases || qtype in g.tc.type_aliases
437 || type_ident in g.tc.structs || qtype in g.tc.structs || type_ident in g.tc.enum_names
438 || qtype in g.tc.enum_names || type_ident in g.tc.sum_types || qtype in g.tc.sum_types
439 if !is_type {
440 return none
441 }
442 // Prefer the module-qualified key: a static method defined in the current (or the
443 // type's) module is emitted under its qualified C name (`game__Animation__load`),
444 // so the call must resolve to the same qualified key even though an unqualified
445 // alias (`Animation.load`) may also be registered.
446 qdirect := '${qtype}.${method}'
447 if qtype != type_ident && (qdirect in g.tc.fn_ret_types || qdirect in g.tc.fn_param_types) {
448 return qdirect
449 }
450 direct := '${type_ident}.${method}'
451 if direct in g.tc.fn_ret_types || direct in g.tc.fn_param_types {
452 return direct
453 }
454 if qdirect in g.tc.fn_ret_types || qdirect in g.tc.fn_param_types {
455 return qdirect
456 }
457 return none
458}
459
460fn (g &FlatGen) resolve_method_name(type_name string, method string) string {
461 direct := '${type_name}.${method}'
462 if direct in g.tc.fn_param_types || direct in g.tc.fn_ret_types {
463 return direct
464 }
465 if g.tc.cur_module.len > 0 && g.tc.cur_module != 'main' && g.tc.cur_module != 'builtin'
466 && !type_name.contains('.') {
467 qualified := '${g.tc.cur_module}.${type_name}.${method}'
468 if qualified in g.tc.fn_param_types || qualified in g.tc.fn_ret_types {
469 return qualified
470 }
471 }
472 return ''
473}
474
475fn c_string_literal_pointer_arg(arg_node flat.Node, expected types.Type) bool {
476 if arg_node.kind != .char_literal || !arg_node.value.starts_with('c:') {
477 return false
478 }
479 if expected is types.Pointer {
480 base := types.unwrap_pointer(expected)
481 return base is types.Char
482 }
483 return false
484}
485
486fn (mut g FlatGen) gen_special_c_callback_arg(fn_name string, arg_idx int, arg_id flat.NodeId, expected_param types.Type) bool {
487 clean_name := fn_name.trim_string_left('C.').all_after_last('.')
488 if clean_name == 'mbedtls_ssl_conf_sni' && arg_idx == 1 {
489 g.write('(int (*)(void *, mbedtls_ssl_context *, const unsigned char *, size_t))')
490 g.gen_expr(arg_id)
491 return true
492 }
493 // Only convert to `(void*)` for an actual C-callback slot. A V `fn (...) ...`
494 // parameter is generated as a `_fn_ptr_*` typedef and must receive the function
495 // pointer directly: `(void*)foo` is an object-pointer-to-function-pointer cast that
496 // strict C rejects and that is not portable across ABIs. C functions (and loosely
497 // typed `voidptr` slots) still need it, because C uses the header prototype.
498 // `fn_type_from` is alias-aware, so a `type Cb = fn ()` parameter is recognised too.
499 if _ := fn_type_from(expected_param) {
500 return false
501 }
502 // A V function passed by name to a C function (a callback) must be cast: the V
503 // declaration's parameter type (often `voidptr`) is ignored by C in favour of the
504 // real header prototype, so the bare name trips -Wincompatible-function-pointer-types.
505 // `(void*)` converts cleanly to any function-pointer parameter.
506 if int(arg_id) >= 0 {
507 arg_node := g.a.nodes[int(arg_id)]
508 if arg_node.kind == .ident {
509 is_local := (g.tc.cur_scope.lookup(arg_node.value) or { types.Type(types.void_) }) !is types.Void
510 if !is_local {
511 call_name := g.call_key(arg_id, arg_node.value)
512 fn_key := if call_name in g.tc.fn_ret_types {
513 call_name
514 } else if arg_node.value in g.tc.fn_ret_types {
515 arg_node.value
516 } else {
517 ''
518 }
519 if fn_key.len > 0 && !fn_key.starts_with('C.') {
520 g.write('(void*)')
521 g.write(c_name(fn_key))
522 return true
523 }
524 }
525 }
526 }
527 return false
528}
529
530fn (mut g FlatGen) spawn_wrapper_decls() {
531 for def in g.spawn_wrapper_defs {
532 g.writeln(def)
533 }
534 if g.spawn_wrapper_defs.len > 0 {
535 g.writeln('')
536 }
537}
538
539// expr_is_addressable reports whether an expression denotes a stable lvalue whose address
540// outlives the enclosing statement expression — a variable, a field/index access reaching one,
541// or a dereference. Rvalues (struct literals, calls, ...) only have temporary storage, so their
542// address must not be captured in a method value's static receiver slot.
543fn (g &FlatGen) expr_is_addressable(id flat.NodeId) bool {
544 if int(id) < 0 {
545 return false
546 }
547 node := g.a.nodes[int(id)]
548 return match node.kind {
549 .ident { true }
550 .index { node.children_count > 0 && g.expr_is_addressable(g.a.child(&node, 0)) }
551 .selector { node.children_count > 0 && g.expr_is_addressable(g.a.child(&node, 0)) }
552 .prefix { node.op == .mul }
553 else { false }
554 }
555}
556
557// gen_method_value_closure handles a method used as a *value* (e.g. `game.draw`
558// passed where a `fn ()` callback is expected) rather than called. A plain struct
559// field access can't represent the bound receiver, so it binds the receiver into a
560// per-site context global and yields a wrapper function that invokes the method on
561// it. Returns false when the selector is an ordinary field access (handled normally).
562//
563// LIMITATION: the captured receiver lives in a single per-site global, so it is
564// only valid while no later evaluation of the *same* selector site overwrites it.
565// That covers the supported cases — passing/storing a method value and invoking it
566// before the site is re-evaluated (immediate callbacks, a single stored callback).
567// It does NOT support keeping several live method values from one site with
568// different receivers (e.g. building an array of `obj.method` in a loop): they all
569// share the global and would observe the last-bound receiver. A correct general
570// form needs a real per-closure captured environment, which the v3 backend has no
571// ABI for yet (anonymous fns are lifted without true capture). Such escaping method
572// values should be reworked (e.g. capture into an explicit struct + free fn) until
573// closure support lands.
574fn (mut g FlatGen) gen_method_value_closure(base_id flat.NodeId, base_type types.Type, method string) bool {
575 clean := types.unwrap_pointer(base_type)
576 if clean !is types.Struct {
577 return false
578 }
579 struct_name := (clean as types.Struct).name
580 // A real field shadows any same-named method: that's a field access, not a value.
581 if _ := g.struct_field_type(struct_name, method) {
582 return false
583 }
584 method_key := g.resolve_method_name(struct_name, method)
585 mut params := []types.Type{}
586 mut ret := types.Type(types.void_)
587 mut cname := ''
588 if method_key.len > 0 {
589 params = g.tc.fn_param_types[method_key] or { return false }
590 ret = g.tc.fn_ret_types[method_key] or { types.Type(types.void_) }
591 cname = c_name(method_key)
592 } else if ci := g.tc.generic_method_value_info['${struct_name}.${method}'] {
593 // Generic receiver (`Box[int]`): the open `Box[T].get` registration is gone by
594 // cgen, so use the substituted params/return the checker stashed, plus the
595 // monomorphised C name `c_name('Box[int].get')` == `Box_int__get`.
596 params = ci.params.clone()
597 // The receiver param is the un-substituted open `Box[T]`; replace it with the
598 // concrete receiver type (keeping the method's pointer-ness) so its C name
599 // resolves to `Box_int` rather than the template `Box`.
600 if params.len > 0 {
601 recv_concrete := types.unwrap_pointer(base_type)
602 params[0] = if params[0] is types.Pointer {
603 types.Type(types.Pointer{
604 base_type: recv_concrete
605 })
606 } else {
607 recv_concrete
608 }
609 }
610 ret = ci.return_type
611 cname = c_name('${struct_name}.${method}')
612 } else {
613 return false
614 }
615 if params.len == 0 {
616 return false
617 }
618 recv_is_ptr := params[0] is types.Pointer
619 recv_ct := g.tc.c_type(params[0])
620 // Use the method's ABI return type (matching `cname`'s signature and the callback
621 // fn-pointer typedef): an option/result is `Optional_T`, a fixed array its
622 // `_v_ret_*` wrapper — not the bare `c_type` (`Optional`/`Array_fixed_*`).
623 ret_ct := g.fn_return_type_name(ret)
624 idx := g.tmp_count
625 g.tmp_count++
626 ctx_name := '_mvctx_${idx}'
627 wrap_name := '_mvwrap_${idx}'
628 mut wparams := []string{}
629 mut call_args := [ctx_name]
630 for i in 1 .. params.len {
631 pt := g.tc.c_type(params[i])
632 wparams << '${pt} a${i}'
633 call_args << 'a${i}'
634 }
635 wparam_str := if wparams.len == 0 { 'void' } else { wparams.join(', ') }
636 g.spawn_wrapper_defs << 'static ${recv_ct} ${ctx_name};'
637 ret_prefix := if ret_ct == 'void' { '' } else { 'return ' }
638 g.spawn_wrapper_defs << 'static ${ret_ct} ${wrap_name}(${wparam_str}) { ${ret_prefix}${cname}(${call_args.join(', ')}); }'
639 base_is_ptr := base_type is types.Pointer
640 // Set the context global to the receiver, then yield the wrapper as the value.
641 if recv_is_ptr && !base_is_ptr && !g.expr_is_addressable(base_id) {
642 // Pointer receiver bound to an rvalue base (`Foo{..}.tick`, `make_foo().tick`): taking
643 // `&(rvalue)` captures a temporary that dies with the enclosing statement expression,
644 // before the callback runs. Copy the receiver into a durable static slot and point at it.
645 val_ct := g.tc.c_type(types.unwrap_pointer(params[0]))
646 g.spawn_wrapper_defs << 'static ${val_ct} ${ctx_name}_recv;'
647 g.write('({ ${ctx_name}_recv = ')
648 g.gen_expr(base_id)
649 g.write('; ${ctx_name} = &${ctx_name}_recv')
650 } else {
651 g.write('({ ${ctx_name} = ')
652 if recv_is_ptr && !base_is_ptr {
653 g.write('&(')
654 g.gen_expr(base_id)
655 g.write(')')
656 } else if !recv_is_ptr && base_is_ptr {
657 g.write('*(')
658 g.gen_expr(base_id)
659 g.write(')')
660 } else {
661 g.gen_expr(base_id)
662 }
663 }
664 // Yield the wrapper as the callback value. A V `fn (...) ...` parameter is a
665 // `_fn_ptr_*` typedef and needs the wrapper cast to that function-pointer type; only
666 // a C / `voidptr` callback slot gets the object-pointer `(void*)` cast (which strict
667 // C rejects for function pointers). `fn_type_from` is alias-aware, so a method value
668 // passed through a `type Cb = fn ()` parameter (an `Alias`, not a bare `FnType`) is
669 // recognised too and cast to the function-pointer typedef rather than `(void*)`.
670 if fnt := fn_type_from(g.expected_expr_type) {
671 fnptr_ct := g.value_c_type(fnt)
672 g.write('; (${fnptr_ct})${wrap_name}; })')
673 } else {
674 g.write('; (void*)${wrap_name}; })')
675 }
676 return true
677}
678
679fn (mut g FlatGen) callback_wrapper_decls() {
680 for def in g.callback_wrapper_defs {
681 g.writeln(def)
682 }
683 if g.callback_wrapper_defs.len > 0 {
684 g.writeln('')
685 }
686}
687
688fn (mut g FlatGen) gen_spawn_expr(node flat.Node) {
689 if node.children_count == 0 {
690 g.write('(void*)0')
691 return
692 }
693 call_id := g.a.child(&node, 0)
694 call_node := g.a.nodes[int(call_id)]
695 if call_node.kind != .call || call_node.children_count == 0 {
696 g.write('(void*)0')
697 return
698 }
699 fn_node := g.a.child_node(&call_node, 0)
700 // The spawned call's return type: heap-captured by the wrapper so a later
701 // `[]thread T .wait()` can recover the value (void callees just return NULL).
702 // Use the callee's ABI return type, not the bare value type: an option/result
703 // return is `Optional_T` (not the generic `Optional`, whose payload is `int`) and a
704 // fixed-array return is its `_v_ret_*` wrapper struct. The wrapper mallocs and
705 // assigns this type, and `[]thread T.wait()` must read back the same layout.
706 ret_ct := g.fn_return_type_name(g.tc.resolve_type(call_id))
707 mut wrapper := ''
708 mut arg_expr := 'NULL'
709 if fn_node.kind == .ident {
710 call_key := g.call_key(call_id, fn_node.value)
711 looked_up := g.tc.cur_scope.lookup(fn_node.value) or { types.Type(types.void_) }
712 cfn := if looked_up !is types.Void && fn_type_from(looked_up) != none {
713 c_name(fn_node.value)
714 } else if call_key in g.tc.fn_ret_types || call_key in g.tc.fn_param_types {
715 g.direct_call_name_for_call(call_id, call_key)
716 } else {
717 c_name(fn_node.value)
718 }
719 if call_node.children_count == 1 {
720 wrapper = g.ensure_noarg_spawn_wrapper(cfn, ret_ct)
721 } else {
722 // `spawn work(a, b)` packs the arguments into a heap struct so the
723 // spawned thread receives them, instead of silently dropping them.
724 param_types := g.param_types_for(call_key, fn_node.value)
725 if param_types.len > 0 && param_types.len == int(call_node.children_count) - 1 {
726 mut param_cts := []string{}
727 mut arg_exprs := []string{}
728 for i, pt in param_types {
729 param_cts << g.tc.c_type(pt)
730 arg_exprs << g.expr_to_string(g.a.child(&call_node, i + 1))
731 }
732 g.emit_args_spawn_expr(cfn, param_cts, arg_exprs, ret_ct)
733 return
734 }
735 }
736 } else if fn_node.kind == .selector && fn_node.children_count > 0 {
737 base_id := g.a.child(fn_node, 0)
738 base_type := g.receiver_base_type(base_id)
739 clean_type := types.unwrap_pointer(base_type)
740 method_name := g.resolved_method_name_for_spawn(clean_type, fn_node.value)
741 if method_name.len > 0 {
742 param_types := g.param_types_for(method_name, fn_node.value)
743 if param_types.len > 0 {
744 receiver_type := param_types[0]
745 receiver_ct := g.tc.c_type(receiver_type)
746 base_expr := g.expr_to_string(base_id)
747 if call_node.children_count == 1 {
748 if receiver_type is types.Pointer {
749 wrapper = g.ensure_receiver_spawn_wrapper(c_name(method_name), receiver_ct,
750 ret_ct)
751 if base_type is types.Pointer {
752 arg_expr = '(${receiver_ct})(${base_expr})'
753 } else {
754 arg_expr = '(${receiver_ct})(&(${base_expr}))'
755 }
756 } else {
757 // Casting the void* thread argument straight to a struct type
758 // is invalid C, so copy the value receiver into the heap arg
759 // struct and dispatch through the argument-packing path.
760 receiver_value := if base_type is types.Pointer {
761 '*(${base_expr})'
762 } else {
763 base_expr
764 }
765 g.emit_args_spawn_expr(c_name(method_name), [receiver_ct], [
766 receiver_value,
767 ], ret_ct)
768 return
769 }
770 } else if param_types.len == int(call_node.children_count) {
771 // `spawn recv.method(a, b)` packs the receiver and arguments
772 // into a heap struct rather than dropping the call.
773 receiver_expr := if receiver_type is types.Pointer {
774 if base_type is types.Pointer {
775 '(${receiver_ct})(${base_expr})'
776 } else {
777 '(${receiver_ct})(&(${base_expr}))'
778 }
779 } else if base_type is types.Pointer {
780 '*(${base_expr})'
781 } else {
782 base_expr
783 }
784 mut param_cts := [receiver_ct]
785 mut arg_exprs := [receiver_expr]
786 for i in 1 .. param_types.len {
787 param_cts << g.tc.c_type(param_types[i])
788 arg_exprs << g.expr_to_string(g.a.child(&call_node, i))
789 }
790 g.emit_args_spawn_expr(c_name(method_name), param_cts, arg_exprs, ret_ct)
791 return
792 }
793 }
794 }
795 }
796 if wrapper.len == 0 {
797 g.write('(void*)0')
798 return
799 }
800 tmp := g.tmp_count
801 g.tmp_count++
802 g.write('({ pthread_t _t${tmp}; pthread_attr_t _a${tmp}; pthread_attr_init(&_a${tmp}); ')
803 g.write('pthread_attr_setstacksize(&_a${tmp}, 8388608); ')
804 g.write('int _r${tmp} = pthread_create(&_t${tmp}, &_a${tmp}, ${wrapper}, (void*)(${arg_expr})); ')
805 g.write('pthread_attr_destroy(&_a${tmp}); (void)_r${tmp}; (void*)_t${tmp}; })')
806}
807
808// spawn_wrapper_body builds the thread-wrapper statement that invokes the spawned
809// call and returns its result as a `void*`. When the callee returns a value, the
810// result is heap-copied so `[]thread T .wait()` can recover it (the wait fn frees
811// it); a void callee returns NULL. `post` runs after the call (e.g. `free(p);`).
812fn spawn_wrapper_body(call_expr string, ret_ct string, post string) string {
813 if ret_ct == 'void' || ret_ct.len == 0 {
814 return '${call_expr}; ${post}return NULL;'
815 }
816 return '${ret_ct}* __tr = (${ret_ct}*)malloc(sizeof(${ret_ct})); *__tr = ${call_expr}; ${post}return (void*)__tr;'
817}
818
819fn (mut g FlatGen) ensure_noarg_spawn_wrapper(cfn string, ret_ct string) string {
820 key := 'noarg|${cfn}'
821 if name := g.spawn_wrapper_names[key] {
822 return name
823 }
824 name := c_name('${cfn}_thread_wrapper')
825 g.spawn_wrapper_names[key] = name
826 body := spawn_wrapper_body('${cfn}()', ret_ct, '')
827 g.spawn_wrapper_defs << 'static void* ${name}(void* arg) { (void)arg; ${body} }'
828 return name
829}
830
831fn (mut g FlatGen) ensure_receiver_spawn_wrapper(cfn string, receiver_ct string, ret_ct string) string {
832 key := 'receiver|${cfn}|${receiver_ct}'
833 if name := g.spawn_wrapper_names[key] {
834 return name
835 }
836 name := c_name('${cfn}_thread_wrapper')
837 g.spawn_wrapper_names[key] = name
838 body := spawn_wrapper_body('${cfn}((${receiver_ct})arg)', ret_ct, '')
839 g.spawn_wrapper_defs << 'static void* ${name}(void* arg) { ${body} }'
840 return name
841}
842
843// ensure_args_spawn_wrapper registers (once per callee) a heap-arg struct plus a
844// thread wrapper that unpacks the struct, calls the function with all arguments,
845// and frees the struct. Returns the wrapper name and the arg struct name.
846fn (mut g FlatGen) ensure_args_spawn_wrapper(cfn string, param_cts []string, ret_ct string) (string, string) {
847 struct_name := c_name('${cfn}_thread_args')
848 key := 'args|${cfn}'
849 if name := g.spawn_wrapper_names[key] {
850 return name, struct_name
851 }
852 name := c_name('${cfn}_args_thread_wrapper')
853 g.spawn_wrapper_names[key] = name
854 mut fields := ''
855 mut call_args := []string{}
856 for i, ct in param_cts {
857 fields += '${ct} a${i}; '
858 call_args << 'p->a${i}'
859 }
860 g.spawn_wrapper_defs << 'typedef struct { ${fields}} ${struct_name};'
861 body := spawn_wrapper_body('${cfn}(${call_args.join(', ')})', ret_ct, 'free(p); ')
862 g.spawn_wrapper_defs << 'static void* ${name}(void* arg) { ${struct_name}* p = (${struct_name}*)arg; ${body} }'
863 return name, struct_name
864}
865
866// emit_args_spawn_expr writes a statement-expression that heap-allocates the arg
867// struct, populates it, and starts the thread on the packing wrapper.
868fn (mut g FlatGen) emit_args_spawn_expr(cfn string, param_cts []string, arg_exprs []string, ret_ct string) {
869 wrapper, struct_name := g.ensure_args_spawn_wrapper(cfn, param_cts, ret_ct)
870 tmp := g.tmp_count
871 g.tmp_count++
872 g.write('({ ${struct_name}* _sa${tmp} = (${struct_name}*)malloc(sizeof(${struct_name})); ')
873 for i, e in arg_exprs {
874 g.write('_sa${tmp}->a${i} = ${e}; ')
875 }
876 g.write('pthread_t _t${tmp}; pthread_attr_t _at${tmp}; pthread_attr_init(&_at${tmp}); ')
877 g.write('pthread_attr_setstacksize(&_at${tmp}, 8388608); ')
878 g.write('int _r${tmp} = pthread_create(&_t${tmp}, &_at${tmp}, ${wrapper}, (void*)_sa${tmp}); ')
879 g.write('pthread_attr_destroy(&_at${tmp}); (void)_r${tmp}; (void*)_t${tmp}; })')
880}
881
882fn (g &FlatGen) resolved_method_name_for_spawn(clean_type types.Type, method string) string {
883 mut type_name := clean_type.name()
884 if clean_type is types.Struct {
885 type_name = clean_type.name
886 }
887 method_name := '${type_name}.${method}'
888 if method_name in g.tc.fn_param_types {
889 return method_name
890 }
891 for alias, target in g.tc.type_aliases {
892 if target == type_name {
893 alias_method := '${alias}.${method}'
894 if alias_method in g.tc.fn_param_types {
895 return alias_method
896 }
897 }
898 }
899 return ''
900}
901
902// gen_fn_in_module emits fn in module output for c.
903fn (mut g FlatGen) gen_fn_in_module(node flat.Node, module_name string) {
904 g.tc.cur_module = module_name
905 g.cur_fn_name = node.value
906 g.tc.push_scope()
907 g.defers = []flat.NodeId{}
908 g.fn_defers = []flat.NodeId{}
909 g.fn_defer_counts = map[int]string{}
910 g.defer_capture_names = []string{}
911 g.defer_capture_types = map[string]types.Type{}
912 g.set_cur_fn_ret(types.Type(types.void_))
913 old_param_names := g.cur_param_names.clone()
914 old_param_type_values := g.cur_param_type_values.clone()
915 old_param_types := g.cur_param_types.clone()
916 g.cur_param_names = []string{}
917 g.cur_param_type_values = []types.Type{}
918 g.cur_param_types = map[string]types.Type{}
919 for i in 0 .. node.children_count {
920 param_id := g.a.child(&node, i)
921 p := g.a.node(param_id)
922 if node_kind_id(p) == 75 && p.value.len > 0 {
923 param_type := g.tc.parse_type(p.typ)
924 g.cur_param_names << p.value
925 g.cur_param_type_values << param_type
926 g.cur_param_types[p.value] = param_type
927 g.tc.cur_scope.insert(p.value, param_type)
928 }
929 }
930 g.insert_cur_implicit_veb_ctx_param(node)
931 fn_defer_ids := g.collect_function_defer_ids(node)
932 g.prepare_function_defers(fn_defer_ids)
933 is_entry_main := is_main_fn_in_main_module(module_name, node.value) && g.test_files.len == 0
934 if is_entry_main {
935 g.writeln('int main(int argc, char** argv) {')
936 if g.has_builtins {
937 g.writeln('\tg_main_argc = argc;')
938 g.writeln('\tg_main_argv = argv;')
939 }
940 g.gen_compiler_vexe_env_setup()
941 if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
942 g.writeln('\t_vinit();')
943 }
944 } else {
945 ret_type := g.fn_node_return_type(node, module_name)
946 g.set_cur_fn_ret(ret_type)
947 g.write(g.fn_return_type_name(ret_type))
948 g.write(' ')
949 g.write(g.fn_c_name_in_module(module_name, node.value))
950 g.write('(')
951 g.write_fn_node_params(node)
952 g.writeln(') {')
953 }
954 g.indent++
955 g.gen_function_defer_prelude()
956
957 for i in 0 .. node.children_count {
958 id := g.a.child(&node, i)
959 child := g.a.node(id)
960 if child.kind != .param {
961 g.tc.cur_module = module_name
962 g.gen_node(id)
963 }
964 }
965 g.gen_all_defers()
966 if is_entry_main {
967 g.writeln('return 0;')
968 } else if g.cur_fn_ret_is_optional {
969 ct := g.optional_type_name(g.cur_fn_ret)
970 g.writeln('return (${ct}){.ok = true};')
971 }
972 g.indent--
973 g.writeln('}')
974 g.writeln('')
975 if !is_entry_main {
976 g.gen_export_wrapper_for_fn(node, module_name)
977 }
978 g.cur_param_names = old_param_names.clone()
979 g.cur_param_type_values = old_param_type_values.clone()
980 g.cur_param_types = old_param_types.clone()
981 g.tc.pop_scope()
982}
983
984fn (mut g FlatGen) gen_export_wrapper_for_fn(node flat.Node, module_name string) {
985 export_name := g.export_fn_name_in_module(module_name, node.value) or { return }
986 canonical_name := g.fn_c_name_in_module(module_name, node.value)
987 ret_type := g.fn_node_return_type(node, module_name)
988 ret_ct := g.fn_return_type_name(ret_type)
989 g.write(ret_ct)
990 g.write(' ')
991 g.write(export_name)
992 g.write('(')
993 g.write_fn_node_params(node)
994 g.writeln(') {')
995 g.indent++
996 args := g.export_wrapper_arg_names(node)
997 call := '${canonical_name}(${args.join(', ')})'
998 if ret_type is types.Void {
999 g.writeln('${call};')
1000 } else {
1001 g.writeln('return ${call};')
1002 }
1003 g.indent--
1004 g.writeln('}')
1005 g.writeln('')
1006}
1007
1008fn (mut g FlatGen) export_wrapper_arg_names(node flat.Node) []string {
1009 mut args := []string{}
1010 needs_implicit_ctx := g.fn_needs_implicit_veb_ctx(node)
1011 insert_implicit_ctx_after_first := needs_implicit_ctx && g.fn_has_receiver_param(node)
1012 mut written := 0
1013 mut implicit_ctx_written := false
1014 for i in 0 .. node.children_count {
1015 param_id := g.a.child(&node, i)
1016 p := g.a.node(param_id)
1017 if p.kind != .param {
1018 continue
1019 }
1020 param_name := if p.value == '_' { '_${written}' } else { c_name(p.value) }
1021 args << param_name
1022 written++
1023 if insert_implicit_ctx_after_first && !implicit_ctx_written {
1024 args << 'ctx'
1025 written++
1026 implicit_ctx_written = true
1027 }
1028 }
1029 if needs_implicit_ctx && !implicit_ctx_written {
1030 args << 'ctx'
1031 }
1032 return args
1033}
1034
1035fn (mut g FlatGen) gen_top_level_main(stmts []TopLevelStmt) {
1036 old_tc_file := g.tc.cur_file
1037 old_tc_module := g.tc.cur_module
1038 g.tc.cur_module = 'main'
1039 old_fn_name := g.cur_fn_name
1040 g.cur_fn_name = 'main'
1041 g.tc.push_scope()
1042 g.defers = []flat.NodeId{}
1043 g.fn_defers = []flat.NodeId{}
1044 g.fn_defer_counts = map[int]string{}
1045 g.defer_capture_names = []string{}
1046 g.defer_capture_types = map[string]types.Type{}
1047 g.set_cur_fn_ret(types.Type(types.void_))
1048 old_param_names := g.cur_param_names.clone()
1049 old_param_type_values := g.cur_param_type_values.clone()
1050 old_param_types := g.cur_param_types.clone()
1051 g.cur_param_names = []string{}
1052 g.cur_param_type_values = []types.Type{}
1053 g.cur_param_types = map[string]types.Type{}
1054 mut fn_defer_ids := []flat.NodeId{}
1055 for stmt in stmts {
1056 g.collect_function_defer_ids_from(stmt.id, mut fn_defer_ids)
1057 }
1058 g.prepare_function_defers(fn_defer_ids)
1059 g.writeln('int main(int argc, char** argv) {')
1060 if g.has_builtins {
1061 g.writeln('\tg_main_argc = argc;')
1062 g.writeln('\tg_main_argv = argv;')
1063 }
1064 g.gen_compiler_vexe_env_setup()
1065 if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
1066 g.writeln('\t_vinit();')
1067 }
1068 g.indent++
1069 g.gen_function_defer_prelude()
1070 for stmt in stmts {
1071 g.tc.cur_file = stmt.file
1072 g.tc.cur_module = stmt.module
1073 g.gen_top_level_main_stmt(stmt.id)
1074 }
1075 g.gen_all_defers()
1076 g.writeln('return 0;')
1077 g.indent--
1078 g.writeln('}')
1079 g.writeln('')
1080 g.cur_param_names = old_param_names.clone()
1081 g.cur_param_type_values = old_param_type_values.clone()
1082 g.cur_param_types = old_param_types.clone()
1083 g.cur_fn_name = old_fn_name
1084 g.tc.cur_file = old_tc_file
1085 g.tc.cur_module = old_tc_module
1086 g.tc.pop_scope()
1087}
1088
1089fn (mut g FlatGen) gen_top_level_main_stmt(id flat.NodeId) {
1090 if int(id) < 0 || int(id) >= g.a.nodes.len {
1091 return
1092 }
1093 node := g.a.nodes[int(id)]
1094 if node.kind == .block {
1095 for i in 0 .. node.children_count {
1096 child_id := g.a.child(&node, i)
1097 if g.cgen_is_top_level_stmt(child_id) {
1098 g.gen_top_level_main_stmt(child_id)
1099 }
1100 }
1101 return
1102 }
1103 g.gen_node(id)
1104}
1105
1106fn (mut g FlatGen) gen_test_main() {
1107 tests, hooks := g.test_harness_fns()
1108 g.tc.cur_module = 'main'
1109 g.writeln('int main(int argc, char** argv) {')
1110 if g.has_builtins {
1111 g.writeln('\tg_main_argc = argc;')
1112 g.writeln('\tg_main_argv = argv;')
1113 }
1114 g.gen_compiler_vexe_env_setup()
1115 if g.runtime_inits.len > 0 || g.module_init_fns.len > 0 || g.global_inits.len > 0 {
1116 g.writeln('\t_vinit();')
1117 }
1118 g.indent++
1119 if hooks.testsuite_begin.len > 0 {
1120 g.writeln('${hooks.testsuite_begin}();')
1121 }
1122 for idx, test_fn in tests {
1123 if hooks.before_each.len > 0 {
1124 g.writeln('${hooks.before_each}();')
1125 }
1126 g.gen_test_fn_call(test_fn, hooks, idx)
1127 if hooks.after_each.len > 0 {
1128 g.writeln('${hooks.after_each}();')
1129 }
1130 }
1131 if hooks.testsuite_end.len > 0 {
1132 g.writeln('${hooks.testsuite_end}();')
1133 }
1134 g.writeln('return 0;')
1135 g.indent--
1136 g.writeln('}')
1137 g.writeln('')
1138}
1139
1140fn (mut g FlatGen) gen_test_fn_call(test_fn TestHarnessFn, hooks TestHarnessHooks, idx int) {
1141 if test_fn.ret is types.OptionType || test_fn.ret is types.ResultType {
1142 ct := g.optional_type_name(test_fn.ret)
1143 tmp_name := '__test_opt_${idx}'
1144 g.writeln('${ct} ${tmp_name} = ${test_fn.c_name}();')
1145 g.writeln('if (!${tmp_name}.ok) {')
1146 g.indent++
1147 g.writeln('fprintf(stderr, "test failed: ${c_escape(test_fn.name)}\\n");')
1148 if hooks.after_each.len > 0 {
1149 g.writeln('${hooks.after_each}();')
1150 }
1151 if hooks.testsuite_end.len > 0 {
1152 g.writeln('${hooks.testsuite_end}();')
1153 }
1154 g.writeln('return 1;')
1155 g.indent--
1156 g.writeln('}')
1157 return
1158 }
1159 g.writeln('${test_fn.c_name}();')
1160}
1161
1162fn (g &FlatGen) test_harness_fns() ([]TestHarnessFn, TestHarnessHooks) {
1163 mut tests := []TestHarnessFn{}
1164 mut hooks := TestHarnessHooks{}
1165 for file_idx, file_node in g.a.nodes {
1166 if !g.is_user_test_file_node(file_idx, file_node) {
1167 continue
1168 }
1169 module_name := g.test_file_module_name(file_node)
1170 if module_name.len > 0 && module_name != 'main' {
1171 continue
1172 }
1173 mut decl_ids := []flat.NodeId{}
1174 g.collect_test_harness_decl_ids(file_node, mut decl_ids)
1175 for child_id in decl_ids {
1176 child := g.a.nodes[int(child_id)]
1177 cname := qualified_fn_name_in_module(module_name, child.value)
1178 match child.value {
1179 'testsuite_begin' {
1180 if hooks.testsuite_begin.len == 0 && g.is_supported_test_hook_decl(child) {
1181 hooks.testsuite_begin = cname
1182 }
1183 }
1184 'testsuite_end' {
1185 if hooks.testsuite_end.len == 0 && g.is_supported_test_hook_decl(child) {
1186 hooks.testsuite_end = cname
1187 }
1188 }
1189 'before_each' {
1190 if hooks.before_each.len == 0 && g.is_supported_test_hook_decl(child) {
1191 hooks.before_each = cname
1192 }
1193 }
1194 'after_each' {
1195 if hooks.after_each.len == 0 && g.is_supported_test_hook_decl(child) {
1196 hooks.after_each = cname
1197 }
1198 }
1199 else {
1200 if child.value.starts_with('test_') && g.is_supported_test_fn_decl(child) {
1201 tests << TestHarnessFn{
1202 name: child.value
1203 c_name: cname
1204 ret: g.tc.parse_type(child.typ)
1205 }
1206 }
1207 }
1208 }
1209 }
1210 }
1211 return tests, hooks
1212}
1213
1214fn (g &FlatGen) collect_test_harness_decl_ids(node flat.Node, mut ids []flat.NodeId) {
1215 if node.kind != .file && node.kind != .block {
1216 return
1217 }
1218 for i in 0 .. node.children_count {
1219 child_id := g.a.child(&node, i)
1220 if int(child_id) < g.a.user_code_start {
1221 continue
1222 }
1223 child := g.a.nodes[int(child_id)]
1224 if child.kind == .fn_decl {
1225 ids << child_id
1226 } else if child.kind == .block {
1227 g.collect_test_harness_decl_ids(child, mut ids)
1228 }
1229 }
1230}
1231
1232fn (g &FlatGen) is_supported_test_fn_decl(node flat.Node) bool {
1233 if node.generic_params.len > 0 {
1234 return false
1235 }
1236 if g.test_fn_param_count(node) != 0 {
1237 return false
1238 }
1239 return test_harness_fn_return_supported(g.tc.parse_type(node.typ))
1240}
1241
1242fn (g &FlatGen) is_supported_test_hook_decl(node flat.Node) bool {
1243 if node.generic_params.len > 0 {
1244 return false
1245 }
1246 return g.test_fn_param_count(node) == 0 && g.tc.parse_type(node.typ) is types.Void
1247}
1248
1249fn (g &FlatGen) test_fn_param_count(node flat.Node) int {
1250 mut count := 0
1251 for i in 0 .. node.children_count {
1252 child := g.a.child_node(&node, i)
1253 if child.kind == .param {
1254 count++
1255 }
1256 }
1257 return count
1258}
1259
1260fn test_harness_fn_return_supported(ret types.Type) bool {
1261 return ret is types.Void || ret is types.OptionType || ret is types.ResultType
1262}
1263
1264fn (g &FlatGen) is_user_test_file_node(file_idx int, file_node flat.Node) bool {
1265 if file_idx < g.a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
1266 return false
1267 }
1268 return g.test_files[file_node.value]
1269}
1270
1271fn (g &FlatGen) test_file_module_name(file_node flat.Node) string {
1272 for i in 0 .. file_node.children_count {
1273 child := g.a.child_node(&file_node, i)
1274 if child.kind == .module_decl {
1275 return child.value
1276 }
1277 }
1278 return ''
1279}
1280
1281// collect_function_defer_ids updates collect function defer ids state for c.
1282fn (mut g FlatGen) collect_function_defer_ids(node flat.Node) []flat.NodeId {
1283 mut ids := []flat.NodeId{}
1284 for i in 0 .. node.children_count {
1285 g.collect_function_defer_ids_from(g.a.child(&node, i), mut ids)
1286 }
1287 return ids
1288}
1289
1290// collect_function_defer_ids_from updates collect function defer ids from state for c.
1291fn (mut g FlatGen) collect_function_defer_ids_from(id flat.NodeId, mut ids []flat.NodeId) {
1292 if !g.valid_node_id(id) {
1293 return
1294 }
1295 node := g.a.nodes[int(id)]
1296 if node.kind == .fn_decl || node.kind == .c_fn_decl || node.kind == .fn_literal {
1297 return
1298 }
1299 if node.kind == .defer_stmt && node.value == 'function' {
1300 ids << id
1301 return
1302 }
1303 for i in 0 .. node.children_count {
1304 g.collect_function_defer_ids_from(g.a.child(&node, i), mut ids)
1305 }
1306}
1307
1308// prepare_function_defers supports prepare function defers handling for FlatGen.
1309fn (mut g FlatGen) prepare_function_defers(fn_defer_ids []flat.NodeId) {
1310 for idx, defer_id in fn_defer_ids {
1311 g.fn_defer_counts[int(defer_id)] = '${c_name(g.cur_fn_name)}_defer_${idx}_count'
1312 defer_node := g.a.nodes[int(defer_id)]
1313 if defer_node.children_count > 0 {
1314 g.collect_function_defer_captures(g.a.child(&defer_node, 0))
1315 }
1316 }
1317}
1318
1319// collect_function_defer_captures updates collect function defer captures state for c.
1320fn (mut g FlatGen) collect_function_defer_captures(id flat.NodeId) {
1321 if !g.valid_node_id(id) {
1322 return
1323 }
1324 node := g.a.nodes[int(id)]
1325 if node.kind == .fn_decl || node.kind == .c_fn_decl || node.kind == .fn_literal {
1326 return
1327 }
1328 if node.kind == .ident {
1329 g.add_function_defer_capture(id, node.value)
1330 }
1331 for i in 0 .. node.children_count {
1332 g.collect_function_defer_captures(g.a.child(&node, i))
1333 }
1334}
1335
1336// add_function_defer_capture updates add function defer capture state for FlatGen.
1337fn (mut g FlatGen) add_function_defer_capture(id flat.NodeId, name string) {
1338 if name.len == 0 || name == '_' || name in g.cur_param_names || g.has_import_alias(name)
1339 || name in g.global_modules || name in g.defer_capture_types {
1340 return
1341 }
1342 typ := g.usable_expr_type(id)
1343 if typ is types.Void || typ is types.Unknown || typ is types.FnType {
1344 return
1345 }
1346 ct := g.tc.c_type(typ)
1347 if ct.len == 0 || ct == 'void' || ct.starts_with('fn_ptr:') {
1348 return
1349 }
1350 g.defer_capture_names << name
1351 g.defer_capture_types[name] = typ
1352}
1353
1354// gen_function_defer_prelude emits function defer prelude output for c.
1355fn (mut g FlatGen) gen_function_defer_prelude() {
1356 for _, count_name in g.fn_defer_counts {
1357 g.writeln('int ${count_name} = 0;')
1358 }
1359 for name in g.defer_capture_names {
1360 typ := g.defer_capture_types[name] or { continue }
1361 ct := g.tc.c_type(typ)
1362 g.write('${ct} ${c_name(name)} = ')
1363 g.gen_default_value_for_type(typ)
1364 g.writeln(';')
1365 g.tc.cur_scope.insert(name, typ)
1366 }
1367}
1368
1369// set_cur_fn_ret updates set cur fn ret state for c.
1370fn (mut g FlatGen) set_cur_fn_ret(ret_type types.Type) {
1371 g.cur_fn_ret = ret_type
1372 g.cur_fn_ret_is_optional = false
1373 g.cur_fn_ret_base = types.Type(types.void_)
1374 if ret_type is types.OptionType {
1375 g.cur_fn_ret_is_optional = true
1376 g.cur_fn_ret_base = ret_type.base_type
1377 } else if ret_type is types.ResultType {
1378 g.cur_fn_ret_is_optional = true
1379 g.cur_fn_ret_base = ret_type.base_type
1380 }
1381}
1382
1383// gen_compiler_vexe_env_setup emits compiler vexe env setup output for c.
1384fn (mut g FlatGen) gen_compiler_vexe_env_setup() {
1385 if g.compiler_vroot.len == 0 {
1386 return
1387 }
1388 root := c_escape(g.compiler_vroot)
1389 g.writeln('\tif (getenv("VEXE") == NULL || getenv("VEXE")[0] == 0) {')
1390 g.writeln('\t\tconst char* v3_arg0 = argc > 0 ? argv[0] : "v";')
1391 g.writeln("\t\tconst char* v3_base = strrchr(v3_arg0, '/');")
1392 g.writeln('\t\tv3_base = v3_base == NULL ? v3_arg0 : v3_base + 1;')
1393 g.writeln('\t\tif (v3_base[0] == 0) v3_base = "v";')
1394 g.writeln('\t\tconst char* v3_checkout_root = "${root}";')
1395 g.writeln('\t\tchar v3_checkout_vexe[4096];')
1396 g.writeln('\t\tsnprintf(v3_checkout_vexe, sizeof(v3_checkout_vexe), "%s/%s", v3_checkout_root, v3_base);')
1397 g.writeln('\t\tif (access(v3_checkout_vexe, F_OK) != 0) snprintf(v3_checkout_vexe, sizeof(v3_checkout_vexe), "%s/v", v3_checkout_root);')
1398 g.writeln('\t\tchar v3_src_real[4096];')
1399 g.writeln('\t\tchar* v3_src_real_result = realpath(v3_arg0, v3_src_real);')
1400 g.writeln('\t\tconst char* v3_vexe = v3_src_real_result != NULL ? v3_src_real : v3_arg0;')
1401 g.writeln('\t\tif (access(v3_checkout_vexe, F_OK) == 0) v3_vexe = v3_checkout_vexe;')
1402 g.writeln('\t\tif (v3_vexe[0] != 0) {')
1403 g.writeln('#ifdef _WIN32')
1404 g.writeln('\t\t\t_putenv_s("VEXE", v3_vexe);')
1405 g.writeln('#else')
1406 g.writeln('\t\t\tsetenv("VEXE", v3_vexe, 1);')
1407 g.writeln('#endif')
1408 g.writeln('\t\t}')
1409 g.writeln('\t}')
1410}
1411
1412// gen_defers emits defers output for c.
1413fn (mut g FlatGen) gen_defers() {
1414 g.gen_defers_from(0)
1415}
1416
1417// gen_all_defers emits all defers output for c.
1418fn (mut g FlatGen) gen_all_defers() {
1419 g.gen_defers()
1420 g.gen_fn_defers()
1421}
1422
1423// gen_defers_from emits defers from output for c.
1424fn (mut g FlatGen) gen_defers_from(start int) {
1425 if g.defers.len == 0 {
1426 return
1427 }
1428 mut i := g.defers.len
1429 for i > start {
1430 i--
1431 defer_body := g.a.nodes[int(g.defers[i])]
1432 g.writeln('{')
1433 g.indent++
1434 for j in 0 .. defer_body.children_count {
1435 g.gen_node(g.a.child(&defer_body, j))
1436 }
1437 g.indent--
1438 g.writeln('}')
1439 }
1440}
1441
1442// gen_fn_defers emits fn defers output for c.
1443fn (mut g FlatGen) gen_fn_defers() {
1444 if g.fn_defers.len == 0 {
1445 return
1446 }
1447 mut i := g.fn_defers.len
1448 for i > 0 {
1449 i--
1450 defer_id := g.fn_defers[i]
1451 defer_node := g.a.nodes[int(defer_id)]
1452 defer_body := g.a.nodes[int(g.a.child(&defer_node, 0))]
1453 count_name := g.fn_defer_counts[int(defer_id)] or { '0' }
1454 iter_name := '${count_name}_i'
1455 g.writeln('for (int ${iter_name} = 0; ${iter_name} < ${count_name}; ${iter_name}++) {')
1456 g.indent++
1457 for j in 0 .. defer_body.children_count {
1458 g.gen_node(g.a.child(&defer_body, j))
1459 }
1460 g.indent--
1461 g.writeln('}')
1462 }
1463}
1464
1465// trim_defers transforms trim defers data for c.
1466fn (mut g FlatGen) trim_defers(start int) {
1467 if start >= g.defers.len {
1468 return
1469 }
1470 g.defers = g.defers[..start].clone()
1471}
1472
1473// gen_ierror_from_error_call converts gen ierror from error call data for c.
1474fn (mut g FlatGen) gen_ierror_from_error_call(node flat.Node) {
1475 fn_node := g.a.child_node(&node, 0)
1476 g.write('(IError){._typ = 0, ._object = NULL, .message = ')
1477 if node.children_count > 1 {
1478 g.gen_expr(g.a.child(&node, 1))
1479 } else {
1480 g.write('_S("")')
1481 }
1482 g.write(', .code = ')
1483 if fn_node.value == 'error_with_code' && node.children_count > 2 {
1484 g.gen_expr(g.a.child(&node, 2))
1485 } else {
1486 g.write('0')
1487 }
1488 g.write('}')
1489}
1490
1491// gen_optional_error_from_call converts gen optional error from call data for c.
1492fn (mut g FlatGen) gen_optional_error_from_call(ct string, node flat.Node) {
1493 g.write('(${ct}){.ok = false, .err = ')
1494 g.gen_ierror_from_error_call(node)
1495 g.write('}')
1496}
1497
1498// gen_call emits call output for c.
1499fn (mut g FlatGen) gen_call(id flat.NodeId, node flat.Node) {
1500 fn_node := g.a.child_node(&node, 0)
1501 fn_name := fn_node.value
1502 target_name := g.call_target_name(g.a.child(&node, 0))
1503 if target_name in ['json.decode', 'json2.decode'] {
1504 ret_type := g.json_decode_result_type(g.a.child(&node, 0)) or {
1505 g.call_default_return_type(id)
1506 }
1507 g.gen_default_value_for_type(ret_type)
1508 return
1509 }
1510 if target_name in ['json.encode', 'json2.encode']
1511 || (g.tc.cur_module == 'json2' && target_name == 'encode') {
1512 g.gen_default_value_for_type(g.call_default_return_type(id))
1513 return
1514 }
1515 if g.is_veb_json_result_call(fn_node) {
1516 g.gen_default_value_for_type(g.call_default_return_type(id))
1517 return
1518 }
1519 if target_name == 'veb.run_at' {
1520 g.gen_default_value_for_type(g.call_default_return_type(id))
1521 return
1522 }
1523 if g.is_missing_middleware_use_call(fn_node) {
1524 g.write('0')
1525 return
1526 }
1527 if fn_node.kind == .selector && fn_node.value == 'str' {
1528 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1529 clean_type := types.unwrap_pointer(base_type)
1530 if clean_type is types.Enum {
1531 if _ := g.enum_receiver_method_name(clean_type, fn_node.value) {
1532 // Let normal method call generation handle custom enum str methods.
1533 } else {
1534 g.gen_enum_str_call(fn_node, clean_type)
1535 return
1536 }
1537 }
1538 }
1539 if fn_node.kind == .selector && fn_node.value == 'close' {
1540 base_id := g.a.child(fn_node, 0)
1541 base_type := g.tc.resolve_type(base_id)
1542 if base_type is types.Channel {
1543 g.write('sync__Channel__close(')
1544 g.gen_expr(base_id)
1545 g.write(', array_new(sizeof(IError), 0, 0))')
1546 return
1547 }
1548 }
1549 match fn_name {
1550 'new_map' {
1551 if node.typ.starts_with('map[') {
1552 map_type := g.tc.parse_type(node.typ)
1553 if map_type is types.Map {
1554 g.write_new_map(map_type.key_type, map_type.value_type)
1555 return
1556 }
1557 }
1558 g.write('new_map(')
1559 g.gen_call_args(fn_name, node, 1)
1560 g.write(')')
1561 return
1562 }
1563 'panic' {
1564 g.write('panic(')
1565 if node.children_count > 1 {
1566 arg_id := g.a.child(&node, 1)
1567 arg_type := g.tc.resolve_type(arg_id)
1568 if arg_type is types.Struct && arg_type.name == 'IError' {
1569 g.gen_expr(arg_id)
1570 g.write('.message')
1571 } else {
1572 g.gen_expr(arg_id)
1573 }
1574 }
1575 g.write(')')
1576 return
1577 }
1578 'error' {
1579 if g.cur_fn_ret_is_optional {
1580 ct := g.optional_type_name(g.cur_fn_ret)
1581 g.gen_optional_error_from_call(ct, node)
1582 } else {
1583 g.gen_ierror_from_error_call(node)
1584 }
1585 return
1586 }
1587 'error_with_code' {
1588 if g.cur_fn_ret_is_optional {
1589 ct := g.optional_type_name(g.cur_fn_ret)
1590 g.gen_optional_error_from_call(ct, node)
1591 } else {
1592 g.gen_ierror_from_error_call(node)
1593 }
1594 return
1595 }
1596 else {
1597 mut is_method := false
1598 mut is_c_call := false
1599 mut method_name := ''
1600 mut base_id := flat.NodeId(0)
1601 if fn_node.kind == .selector {
1602 base := g.a.child_node(fn_node, 0)
1603 base_is_local := if base.kind == .ident {
1604 (g.tc.cur_scope.lookup(base.value) or { types.Type(types.void_) }) !is types.Void
1605 } else {
1606 false
1607 }
1608 if base.kind == .ident && base.value == 'C' {
1609 g.write(g.direct_call_name('C.${fn_node.value}'))
1610 is_c_call = true
1611 } else if g.is_flag_enum_method(fn_node) {
1612 g.gen_flag_enum_call(node)
1613 return
1614 } else if base.kind == .ident && !base_is_local && g.has_import_alias(base.value) {
1615 mod := g.import_alias_module(base.value) or { '' }
1616 full_name := '${mod}.${fn_node.value}'
1617 if full_name in g.tc.type_aliases || full_name in g.tc.structs
1618 || full_name in g.tc.enum_names || full_name in g.tc.sum_types {
1619 target_type := g.tc.parse_type(full_name)
1620 ct := g.tc.c_type(target_type)
1621 if target_type is types.SumType && node.children_count > 1 {
1622 g.gen_sum_cast_expr(target_type, g.a.child(&node, 1))
1623 } else {
1624 g.write('(${ct})(')
1625 for i in 1 .. node.children_count {
1626 if i > 1 {
1627 g.write(', ')
1628 }
1629 g.gen_expr(g.a.child(&node, i))
1630 }
1631 g.write(')')
1632 }
1633 return
1634 }
1635 g.write(c_name(full_name))
1636 g.write('(')
1637 g.gen_call_args(full_name, node, 1)
1638 g.write(')')
1639 return
1640 } else if base.kind == .ident && !base_is_local
1641 && g.static_method_fn_name(base.value, fn_node.value) != none {
1642 // `Type.method(...)` where the base ident names a (possibly
1643 // imported) type, not a value — e.g. `Animation.load(path)` inside
1644 // the type's own module. Resolve to the module-qualified static fn.
1645 static_fn := g.static_method_fn_name(base.value, fn_node.value) or { '' }
1646 g.write(g.direct_call_name(static_fn))
1647 g.write('(')
1648 // Lower the arguments through the ordinary call path so a static method
1649 // parameter that needs coercion — option/result wrapping, fn-pointer alias
1650 // callbacks, sum variants, fixed-array decay, variadic/`@[params]` handling —
1651 // is emitted against its expected type, not as the raw expression.
1652 g.gen_call_args(static_fn, node, 1)
1653 g.write(')')
1654 return
1655 } else if base.kind == .selector {
1656 inner := g.a.child_node(base, 0)
1657 inner_is_local := if inner.kind == .ident {
1658 (g.tc.cur_scope.lookup(inner.value) or { types.Type(types.void_) }) !is types.Void
1659 } else {
1660 false
1661 }
1662 if inner.kind == .ident && !inner_is_local {
1663 if mod := g.import_alias_module(inner.value) {
1664 full_name := '${mod}.${base.value}.${fn_node.value}'
1665 g.write(c_name(full_name))
1666 g.write('(')
1667 g.gen_call_args(full_name, node, 1)
1668 g.write(')')
1669 return
1670 }
1671 }
1672 {
1673 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1674 clean_type := types.unwrap_pointer(base_type)
1675 if g.gen_interface_method_call(node, fn_node, base_type) {
1676 return
1677 }
1678 if g.gen_fn_field_call(node, fn_node, base_type) {
1679 return
1680 }
1681 if arr := array_like_type(clean_type) {
1682 g.gen_array_method_call(node, fn_node, arr)
1683 return
1684 }
1685 if clean_type is types.ArrayFixed && fn_node.value == 'bytestr' {
1686 g.write('u8__vstring_with_len((u8*)')
1687 g.gen_expr(g.a.child(fn_node, 0))
1688 len_expr := g.fixed_array_len_value(clean_type)
1689 g.write(', ${len_expr})')
1690 return
1691 }
1692 if clean_type is types.Map {
1693 if fn_node.value == 'delete' {
1694 g.gen_map_delete(node, fn_node, clean_type, base_type)
1695 return
1696 } else if fn_node.value == 'clone' {
1697 g.write('map__clone(')
1698 g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1699 g.write(')')
1700 return
1701 } else if fn_node.value == 'clear' {
1702 g.write('map__clear(')
1703 g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1704 g.write(')')
1705 return
1706 } else if fn_node.value == 'free' {
1707 g.write('map__free(')
1708 if base_type is types.Pointer {
1709 g.gen_expr(g.a.child(fn_node, 0))
1710 } else {
1711 g.write('&')
1712 g.gen_expr(g.a.child(fn_node, 0))
1713 }
1714 g.write(')')
1715 return
1716 }
1717 }
1718 if clean_type is types.String {
1719 method_name = 'string.${fn_node.value}'
1720 if method_name in g.tc.fn_param_types {
1721 is_method = true
1722 base_id = g.a.child(fn_node, 0)
1723 g.write_method_c_name(id, node, method_name)
1724 } else {
1725 g.write('string__${fn_node.value}(')
1726 g.gen_expr(g.a.child(fn_node, 0))
1727 for i in 1 .. node.children_count {
1728 g.write(', ')
1729 g.gen_expr(g.a.child(&node, i))
1730 }
1731 g.write(')')
1732 return
1733 }
1734 }
1735 if !is_method && (clean_type is types.Primitive
1736 || clean_type is types.ISize || clean_type is types.USize
1737 || clean_type is types.Rune) {
1738 tname := clean_type.name()
1739 prim_method := '${tname}.${fn_node.value}'
1740 if prim_method in g.tc.fn_param_types {
1741 is_method = true
1742 base_id = g.a.child(fn_node, 0)
1743 g.write(c_name(prim_method))
1744 } else {
1745 mut prim_found := false
1746 if alias_method := g.find_alias_method(tname, fn_node.value) {
1747 is_method = true
1748 prim_found = true
1749 base_id = g.a.child(fn_node, 0)
1750 g.write(c_name(alias_method))
1751 }
1752 if !prim_found {
1753 alt_name := g.find_prim_method(fn_node.value)
1754 if alt_name.len > 0 {
1755 is_method = true
1756 base_id = g.a.child(fn_node, 0)
1757 g.write(alt_name)
1758 }
1759 }
1760 }
1761 }
1762 if !is_method {
1763 mut struct_name := clean_type.name()
1764 if clean_type is types.Struct {
1765 struct_name = clean_type.name
1766 }
1767 method_name = g.resolve_method_name(struct_name, fn_node.value)
1768 if method_name.len == 0 {
1769 for alias, target in g.tc.type_aliases {
1770 if target == struct_name {
1771 alias_method := '${alias}.${fn_node.value}'
1772 if alias_method in g.tc.fn_param_types {
1773 method_name = alias_method
1774 break
1775 }
1776 }
1777 }
1778 }
1779 if method_name.len > 0 && method_name in g.tc.fn_param_types {
1780 is_method = true
1781 base_id = g.a.child(fn_node, 0)
1782 g.write_method_c_name(id, node, method_name)
1783 } else {
1784 str_method := 'string.${fn_node.value}'
1785 if str_method in g.tc.fn_param_types {
1786 is_method = true
1787 method_name = str_method
1788 base_id = g.a.child(fn_node, 0)
1789 g.write(c_name(str_method))
1790 } else if embedded_method := g.embedded_method_name_for_type(clean_type,
1791 fn_node.value)
1792 {
1793 is_method = true
1794 method_name = embedded_method
1795 base_id = g.a.child(fn_node, 0)
1796 g.write(c_name(embedded_method))
1797 } else if struct_name.len > 0 {
1798 is_method = true
1799 base_id = g.a.child(fn_node, 0)
1800 fallback_method := '${struct_name}.${fn_node.value}'
1801 g.write_method_c_name(id, node, fallback_method)
1802 } else {
1803 g.gen_expr(g.a.child(&node, 0))
1804 }
1805 }
1806 }
1807 }
1808 } else if base.kind == .ident
1809 && (base.value in g.tc.structs || base.value in g.tc.enum_names || g.tc.qualify_name(base.value) in g.tc.structs
1810 || g.tc.qualify_name(base.value) in g.tc.enum_names) {
1811 qname := if base.value in g.tc.structs || base.value in g.tc.enum_names {
1812 base.value
1813 } else {
1814 g.tc.qualify_name(base.value)
1815 }
1816 static_name := '${qname}.${fn_node.value}'
1817 g.write(c_name(static_name))
1818 g.write('(')
1819 for i in 1 .. node.children_count {
1820 if i > 1 {
1821 g.write(', ')
1822 }
1823 g.gen_expr(g.a.child(&node, i))
1824 }
1825 g.write(')')
1826 return
1827 } else {
1828 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
1829 clean_type := types.unwrap_pointer(base_type)
1830 if g.gen_interface_method_call(node, fn_node, base_type) {
1831 return
1832 }
1833 if g.gen_fn_field_call(node, fn_node, base_type) {
1834 return
1835 }
1836 if arr := array_like_type(clean_type) {
1837 g.gen_array_method_call(node, fn_node, arr)
1838 return
1839 }
1840 if clean_type is types.ArrayFixed && fn_node.value == 'bytestr' {
1841 g.write('u8__vstring_with_len((u8*)')
1842 g.gen_expr(g.a.child(fn_node, 0))
1843 len_expr := g.fixed_array_len_value(clean_type)
1844 g.write(', ${len_expr})')
1845 return
1846 }
1847 if clean_type is types.Map {
1848 if fn_node.value == 'delete' {
1849 g.gen_map_delete(node, fn_node, clean_type, base_type)
1850 return
1851 } else if fn_node.value == 'clone' {
1852 g.write('map__clone(')
1853 g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1854 g.write(')')
1855 return
1856 } else if fn_node.value == 'clear' {
1857 g.write('map__clear(')
1858 g.gen_map_ref_arg(g.a.child(fn_node, 0), base_type)
1859 g.write(')')
1860 return
1861 } else if fn_node.value == 'free' {
1862 g.write('map__free(')
1863 if base_type is types.Pointer {
1864 g.gen_expr(g.a.child(fn_node, 0))
1865 } else {
1866 g.write('&')
1867 g.gen_expr(g.a.child(fn_node, 0))
1868 }
1869 g.write(')')
1870 return
1871 }
1872 }
1873 if clean_type is types.String {
1874 method_name = 'string.${fn_node.value}'
1875 if method_name in g.tc.fn_param_types {
1876 is_method = true
1877 base_id = g.a.child(fn_node, 0)
1878 g.write_method_c_name(id, node, method_name)
1879 } else {
1880 g.write('string__${fn_node.value}(')
1881 g.gen_expr(g.a.child(fn_node, 0))
1882 for i in 1 .. node.children_count {
1883 g.write(', ')
1884 g.gen_expr(g.a.child(&node, i))
1885 }
1886 g.write(')')
1887 return
1888 }
1889 }
1890 if !is_method && (clean_type is types.Void || clean_type is types.Primitive)
1891 && fn_node.value in ['vstring', 'vstring_with_len'] {
1892 g.write('u8__${fn_node.value}((u8*)')
1893 g.gen_expr(g.a.child(fn_node, 0))
1894 for i in 1 .. node.children_count {
1895 g.write(', ')
1896 g.gen_expr(g.a.child(&node, i))
1897 }
1898 g.write(')')
1899 return
1900 }
1901 if !is_method && clean_type is types.Struct && clean_type.name == 'IError' {
1902 if fn_node.value == 'msg' {
1903 g.gen_expr(g.a.child(fn_node, 0))
1904 g.write('.message')
1905 return
1906 } else if fn_node.value == 'code' {
1907 g.gen_expr(g.a.child(fn_node, 0))
1908 g.write('.code')
1909 return
1910 }
1911 }
1912 if !is_method && clean_type is types.Struct && clean_type.name == 'array'
1913 && fn_node.value == 'free' {
1914 g.write('array__free(')
1915 if base_type is types.Pointer {
1916 g.gen_expr(g.a.child(fn_node, 0))
1917 } else {
1918 g.write('&')
1919 g.gen_expr(g.a.child(fn_node, 0))
1920 }
1921 g.write(')')
1922 return
1923 }
1924 if !is_method && (clean_type is types.Primitive
1925 || clean_type is types.ISize || clean_type is types.USize
1926 || clean_type is types.Rune) {
1927 tname := clean_type.name()
1928 prim_method := '${tname}.${fn_node.value}'
1929 if prim_method in g.tc.fn_param_types {
1930 is_method = true
1931 base_id = g.a.child(fn_node, 0)
1932 g.write(c_name(prim_method))
1933 } else {
1934 mut prim_found := false
1935 if alias_method := g.find_alias_method(tname, fn_node.value) {
1936 is_method = true
1937 prim_found = true
1938 base_id = g.a.child(fn_node, 0)
1939 g.write(c_name(alias_method))
1940 }
1941 if !prim_found {
1942 alt_name := g.find_prim_method(fn_node.value)
1943 if alt_name.len > 0 {
1944 is_method = true
1945 base_id = g.a.child(fn_node, 0)
1946 g.write(alt_name)
1947 }
1948 }
1949 }
1950 }
1951 if !is_method {
1952 // Static method on a named type / type alias (e.g. `SimdFloat4.new(...)`,
1953 // where `SimdFloat4` names a type, not a value). The base ident resolves to
1954 // no value type, so handle it before the instance-method fallback.
1955 base_node_s := g.a.child_node(fn_node, 0)
1956 if base_node_s.kind == .ident {
1957 if static_fn := g.static_method_fn_name(base_node_s.value,
1958 fn_node.value)
1959 {
1960 g.write(g.direct_call_name(static_fn))
1961 g.write('(')
1962 for i in 1 .. node.children_count {
1963 if i > 1 {
1964 g.write(', ')
1965 }
1966 g.gen_expr(g.a.child(&node, i))
1967 }
1968 g.write(')')
1969 return
1970 }
1971 }
1972 }
1973 if !is_method {
1974 mut struct_name := clean_type.name()
1975 if clean_type is types.Struct {
1976 struct_name = clean_type.name
1977 }
1978 method_name = g.resolve_method_name(struct_name, fn_node.value)
1979 if method_name.len == 0 {
1980 for alias, target in g.tc.type_aliases {
1981 if target == struct_name {
1982 alias_method := '${alias}.${fn_node.value}'
1983 if alias_method in g.tc.fn_param_types {
1984 method_name = alias_method
1985 break
1986 }
1987 }
1988 }
1989 }
1990 if method_name.len > 0 && method_name in g.tc.fn_param_types {
1991 is_method = true
1992 base_id = g.a.child(fn_node, 0)
1993 g.write_method_c_name(id, node, method_name)
1994 } else {
1995 str_method := 'string.${fn_node.value}'
1996 if str_method in g.tc.fn_param_types {
1997 is_method = true
1998 method_name = str_method
1999 base_id = g.a.child(fn_node, 0)
2000 g.write(c_name(str_method))
2001 } else if embedded_method := g.embedded_method_name_for_type(clean_type,
2002 fn_node.value)
2003 {
2004 is_method = true
2005 method_name = embedded_method
2006 base_id = g.a.child(fn_node, 0)
2007 g.write(c_name(embedded_method))
2008 } else if struct_name.len > 0 {
2009 is_method = true
2010 base_id = g.a.child(fn_node, 0)
2011 fallback_method := '${struct_name}.${fn_node.value}'
2012 g.write_method_c_name(id, node, fallback_method)
2013 } else {
2014 g.gen_expr(g.a.child(&node, 0))
2015 }
2016 }
2017 }
2018 // !is_method
2019 }
2020 } else {
2021 fn_id := g.a.child(&node, 0)
2022 fn_ident := g.a.nodes[int(fn_id)]
2023 if fn_ident.kind == .ident {
2024 qname := g.tc.qualify_name(fn_ident.value)
2025 if fn_ident.value in g.tc.type_aliases || qname in g.tc.type_aliases
2026 || fn_ident.value in g.tc.structs || qname in g.tc.structs
2027 || fn_ident.value in g.tc.enum_names || qname in g.tc.enum_names
2028 || fn_ident.value in g.tc.sum_types || qname in g.tc.sum_types {
2029 type_name := if fn_ident.value in g.tc.type_aliases
2030 || fn_ident.value in g.tc.structs || fn_ident.value in g.tc.enum_names
2031 || fn_ident.value in g.tc.sum_types {
2032 fn_ident.value
2033 } else {
2034 qname
2035 }
2036 target_type := g.tc.parse_type(type_name)
2037 ct := g.tc.c_type(target_type)
2038 if target_type is types.SumType && node.children_count > 1 {
2039 g.gen_sum_cast_expr(target_type, g.a.child(&node, 1))
2040 } else {
2041 g.write('(${ct})(')
2042 for i in 1 .. node.children_count {
2043 if i > 1 {
2044 g.write(', ')
2045 }
2046 g.gen_expr(g.a.child(&node, i))
2047 }
2048 g.write(')')
2049 }
2050 return
2051 }
2052 call_key := g.call_key(id, fn_ident.value)
2053 looked_up := g.tc.cur_scope.lookup(fn_ident.value) or {
2054 types.Type(types.void_)
2055 }
2056 if looked_up !is types.Void && fn_type_from(looked_up) != none {
2057 g.write(c_name(fn_ident.value))
2058 } else if call_key in g.tc.fn_ret_types || call_key in g.tc.fn_param_types {
2059 g.write(g.direct_call_name_for_call(id, call_key))
2060 } else {
2061 g.write(c_name(fn_ident.value))
2062 }
2063 } else {
2064 g.gen_expr(fn_id)
2065 }
2066 }
2067 g.write('(')
2068 actual_fn := if is_method {
2069 method_name
2070 } else if target_name.contains('.') {
2071 g.call_key(id, target_name)
2072 } else {
2073 g.call_key(id, fn_name)
2074 }
2075 mut param_types := g.param_types_for(actual_fn, fn_name)
2076 if param_types.len == 0 && !is_method {
2077 // Calling through a function-typed value (a generic `f F` parameter or a
2078 // local fn variable): the callee is not a named function, so recover the
2079 // parameter types from the value's own function type. This lets `mut` /
2080 // pointer arguments receive their `&` exactly as a direct call would.
2081 if ft := fn_type_from(g.tc.resolve_type(g.a.child(&node, 0))) {
2082 param_types = ft.params.clone()
2083 }
2084 }
2085 mut arg_start := 1
2086 if is_method {
2087 base_type := g.receiver_base_type(base_id)
2088 wants_ptr := param_types.len > 0 && param_types[0] is types.Pointer
2089 receiver_type_name := g.type_lookup_name(base_type)
2090 method_short := method_name.all_after_last('.').all_after_last('__')
2091 atomic_receiver_wants_ptr := method_name.starts_with('stdatomic.AtomicVal_')
2092 || method_name.starts_with('stdatomic.AtomicVal.')
2093 || method_name.starts_with('AtomicVal_')
2094 || method_name.starts_with('AtomicVal.')
2095 || (receiver_type_name.contains('AtomicVal')
2096 && method_short in ['load', 'store', 'add', 'sub', 'swap', 'compare_and_swap'])
2097 if param_types.len > 0
2098 && g.gen_embedded_method_receiver(base_id, base_type, param_types[0], wants_ptr) {
2099 arg_start = 1
2100 } else {
2101 mut is_ptr_base := base_type is types.Pointer
2102 // A `string.*` method always takes its receiver by value. If the
2103 // receiver mis-resolves to a char pointer (e.g. a `const x =
2104 // os.getenv(..)` whose type was inferred as `&char`), the actual
2105 // storage is still a `string`, so do not dereference it.
2106 if is_ptr_base && method_name.starts_with('string.')
2107 && base_type is types.Pointer && base_type.base_type is types.Char {
2108 is_ptr_base = false
2109 }
2110 if (wants_ptr || atomic_receiver_wants_ptr) && !is_ptr_base {
2111 g.write('&')
2112 } else if !wants_ptr && !atomic_receiver_wants_ptr && is_ptr_base {
2113 g.write('*')
2114 }
2115 g.gen_expr(base_id)
2116 arg_start = 1
2117 }
2118 }
2119 num_call_args := node.children_count - arg_start
2120 is_variadic_fn := !is_method && !is_c_call && (g.tc.fn_variadic[actual_fn] or { false })
2121 variadic_idx := if is_variadic_fn && param_types.len > 0
2122 && param_types[param_types.len - 1] is types.Array {
2123 param_types.len - 1
2124 } else {
2125 -1
2126 }
2127 // A veb handler whose hidden `Context` parameter (param index 1, right
2128 // after the receiver) was omitted by the caller forwards the enclosing
2129 // handler's `ctx` in that slot so the remaining explicit arguments still
2130 // line up with their parameters.
2131 expected_non_ctx := if is_method { param_types.len - 1 } else { param_types.len }
2132 forward_ctx := param_types.len > 1 && g.is_implicit_veb_ctx_param(param_types[1])
2133 && num_call_args < expected_non_ctx && g.cur_scope_has_ctx()
2134 if forward_ctx && is_method {
2135 g.write(', ctx')
2136 }
2137 mut emitted_arg_count := 0
2138 for i in arg_start .. node.children_count {
2139 mut arg_idx := if is_method { i } else { i - 1 }
2140 if forward_ctx && (is_method || i - arg_start >= 1) {
2141 arg_idx++
2142 }
2143 arg_id := g.a.child(&node, i)
2144 if int(arg_id) < 0 {
2145 continue
2146 }
2147 arg_node := g.a.nodes[int(arg_id)]
2148 if is_method || emitted_arg_count > 0 {
2149 g.write(', ')
2150 }
2151 emitted_arg_count++
2152 if arg_node.kind == .field_init && variadic_idx >= 0 && arg_idx == variadic_idx {
2153 variadic_type := param_types[variadic_idx]
2154 if variadic_type is types.Array {
2155 if variadic_type.elem_type is types.Struct {
2156 c_elem := g.tc.c_type(variadic_type.elem_type)
2157 g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
2158 g.gen_params_struct_arg(variadic_type.elem_type, node, i)
2159 g.write('})')
2160 break
2161 }
2162 }
2163 }
2164 if arg_node.kind == .field_init {
2165 // `@[params]` struct argument: trailing `key: value` args form a struct literal
2166 ptyp := if arg_idx < param_types.len {
2167 param_types[arg_idx]
2168 } else {
2169 types.Type(types.void_)
2170 }
2171 g.gen_params_struct_arg(ptyp, node, i)
2172 break
2173 }
2174 if fixed := array_fixed_type(g.tc.resolve_type(arg_id)) {
2175 g.gen_fixed_array_data_arg(arg_id, fixed)
2176 continue
2177 }
2178 if !is_c_call && arg_idx < param_types.len {
2179 if fixed := array_fixed_type(param_types[arg_idx]) {
2180 g.gen_fixed_array_data_arg(arg_id, fixed)
2181 continue
2182 }
2183 }
2184 if !is_c_call && arg_idx < param_types.len
2185 && g.gen_pointer_arg_from_array_literal(arg_node, param_types[arg_idx]) {
2186 continue
2187 }
2188 cb_param := if arg_idx >= 0 && arg_idx < param_types.len {
2189 param_types[arg_idx]
2190 } else {
2191 types.Type(types.void_)
2192 }
2193 if g.gen_special_c_callback_arg(target_name, arg_idx, arg_id, cb_param) {
2194 continue
2195 }
2196 if variadic_idx >= 0 && arg_idx == variadic_idx {
2197 variadic_type := param_types[variadic_idx]
2198 if variadic_type is types.Array {
2199 if num_call_args > param_types.len {
2200 c_elem := g.tc.c_type(variadic_type.elem_type)
2201 count := num_call_args - variadic_idx
2202 g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
2203 for j in i .. node.children_count {
2204 if j > i {
2205 g.write(', ')
2206 }
2207 g.gen_expr_with_expected_type(g.a.child(&node, j),
2208 variadic_type.elem_type)
2209 }
2210 g.write('})')
2211 break
2212 }
2213 arg_type := g.tc.resolve_type(arg_id)
2214 if arg_type !is types.Array {
2215 c_elem := g.tc.c_type(variadic_type.elem_type)
2216 g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
2217 g.gen_expr_with_expected_type(arg_id, variadic_type.elem_type)
2218 g.write('})')
2219 continue
2220 }
2221 }
2222 }
2223 mut needs_addr := false
2224 if !is_c_call && arg_idx < param_types.len && param_types[arg_idx] is types.Pointer
2225 && !(arg_node.kind == .prefix && arg_node.op == .amp) {
2226 arg_type := g.tc.resolve_type(arg_id)
2227 if arg_type !is types.Pointer
2228 && !c_string_literal_pointer_arg(arg_node, param_types[arg_idx]) {
2229 needs_addr = true
2230 }
2231 }
2232 if !is_c_call && arg_idx < param_types.len {
2233 pt := param_types[arg_idx]
2234 if pt is types.Enum {
2235 g.expected_enum = pt.name
2236 }
2237 }
2238 if !is_c_call && arg_idx < param_types.len {
2239 if child_id := g.addressed_rvalue_arg(arg_node) {
2240 pt := param_types[arg_idx]
2241 if pt is types.Pointer {
2242 ct := g.tc.c_type(types.unwrap_pointer(pt))
2243 g.write('({${ct} _t${g.tmp_count} = ')
2244 g.gen_expr_with_expected_type(child_id, types.unwrap_pointer(pt))
2245 g.write('; &_t${g.tmp_count};})')
2246 g.tmp_count++
2247 g.expected_enum = ''
2248 continue
2249 }
2250 }
2251 }
2252 is_rvalue := arg_node.kind == .call
2253 || (arg_node.kind == .index && arg_node.value == 'range')
2254 if needs_addr && is_rvalue {
2255 pt := param_types[arg_idx]
2256 ct := g.tc.c_type(types.unwrap_pointer(pt))
2257 g.write('({${ct} _t${g.tmp_count} = ')
2258 g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
2259 g.write('; &_t${g.tmp_count};})')
2260 g.tmp_count++
2261 } else {
2262 if needs_addr {
2263 g.write('&')
2264 }
2265 emitted_variant := !needs_addr && !is_c_call && arg_idx < param_types.len
2266 && g.gen_sum_variant_arg(arg_id, param_types[arg_idx])
2267 if !emitted_variant {
2268 if !is_c_call && arg_idx < param_types.len
2269 && g.gen_optional_arg(arg_id, param_types[arg_idx]) {
2270 // handled
2271 } else if !is_c_call && arg_idx < param_types.len {
2272 g.gen_expr_with_expected_type(arg_id, param_types[arg_idx])
2273 } else {
2274 g.gen_expr(arg_id)
2275 }
2276 }
2277 }
2278 g.expected_enum = ''
2279 // A no-arg delegation leaves the forwarded ctx as the final argument;
2280 // emit it here, right after the receiver, for the lowered free call.
2281 if forward_ctx && !is_method && i - arg_start == 0 {
2282 g.write(', ctx')
2283 }
2284 }
2285 // Count the forwarded ctx (if any) as already supplied.
2286 actual_args := emitted_arg_count + (if forward_ctx { 1 } else { 0 })
2287 expected_args := if is_method {
2288 param_types.len - 1
2289 } else {
2290 param_types.len
2291 }
2292 if !is_c_call && expected_args > 0 && actual_args < expected_args {
2293 for pi in actual_args .. expected_args {
2294 if is_method || pi > 0 {
2295 g.write(', ')
2296 }
2297 pidx := if is_method { pi + 1 } else { pi }
2298 pt := param_types[pidx]
2299 // The implicit veb `Context` parameter is supplied from the
2300 // enclosing handler's `ctx`, not a zero/default value.
2301 if g.is_implicit_veb_ctx_param(pt) && g.cur_scope_has_ctx() {
2302 g.write('ctx')
2303 } else {
2304 g.gen_default_value_for_type(pt)
2305 }
2306 }
2307 }
2308 g.write(')')
2309 }
2310 }
2311}
2312
2313// receiver_base_type supports receiver base type handling for FlatGen.
2314fn (g &FlatGen) receiver_base_type(base_id flat.NodeId) types.Type {
2315 if int(base_id) < 0 {
2316 return types.Type(types.void_)
2317 }
2318 base := g.a.nodes[int(base_id)]
2319 if base.kind == .ident {
2320 if typ := g.current_param_type(base.value) {
2321 return typ
2322 }
2323 if typ := g.cur_param_types[base.value] {
2324 return typ
2325 }
2326 if typ := g.tc.cur_scope.lookup(base.value) {
2327 return typ
2328 }
2329 }
2330 return g.tc.resolve_type(base_id)
2331}
2332
2333fn (g &FlatGen) receiver_storage_type(base_id flat.NodeId) ?types.Type {
2334 if int(base_id) < 0 || int(base_id) >= g.a.nodes.len {
2335 return none
2336 }
2337 node := g.a.nodes[int(base_id)]
2338 if node.kind == .selector && node.children_count > 0 {
2339 parent_type := g.tc.resolve_type(g.a.child(&node, 0))
2340 return g.field_type(parent_type, node.value)
2341 }
2342 return none
2343}
2344
2345fn (g &FlatGen) receiver_needs_address(base_id flat.NodeId, base_type types.Type) bool {
2346 if int(base_id) >= 0 && int(base_id) < g.a.nodes.len {
2347 node := g.a.nodes[int(base_id)]
2348 if node.kind == .selector {
2349 if storage_type := g.receiver_storage_type(base_id) {
2350 return storage_type !is types.Pointer
2351 }
2352 }
2353 }
2354 return base_type !is types.Pointer
2355}
2356
2357fn (mut g FlatGen) gen_interface_method_call(node flat.Node, fn_node flat.Node, base_type types.Type) bool {
2358 clean0 := types.unwrap_pointer(base_type)
2359 mut clean := clean0
2360 if clean0 is types.Alias {
2361 clean = clean0.base_type
2362 }
2363 if clean !is types.Interface {
2364 return false
2365 }
2366 iface := clean as types.Interface
2367 if c_name(iface.name) == 'IError' {
2368 return false
2369 }
2370 if iface.name !in g.interfaces {
2371 return false
2372 }
2373 method_name := '${iface.name}.${fn_node.value}'
2374 param_types := g.interface_method_param_types(method_name) or { []types.Type{} }
2375 base_id := g.a.child(fn_node, 0)
2376 g.write(c_name(method_name))
2377 g.write('(')
2378 if g.receiver_needs_address(base_id, base_type) {
2379 g.write('&')
2380 }
2381 g.gen_expr(base_id)
2382 mut emitted_arg_count := 0
2383 for i in 1 .. node.children_count {
2384 arg_id := g.a.child(&node, i)
2385 if int(arg_id) < 0 {
2386 continue
2387 }
2388 arg_node := g.a.nodes[int(arg_id)]
2389 g.write(', ')
2390 emitted_arg_count++
2391 param_idx := i
2392 if param_idx < param_types.len {
2393 if fixed := array_fixed_type(param_types[param_idx]) {
2394 g.gen_fixed_array_data_arg(arg_id, fixed)
2395 continue
2396 }
2397 if g.gen_pointer_arg_from_array_literal(arg_node, param_types[param_idx]) {
2398 continue
2399 }
2400 if child_id := g.addressed_rvalue_arg(arg_node) {
2401 pt := param_types[param_idx]
2402 if pt is types.Pointer {
2403 ct := g.tc.c_type(types.unwrap_pointer(pt))
2404 g.write('({${ct} _t${g.tmp_count} = ')
2405 g.gen_expr_with_expected_type(child_id, types.unwrap_pointer(pt))
2406 g.write('; &_t${g.tmp_count};})')
2407 g.tmp_count++
2408 continue
2409 }
2410 }
2411 emitted_variant := g.gen_sum_variant_arg(arg_id, param_types[param_idx])
2412 if !emitted_variant {
2413 if g.gen_optional_arg(arg_id, param_types[param_idx]) {
2414 // handled
2415 } else {
2416 g.gen_expr_with_expected_type(arg_id, param_types[param_idx])
2417 }
2418 }
2419 } else {
2420 g.gen_expr(arg_id)
2421 }
2422 }
2423 expected_args := if param_types.len > 0 { param_types.len - 1 } else { 0 }
2424 if emitted_arg_count < expected_args {
2425 for pi in emitted_arg_count .. expected_args {
2426 g.write(', ')
2427 g.gen_default_value_for_type(param_types[pi + 1])
2428 }
2429 }
2430 g.write(')')
2431 return true
2432}
2433
2434fn (g &FlatGen) call_target_name(id flat.NodeId) string {
2435 if int(id) < 0 || int(id) >= g.a.nodes.len {
2436 return ''
2437 }
2438 node := g.a.nodes[int(id)]
2439 match node.kind {
2440 .ident {
2441 return node.value
2442 }
2443 .selector {
2444 if node.children_count == 0 {
2445 return node.value
2446 }
2447 base := g.a.child_node(&node, 0)
2448 if base.kind == .ident {
2449 if mod := g.import_alias_module(base.value) {
2450 return '${mod}.${node.value}'
2451 }
2452 return '${base.value}.${node.value}'
2453 }
2454 return node.value
2455 }
2456 .index {
2457 if node.children_count > 0 {
2458 return g.call_target_name(g.a.child(&node, 0))
2459 }
2460 return node.value
2461 }
2462 else {
2463 return node.value
2464 }
2465 }
2466}
2467
2468fn (g &FlatGen) call_has_selector_name(id flat.NodeId, name string) bool {
2469 if int(id) < 0 || int(id) >= g.a.nodes.len {
2470 return false
2471 }
2472 node := g.a.nodes[int(id)]
2473 if node.kind == .selector && node.value == name {
2474 return true
2475 }
2476 for i in 0 .. node.children_count {
2477 if g.call_has_selector_name(g.a.child(&node, i), name) {
2478 return true
2479 }
2480 }
2481 return false
2482}
2483
2484fn (g &FlatGen) is_veb_json_result_call(fn_node flat.Node) bool {
2485 if fn_node.kind == .ident {
2486 if fn_node.value in ['veb.Context.json', 'veb.Context.json_pretty'] {
2487 return true
2488 }
2489 if fn_node.value !in ['Context.json', 'Context.json_pretty'] {
2490 return false
2491 }
2492 if fn_node.value in g.tc.fn_param_types || fn_node.value in g.tc.fn_ret_types {
2493 return false
2494 }
2495 return g.type_name_known_in_current_module('Context')
2496 && g.type_embeds_veb_context(g.tc.parse_type('Context'))
2497 }
2498 if fn_node.kind != .selector || fn_node.children_count == 0 {
2499 return false
2500 }
2501 if fn_node.value !in ['json', 'json_pretty'] {
2502 return false
2503 }
2504 receiver_id := g.a.child(fn_node, 0)
2505 receiver_type := types.unwrap_pointer(g.tc.resolve_type(receiver_id))
2506 if receiver_type is types.Struct {
2507 receiver_name := receiver_type.name
2508 if receiver_name != 'veb.Context' {
2509 method_name := '${receiver_name}.${fn_node.value}'
2510 if method_name in g.tc.fn_param_types || method_name in g.tc.fn_ret_types {
2511 return false
2512 }
2513 }
2514 }
2515 if embedded_method := g.embedded_method_name_for_type(receiver_type, fn_node.value) {
2516 return embedded_method in ['veb.Context.json', 'veb.Context.json_pretty']
2517 }
2518 return g.is_veb_context_receiver(receiver_id)
2519}
2520
2521fn (g &FlatGen) is_veb_context_receiver(id flat.NodeId) bool {
2522 typ := g.tc.resolve_type(id)
2523 return g.type_embeds_veb_context(typ)
2524}
2525
2526fn (g &FlatGen) type_embeds_veb_context(typ types.Type) bool {
2527 clean := types.unwrap_pointer(typ)
2528 if clean is types.Alias {
2529 return g.type_embeds_veb_context(clean.base_type)
2530 }
2531 if clean !is types.Struct {
2532 return false
2533 }
2534 struct_name := clean.name()
2535 if struct_name == 'veb.Context' {
2536 return true
2537 }
2538 fields := g.struct_fields_for_type(struct_name) or { return false }
2539 for field in fields {
2540 embedded_type_name := g.embedded_field_type_name(field)
2541 if embedded_type_name == 'veb.Context' {
2542 return true
2543 }
2544 if embedded_type_name.len > 0
2545 && g.type_embeds_veb_context(g.tc.parse_type(embedded_type_name)) {
2546 return true
2547 }
2548 }
2549 return false
2550}
2551
2552fn (g &FlatGen) is_missing_middleware_use_call(fn_node flat.Node) bool {
2553 if fn_node.kind == .ident {
2554 if !fn_node.value.ends_with('.use') {
2555 return false
2556 }
2557 if fn_node.value in g.tc.fn_param_types || fn_node.value in g.tc.fn_ret_types {
2558 return false
2559 }
2560 receiver := fn_node.value.all_before_last('.')
2561 return g.struct_has_middleware_receiver(receiver)
2562 }
2563 if fn_node.kind != .selector || fn_node.value != 'use' || fn_node.children_count == 0 {
2564 return false
2565 }
2566 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
2567 clean_type := types.unwrap_pointer(base_type)
2568 if clean_type !is types.Struct {
2569 return false
2570 }
2571 method_name := '${clean_type.name()}.use'
2572 if method_name in g.tc.fn_param_types || method_name in g.tc.fn_ret_types {
2573 return false
2574 }
2575 return g.struct_has_middleware_receiver(clean_type.name())
2576}
2577
2578fn (g &FlatGen) struct_has_middleware_receiver(type_name string) bool {
2579 if is_middleware_type_name(type_name) {
2580 return true
2581 }
2582 fields := g.struct_fields_for_type(type_name) or { return false }
2583 for field in fields {
2584 embedded_type_name := g.embedded_field_type_name(field)
2585 if is_middleware_type_name(embedded_type_name) {
2586 return true
2587 }
2588 }
2589 return false
2590}
2591
2592fn is_middleware_type_name(name string) bool {
2593 base := if name.contains('[') { name.all_before('[') } else { name }
2594 return base == 'veb.Middleware'
2595}
2596
2597fn (g &FlatGen) call_default_return_type(id flat.NodeId) types.Type {
2598 if g.expected_expr_type is types.OptionType || g.expected_expr_type is types.ResultType {
2599 return g.expected_expr_type
2600 }
2601 if g.expected_expr_type is types.Struct && g.expected_expr_type.name.starts_with('Optional') {
2602 return g.expected_expr_type
2603 }
2604 if g.expected_expr_type is types.String {
2605 return g.expected_expr_type
2606 }
2607 if typ := g.tc.expr_type(id) {
2608 if typ !is types.Unknown && typ !is types.Void {
2609 return typ
2610 }
2611 }
2612 return g.tc.resolve_type(id)
2613}
2614
2615fn (g &FlatGen) json_decode_result_type(callee_id flat.NodeId) ?types.Type {
2616 if int(callee_id) < 0 || int(callee_id) >= g.a.nodes.len {
2617 return none
2618 }
2619 callee := g.a.nodes[int(callee_id)]
2620 if callee.kind != .index || callee.children_count < 2 {
2621 return none
2622 }
2623 arg := g.a.child_node(&callee, 1)
2624 mut type_name := ''
2625 if arg.typ.len > 0 {
2626 type_name = arg.typ
2627 } else if arg.kind == .array_init && arg.value.len > 0 {
2628 type_name = '[]${arg.value}'
2629 } else if arg.value.len > 0 {
2630 type_name = arg.value
2631 }
2632 if type_name.len == 0 {
2633 return none
2634 }
2635 return types.Type(types.ResultType{
2636 base_type: g.tc.parse_type(type_name)
2637 })
2638}
2639
2640fn (g &FlatGen) embedded_method_name_for_type(base_type types.Type, method string) ?string {
2641 type_name := g.type_lookup_name(base_type)
2642 if type_name.len == 0 || method.len == 0 {
2643 return none
2644 }
2645 return g.embedded_method_name_for_struct(type_name, method)
2646}
2647
2648fn (g &FlatGen) embedded_method_name_for_struct(type_name string, method string) ?string {
2649 fields := g.struct_fields_for_type(type_name) or { return none }
2650 for field in fields {
2651 embedded_type_name := g.embedded_field_type_name(field)
2652 if embedded_type_name.len == 0 {
2653 continue
2654 }
2655 method_name := '${embedded_type_name}.${method}'
2656 if method_name in g.tc.fn_param_types {
2657 return method_name
2658 }
2659 if found := g.embedded_method_name_for_struct(embedded_type_name, method) {
2660 return found
2661 }
2662 }
2663 return none
2664}
2665
2666fn (mut g FlatGen) gen_embedded_method_receiver(base_id flat.NodeId, base_type types.Type, expected_type types.Type, wants_ptr bool) bool {
2667 field := g.embedded_receiver_field_for_expected(base_type, expected_type) or { return false }
2668 field_is_ptr := field.typ is types.Pointer
2669 if wants_ptr && !field_is_ptr {
2670 g.write('&')
2671 } else if !wants_ptr && field_is_ptr {
2672 g.write('*')
2673 }
2674 needs_paren := g.a.nodes[int(base_id)].kind !in [.ident, .selector]
2675 if needs_paren {
2676 g.write('(')
2677 }
2678 g.gen_expr(base_id)
2679 if needs_paren {
2680 g.write(')')
2681 }
2682 first_op := if base_type is types.Pointer { '->' } else { '.' }
2683 g.write('${first_op}${c_name(field.name)}')
2684 return true
2685}
2686
2687fn (g &FlatGen) embedded_receiver_field_for_expected(base_type types.Type, expected_type types.Type) ?types.StructField {
2688 base_name := g.type_lookup_name(base_type)
2689 expected_name := g.type_lookup_name(expected_type)
2690 if base_name.len == 0 || expected_name.len == 0 {
2691 return none
2692 }
2693 fields := g.struct_fields_for_type(base_name) or { return none }
2694 for field in fields {
2695 embedded_type_name := g.embedded_field_type_name(field)
2696 if embedded_type_name == expected_name {
2697 return field
2698 }
2699 }
2700 return none
2701}
2702
2703// current_param_type returns current param type data for FlatGen.
2704fn (g &FlatGen) current_param_type(name string) ?types.Type {
2705 for i in 0 .. g.cur_param_names.len {
2706 if g.cur_param_names[i] == name {
2707 return g.cur_param_type_values[i]
2708 }
2709 }
2710 return none
2711}
2712
2713// gen_enum_str_call emits an explicit `enum_val.str()` (with no user-defined `str`)
2714// by routing to the compiler-synthesized `<Enum>__autostr`, so it matches `${enum}`
2715// interpolation exactly — including `[flag]` enums' `Enum{.a | .b}` form, which the
2716// old inline single-value ternary chain could not render.
2717fn (mut g FlatGen) gen_enum_str_call(fn_node &flat.Node, enum_type types.Enum) {
2718 mut name := enum_type.name
2719 if name.starts_with('main.') {
2720 name = name[5..]
2721 }
2722 g.write('${c_name(name)}__autostr(')
2723 g.gen_expr(g.a.child(fn_node, 0))
2724 g.write(')')
2725}
2726
2727// enum_receiver_method_name supports enum receiver method name handling for FlatGen.
2728fn (g &FlatGen) enum_receiver_method_name(enum_type types.Enum, method string) ?string {
2729 name := enum_type.name
2730 direct := '${name}.${method}'
2731 if direct in g.tc.fn_param_types {
2732 return direct
2733 }
2734 if name.contains('.') {
2735 return none
2736 }
2737 for candidate, _ in g.tc.fn_param_types {
2738 if candidate.ends_with('.${direct}') {
2739 return candidate
2740 }
2741 }
2742 return none
2743}
2744
2745// gen_fn_field_call emits fn field call output for c.
2746fn (mut g FlatGen) gen_fn_field_call(node flat.Node, fn_node &flat.Node, base_type types.Type) bool {
2747 fn_type := g.fn_field_type(base_type, fn_node.value) or { return false }
2748 base_id := g.a.child(fn_node, 0)
2749 base := g.a.nodes[int(base_id)]
2750 needs_paren := base.kind !in [.ident, .selector, .call]
2751 if needs_paren {
2752 g.write('(')
2753 }
2754 g.gen_expr(base_id)
2755 if needs_paren {
2756 g.write(')')
2757 }
2758 if base_type is types.Pointer {
2759 g.write('->')
2760 } else {
2761 g.write('.')
2762 }
2763 g.write(c_name(fn_node.value))
2764 g.write('(')
2765 for i in 1 .. node.children_count {
2766 if i > 1 {
2767 g.write(', ')
2768 }
2769 arg_id := g.a.child(&node, i)
2770 arg_idx := i - 1
2771 if arg_idx < fn_type.params.len {
2772 g.gen_arg_for_expected_type(arg_id, fn_type.params[arg_idx])
2773 } else {
2774 g.gen_expr(arg_id)
2775 }
2776 }
2777 g.write(')')
2778 return true
2779}
2780
2781// call_key updates call key state for FlatGen.
2782fn (g &FlatGen) call_key(id flat.NodeId, name string) string {
2783 if name.contains('.') {
2784 normalized := g.normalize_call_key(name)
2785 if normalized in g.tc.fn_param_types || normalized in g.tc.fn_ret_types {
2786 return normalized
2787 }
2788 }
2789 if resolved := g.tc.resolved_call_name(id) {
2790 return g.normalize_call_key(resolved)
2791 }
2792 return g.normalize_call_key(name)
2793}
2794
2795// normalize_call_key transforms normalize call key data for c.
2796fn (g &FlatGen) normalize_call_key(name string) string {
2797 if name.starts_with('main.') {
2798 short_name := name.all_after_last('.')
2799 if short_name in g.tc.fn_param_types || short_name in g.tc.fn_ret_types {
2800 return short_name
2801 }
2802 }
2803 if !name.contains('.') && g.tc.cur_module.len > 0 && g.tc.cur_module != 'main'
2804 && g.tc.cur_module != 'builtin' {
2805 local := '${g.tc.cur_module}.${name}'
2806 if local in g.tc.fn_param_types || local in g.tc.fn_ret_types {
2807 return local
2808 }
2809 }
2810 if name in g.tc.fn_param_types || name in g.tc.fn_ret_types {
2811 return name
2812 }
2813 qname := g.tc.qualify_fn_name(name)
2814 if qname in g.tc.fn_param_types || qname in g.tc.fn_ret_types {
2815 return qname
2816 }
2817 for _, mod_name in g.tc.imports {
2818 imported := '${mod_name}.${name}'
2819 if imported in g.tc.fn_param_types || imported in g.tc.fn_ret_types {
2820 return imported
2821 }
2822 }
2823 return qname
2824}
2825
2826// param_types_for supports param types for handling for FlatGen.
2827// param_types_for resolves the parameter types of a called function. It is invoked once
2828// per call site during codegen, and the slow path below scans every known function, so
2829// results are memoized: without this, generic/monomorphized call names that miss the
2830// direct lookups re-scan (and copy) the whole function table on every call (O(n^2)).
2831fn (mut g FlatGen) param_types_for(name string, fallback string) []types.Type {
2832 cache_key := '${name}\x01${fallback}'
2833 if cached := g.param_types_cache[cache_key] {
2834 return cached
2835 }
2836 result := g.param_types_for_uncached(name, fallback)
2837 g.param_types_cache[cache_key] = result
2838 return result
2839}
2840
2841fn (mut g FlatGen) param_types_for_uncached(name string, fallback string) []types.Type {
2842 if generic_params := g.generic_receiver_method_param_types(name) {
2843 return generic_params
2844 }
2845 if interface_types := g.interface_method_param_types(name) {
2846 return interface_types
2847 }
2848 for candidate in [name, fallback] {
2849 if params := g.tc.fn_param_types[candidate] {
2850 return params
2851 }
2852 if candidate.starts_with('main.') {
2853 short_name := candidate.all_after_last('.')
2854 if params := g.tc.fn_param_types[short_name] {
2855 return params
2856 }
2857 }
2858 }
2859 decl_types := g.param_types_from_decl(name, fallback)
2860 if decl_types.len > 0 {
2861 return decl_types
2862 }
2863 if name.contains('.') {
2864 // O(1) lookup via the precomputed short-name index instead of scanning the whole
2865 // function table on every (cache-missing) call — this fallback ran ~3000× and was
2866 // a top cgen self-time cost (each scan is O(functions)).
2867 short_name := name.all_after_last('.')
2868 if params := g.param_types_by_short[short_name] {
2869 return params
2870 }
2871 }
2872 return []types.Type{}
2873}
2874
2875fn (g &FlatGen) generic_receiver_method_param_types(name string) ?[]types.Type {
2876 if !name.contains('.') || !name.contains('[') || !name.contains(']') {
2877 return none
2878 }
2879 receiver := name.all_before_last('.')
2880 method := name.all_after_last('.')
2881 info := g.tc.resolve_generic_struct_method(receiver, method) or { return none }
2882 return info.params.clone()
2883}
2884
2885// precompute_param_type_index builds short-name -> param-types, preserving the fallback's
2886// priority (fn_decl_param_types first, then the checker's fn_param_types; first match wins).
2887fn (mut g FlatGen) precompute_param_type_index() {
2888 for name, params in g.fn_decl_param_types {
2889 if name.contains('.') {
2890 short := name.all_after_last('.')
2891 if short !in g.param_types_by_short {
2892 g.param_types_by_short[short] = params
2893 }
2894 }
2895 }
2896 for name, params in g.tc.fn_param_types {
2897 if name.contains('.') {
2898 short := name.all_after_last('.')
2899 if short !in g.param_types_by_short {
2900 g.param_types_by_short[short] = params
2901 }
2902 }
2903 }
2904}
2905
2906fn (g &FlatGen) interface_method_param_types(name string) ?[]types.Type {
2907 if !name.contains('.') {
2908 return none
2909 }
2910 iface_name := name.all_before_last('.')
2911 if iface_name !in g.interfaces {
2912 return none
2913 }
2914 method := name.all_after_last('.')
2915 if iface_name == 'IError' && method == 'str' {
2916 return none
2917 }
2918 decl_key := g.interface_method_signature_key(iface_name, method) or { return none }
2919 decl_params := g.tc.fn_param_types[decl_key] or { return none }
2920 mut params := []types.Type{cap: decl_params.len}
2921 params << types.Type(types.Pointer{
2922 base_type: types.Type(types.Interface{
2923 name: iface_name
2924 })
2925 })
2926 if decl_params.len > 1 {
2927 for i in 1 .. decl_params.len {
2928 params << decl_params[i]
2929 }
2930 }
2931 return params
2932}
2933
2934// param_types_from_decl converts param types from decl data for c.
2935fn (mut g FlatGen) param_types_from_decl(name string, fallback string) []types.Type {
2936 if name.contains('.') {
2937 if ptypes := g.fn_decl_param_types[name] {
2938 return ptypes
2939 }
2940 } else {
2941 for candidate in [fallback, name] {
2942 if ptypes := g.fn_decl_param_types[candidate] {
2943 return ptypes
2944 }
2945 }
2946 }
2947 return []types.Type{}
2948}
2949
2950// short_receiver_method_name supports short receiver method name handling for c.
2951fn short_receiver_method_name(name string) string {
2952 if !name.contains('.') {
2953 return ''
2954 }
2955 receiver := name.all_before_last('.')
2956 if !receiver.contains('.') {
2957 return ''
2958 }
2959 return '${receiver.all_after_last('.')}.${name.all_after_last('.')}'
2960}
2961
2962// gen_arg_for_expected_type emits arg for expected type output for c.
2963fn (mut g FlatGen) gen_arg_for_expected_type(arg_id flat.NodeId, expected types.Type) {
2964 arg_node := g.a.nodes[int(arg_id)]
2965 mut needs_addr := false
2966 if expected is types.Pointer && !(arg_node.kind == .prefix && arg_node.op == .amp) {
2967 arg_type := g.tc.resolve_type(arg_id)
2968 if arg_type !is types.Pointer {
2969 needs_addr = true
2970 }
2971 }
2972 if needs_addr {
2973 g.write('&')
2974 }
2975 if !needs_addr && g.gen_sum_variant_arg(arg_id, expected) {
2976 return
2977 }
2978 if !needs_addr && g.gen_optional_arg(arg_id, expected) {
2979 return
2980 }
2981 g.gen_expr_with_expected_type(arg_id, expected)
2982}
2983
2984fn (mut g FlatGen) gen_callback_fn_value_for_expected_type(arg_id flat.NodeId, expected types.Type) bool {
2985 return g.gen_callback_fn_value_for_expected_c_abi(arg_id, expected, '')
2986}
2987
2988fn (mut g FlatGen) gen_callback_fn_value_for_expected_c_abi(arg_id flat.NodeId, expected types.Type, expected_c_abi string) bool {
2989 expected_fn := fn_type_from(expected) or { return false }
2990 actual_name := g.callback_fn_value_name(arg_id, expected) or { return false }
2991 actual_fn := g.callback_fn_value_type(actual_name) or { return false }
2992 wrapper := g.ensure_callback_userdata_wrapper(actual_name, actual_fn, expected_fn,
2993 expected_c_abi) or { return false }
2994 g.write(wrapper)
2995 return true
2996}
2997
2998fn (mut g FlatGen) gen_callback_fn_value_for_field_c_abi(arg_id flat.NodeId, expected types.Type, expected_c_abi string) bool {
2999 if expected_c_abi.len == 0 {
3000 return false
3001 }
3002 if g.gen_callback_fn_value_for_expected_c_abi(arg_id, expected, expected_c_abi) {
3003 return true
3004 }
3005 if call_name := g.callback_direct_fn_value_name_for_c_abi(arg_id, expected, expected_c_abi) {
3006 g.write(g.callback_c_fn_name(call_name))
3007 return true
3008 }
3009 if _ := g.callback_fn_value_name(arg_id, expected) {
3010 g.gen_expr(arg_id)
3011 return true
3012 }
3013 return false
3014}
3015
3016fn (mut g FlatGen) callback_fn_value_name(id flat.NodeId, expected types.Type) ?string {
3017 if name := g.tc.resolved_fn_value_name(id) {
3018 return name
3019 }
3020 if int(id) < 0 || int(id) >= g.a.nodes.len {
3021 return none
3022 }
3023 node := g.a.nodes[int(id)]
3024 if node.kind in [.cast_expr, .paren, .expr_stmt] && node.children_count > 0 {
3025 return g.callback_fn_value_name(g.a.child(&node, 0), expected)
3026 }
3027 return none
3028}
3029
3030fn (mut g FlatGen) callback_direct_fn_value_name(id flat.NodeId, expected types.Type) ?string {
3031 return g.callback_direct_fn_value_name_for_c_abi(id, expected, '')
3032}
3033
3034fn (mut g FlatGen) callback_direct_fn_value_name_for_c_abi(id flat.NodeId, expected types.Type, expected_c_abi string) ?string {
3035 expected_fn := fn_type_from(expected) or { return none }
3036 actual_name := g.callback_fn_value_name(id, expected) or { return none }
3037 actual_fn := g.callback_fn_value_type(actual_name) or { return none }
3038 if !g.callback_fn_types_direct_compatible(actual_fn, expected_fn, expected_c_abi) {
3039 return none
3040 }
3041 return actual_name
3042}
3043
3044fn (mut g FlatGen) callback_fn_value_type(name string) ?types.FnType {
3045 params := g.tc.fn_param_types[name] or { return none }
3046 ret := g.tc.fn_ret_types[name] or { return none }
3047 return types.FnType{
3048 params: params.clone()
3049 return_type: ret
3050 }
3051}
3052
3053fn (mut g FlatGen) callback_fn_types_direct_compatible(actual types.FnType, expected types.FnType, expected_c_abi string) bool {
3054 if actual.params.len != expected.params.len {
3055 return false
3056 }
3057 if g.callback_c_type(actual.return_type) != g.callback_expected_return_c_type(expected.return_type,
3058 expected_c_abi) {
3059 return false
3060 }
3061 for i in 0 .. expected.params.len {
3062 if g.callback_c_type(fn_type_param(actual, i)) != g.callback_expected_param_c_type(expected,
3063 i, expected_c_abi) {
3064 return false
3065 }
3066 }
3067 return true
3068}
3069
3070fn (mut g FlatGen) ensure_callback_userdata_wrapper(actual_name string, actual types.FnType, expected types.FnType, expected_c_abi string) ?string {
3071 if actual.params.len != expected.params.len {
3072 return none
3073 }
3074 actual_ret_ct := g.callback_c_type(actual.return_type)
3075 expected_ret_ct := g.callback_expected_return_c_type(expected.return_type, expected_c_abi)
3076 if actual_ret_ct != expected_ret_ct {
3077 return none
3078 }
3079 mut needs_wrapper := false
3080 mut param_decls := []string{}
3081 mut call_args := []string{}
3082 for i in 0 .. expected.params.len {
3083 expected_param := fn_type_param(expected, i)
3084 actual_param := fn_type_param(actual, i)
3085 expected_ct := g.callback_expected_param_c_type(expected, i, expected_c_abi)
3086 actual_ct := g.callback_c_type(actual_param)
3087 param_decls << '${expected_ct} arg${i}'
3088 if actual_ct == expected_ct {
3089 call_args << 'arg${i}'
3090 continue
3091 }
3092 if g.callback_can_cast_userdata_param(actual_param, expected_param) {
3093 call_args << '(${actual_ct})arg${i}'
3094 needs_wrapper = true
3095 continue
3096 }
3097 if callback_can_cast_const_abi_param(actual_ct, expected_ct) {
3098 call_args << '(${actual_ct})arg${i}'
3099 needs_wrapper = true
3100 continue
3101 }
3102 return none
3103 }
3104 if !needs_wrapper {
3105 return none
3106 }
3107 actual_c_name := g.callback_c_fn_name(actual_name)
3108 expected_key := if expected_c_abi.len > 0 {
3109 expected_c_abi
3110 } else {
3111 g.callback_fn_type_key(expected)
3112 }
3113 key := '${actual_c_name}|${g.callback_fn_type_key(actual)}|${expected_key}'
3114 if name := g.callback_wrapper_names[key] {
3115 return name
3116 }
3117 name := c_name('${actual_c_name}_callback_adapter_${callback_stable_key_hash(key)}')
3118 g.callback_wrapper_names[key] = name
3119 params := if param_decls.len == 0 { 'void' } else { param_decls.join(', ') }
3120 call := '${actual_c_name}(${call_args.join(', ')})'
3121 body := if expected_ret_ct == 'void' {
3122 'static void ${name}(${params}) { ${call}; }'
3123 } else {
3124 'static ${expected_ret_ct} ${name}(${params}) { return ${call}; }'
3125 }
3126 g.callback_wrapper_defs << body
3127 return name
3128}
3129
3130fn (mut g FlatGen) callback_expected_return_c_type(typ types.Type, expected_c_abi string) string {
3131 if expected_c_abi.len > 0 {
3132 ret, _ := fn_ptr_typedef_parts(expected_c_abi)
3133 return ret.trim_space()
3134 }
3135 return g.callback_c_type(typ)
3136}
3137
3138fn (mut g FlatGen) callback_expected_param_c_type(expected types.FnType, idx int, expected_c_abi string) string {
3139 if expected_c_abi.len > 0 {
3140 params := callback_fn_ptr_param_c_types(expected_c_abi)
3141 if idx < params.len {
3142 return params[idx]
3143 }
3144 }
3145 return g.callback_c_type(fn_type_param(expected, idx))
3146}
3147
3148fn callback_fn_ptr_param_c_types(encoded string) []string {
3149 _, params := fn_ptr_typedef_parts(encoded)
3150 clean := params.trim_space()
3151 if clean.len == 0 || clean == 'void' {
3152 return []string{}
3153 }
3154 mut out := []string{}
3155 for param in clean.split(',') {
3156 out << param.trim_space()
3157 }
3158 return out
3159}
3160
3161fn callback_can_cast_const_abi_param(actual_ct string, expected_ct string) bool {
3162 actual := actual_ct.trim_space()
3163 expected := expected_ct.trim_space()
3164 if !expected.starts_with('const ') || !expected.ends_with('*') {
3165 return false
3166 }
3167 return actual == expected['const '.len..].trim_space()
3168}
3169
3170fn (mut g FlatGen) callback_c_type(typ types.Type) string {
3171 if typ is types.Void {
3172 return 'void'
3173 }
3174 mut ct := if typ is types.OptionType || typ is types.ResultType {
3175 g.optional_type_name(typ)
3176 } else {
3177 g.tc.c_type(typ)
3178 }
3179 if ct.starts_with('fn_ptr:') {
3180 ct = g.resolve_fn_ptr_type(ct)
3181 }
3182 return ct
3183}
3184
3185fn (mut g FlatGen) callback_fn_type_key(typ types.FnType) string {
3186 mut parts := []string{}
3187 for i in 0 .. typ.params.len {
3188 parts << g.callback_c_type(fn_type_param(typ, i))
3189 }
3190 return '${g.callback_c_type(typ.return_type)}|${parts.join(',')}'
3191}
3192
3193fn callback_stable_key_hash(key string) string {
3194 mut hash := u64(1469598103934665603)
3195 for b in key.bytes() {
3196 hash ^= u64(b)
3197 hash *= u64(1099511628211)
3198 }
3199 return '${hash}'
3200}
3201
3202fn (g &FlatGen) callback_can_cast_userdata_param(actual types.Type, expected types.Type) bool {
3203 return (callback_is_voidptr_type(expected) && callback_is_nonvoid_pointer_type(actual))
3204 || (callback_is_nonvoid_pointer_type(expected) && callback_is_voidptr_type(actual))
3205}
3206
3207fn callback_is_voidptr_type(typ types.Type) bool {
3208 clean := callback_unalias_type(typ)
3209 if clean is types.Pointer {
3210 base := callback_unalias_type(clean.base_type)
3211 return base is types.Void
3212 }
3213 return false
3214}
3215
3216fn callback_is_nonvoid_pointer_type(typ types.Type) bool {
3217 clean := callback_unalias_type(typ)
3218 if clean is types.Pointer {
3219 base := callback_unalias_type(clean.base_type)
3220 return base !is types.Void
3221 }
3222 return false
3223}
3224
3225fn callback_unalias_type(typ types.Type) types.Type {
3226 if typ is types.Alias {
3227 return callback_unalias_type(typ.base_type)
3228 }
3229 return typ
3230}
3231
3232fn (g &FlatGen) callback_c_fn_name(name string) string {
3233 if name.starts_with('C.') {
3234 return c_name(name.all_after_last('.'))
3235 }
3236 if g.test_files.len > 0 && (name == 'main' || name == 'main.main') {
3237 return g.test_user_main_c_name()
3238 }
3239 if name.starts_with('main.') {
3240 return c_name(name.all_after_last('.'))
3241 }
3242 return c_name(name)
3243}
3244
3245fn fn_type_param(typ types.FnType, idx int) types.Type {
3246 return typ.params[idx]
3247}
3248
3249// gen_optional_arg emits optional arg output for c.
3250fn (mut g FlatGen) gen_optional_arg(arg_id flat.NodeId, expected types.Type) bool {
3251 mut base_type := types.Type(types.void_)
3252 if expected is types.OptionType {
3253 base_type = expected.base_type
3254 } else if expected is types.ResultType {
3255 base_type = expected.base_type
3256 } else {
3257 return false
3258 }
3259 if g.expr_is_optional_literal(arg_id, expected) {
3260 g.gen_expr_with_expected_type(g.collapsed_optional_literal(arg_id, expected), expected)
3261 return true
3262 }
3263 arg_type := g.usable_expr_type(arg_id)
3264 if arg_type is types.OptionType || arg_type is types.ResultType {
3265 if g.type_names_match(arg_type, expected) {
3266 g.gen_expr_with_expected_type(arg_id, expected)
3267 return true
3268 }
3269 arg_node := g.a.nodes[int(arg_id)]
3270 if arg_node.kind == .none_expr || g.expr_really_returns_optional(arg_id) {
3271 g.gen_expr_with_expected_type(arg_id, expected)
3272 return true
3273 }
3274 }
3275 ct := g.optional_type_name(expected)
3276 if base_type is types.Void {
3277 g.write('(${ct}){.ok = true}')
3278 return true
3279 }
3280 g.write('(${ct}){.ok = true, .value = ')
3281 g.gen_expr_with_expected_type(arg_id, base_type)
3282 g.write('}')
3283 return true
3284}
3285
3286// expr_is_optional_literal supports expr is optional literal handling for FlatGen.
3287fn (mut g FlatGen) expr_is_optional_literal(id flat.NodeId, expected types.Type) bool {
3288 if int(id) < 0 {
3289 return false
3290 }
3291 node := g.a.nodes[int(id)]
3292 if node.kind != .struct_init && node.kind != .cast_expr {
3293 return false
3294 }
3295 return node.value.starts_with('?') || node.value.starts_with('!')
3296 || node.value == g.optional_type_name(expected) || node.value.starts_with('Optional')
3297}
3298
3299// collapsed_optional_literal supports collapsed optional literal handling for FlatGen.
3300fn (mut g FlatGen) collapsed_optional_literal(id flat.NodeId, expected types.Type) flat.NodeId {
3301 mut current := id
3302 for _ in 0 .. 4 {
3303 value_id := g.optional_literal_value_id(current) or { break }
3304 if !g.expr_is_optional_literal(value_id, expected) {
3305 break
3306 }
3307 current = value_id
3308 }
3309 return current
3310}
3311
3312// optional_literal_value_id supports optional literal value id handling for FlatGen.
3313fn (g &FlatGen) optional_literal_value_id(id flat.NodeId) ?flat.NodeId {
3314 if int(id) < 0 {
3315 return none
3316 }
3317 node := g.a.nodes[int(id)]
3318 if node.kind != .struct_init && node.kind != .cast_expr {
3319 return none
3320 }
3321 for i in 0 .. node.children_count {
3322 field := g.a.child_node(&node, i)
3323 if field.kind == .field_init && field.value == 'value' && field.children_count > 0 {
3324 return g.a.child(field, 0)
3325 }
3326 }
3327 return none
3328}
3329
3330// fn_field_type supports fn field type handling for FlatGen.
3331fn (g &FlatGen) fn_field_type(base_type types.Type, field_name string) ?types.FnType {
3332 field_type := g.field_type(base_type, field_name) or { return none }
3333 return fn_type_from(field_type)
3334}
3335
3336// field_type supports field type handling for FlatGen.
3337fn (g &FlatGen) field_type(base_type types.Type, field_name string) ?types.Type {
3338 clean0 := types.unwrap_pointer(base_type)
3339 mut clean := clean0
3340 if clean0 is types.Alias {
3341 clean = clean0.base_type
3342 }
3343 mut struct_name := ''
3344 if clean is types.Struct {
3345 struct_name = clean.name
3346 } else if clean is types.Array {
3347 struct_name = 'array'
3348 } else if clean is types.Map {
3349 struct_name = 'map'
3350 } else if clean is types.String {
3351 struct_name = 'string'
3352 }
3353 if struct_name.len == 0 {
3354 return none
3355 }
3356 fields := g.tc.structs[struct_name] or { return none }
3357 for field in fields {
3358 if field.name == field_name {
3359 return field.typ
3360 }
3361 }
3362 return none
3363}
3364
3365// fn_type_from supports fn type from handling for c.
3366fn fn_type_from(t types.Type) ?types.FnType {
3367 if t is types.FnType {
3368 return t
3369 }
3370 if t is types.Alias {
3371 return fn_type_from(t.base_type)
3372 }
3373 return none
3374}
3375
3376// gen_call_args emits call args output for c.
3377fn (mut g FlatGen) gen_call_args(fn_name string, node flat.Node, start int) {
3378 mut param_types := g.param_types_for(fn_name, fn_name.all_after_last('.'))
3379 // Retry with the bare method name only when the qualified name is genuinely unresolved —
3380 // not when it is a known function that simply takes no parameters. The short-name index
3381 // matches any same-named function, so retrying a real 0-param fn (e.g. a static method
3382 // `Type.build()`) would adopt an unrelated `build`'s parameters and append phantom args.
3383 if param_types.len == 0 && fn_name.contains('.') && fn_name !in g.tc.fn_param_types
3384 && c_name(fn_name) !in g.tc.fn_param_types {
3385 param_types = g.param_types_for(fn_name.all_after_last('.'), fn_name.all_after_last('.'))
3386 }
3387 is_c_variadic_fn := g.tc.c_variadic_fns[fn_name] or { false }
3388 is_variadic_fn := !is_c_variadic_fn && (g.tc.fn_variadic[fn_name] or { false })
3389 variadic_idx := if is_variadic_fn && param_types.len > 0
3390 && param_types[param_types.len - 1] is types.Array {
3391 param_types.len - 1
3392 } else {
3393 -1
3394 }
3395 typed_param_count := if is_c_variadic_fn && param_types.len > 0
3396 && param_types[param_types.len - 1] is types.Array {
3397 param_types.len - 1
3398 } else {
3399 param_types.len
3400 }
3401 num_args := node.children_count - start
3402 is_variadic := variadic_idx >= 0 && num_args > param_types.len
3403 for i in start .. node.children_count {
3404 if i > start {
3405 g.write(', ')
3406 }
3407 arg_idx := i - start
3408 arg_id := g.a.child(&node, i)
3409 arg_node := g.a.nodes[int(arg_id)]
3410 if arg_node.kind == .field_init && variadic_idx >= 0 && arg_idx == variadic_idx {
3411 variadic_type := param_types[variadic_idx]
3412 if variadic_type is types.Array {
3413 if variadic_type.elem_type is types.Struct {
3414 c_elem := g.tc.c_type(variadic_type.elem_type)
3415 g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
3416 g.gen_params_struct_arg(variadic_type.elem_type, node, i)
3417 g.write('})')
3418 break
3419 }
3420 }
3421 }
3422 if arg_node.kind == .field_init {
3423 // `@[params]` struct argument: trailing `key: value` args form a struct literal
3424 ptyp := if arg_idx < typed_param_count {
3425 param_types[arg_idx]
3426 } else {
3427 types.Type(types.void_)
3428 }
3429 g.gen_params_struct_arg(ptyp, node, i)
3430 break
3431 }
3432 if fixed := array_fixed_type(g.tc.resolve_type(arg_id)) {
3433 g.gen_fixed_array_data_arg(arg_id, fixed)
3434 continue
3435 }
3436 if arg_idx < typed_param_count {
3437 if fixed := array_fixed_type(param_types[arg_idx]) {
3438 g.gen_fixed_array_data_arg(arg_id, fixed)
3439 continue
3440 }
3441 }
3442 if arg_idx < typed_param_count
3443 && g.gen_pointer_arg_from_array_literal(arg_node, param_types[arg_idx]) {
3444 continue
3445 }
3446 cb_param := if arg_idx >= 0 && arg_idx < param_types.len {
3447 param_types[arg_idx]
3448 } else {
3449 types.Type(types.void_)
3450 }
3451 if g.gen_special_c_callback_arg(fn_name, arg_idx, arg_id, cb_param) {
3452 continue
3453 }
3454 if is_variadic && arg_idx == variadic_idx {
3455 variadic_type := param_types[variadic_idx]
3456 if variadic_type is types.Array {
3457 c_elem := g.tc.c_type(variadic_type.elem_type)
3458 count := num_args - variadic_idx
3459 g.write('new_array_from_c_array(${count}, ${count}, sizeof(${c_elem}), (${c_elem}[]){')
3460 for j in i .. node.children_count {
3461 if j > i {
3462 g.write(', ')
3463 }
3464 g.gen_expr_with_expected_type(g.a.child(&node, j), variadic_type.elem_type)
3465 }
3466 g.write('})')
3467 }
3468 break
3469 }
3470 if variadic_idx >= 0 && arg_idx == variadic_idx && num_args == param_types.len {
3471 arg_type := g.tc.resolve_type(arg_id)
3472 if arg_type !is types.Array {
3473 variadic_type := param_types[variadic_idx]
3474 if variadic_type is types.Array {
3475 c_elem := g.tc.c_type(variadic_type.elem_type)
3476 g.write('new_array_from_c_array(1, 1, sizeof(${c_elem}), (${c_elem}[]){')
3477 g.gen_expr_with_expected_type(arg_id, variadic_type.elem_type)
3478 g.write('})')
3479 continue
3480 }
3481 }
3482 }
3483 mut needs_addr := false
3484 if arg_idx < typed_param_count && param_types[arg_idx] is types.Pointer
3485 && !(arg_node.kind == .prefix && arg_node.op == .amp) {
3486 arg_type := g.tc.resolve_type(arg_id)
3487 if arg_type !is types.Pointer
3488 && !c_string_literal_pointer_arg(arg_node, param_types[arg_idx]) {
3489 needs_addr = true
3490 }
3491 }
3492 is_rvalue := arg_node.kind == .call
3493 || (arg_node.kind == .index && arg_node.value == 'range')
3494 if needs_addr && is_rvalue {
3495 pt := param_types[arg_idx]
3496 ct := g.tc.c_type(types.unwrap_pointer(pt))
3497 g.write('({${ct} _t${g.tmp_count} = ')
3498 g.gen_expr_with_expected_type(arg_id, types.unwrap_pointer(pt))
3499 g.write('; &_t${g.tmp_count};})')
3500 g.tmp_count++
3501 } else {
3502 if arg_idx < typed_param_count {
3503 if child_id := g.addressed_rvalue_arg(arg_node) {
3504 pt := param_types[arg_idx]
3505 if pt is types.Pointer {
3506 ct := g.tc.c_type(types.unwrap_pointer(pt))
3507 g.write('({${ct} _t${g.tmp_count} = ')
3508 g.gen_expr_with_expected_type(child_id, types.unwrap_pointer(pt))
3509 g.write('; &_t${g.tmp_count};})')
3510 g.tmp_count++
3511 continue
3512 }
3513 }
3514 }
3515 if needs_addr {
3516 g.write('&')
3517 }
3518 emitted_variant := !needs_addr && arg_idx < typed_param_count
3519 && g.gen_sum_variant_arg(arg_id, param_types[arg_idx])
3520 if !emitted_variant {
3521 if arg_idx < typed_param_count && g.gen_optional_arg(arg_id, param_types[arg_idx]) {
3522 // handled
3523 } else if arg_idx < typed_param_count {
3524 g.gen_expr_with_expected_type(arg_id, param_types[arg_idx])
3525 } else {
3526 g.gen_expr(arg_id)
3527 }
3528 }
3529 }
3530 if variadic_idx >= 0 && num_args == variadic_idx {
3531 if node.children_count > start {
3532 g.write(', ')
3533 }
3534 variadic_type := param_types[variadic_idx]
3535 if variadic_type is types.Array {
3536 c_elem := g.tc.c_type(variadic_type.elem_type)
3537 g.write('new_array_from_c_array(0, 0, sizeof(${c_elem}), (${c_elem}[]){0})')
3538 }
3539 }
3540 }
3541 num_provided := node.children_count - start
3542 if num_provided < typed_param_count {
3543 for i in num_provided .. typed_param_count {
3544 if num_provided > 0 || i > num_provided {
3545 g.write(', ')
3546 }
3547 g.gen_default_value_for_type(param_types[i])
3548 }
3549 }
3550}
3551
3552// is_flag_enum_method reports whether is flag enum method applies in c.
3553fn (g &FlatGen) is_flag_enum_method(fn_node &flat.Node) bool {
3554 if fn_node.kind != .selector {
3555 return false
3556 }
3557 method := fn_node.value
3558 if method !in ['has', 'all', 'set', 'clear'] {
3559 return false
3560 }
3561 base_type := g.tc.resolve_type(g.a.child(fn_node, 0))
3562 clean := types.unwrap_pointer(base_type)
3563 if clean is types.Enum {
3564 return true
3565 } else if clean is types.Primitive {
3566 return clean.props.has(.integer)
3567 } else if clean is types.Unknown {
3568 return true
3569 }
3570 return false
3571}
3572
3573// gen_flag_enum_call emits flag enum call output for c.
3574fn (mut g FlatGen) gen_flag_enum_call(node flat.Node) {
3575 fn_node := g.a.child_node(&node, 0)
3576 method := fn_node.value
3577 base_id := g.a.child(fn_node, 0)
3578 base_type := types.unwrap_pointer(g.tc.resolve_type(base_id))
3579 match method {
3580 'has' {
3581 g.write('((')
3582 g.gen_expr(base_id)
3583 g.write(' & ')
3584 if node.children_count > 1 {
3585 g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
3586 }
3587 g.write(') != 0)')
3588 }
3589 'all' {
3590 g.write('((')
3591 g.gen_expr(base_id)
3592 g.write(' & (')
3593 if node.children_count > 1 {
3594 g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
3595 }
3596 g.write(')) == (')
3597 if node.children_count > 1 {
3598 g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
3599 }
3600 g.write('))')
3601 }
3602 'set' {
3603 g.gen_expr(base_id)
3604 g.write(' |= ')
3605 if node.children_count > 1 {
3606 g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
3607 }
3608 }
3609 'clear' {
3610 g.gen_expr(base_id)
3611 g.write(' &= ~(')
3612 if node.children_count > 1 {
3613 g.gen_flag_enum_arg(g.a.child(&node, 1), base_type)
3614 }
3615 g.write(')')
3616 }
3617 else {}
3618 }
3619}
3620
3621// gen_flag_enum_arg emits flag enum arg output for c.
3622fn (mut g FlatGen) gen_flag_enum_arg(arg_id flat.NodeId, base_type types.Type) {
3623 if base_type is types.Enum {
3624 g.gen_expr_with_expected_type(arg_id, base_type)
3625 } else {
3626 g.gen_expr(arg_id)
3627 }
3628}
3629
3630// is_generic_type reports whether is generic type applies in c.
3631fn is_generic_type(typ string) bool {
3632 t := typ.trim_left('&?!')
3633 return t.len == 1 && t[0] >= `A` && t[0] <= `Z`
3634}
3635
3636// has_generic_params reports whether has generic params applies in c.
3637fn (g &FlatGen) has_generic_params(node flat.Node) bool {
3638 for i in 0 .. node.children_count {
3639 child := g.a.child_node(&node, i)
3640 if child.kind == .param && is_generic_type(child.typ) {
3641 return true
3642 }
3643 }
3644 return is_generic_type(node.typ)
3645}
3646
3647fn (g &FlatGen) addressed_rvalue_arg(arg_node flat.Node) ?flat.NodeId {
3648 if arg_node.kind != .prefix || arg_node.op != .amp || arg_node.children_count == 0 {
3649 return none
3650 }
3651 child_id := g.a.child(&arg_node, 0)
3652 child := g.a.nodes[int(child_id)]
3653 if child.kind == .call || (child.kind == .index && child.value == 'range') {
3654 return child_id
3655 }
3656 return none
3657}
3658
3659// find_prim_method resolves find prim method information for c.
3660fn (g &FlatGen) find_prim_method(method string) string {
3661 if 'u8.${method}' in g.tc.fn_param_types {
3662 return c_name('u8.${method}')
3663 }
3664 if 'int.${method}' in g.tc.fn_param_types {
3665 return c_name('int.${method}')
3666 }
3667 if 'i64.${method}' in g.tc.fn_param_types {
3668 return c_name('i64.${method}')
3669 }
3670 if 'u32.${method}' in g.tc.fn_param_types {
3671 return c_name('u32.${method}')
3672 }
3673 if 'u64.${method}' in g.tc.fn_param_types {
3674 return c_name('u64.${method}')
3675 }
3676 return ''
3677}
3678
3679// find_alias_method converts find alias method data for c.
3680fn (g &FlatGen) find_alias_method(target string, method string) ?string {
3681 mut fallback := ''
3682 for alias, alias_target in g.tc.type_aliases {
3683 if alias_target != target {
3684 continue
3685 }
3686 alias_method := '${alias}.${method}'
3687 if alias_method !in g.tc.fn_param_types {
3688 if alias.contains('.') {
3689 short_method := '${alias.all_after_last('.')}.${method}'
3690 if short_method in g.tc.fn_param_types {
3691 return alias_method
3692 }
3693 }
3694 continue
3695 }
3696 if alias.contains('.') {
3697 return alias_method
3698 }
3699 if fallback.len == 0 {
3700 fallback = alias_method
3701 }
3702 }
3703 if fallback.len > 0 {
3704 return fallback
3705 }
3706 return none
3707}
3708
3709// gen_sum_variant_arg emits sum variant arg output for c.
3710fn (mut g FlatGen) gen_sum_variant_arg(arg_id flat.NodeId, expected types.Type) bool {
3711 actual0 := types.unwrap_pointer(g.tc.resolve_type(arg_id))
3712 mut actual := actual0
3713 if actual0 is types.Alias {
3714 actual = actual0.base_type
3715 }
3716 expected0 := expected
3717 mut expected_type := expected0
3718 if expected0 is types.Alias {
3719 expected_type = expected0.base_type
3720 }
3721 if expected_type is types.SumType {
3722 return false
3723 }
3724 if actual !is types.SumType {
3725 return false
3726 }
3727 sum_type := actual as types.SumType
3728 sum_name := sum_type.name
3729 variant := g.resolve_variant(sum_name, expected_type.name())
3730 variants := g.tc.sum_types[sum_name] or { return false }
3731 if variant !in variants {
3732 return false
3733 }
3734 is_ptr_arg := g.tc.resolve_type(arg_id) is types.Pointer
3735 is_ref_variant := g.variant_references_sum(variant, sum_name)
3736 if is_ref_variant {
3737 g.write('(*')
3738 }
3739 g.gen_expr(arg_id)
3740 if is_ptr_arg {
3741 g.write('->')
3742 } else {
3743 g.write('.')
3744 }
3745 g.write(g.sum_field_name(variant))
3746 if is_ref_variant {
3747 g.write(')')
3748 }
3749 return true
3750}
3751
3752// forward_decls supports forward decls handling for FlatGen.
3753fn (mut g FlatGen) forward_decls() {
3754 mut cur_module := ''
3755 mut cur_file := ''
3756 mut forwarded := map[string]bool{}
3757 for i in 0 .. g.a.nodes.len {
3758 node := g.a.nodes[i]
3759 kind_id := node_kind_id(node)
3760 if kind_id == 77 {
3761 cur_file = node.value
3762 g.tc.cur_file = cur_file
3763 cur_module = ''
3764 g.tc.cur_module = cur_module
3765 continue
3766 }
3767 if kind_id == 73 {
3768 cur_module = node.value
3769 g.tc.cur_file = cur_file
3770 g.tc.cur_module = cur_module
3771 continue
3772 }
3773
3774 is_entry_main := is_main_fn_in_main_module(cur_module, node.value) && g.test_files.len == 0
3775 if kind_id == 61 && !is_entry_main {
3776 if !g.should_emit_fn_node_in_module(node, i, cur_module) {
3777 continue
3778 }
3779 qfn := g.fn_c_name_in_module(cur_module, node.value)
3780 if forwarded[qfn] {
3781 continue
3782 }
3783 forwarded[qfn] = true
3784 g.tc.cur_file = cur_file
3785 g.tc.cur_module = cur_module
3786 ret_type := g.fn_node_return_type(node, cur_module)
3787 g.write(g.fn_return_type_name(ret_type))
3788 g.write(' ')
3789 g.write(qfn)
3790 g.write('(')
3791 g.write_fn_node_params(node)
3792 g.writeln(');')
3793 if export_name := g.export_fn_name_in_module(cur_module, node.value) {
3794 if !forwarded[export_name] {
3795 forwarded[export_name] = true
3796 g.write(g.fn_return_type_name(ret_type))
3797 g.write(' ')
3798 g.write(export_name)
3799 g.write('(')
3800 g.write_fn_node_params(node)
3801 g.writeln(');')
3802 }
3803 }
3804 } else if kind_id == 76
3805 && (node.value.starts_with('C.v_filelock_') || node.value.starts_with('v_filelock_')) {
3806 g.tc.cur_file = cur_file
3807 g.tc.cur_module = cur_module
3808 ret_type := g.tc.parse_type(node.typ)
3809 cfn := c_name(node.value)
3810 if forwarded[cfn] {
3811 continue
3812 }
3813 forwarded[cfn] = true
3814 g.write(g.optional_type_name(ret_type))
3815 g.write(' ')
3816 g.write(cfn)
3817 g.write('(')
3818 g.write_c_fn_node_params(node)
3819 g.writeln(');')
3820 }
3821 }
3822 g.writeln('')
3823}
3824
3825fn (mut g FlatGen) insert_cur_implicit_veb_ctx_param(node flat.Node) {
3826 if !g.fn_needs_implicit_veb_ctx(node) {
3827 return
3828 }
3829 insert_idx := g.fn_implicit_veb_ctx_insert_index(node)
3830 ctx_type := g.implicit_veb_ctx_type()
3831 mut names := []string{cap: g.cur_param_names.len + 1}
3832 mut type_values := []types.Type{cap: g.cur_param_type_values.len + 1}
3833 for i, name in g.cur_param_names {
3834 if i == insert_idx {
3835 names << 'ctx'
3836 type_values << ctx_type
3837 }
3838 names << name
3839 type_values << g.cur_param_type_values[i]
3840 }
3841 if insert_idx >= g.cur_param_names.len {
3842 names << 'ctx'
3843 type_values << ctx_type
3844 }
3845 g.cur_param_names = names
3846 g.cur_param_type_values = type_values
3847 g.cur_param_types['ctx'] = ctx_type
3848 g.tc.cur_scope.insert('ctx', ctx_type)
3849}
3850
3851fn (mut g FlatGen) fn_param_types_with_implicit_veb_ctx(node flat.Node, params []types.Type) []types.Type {
3852 if !g.fn_needs_implicit_veb_ctx(node) {
3853 return params
3854 }
3855 insert_idx := g.fn_implicit_veb_ctx_insert_index(node)
3856 ctx_type := g.implicit_veb_ctx_type()
3857 mut result := []types.Type{cap: params.len + 1}
3858 for i, param in params {
3859 if i == insert_idx {
3860 result << ctx_type
3861 }
3862 result << param
3863 }
3864 if insert_idx >= params.len {
3865 result << ctx_type
3866 }
3867 return result
3868}
3869
3870fn (mut g FlatGen) fn_needs_implicit_veb_ctx(node flat.Node) bool {
3871 return g.fn_returns_veb_result(node) && g.fn_has_receiver_param(node)
3872 && !g.fn_receiver_type_is_context(node) && !g.fn_has_param(node, 'ctx')
3873 && g.type_name_known_in_current_module('Context')
3874}
3875
3876// is_implicit_veb_ctx_param reports whether a callee parameter is the hidden
3877// veb `Context` pointer that callers do not supply explicitly.
3878fn (g &FlatGen) is_implicit_veb_ctx_param(pt types.Type) bool {
3879 if pt is types.Pointer {
3880 return pt.base_type.name().all_after_last('.') == 'Context'
3881 }
3882 return false
3883}
3884
3885// cur_scope_has_ctx reports whether the current function exposes a `ctx`
3886// variable (the implicit veb context) that can be forwarded to delegated calls.
3887fn (g &FlatGen) cur_scope_has_ctx() bool {
3888 if _ := g.tc.cur_scope.lookup('ctx') {
3889 return true
3890 }
3891 return false
3892}
3893
3894fn (g &FlatGen) type_name_known_in_current_module(name string) bool {
3895 qname := g.tc.qualify_name(name)
3896 return qname in g.struct_decl_infos || qname in g.tc.type_aliases || qname in g.tc.enum_names
3897 || qname in g.tc.sum_types || qname in g.tc.interface_names
3898}
3899
3900fn (g &FlatGen) type_name_known(name string) bool {
3901 qname := g.tc.qualify_name(name)
3902 return name in g.struct_decl_infos || qname in g.struct_decl_infos || name in g.tc.type_aliases
3903 || qname in g.tc.type_aliases || name in g.tc.enum_names || qname in g.tc.enum_names
3904 || name in g.tc.sum_types || qname in g.tc.sum_types || name in g.tc.interface_names
3905 || qname in g.tc.interface_names
3906}
3907
3908fn (mut g FlatGen) fn_returns_veb_result(node flat.Node) bool {
3909 if node.typ == 'veb.Result' {
3910 return true
3911 }
3912 ret := g.tc.parse_type(node.typ)
3913 return ret.name() == 'veb.Result'
3914}
3915
3916fn (g &FlatGen) fn_has_param(node flat.Node, name string) bool {
3917 for i in 0 .. node.children_count {
3918 p := g.a.child_node(&node, i)
3919 if p.kind == .param && p.value == name {
3920 return true
3921 }
3922 }
3923 return false
3924}
3925
3926fn (g &FlatGen) fn_implicit_veb_ctx_insert_index(node flat.Node) int {
3927 if g.fn_has_receiver_param(node) {
3928 return 1
3929 }
3930 return 0
3931}
3932
3933fn (g &FlatGen) fn_has_receiver_param(node flat.Node) bool {
3934 if !node.value.contains('.') || node.children_count == 0 {
3935 return false
3936 }
3937 first := g.a.child_node(&node, 0)
3938 if first.kind != .param || first.typ.len == 0 {
3939 return false
3940 }
3941 receiver := node.value.all_before_last('.').all_after_last('.')
3942 param_type := first.typ.trim_left('&').all_after_last('.')
3943 return receiver == param_type
3944}
3945
3946fn (g &FlatGen) fn_receiver_type_is_context(node flat.Node) bool {
3947 if !g.fn_has_receiver_param(node) {
3948 return false
3949 }
3950 first := g.a.child_node(&node, 0)
3951 return first.typ.trim_left('&').all_after_last('.') == 'Context'
3952}
3953
3954fn (mut g FlatGen) implicit_veb_ctx_type() types.Type {
3955 return g.tc.parse_type('mut Context')
3956}
3957
3958fn (mut g FlatGen) fn_node_return_type(node flat.Node, module_name string) types.Type {
3959 for candidate in g.fn_node_signature_names(node, module_name) {
3960 if rt := g.tc.fn_ret_types[candidate] {
3961 return rt
3962 }
3963 }
3964 if info := g.generic_receiver_method_call_info(node.value) {
3965 return info.return_type
3966 }
3967 return g.tc.parse_type(node.typ)
3968}
3969
3970fn (mut g FlatGen) fn_node_param_types(node flat.Node, module_name string) []types.Type {
3971 if g.fn_needs_implicit_veb_ctx(node) {
3972 return []types.Type{}
3973 }
3974 mut explicit_params := 0
3975 for i in 0 .. node.children_count {
3976 if g.a.child_node(&node, i).kind == .param {
3977 explicit_params++
3978 }
3979 }
3980 for candidate in g.fn_node_signature_names(node, module_name) {
3981 if params := g.tc.fn_param_types[candidate] {
3982 if params.len == explicit_params {
3983 return params
3984 }
3985 }
3986 }
3987 if info := g.generic_receiver_method_call_info(node.value) {
3988 if info.params.len == explicit_params {
3989 return info.params.clone()
3990 }
3991 }
3992 return []types.Type{}
3993}
3994
3995fn (g &FlatGen) generic_receiver_method_call_info(name string) ?types.CallInfo {
3996 if !name.contains('.') {
3997 return none
3998 }
3999 receiver := name.all_before_last('.')
4000 if !receiver.contains('[') || !receiver.contains(']') {
4001 return none
4002 }
4003 return g.tc.resolve_generic_struct_method(receiver, name.all_after_last('.'))
4004}
4005
4006fn (mut g FlatGen) fn_node_signature_names(node flat.Node, module_name string) []string {
4007 dotted_name := qualify_name_in_module(module_name, node.value)
4008 cname := g.fn_c_name_in_module(module_name, node.value)
4009 mut names := []string{}
4010 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
4011 names << dotted_name
4012 names << c_name(dotted_name)
4013 names << cname
4014 names << node.value
4015 names << c_name(node.value)
4016 } else {
4017 names << node.value
4018 names << c_name(node.value)
4019 names << dotted_name
4020 names << c_name(dotted_name)
4021 names << cname
4022 }
4023 mut deduped := []string{cap: names.len}
4024 for name in names {
4025 if name.len > 0 && name !in deduped {
4026 deduped << name
4027 }
4028 }
4029 return deduped
4030}
4031
4032// write_fn_node_params writes fn node params output for c.
4033fn (mut g FlatGen) write_fn_node_params(node flat.Node) {
4034 mut params_len := 0
4035 for i in 0 .. node.children_count {
4036 if g.a.child_node(&node, i).kind == .param {
4037 params_len++
4038 }
4039 }
4040 needs_implicit_ctx := g.fn_needs_implicit_veb_ctx(node)
4041 if needs_implicit_ctx {
4042 params_len++
4043 }
4044 if params_len == 0 {
4045 g.write('void')
4046 return
4047 }
4048 mut written := 0
4049 mut param_idx := 0
4050 mut implicit_ctx_written := false
4051 insert_implicit_ctx_after_first := needs_implicit_ctx && g.fn_has_receiver_param(node)
4052 typed_params := g.fn_node_param_types(node, g.tc.cur_module)
4053 concrete_optional_params := g.is_specialized_generic_fn_node(node)
4054 for i in 0 .. node.children_count {
4055 param_id := g.a.child(&node, i)
4056 p := g.a.node(param_id)
4057 if p.kind != .param {
4058 continue
4059 }
4060 pt := if param_idx < typed_params.len {
4061 typed_params[param_idx]
4062 } else {
4063 g.tc.parse_type(p.typ)
4064 }
4065 param_idx++
4066 ct := if concrete_optional_params && (pt is types.OptionType || pt is types.ResultType) {
4067 g.concrete_optional_type_name(pt)
4068 } else if pt is types.ArrayFixed {
4069 '${g.tc.c_type(pt.elem_type)}*'
4070 } else if pt is types.OptionType || pt is types.ResultType {
4071 g.optional_type_name(pt)
4072 } else {
4073 g.tc.c_type(pt)
4074 }
4075 if ct.starts_with('fn_ptr:') {
4076 g.write(g.resolve_fn_ptr_type(ct))
4077 } else {
4078 g.write(ct)
4079 }
4080 if p.value.len > 0 {
4081 g.write(' ')
4082 param_name := if p.value == '_' { '_${written}' } else { c_name(p.value) }
4083 g.write(param_name)
4084 }
4085 written++
4086 if insert_implicit_ctx_after_first && !implicit_ctx_written {
4087 if written < params_len {
4088 g.write(', ')
4089 }
4090 g.write_implicit_veb_ctx_param()
4091 written++
4092 implicit_ctx_written = true
4093 }
4094 if written < params_len {
4095 g.write(', ')
4096 }
4097 }
4098 if needs_implicit_ctx && !implicit_ctx_written {
4099 g.write_implicit_veb_ctx_param()
4100 }
4101}
4102
4103fn (g &FlatGen) is_specialized_generic_fn_node(node flat.Node) bool {
4104 return node.value.contains('[') || node.value.contains('_T_')
4105}
4106
4107fn (mut g FlatGen) concrete_optional_type_name(t types.Type) string {
4108 mut base_type := types.Type(types.void_)
4109 if t is types.OptionType {
4110 base_type = t.base_type
4111 } else if t is types.ResultType {
4112 base_type = t.base_type
4113 } else {
4114 return g.tc.c_type(t)
4115 }
4116 mut inner_ct := g.tc.c_type(base_type)
4117 if inner_ct.starts_with('fn_ptr:') {
4118 inner_ct = g.resolve_fn_ptr_type(inner_ct)
4119 }
4120 safe_name := inner_ct.replace('*', 'ptr').replace(' ', '_')
4121 opt_name := 'Optional_${safe_name}'
4122 g.needed_optional_types[opt_name] = inner_ct
4123 return opt_name
4124}
4125
4126fn (mut g FlatGen) write_implicit_veb_ctx_param() {
4127 pt := g.implicit_veb_ctx_type()
4128 g.write(g.tc.c_type(pt))
4129 g.write(' ctx')
4130}
4131
4132// write_c_fn_node_params writes c fn node params output for c.
4133fn (mut g FlatGen) write_c_fn_node_params(node flat.Node) {
4134 if node.children_count == 0 {
4135 g.write('void')
4136 return
4137 }
4138 mut written := 0
4139 for i in 0 .. node.children_count {
4140 param_id := g.a.child(&node, i)
4141 p := g.a.node(param_id)
4142 if p.kind != .param {
4143 continue
4144 }
4145 raw_typ := if p.typ.len > 0 { p.typ } else { p.value }
4146 if raw_typ.len == 0 {
4147 continue
4148 }
4149 pt := g.tc.parse_type(raw_typ)
4150 ct := if pt is types.OptionType || pt is types.ResultType {
4151 g.optional_type_name(pt)
4152 } else {
4153 g.tc.c_type(pt)
4154 }
4155 if written > 0 {
4156 g.write(', ')
4157 }
4158 if ct.starts_with('fn_ptr:') {
4159 g.write(g.resolve_fn_ptr_type(ct))
4160 } else {
4161 g.write(ct)
4162 }
4163 if p.typ.len > 0 && p.value.len > 0 {
4164 g.write(' ')
4165 param_name := if p.value == '_' { '_${written}' } else { c_name(p.value) }
4166 g.write(param_name)
4167 }
4168 written++
4169 }
4170 if written == 0 {
4171 g.write('void')
4172 }
4173}
4174
4175// fn_ptr_typedefs supports fn ptr typedefs handling for FlatGen.
4176fn (mut g FlatGen) fn_ptr_typedefs() {
4177 mut emitted := map[string]bool{}
4178 for {
4179 mut pending_encoded := []string{}
4180 mut pending_name := []string{}
4181 for encoded, name in g.fn_ptr_types {
4182 if emitted[encoded] {
4183 continue
4184 }
4185 pending_encoded << encoded
4186 pending_name << name
4187 }
4188 if pending_encoded.len == 0 {
4189 break
4190 }
4191 for i in 0 .. pending_encoded.len {
4192 g.emit_fn_ptr_typedef(pending_encoded[i], pending_name[i], mut emitted)
4193 }
4194 }
4195 if emitted.len > 0 {
4196 g.writeln('')
4197 }
4198}
4199
4200fn (mut g FlatGen) emit_fn_ptr_typedef(encoded string, name string, mut emitted map[string]bool) {
4201 if emitted[encoded] {
4202 return
4203 }
4204 emitted[encoded] = true
4205 ret, params := fn_ptr_typedef_parts(encoded)
4206 ret_ct := g.fn_ptr_return_ct(g.fn_ptr_typedef_type(ret, mut emitted))
4207 params_ct := g.fn_ptr_typedef_params(params, mut emitted)
4208 g.writeln('typedef ${ret_ct} (*${name})(${params_ct});')
4209}
4210
4211fn fn_ptr_typedef_parts(encoded string) (string, string) {
4212 payload := if encoded.starts_with('fn_ptr:') { encoded['fn_ptr:'.len..] } else { encoded }
4213 if payload.starts_with('fn_ptr:') {
4214 first_pipe_idx := payload.index('|') or { return payload, 'void' }
4215 rest := payload[first_pipe_idx + 1..]
4216 second_pipe_idx := rest.index('|') or { return payload, 'void' }
4217 split_idx := first_pipe_idx + 1 + second_pipe_idx
4218 return payload[..split_idx], payload[split_idx + 1..]
4219 }
4220 pipe_idx := payload.index('|') or { return payload, 'void' }
4221 return payload[..pipe_idx], payload[pipe_idx + 1..]
4222}
4223
4224fn (mut g FlatGen) fn_ptr_typedef_params(params string, mut emitted map[string]bool) string {
4225 clean := params.trim_space()
4226 if clean.len == 0 || clean == 'void' {
4227 return 'void'
4228 }
4229 mut out := []string{}
4230 for param in clean.split(',') {
4231 out << g.fn_ptr_typedef_type(param, mut emitted)
4232 }
4233 return out.join(', ')
4234}
4235
4236fn (mut g FlatGen) fn_ptr_typedef_type(typ string, mut emitted map[string]bool) string {
4237 mut clean := typ.trim_space()
4238 if clean.len == 0 {
4239 return 'void'
4240 }
4241 if clean.starts_with('fn_ptr:') {
4242 clean = fn_ptr_typedef_normalized(clean)
4243 name := g.resolve_fn_ptr_type(clean)
4244 g.emit_fn_ptr_typedef(clean, name, mut emitted)
4245 return name
4246 }
4247 if clean == 'Optional' {
4248 return 'struct Optional'
4249 }
4250 if clean.starts_with('Optional_') {
4251 return 'struct ${clean}'
4252 }
4253 if fn_ptr_typedef_is_generic_placeholder(clean) {
4254 return 'int'
4255 }
4256 return clean
4257}
4258
4259fn fn_ptr_typedef_normalized(typ string) string {
4260 clean := typ.trim_space()
4261 if !clean.starts_with('fn_ptr:') {
4262 return clean
4263 }
4264 payload := clean['fn_ptr:'.len..]
4265 if payload.contains('|') {
4266 return clean
4267 }
4268 return 'fn_ptr:${payload}|void'
4269}
4270
4271fn fn_ptr_typedef_is_generic_placeholder(typ string) bool {
4272 mut clean := typ.trim_space()
4273 for clean.ends_with('*') {
4274 clean = clean[..clean.len - 1].trim_space()
4275 }
4276 if clean.starts_with('struct ') {
4277 clean = clean['struct '.len..].trim_space()
4278 }
4279 short := if clean.contains('__') {
4280 clean.all_after_last('__')
4281 } else if clean.contains('.') {
4282 clean.all_after_last('.')
4283 } else {
4284 clean
4285 }
4286 if short in ['T', 'U', 'V', 'K', 'R'] {
4287 return true
4288 }
4289 return short.len == 1 && short[0] >= `A` && short[0] <= `Z`
4290}
4291
4292// multi_return_forward_decls forward-declares every multi-return struct that the
4293// generated C can reference by name. It must run before fn_ptr_typedefs(), because
4294// a function-pointer typedef may name a multi-return as its (by-value) return type
4295// — and a `typedef RET (*fp)(...)` only needs RET's tag declared, not its full
4296// layout. The full struct bodies are emitted later by multi_return_typedefs(),
4297// after the member struct definitions they depend on are available.
4298fn (mut g FlatGen) multi_return_forward_decls() {
4299 mut emitted := map[string]bool{}
4300 g.walk_multi_return_typedefs(mut emitted, true)
4301 // Also cover multi-returns reachable only as a fn-pointer return type: parallel
4302 // cgen preseeds fn-ptr types that the serial path never materializes, so their
4303 // return multi-returns may not appear among the function/expression types above.
4304 for encoded, _ in g.fn_ptr_types {
4305 ret, _ := fn_ptr_typedef_parts(encoded)
4306 if ret.starts_with('multi_return_') && ret !in emitted {
4307 emitted[ret] = true
4308 g.writeln('typedef struct ${ret} ${ret};')
4309 }
4310 }
4311 if emitted.len > 0 {
4312 g.writeln('')
4313 }
4314}
4315
4316// multi_return_typedefs emits the full struct definitions for multi-return types.
4317fn (mut g FlatGen) multi_return_typedefs() {
4318 mut emitted := map[string]bool{}
4319 g.walk_multi_return_typedefs(mut emitted, false)
4320 if emitted.len > 0 {
4321 g.writeln('')
4322 }
4323}
4324
4325// walk_multi_return_typedefs visits every multi-return type reachable from a
4326// function return type or an expression type and emits it via emit_multi_return_typedef.
4327// Shared by the forward-declaration and full-definition passes so both see the same
4328// set in the same (deterministic) order.
4329fn (mut g FlatGen) walk_multi_return_typedefs(mut emitted map[string]bool, forward_only bool) {
4330 for _, ret in g.tc.fn_ret_types {
4331 g.emit_multi_return_typedef(ret, mut emitted, forward_only)
4332 }
4333 mut cur_module := ''
4334 mut cur_file := ''
4335 for node in g.a.nodes {
4336 kind_id := node_kind_id(node)
4337 if kind_id == 77 {
4338 cur_file = node.value
4339 g.tc.cur_file = cur_file
4340 cur_module = ''
4341 g.tc.cur_module = cur_module
4342 continue
4343 }
4344 if kind_id == 73 {
4345 cur_module = node.value
4346 g.tc.cur_file = cur_file
4347 g.tc.cur_module = cur_module
4348 continue
4349 }
4350 g.tc.cur_file = cur_file
4351 g.tc.cur_module = cur_module
4352 // emit_multi_return_typedef only acts on (optionally `?`/`!`-wrapped) multi-return
4353 // types, whose string form always begins with `(`. Skip parse_type for everything
4354 // else — this ran on every node's type (~hundreds of thousands of parse_type calls).
4355 typ := node.typ
4356 if typ.len > 0 && (typ[0] == `(` || ((typ[0] == `?` || typ[0] == `!`) && typ.len > 1
4357 && typ[1] == `(`)) {
4358 g.emit_multi_return_typedef(g.tc.parse_type(typ), mut emitted, forward_only)
4359 }
4360 }
4361}
4362
4363// emit_multi_return_typedef emits one multi-return type: a forward declaration
4364// (`typedef struct NAME NAME;`) when forward_only, otherwise the full struct body
4365// (`struct NAME { ... };`). The two forms are paired — the forward decl provides the
4366// typedef name, the body completes the tagged struct.
4367fn (mut g FlatGen) emit_multi_return_typedef(ret types.Type, mut emitted map[string]bool, forward_only bool) {
4368 if ret is types.OptionType {
4369 g.emit_multi_return_typedef(ret.base_type, mut emitted, forward_only)
4370 return
4371 }
4372 if ret is types.ResultType {
4373 g.emit_multi_return_typedef(ret.base_type, mut emitted, forward_only)
4374 return
4375 }
4376 if ret is types.MultiReturn {
4377 name := g.tc.c_type(ret)
4378 if name in emitted {
4379 return
4380 }
4381 emitted[name] = true
4382 if forward_only {
4383 g.writeln('typedef struct ${name} ${name};')
4384 } else {
4385 g.writeln('struct ${name} {')
4386 for i, typ in ret.types {
4387 g.writeln('\t${g.tc.c_type(typ)} arg${i};')
4388 }
4389 g.writeln('};')
4390 }
4391 }
4392}
4393
4394// resolve_fn_ptr_type resolves resolve fn ptr type information for c.
4395fn (mut g FlatGen) resolve_fn_ptr_type(typ string) string {
4396 if typ in g.fn_ptr_types {
4397 return g.fn_ptr_types[typ]
4398 }
4399 name := '_fn_ptr_${g.fn_ptr_types.len}'
4400 g.fn_ptr_types[typ] = name
4401 return name
4402}
4403