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