vxx / vlib / v2_toberemoved / gen / cleanc / fn.v
13687 lines · 13253 sloc · 408.89 KB · c0624b274a458fe3e487d89ae9554c2e8c25bdb6
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module cleanc
6
7import v2.ast
8import v2.markused
9import v2.types
10import strings
11import os
12
13fn (mut g Gen) is_error_call_expr(expr ast.Expr) bool {
14 match expr {
15 ast.CallOrCastExpr {
16 if expr.lhs is ast.Ident {
17 name := sanitize_fn_ident(expr.lhs.name)
18 if name in ['error', 'error_posix', 'error_with_code', 'error_win32'] {
19 return true
20 }
21 }
22 }
23 ast.CallExpr {
24 if expr.lhs is ast.Ident {
25 name := sanitize_fn_ident(expr.lhs.name)
26 if name in ['error', 'error_posix', 'error_with_code', 'error_win32'] {
27 return true
28 }
29 }
30 // Check if the call returns IError
31 ret := g.get_call_return_type(expr.lhs, expr.args) or { '' }
32 if ret == 'IError' {
33 return true
34 }
35 }
36 else {}
37 }
38
39 // Also check environment type
40 expr_type := g.get_expr_type(expr)
41 if expr_type == 'IError' {
42 return true
43 }
44 return false
45}
46
47pub fn (mut g Gen) set_cached_init_calls(calls []string) {
48 g.cached_init_calls = calls.clone()
49}
50
51pub fn (mut g Gen) set_used_fn_keys(used map[string]bool) {
52 g.used_fn_keys = used.clone()
53}
54
55pub fn (mut g Gen) set_force_emit_fn_names(names []string) {
56 for name in names {
57 if name.len > 0 {
58 g.force_emit_fn_names[name] = true
59 }
60 }
61}
62
63pub fn (mut g Gen) add_called_fn_names(names []string) {
64 for name in names {
65 g.mark_called_fn_name(name)
66 }
67}
68
69fn (mut g Gen) mark_called_fn_name(name string) {
70 if name.len > 0 {
71 g.called_fn_names[name] = true
72 g.remember_called_specialized_name(name)
73 }
74}
75
76pub fn (g &Gen) external_called_fn_names() []string {
77 mut names := []string{}
78 for name, _ in g.called_fn_names {
79 if name.len == 0 || 'fn_${name}' in g.emitted_types {
80 continue
81 }
82 names << name
83 }
84 names.sort()
85 return names
86}
87
88fn (g &Gen) should_emit_fn_decl(module_name string, decl ast.FnDecl) bool {
89 if g.cached_init_calls.len > 0 && !g.should_emit_module(module_name) {
90 return true
91 }
92 if g.used_fn_keys.len == 0 || g.env == unsafe { nil } {
93 return true
94 }
95 if should_always_emit_for_markused(g.cur_file_name) {
96 return true
97 }
98 if module_name == 'builtin' && decl.name == 'print_backtrace' {
99 return true
100 }
101 if module_name == 'json2' && decl.name == 'enum_uses_json_as_number' {
102 return true
103 }
104 if module_name == 'time' && decl.is_method && decl.receiver.typ is ast.Ident
105 && decl.receiver.typ.name == 'Duration' {
106 return true
107 }
108 if module_name == 'sync' {
109 if decl.name in ['try_pop_priv', 'try_push_priv', 'new_channel_st', 'new_spin_lock'] {
110 return true
111 }
112 if decl.is_method && decl.name == 'try_wait' {
113 return true
114 }
115 }
116 if is_builtin_map_file(g.cur_file_name) && should_keep_builtin_map_decl(decl) {
117 return true
118 }
119 if is_builtin_string_file(g.cur_file_name) && should_keep_builtin_string_decl(decl) {
120 return true
121 }
122 if is_builtin_array_file(g.cur_file_name) && should_keep_builtin_array_decl(decl) {
123 return true
124 }
125 if decl.name.starts_with('__v_init_consts_') {
126 return true
127 }
128 // Module init/deinit functions are called from the synthesized test main,
129 // which runs after markused, so they won't be in used_fn_keys.
130 if !decl.is_method && !decl.is_static && (decl.name == 'init' || decl.name == 'deinit') {
131 return true
132 }
133 if decl.name == 'str' {
134 return true
135 }
136 if decl.name.ends_with('__str') {
137 if g.emit_files.len > 0 && g.cache_bundle_name != 'virtuals' {
138 return false
139 }
140 return true
141 }
142 if decl.name.starts_with('__sort_cmp_') {
143 if g.emit_files.len > 0 && g.cache_bundle_name == '' && decl.name !in g.force_emit_fn_names {
144 return false
145 }
146 return true
147 }
148 // Methods on array types ([]T) and other types with unresolvable receivers
149 // may produce 'unknown' receiver in the markused key, causing them to be
150 // incorrectly pruned. Always emit methods whose receiver can't be resolved.
151 // Also always emit methods on array receivers ([]T), since the markused
152 // key for these can differ between the walker and the gen lookup.
153 if decl.is_method {
154 key2 := markused.decl_key(module_name, decl, g.env)
155 if key2.contains('|unknown|') {
156 return true
157 }
158 if decl.name in ['+', '-', '*', '/', '%', '==', '!=', '<', '>', '<=', '>='] {
159 return true
160 }
161 if decl.receiver.typ is ast.Type && decl.receiver.typ is ast.ArrayType {
162 return true
163 }
164 }
165 if g.force_emit_fn_names.len > 0 {
166 c_name := if decl.is_method {
167 g.method_decl_c_name_for_module(module_name, decl)
168 } else if module_name.len > 0 && module_name != 'builtin' && module_name != 'main' {
169 '${module_name}__${sanitize_fn_ident(decl.name)}'
170 } else {
171 sanitize_fn_ident(decl.name)
172 }
173 if c_name in g.force_emit_fn_names {
174 return true
175 }
176 }
177 // Check if this function was force-requested by generated code (e.g. map str functions).
178 if g.force_emit_fn_names.len > 0 && decl.name == 'str' && decl.is_method {
179 c_name := g.method_decl_c_name_for_module(module_name, decl)
180 if c_name in g.force_emit_fn_names {
181 return true
182 }
183 }
184 key := markused.decl_key(module_name, decl, g.env)
185 return key in g.used_fn_keys
186}
187
188fn (mut g Gen) should_emit_fn_decl_cached(module_name string, decl ast.FnDecl) bool {
189 cache_key := if decl.pos.id > 0 {
190 '${module_name}:${decl.pos.id}:${decl.name}'
191 } else {
192 '${module_name}:${g.cur_file_name}:${decl.name}:${decl.is_method}:${decl.is_static}'
193 }
194 if cached := g.should_emit_fn_decl_cache[cache_key] {
195 return cached
196 }
197 result := g.transformed_specialization_belongs_to_cache(module_name, decl)
198 && g.should_emit_fn_decl(module_name, decl)
199 g.should_emit_fn_decl_cache[cache_key] = result
200 return result
201}
202
203fn (mut g Gen) transformed_specialization_belongs_to_cache(module_name string, decl ast.FnDecl) bool {
204 c_name := if decl.is_method {
205 g.method_decl_c_name_for_module(module_name, decl)
206 } else {
207 decl.name
208 }
209 if g.cache_bundle_name.len == 0 || (!decl.name.contains('_T_') && !c_name.contains('_T_')) {
210 return true
211 }
212 if decl.is_method && !g.transformed_specialization_type_expr_belongs_to_cache(decl.receiver.typ) {
213 return false
214 }
215 for param in decl.typ.params {
216 if !g.transformed_specialization_type_expr_belongs_to_cache(param.typ) {
217 return false
218 }
219 }
220 if decl.typ.return_type !is ast.EmptyExpr
221 && !g.transformed_specialization_type_expr_belongs_to_cache(decl.typ.return_type) {
222 return false
223 }
224 return true
225}
226
227fn (mut g Gen) transformed_specialization_type_expr_belongs_to_cache(expr ast.Expr) bool {
228 c_name := g.expr_type_to_c(expr).trim_space()
229 if c_name == '' {
230 return true
231 }
232 return g.generic_concrete_c_name_belongs_to_emit_modules(c_name)
233}
234
235fn (g &Gen) method_decl_c_name_for_module(module_name string, decl ast.FnDecl) string {
236 if !decl.is_method {
237 return ''
238 }
239 recv_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
240
241 if recv_type_name == '' {
242 return ''
243 }
244 qualified_recv := if module_name.len > 0 && module_name != 'builtin' && module_name != 'main'
245 && !recv_type_name.contains('__') {
246 '${module_name}__${recv_type_name}'
247 } else {
248 recv_type_name
249 }
250 return '${qualified_recv}__${sanitize_fn_ident(decl.name)}'
251}
252
253fn (mut g Gen) emit_cached_init_call_decls() {
254 if g.cached_init_calls.len == 0 {
255 return
256 }
257 for fn_name in g.cached_init_calls {
258 key := 'cached_init_decl_${fn_name}'
259 if key in g.emitted_types {
260 continue
261 }
262 g.emitted_types[key] = true
263 g.sb.writeln('void ${fn_name}(void);')
264 }
265 g.sb.writeln('')
266}
267
268fn (mut g Gen) ensure_tuple_alias_for_fn_return(node ast.FnDecl, ret_type string) {
269 tuple_name := tuple_payload_type_name(ret_type)
270 if tuple_name == '' || tuple_name in g.tuple_aliases {
271 return
272 }
273 // Resolve tuple element types from the Environment's function type metadata.
274 if g.env != unsafe { nil } {
275 fn_name := if node.is_method {
276 v_type_name := g.receiver_type_to_scope_name(node.receiver.typ)
277 if v_type_name != '' {
278 '${v_type_name}__${node.name}'
279 } else {
280 node.name
281 }
282 } else {
283 node.name
284 }
285 if fields := g.tuple_fields_from_env(fn_name) {
286 if fields.len > 0 {
287 g.tuple_aliases[tuple_name] = fields.clone()
288 }
289 return
290 }
291 }
292 mut tuple_types := []ast.Expr{}
293 if node.typ.return_type is ast.Type && node.typ.return_type is ast.TupleType {
294 tuple_types = (node.typ.return_type as ast.TupleType).types.clone()
295 } else if node.typ.return_type is ast.Type && node.typ.return_type is ast.OptionType {
296 opt_type := node.typ.return_type as ast.OptionType
297 if opt_type.base_type is ast.Type && opt_type.base_type is ast.TupleType {
298 tuple_types = (opt_type.base_type as ast.TupleType).types.clone()
299 }
300 } else if node.typ.return_type is ast.Type && node.typ.return_type is ast.ResultType {
301 res_type := node.typ.return_type as ast.ResultType
302 if res_type.base_type is ast.Type && res_type.base_type is ast.TupleType {
303 tuple_types = (res_type.base_type as ast.TupleType).types.clone()
304 }
305 }
306 if tuple_types.len == 0 {
307 return
308 }
309 mut fields := []string{cap: tuple_types.len}
310 for tuple_type in tuple_types {
311 c_type := g.expr_type_to_c(tuple_type)
312 if c_type == '' {
313 return
314 }
315 fields << c_type
316 }
317 if fields.len > 0 {
318 g.tuple_aliases[tuple_name] = fields.clone()
319 }
320}
321
322// tuple_fields_from_env looks up a function in the module scope and extracts
323// tuple element types from its return type.
324fn (mut g Gen) tuple_fields_from_env(fn_name string) ?[]string {
325 if g.env == unsafe { nil } {
326 return none
327 }
328 // Look up the function object in the module scope.
329 if mut mod_scope := g.env_scope(g.cur_module) {
330 if obj := mod_scope.lookup_parent(fn_name, 0) {
331 if obj is types.Fn {
332 fn_obj := obj as types.Fn
333 fn_typ := fn_obj.get_typ()
334 if fn_typ is types.FnType {
335 return g.extract_tuple_fields_from_return_type(fn_typ)
336 }
337 }
338 }
339 }
340 // Also check builtin scope for builtin functions.
341 if g.cur_module != 'builtin' {
342 if mut builtin_scope := g.env_scope('builtin') {
343 if obj := builtin_scope.lookup_parent(fn_name, 0) {
344 if obj is types.Fn {
345 fn_obj := obj as types.Fn
346 fn_typ := fn_obj.get_typ()
347 if fn_typ is types.FnType {
348 return g.extract_tuple_fields_from_return_type(fn_typ)
349 }
350 }
351 }
352 }
353 }
354 return none
355}
356
357fn (mut g Gen) extract_tuple_fields_from_return_type(fn_typ types.FnType) ?[]string {
358 ret_type := fn_typ.get_return_type() or { return none }
359 // Unwrap Option/Result wrappers to get the payload type.
360 inner := match ret_type {
361 types.OptionType { ret_type.base_type }
362 types.ResultType { ret_type.base_type }
363 else { ret_type }
364 }
365
366 if inner is types.Tuple {
367 tuple_types := inner.get_types()
368 mut fields := []string{cap: tuple_types.len}
369 for elem_type in tuple_types {
370 c := g.types_type_to_c(elem_type)
371 if c == '' {
372 return none
373 }
374 fields << c
375 }
376 return fields
377 }
378 return none
379}
380
381fn fixed_array_return_wrapper_name(ret_type string) string {
382 return '_v_${ret_type}'
383}
384
385fn (g &Gen) c_fn_return_type_from_v(ret_type string) string {
386 if ret_type.starts_with('Array_fixed_') {
387 return g.fixed_array_ret_wrappers[ret_type] or { fixed_array_return_wrapper_name(ret_type) }
388 }
389 return ret_type
390}
391
392fn (mut g Gen) c_callback_return_type_from_v(ret_type string) string {
393 if ret_type.starts_with('Array_fixed_') {
394 g.fixed_array_ret_wrappers[ret_type] = fixed_array_return_wrapper_name(ret_type)
395 }
396 return g.c_fn_return_type_from_v(ret_type)
397}
398
399fn (mut g Gen) infer_vector_return_type_from_stmts(stmts []ast.Stmt) string {
400 for stmt in stmts {
401 match stmt {
402 ast.ReturnStmt {
403 if stmt.exprs.len == 0 {
404 continue
405 }
406 ret_expr := stmt.exprs[0]
407 if ret_expr is ast.InitExpr {
408 init_type := g.expr_type_to_c(ret_expr.typ)
409 if vector_elem_type_for_name(init_type) != '' {
410 return init_type
411 }
412 }
413 mut expr_type := g.get_expr_type(ret_expr)
414 if expr_type == '' || expr_type == 'int' {
415 if raw := g.get_raw_type(ret_expr) {
416 expr_type = g.types_type_to_c(raw)
417 }
418 }
419 expr_type = expr_type.trim_right('*')
420 if vector_elem_type_for_name(expr_type) != '' {
421 return expr_type
422 }
423 }
424 ast.BlockStmt {
425 inferred := g.infer_vector_return_type_from_stmts(stmt.stmts)
426 if inferred != '' {
427 return inferred
428 }
429 }
430 ast.ForStmt {
431 inferred := g.infer_vector_return_type_from_stmts(stmt.stmts)
432 if inferred != '' {
433 return inferred
434 }
435 }
436 ast.ExprStmt {
437 if stmt.expr is ast.IfExpr {
438 inferred := g.infer_vector_return_type_from_stmts(stmt.expr.stmts)
439 if inferred != '' {
440 return inferred
441 }
442 if stmt.expr.else_expr is ast.IfExpr {
443 else_inferred :=
444 g.infer_vector_return_type_from_stmts(stmt.expr.else_expr.stmts)
445 if else_inferred != '' {
446 return else_inferred
447 }
448 }
449 }
450 }
451 else {}
452 }
453 }
454 return ''
455}
456
457fn (mut g Gen) collect_fn_signatures() {
458 if g.has_flat() {
459 for i in 0 .. g.flat.files.len {
460 fc := g.flat.file_cursor(i)
461 g.set_file_cursor_module(fc)
462 stmts := fc.stmts()
463 for j in 0 .. stmts.len() {
464 stmt := stmts.at(j)
465 if stmt.kind() != .stmt_fn_decl {
466 continue
467 }
468 g.collect_fn_signature_from_cursor(stmt)
469 }
470 }
471 return
472 }
473 for file in g.files {
474 g.set_file_module(file)
475 for stmt in file.stmts {
476 if !stmt_has_valid_data(stmt) {
477 continue
478 }
479 match stmt {
480 ast.FnDecl {
481 g.collect_fn_signature_from_decl(stmt, stmt.stmts.len)
482 }
483 else {}
484 }
485 }
486 }
487}
488
489fn (mut g Gen) collect_fn_signature_from_cursor(stmt ast.Cursor) {
490 decl := stmt.fn_decl_signature()
491 body_len := stmt.list_at(3).len()
492 if body_len > 0 && g.fn_signature_collection_needs_body(decl) {
493 g.collect_fn_signature_from_body_source(decl, body_len, stmt, true)
494 return
495 }
496 g.collect_fn_signature_from_decl(decl, body_len)
497}
498
499fn (mut g Gen) fn_signature_collection_needs_body(decl ast.FnDecl) bool {
500 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
501 return false
502 }
503 if decl.language == .js || g.should_skip_backend_generic_fn(decl) {
504 return false
505 }
506 if g.generic_fn_param_names(decl).len > 0 || receiver_generic_param_names(decl).len > 0 {
507 return true
508 }
509 return decl.typ.return_type !is ast.EmptyExpr
510 && expr_has_generic_placeholder(decl.typ.return_type)
511}
512
513fn (mut g Gen) collect_fn_signature_from_decl(decl ast.FnDecl, body_len int) {
514 g.collect_fn_signature_from_body_source(decl, body_len, ast.Cursor{}, false)
515}
516
517fn (mut g Gen) scan_fn_body_for_generic_types_from_source(decl ast.FnDecl, body_cursor ast.Cursor, use_cursor bool, spec_name string) {
518 if use_cursor {
519 g.scan_fn_body_cursor_for_generic_types(body_cursor, decl, spec_name)
520 } else {
521 g.scan_fn_body_for_generic_types(decl, spec_name)
522 }
523}
524
525fn (mut g Gen) collect_fn_signature_from_body_source(decl ast.FnDecl, body_len int, body_cursor ast.Cursor, use_cursor bool) {
526 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
527 return
528 }
529 if decl.language == .js {
530 return
531 }
532 if g.should_skip_backend_generic_fn(decl) {
533 return
534 }
535 if g.generic_fn_param_names(decl).len > 0 {
536 prev_generic_types := g.active_generic_types.clone()
537 prev_fn_name := g.cur_fn_name
538 prev_fn_c_name := g.cur_fn_c_name
539 prev_fn_scope := g.cur_fn_scope
540 prev_runtime_local_types := g.runtime_local_types.clone()
541 prev_runtime_decl_types := g.runtime_decl_types.clone()
542 prev_not_local_var_cache := g.not_local_var_cache.clone()
543 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
544 for spec in g.generic_fn_specializations_for_emit_scope_with_receiver_bindings(decl) {
545 g.active_generic_types = spec.generic_types.clone()
546 g.cur_fn_name = decl.name
547 g.cur_fn_c_name = spec.name
548 g.cur_fn_scope = unsafe { nil }
549 g.runtime_local_types = map[string]string{}
550 g.runtime_decl_types = map[string]string{}
551 g.not_local_var_cache = map[string]bool{}
552 g.cur_fn_generic_params = map[string]string{}
553 scope_fn_name := if decl.is_method {
554 v_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
555 if v_type_name != '' {
556 '${v_type_name}__${decl.name}'
557 } else {
558 decl.name
559 }
560 } else {
561 decl.name
562 }
563 if g.env != unsafe { nil } {
564 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
565 g.cur_fn_scope = fn_scope
566 }
567 }
568 // Function-pointer fields can reference another generic function value before that
569 // function's signature is collected (veb.run_new assigns parallel_request_handler[A, X]).
570 g.seed_fn_scan_runtime_types(decl, spec.name)
571 g.scan_fn_body_for_generic_types_from_source(decl, body_cursor, use_cursor, spec.name)
572 g.register_fn_signature(decl, spec.name)
573 }
574 g.active_generic_types = prev_generic_types.clone()
575 g.cur_fn_name = prev_fn_name
576 g.cur_fn_c_name = prev_fn_c_name
577 g.cur_fn_scope = prev_fn_scope
578 g.runtime_local_types = prev_runtime_local_types.clone()
579 g.runtime_decl_types = prev_runtime_decl_types.clone()
580 g.not_local_var_cache = prev_not_local_var_cache.clone()
581 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
582 return
583 }
584 recv_gp := receiver_generic_param_names(decl)
585 if recv_gp.len > 0 {
586 all_bindings := g.get_all_receiver_generic_bindings(decl)
587 if all_bindings.len > 0 {
588 prev_gt := g.active_generic_types.clone()
589 prev_fn_name := g.cur_fn_name
590 prev_fn_c_name := g.cur_fn_c_name
591 prev_fn_scope := g.cur_fn_scope
592 prev_runtime_local_types := g.runtime_local_types.clone()
593 prev_runtime_decl_types := g.runtime_decl_types.clone()
594 prev_not_local_var_cache := g.not_local_var_cache.clone()
595 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
596 prev_generic_call_spec_scan_only := g.generic_call_spec_scan_only
597 for bi in all_bindings {
598 g.active_generic_types = bi.clone()
599 gfn := g.get_fn_name(decl)
600 if gfn != '' {
601 g.cur_fn_name = decl.name
602 g.cur_fn_c_name = gfn
603 g.cur_fn_scope = unsafe { nil }
604 g.runtime_local_types = map[string]string{}
605 g.runtime_decl_types = map[string]string{}
606 g.not_local_var_cache = map[string]bool{}
607 g.cur_fn_generic_params = map[string]string{}
608 scope_fn_name := if decl.is_method {
609 v_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
610 if v_type_name != '' {
611 '${v_type_name}__${decl.name}'
612 } else {
613 decl.name
614 }
615 } else {
616 decl.name
617 }
618 if g.env != unsafe { nil } {
619 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
620 g.cur_fn_scope = fn_scope
621 }
622 }
623 g.seed_fn_scan_runtime_types(decl, gfn)
624 g.generic_call_spec_scan_only = true
625 g.scan_fn_body_for_generic_types_from_source(decl, body_cursor, use_cursor, gfn)
626 g.generic_call_spec_scan_only = prev_generic_call_spec_scan_only
627 g.register_fn_signature(decl, gfn)
628 }
629 }
630 g.active_generic_types = prev_gt.clone()
631 g.cur_fn_name = prev_fn_name
632 g.cur_fn_c_name = prev_fn_c_name
633 g.cur_fn_scope = prev_fn_scope
634 g.runtime_local_types = prev_runtime_local_types.clone()
635 g.runtime_decl_types = prev_runtime_decl_types.clone()
636 g.not_local_var_cache = prev_not_local_var_cache.clone()
637 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
638 g.generic_call_spec_scan_only = prev_generic_call_spec_scan_only
639 return
640 } else if bindings := g.get_receiver_generic_bindings(decl) {
641 prev_gt := g.active_generic_types.clone()
642 prev_fn_name := g.cur_fn_name
643 prev_fn_c_name := g.cur_fn_c_name
644 prev_fn_scope := g.cur_fn_scope
645 prev_runtime_local_types := g.runtime_local_types.clone()
646 prev_runtime_decl_types := g.runtime_decl_types.clone()
647 prev_not_local_var_cache := g.not_local_var_cache.clone()
648 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
649 prev_generic_call_spec_scan_only := g.generic_call_spec_scan_only
650 g.active_generic_types = bindings.clone()
651 gfn := g.get_fn_name(decl)
652 if gfn != '' {
653 g.cur_fn_name = decl.name
654 g.cur_fn_c_name = gfn
655 g.cur_fn_scope = unsafe { nil }
656 g.runtime_local_types = map[string]string{}
657 g.runtime_decl_types = map[string]string{}
658 g.not_local_var_cache = map[string]bool{}
659 g.cur_fn_generic_params = map[string]string{}
660 scope_fn_name := if decl.is_method {
661 v_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
662 if v_type_name != '' {
663 '${v_type_name}__${decl.name}'
664 } else {
665 decl.name
666 }
667 } else {
668 decl.name
669 }
670 if g.env != unsafe { nil } {
671 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
672 g.cur_fn_scope = fn_scope
673 }
674 }
675 g.seed_fn_scan_runtime_types(decl, gfn)
676 g.generic_call_spec_scan_only = true
677 g.scan_fn_body_for_generic_types_from_source(decl, body_cursor, use_cursor, gfn)
678 g.generic_call_spec_scan_only = prev_generic_call_spec_scan_only
679 g.register_fn_signature(decl, gfn)
680 }
681 g.active_generic_types = prev_gt.clone()
682 g.cur_fn_name = prev_fn_name
683 g.cur_fn_c_name = prev_fn_c_name
684 g.cur_fn_scope = prev_fn_scope
685 g.runtime_local_types = prev_runtime_local_types.clone()
686 g.runtime_decl_types = prev_runtime_decl_types.clone()
687 g.not_local_var_cache = prev_not_local_var_cache.clone()
688 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
689 g.generic_call_spec_scan_only = prev_generic_call_spec_scan_only
690 return
691 }
692 return
693 }
694 mut fn_name := ''
695 if body_len == 0 && decl.language == .c {
696 // Keep raw symbol names for C / system declaration-only functions.
697 fn_name = sanitize_fn_ident(decl.name)
698 } else {
699 fn_name = g.get_fn_name(decl)
700 }
701 g.register_fn_signature(decl, fn_name)
702}
703
704fn (mut g Gen) collect_fn_signatures_to_fixed_point() {
705 for _ in 0 .. 8 {
706 before_late_specs := g.late_generic_spec_count()
707 before_signatures := g.fn_return_types.len + g.fn_param_is_ptr.len + g.fn_param_types.len
708 before_options := g.option_aliases.len
709 before_results := g.result_aliases.len
710 g.build_generic_spec_index()
711 g.collect_fn_signatures()
712 if g.late_generic_spec_count() == before_late_specs
713 && g.fn_return_types.len + g.fn_param_is_ptr.len + g.fn_param_types.len == before_signatures
714 && g.option_aliases.len == before_options && g.result_aliases.len == before_results {
715 break
716 }
717 }
718 g.build_generic_spec_index()
719}
720
721fn (mut g Gen) register_fn_signature(node ast.FnDecl, fn_name string) {
722 if fn_name == '' {
723 return
724 }
725 saved_cur_module := g.cur_module
726 saved_fn_scope := g.cur_fn_scope
727 defer {
728 g.cur_module = saved_cur_module
729 g.cur_fn_scope = saved_fn_scope
730 }
731 sig_module := module_prefix_from_fn_name(fn_name)
732 if sig_module.len > 0 && sig_module[0] >= `a` && sig_module[0] <= `z` {
733 g.cur_module = sig_module
734 }
735 g.cur_fn_scope = unsafe { nil }
736 if g.env != unsafe { nil } {
737 scope_fn_name := if node.is_method {
738 v_type_name := g.receiver_type_to_scope_name(node.receiver.typ)
739 if v_type_name != '' {
740 '${v_type_name}__${node.name}'
741 } else {
742 fn_name
743 }
744 } else {
745 node.name
746 }
747 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
748 g.cur_fn_scope = fn_scope
749 }
750 }
751 // If this function has an @[export] attribute, map the V-qualified
752 // name to the export name so call sites resolve correctly.
753 for attr in node.attributes {
754 if attr.name == 'export' {
755 if attr.value is ast.StringLiteral {
756 export_name := attr.value.value.trim('\'"')
757 if export_name.len > 0 {
758 v_name := sanitize_fn_ident(node.name)
759 v_qualified := if g.cur_module != '' && g.cur_module != 'main'
760 && g.cur_module != 'builtin' {
761 '${g.cur_module}__${v_name}'
762 } else {
763 v_name
764 }
765 if v_qualified != export_name {
766 g.export_fn_names[v_qualified] = export_name
767 }
768 }
769 }
770 }
771 }
772 is_program_main := g.is_program_main_fn(&node)
773 mut ret_type := if is_program_main {
774 'int'
775 } else if node.typ.return_type !is ast.EmptyExpr {
776 g.signature_expr_type_to_c(node.typ.return_type)
777 } else {
778 'void'
779 }
780 ret_type = normalize_signature_type_name(ret_type, 'void')
781 if ret_type == 'int' && node.typ.return_type !is ast.EmptyExpr
782 && expr_has_generic_placeholder(node.typ.return_type) {
783 inferred := g.infer_vector_return_type_from_stmts(node.stmts)
784 if inferred != '' {
785 ret_type = inferred
786 }
787 }
788 if ret_type.starts_with('_option_') || ret_type.starts_with('_result_') {
789 g.register_alias_type(ret_type)
790 }
791 if ret_type.starts_with('Array_fixed_') {
792 g.fixed_array_ret_wrappers[ret_type] = fixed_array_return_wrapper_name(ret_type)
793 }
794 g.ensure_tuple_alias_for_fn_return(node, ret_type)
795 mut params := []bool{}
796 mut param_types := []string{}
797 if node.is_method && !node.is_static && node.receiver.name != '' {
798 recv_base_type := if concrete_receiver := g.receiver_generic_instance_c_name_for_active_bindings(node) {
799 if param_type_is_pointer_expr(node.receiver.typ) && !concrete_receiver.ends_with('*') {
800 concrete_receiver + '*'
801 } else {
802 concrete_receiver
803 }
804 } else {
805 g.signature_expr_type_to_c(node.receiver.typ)
806 }
807 recv_type := normalize_signature_type_name(recv_base_type, 'void*')
808 params << (node.receiver.is_mut || recv_type.ends_with('*'))
809 param_types << if node.receiver.is_mut && !recv_type.ends_with('*') {
810 recv_type + '*'
811 } else {
812 recv_type
813 }
814 }
815 if g.needs_implicit_veb_ctx_param(&node, fn_name) {
816 params << true
817 param_types << g.implicit_veb_ctx_c_type()
818 }
819 for param in node.typ.params {
820 param_type := normalize_signature_type_name(g.fn_param_signature_c_type(param), 'int')
821 params << (param.is_mut || param_type.ends_with('*'))
822 param_types << if param.is_mut && !param_type.ends_with('*') {
823 param_type + '*'
824 } else {
825 param_type
826 }
827 }
828 g.fn_param_is_ptr[fn_name] = params
829 g.fn_param_types[fn_name] = param_types
830 g.fn_return_types[fn_name] = ret_type
831 if node.language == .v {
832 g.v_fn_return_types[fn_name] = ret_type
833 }
834 if node.is_method && g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin'
835 && !fn_name.starts_with(g.cur_module + '__') {
836 recv_type := g.expr_type_to_c(node.receiver.typ).trim_right('*')
837 if recv_type != '' && !recv_type.contains('__') && g.is_module_local_type(recv_type) {
838 qualified_name := '${g.cur_module}__${fn_name}'
839 g.fn_param_is_ptr[qualified_name] = params.clone()
840 g.fn_param_types[qualified_name] = param_types.clone()
841 g.fn_return_types[qualified_name] = ret_type
842 if node.language == .v {
843 g.v_fn_return_types[qualified_name] = ret_type
844 }
845 }
846 }
847 g.remember_specialized_fn_base(fn_name)
848}
849
850fn (mut g Gen) remember_specialized_fn_base(fn_name string) {
851 if !fn_name.contains('_T_') {
852 return
853 }
854 base_name := fn_name.all_before('_T_')
855 if base_name != '' {
856 g.specialized_fn_bases[base_name] = true
857 }
858 g.remember_specialized_receiver_method(fn_name, base_name)
859}
860
861fn (mut g Gen) remember_specialized_receiver_method(fn_name string, receiver_base string) {
862 if receiver_base == '' {
863 return
864 }
865 generic_tail := fn_name.all_after('_T_')
866 if !generic_tail.contains('__') {
867 return
868 }
869 method_name := fn_name.all_after_last('__')
870 if method_name == '' {
871 return
872 }
873 key := specialized_receiver_method_key(receiver_base, method_name)
874 g.specialized_receiver_method_miss.delete(key)
875 if key in g.specialized_receiver_method_ambiguous {
876 return
877 }
878 if existing := g.specialized_receiver_methods[key] {
879 if existing != fn_name {
880 g.specialized_receiver_methods.delete(key)
881 g.specialized_receiver_method_ambiguous[key] = true
882 }
883 return
884 }
885 g.specialized_receiver_methods[key] = fn_name
886}
887
888fn specialized_receiver_method_key(receiver_type string, method_name string) string {
889 return '${receiver_type}|${method_name}'
890}
891
892fn (mut g Gen) ensure_specialized_receiver_method_index() {
893 if g.specialized_receiver_method_indexed {
894 return
895 }
896 for fn_name, _ in g.fn_return_types {
897 g.remember_specialized_fn_base(fn_name)
898 }
899 for fn_name, _ in g.fn_param_is_ptr {
900 g.remember_specialized_fn_base(fn_name)
901 }
902 g.specialized_receiver_method_indexed = true
903}
904
905fn (mut g Gen) needs_implicit_veb_ctx_param(node &ast.FnDecl, fn_name string) bool {
906 if node.receiver.name == 'ctx' {
907 return false
908 }
909 for param in node.typ.params {
910 if param.name == 'ctx' {
911 return false
912 }
913 }
914 mut ret_type := if fn_ret := g.fn_return_types[fn_name] {
915 fn_ret
916 } else if node.typ.return_type !is ast.EmptyExpr {
917 g.expr_type_to_c(node.typ.return_type)
918 } else {
919 'void'
920 }
921 ret_type = normalize_signature_type_name(ret_type, 'void')
922 if ret_type !in ['veb__Result', 'veb.Result', 'Result'] {
923 return false
924 }
925 if g.cur_fn_scope != unsafe { nil } {
926 if _ := g.cur_fn_scope.lookup_parent('ctx', 0) {
927 return true
928 }
929 }
930 return false
931}
932
933fn (mut g Gen) implicit_veb_ctx_c_type() string {
934 if g.cur_fn_scope != unsafe { nil } {
935 if ctx_type := g.cur_fn_scope.lookup_var_type('ctx') {
936 c_type := normalize_signature_type_name(g.types_type_to_c(ctx_type), 'Context*')
937 if c_type != '' {
938 return c_type
939 }
940 }
941 }
942 return 'Context*'
943}
944
945fn collect_generic_placeholder_names_from_expr(expr ast.Expr, mut seen map[string]bool, mut out []string) {
946 match expr {
947 ast.Ident {
948 if is_generic_placeholder_type_name(expr.name) && expr.name !in seen {
949 seen[expr.name] = true
950 out << expr.name
951 }
952 }
953 ast.PrefixExpr {
954 collect_generic_placeholder_names_from_expr(expr.expr, mut seen, mut out)
955 }
956 ast.ModifierExpr {
957 collect_generic_placeholder_names_from_expr(expr.expr, mut seen, mut out)
958 }
959 ast.ParenExpr {
960 collect_generic_placeholder_names_from_expr(expr.expr, mut seen, mut out)
961 }
962 ast.SelectorExpr {
963 collect_generic_placeholder_names_from_expr(expr.lhs, mut seen, mut out)
964 }
965 ast.GenericArgOrIndexExpr {
966 collect_generic_placeholder_names_from_expr(expr.lhs, mut seen, mut out)
967 collect_generic_placeholder_names_from_expr(expr.expr, mut seen, mut out)
968 }
969 ast.GenericArgs {
970 collect_generic_placeholder_names_from_expr(expr.lhs, mut seen, mut out)
971 for arg in expr.args {
972 collect_generic_placeholder_names_from_expr(arg, mut seen, mut out)
973 }
974 }
975 ast.Type {
976 match expr {
977 ast.ArrayType {
978 collect_generic_placeholder_names_from_expr(expr.elem_type, mut seen, mut out)
979 }
980 ast.ArrayFixedType {
981 collect_generic_placeholder_names_from_expr(expr.elem_type, mut seen, mut out)
982 collect_generic_placeholder_names_from_expr(expr.len, mut seen, mut out)
983 }
984 ast.MapType {
985 collect_generic_placeholder_names_from_expr(expr.key_type, mut seen, mut out)
986 collect_generic_placeholder_names_from_expr(expr.value_type, mut seen, mut out)
987 }
988 ast.PointerType {
989 collect_generic_placeholder_names_from_expr(expr.base_type, mut seen, mut out)
990 }
991 ast.OptionType {
992 collect_generic_placeholder_names_from_expr(expr.base_type, mut seen, mut out)
993 }
994 ast.ResultType {
995 collect_generic_placeholder_names_from_expr(expr.base_type, mut seen, mut out)
996 }
997 ast.TupleType {
998 for typ in expr.types {
999 collect_generic_placeholder_names_from_expr(typ, mut seen, mut out)
1000 }
1001 }
1002 ast.FnType {
1003 for param in expr.params {
1004 collect_generic_placeholder_names_from_expr(param.typ, mut seen, mut out)
1005 }
1006 if expr.return_type !is ast.EmptyExpr {
1007 collect_generic_placeholder_names_from_expr(expr.return_type, mut seen, mut
1008 out)
1009 }
1010 }
1011 ast.GenericType {
1012 // Recurse into params (e.g. LinkedList[T] → extract T)
1013 for param in expr.params {
1014 collect_generic_placeholder_names_from_expr(param, mut seen, mut out)
1015 }
1016 }
1017 else {}
1018 }
1019 }
1020 else {}
1021 }
1022}
1023
1024fn (g &Gen) generic_fn_param_names(node ast.FnDecl) []string {
1025 mut names := []string{}
1026 for gp in node.typ.generic_params {
1027 if gp is ast.Ident && gp.name != '' {
1028 names << gp.name
1029 }
1030 }
1031 return names
1032}
1033
1034fn (g &Gen) should_skip_backend_generic_fn(node ast.FnDecl) bool {
1035 return g.generic_fn_param_names(node).len > 0 || receiver_generic_param_names(node).len > 0
1036}
1037
1038// receiver_generic_param_names collects generic placeholder names from a
1039// method's receiver type (e.g. LinkedList[T] → ['T']). These params are
1040// inherited from the struct definition, not the function's own generic_params.
1041fn receiver_generic_param_names(node ast.FnDecl) []string {
1042 if !node.is_method {
1043 return []string{}
1044 }
1045 mut seen := map[string]bool{}
1046 mut names := []string{}
1047 if node.receiver.typ !is ast.EmptyExpr {
1048 collect_generic_placeholder_names_from_expr(node.receiver.typ, mut seen, mut names)
1049 }
1050 return names
1051}
1052
1053// get_receiver_generic_bindings returns the concrete type bindings for a method
1054// on a generic struct by looking up the struct's recorded bindings from
1055// GenericType instantiations (e.g. LinkedList[ValueInfo] → {T: ValueInfo}).
1056fn (mut g Gen) get_receiver_generic_bindings(node ast.FnDecl) ?map[string]types.Type {
1057 if !node.is_method {
1058 return none
1059 }
1060 // Use the receiver's generic base name directly. Calling expr_type_to_c on
1061 // LinkedList[T] without active bindings can resolve T through the fallback
1062 // placeholder type and point method emission at a stray specialization.
1063 recv_c_name := g.receiver_generic_base_c_name(node)
1064 if recv_c_name == '' {
1065 return none
1066 }
1067 if instances := g.generic_struct_instances[recv_c_name] {
1068 if instances.len > 0 {
1069 return instances[0].bindings
1070 }
1071 }
1072 if bindings := g.generic_struct_bindings[recv_c_name] {
1073 return bindings
1074 }
1075 // Also try with current module prefix
1076 if !recv_c_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
1077 && g.cur_module != 'builtin' {
1078 qualified := '${g.cur_module}__${recv_c_name}'
1079 if instances := g.generic_struct_instances[qualified] {
1080 if instances.len > 0 {
1081 return instances[0].bindings
1082 }
1083 }
1084 if bindings := g.generic_struct_bindings[qualified] {
1085 return bindings
1086 }
1087 }
1088 return none
1089}
1090
1091// get_all_receiver_generic_bindings returns ALL concrete type binding sets for a method
1092// on a generic struct (for multi-instantiation, e.g. LinkedList[ValueInfo] AND LinkedList[StructFieldInfo]).
1093fn (mut g Gen) get_all_receiver_generic_bindings(node ast.FnDecl) []map[string]types.Type {
1094 instances := g.get_all_receiver_generic_instances(node)
1095 if instances.len == 0 {
1096 return []
1097 }
1098 mut all_bindings := []map[string]types.Type{cap: instances.len}
1099 for inst in instances {
1100 all_bindings << inst.bindings
1101 }
1102 return all_bindings
1103}
1104
1105fn (mut g Gen) get_all_receiver_generic_instances(node ast.FnDecl) []GenericStructInstance {
1106 if !node.is_method {
1107 return []
1108 }
1109 recv_c_name := g.receiver_generic_base_c_name(node)
1110 if recv_c_name == '' {
1111 return []
1112 }
1113 mut struct_name := recv_c_name
1114 if !struct_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
1115 && g.cur_module != 'builtin' {
1116 struct_name = '${g.cur_module}__${recv_c_name}'
1117 }
1118 if instances := g.generic_struct_instances[struct_name] {
1119 if instances.len > 0 {
1120 return instances
1121 }
1122 }
1123 if struct_name != recv_c_name {
1124 if instances := g.generic_struct_instances[recv_c_name] {
1125 if instances.len > 0 {
1126 return instances
1127 }
1128 }
1129 }
1130 return []
1131}
1132
1133fn (mut g Gen) receiver_generic_base_c_name(node ast.FnDecl) string {
1134 if !node.is_method || node.receiver.typ is ast.EmptyExpr {
1135 return ''
1136 }
1137 return g.receiver_generic_base_c_name_from_expr(node.receiver.typ).trim_right('*')
1138}
1139
1140fn (mut g Gen) receiver_generic_base_c_name_from_expr(expr ast.Expr) string {
1141 match expr {
1142 ast.GenericArgOrIndexExpr {
1143 return g.expr_type_to_c(expr.lhs)
1144 }
1145 ast.GenericArgs {
1146 return g.expr_type_to_c(expr.lhs)
1147 }
1148 ast.PrefixExpr {
1149 if expr.op in [.amp, .mul] {
1150 return g.receiver_generic_base_c_name_from_expr(expr.expr)
1151 }
1152 }
1153 ast.ModifierExpr {
1154 return g.receiver_generic_base_c_name_from_expr(expr.expr)
1155 }
1156 ast.Type {
1157 match expr {
1158 ast.PointerType {
1159 return g.receiver_generic_base_c_name_from_expr(expr.base_type)
1160 }
1161 ast.GenericType {
1162 return g.expr_type_to_c(expr.name)
1163 }
1164 else {}
1165 }
1166 }
1167 else {}
1168 }
1169
1170 mut name := g.expr_type_to_c(expr)
1171 if name.contains('_T_') {
1172 name = name.all_before('_T_')
1173 }
1174 return name
1175}
1176
1177fn (mut g Gen) receiver_generic_instance_c_name_for_active_bindings(node ast.FnDecl) ?string {
1178 recv_params := receiver_generic_param_names(node)
1179 if recv_params.len == 0 || g.active_generic_types.len == 0 {
1180 return none
1181 }
1182 base_name := g.receiver_generic_base_c_name(node)
1183 if base_name == '' {
1184 return none
1185 }
1186 mut instances := g.generic_struct_instances[base_name]
1187 if instances.len == 0 && !base_name.contains('__') && g.cur_module != ''
1188 && g.cur_module != 'main' && g.cur_module != 'builtin' {
1189 instances = g.generic_struct_instances['${g.cur_module}__${base_name}']
1190 }
1191 for inst in instances {
1192 mut matches := true
1193 for param_name in recv_params {
1194 active_type := g.active_generic_types[param_name] or {
1195 matches = false
1196 break
1197 }
1198 inst_type := inst.bindings[param_name] or {
1199 matches = false
1200 break
1201 }
1202 active_token := g.generic_specialization_token_from_type(active_type)
1203 inst_token := g.generic_specialization_token_from_type(inst_type)
1204 if active_token != inst_token {
1205 matches = false
1206 break
1207 }
1208 }
1209 if matches {
1210 return inst.c_name
1211 }
1212 }
1213 return none
1214}
1215
1216fn (mut g Gen) specialized_fn_name(node ast.FnDecl, generic_types map[string]types.Type) string {
1217 generic_params := g.generic_fn_param_names(node)
1218 if generic_params.len == 0 {
1219 return ''
1220 }
1221 prev_generic_types := g.active_generic_types.clone()
1222 g.active_generic_types = generic_types.clone()
1223 base_name := g.get_fn_name(node)
1224 g.active_generic_types = prev_generic_types.clone()
1225 if base_name == '' {
1226 return ''
1227 }
1228 mut suffixes := []string{}
1229 for param_name in generic_params {
1230 if concrete := generic_types[param_name] {
1231 suffixes << g.generic_specialization_token_from_type(concrete)
1232 }
1233 }
1234 if suffixes.len == 0 {
1235 return base_name
1236 }
1237 return '${base_name}_T_${suffixes.join('_')}'
1238}
1239
1240fn (g &Gen) generic_key_matches_decl(node ast.FnDecl, key string) bool {
1241 return key == node.name || key.starts_with('${node.name}[') || key.ends_with('.${node.name}')
1242 || key.contains('.${node.name}[') || key.ends_with('__${node.name}')
1243 || key.contains('__${node.name}[')
1244}
1245
1246fn (mut g Gen) build_generic_fn_decl_index() {
1247 g.generic_fn_decl_index = map[string]GenericFnDeclInfo{}
1248 prev_module := g.cur_module
1249 prev_file_name := g.cur_file_name
1250 prev_active_generic_types := g.active_generic_types.clone()
1251 g.active_generic_types = map[string]types.Type{}
1252 defer {
1253 g.cur_module = prev_module
1254 g.cur_file_name = prev_file_name
1255 g.active_generic_types = prev_active_generic_types.clone()
1256 }
1257 if g.has_flat() {
1258 for file_idx in 0 .. g.flat.files.len {
1259 fc := g.flat.file_cursor(file_idx)
1260 g.set_file_cursor_module(fc)
1261 stmts := fc.stmts()
1262 for stmt_idx in 0 .. stmts.len() {
1263 stmt := stmts.at(stmt_idx)
1264 if stmt.kind() != .stmt_fn_decl {
1265 continue
1266 }
1267 decl := stmt.fn_decl_signature()
1268 if g.generic_fn_param_names(decl).len == 0
1269 && receiver_generic_param_names(decl).len == 0 {
1270 continue
1271 }
1272 info := GenericFnDeclInfo{
1273 file_idx: file_idx
1274 stmt_idx: stmt_idx
1275 }
1276 qualified := g.get_fn_name(decl)
1277 if qualified != '' && qualified !in g.generic_fn_decl_index {
1278 g.generic_fn_decl_index[qualified] = info
1279 }
1280 if !decl.is_method && decl.name != '' && decl.name !in g.generic_fn_decl_index {
1281 g.generic_fn_decl_index[decl.name] = info
1282 }
1283 if decl.is_method && decl.name != '' && decl.name !in g.generic_fn_decl_index {
1284 g.generic_fn_decl_index[decl.name] = info
1285 }
1286 sanitized := sanitize_fn_ident(decl.name)
1287 if !decl.is_method && sanitized != '' && sanitized !in g.generic_fn_decl_index {
1288 g.generic_fn_decl_index[sanitized] = info
1289 }
1290 }
1291 }
1292 return
1293 }
1294 for file_idx, file in g.files {
1295 g.set_file_module(file)
1296 for stmt_idx, stmt in file.stmts {
1297 if !stmt_has_valid_data(stmt) {
1298 continue
1299 }
1300 if stmt is ast.FnDecl {
1301 if g.generic_fn_param_names(stmt).len == 0
1302 && receiver_generic_param_names(stmt).len == 0 {
1303 continue
1304 }
1305 info := GenericFnDeclInfo{
1306 file_idx: file_idx
1307 stmt_idx: stmt_idx
1308 }
1309 qualified := g.get_fn_name(stmt)
1310 if qualified != '' && qualified !in g.generic_fn_decl_index {
1311 g.generic_fn_decl_index[qualified] = info
1312 }
1313 if !stmt.is_method && stmt.name != '' && stmt.name !in g.generic_fn_decl_index {
1314 g.generic_fn_decl_index[stmt.name] = info
1315 }
1316 if stmt.is_method && stmt.name != '' && stmt.name !in g.generic_fn_decl_index {
1317 g.generic_fn_decl_index[stmt.name] = info
1318 }
1319 sanitized := sanitize_fn_ident(stmt.name)
1320 if !stmt.is_method && sanitized != '' && sanitized !in g.generic_fn_decl_index {
1321 g.generic_fn_decl_index[sanitized] = info
1322 }
1323 }
1324 }
1325 }
1326}
1327
1328// discover_comptime_generic_specs pre-scans generic functions that use comptime
1329// `$for field in T.fields` with recursive generic calls (e.g., decode_value, encode_value).
1330// For each existing specialization T=SomeStruct, it discovers the struct's field types
1331// and registers additional specializations (e.g., T=string) so function bodies get emitted.
1332fn (mut g Gen) discover_comptime_generic_specs() {
1333 if g.env == unsafe { nil } {
1334 return
1335 }
1336 // For each generic function specialization where T is a struct,
1337 // discover field types and register additional specializations
1338 // so that comptime $for field in T.fields { decode_value(field) }
1339 // can call decode_value_T_<field_type>. Newly discovered struct specs
1340 // can expose more field types, so iterate to a small fixed point.
1341 for _ in 0 .. 8 {
1342 before := g.late_generic_spec_count()
1343 mut all_keys := map[string]bool{}
1344 for key, _ in g.env.generic_types {
1345 all_keys[key] = true
1346 }
1347 for key, _ in g.late_generic_specs {
1348 all_keys[key] = true
1349 }
1350 for key, _ in all_keys {
1351 decl := g.find_generic_fn_decl_by_spec_key(key) or { continue }
1352 if !fn_has_comptime_stmt(decl) {
1353 continue
1354 }
1355 mut spec_list := []map[string]types.Type{}
1356 for spec in g.env.generic_types[key] {
1357 spec_list << spec.clone()
1358 }
1359 for spec in g.late_generic_specs[key] {
1360 spec_list << spec.clone()
1361 }
1362 for spec in spec_list {
1363 for param_name, concrete_type in spec {
1364 if concrete_type is types.Struct {
1365 mut struct_type := concrete_type as types.Struct
1366 if struct_type.fields.len == 0 {
1367 struct_c_name :=
1368 g.types_type_to_c(concrete_type).trim_space().trim_right('*')
1369 full_struct_type := g.lookup_struct_type_by_c_name(struct_c_name)
1370 if full_struct_type.fields.len > 0 {
1371 struct_type = full_struct_type
1372 }
1373 }
1374 if struct_type.fields.len == 0 {
1375 continue
1376 }
1377 mut seen_types := map[string]bool{}
1378 // Mark existing specs as seen to avoid duplicates
1379 for existing_spec in spec_list {
1380 if existing_t := existing_spec[param_name] {
1381 seen_types[existing_t.name()] = true
1382 }
1383 }
1384 for field in struct_type.fields {
1385 field_type_name := field.typ.name()
1386 if field_type_name in seen_types {
1387 continue
1388 }
1389 seen_types[field_type_name] = true
1390 g.record_late_generic_call_spec(key, {
1391 param_name: field.typ
1392 })
1393 }
1394 }
1395 }
1396 }
1397 }
1398 if g.late_generic_spec_count() == before {
1399 break
1400 }
1401 }
1402}
1403
1404fn fn_has_comptime_stmt(decl ast.FnDecl) bool {
1405 for stmt in decl.stmts {
1406 if stmt_has_comptime_stmt(stmt) {
1407 return true
1408 }
1409 }
1410 return false
1411}
1412
1413fn stmt_has_comptime_stmt(stmt ast.Stmt) bool {
1414 match stmt {
1415 ast.ComptimeStmt {
1416 return true
1417 }
1418 ast.BlockStmt {
1419 for sub in stmt.stmts {
1420 if stmt_has_comptime_stmt(sub) {
1421 return true
1422 }
1423 }
1424 }
1425 ast.DeferStmt {
1426 for sub in stmt.stmts {
1427 if stmt_has_comptime_stmt(sub) {
1428 return true
1429 }
1430 }
1431 }
1432 ast.ForStmt {
1433 for sub in stmt.stmts {
1434 if stmt_has_comptime_stmt(sub) {
1435 return true
1436 }
1437 }
1438 }
1439 ast.ExprStmt {
1440 return expr_has_comptime_stmt(stmt.expr)
1441 }
1442 ast.AssignStmt {
1443 for rhs in stmt.rhs {
1444 if expr_has_comptime_stmt(rhs) {
1445 return true
1446 }
1447 }
1448 }
1449 ast.ReturnStmt {
1450 for expr in stmt.exprs {
1451 if expr_has_comptime_stmt(expr) {
1452 return true
1453 }
1454 }
1455 }
1456 else {}
1457 }
1458
1459 return false
1460}
1461
1462fn expr_has_comptime_stmt(expr ast.Expr) bool {
1463 match expr {
1464 ast.ComptimeExpr {
1465 return true
1466 }
1467 ast.IfExpr {
1468 if expr_has_comptime_stmt(expr.cond) {
1469 return true
1470 }
1471 for stmt in expr.stmts {
1472 if stmt_has_comptime_stmt(stmt) {
1473 return true
1474 }
1475 }
1476 return expr.else_expr !is ast.EmptyExpr && expr_has_comptime_stmt(expr.else_expr)
1477 }
1478 ast.MatchExpr {
1479 if expr_has_comptime_stmt(expr.expr) {
1480 return true
1481 }
1482 for branch in expr.branches {
1483 for cond in branch.cond {
1484 if expr_has_comptime_stmt(cond) {
1485 return true
1486 }
1487 }
1488 for stmt in branch.stmts {
1489 if stmt_has_comptime_stmt(stmt) {
1490 return true
1491 }
1492 }
1493 }
1494 }
1495 ast.OrExpr {
1496 if expr_has_comptime_stmt(expr.expr) {
1497 return true
1498 }
1499 for stmt in expr.stmts {
1500 if stmt_has_comptime_stmt(stmt) {
1501 return true
1502 }
1503 }
1504 }
1505 else {}
1506 }
1507
1508 return false
1509}
1510
1511fn fn_cursor_has_comptime_stmt(decl ast.Cursor) bool {
1512 return stmt_cursor_list_has_comptime_stmt(decl.list_at(3))
1513}
1514
1515fn stmt_cursor_list_has_comptime_stmt(stmts ast.CursorList) bool {
1516 for i in 0 .. stmts.len() {
1517 if stmt_cursor_has_comptime_stmt(stmts.at(i)) {
1518 return true
1519 }
1520 }
1521 return false
1522}
1523
1524fn stmt_cursor_has_comptime_stmt(stmt ast.Cursor) bool {
1525 if !stmt.is_valid() {
1526 return false
1527 }
1528 match stmt.kind() {
1529 .stmt_comptime {
1530 return true
1531 }
1532 .stmt_block, .stmt_defer {
1533 for i in 0 .. stmt.edge_count() {
1534 if stmt_cursor_has_comptime_stmt(stmt.edge(i)) {
1535 return true
1536 }
1537 }
1538 }
1539 .stmt_for {
1540 return stmt_cursor_list_has_comptime_stmt(stmt.for_body_list())
1541 }
1542 .stmt_expr {
1543 return expr_cursor_has_comptime_stmt(stmt.edge(0))
1544 }
1545 .stmt_assign {
1546 lhs_len := stmt.extra_int()
1547 for i in lhs_len .. stmt.edge_count() {
1548 if expr_cursor_has_comptime_stmt(stmt.edge(i)) {
1549 return true
1550 }
1551 }
1552 }
1553 .stmt_return {
1554 for i in 0 .. stmt.edge_count() {
1555 if expr_cursor_has_comptime_stmt(stmt.edge(i)) {
1556 return true
1557 }
1558 }
1559 }
1560 else {}
1561 }
1562
1563 return false
1564}
1565
1566fn expr_cursor_has_comptime_stmt(expr ast.Cursor) bool {
1567 if !expr.is_valid() {
1568 return false
1569 }
1570 match expr.kind() {
1571 .expr_comptime {
1572 return true
1573 }
1574 .expr_if {
1575 if expr_cursor_has_comptime_stmt(expr.edge(0)) {
1576 return true
1577 }
1578 for i in 2 .. expr.edge_count() {
1579 if stmt_cursor_has_comptime_stmt(expr.edge(i)) {
1580 return true
1581 }
1582 }
1583 return expr_cursor_has_comptime_stmt(expr.edge(1))
1584 }
1585 .expr_match {
1586 if expr_cursor_has_comptime_stmt(expr.edge(0)) {
1587 return true
1588 }
1589 for i in 1 .. expr.edge_count() {
1590 branch := expr.edge(i)
1591 conds := branch.list_at(0)
1592 for ci in 0 .. conds.len() {
1593 if expr_cursor_has_comptime_stmt(conds.at(ci)) {
1594 return true
1595 }
1596 }
1597 if stmt_cursor_list_has_comptime_stmt(branch.list_at(1)) {
1598 return true
1599 }
1600 }
1601 }
1602 .expr_or {
1603 if expr_cursor_has_comptime_stmt(expr.edge(0)) {
1604 return true
1605 }
1606 for i in 1 .. expr.edge_count() {
1607 if stmt_cursor_has_comptime_stmt(expr.edge(i)) {
1608 return true
1609 }
1610 }
1611 }
1612 else {}
1613 }
1614
1615 return false
1616}
1617
1618fn (mut g Gen) fn_body_has_generic_call(decl ast.FnDecl) bool {
1619 for stmt in decl.stmts {
1620 if g.stmt_has_generic_call(stmt) {
1621 return true
1622 }
1623 }
1624 return false
1625}
1626
1627fn (mut g Gen) stmt_has_generic_call(stmt ast.Stmt) bool {
1628 match stmt {
1629 ast.AssignStmt {
1630 for expr in stmt.lhs {
1631 if g.expr_has_generic_call(expr) {
1632 return true
1633 }
1634 }
1635 for expr in stmt.rhs {
1636 if g.expr_has_generic_call(expr) {
1637 return true
1638 }
1639 }
1640 }
1641 ast.BlockStmt {
1642 for sub in stmt.stmts {
1643 if g.stmt_has_generic_call(sub) {
1644 return true
1645 }
1646 }
1647 }
1648 ast.ComptimeStmt {
1649 return g.stmt_has_generic_call(stmt.stmt)
1650 }
1651 ast.DeferStmt {
1652 for sub in stmt.stmts {
1653 if g.stmt_has_generic_call(sub) {
1654 return true
1655 }
1656 }
1657 }
1658 ast.ExprStmt {
1659 return g.expr_has_generic_call(stmt.expr)
1660 }
1661 ast.ForStmt {
1662 if stmt.init !is ast.EmptyStmt && g.stmt_has_generic_call(stmt.init) {
1663 return true
1664 }
1665 if g.expr_has_generic_call(stmt.cond) {
1666 return true
1667 }
1668 if stmt.post !is ast.EmptyStmt && g.stmt_has_generic_call(stmt.post) {
1669 return true
1670 }
1671 for sub in stmt.stmts {
1672 if g.stmt_has_generic_call(sub) {
1673 return true
1674 }
1675 }
1676 }
1677 ast.ReturnStmt {
1678 for expr in stmt.exprs {
1679 if g.expr_has_generic_call(expr) {
1680 return true
1681 }
1682 }
1683 }
1684 else {}
1685 }
1686
1687 return false
1688}
1689
1690fn (mut g Gen) expr_has_generic_call(expr ast.Expr) bool {
1691 match expr {
1692 ast.CallExpr {
1693 if _ := g.generic_call_decl_from_lhs(expr.lhs) {
1694 return true
1695 }
1696 if g.expr_has_generic_call(expr.lhs) {
1697 return true
1698 }
1699 for arg in expr.args {
1700 if g.expr_has_generic_call(arg) {
1701 return true
1702 }
1703 }
1704 }
1705 ast.CallOrCastExpr {
1706 return g.expr_has_generic_call(expr.lhs) || g.expr_has_generic_call(expr.expr)
1707 }
1708 ast.ComptimeExpr {
1709 return g.expr_has_generic_call(expr.expr)
1710 }
1711 ast.GenericArgOrIndexExpr {
1712 return g.expr_has_generic_call(expr.lhs) || g.expr_has_generic_call(expr.expr)
1713 }
1714 ast.GenericArgs {
1715 if g.expr_has_generic_call(expr.lhs) {
1716 return true
1717 }
1718 for arg in expr.args {
1719 if g.expr_has_generic_call(arg) {
1720 return true
1721 }
1722 }
1723 }
1724 ast.IfExpr {
1725 if g.expr_has_generic_call(expr.cond) {
1726 return true
1727 }
1728 for sub in expr.stmts {
1729 if g.stmt_has_generic_call(sub) {
1730 return true
1731 }
1732 }
1733 return expr.else_expr !is ast.EmptyExpr && g.expr_has_generic_call(expr.else_expr)
1734 }
1735 ast.IndexExpr {
1736 return g.expr_has_generic_call(expr.lhs) || g.expr_has_generic_call(expr.expr)
1737 }
1738 ast.InfixExpr {
1739 return g.expr_has_generic_call(expr.lhs) || g.expr_has_generic_call(expr.rhs)
1740 }
1741 ast.MatchExpr {
1742 if g.expr_has_generic_call(expr.expr) {
1743 return true
1744 }
1745 for branch in expr.branches {
1746 for cond in branch.cond {
1747 if g.expr_has_generic_call(cond) {
1748 return true
1749 }
1750 }
1751 for sub in branch.stmts {
1752 if g.stmt_has_generic_call(sub) {
1753 return true
1754 }
1755 }
1756 }
1757 }
1758 ast.ModifierExpr {
1759 return g.expr_has_generic_call(expr.expr)
1760 }
1761 ast.OrExpr {
1762 if g.expr_has_generic_call(expr.expr) {
1763 return true
1764 }
1765 for sub in expr.stmts {
1766 if g.stmt_has_generic_call(sub) {
1767 return true
1768 }
1769 }
1770 }
1771 ast.ParenExpr {
1772 return g.expr_has_generic_call(expr.expr)
1773 }
1774 ast.PrefixExpr {
1775 return g.expr_has_generic_call(expr.expr)
1776 }
1777 ast.SelectorExpr {
1778 return g.expr_has_generic_call(expr.lhs)
1779 }
1780 ast.Tuple {
1781 for item in expr.exprs {
1782 if g.expr_has_generic_call(item) {
1783 return true
1784 }
1785 }
1786 }
1787 ast.UnsafeExpr {
1788 for sub in expr.stmts {
1789 if g.stmt_has_generic_call(sub) {
1790 return true
1791 }
1792 }
1793 }
1794 else {}
1795 }
1796
1797 return false
1798}
1799
1800fn (mut g Gen) fn_cursor_body_has_generic_call(decl ast.Cursor) bool {
1801 body := decl.list_at(3)
1802 for i in 0 .. body.len() {
1803 if g.stmt_cursor_has_generic_call(body.at(i)) {
1804 return true
1805 }
1806 }
1807 return false
1808}
1809
1810fn (mut g Gen) stmt_cursor_has_generic_call(stmt ast.Cursor) bool {
1811 if !stmt.is_valid() {
1812 return false
1813 }
1814 match stmt.kind() {
1815 .stmt_assign {
1816 lhs_len := stmt.extra_int()
1817 for i in 0 .. lhs_len {
1818 if g.expr_cursor_has_generic_call(stmt.edge(i)) {
1819 return true
1820 }
1821 }
1822 for i in lhs_len .. stmt.edge_count() {
1823 if g.expr_cursor_has_generic_call(stmt.edge(i)) {
1824 return true
1825 }
1826 }
1827 }
1828 .stmt_block, .stmt_defer {
1829 for i in 0 .. stmt.edge_count() {
1830 if g.stmt_cursor_has_generic_call(stmt.edge(i)) {
1831 return true
1832 }
1833 }
1834 }
1835 .stmt_comptime {
1836 return g.stmt_cursor_has_generic_call(stmt.edge(0))
1837 }
1838 .stmt_expr {
1839 return g.expr_cursor_has_generic_call(stmt.edge(0))
1840 }
1841 .stmt_for {
1842 if g.stmt_cursor_has_generic_call(stmt.edge(0)) {
1843 return true
1844 }
1845 if g.expr_cursor_has_generic_call(stmt.edge(1)) {
1846 return true
1847 }
1848 if g.stmt_cursor_has_generic_call(stmt.edge(2)) {
1849 return true
1850 }
1851 body := stmt.for_body_list()
1852 for i in 0 .. body.len() {
1853 if g.stmt_cursor_has_generic_call(body.at(i)) {
1854 return true
1855 }
1856 }
1857 }
1858 .stmt_return {
1859 for i in 0 .. stmt.edge_count() {
1860 if g.expr_cursor_has_generic_call(stmt.edge(i)) {
1861 return true
1862 }
1863 }
1864 }
1865 else {}
1866 }
1867
1868 return false
1869}
1870
1871fn (mut g Gen) call_cursor_lhs_is_generic_call(lhs ast.Cursor) bool {
1872 call_name := generic_call_short_name_cursor(lhs)
1873 if call_name == '' {
1874 return false
1875 }
1876 if g.generic_fn_decl_index.len == 0 {
1877 if _ := g.generic_call_decl_from_lhs_cursor(lhs) {
1878 return true
1879 }
1880 return false
1881 }
1882 for candidate in generic_call_decl_candidates(call_name) {
1883 if candidate in g.generic_fn_decl_index {
1884 return true
1885 }
1886 }
1887 return false
1888}
1889
1890fn (mut g Gen) expr_cursor_has_generic_call(expr ast.Cursor) bool {
1891 if !expr.is_valid() {
1892 return false
1893 }
1894 match expr.kind() {
1895 .expr_call {
1896 if g.call_cursor_lhs_is_generic_call(expr.edge(0)) {
1897 return true
1898 }
1899 if g.expr_cursor_has_generic_call(expr.edge(0)) {
1900 return true
1901 }
1902 for i in 1 .. expr.edge_count() {
1903 if g.expr_cursor_has_generic_call(expr.edge(i)) {
1904 return true
1905 }
1906 }
1907 }
1908 .expr_call_or_cast, .expr_generic_arg_or_index, .expr_index, .expr_infix {
1909 return g.expr_cursor_has_generic_call(expr.edge(0))
1910 || g.expr_cursor_has_generic_call(expr.edge(1))
1911 }
1912 .expr_comptime, .expr_modifier, .expr_paren, .expr_prefix, .expr_selector {
1913 return g.expr_cursor_has_generic_call(expr.edge(0))
1914 }
1915 .expr_generic_args, .expr_tuple {
1916 for i in 0 .. expr.edge_count() {
1917 if g.expr_cursor_has_generic_call(expr.edge(i)) {
1918 return true
1919 }
1920 }
1921 }
1922 .expr_if {
1923 if g.expr_cursor_has_generic_call(expr.edge(0)) {
1924 return true
1925 }
1926 for i in 2 .. expr.edge_count() {
1927 if g.stmt_cursor_has_generic_call(expr.edge(i)) {
1928 return true
1929 }
1930 }
1931 return g.expr_cursor_has_generic_call(expr.edge(1))
1932 }
1933 .expr_match {
1934 if g.expr_cursor_has_generic_call(expr.edge(0)) {
1935 return true
1936 }
1937 for i in 1 .. expr.edge_count() {
1938 branch := expr.edge(i)
1939 conds := branch.list_at(0)
1940 for ci in 0 .. conds.len() {
1941 if g.expr_cursor_has_generic_call(conds.at(ci)) {
1942 return true
1943 }
1944 }
1945 stmts := branch.list_at(1)
1946 for si in 0 .. stmts.len() {
1947 if g.stmt_cursor_has_generic_call(stmts.at(si)) {
1948 return true
1949 }
1950 }
1951 }
1952 }
1953 .expr_or {
1954 if g.expr_cursor_has_generic_call(expr.edge(0)) {
1955 return true
1956 }
1957 for i in 1 .. expr.edge_count() {
1958 if g.stmt_cursor_has_generic_call(expr.edge(i)) {
1959 return true
1960 }
1961 }
1962 }
1963 .expr_unsafe {
1964 for i in 0 .. expr.edge_count() {
1965 if g.stmt_cursor_has_generic_call(expr.edge(i)) {
1966 return true
1967 }
1968 }
1969 }
1970 else {}
1971 }
1972
1973 return false
1974}
1975
1976fn (mut g Gen) accepts_broad_generic_fallback_type(node ast.FnDecl, concrete types.Type) bool {
1977 if concrete.name() == 'void' {
1978 return false
1979 }
1980 if g.cur_module != 'json2' && g.broad_generic_fallback_type_is_generic_instance(concrete) {
1981 return false
1982 }
1983 if g.cur_module == 'json2'
1984 && node.name in ['decode', 'decode_value', 'decode_struct_key', 'check_required_struct_fields', 'cached_struct_field_infos']
1985 && is_json2_runtime_internal_type(concrete) {
1986 return false
1987 }
1988 if g.cur_module == 'stdatomic' && node.name == 'new_atomic' {
1989 return concrete.name() in ['bool', 'voidptr', 'int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16',
1990 'u32', 'u64', 'byte', 'isize', 'usize']
1991 }
1992 return g.generic_fallback_type_satisfies_body_requirements(node, concrete)
1993}
1994
1995fn (g &Gen) broad_generic_fallback_type_is_generic_instance(concrete types.Type) bool {
1996 c_name := g.types_type_to_c(normalize_generic_concrete_type(concrete))
1997 return c_name.contains('_T_')
1998}
1999
2000fn (mut g Gen) generic_fallback_type_satisfies_body_requirements(node ast.FnDecl, concrete types.Type) bool {
2001 generic_params := g.generic_fn_param_names(node)
2002 if generic_params.len != 1 || node.stmts.len == 0 {
2003 return true
2004 }
2005 return g.generic_fallback_type_satisfies_body_requirements_for_param(node, generic_params[0],
2006 concrete, 0)
2007}
2008
2009fn (mut g Gen) generic_fallback_type_satisfies_body_requirements_for_param(node ast.FnDecl, param_name string, concrete types.Type, depth int) bool {
2010 if param_name == '' || node.stmts.len == 0 {
2011 return true
2012 }
2013 if generic_param_is_scalar_numeric_param(node, param_name)
2014 && !generic_fallback_type_supports_numeric(concrete) {
2015 return false
2016 }
2017 if generic_fn_has_unconditional_comptime_fields_loop(node.stmts, param_name) {
2018 if _ := g.generic_fallback_struct_type(concrete) {
2019 } else {
2020 return false
2021 }
2022 }
2023 mut value_names, container_names := generic_param_value_and_container_names(node, param_name)
2024 if value_names.len == 0 && container_names.len == 0 {
2025 return true
2026 }
2027 mut required_fields := map[string]bool{}
2028 mut required_methods := map[string]bool{}
2029 collect_generic_param_member_requirements_from_stmts(node.stmts, value_names, mut
2030 required_fields, mut required_methods)
2031 mut required_ops := map[string]bool{}
2032 mut op_value_names := value_names.clone()
2033 collect_generic_param_operator_requirements_from_stmts(node.stmts, mut op_value_names,
2034 container_names, mut required_ops)
2035 if required_ops['ordered'] && !generic_fallback_type_supports_ordered(concrete) {
2036 return false
2037 }
2038 if required_ops['add'] && !generic_fallback_type_supports_add(concrete) {
2039 return false
2040 }
2041 if required_ops['numeric'] && !generic_fallback_type_supports_numeric(concrete) {
2042 return false
2043 }
2044 for field_name, _ in required_fields {
2045 if !g.generic_fallback_type_has_field(concrete, field_name) {
2046 return false
2047 }
2048 }
2049 for method_name, _ in required_methods {
2050 if !g.generic_fallback_type_has_method(concrete, method_name) {
2051 return false
2052 }
2053 }
2054 if !g.generic_param_call_requirements_satisfied_from_stmts(node.stmts, param_name, value_names,
2055 container_names, concrete, depth) {
2056 return false
2057 }
2058 return true
2059}
2060
2061fn generic_param_is_scalar_numeric_param(node ast.FnDecl, param_name string) bool {
2062 if !node.name.contains('scalar') {
2063 return false
2064 }
2065 for param in node.typ.params {
2066 if param.name == 'scalar' && generic_type_expr_is_direct_placeholder(param.typ, param_name) {
2067 return true
2068 }
2069 }
2070 return false
2071}
2072
2073fn (mut g Gen) generic_param_call_requirements_satisfied_from_stmts(stmts []ast.Stmt, param_name string, value_names map[string]bool, container_names map[string]bool, concrete types.Type, depth int) bool {
2074 for stmt in stmts {
2075 match stmt {
2076 ast.AssignStmt {
2077 for expr in stmt.lhs {
2078 if !g.generic_param_call_requirements_satisfied_from_expr(expr, param_name,
2079 value_names, container_names, concrete, depth) {
2080 return false
2081 }
2082 }
2083 for expr in stmt.rhs {
2084 if !g.generic_param_call_requirements_satisfied_from_expr(expr, param_name,
2085 value_names, container_names, concrete, depth) {
2086 return false
2087 }
2088 }
2089 }
2090 ast.BlockStmt, ast.DeferStmt {
2091 if !g.generic_param_call_requirements_satisfied_from_stmts(stmt.stmts, param_name,
2092 value_names, container_names, concrete, depth) {
2093 return false
2094 }
2095 }
2096 ast.ComptimeStmt {
2097 if stmt.stmt is ast.ExprStmt && stmt.stmt.expr is ast.ComptimeExpr
2098 && (stmt.stmt.expr as ast.ComptimeExpr).expr is ast.IfExpr {
2099 continue
2100 }
2101 if !g.generic_param_call_requirements_satisfied_from_stmts([stmt.stmt], param_name,
2102 value_names, container_names, concrete, depth) {
2103 return false
2104 }
2105 }
2106 ast.ExprStmt {
2107 if !g.generic_param_call_requirements_satisfied_from_expr(stmt.expr, param_name,
2108 value_names, container_names, concrete, depth) {
2109 return false
2110 }
2111 }
2112 ast.ForStmt {
2113 if stmt.init !is ast.EmptyStmt
2114 && !g.generic_param_call_requirements_satisfied_from_stmts([stmt.init], param_name, value_names, container_names, concrete, depth) {
2115 return false
2116 }
2117 if !g.generic_param_call_requirements_satisfied_from_expr(stmt.cond, param_name,
2118 value_names, container_names, concrete, depth) {
2119 return false
2120 }
2121 if stmt.post !is ast.EmptyStmt
2122 && !g.generic_param_call_requirements_satisfied_from_stmts([stmt.post], param_name, value_names, container_names, concrete, depth) {
2123 return false
2124 }
2125 if !g.generic_param_call_requirements_satisfied_from_stmts(stmt.stmts, param_name,
2126 value_names, container_names, concrete, depth) {
2127 return false
2128 }
2129 }
2130 ast.ReturnStmt {
2131 for expr in stmt.exprs {
2132 if !g.generic_param_call_requirements_satisfied_from_expr(expr, param_name,
2133 value_names, container_names, concrete, depth) {
2134 return false
2135 }
2136 }
2137 }
2138 else {}
2139 }
2140 }
2141 return true
2142}
2143
2144fn (mut g Gen) generic_param_call_requirements_satisfied_from_expr(expr ast.Expr, param_name string, value_names map[string]bool, container_names map[string]bool, concrete types.Type, depth int) bool {
2145 match expr {
2146 ast.ArrayInitExpr {
2147 for item in expr.exprs {
2148 if !g.generic_param_call_requirements_satisfied_from_expr(item, param_name,
2149 value_names, container_names, concrete, depth) {
2150 return false
2151 }
2152 }
2153 }
2154 ast.CallExpr {
2155 if !g.generic_param_call_requirements_satisfied_for_call(expr, value_names,
2156 container_names, concrete, depth) {
2157 return false
2158 }
2159 if !g.generic_param_call_requirements_satisfied_from_expr(expr.lhs, param_name,
2160 value_names, container_names, concrete, depth) {
2161 return false
2162 }
2163 for arg in expr.args {
2164 if !g.generic_param_call_requirements_satisfied_from_expr(arg, param_name,
2165 value_names, container_names, concrete, depth) {
2166 return false
2167 }
2168 }
2169 }
2170 ast.CallOrCastExpr {
2171 return
2172 g.generic_param_call_requirements_satisfied_from_expr(expr.lhs, param_name, value_names, container_names, concrete, depth)
2173 && g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name, value_names, container_names, concrete, depth)
2174 }
2175 ast.ComptimeExpr {
2176 if expr.expr is ast.IfExpr {
2177 return true
2178 }
2179 return g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name,
2180 value_names, container_names, concrete, depth)
2181 }
2182 ast.IfExpr {
2183 if !g.generic_param_call_requirements_satisfied_from_expr(expr.cond, param_name,
2184 value_names, container_names, concrete, depth) {
2185 return false
2186 }
2187 if !g.generic_param_call_requirements_satisfied_from_stmts(expr.stmts, param_name,
2188 value_names, container_names, concrete, depth) {
2189 return false
2190 }
2191 if expr.else_expr !is ast.EmptyExpr {
2192 return g.generic_param_call_requirements_satisfied_from_expr(expr.else_expr,
2193 param_name, value_names, container_names, concrete, depth)
2194 }
2195 }
2196 ast.IndexExpr {
2197 return
2198 g.generic_param_call_requirements_satisfied_from_expr(expr.lhs, param_name, value_names, container_names, concrete, depth)
2199 && g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name, value_names, container_names, concrete, depth)
2200 }
2201 ast.InfixExpr {
2202 return
2203 g.generic_param_call_requirements_satisfied_from_expr(expr.lhs, param_name, value_names, container_names, concrete, depth)
2204 && g.generic_param_call_requirements_satisfied_from_expr(expr.rhs, param_name, value_names, container_names, concrete, depth)
2205 }
2206 ast.MatchExpr {
2207 if !g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name,
2208 value_names, container_names, concrete, depth) {
2209 return false
2210 }
2211 for branch in expr.branches {
2212 for cond in branch.cond {
2213 if !g.generic_param_call_requirements_satisfied_from_expr(cond, param_name,
2214 value_names, container_names, concrete, depth) {
2215 return false
2216 }
2217 }
2218 if !g.generic_param_call_requirements_satisfied_from_stmts(branch.stmts,
2219 param_name, value_names, container_names, concrete, depth) {
2220 return false
2221 }
2222 }
2223 }
2224 ast.ModifierExpr, ast.ParenExpr, ast.PostfixExpr, ast.PrefixExpr {
2225 return g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name,
2226 value_names, container_names, concrete, depth)
2227 }
2228 ast.OrExpr {
2229 return
2230 g.generic_param_call_requirements_satisfied_from_expr(expr.expr, param_name, value_names, container_names, concrete, depth)
2231 && g.generic_param_call_requirements_satisfied_from_stmts(expr.stmts, param_name, value_names, container_names, concrete, depth)
2232 }
2233 ast.SelectorExpr {
2234 return g.generic_param_call_requirements_satisfied_from_expr(expr.lhs, param_name,
2235 value_names, container_names, concrete, depth)
2236 }
2237 ast.Tuple {
2238 for item in expr.exprs {
2239 if !g.generic_param_call_requirements_satisfied_from_expr(item, param_name,
2240 value_names, container_names, concrete, depth) {
2241 return false
2242 }
2243 }
2244 }
2245 ast.UnsafeExpr {
2246 return g.generic_param_call_requirements_satisfied_from_stmts(expr.stmts, param_name,
2247 value_names, container_names, concrete, depth)
2248 }
2249 else {}
2250 }
2251
2252 return true
2253}
2254
2255fn (mut g Gen) generic_param_call_requirements_satisfied_for_call(call ast.CallExpr, value_names map[string]bool, container_names map[string]bool, concrete types.Type, depth int) bool {
2256 mut call_args := []ast.Expr{cap: call.args.len + 1}
2257 if call.lhs is ast.SelectorExpr {
2258 sel := call.lhs as ast.SelectorExpr
2259 mut is_static_or_module_call := false
2260 if sel.lhs is ast.Ident {
2261 lhs_name := (sel.lhs as ast.Ident).name
2262 is_static_or_module_call = g.is_type_name(lhs_name) || g.is_module_ident(lhs_name)
2263 }
2264 if !is_static_or_module_call {
2265 call_args << sel.lhs
2266 }
2267 }
2268 call_args << call.args
2269 mut call_name := g.resolve_call_name(call.lhs, call.args.len)
2270 if call_name == '' {
2271 return true
2272 }
2273 if call_name.contains('_T_') {
2274 call_name = call_name.all_before('_T_')
2275 }
2276 if call_args.len > 0 && call_name.contains('__')
2277 && generic_expr_is_generic_value(call_args[0], value_names, container_names) {
2278 owner := call_name.all_before_last('__')
2279 method_name := call_name.all_after_last('__')
2280 if g.generic_requirement_owner_is_type(owner) {
2281 if declared_method := g.resolve_method_on_concrete_type(owner, method_name) {
2282 if declared_method == call_name
2283 && !g.generic_fallback_type_has_method(concrete, method_name) {
2284 return false
2285 }
2286 }
2287 }
2288 }
2289 if !g.generic_call_interface_args_satisfy_requirements(call_name, call_args, value_names,
2290 container_names, concrete) {
2291 return false
2292 }
2293 if depth >= 8 {
2294 return true
2295 }
2296 nested_decl := g.find_generic_fn_decl_for_requirements(call_name) or { return true }
2297 nested_generic_params := g.generic_fn_param_names(nested_decl)
2298 if nested_generic_params.len == 0 {
2299 return true
2300 }
2301 arg_offset := if nested_decl.is_method && call_args.len == nested_decl.typ.params.len + 1 {
2302 1
2303 } else {
2304 0
2305 }
2306 for arg_idx, arg in call_args {
2307 if !generic_expr_is_generic_value(arg, value_names, container_names) {
2308 continue
2309 }
2310 param_idx := arg_idx - arg_offset
2311 if param_idx < 0 || param_idx >= nested_decl.typ.params.len {
2312 continue
2313 }
2314 formal := nested_decl.typ.params[param_idx]
2315 for nested_param_name in nested_generic_params {
2316 if !generic_type_expr_is_direct_placeholder(formal.typ, nested_param_name) {
2317 continue
2318 }
2319 if !g.generic_fallback_type_satisfies_body_requirements_for_param(nested_decl,
2320 nested_param_name, concrete, depth + 1) {
2321 return false
2322 }
2323 }
2324 }
2325 return true
2326}
2327
2328fn (mut g Gen) generic_requirement_owner_is_type(owner string) bool {
2329 if owner == '' {
2330 return false
2331 }
2332 clean_owner := strip_pointer_type_name(unmangle_c_ptr_type(owner))
2333 if clean_owner == '' || clean_owner in primitive_types {
2334 return false
2335 }
2336 if clean_owner in g.c_struct_types || clean_owner in g.typedef_c_types
2337 || clean_owner in g.enum_type_fields {
2338 return true
2339 }
2340 if _ := g.lookup_type_by_c_name(clean_owner) {
2341 return true
2342 }
2343 if !clean_owner.contains('__') && g.unqualified_type_name_declared_in_files(clean_owner) {
2344 return true
2345 }
2346 return false
2347}
2348
2349fn (mut g Gen) find_generic_fn_decl_for_requirements(call_name string) ?ast.FnDecl {
2350 if decl := g.find_generic_fn_decl_by_base_name(call_name) {
2351 return decl
2352 }
2353 mut short_name := call_name
2354 if short_name.contains('_T_') {
2355 short_name = short_name.all_before('_T_')
2356 }
2357 if short_name.contains('__') {
2358 short_name = short_name.all_after_last('__')
2359 }
2360 if g.has_flat() {
2361 for i in 0 .. g.flat.files.len {
2362 stmts := g.flat.file_cursor(i).stmts()
2363 for j in 0 .. stmts.len() {
2364 stmt := stmts.at(j)
2365 if stmt.kind() != .stmt_fn_decl || stmt.name() != short_name {
2366 continue
2367 }
2368 decl := stmt.fn_decl_signature()
2369 if g.generic_fn_param_names(decl).len > 0 {
2370 return decl
2371 }
2372 }
2373 }
2374 return none
2375 }
2376 for file in g.files {
2377 for stmt in file.stmts {
2378 if stmt is ast.FnDecl && stmt.name == short_name
2379 && g.generic_fn_param_names(stmt).len > 0 {
2380 return stmt
2381 }
2382 }
2383 }
2384 return none
2385}
2386
2387fn (mut g Gen) generic_call_interface_args_satisfy_requirements(call_name string, call_args []ast.Expr, value_names map[string]bool, container_names map[string]bool, concrete types.Type) bool {
2388 param_types := g.generic_call_param_types_for_requirements(call_name)
2389 if param_types.len == 0 {
2390 return true
2391 }
2392 for i, arg in call_args {
2393 if i >= param_types.len || !generic_expr_is_generic_value(arg, value_names, container_names) {
2394 continue
2395 }
2396 param_base := strip_pointer_type_name(unmangle_c_ptr_type(param_types[i].trim_space()))
2397 if param_base == '' || !g.is_interface_type(param_base) {
2398 continue
2399 }
2400 if !g.generic_fallback_type_satisfies_interface(concrete, param_base) {
2401 return false
2402 }
2403 }
2404 return true
2405}
2406
2407fn (g &Gen) generic_call_param_types_for_requirements(call_name string) []string {
2408 mut candidates := []string{}
2409 if call_name != '' {
2410 candidates << call_name
2411 if call_name.contains('_T_') {
2412 candidates << call_name.all_before('_T_')
2413 }
2414 qualified := g.qualify_local_call_name(call_name)
2415 if qualified != call_name {
2416 candidates << qualified
2417 }
2418 if !call_name.contains('__') {
2419 if known := g.find_known_qualified_fn_name(call_name) {
2420 candidates << known
2421 }
2422 }
2423 }
2424 for candidate in candidates {
2425 if param_types := g.fn_param_types[candidate] {
2426 return param_types
2427 }
2428 }
2429 return []string{}
2430}
2431
2432fn (mut g Gen) generic_fallback_type_satisfies_interface(concrete types.Type, iface_name string) bool {
2433 methods := g.interface_methods_for_requirements(iface_name)
2434 if methods.len == 0 {
2435 return true
2436 }
2437 for method in methods {
2438 if !g.generic_fallback_type_has_method(concrete, method.name) {
2439 return false
2440 }
2441 }
2442 return true
2443}
2444
2445fn (g &Gen) interface_methods_for_requirements(iface_name string) []InterfaceMethodInfo {
2446 mut candidates := []string{}
2447 if iface_name != '' {
2448 candidates << iface_name
2449 short_name := iface_name.all_after_last('__')
2450 if short_name != iface_name {
2451 candidates << short_name
2452 }
2453 if g.cur_module != '' && g.cur_module != 'main' {
2454 candidates << '${g.cur_module}__${short_name}'
2455 }
2456 candidates << 'builtin__${short_name}'
2457 }
2458 for candidate in candidates {
2459 if methods := g.interface_methods[candidate] {
2460 return methods
2461 }
2462 }
2463 return []InterfaceMethodInfo{}
2464}
2465
2466fn generic_param_value_and_container_names(node ast.FnDecl, param_name string) (map[string]bool, map[string]bool) {
2467 mut value_names := map[string]bool{}
2468 mut container_names := map[string]bool{}
2469 for param in node.typ.params {
2470 if generic_type_expr_is_direct_placeholder(param.typ, param_name) {
2471 value_names[param.name] = true
2472 } else if direct_generic_placeholder_name(param.typ) == param_name {
2473 container_names[param.name] = true
2474 }
2475 }
2476 return value_names, container_names
2477}
2478
2479fn generic_type_expr_is_direct_placeholder(expr ast.Expr, param_name string) bool {
2480 match expr {
2481 ast.Ident {
2482 return expr.name == param_name
2483 }
2484 ast.ModifierExpr {
2485 return generic_type_expr_is_direct_placeholder(expr.expr, param_name)
2486 }
2487 ast.PrefixExpr {
2488 return generic_type_expr_is_direct_placeholder(expr.expr, param_name)
2489 }
2490 else {}
2491 }
2492
2493 return false
2494}
2495
2496fn generic_fn_has_unconditional_comptime_fields_loop(stmts []ast.Stmt, param_name string) bool {
2497 for stmt in stmts {
2498 match stmt {
2499 ast.BlockStmt {
2500 if generic_fn_has_unconditional_comptime_fields_loop(stmt.stmts, param_name) {
2501 return true
2502 }
2503 }
2504 ast.ComptimeStmt {
2505 if comptime_stmt_is_generic_fields_loop(stmt.stmt, param_name) {
2506 return true
2507 }
2508 }
2509 ast.ExprStmt {
2510 if stmt.expr is ast.UnsafeExpr {
2511 if generic_fn_has_unconditional_comptime_fields_loop(stmt.expr.stmts,
2512 param_name)
2513 {
2514 return true
2515 }
2516 }
2517 }
2518 else {}
2519 }
2520 }
2521 return false
2522}
2523
2524fn comptime_stmt_is_generic_fields_loop(stmt ast.Stmt, param_name string) bool {
2525 if stmt !is ast.ForStmt {
2526 return false
2527 }
2528 if stmt.init !is ast.ForInStmt {
2529 return false
2530 }
2531 for_in := stmt.init as ast.ForInStmt
2532 if for_in.expr !is ast.SelectorExpr {
2533 return false
2534 }
2535 sel := for_in.expr as ast.SelectorExpr
2536 return sel.rhs.name == 'fields' && sel.lhs is ast.Ident
2537 && (sel.lhs as ast.Ident).name == param_name
2538}
2539
2540fn collect_generic_param_member_requirements_from_stmts(stmts []ast.Stmt, value_names map[string]bool, mut required_fields map[string]bool, mut required_methods map[string]bool) {
2541 for stmt in stmts {
2542 match stmt {
2543 ast.AssignStmt {
2544 for expr in stmt.lhs {
2545 collect_generic_param_member_requirements_from_expr(expr, value_names, mut
2546 required_fields, mut required_methods)
2547 }
2548 for expr in stmt.rhs {
2549 collect_generic_param_member_requirements_from_expr(expr, value_names, mut
2550 required_fields, mut required_methods)
2551 }
2552 }
2553 ast.BlockStmt {
2554 collect_generic_param_member_requirements_from_stmts(stmt.stmts, value_names, mut
2555 required_fields, mut required_methods)
2556 }
2557 ast.ComptimeStmt {
2558 if stmt.stmt is ast.ExprStmt && stmt.stmt.expr is ast.ComptimeExpr
2559 && (stmt.stmt.expr as ast.ComptimeExpr).expr is ast.IfExpr {
2560 continue
2561 }
2562 collect_generic_param_member_requirements_from_stmts([stmt.stmt], value_names, mut
2563 required_fields, mut required_methods)
2564 }
2565 ast.DeferStmt {
2566 collect_generic_param_member_requirements_from_stmts(stmt.stmts, value_names, mut
2567 required_fields, mut required_methods)
2568 }
2569 ast.ExprStmt {
2570 collect_generic_param_member_requirements_from_expr(stmt.expr, value_names, mut
2571 required_fields, mut required_methods)
2572 }
2573 ast.ForStmt {
2574 if stmt.init !is ast.EmptyStmt {
2575 collect_generic_param_member_requirements_from_stmts([stmt.init], value_names, mut
2576 required_fields, mut required_methods)
2577 }
2578 collect_generic_param_member_requirements_from_expr(stmt.cond, value_names, mut
2579 required_fields, mut required_methods)
2580 if stmt.post !is ast.EmptyStmt {
2581 collect_generic_param_member_requirements_from_stmts([stmt.post], value_names, mut
2582 required_fields, mut required_methods)
2583 }
2584 collect_generic_param_member_requirements_from_stmts(stmt.stmts, value_names, mut
2585 required_fields, mut required_methods)
2586 }
2587 ast.ReturnStmt {
2588 for expr in stmt.exprs {
2589 collect_generic_param_member_requirements_from_expr(expr, value_names, mut
2590 required_fields, mut required_methods)
2591 }
2592 }
2593 else {}
2594 }
2595 }
2596}
2597
2598fn collect_generic_param_member_requirements_from_expr(expr ast.Expr, value_names map[string]bool, mut required_fields map[string]bool, mut required_methods map[string]bool) {
2599 match expr {
2600 ast.ArrayInitExpr {
2601 for item in expr.exprs {
2602 collect_generic_param_member_requirements_from_expr(item, value_names, mut
2603 required_fields, mut required_methods)
2604 }
2605 }
2606 ast.CallExpr {
2607 if expr.lhs is ast.SelectorExpr {
2608 sel := expr.lhs as ast.SelectorExpr
2609 if is_comptime_selector_rhs_name(sel.rhs.name) {
2610 collect_generic_param_member_requirements_from_expr(sel.lhs, value_names, mut
2611 required_fields, mut required_methods)
2612 } else if sel.lhs is ast.Ident && (sel.lhs as ast.Ident).name in value_names {
2613 required_methods[sel.rhs.name] = true
2614 } else {
2615 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2616 required_fields, mut required_methods)
2617 }
2618 } else {
2619 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2620 required_fields, mut required_methods)
2621 }
2622 for arg in expr.args {
2623 collect_generic_param_member_requirements_from_expr(arg, value_names, mut
2624 required_fields, mut required_methods)
2625 }
2626 }
2627 ast.CallOrCastExpr {
2628 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2629 required_fields, mut required_methods)
2630 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2631 required_fields, mut required_methods)
2632 }
2633 ast.ComptimeExpr {
2634 if expr.expr is ast.IfExpr {
2635 return
2636 }
2637 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2638 required_fields, mut required_methods)
2639 }
2640 ast.IfExpr {
2641 collect_generic_param_member_requirements_from_expr(expr.cond, value_names, mut
2642 required_fields, mut required_methods)
2643 collect_generic_param_member_requirements_from_stmts(expr.stmts, value_names, mut
2644 required_fields, mut required_methods)
2645 if expr.else_expr !is ast.EmptyExpr {
2646 collect_generic_param_member_requirements_from_expr(expr.else_expr, value_names, mut
2647 required_fields, mut required_methods)
2648 }
2649 }
2650 ast.IndexExpr {
2651 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2652 required_fields, mut required_methods)
2653 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2654 required_fields, mut required_methods)
2655 }
2656 ast.InfixExpr {
2657 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2658 required_fields, mut required_methods)
2659 collect_generic_param_member_requirements_from_expr(expr.rhs, value_names, mut
2660 required_fields, mut required_methods)
2661 }
2662 ast.MatchExpr {
2663 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2664 required_fields, mut required_methods)
2665 for branch in expr.branches {
2666 for cond in branch.cond {
2667 collect_generic_param_member_requirements_from_expr(cond, value_names, mut
2668 required_fields, mut required_methods)
2669 }
2670 collect_generic_param_member_requirements_from_stmts(branch.stmts, value_names, mut
2671 required_fields, mut required_methods)
2672 }
2673 }
2674 ast.ModifierExpr {
2675 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2676 required_fields, mut required_methods)
2677 }
2678 ast.OrExpr {
2679 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2680 required_fields, mut required_methods)
2681 collect_generic_param_member_requirements_from_stmts(expr.stmts, value_names, mut
2682 required_fields, mut required_methods)
2683 }
2684 ast.ParenExpr {
2685 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2686 required_fields, mut required_methods)
2687 }
2688 ast.PostfixExpr {
2689 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2690 required_fields, mut required_methods)
2691 }
2692 ast.PrefixExpr {
2693 collect_generic_param_member_requirements_from_expr(expr.expr, value_names, mut
2694 required_fields, mut required_methods)
2695 }
2696 ast.SelectorExpr {
2697 if is_comptime_selector_rhs_name(expr.rhs.name) {
2698 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2699 required_fields, mut required_methods)
2700 return
2701 }
2702 if expr.lhs is ast.Ident && (expr.lhs as ast.Ident).name in value_names {
2703 required_fields[expr.rhs.name] = true
2704 }
2705 collect_generic_param_member_requirements_from_expr(expr.lhs, value_names, mut
2706 required_fields, mut required_methods)
2707 }
2708 ast.Tuple {
2709 for item in expr.exprs {
2710 collect_generic_param_member_requirements_from_expr(item, value_names, mut
2711 required_fields, mut required_methods)
2712 }
2713 }
2714 ast.UnsafeExpr {
2715 collect_generic_param_member_requirements_from_stmts(expr.stmts, value_names, mut
2716 required_fields, mut required_methods)
2717 }
2718 else {}
2719 }
2720}
2721
2722fn collect_generic_param_operator_requirements_from_stmts(stmts []ast.Stmt, mut value_names map[string]bool, container_names map[string]bool, mut required_ops map[string]bool) {
2723 for stmt in stmts {
2724 match stmt {
2725 ast.AssignStmt {
2726 for expr in stmt.rhs {
2727 collect_generic_param_operator_requirements_from_expr(expr, value_names,
2728 container_names, mut required_ops)
2729 }
2730 if stmt.op == .plus_assign {
2731 for expr in stmt.lhs {
2732 if generic_expr_is_generic_value(expr, value_names, container_names) {
2733 required_ops['add'] = true
2734 }
2735 }
2736 for expr in stmt.rhs {
2737 if generic_expr_is_generic_value(expr, value_names, container_names) {
2738 required_ops['add'] = true
2739 }
2740 }
2741 }
2742 for i, lhs in stmt.lhs {
2743 lhs_name := generic_wrapped_ident_name(lhs)
2744 if lhs_name != '' && i < stmt.rhs.len
2745 && generic_expr_is_generic_value(stmt.rhs[i], value_names, container_names) {
2746 value_names[lhs_name] = true
2747 }
2748 }
2749 for expr in stmt.lhs {
2750 collect_generic_param_operator_requirements_from_expr(expr, value_names,
2751 container_names, mut required_ops)
2752 }
2753 }
2754 ast.BlockStmt {
2755 collect_generic_param_operator_requirements_from_stmts(stmt.stmts, mut value_names,
2756 container_names, mut required_ops)
2757 }
2758 ast.ComptimeStmt {
2759 if stmt.stmt is ast.ExprStmt && stmt.stmt.expr is ast.ComptimeExpr
2760 && (stmt.stmt.expr as ast.ComptimeExpr).expr is ast.IfExpr {
2761 continue
2762 }
2763 collect_generic_param_operator_requirements_from_stmts([stmt.stmt], mut
2764 value_names, container_names, mut required_ops)
2765 }
2766 ast.DeferStmt {
2767 collect_generic_param_operator_requirements_from_stmts(stmt.stmts, mut value_names,
2768 container_names, mut required_ops)
2769 }
2770 ast.ExprStmt {
2771 collect_generic_param_operator_requirements_from_expr(stmt.expr, value_names,
2772 container_names, mut required_ops)
2773 }
2774 ast.ForStmt {
2775 if stmt.init is ast.ForInStmt {
2776 for_in := stmt.init as ast.ForInStmt
2777 if generic_expr_is_generic_container(for_in.expr, container_names) {
2778 if for_in.value is ast.Ident {
2779 value_names[(for_in.value as ast.Ident).name] = true
2780 } else if for_in.value is ast.ModifierExpr {
2781 mod_value := for_in.value as ast.ModifierExpr
2782 if mod_value.expr is ast.Ident {
2783 value_names[(mod_value.expr as ast.Ident).name] = true
2784 }
2785 }
2786 }
2787 } else if stmt.init !is ast.EmptyStmt {
2788 collect_generic_param_operator_requirements_from_stmts([stmt.init], mut
2789 value_names, container_names, mut required_ops)
2790 }
2791 collect_generic_param_operator_requirements_from_expr(stmt.cond, value_names,
2792 container_names, mut required_ops)
2793 if stmt.post !is ast.EmptyStmt {
2794 collect_generic_param_operator_requirements_from_stmts([stmt.post], mut
2795 value_names, container_names, mut required_ops)
2796 }
2797 collect_generic_param_operator_requirements_from_stmts(stmt.stmts, mut value_names,
2798 container_names, mut required_ops)
2799 }
2800 ast.ReturnStmt {
2801 for expr in stmt.exprs {
2802 collect_generic_param_operator_requirements_from_expr(expr, value_names,
2803 container_names, mut required_ops)
2804 }
2805 }
2806 else {}
2807 }
2808 }
2809}
2810
2811fn collect_generic_param_operator_requirements_from_expr(expr ast.Expr, value_names map[string]bool, container_names map[string]bool, mut required_ops map[string]bool) {
2812 match expr {
2813 ast.ArrayInitExpr {
2814 for item in expr.exprs {
2815 collect_generic_param_operator_requirements_from_expr(item, value_names,
2816 container_names, mut required_ops)
2817 }
2818 }
2819 ast.CallExpr {
2820 collect_generic_param_operator_requirements_from_expr(expr.lhs, value_names,
2821 container_names, mut required_ops)
2822 for arg in expr.args {
2823 collect_generic_param_operator_requirements_from_expr(arg, value_names,
2824 container_names, mut required_ops)
2825 }
2826 }
2827 ast.CallOrCastExpr {
2828 collect_generic_param_operator_requirements_from_expr(expr.lhs, value_names,
2829 container_names, mut required_ops)
2830 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2831 container_names, mut required_ops)
2832 }
2833 ast.ComptimeExpr {
2834 if expr.expr is ast.IfExpr {
2835 return
2836 }
2837 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2838 container_names, mut required_ops)
2839 }
2840 ast.IfExpr {
2841 collect_generic_param_operator_requirements_from_expr(expr.cond, value_names,
2842 container_names, mut required_ops)
2843 mut branch_value_names := value_names.clone()
2844 collect_generic_param_operator_requirements_from_stmts(expr.stmts, mut
2845 branch_value_names, container_names, mut required_ops)
2846 if expr.else_expr !is ast.EmptyExpr {
2847 collect_generic_param_operator_requirements_from_expr(expr.else_expr, value_names,
2848 container_names, mut required_ops)
2849 }
2850 }
2851 ast.IndexExpr {
2852 collect_generic_param_operator_requirements_from_expr(expr.lhs, value_names,
2853 container_names, mut required_ops)
2854 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2855 container_names, mut required_ops)
2856 }
2857 ast.InfixExpr {
2858 if expr.op in [.lt, .le, .gt, .ge]
2859 && (generic_expr_is_generic_value(expr.lhs, value_names, container_names)
2860 || generic_expr_is_generic_value(expr.rhs, value_names, container_names)) {
2861 required_ops['ordered'] = true
2862 }
2863 if expr.op == .plus
2864 && (generic_expr_is_generic_value(expr.lhs, value_names, container_names)
2865 || generic_expr_is_generic_value(expr.rhs, value_names, container_names)) {
2866 required_ops['add'] = true
2867 }
2868 if expr.op in [.minus, .mul, .div, .mod]
2869 && (generic_expr_is_generic_value(expr.lhs, value_names, container_names)
2870 || generic_expr_is_generic_value(expr.rhs, value_names, container_names)) {
2871 required_ops['numeric'] = true
2872 }
2873 collect_generic_param_operator_requirements_from_expr(expr.lhs, value_names,
2874 container_names, mut required_ops)
2875 collect_generic_param_operator_requirements_from_expr(expr.rhs, value_names,
2876 container_names, mut required_ops)
2877 }
2878 ast.MatchExpr {
2879 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2880 container_names, mut required_ops)
2881 for branch in expr.branches {
2882 for cond in branch.cond {
2883 collect_generic_param_operator_requirements_from_expr(cond, value_names,
2884 container_names, mut required_ops)
2885 }
2886 mut branch_value_names := value_names.clone()
2887 collect_generic_param_operator_requirements_from_stmts(branch.stmts, mut
2888 branch_value_names, container_names, mut required_ops)
2889 }
2890 }
2891 ast.ModifierExpr {
2892 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2893 container_names, mut required_ops)
2894 }
2895 ast.OrExpr {
2896 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2897 container_names, mut required_ops)
2898 mut or_value_names := value_names.clone()
2899 collect_generic_param_operator_requirements_from_stmts(expr.stmts, mut or_value_names,
2900 container_names, mut required_ops)
2901 }
2902 ast.ParenExpr {
2903 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2904 container_names, mut required_ops)
2905 }
2906 ast.PostfixExpr {
2907 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2908 container_names, mut required_ops)
2909 }
2910 ast.PrefixExpr {
2911 collect_generic_param_operator_requirements_from_expr(expr.expr, value_names,
2912 container_names, mut required_ops)
2913 }
2914 ast.SelectorExpr {
2915 collect_generic_param_operator_requirements_from_expr(expr.lhs, value_names,
2916 container_names, mut required_ops)
2917 }
2918 ast.Tuple {
2919 for item in expr.exprs {
2920 collect_generic_param_operator_requirements_from_expr(item, value_names,
2921 container_names, mut required_ops)
2922 }
2923 }
2924 ast.UnsafeExpr {
2925 mut unsafe_value_names := value_names.clone()
2926 collect_generic_param_operator_requirements_from_stmts(expr.stmts, mut
2927 unsafe_value_names, container_names, mut required_ops)
2928 }
2929 else {}
2930 }
2931}
2932
2933fn generic_expr_is_generic_container(expr ast.Expr, container_names map[string]bool) bool {
2934 if expr is ast.Ident {
2935 return expr.name in container_names
2936 }
2937 if expr is ast.IndexExpr {
2938 return generic_expr_is_generic_container(expr.lhs, container_names)
2939 }
2940 if expr is ast.ParenExpr {
2941 return generic_expr_is_generic_container(expr.expr, container_names)
2942 }
2943 return false
2944}
2945
2946fn generic_wrapped_ident_name(expr ast.Expr) string {
2947 match expr {
2948 ast.Ident {
2949 return expr.name
2950 }
2951 ast.ModifierExpr {
2952 return generic_wrapped_ident_name(expr.expr)
2953 }
2954 ast.ParenExpr {
2955 return generic_wrapped_ident_name(expr.expr)
2956 }
2957 else {}
2958 }
2959
2960 return ''
2961}
2962
2963fn generic_wrapped_ident_name_cursor(expr ast.Cursor) string {
2964 if !expr.is_valid() {
2965 return ''
2966 }
2967 match expr.kind() {
2968 .expr_ident {
2969 return expr.name()
2970 }
2971 .expr_modifier, .expr_paren {
2972 return generic_wrapped_ident_name_cursor(expr.edge(0))
2973 }
2974 else {}
2975 }
2976
2977 return ''
2978}
2979
2980fn generic_expr_is_generic_value(expr ast.Expr, value_names map[string]bool, container_names map[string]bool) bool {
2981 match expr {
2982 ast.Ident {
2983 return expr.name in value_names
2984 }
2985 ast.IndexExpr {
2986 if generic_expr_is_generic_container(expr.lhs, container_names) {
2987 return true
2988 }
2989 return generic_expr_is_generic_value(expr.lhs, value_names, container_names)
2990 }
2991 ast.CallExpr {
2992 for arg in expr.args {
2993 if generic_expr_is_generic_value(arg, value_names, container_names) {
2994 return true
2995 }
2996 }
2997 }
2998 ast.CallOrCastExpr {
2999 return generic_expr_is_generic_value(expr.expr, value_names, container_names)
3000 }
3001 ast.CastExpr {
3002 return generic_expr_is_generic_value(expr.expr, value_names, container_names)
3003 }
3004 ast.ModifierExpr {
3005 return generic_expr_is_generic_value(expr.expr, value_names, container_names)
3006 }
3007 ast.ParenExpr {
3008 return generic_expr_is_generic_value(expr.expr, value_names, container_names)
3009 }
3010 ast.PrefixExpr {
3011 return generic_expr_is_generic_value(expr.expr, value_names, container_names)
3012 }
3013 else {}
3014 }
3015
3016 return false
3017}
3018
3019fn generic_fallback_type_supports_ordered(concrete types.Type) bool {
3020 match concrete {
3021 types.Alias {
3022 return generic_fallback_type_supports_ordered(concrete.base_type)
3023 }
3024 types.Primitive {
3025 return concrete.props.has(.integer) || concrete.props.has(.float)
3026 }
3027 types.Char, types.Enum, types.ISize, types.Rune, types.String, types.USize {
3028 return true
3029 }
3030 else {}
3031 }
3032
3033 return false
3034}
3035
3036fn generic_fallback_type_supports_add(concrete types.Type) bool {
3037 match concrete {
3038 types.Alias {
3039 return generic_fallback_type_supports_add(concrete.base_type)
3040 }
3041 types.Primitive {
3042 return concrete.props.has(.integer) || concrete.props.has(.float)
3043 }
3044 types.Char, types.ISize, types.Rune, types.String, types.USize {
3045 return true
3046 }
3047 else {}
3048 }
3049
3050 return false
3051}
3052
3053fn generic_fallback_type_supports_numeric(concrete types.Type) bool {
3054 match concrete {
3055 types.Alias {
3056 return generic_fallback_type_supports_numeric(concrete.base_type)
3057 }
3058 types.Primitive {
3059 return concrete.props.has(.integer) || concrete.props.has(.float)
3060 }
3061 types.Char, types.ISize, types.Rune, types.USize {
3062 return true
3063 }
3064 else {}
3065 }
3066
3067 return false
3068}
3069
3070fn (mut g Gen) generic_fallback_struct_type(concrete types.Type) ?types.Struct {
3071 match concrete {
3072 types.Struct {
3073 c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
3074 full_struct_type := g.lookup_struct_type_by_c_name(c_name)
3075 if full_struct_type.fields.len > 0 || full_struct_type.embedded.len > 0 {
3076 return full_struct_type
3077 }
3078 return concrete
3079 }
3080 types.Alias {
3081 return g.generic_fallback_struct_type(concrete.base_type)
3082 }
3083 types.Pointer {
3084 return g.generic_fallback_struct_type(concrete.base_type)
3085 }
3086 else {}
3087 }
3088
3089 return none
3090}
3091
3092fn (mut g Gen) generic_fallback_type_has_field(concrete types.Type, field_name string) bool {
3093 struct_type := g.generic_fallback_struct_type(concrete) or { return false }
3094 for field in struct_type.fields {
3095 if field.name == field_name {
3096 return true
3097 }
3098 }
3099 for embedded in struct_type.embedded {
3100 if g.generic_fallback_type_has_field(types.Type(embedded), field_name) {
3101 return true
3102 }
3103 }
3104 return false
3105}
3106
3107fn (mut g Gen) generic_fallback_type_has_method(concrete types.Type, method_name string) bool {
3108 if g.env == unsafe { nil } {
3109 concrete_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
3110 if _ := g.resolve_method_on_concrete_type(concrete_c_name, method_name) {
3111 return true
3112 }
3113 return false
3114 }
3115 struct_type := g.generic_fallback_struct_type(concrete) or { return false }
3116 mut names := []string{}
3117 if struct_type.name != '' {
3118 names << struct_type.name
3119 if struct_type.name.contains('__') {
3120 names << struct_type.name.all_after_last('__')
3121 }
3122 }
3123 for type_name in names {
3124 if _ := g.env.lookup_method(type_name, method_name) {
3125 return true
3126 }
3127 if _ := g.resolve_method_on_concrete_type(type_name, method_name) {
3128 return true
3129 }
3130 }
3131 concrete_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
3132 if _ := g.resolve_method_on_concrete_type(concrete_c_name, method_name) {
3133 return true
3134 }
3135 for embedded in struct_type.embedded {
3136 if g.generic_fallback_type_has_method(types.Type(embedded), method_name) {
3137 return true
3138 }
3139 }
3140 return false
3141}
3142
3143fn generic_params_are_single_t(generic_params []string) bool {
3144 return generic_params.len == 1 && generic_params[0] == 'T'
3145}
3146
3147fn (g &Gen) is_stdatomic_atomic_val_method(node ast.FnDecl, generic_params []string) bool {
3148 // AtomicVal[T] is emitted as one fallback struct in V2 for now. Restrict its
3149 // receiver methods to the same integer binding used by the struct body, so
3150 // calls like AtomicVal[int].add() do not fall through to the f64 `$else`
3151 // branch and panic as unreachable.
3152 return g.cur_module == 'stdatomic' && node.is_method
3153 && generic_params_are_single_t(generic_params)
3154 && node.name in ['load', 'store', 'add', 'sub', 'swap', 'compare_and_swap']
3155}
3156
3157fn is_json2_runtime_internal_type(concrete types.Type) bool {
3158 match concrete {
3159 types.Struct {
3160 return concrete.name in ['array', 'map', 'DenseArray']
3161 }
3162 types.Alias {
3163 if concrete.name in ['MapHashFn', 'MapEqFn', 'MapCloneFn', 'MapFreeFn'] {
3164 return true
3165 }
3166 return is_json2_runtime_internal_type(concrete.base_type)
3167 }
3168 types.FnType {
3169 return true
3170 }
3171 else {}
3172 }
3173
3174 return false
3175}
3176
3177// build_generic_spec_index precomputes a reverse index from function names
3178// to matching keys in env.generic_types. This avoids O(n*m) iteration
3179// in generic_fn_specializations (called per generic fn per file).
3180fn (mut g Gen) build_generic_spec_index() {
3181 g.generic_spec_index = map[string][]string{}
3182 if g.env == unsafe { nil } {
3183 return
3184 }
3185 // Index both env.generic_types and late_generic_specs keys
3186 mut all_keys := map[string]bool{}
3187 for key, _ in g.env.generic_types {
3188 all_keys[key] = true
3189 }
3190 for key, _ in g.late_generic_specs {
3191 all_keys[key] = true
3192 }
3193 for key, _ in all_keys {
3194 // Extract the base function name from the key.
3195 // Keys can be: "fn_name", "fn_name[T]", "module.fn_name[T]"
3196 mut fn_name := key
3197 bracket_idx := key.index_u8(`[`)
3198 if bracket_idx > 0 {
3199 fn_name = key[..bracket_idx]
3200 }
3201 // If qualified (contains '.'), also index by the short name
3202 dot_idx := fn_name.last_index_u8(`.`)
3203 if dot_idx > 0 && dot_idx < fn_name.len - 1 {
3204 short_name := fn_name[dot_idx + 1..]
3205 if short_name.len > 0 {
3206 g.generic_spec_index[short_name] << key
3207 }
3208 }
3209 double_underscore_idx := fn_name.last_index('__') or { -1 }
3210 if double_underscore_idx > 0 && double_underscore_idx < fn_name.len - 2 {
3211 short_name := fn_name[double_underscore_idx + 2..]
3212 if short_name.len > 0 {
3213 g.generic_spec_index[short_name] << key
3214 }
3215 }
3216 g.generic_spec_index[fn_name] << key
3217 }
3218}
3219
3220fn (g &Gen) late_generic_spec_count() int {
3221 mut count := 0
3222 for _, specs in g.late_generic_specs {
3223 count += specs.len
3224 }
3225 return count
3226}
3227
3228fn (mut g Gen) discover_nested_generic_specs() {
3229 if g.env == unsafe { nil } {
3230 return
3231 }
3232 mut scanned_specs := map[string]bool{}
3233 for _ in 0 .. 8 {
3234 g.build_generic_spec_index()
3235 before := g.late_generic_spec_count()
3236 if g.has_flat() {
3237 for i in 0 .. g.flat.files.len {
3238 fc := g.flat.file_cursor(i)
3239 g.set_file_cursor_module(fc)
3240 stmts := fc.stmts()
3241 for j in 0 .. stmts.len() {
3242 stmt := stmts.at(j)
3243 if stmt.kind() == .stmt_fn_decl {
3244 g.discover_nested_generic_specs_for_cursor(stmt, mut scanned_specs)
3245 }
3246 }
3247 }
3248 } else {
3249 for file in g.files {
3250 g.set_file_module(file)
3251 for stmt in file.stmts {
3252 if stmt is ast.FnDecl {
3253 g.discover_nested_generic_specs_for_decl(stmt, mut scanned_specs)
3254 }
3255 }
3256 }
3257 }
3258 if g.late_generic_spec_count() == before {
3259 break
3260 }
3261 }
3262 g.build_generic_spec_index()
3263}
3264
3265fn (mut g Gen) discover_nested_generic_specs_for_cursor(stmt ast.Cursor, mut scanned_specs map[string]bool) {
3266 decl := stmt.fn_decl_signature()
3267 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
3268 return
3269 }
3270 if g.generic_fn_param_names(decl).len == 0 {
3271 return
3272 }
3273 if stmt.list_at(3).len() == 0 {
3274 return
3275 }
3276 specs := if fn_cursor_has_comptime_stmt(stmt) || g.fn_cursor_body_has_generic_call(stmt) {
3277 g.generic_fn_specializations(decl)
3278 } else {
3279 g.direct_generic_fn_specializations(decl)
3280 }
3281 g.discover_nested_generic_specs_from_body_source(decl, stmt, true, specs, mut scanned_specs)
3282}
3283
3284fn (mut g Gen) discover_nested_generic_specs_for_decl(decl ast.FnDecl, mut scanned_specs map[string]bool) {
3285 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
3286 return
3287 }
3288 if g.generic_fn_param_names(decl).len == 0 {
3289 return
3290 }
3291 specs := if fn_has_comptime_stmt(decl) || g.fn_body_has_generic_call(decl) {
3292 g.generic_fn_specializations(decl)
3293 } else {
3294 g.direct_generic_fn_specializations(decl)
3295 }
3296 g.discover_nested_generic_specs_from_body_source(decl, ast.Cursor{}, false, specs, mut
3297 scanned_specs)
3298}
3299
3300fn (mut g Gen) discover_nested_generic_specs_from_body_source(decl ast.FnDecl, body_cursor ast.Cursor, use_cursor bool, specs []GenericFnSpecialization, mut scanned_specs map[string]bool) {
3301 if specs.len == 0 {
3302 return
3303 }
3304 prev_fn_name := g.cur_fn_name
3305 prev_fn_c_name := g.cur_fn_c_name
3306 prev_fn_scope := g.cur_fn_scope
3307 mut prev_active_generic_types := g.active_generic_types.clone()
3308 mut prev_runtime_local_types := g.runtime_local_types.clone()
3309 mut prev_runtime_decl_types := g.runtime_decl_types.clone()
3310 mut prev_not_local_var_cache := g.not_local_var_cache.clone()
3311 mut prev_is_module_ident_cache := g.is_module_ident_cache.clone()
3312 mut prev_resolved_module_names := g.resolved_module_names.clone()
3313 mut prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
3314 scope_fn_name := if decl.is_method {
3315 v_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
3316 if v_type_name != '' {
3317 '${v_type_name}__${decl.name}'
3318 } else {
3319 decl.name
3320 }
3321 } else {
3322 decl.name
3323 }
3324 for spec in specs {
3325 scan_key := '${g.cur_module}.${scope_fn_name}:${spec.name}'
3326 if scan_key in scanned_specs {
3327 continue
3328 }
3329 scanned_specs[scan_key] = true
3330 g.cur_fn_name = decl.name
3331 g.cur_fn_c_name = spec.name
3332 g.cur_fn_scope = unsafe { nil }
3333 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
3334 g.cur_fn_scope = fn_scope
3335 }
3336 g.active_generic_types = spec.generic_types.clone()
3337 g.runtime_local_types = map[string]string{}
3338 g.runtime_decl_types = map[string]string{}
3339 g.not_local_var_cache = map[string]bool{}
3340 g.is_module_ident_cache = map[string]bool{}
3341 g.resolved_module_names = map[string]string{}
3342 g.cur_fn_generic_params = map[string]string{}
3343 g.seed_fn_scan_runtime_types(decl, spec.name)
3344 g.scan_fn_body_for_generic_types_from_source(decl, body_cursor, use_cursor, spec.name)
3345 }
3346 g.cur_fn_name = prev_fn_name
3347 g.cur_fn_c_name = prev_fn_c_name
3348 g.cur_fn_scope = prev_fn_scope
3349 g.active_generic_types = prev_active_generic_types.move()
3350 g.runtime_local_types = prev_runtime_local_types.move()
3351 g.runtime_decl_types = prev_runtime_decl_types.move()
3352 g.not_local_var_cache = prev_not_local_var_cache.move()
3353 g.is_module_ident_cache = prev_is_module_ident_cache.move()
3354 g.resolved_module_names = prev_resolved_module_names.move()
3355 g.cur_fn_generic_params = prev_cur_fn_generic_params.move()
3356}
3357
3358fn (mut g Gen) discover_direct_generic_call_specs() {
3359 if g.env == unsafe { nil } {
3360 return
3361 }
3362 g.build_generic_spec_index()
3363 before := g.late_generic_spec_count()
3364 if g.has_flat() {
3365 for i in 0 .. g.flat.files.len {
3366 fc := g.flat.file_cursor(i)
3367 g.set_file_cursor_module(fc)
3368 stmts := fc.stmts()
3369 for j in 0 .. stmts.len() {
3370 stmt := stmts.at(j)
3371 if stmt.kind() == .stmt_fn_decl {
3372 g.discover_direct_generic_call_specs_for_cursor(stmt)
3373 }
3374 }
3375 }
3376 } else {
3377 for file in g.files {
3378 g.set_file_module(file)
3379 for stmt in file.stmts {
3380 if stmt is ast.FnDecl {
3381 g.discover_direct_generic_call_specs_for_decl(stmt)
3382 }
3383 }
3384 }
3385 }
3386 if g.late_generic_spec_count() != before {
3387 g.build_generic_spec_index()
3388 }
3389}
3390
3391fn (mut g Gen) discover_direct_generic_call_specs_for_cursor(stmt ast.Cursor) {
3392 decl := stmt.fn_decl_signature()
3393 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
3394 return
3395 }
3396 if g.generic_fn_param_names(decl).len != 0 {
3397 return
3398 }
3399 if stmt.list_at(3).len() == 0 {
3400 return
3401 }
3402 g.discover_direct_generic_call_specs_from_body_source(decl, stmt, true)
3403}
3404
3405fn (mut g Gen) discover_direct_generic_call_specs_for_decl(decl ast.FnDecl) {
3406 if !g.should_emit_fn_decl_cached(g.cur_module, decl) {
3407 return
3408 }
3409 if g.generic_fn_param_names(decl).len != 0 {
3410 return
3411 }
3412 g.discover_direct_generic_call_specs_from_body_source(decl, ast.Cursor{}, false)
3413}
3414
3415fn (mut g Gen) discover_direct_generic_call_specs_from_body_source(decl ast.FnDecl, body_cursor ast.Cursor, use_cursor bool) {
3416 prev_fn_name := g.cur_fn_name
3417 prev_fn_c_name := g.cur_fn_c_name
3418 prev_fn_scope := g.cur_fn_scope
3419 mut prev_active_generic_types := g.active_generic_types.clone()
3420 mut prev_runtime_local_types := g.runtime_local_types.clone()
3421 mut prev_runtime_decl_types := g.runtime_decl_types.clone()
3422 mut prev_not_local_var_cache := g.not_local_var_cache.clone()
3423 mut prev_is_module_ident_cache := g.is_module_ident_cache.clone()
3424 mut prev_resolved_module_names := g.resolved_module_names.clone()
3425 mut prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
3426 scope_fn_name := if decl.is_method {
3427 v_type_name := g.receiver_type_to_scope_name(decl.receiver.typ)
3428 if v_type_name != '' {
3429 '${v_type_name}__${decl.name}'
3430 } else {
3431 decl.name
3432 }
3433 } else {
3434 decl.name
3435 }
3436 g.cur_fn_name = decl.name
3437 g.cur_fn_c_name = g.get_fn_name(decl)
3438 g.cur_fn_scope = unsafe { nil }
3439 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
3440 g.cur_fn_scope = fn_scope
3441 }
3442 g.active_generic_types = map[string]types.Type{}
3443 g.runtime_local_types = map[string]string{}
3444 g.runtime_decl_types = map[string]string{}
3445 g.not_local_var_cache = map[string]bool{}
3446 g.is_module_ident_cache = map[string]bool{}
3447 g.resolved_module_names = map[string]string{}
3448 g.cur_fn_generic_params = map[string]string{}
3449 g.seed_fn_scan_runtime_types(decl, g.cur_fn_c_name)
3450 g.scan_fn_body_for_generic_types_from_source(decl, body_cursor, use_cursor, 'direct')
3451 g.cur_fn_name = prev_fn_name
3452 g.cur_fn_c_name = prev_fn_c_name
3453 g.cur_fn_scope = prev_fn_scope
3454 g.active_generic_types = prev_active_generic_types.move()
3455 g.runtime_local_types = prev_runtime_local_types.move()
3456 g.runtime_decl_types = prev_runtime_decl_types.move()
3457 g.not_local_var_cache = prev_not_local_var_cache.move()
3458 g.is_module_ident_cache = prev_is_module_ident_cache.move()
3459 g.resolved_module_names = prev_resolved_module_names.move()
3460 g.cur_fn_generic_params = prev_cur_fn_generic_params.move()
3461}
3462
3463fn (mut g Gen) seed_fn_scan_runtime_types(node ast.FnDecl, fn_name string) {
3464 if node.is_method && !node.is_static && node.receiver.name != '' {
3465 mut receiver_type := ''
3466 if sig_param_types := g.fn_param_types[fn_name] {
3467 if sig_param_types.len > 0 {
3468 receiver_type = normalize_signature_type_name(sig_param_types[0], '')
3469 }
3470 }
3471 if receiver_type == '' {
3472 receiver_type = g.expr_type_to_c(node.receiver.typ)
3473 }
3474 if receiver_type != '' {
3475 g.remember_runtime_local_type(node.receiver.name, receiver_type)
3476 }
3477 }
3478 mut param_ptr_offset := 0
3479 if node.is_method && !node.is_static && node.receiver.name != '' {
3480 if sig_param_types := g.fn_param_types[fn_name] {
3481 if sig_param_types.len >= node.typ.params.len + 1 {
3482 param_ptr_offset = 1
3483 }
3484 } else if !node.is_static {
3485 param_ptr_offset = 1
3486 }
3487 }
3488 if g.needs_implicit_veb_ctx_param(&node, fn_name) {
3489 ctx_type := g.implicit_veb_ctx_c_type()
3490 g.remember_runtime_local_type('ctx', ctx_type)
3491 param_ptr_offset++
3492 }
3493 for param_idx, param in node.typ.params {
3494 mut param_type := g.fn_param_signature_c_type(param)
3495 mut param_is_ptr := param.is_mut
3496 if sig_param_types := g.fn_param_types[fn_name] {
3497 sig_idx := param_idx + param_ptr_offset
3498 if sig_idx < sig_param_types.len {
3499 sig_type := normalize_signature_type_name(sig_param_types[sig_idx], '')
3500 if sig_type != '' {
3501 param_type = sig_type
3502 if sig_type.ends_with('*') {
3503 param_is_ptr = true
3504 }
3505 }
3506 }
3507 }
3508 if param_type != '' && param.name != '' {
3509 ptype := if param_is_ptr && !param_type.ends_with('*') {
3510 param_type + '*'
3511 } else {
3512 param_type
3513 }
3514 g.remember_runtime_local_type(param.name, ptype)
3515 if param.name == 'array' {
3516 g.remember_runtime_local_type('_v_array', ptype)
3517 }
3518 }
3519 placeholder := direct_generic_placeholder_name(param.typ)
3520 if placeholder != '' {
3521 g.cur_fn_generic_params[param.name] = placeholder
3522 }
3523 }
3524}
3525
3526fn generic_param_names(params []ast.Expr) []string {
3527 mut seen := map[string]bool{}
3528 mut names := []string{}
3529 for param in params {
3530 collect_generic_placeholder_names_from_expr(param, mut seen, mut names)
3531 }
3532 return names
3533}
3534
3535fn (g &Gen) fallback_generic_bindings_for_names(param_names []string) ?map[string]types.Type {
3536 if param_names.len == 0 || g.env == unsafe { nil } {
3537 return none
3538 }
3539 mut bindings := map[string]types.Type{}
3540 for param_name in param_names {
3541 mut found := false
3542 for _, generic_maps in g.env.generic_types {
3543 for generic_types in generic_maps {
3544 concrete := generic_types[param_name] or { continue }
3545 if concrete.name() == param_name || type_contains_generic_placeholder(concrete) {
3546 continue
3547 }
3548 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
3549 continue
3550 }
3551 if !g.generic_specialization_belongs_to_emit_modules({
3552 param_name: concrete
3553 }) {
3554 continue
3555 }
3556 bindings[param_name] = concrete
3557 found = true
3558 break
3559 }
3560 if found {
3561 break
3562 }
3563 }
3564 if !found {
3565 return none
3566 }
3567 }
3568 return bindings
3569}
3570
3571fn (mut g Gen) direct_generic_fn_specializations(node ast.FnDecl) []GenericFnSpecialization {
3572 return g.generic_fn_specializations_with_fallback(node, false)
3573}
3574
3575fn (mut g Gen) generic_fn_specializations(node ast.FnDecl) []GenericFnSpecialization {
3576 return g.generic_fn_specializations_with_fallback(node, true)
3577}
3578
3579fn (mut g Gen) generic_fn_specializations_for_emit_scope(node ast.FnDecl) []GenericFnSpecialization {
3580 mut specs := if g.emit_files.len == 0 {
3581 g.generic_fn_specializations(node)
3582 } else {
3583 g.direct_generic_fn_specializations(node)
3584 }
3585 mut seen := map[string]bool{}
3586 for spec in specs {
3587 seen[spec.name] = true
3588 }
3589 mut needed_names := g.called_fn_names.clone()
3590 for spec in g.late_generic_fn_specializations(node) {
3591 needed_names[spec.name] = true
3592 if spec.name in seen {
3593 continue
3594 }
3595 seen[spec.name] = true
3596 specs << spec
3597 }
3598 generic_params := g.generic_fn_param_names(node)
3599 if needed_names.len > 0 && generic_params.len > 0 {
3600 mut prev_generic_types := g.active_generic_types.move()
3601 g.active_generic_types = map[string]types.Type{}
3602 base_name := g.get_fn_name(node)
3603 g.active_generic_types = prev_generic_types.move()
3604 prefix := '${base_name}_T_'
3605 for called_name, _ in needed_names {
3606 if !called_name.starts_with(prefix) || called_name in seen {
3607 continue
3608 }
3609 generic_types := g.generic_types_from_specialized_fn_name(node, called_name) or {
3610 continue
3611 }
3612 seen[called_name] = true
3613 specs << GenericFnSpecialization{
3614 name: called_name
3615 generic_types: generic_types.clone()
3616 }
3617 }
3618 }
3619 return specs
3620}
3621
3622fn (mut g Gen) generic_fn_specializations_for_emit_scope_with_receiver_bindings(node ast.FnDecl) []GenericFnSpecialization {
3623 specs := g.generic_fn_specializations_for_emit_scope(node)
3624 recv_params := receiver_generic_param_names(node)
3625 if recv_params.len == 0 {
3626 return specs
3627 }
3628 mut receiver_bindings := g.get_all_receiver_generic_bindings(node)
3629 if receiver_bindings.len == 0 {
3630 if bindings := g.get_receiver_generic_bindings(node) {
3631 receiver_bindings << bindings
3632 }
3633 }
3634 if receiver_bindings.len == 0 {
3635 return specs
3636 }
3637 mut out := []GenericFnSpecialization{}
3638 mut seen := map[string]bool{}
3639 for spec in specs {
3640 for bindings in receiver_bindings {
3641 mut generic_types := bindings.clone()
3642 for param_name, concrete in spec.generic_types {
3643 generic_types[param_name] = concrete
3644 }
3645 name := g.specialized_fn_name(node, generic_types)
3646 if name == '' || name in seen {
3647 continue
3648 }
3649 seen[name] = true
3650 out << GenericFnSpecialization{
3651 name: name
3652 generic_types: generic_types.clone()
3653 }
3654 }
3655 }
3656 if out.len == 0 {
3657 return specs
3658 }
3659 return out
3660}
3661
3662fn (mut g Gen) generic_fn_specializations_with_fallback(node ast.FnDecl, include_fallback bool) []GenericFnSpecialization {
3663 generic_params := g.generic_fn_param_names(node)
3664 if generic_params.len == 0 || g.env == unsafe { nil } {
3665 return []GenericFnSpecialization{}
3666 }
3667 mut specs := []GenericFnSpecialization{}
3668 mut seen := map[string]bool{}
3669 // Use precomputed index for O(1) lookup instead of iterating all generic types.
3670 matching_keys := g.generic_spec_index[node.name]
3671 for key in matching_keys {
3672 if !g.generic_key_matches_decl(node, key) {
3673 continue
3674 }
3675 // Merge env specs and late-discovered comptime specs for this key
3676 mut all_maps := g.env.generic_types[key].clone()
3677 for late_spec in g.late_generic_specs[key] {
3678 all_maps << late_spec
3679 }
3680 for generic_types in all_maps {
3681 mut skip_spec := false
3682 mut normalized_generic_types := generic_types.clone()
3683 for param_name in generic_params {
3684 concrete0 := generic_types[param_name] or {
3685 skip_spec = true
3686 break
3687 }
3688 concrete := normalize_generic_concrete_type(concrete0)
3689 normalized_generic_types[param_name] = concrete
3690 // `void` can be discovered from dereferencing `voidptr` in
3691 // comptime-generic code, but a C value parameter of type `void`
3692 // is invalid. Keep `voidptr` specializations, skip only true void.
3693 if concrete.name() == 'void' || concrete.name() == param_name
3694 || type_contains_generic_placeholder(concrete)
3695 || !g.generic_concrete_type_is_runtime_specializable(concrete) {
3696 skip_spec = true
3697 break
3698 }
3699 }
3700 if skip_spec
3701 || !g.generic_specialization_belongs_to_emit_modules(normalized_generic_types) {
3702 continue
3703 }
3704 mut body_requirements_satisfied := true
3705 for param_name in generic_params {
3706 concrete := normalized_generic_types[param_name] or { continue }
3707 param_ok := g.generic_fallback_type_satisfies_body_requirements_for_param(node,
3708 param_name, concrete, 0)
3709 if !param_ok {
3710 body_requirements_satisfied = false
3711 break
3712 }
3713 }
3714 if !body_requirements_satisfied {
3715 continue
3716 }
3717 spec_name := g.specialized_fn_name(node, normalized_generic_types)
3718 if spec_name == '' || spec_name in seen {
3719 continue
3720 }
3721 seen[spec_name] = true
3722 specs << GenericFnSpecialization{
3723 name: spec_name
3724 generic_types: normalized_generic_types.clone()
3725 }
3726 }
3727 }
3728 if g.cur_module == 'json2' && node.is_method && node.name == 'decode_string'
3729 && generic_params_are_single_t(generic_params) {
3730 generic_types := {
3731 'T': types.Type(types.string_)
3732 }
3733 spec_name := g.specialized_fn_name(node, generic_types)
3734 if spec_name != '' && spec_name !in seen {
3735 seen[spec_name] = true
3736 specs << GenericFnSpecialization{
3737 name: spec_name
3738 generic_types: generic_types.clone()
3739 }
3740 }
3741 }
3742 if g.is_stdatomic_atomic_val_method(node, generic_params) {
3743 generic_types := {
3744 'T': types.Type(types.int_)
3745 }
3746 spec_name := g.specialized_fn_name(node, generic_types)
3747 if spec_name != '' && spec_name !in seen {
3748 seen[spec_name] = true
3749 specs << GenericFnSpecialization{
3750 name: spec_name
3751 generic_types: generic_types.clone()
3752 }
3753 }
3754 return specs
3755 }
3756 use_json2_decode_fallback := g.cur_module == 'json2'
3757 && generic_params_are_single_t(generic_params)
3758 && ((!node.is_method && node.name in ['decode_struct_key', 'check_required_struct_fields'])
3759 || (node.is_method && node.name in ['decode_value', 'cached_struct_field_infos']))
3760 use_json2_encode_fallback := g.cur_module == 'json2'
3761 && generic_params_are_single_t(generic_params)
3762 && ((!node.is_method && node.name == 'encode') || (node.is_method
3763 && node.name in ['encode_value', 'encode_struct_fields', 'cached_field_infos']))
3764 use_json2_encode_helper_fallback := g.cur_module == 'json2'
3765 && generic_params_are_single_t(generic_params)
3766 && ((!node.is_method && node.name == 'enum_uses_json_as_number')
3767 || (node.is_method && node.name == 'encode_number'))
3768 use_json2_fallback := use_json2_decode_fallback || use_json2_encode_fallback
3769 || use_json2_encode_helper_fallback
3770 if generic_params.len == 0 || !include_fallback {
3771 return specs
3772 }
3773 if !use_json2_fallback {
3774 return specs
3775 }
3776 // Fallback: nested generic helpers inside another specialized generic function
3777 // may only record placeholder bindings like `T -> T` in env.generic_types.
3778 // Reuse concrete bindings seen for the same generic parameter names elsewhere.
3779 if generic_params.len == 1 {
3780 param_name := generic_params[0]
3781 mut fallback_types := map[string]types.Type{}
3782 allow_direct_bound_fallback := generic_param_accepts_direct_fallback_binding(node,
3783 param_name)
3784 if use_json2_decode_fallback {
3785 g.add_json2_decode_root_fallback_types(mut fallback_types)
3786 if node.is_method && node.name == 'cached_struct_field_infos' {
3787 g.add_json2_fallback_types_from_specialized_fns(mut fallback_types, [
3788 'json2__Decoder__decode_value_T_',
3789 ])
3790 }
3791 } else if use_json2_encode_fallback {
3792 g.add_json2_encode_root_fallback_types(mut fallback_types)
3793 if node.is_method && node.name == 'cached_field_infos' {
3794 g.add_json2_fallback_types_from_specialized_fns(mut fallback_types, [
3795 'json2__Encoder__encode_value_T_',
3796 'json2__Encoder__encode_struct_fields_T_',
3797 ])
3798 }
3799 } else if use_json2_encode_helper_fallback {
3800 if node.is_method && node.name == 'encode_number' {
3801 add_json2_encode_number_fallback_types(mut fallback_types)
3802 } else {
3803 g.add_json2_encode_enum_fallback_types(mut fallback_types)
3804 }
3805 } else {
3806 for _, generic_maps in g.env.generic_types {
3807 for generic_types in generic_maps {
3808 for _, concrete0 in generic_types {
3809 concrete := normalize_generic_concrete_type(concrete0)
3810 if derived := generic_container_fallback_type(node, param_name, concrete) {
3811 if derived.name() != param_name
3812 && !type_contains_generic_placeholder(derived)
3813 && generic_concrete_type_is_runtime_specializable(derived)
3814 && g.accepts_broad_generic_fallback_type(node, derived) {
3815 fallback_types[derived.name()] = derived
3816 }
3817 }
3818 }
3819 if allow_direct_bound_fallback {
3820 if bound_concrete := generic_types[param_name] {
3821 concrete := normalize_generic_concrete_type(bound_concrete)
3822 if concrete.name() == param_name
3823 || type_contains_generic_placeholder(concrete)
3824 || !g.generic_concrete_type_is_runtime_specializable(concrete) {
3825 continue
3826 }
3827 if !g.accepts_broad_generic_fallback_type(node, concrete) {
3828 continue
3829 }
3830 fallback_types[concrete.name()] = concrete
3831 }
3832 }
3833 }
3834 }
3835 // Also include late-discovered comptime specs
3836 for _, late_maps in g.late_generic_specs {
3837 for late_spec in late_maps {
3838 for _, concrete0 in late_spec {
3839 concrete := normalize_generic_concrete_type(concrete0)
3840 if derived := generic_container_fallback_type(node, param_name, concrete) {
3841 if derived.name() != param_name
3842 && !type_contains_generic_placeholder(derived)
3843 && generic_concrete_type_is_runtime_specializable(derived)
3844 && g.accepts_broad_generic_fallback_type(node, derived) {
3845 fallback_types[derived.name()] = derived
3846 }
3847 }
3848 }
3849 if allow_direct_bound_fallback {
3850 if bound_concrete := late_spec[param_name] {
3851 concrete := normalize_generic_concrete_type(bound_concrete)
3852 if concrete.name() == param_name
3853 || type_contains_generic_placeholder(concrete)
3854 || !g.generic_concrete_type_is_runtime_specializable(concrete) {
3855 continue
3856 }
3857 if !g.accepts_broad_generic_fallback_type(node, concrete) {
3858 continue
3859 }
3860 fallback_types[concrete.name()] = concrete
3861 }
3862 }
3863 }
3864 }
3865 }
3866 if g.cur_module == 'json2' && node.is_method
3867 && node.name in ['encode_value', 'encode_struct_fields'] && param_name == 'T' {
3868 add_json2_encode_value_nested_fallback_types(mut fallback_types)
3869 }
3870 for _, concrete in fallback_types {
3871 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
3872 continue
3873 }
3874 if !g.accepts_broad_generic_fallback_type(node, concrete) {
3875 continue
3876 }
3877 generic_types := {
3878 param_name: concrete
3879 }
3880 if !g.generic_specialization_belongs_to_emit_modules(generic_types) {
3881 continue
3882 }
3883 spec_name := g.specialized_fn_name(node, generic_types)
3884 if spec_name == '' || spec_name in seen {
3885 continue
3886 }
3887 seen[spec_name] = true
3888 specs << GenericFnSpecialization{
3889 name: spec_name
3890 generic_types: generic_types.clone()
3891 }
3892 }
3893 }
3894 if specs.len == 0 && generic_params.len == 1 {
3895 if bindings := g.fallback_generic_bindings_for_names(generic_params) {
3896 mut bindings_match_param_shape := true
3897 for param_name, _ in bindings {
3898 if !generic_param_accepts_direct_fallback_binding(node, param_name) {
3899 bindings_match_param_shape = false
3900 break
3901 }
3902 }
3903 if !bindings_match_param_shape {
3904 return specs
3905 }
3906 mut bindings_supported := true
3907 for _, concrete in bindings {
3908 if !g.generic_concrete_type_is_runtime_specializable(concrete)
3909 || !g.accepts_broad_generic_fallback_type(node, concrete) {
3910 bindings_supported = false
3911 break
3912 }
3913 }
3914 if bindings_supported && !g.generic_specialization_belongs_to_emit_modules(bindings) {
3915 bindings_supported = false
3916 }
3917 if !bindings_supported {
3918 return specs
3919 }
3920 prev_generic_types := g.active_generic_types.clone()
3921 g.active_generic_types = bindings.clone()
3922 spec_name := g.get_fn_name(node)
3923 g.active_generic_types = prev_generic_types.clone()
3924 if spec_name != '' && spec_name !in seen {
3925 seen[spec_name] = true
3926 specs << GenericFnSpecialization{
3927 name: spec_name
3928 generic_types: bindings.clone()
3929 }
3930 }
3931 }
3932 }
3933 return specs
3934}
3935
3936fn generic_param_accepts_direct_fallback_binding(node ast.FnDecl, param_name string) bool {
3937 value_names, container_names := generic_param_value_and_container_names(node, param_name)
3938 return value_names.len > 0 || container_names.len == 0
3939}
3940
3941fn (g &Gen) generic_specialization_belongs_to_emit_modules(generic_types map[string]types.Type) bool {
3942 if g.cache_bundle_name.len == 0 {
3943 return true
3944 }
3945 for _, concrete0 in generic_types {
3946 concrete := normalize_generic_concrete_type(concrete0)
3947 c_name := g.types_type_to_c(concrete).trim_space()
3948 if c_name != '' && !g.generic_concrete_c_name_belongs_to_emit_modules(c_name) {
3949 return false
3950 }
3951 }
3952 return true
3953}
3954
3955fn (g &Gen) module_has_emit_file(module_name string) bool {
3956 if module_name in g.emit_file_modules {
3957 return true
3958 }
3959 if g.emit_file_modules.len > 0 || g.declared_type_names_in_emit_files.len > 0 {
3960 return false
3961 }
3962 if g.has_flat() {
3963 for i in 0 .. g.flat.files.len {
3964 fc := g.flat.file_cursor(i)
3965 if flat_file_module_name(fc) != module_name {
3966 continue
3967 }
3968 file_name := fc.name()
3969 if os.norm_path(file_name) in g.emit_files
3970 || os.norm_path(os.abs_path(file_name)) in g.emit_files {
3971 return true
3972 }
3973 }
3974 return false
3975 }
3976 for file in g.files {
3977 if file_module_name(file) != module_name {
3978 continue
3979 }
3980 if os.norm_path(file.name) in g.emit_files
3981 || os.norm_path(os.abs_path(file.name)) in g.emit_files {
3982 return true
3983 }
3984 }
3985 return false
3986}
3987
3988fn (g &Gen) unqualified_type_name_declared_in_emit_files(name string) bool {
3989 if name in g.declared_type_names_in_emit_files {
3990 return true
3991 }
3992 if g.emit_file_modules.len > 0 || g.declared_type_names_in_emit_files.len > 0 {
3993 return false
3994 }
3995 if g.has_flat() {
3996 for i in 0 .. g.flat.files.len {
3997 fc := g.flat.file_cursor(i)
3998 file_name := fc.name()
3999 if !(os.norm_path(file_name) in g.emit_files
4000 || os.norm_path(os.abs_path(file_name)) in g.emit_files) {
4001 continue
4002 }
4003 module_name := flat_file_module_name(fc)
4004 stmts := fc.stmts()
4005 for j in 0 .. stmts.len() {
4006 stmt := stmts.at(j)
4007 match stmt.kind() {
4008 .stmt_struct_decl, .stmt_enum_decl, .stmt_interface_decl, .stmt_type_decl {
4009 if type_decl_name_matches_cache_alias(stmt.name(), module_name, name) {
4010 return true
4011 }
4012 }
4013 else {}
4014 }
4015 }
4016 }
4017 return false
4018 }
4019 for file in g.files {
4020 if !(os.norm_path(file.name) in g.emit_files
4021 || os.norm_path(os.abs_path(file.name)) in g.emit_files) {
4022 continue
4023 }
4024 module_name := file_module_name(file)
4025 for stmt in file.stmts {
4026 match stmt {
4027 ast.StructDecl {
4028 if type_decl_name_matches_cache_alias(stmt.name, module_name, name) {
4029 return true
4030 }
4031 }
4032 ast.EnumDecl {
4033 if type_decl_name_matches_cache_alias(stmt.name, module_name, name) {
4034 return true
4035 }
4036 }
4037 ast.InterfaceDecl {
4038 if type_decl_name_matches_cache_alias(stmt.name, module_name, name) {
4039 return true
4040 }
4041 }
4042 ast.TypeDecl {
4043 if type_decl_name_matches_cache_alias(stmt.name, module_name, name) {
4044 return true
4045 }
4046 }
4047 else {}
4048 }
4049 }
4050 }
4051 return false
4052}
4053
4054fn (g &Gen) generic_concrete_c_name_belongs_to_emit_modules(raw_name string) bool {
4055 c_name := g.qualify_module_local_generic_c_name(raw_name)
4056 if !g.alias_type_belongs_to_emit_modules(c_name) {
4057 return false
4058 }
4059 return g.generic_concrete_c_name_has_cache_visible_origin(c_name)
4060}
4061
4062fn (g &Gen) generic_concrete_c_name_has_cache_visible_origin(raw_name string) bool {
4063 mut name := raw_name.trim_space()
4064 if name == '' {
4065 return true
4066 }
4067 for name.ends_with('*') {
4068 name = name[..name.len - 1].trim_space()
4069 }
4070 if name.starts_with('struct ') {
4071 name = name['struct '.len..].trim_space()
4072 }
4073 if name == '' || name in primitive_types
4074 || name in ['string', 'array', 'map', 'chan', 'None__', 'IError']
4075 || is_generic_placeholder_type_name(name) {
4076 return true
4077 }
4078 if name.starts_with('Array_fixed_') {
4079 rest := name['Array_fixed_'.len..]
4080 last_underscore := rest.last_index_u8(`_`)
4081 if last_underscore < 0 {
4082 return true
4083 }
4084 return g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(rest[..last_underscore]))
4085 }
4086 if name.starts_with('Array_') {
4087 return g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(name['Array_'.len..]))
4088 }
4089 if name.starts_with('Map_') {
4090 rest := name['Map_'.len..]
4091 key, value := g.parse_map_kv_types(rest)
4092 if key == '' || value == '' {
4093 return g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(rest))
4094 }
4095 return
4096 g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(key))
4097 && g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(value))
4098 }
4099 if name.starts_with('Tuple_') {
4100 for part in name['Tuple_'.len..].split('_') {
4101 if part != ''
4102 && !g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(part)) {
4103 return false
4104 }
4105 }
4106 return true
4107 }
4108 if name.starts_with('_option_') {
4109 return g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(name['_option_'.len..]))
4110 }
4111 if name.starts_with('_result_') {
4112 return g.generic_concrete_c_name_has_cache_visible_origin(unmangle_alias_component_to_c(name['_result_'.len..]))
4113 }
4114 if name.contains('__') {
4115 return true
4116 }
4117 if name in g.c_struct_types || name in g.typedef_c_types || is_known_external_c_type_name(name) {
4118 return true
4119 }
4120 return g.unqualified_type_name_declared_in_files(name)
4121}
4122
4123fn generic_spec_key_short_name(key string) string {
4124 mut base := key
4125 bracket_idx := base.index_u8(`[`)
4126 if bracket_idx >= 0 {
4127 base = base[..bracket_idx]
4128 }
4129 dot_idx := base.last_index_u8(`.`)
4130 if dot_idx >= 0 && dot_idx < base.len - 1 {
4131 return base[dot_idx + 1..]
4132 }
4133 double_underscore_idx := base.last_index('__') or { -1 }
4134 if double_underscore_idx >= 0 && double_underscore_idx < base.len - 2 {
4135 return base[double_underscore_idx + 2..]
4136 }
4137 return base
4138}
4139
4140fn (mut g Gen) add_json2_decode_root_fallback_types(mut fallback_types map[string]types.Type) {
4141 if g.env == unsafe { nil } {
4142 return
4143 }
4144 for key, generic_maps in g.env.generic_types {
4145 if generic_spec_key_short_name(key) !in ['decode', 'decode_value'] {
4146 continue
4147 }
4148 for generic_types in generic_maps {
4149 if concrete := generic_types['T'] {
4150 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete)
4151 }
4152 }
4153 }
4154 for key, late_maps in g.late_generic_specs {
4155 if generic_spec_key_short_name(key) !in ['decode', 'decode_value'] {
4156 continue
4157 }
4158 for late_spec in late_maps {
4159 if concrete := late_spec['T'] {
4160 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete)
4161 }
4162 }
4163 }
4164 g.add_json2_decode_value_nested_fallback_types(mut fallback_types)
4165}
4166
4167fn add_json2_encode_number_fallback_types(mut fallback_types map[string]types.Type) {
4168 for name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'usize', 'isize',
4169 'f32', 'f64'] {
4170 if concrete := resolve_primitive_type_name(name) {
4171 fallback_types[concrete.name()] = concrete
4172 }
4173 }
4174}
4175
4176fn (mut g Gen) add_json2_encode_root_fallback_types(mut fallback_types map[string]types.Type) {
4177 if g.env == unsafe { nil } {
4178 return
4179 }
4180 for key, generic_maps in g.env.generic_types {
4181 if generic_spec_key_short_name(key) !in ['encode', 'encode_value'] {
4182 continue
4183 }
4184 for generic_types in generic_maps {
4185 if concrete := generic_types['T'] {
4186 add_json2_encode_value_fallback_type(mut fallback_types, concrete)
4187 }
4188 }
4189 }
4190 for key, late_maps in g.late_generic_specs {
4191 if generic_spec_key_short_name(key) !in ['encode', 'encode_value'] {
4192 continue
4193 }
4194 for late_spec in late_maps {
4195 if concrete := late_spec['T'] {
4196 add_json2_encode_value_fallback_type(mut fallback_types, concrete)
4197 }
4198 }
4199 }
4200}
4201
4202fn (mut g Gen) add_json2_encode_enum_fallback_types(mut fallback_types map[string]types.Type) {
4203 if g.env == unsafe { nil } {
4204 return
4205 }
4206 for fn_name, _ in g.fn_return_types {
4207 g.add_json2_encode_enum_fallback_type_from_fn_name(mut fallback_types, fn_name)
4208 }
4209 for fn_name, _ in g.called_fn_names {
4210 g.add_json2_encode_enum_fallback_type_from_fn_name(mut fallback_types, fn_name)
4211 }
4212 for _, generic_maps in g.env.generic_types {
4213 for generic_types in generic_maps {
4214 for _, concrete0 in generic_types {
4215 concrete := normalize_generic_concrete_type(concrete0)
4216 concrete_name := concrete.name()
4217 if concrete is types.Enum {
4218 fallback_types[concrete_name] = concrete
4219 }
4220 }
4221 }
4222 }
4223 for _, late_maps in g.late_generic_specs {
4224 for late_spec in late_maps {
4225 for _, concrete0 in late_spec {
4226 concrete := normalize_generic_concrete_type(concrete0)
4227 concrete_name := concrete.name()
4228 if concrete is types.Enum {
4229 fallback_types[concrete_name] = concrete
4230 }
4231 }
4232 }
4233 }
4234}
4235
4236fn (mut g Gen) add_json2_encode_enum_fallback_type_from_fn_name(mut fallback_types map[string]types.Type, fn_name string) {
4237 prefix := 'json2__Encoder__encode_value_T_'
4238 if !fn_name.starts_with(prefix) {
4239 return
4240 }
4241 type_name := fn_name[prefix.len..]
4242 concrete := g.concrete_type_from_c_name(type_name) or { return }
4243 concrete_name := concrete.name()
4244 if concrete is types.Enum {
4245 fallback_types[concrete_name] = concrete
4246 }
4247}
4248
4249fn (mut g Gen) add_json2_fallback_types_from_specialized_fns(mut fallback_types map[string]types.Type, prefixes []string) {
4250 for fn_name, _ in g.fn_return_types {
4251 g.add_json2_fallback_type_from_specialized_fn_name(mut fallback_types, fn_name, prefixes)
4252 }
4253 for fn_name, _ in g.called_fn_names {
4254 g.add_json2_fallback_type_from_specialized_fn_name(mut fallback_types, fn_name, prefixes)
4255 }
4256}
4257
4258fn (mut g Gen) add_json2_fallback_type_from_specialized_fn_name(mut fallback_types map[string]types.Type, fn_name string, prefixes []string) {
4259 for prefix in prefixes {
4260 if !fn_name.starts_with(prefix) {
4261 continue
4262 }
4263 token := fn_name[prefix.len..]
4264 if token == '' {
4265 continue
4266 }
4267 concrete := g.concrete_type_from_specialization_token(token)
4268 if type_contains_generic_placeholder(concrete)
4269 || !g.generic_concrete_type_is_runtime_specializable(concrete) {
4270 continue
4271 }
4272 fallback_types[concrete.name()] = concrete
4273 }
4274}
4275
4276fn (mut g Gen) concrete_type_from_specialization_token(token string) types.Type {
4277 c_name := unmangle_alias_component_to_c(token)
4278 if c_name.ends_with('*') {
4279 base_name := g.c_name_from_specialization_token_base(c_name.trim_right('*'))
4280 mut base_type := if primitive := resolve_primitive_type_name(base_name) {
4281 primitive
4282 } else {
4283 types.Type(types.Struct{
4284 name: base_name
4285 })
4286 }
4287 if resolved := g.lookup_type_by_c_name(base_name) {
4288 base_type = resolved
4289 }
4290 return types.Type(types.Pointer{
4291 base_type: normalize_generic_concrete_type(base_type)
4292 })
4293 }
4294 resolved_c_name := g.c_name_from_specialization_token_base(c_name)
4295 if resolved_c_name != c_name {
4296 return g.concrete_type_from_specialization_token(resolved_c_name)
4297 }
4298 if primitive := resolve_primitive_type_name(c_name) {
4299 return normalize_generic_concrete_type(primitive)
4300 }
4301 if resolved := g.lookup_type_by_c_name(c_name) {
4302 return normalize_generic_concrete_type(resolved)
4303 }
4304 return normalize_generic_concrete_type(types.Type(types.Struct{
4305 name: c_name
4306 }))
4307}
4308
4309fn (g &Gen) c_name_from_specialization_token_base(name string) string {
4310 if name == '' || name.contains('__') || is_builtin_specialization_base(name)
4311 || name.starts_with('Array_') || name.starts_with('Array_fixed_')
4312 || name.starts_with('Map_') || name.starts_with('_option_') || name.starts_with('_result_') {
4313 return name
4314 }
4315 sep := name.index_u8(`_`)
4316 if sep <= 0 || sep >= name.len - 1 {
4317 return name
4318 }
4319 mod_name := name[..sep]
4320 type_name := name[sep + 1..]
4321 if is_builtin_specialization_base(type_name)
4322 && !g.c_type_name_declared_in_module(mod_name, type_name) {
4323 return type_name
4324 }
4325 candidate := '${mod_name}__${type_name}'
4326 if candidate in g.c_struct_types || candidate in g.typedef_c_types
4327 || 'enum_${candidate}' in g.emitted_types || candidate in g.enum_type_fields {
4328 return candidate
4329 }
4330 if g.c_type_name_declared_in_module(mod_name, type_name) {
4331 return candidate
4332 }
4333 if scope := g.env_scope(mod_name) {
4334 if _ := scope.lookup_type(type_name) {
4335 return candidate
4336 }
4337 }
4338 return name
4339}
4340
4341fn (g &Gen) c_type_name_declared_in_module(mod_name string, type_name string) bool {
4342 if mod_name == '' || type_name == '' {
4343 return false
4344 }
4345 if g.has_flat() {
4346 for i in 0 .. g.flat.files.len {
4347 fc := g.flat.file_cursor(i)
4348 if flat_file_module_name(fc) != mod_name {
4349 continue
4350 }
4351 stmts := fc.stmts()
4352 for j in 0 .. stmts.len() {
4353 stmt := stmts.at(j)
4354 match stmt.kind() {
4355 .stmt_struct_decl, .stmt_enum_decl, .stmt_interface_decl, .stmt_type_decl {
4356 if stmt.name() == type_name {
4357 return true
4358 }
4359 }
4360 else {}
4361 }
4362 }
4363 }
4364 return false
4365 }
4366 for file in g.files {
4367 if file_module_name(file) != mod_name {
4368 continue
4369 }
4370 for stmt in file.stmts {
4371 match stmt {
4372 ast.StructDecl, ast.EnumDecl, ast.InterfaceDecl {
4373 if stmt.name == type_name {
4374 return true
4375 }
4376 }
4377 ast.TypeDecl {
4378 if stmt.name == type_name {
4379 return true
4380 }
4381 }
4382 else {}
4383 }
4384 }
4385 }
4386 return false
4387}
4388
4389fn add_json2_encode_value_nested_fallback_types(mut fallback_types map[string]types.Type) {
4390 for _ in 0 .. 4 {
4391 before := fallback_types.len
4392 snapshot := fallback_types.clone()
4393 for _, concrete0 in snapshot {
4394 concrete := normalize_generic_concrete_type(concrete0)
4395 match concrete {
4396 types.Array {
4397 add_json2_encode_value_fallback_type(mut fallback_types, concrete.elem_type)
4398 }
4399 types.ArrayFixed {
4400 add_json2_encode_value_fallback_type(mut fallback_types, concrete.elem_type)
4401 }
4402 types.Map {
4403 add_json2_encode_value_fallback_type(mut fallback_types, concrete.key_type)
4404 add_json2_encode_value_fallback_type(mut fallback_types, concrete.value_type)
4405 }
4406 types.Pointer {
4407 add_json2_encode_value_fallback_type(mut fallback_types, concrete.base_type)
4408 }
4409 else {}
4410 }
4411 }
4412 if fallback_types.len == before {
4413 break
4414 }
4415 }
4416}
4417
4418fn add_json2_encode_value_fallback_type(mut fallback_types map[string]types.Type, typ types.Type) {
4419 concrete := normalize_generic_concrete_type(typ)
4420 if concrete.name() == '' || concrete.name() == 'void'
4421 || type_contains_generic_placeholder(concrete) {
4422 return
4423 }
4424 fallback_types[concrete.name()] = concrete
4425}
4426
4427fn (mut g Gen) add_json2_decode_value_nested_fallback_types(mut fallback_types map[string]types.Type) {
4428 for _ in 0 .. 6 {
4429 before := fallback_types.len
4430 snapshot := fallback_types.clone()
4431 for _, concrete0 in snapshot {
4432 concrete := normalize_generic_concrete_type(concrete0)
4433 match concrete {
4434 types.Array {
4435 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete.elem_type)
4436 }
4437 types.Map {
4438 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete.value_type)
4439 }
4440 types.OptionType {
4441 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete.base_type)
4442 }
4443 types.ResultType {
4444 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete.base_type)
4445 }
4446 types.Alias {
4447 g.add_json2_decode_value_fallback_type(mut fallback_types, concrete.base_type)
4448 }
4449 types.Struct {
4450 mut struct_type := concrete
4451 if struct_type.fields.len == 0 {
4452 struct_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
4453 full_struct_type := g.lookup_struct_type_by_c_name(struct_c_name)
4454 if full_struct_type.fields.len > 0 {
4455 struct_type = full_struct_type
4456 }
4457 }
4458 for field in struct_type.fields {
4459 g.add_json2_decode_value_fallback_type(mut fallback_types, field.typ)
4460 }
4461 }
4462 else {}
4463 }
4464 }
4465 if fallback_types.len == before {
4466 break
4467 }
4468 }
4469}
4470
4471fn (mut g Gen) add_json2_decode_value_fallback_type(mut fallback_types map[string]types.Type, typ types.Type) {
4472 concrete := normalize_generic_concrete_type(typ)
4473 if concrete.name() == '' || concrete.name() in ['void', 'voidptr']
4474 || type_contains_generic_placeholder(concrete) || is_json2_runtime_internal_type(concrete) {
4475 return
4476 }
4477 fallback_types[concrete.name()] = concrete
4478}
4479
4480fn (mut g Gen) add_json2_decode_value_fallback_types(mut fallback_types map[string]types.Type) {
4481 prev_module := g.cur_module
4482 prev_file_name := g.cur_file_name
4483 prev_active_generic_types := g.active_generic_types.clone()
4484 defer {
4485 g.cur_module = prev_module
4486 g.cur_file_name = prev_file_name
4487 g.active_generic_types = prev_active_generic_types.clone()
4488 }
4489 for file in g.files {
4490 g.set_file_module(file)
4491 if g.cur_module != 'json2' {
4492 continue
4493 }
4494 for stmt in file.stmts {
4495 if stmt is ast.FnDecl && stmt.is_method && stmt.name == 'decode_value'
4496 && generic_params_are_single_t(g.generic_fn_param_names(stmt)) {
4497 for spec in g.generic_fn_specializations(stmt) {
4498 concrete := spec.generic_types['T'] or { continue }
4499 if concrete.name() == 'T' || type_contains_generic_placeholder(concrete) {
4500 continue
4501 }
4502 fallback_types[concrete.name()] = concrete
4503 }
4504 }
4505 }
4506 }
4507}
4508
4509fn (mut g Gen) infer_generic_type_bindings_from_generic_container(name ast.Expr, params []ast.Expr, concrete types.Type, generic_params []string, mut bindings map[string]types.Type) {
4510 base_name := g.expr_type_to_c(name).trim_space()
4511 if base_name == '' {
4512 return
4513 }
4514 runtime_params := runtime_generic_args(params)
4515 if runtime_params.len == 0 {
4516 return
4517 }
4518 concrete_name := g.generic_container_concrete_c_name(concrete)
4519 if concrete_name == '' {
4520 return
4521 }
4522 mut struct_type := g.lookup_struct_type_by_c_name(base_name)
4523 if struct_type.generic_params.len == 0 {
4524 struct_base := if base_name.contains('__') {
4525 base_name.all_after_last('__')
4526 } else {
4527 base_name
4528 }
4529 struct_type = g.lookup_struct_type(struct_base)
4530 }
4531 struct_params := runtime_generic_param_names(struct_type.generic_params)
4532 if struct_params.len == 0 || struct_params.len != runtime_params.len {
4533 return
4534 }
4535 mut concrete_bindings := map[string]types.Type{}
4536 if concrete_name == base_name {
4537 if primary := g.generic_struct_bindings[base_name] {
4538 concrete_bindings = primary.clone()
4539 }
4540 }
4541 if concrete_bindings.len == 0 {
4542 if instances := g.generic_struct_instances[base_name] {
4543 for inst in instances {
4544 if inst.c_name == concrete_name {
4545 concrete_bindings = inst.bindings.clone()
4546 break
4547 }
4548 }
4549 }
4550 }
4551 if concrete_bindings.len == 0 && concrete_name.starts_with('${base_name}_T_') {
4552 arg_names := generic_call_embedded_type_arg_names_from_name(concrete_name,
4553 struct_params.len)
4554 if arg_names.len == struct_params.len {
4555 for i, struct_param in struct_params {
4556 concrete_bindings[struct_param] =
4557 g.concrete_type_from_specialization_token(arg_names[i])
4558 }
4559 }
4560 }
4561 if concrete_bindings.len != struct_params.len {
4562 return
4563 }
4564 for i, param_expr in runtime_params {
4565 struct_param := struct_params[i]
4566 concrete_arg := concrete_bindings[struct_param] or { continue }
4567 g.infer_generic_type_bindings_from_param(param_expr, concrete_arg, generic_params, mut
4568 bindings)
4569 }
4570}
4571
4572fn (mut g Gen) generic_container_concrete_c_name(concrete types.Type) string {
4573 match concrete {
4574 types.Pointer {
4575 return g.generic_container_concrete_c_name(concrete.base_type)
4576 }
4577 types.Struct {
4578 return concrete.name
4579 }
4580 types.Alias {
4581 return concrete.name
4582 }
4583 types.NamedType {
4584 return string(concrete)
4585 }
4586 else {}
4587 }
4588
4589 return strip_pointer_type_name(g.types_type_to_c(concrete).trim_space())
4590}
4591
4592fn (mut g Gen) infer_generic_type_bindings_from_param(param ast.Expr, concrete types.Type, generic_params []string, mut bindings map[string]types.Type) {
4593 match param {
4594 ast.Ident {
4595 if param.name in generic_params {
4596 if param.name !in bindings {
4597 bindings[param.name] = concrete
4598 }
4599 }
4600 }
4601 ast.PrefixExpr {
4602 if concrete is types.Pointer {
4603 g.infer_generic_type_bindings_from_param(param.expr, concrete.base_type,
4604 generic_params, mut bindings)
4605 }
4606 }
4607 ast.ModifierExpr {
4608 g.infer_generic_type_bindings_from_param(param.expr, concrete, generic_params, mut
4609 bindings)
4610 }
4611 ast.GenericArgOrIndexExpr {
4612 g.infer_generic_type_bindings_from_generic_container(param.lhs, [param.expr], concrete,
4613 generic_params, mut bindings)
4614 }
4615 ast.GenericArgs {
4616 g.infer_generic_type_bindings_from_generic_container(param.lhs, param.args, concrete,
4617 generic_params, mut bindings)
4618 }
4619 ast.Type {
4620 match param {
4621 ast.ArrayType {
4622 if concrete is types.Array {
4623 g.infer_generic_type_bindings_from_param(param.elem_type,
4624 concrete.elem_type, generic_params, mut bindings)
4625 }
4626 }
4627 ast.ArrayFixedType {
4628 if concrete is types.ArrayFixed {
4629 g.infer_generic_type_bindings_from_param(param.elem_type,
4630 concrete.elem_type, generic_params, mut bindings)
4631 }
4632 }
4633 ast.MapType {
4634 if concrete is types.Map {
4635 g.infer_generic_type_bindings_from_param(param.key_type, concrete.key_type,
4636 generic_params, mut bindings)
4637 g.infer_generic_type_bindings_from_param(param.value_type,
4638 concrete.value_type, generic_params, mut bindings)
4639 }
4640 }
4641 ast.PointerType {
4642 if concrete is types.Pointer {
4643 g.infer_generic_type_bindings_from_param(param.base_type,
4644 concrete.base_type, generic_params, mut bindings)
4645 }
4646 }
4647 ast.OptionType {
4648 if concrete is types.OptionType {
4649 g.infer_generic_type_bindings_from_param(param.base_type,
4650 concrete.base_type, generic_params, mut bindings)
4651 }
4652 }
4653 ast.ResultType {
4654 if concrete is types.ResultType {
4655 g.infer_generic_type_bindings_from_param(param.base_type,
4656 concrete.base_type, generic_params, mut bindings)
4657 }
4658 }
4659 ast.GenericType {
4660 g.infer_generic_type_bindings_from_generic_container(param.name, param.params,
4661 concrete, generic_params, mut bindings)
4662 }
4663 else {}
4664 }
4665 }
4666 else {}
4667 }
4668}
4669
4670fn (g &Gen) concrete_type_for_generic_param(param ast.Parameter, concrete types.Type) types.Type {
4671 mut normalized := normalize_generic_concrete_type(concrete)
4672 if direct_generic_placeholder_name(param.typ) != '' && !param_type_is_pointer_expr(param.typ)
4673 && normalized is types.Pointer && type_contains_generic_placeholder(normalized.base_type) {
4674 normalized = g.concrete_type_with_active_generics(normalized.base_type)
4675 }
4676 normalized = g.concrete_type_with_active_generics(normalized)
4677 if param.is_mut && !param_type_is_pointer_expr(param.typ) && normalized is types.Pointer {
4678 normalized = normalized.base_type
4679 }
4680 if active := g.active_generic_types[normalized.name()] {
4681 return normalize_generic_concrete_type(active)
4682 }
4683 return normalized
4684}
4685
4686fn (mut g Gen) active_generic_concrete_from_arg(arg ast.Expr) ?types.Type {
4687 if g.active_generic_types.len == 0 {
4688 return none
4689 }
4690 if arg is ast.PrefixExpr && arg.op == .amp {
4691 if concrete := g.active_generic_concrete_from_arg(arg.expr) {
4692 return types.Type(types.Pointer{
4693 base_type: concrete
4694 })
4695 }
4696 }
4697 if arg is ast.Ident {
4698 if param_name := g.cur_fn_generic_params[arg.name] {
4699 if concrete := g.active_generic_types[param_name] {
4700 return normalize_generic_concrete_type(concrete)
4701 }
4702 }
4703 }
4704 raw := g.get_raw_type(arg) or { return none }
4705 concrete := g.concrete_type_with_active_generics(raw)
4706 // Function-typed generic parameters use `void*` at the C ABI. Keep the
4707 // active generic binding (for example `T = fn_i64`) so nested generic calls
4708 // specialize by the V type, not by the erased ABI carrier.
4709 if type_contains_generic_placeholder(concrete) || concrete.name() == raw.name() {
4710 return none
4711 }
4712 return normalize_generic_concrete_type(concrete)
4713}
4714
4715fn (g &Gen) concrete_type_with_active_generics(typ types.Type) types.Type {
4716 if g.active_generic_types.len == 0 || !type_contains_generic_placeholder(typ) {
4717 return normalize_generic_concrete_type(typ)
4718 }
4719 match typ {
4720 types.NamedType {
4721 name := string(typ)
4722 if concrete := g.active_generic_types[name] {
4723 return normalize_generic_concrete_type(concrete)
4724 }
4725 }
4726 types.Pointer {
4727 return types.Type(types.Pointer{
4728 base_type: g.concrete_type_with_active_generics(typ.base_type)
4729 lifetime: typ.lifetime
4730 })
4731 }
4732 types.Array {
4733 return types.Type(types.Array{
4734 elem_type: g.concrete_type_with_active_generics(typ.elem_type)
4735 })
4736 }
4737 types.ArrayFixed {
4738 return types.Type(types.ArrayFixed{
4739 len: typ.len
4740 elem_type: g.concrete_type_with_active_generics(typ.elem_type)
4741 })
4742 }
4743 types.Map {
4744 return types.Type(types.Map{
4745 key_type: g.concrete_type_with_active_generics(typ.key_type)
4746 value_type: g.concrete_type_with_active_generics(typ.value_type)
4747 })
4748 }
4749 types.OptionType {
4750 return types.Type(types.OptionType{
4751 base_type: g.concrete_type_with_active_generics(typ.base_type)
4752 })
4753 }
4754 types.ResultType {
4755 return types.Type(types.ResultType{
4756 base_type: g.concrete_type_with_active_generics(typ.base_type)
4757 })
4758 }
4759 types.Alias {
4760 return types.Type(types.Alias{
4761 name: typ.name
4762 base_type: g.concrete_type_with_active_generics(typ.base_type)
4763 })
4764 }
4765 else {}
4766 }
4767
4768 return normalize_generic_concrete_type(typ)
4769}
4770
4771fn generic_container_fallback_type(node ast.FnDecl, param_name string, concrete types.Type) ?types.Type {
4772 for param in node.typ.params {
4773 if !expr_has_generic_placeholder(param.typ) {
4774 continue
4775 }
4776 if derived := generic_container_fallback_type_from_expr(param.typ, concrete, param_name) {
4777 return derived
4778 }
4779 }
4780 return none
4781}
4782
4783fn generic_container_fallback_type_from_expr(expr ast.Expr, concrete types.Type, param_name string) ?types.Type {
4784 match expr {
4785 ast.Ident {
4786 if expr.name == param_name {
4787 return concrete
4788 }
4789 }
4790 ast.ModifierExpr {
4791 return generic_container_fallback_type_from_expr(expr.expr, concrete, param_name)
4792 }
4793 ast.PrefixExpr {
4794 if concrete is types.Pointer {
4795 return generic_container_fallback_type_from_expr(expr.expr, concrete.base_type,
4796 param_name)
4797 }
4798 }
4799 ast.Type {
4800 match expr {
4801 ast.ArrayType {
4802 if concrete is types.Array {
4803 return generic_container_fallback_type_from_expr(expr.elem_type,
4804 concrete.elem_type, param_name)
4805 }
4806 }
4807 ast.MapType {
4808 if concrete is types.Map {
4809 if derived := generic_container_fallback_type_from_expr(expr.key_type,
4810 concrete.key_type, param_name)
4811 {
4812 return derived
4813 }
4814 return generic_container_fallback_type_from_expr(expr.value_type,
4815 concrete.value_type, param_name)
4816 }
4817 }
4818 ast.OptionType {
4819 if concrete is types.OptionType {
4820 return generic_container_fallback_type_from_expr(expr.base_type,
4821 concrete.base_type, param_name)
4822 }
4823 }
4824 ast.ResultType {
4825 if concrete is types.ResultType {
4826 return generic_container_fallback_type_from_expr(expr.base_type,
4827 concrete.base_type, param_name)
4828 }
4829 }
4830 ast.PointerType {
4831 if concrete is types.Pointer {
4832 return generic_container_fallback_type_from_expr(expr.base_type,
4833 concrete.base_type, param_name)
4834 }
4835 }
4836 else {}
4837 }
4838 }
4839 else {}
4840 }
4841
4842 return none
4843}
4844
4845fn param_type_is_pointer_expr(expr ast.Expr) bool {
4846 match expr {
4847 ast.PrefixExpr {
4848 return expr.op == .amp
4849 }
4850 ast.Type {
4851 return expr is ast.PointerType
4852 }
4853 ast.ModifierExpr {
4854 return param_type_is_pointer_expr(expr.expr)
4855 }
4856 else {}
4857 }
4858
4859 return false
4860}
4861
4862fn param_type_is_array_expr(expr ast.Expr) bool {
4863 match expr {
4864 ast.Type {
4865 match expr {
4866 ast.ArrayType {
4867 return true
4868 }
4869 else {}
4870 }
4871 }
4872 ast.ModifierExpr {
4873 return param_type_is_array_expr(expr.expr)
4874 }
4875 else {}
4876 }
4877
4878 return false
4879}
4880
4881fn direct_generic_placeholder_name(e ast.Expr) string {
4882 match e {
4883 ast.Ident {
4884 if is_generic_placeholder_type_name(e.name) {
4885 return e.name
4886 }
4887 }
4888 ast.PrefixExpr {
4889 return direct_generic_placeholder_name(e.expr)
4890 }
4891 ast.ModifierExpr {
4892 return direct_generic_placeholder_name(e.expr)
4893 }
4894 ast.Type {
4895 match e {
4896 ast.ArrayType {
4897 return direct_generic_placeholder_name(e.elem_type)
4898 }
4899 ast.ArrayFixedType {
4900 return direct_generic_placeholder_name(e.elem_type)
4901 }
4902 ast.MapType {
4903 key_placeholder := direct_generic_placeholder_name(e.key_type)
4904 if key_placeholder != '' {
4905 return key_placeholder
4906 }
4907 return direct_generic_placeholder_name(e.value_type)
4908 }
4909 ast.OptionType {
4910 return direct_generic_placeholder_name(e.base_type)
4911 }
4912 ast.ResultType {
4913 return direct_generic_placeholder_name(e.base_type)
4914 }
4915 ast.PointerType {
4916 return direct_generic_placeholder_name(e.base_type)
4917 }
4918 else {}
4919 }
4920 }
4921 else {}
4922 }
4923
4924 return ''
4925}
4926
4927fn generic_specialization_token_for_direct_param(param ast.Expr, concrete_type_name string) string {
4928 raw_ctype := concrete_type_name.trim_space()
4929 mut ctype := raw_ctype.trim_right('*')
4930 if raw_ctype == 'void*' || raw_ctype == 'voidptr' {
4931 ctype = 'voidptr'
4932 }
4933 match param {
4934 ast.PrefixExpr {
4935 return generic_specialization_token_for_direct_param(param.expr, ctype)
4936 }
4937 ast.ModifierExpr {
4938 return generic_specialization_token_for_direct_param(param.expr, ctype)
4939 }
4940 ast.Type {
4941 match param {
4942 ast.ArrayType {
4943 if ctype.starts_with('Array_') {
4944 ctype = ctype['Array_'.len..]
4945 }
4946 return generic_specialization_token_for_direct_param(param.elem_type, ctype)
4947 }
4948 ast.ArrayFixedType {
4949 if ctype.starts_with('Array_fixed_') {
4950 ctype = ctype['Array_fixed_'.len..]
4951 if last_sep := ctype.last_index('_') {
4952 ctype = ctype[..last_sep]
4953 }
4954 }
4955 return generic_specialization_token_for_direct_param(param.elem_type, ctype)
4956 }
4957 ast.PointerType {
4958 return generic_specialization_token_for_direct_param(param.base_type, ctype)
4959 }
4960 ast.OptionType {
4961 if ctype.starts_with('_option_') {
4962 ctype = ctype['_option_'.len..]
4963 }
4964 return generic_specialization_token_for_direct_param(param.base_type, ctype)
4965 }
4966 ast.ResultType {
4967 if ctype.starts_with('_result_') {
4968 ctype = ctype['_result_'.len..]
4969 }
4970 return generic_specialization_token_for_direct_param(param.base_type, ctype)
4971 }
4972 else {}
4973 }
4974 }
4975 else {}
4976 }
4977
4978 if param is ast.Ident && is_generic_placeholder_type_name(param.name)
4979 && raw_ctype.ends_with('*') && raw_ctype != 'void*' {
4980 return generic_specialization_token_from_c_type_name(raw_ctype)
4981 }
4982 return generic_specialization_token_from_c_type_name(ctype)
4983}
4984
4985fn generic_specialization_token_from_c_type_name(c_type string) string {
4986 mut base := c_type.trim_space()
4987 if base == 'void*' || base == 'voidptr' {
4988 return 'voidptr'
4989 }
4990 mut ptr_count := 0
4991 for base.ends_with('*') {
4992 ptr_count++
4993 base = base[..base.len - 1].trim_space()
4994 }
4995 mut token := sanitize_generic_token_part(base)
4996 for _ in 0 .. ptr_count {
4997 token += 'ptr'
4998 }
4999 return token
5000}
5001
5002fn (g &Gen) qualify_local_call_name(name string) string {
5003 if name != '' && g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin'
5004 && !name.contains('__') {
5005 return '${g.cur_module}__${name}'
5006 }
5007 return name
5008}
5009
5010fn (g &Gen) has_generic_fn_decl_by_base_name(name string) bool {
5011 lookup_name := g.qualified_generic_fn_base_name(name) or { name }
5012 info := g.generic_fn_decl_index[lookup_name] or { return false }
5013 if g.has_flat() {
5014 if info.file_idx < 0 || info.file_idx >= g.flat.files.len {
5015 return false
5016 }
5017 stmts := g.flat.file_cursor(info.file_idx).stmts()
5018 if info.stmt_idx < 0 || info.stmt_idx >= stmts.len() {
5019 return false
5020 }
5021 return stmts.at(info.stmt_idx).kind() == .stmt_fn_decl
5022 }
5023 if info.file_idx < 0 || info.file_idx >= g.files.len {
5024 return false
5025 }
5026 file := g.files[info.file_idx]
5027 if info.stmt_idx < 0 || info.stmt_idx >= file.stmts.len {
5028 return false
5029 }
5030 return file.stmts[info.stmt_idx] is ast.FnDecl
5031}
5032
5033fn (mut g Gen) find_generic_fn_decl_by_base_name(name string) ?ast.FnDecl {
5034 lookup_name := g.qualified_generic_fn_base_name(name) or { name }
5035 info := g.generic_fn_decl_index[lookup_name] or { return none }
5036 if g.has_flat() {
5037 if info.file_idx < 0 || info.file_idx >= g.flat.files.len {
5038 return none
5039 }
5040 stmts := g.flat.file_cursor(info.file_idx).stmts()
5041 if info.stmt_idx < 0 || info.stmt_idx >= stmts.len() {
5042 return none
5043 }
5044 stmt := stmts.at(info.stmt_idx)
5045 if stmt.kind() == .stmt_fn_decl {
5046 return stmt.fn_decl()
5047 }
5048 return none
5049 }
5050 if info.file_idx < 0 || info.file_idx >= g.files.len {
5051 return none
5052 }
5053 file := g.files[info.file_idx]
5054 if info.stmt_idx < 0 || info.stmt_idx >= file.stmts.len {
5055 return none
5056 }
5057 stmt := file.stmts[info.stmt_idx]
5058 if stmt is ast.FnDecl {
5059 return stmt
5060 }
5061 return none
5062}
5063
5064fn (g &Gen) qualified_generic_fn_base_name(name string) ?string {
5065 if name == '' {
5066 return none
5067 }
5068 if name in g.generic_fn_decl_index {
5069 return name
5070 }
5071 if !name.contains('__') {
5072 return none
5073 }
5074 if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
5075 local_name := '${g.cur_module}__${name}'
5076 if local_name in g.generic_fn_decl_index {
5077 return local_name
5078 }
5079 }
5080 suffix := '__${name}'
5081 mut found := ''
5082 for candidate, _ in g.generic_fn_decl_index {
5083 if candidate.ends_with(suffix) {
5084 if found != '' && found != candidate {
5085 return none
5086 }
5087 found = candidate
5088 }
5089 }
5090 if found != '' {
5091 return found
5092 }
5093 return none
5094}
5095
5096fn (mut g Gen) find_generic_fn_decl_by_spec_key(key string) ?ast.FnDecl {
5097 if decl := g.find_generic_fn_decl_by_base_name(key) {
5098 return decl
5099 }
5100 mut base_name := key
5101 bracket_pos := base_name.index_u8(`[`)
5102 if bracket_pos > 0 {
5103 base_name = base_name[..bracket_pos]
5104 }
5105 dot_pos := base_name.last_index_u8(`.`)
5106 short_name := if dot_pos > 0 && dot_pos < base_name.len - 1 {
5107 base_name[dot_pos + 1..]
5108 } else {
5109 base_name
5110 }
5111 if short_name != key {
5112 if decl := g.find_generic_fn_decl_by_base_name(short_name) {
5113 return decl
5114 }
5115 }
5116 if g.generic_fn_decl_index.len > 0 {
5117 return none
5118 }
5119 prev_module := g.cur_module
5120 prev_file_name := g.cur_file_name
5121 defer {
5122 g.cur_module = prev_module
5123 g.cur_file_name = prev_file_name
5124 }
5125 if g.has_flat() {
5126 for i in 0 .. g.flat.files.len {
5127 fc := g.flat.file_cursor(i)
5128 g.set_file_cursor_module(fc)
5129 stmts := fc.stmts()
5130 for j in 0 .. stmts.len() {
5131 stmt := stmts.at(j)
5132 if stmt.kind() != .stmt_fn_decl || stmt.name() != short_name {
5133 continue
5134 }
5135 decl := stmt.fn_decl_signature()
5136 if g.generic_fn_param_names(decl).len > 0 {
5137 return stmt.fn_decl()
5138 }
5139 }
5140 }
5141 return none
5142 }
5143 for file in g.files {
5144 g.set_file_module(file)
5145 for stmt in file.stmts {
5146 if stmt is ast.FnDecl && stmt.name == short_name
5147 && g.generic_fn_param_names(stmt).len > 0 {
5148 return stmt
5149 }
5150 }
5151 }
5152 return none
5153}
5154
5155fn (g &Gen) find_specialized_call_name(name string, token string) ?string {
5156 if name == '' || token == '' {
5157 return none
5158 }
5159 candidate := '${name}_T_${token}'
5160 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5161 return candidate
5162 }
5163 if !name.contains('__') {
5164 qualified := g.qualify_local_call_name(candidate)
5165 if qualified != candidate
5166 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5167 return qualified
5168 }
5169 }
5170 return none
5171}
5172
5173fn (mut g Gen) resolve_sum_payload_arg_type_name(arg ast.Expr, candidate_type string) string {
5174 mut type_name := candidate_type.trim_space()
5175 if type_name == '' || type_name == 'int' {
5176 return type_name
5177 }
5178 ident := sum_payload_ident_from_expr(arg) or { return type_name }
5179 decl_type :=
5180 (g.get_local_var_c_type(ident.name) or { return type_name }).trim_space().trim_right('*')
5181 if decl_type == '' || type_name == decl_type || type_name == decl_type + '*' {
5182 return type_name
5183 }
5184 mut ptr_suffix := ''
5185 for type_name.ends_with('*') {
5186 ptr_suffix += '*'
5187 type_name = type_name[..type_name.len - 1].trim_space()
5188 }
5189 variants := g.get_sum_type_variants_for(decl_type)
5190 if variants.len == 0 {
5191 return type_name + ptr_suffix
5192 }
5193 mut variant_field := g.sum_type_variant_field_name(decl_type, type_name)
5194 if variant_field !in variants {
5195 for variant in variants {
5196 if sum_type_variant_matches(variant, type_name) {
5197 variant_field = variant
5198 break
5199 }
5200 }
5201 }
5202 if variant_field !in variants {
5203 return type_name + ptr_suffix
5204 }
5205 payload_type := g.sum_type_variant_payload_type(decl_type, type_name, variant_field)
5206 if payload_type == '' {
5207 return type_name + ptr_suffix
5208 }
5209 return payload_type + ptr_suffix
5210}
5211
5212fn (mut g Gen) generic_specialization_arg_type_name(arg ast.Expr) string {
5213 base_arg := if arg is ast.ModifierExpr {
5214 arg.expr
5215 } else {
5216 arg
5217 }
5218 if base_arg is ast.PrefixExpr && base_arg.op == .amp {
5219 inner_type_name := g.generic_specialization_arg_type_name(base_arg.expr)
5220 if inner_type_name != '' && inner_type_name != 'int' {
5221 return inner_type_name.trim_right('*') + '*'
5222 }
5223 }
5224 mut arg_type_name := ''
5225 if base_arg is ast.SelectorExpr {
5226 if is_comptime_selector_rhs_name(base_arg.rhs.name) {
5227 comptime_type := g.get_comptime_selector_type(base_arg).trim_space()
5228 if comptime_type != '' && comptime_type != 'int' {
5229 return comptime_type
5230 }
5231 }
5232 if g.active_generic_types.len > 0 {
5233 arg_type_name = g.selector_field_type(base_arg).trim_space()
5234 if arg_type_name == '' || arg_type_name == 'array' {
5235 arg_type_name = g.selector_declared_field_type(base_arg).trim_space()
5236 }
5237 } else {
5238 arg_type_name = g.selector_declared_field_type(base_arg).trim_space()
5239 if arg_type_name == '' || arg_type_name == 'array' {
5240 arg_type_name = g.selector_field_type(base_arg).trim_space()
5241 }
5242 }
5243 if base_arg.lhs is ast.Ident {
5244 lhs_name := base_arg.lhs.name
5245 if placeholder := g.cur_fn_generic_params[lhs_name] {
5246 if concrete := g.active_generic_types[placeholder] {
5247 mut struct_type := g.resolve_type_to_struct(concrete)
5248 if struct_type.fields.len == 0 {
5249 struct_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
5250 struct_type = g.lookup_struct_type_by_c_name(struct_c_name)
5251 }
5252 for field in struct_type.fields {
5253 if field.name == base_arg.rhs.name {
5254 arg_type_name = g.types_type_to_c(field.typ)
5255 break
5256 }
5257 }
5258 }
5259 }
5260 }
5261 }
5262 if arg_type_name == '' {
5263 if active_concrete := g.active_generic_concrete_from_arg(base_arg) {
5264 arg_type_name = g.generic_specialization_token_from_type(active_concrete)
5265 }
5266 }
5267 if arg_type_name == '' && base_arg is ast.InitExpr {
5268 arg_type_name = g.expr_type_to_c(base_arg.typ).trim_space()
5269 }
5270 if arg_type_name == '' || arg_type_name == 'int' {
5271 arg_type_name = g.get_expr_type(base_arg).trim_space()
5272 }
5273 if (arg_type_name == '' || arg_type_name == 'int') && base_arg is ast.Ident {
5274 arg_type_name = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
5275 }
5276 if arg_type_name == '' || arg_type_name == 'int' {
5277 if raw := g.get_raw_type(base_arg) {
5278 arg_type_name = g.types_type_to_c(raw).trim_space()
5279 }
5280 }
5281 return if arg_type_name.trim_space() == 'void*' {
5282 'voidptr'
5283 } else {
5284 g.resolve_sum_payload_arg_type_name(base_arg, arg_type_name).trim_space()
5285 }
5286}
5287
5288fn (g &Gen) find_known_qualified_fn_name(name string) ?string {
5289 if name == '' {
5290 return none
5291 }
5292 if !name.contains('__') {
5293 qualified := g.qualify_local_call_name(name)
5294 if qualified != name && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5295 return qualified
5296 }
5297 suffix := '__${name}'
5298 mut found := ''
5299 for fn_name, _ in g.fn_param_is_ptr {
5300 if fn_name.ends_with(suffix) {
5301 if found != '' && found != fn_name {
5302 return none
5303 }
5304 found = fn_name
5305 }
5306 }
5307 for fn_name, _ in g.fn_return_types {
5308 if fn_name.ends_with(suffix) {
5309 if found != '' && found != fn_name {
5310 return none
5311 }
5312 found = fn_name
5313 }
5314 }
5315 if found != '' {
5316 return found
5317 }
5318 if name.contains('_T_') {
5319 base_name := name.all_before('_T_')
5320 token := name.all_after('_T_')
5321 if base_name != '' && token != '' {
5322 prefix := '__${base_name}_T_'
5323 mut generic_found := ''
5324 for fn_name, _ in g.fn_param_is_ptr {
5325 if fn_name.contains(prefix)
5326 && generic_token_matches_short_name(fn_name.all_after(prefix), token) {
5327 if generic_found != '' && generic_found != fn_name {
5328 return none
5329 }
5330 generic_found = fn_name
5331 }
5332 }
5333 for fn_name, _ in g.fn_return_types {
5334 if fn_name.contains(prefix)
5335 && generic_token_matches_short_name(fn_name.all_after(prefix), token) {
5336 if generic_found != '' && generic_found != fn_name {
5337 return none
5338 }
5339 generic_found = fn_name
5340 }
5341 }
5342 if generic_found != '' {
5343 return generic_found
5344 }
5345 }
5346 }
5347 }
5348 if name in g.fn_param_is_ptr || name in g.fn_return_types {
5349 return name
5350 }
5351 return none
5352}
5353
5354fn generic_token_matches_short_name(actual string, expected string) bool {
5355 if actual == expected {
5356 return true
5357 }
5358 return actual.ends_with('_${expected}')
5359}
5360
5361fn (g &Gen) has_specialized_fn_base(name string) bool {
5362 if name == '' {
5363 return false
5364 }
5365 return name in g.specialized_fn_bases
5366}
5367
5368fn (g &Gen) same_receiver_specialized_method_name(name string) ?string {
5369 if name == '' || g.cur_fn_c_name == '' || !name.contains('_T_')
5370 || !g.cur_fn_c_name.contains('_T_') {
5371 return none
5372 }
5373 base_name := name.all_before('_T_')
5374 cur_base_name := g.cur_fn_c_name.all_before('_T_')
5375 if base_name == '' || base_name != cur_base_name {
5376 return none
5377 }
5378 rest := name.all_after('_T_')
5379 cur_rest := g.cur_fn_c_name.all_after('_T_')
5380 method_sep := rest.last_index('__') or { return none }
5381 cur_method_sep := cur_rest.last_index('__') or { return none }
5382 suffix := rest[..method_sep]
5383 cur_suffix := cur_rest[..cur_method_sep]
5384 if suffix == '' || cur_suffix == '' || suffix == cur_suffix {
5385 return none
5386 }
5387 candidate := '${base_name}_T_${cur_suffix}${rest[method_sep..]}'
5388 if g.knows_fn_signature(candidate) || base_name in g.specialized_fn_bases {
5389 return candidate
5390 }
5391 return none
5392}
5393
5394fn (mut g Gen) ensure_specialized_call_signature(name string) {
5395 if name == '' || !name.contains('_T_') || name in g.fn_param_is_ptr || name in g.fn_return_types {
5396 return
5397 }
5398 base_name := specialized_fn_decl_base_name(name)
5399 decl := g.find_generic_fn_decl_by_base_name(base_name) or { return }
5400 generic_types := g.generic_types_from_specialized_fn_name(decl, name) or {
5401 g.register_fn_signature(decl, name)
5402 return
5403 }
5404 prev_generic_types := g.active_generic_types.clone()
5405 g.active_generic_types = generic_types.clone()
5406 g.register_fn_signature(decl, name)
5407 g.active_generic_types = prev_generic_types.clone()
5408}
5409
5410fn (g &Gen) receiver_only_specialized_call_name(name string) ?string {
5411 if name == '' {
5412 return none
5413 }
5414 base_name := generic_call_base_name_for_specialization(name)
5415 if base_name == '' || base_name == name {
5416 return none
5417 }
5418 if base_name in g.fn_param_types || base_name in g.fn_return_types
5419 || base_name in g.fn_param_is_ptr {
5420 return base_name
5421 }
5422 return none
5423}
5424
5425fn specialized_fn_decl_base_name(name string) string {
5426 base_name := generic_call_base_name_for_specialization(name)
5427 if base_name == '' || !base_name.contains('_T_') {
5428 return base_name
5429 }
5430 method_sep := base_name.last_index('__') or { return base_name.all_before('_T_') }
5431 receiver_name := base_name[..method_sep]
5432 if !receiver_name.contains('_T_') {
5433 return base_name
5434 }
5435 return receiver_name.all_before('_T_') + base_name[method_sep..]
5436}
5437
5438fn (mut g Gen) record_late_single_generic_call_spec_from_c_type(fn_name string, param_name string, c_type string) {
5439 if fn_name == '' || param_name == '' {
5440 return
5441 }
5442 concrete := g.concrete_type_from_call_arg_c_name(c_type.trim_space()) or { return }
5443 if type_contains_generic_placeholder(concrete) {
5444 return
5445 }
5446 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
5447 return
5448 }
5449 g.record_late_generic_call_spec(fn_name, {
5450 param_name: concrete
5451 })
5452}
5453
5454fn (mut g Gen) concrete_type_from_call_arg_c_name(c_name string) ?types.Type {
5455 raw_name := c_name.trim_space()
5456 if raw_name == 'void*' || raw_name == 'voidptr' {
5457 return types.Type(types.voidptr_)
5458 }
5459 clean_name := raw_name.trim_right('*')
5460 if clean_name == '' {
5461 return none
5462 }
5463 mut concrete := if primitive := resolve_primitive_type_name(clean_name) {
5464 normalize_generic_concrete_type(primitive)
5465 } else if named := g.concrete_type_from_c_name(clean_name) {
5466 normalize_generic_concrete_type(named)
5467 } else {
5468 return none
5469 }
5470 if raw_name.ends_with('*') {
5471 return types.Type(types.Pointer{
5472 base_type: concrete
5473 })
5474 }
5475 return concrete
5476}
5477
5478fn (mut g Gen) concrete_type_from_current_return_c_name(c_name string, return_type ast.Expr) ?types.Type {
5479 raw_name := c_name.trim_space()
5480 if raw_name == '' || raw_name == 'void' {
5481 return none
5482 }
5483 if return_type is ast.Type && return_type is ast.ResultType {
5484 if !raw_name.starts_with('_result_') {
5485 return none
5486 }
5487 payload_name := g.result_value_type(raw_name).trim_space()
5488 if payload_name == '' || payload_name == 'void' {
5489 return none
5490 }
5491 payload_type := g.concrete_type_from_call_arg_c_name(payload_name) or { return none }
5492 return types.Type(types.ResultType{
5493 base_type: payload_type
5494 })
5495 }
5496 if return_type is ast.Type && return_type is ast.OptionType {
5497 if !raw_name.starts_with('_option_') {
5498 return none
5499 }
5500 payload_name := option_value_type(raw_name).trim_space()
5501 if payload_name == '' || payload_name == 'void' {
5502 return none
5503 }
5504 payload_type := g.concrete_type_from_call_arg_c_name(payload_name) or { return none }
5505 return types.Type(types.OptionType{
5506 base_type: payload_type
5507 })
5508 }
5509 return g.concrete_type_from_call_arg_c_name(raw_name)
5510}
5511
5512fn (mut g Gen) infer_generic_bindings_from_current_return(decl ast.FnDecl, generic_params []string) ?map[string]types.Type {
5513 if generic_params.len == 0 || g.cur_fn_ret_type == '' || decl.typ.return_type is ast.EmptyExpr
5514 || !expr_has_generic_placeholder(decl.typ.return_type) {
5515 return none
5516 }
5517 concrete_return := g.concrete_type_from_current_return_c_name(g.cur_fn_ret_type,
5518 decl.typ.return_type) or { return none }
5519 mut bindings := map[string]types.Type{}
5520 g.infer_generic_type_bindings_from_param(decl.typ.return_type, concrete_return, generic_params, mut
5521 bindings)
5522 if bindings.len != generic_params.len {
5523 return none
5524 }
5525 for _, concrete in bindings {
5526 if type_contains_generic_placeholder(concrete)
5527 || !g.generic_concrete_type_is_runtime_specializable(concrete) {
5528 return none
5529 }
5530 }
5531 return bindings
5532}
5533
5534fn (mut g Gen) generic_call_has_concrete_arg_bindings(decl ast.FnDecl, call_args []ast.Expr, generic_params []string) bool {
5535 if generic_params.len == 0 || g.env == unsafe { nil } {
5536 return false
5537 }
5538 mut bindings := map[string]types.Type{}
5539 if decl.is_method && call_args.len > 0 && expr_has_generic_placeholder(decl.receiver.typ) {
5540 recv_arg := if call_args[0] is ast.ModifierExpr {
5541 (call_args[0] as ast.ModifierExpr).expr
5542 } else {
5543 call_args[0]
5544 }
5545 if expr_has_valid_data(recv_arg) {
5546 if concrete := g.concrete_type_from_generic_call_arg(recv_arg) {
5547 g.infer_generic_type_bindings_from_param(decl.receiver.typ,
5548 g.concrete_type_with_active_generics(concrete), generic_params, mut bindings)
5549 }
5550 }
5551 }
5552 arg_offset := if decl.is_method && call_args.len == decl.typ.params.len + 1 { 1 } else { 0 }
5553 for i, param in decl.typ.params {
5554 arg_idx := i + arg_offset
5555 if arg_idx >= call_args.len || !expr_has_generic_placeholder(param.typ) {
5556 continue
5557 }
5558 arg := if call_args[arg_idx] is ast.ModifierExpr {
5559 (call_args[arg_idx] as ast.ModifierExpr).expr
5560 } else {
5561 call_args[arg_idx]
5562 }
5563 if !expr_has_valid_data(arg) {
5564 continue
5565 }
5566 mut concrete := types.Type(types.void_)
5567 mut found_concrete := false
5568 if arg is ast.InitExpr {
5569 c_name := g.expr_type_to_c(arg.typ).trim_space()
5570 if concrete_type := g.concrete_type_from_call_arg_c_name(c_name) {
5571 concrete = concrete_type
5572 found_concrete = true
5573 }
5574 }
5575 if !found_concrete && arg is ast.CastExpr {
5576 c_name := g.expr_type_to_c(arg.typ).trim_space()
5577 if concrete_type := g.concrete_type_from_call_arg_c_name(c_name) {
5578 concrete = concrete_type
5579 found_concrete = true
5580 }
5581 }
5582 if !found_concrete {
5583 if active_concrete := g.active_generic_concrete_from_arg(arg) {
5584 concrete = active_concrete
5585 found_concrete = true
5586 }
5587 }
5588 if !found_concrete && arg is ast.SelectorExpr {
5589 arg_type_name := g.selector_declared_field_type(arg).trim_space()
5590 if arg_type_name != '' {
5591 if selector_type := g.concrete_type_from_call_arg_c_name(arg_type_name) {
5592 concrete = selector_type
5593 found_concrete = true
5594 }
5595 }
5596 }
5597 if !found_concrete && arg is ast.Ident {
5598 arg_type_name := (g.get_local_var_c_type(arg.name) or { '' }).trim_space()
5599 if arg_type_name != '' {
5600 if local_type := g.concrete_type_from_call_arg_c_name(arg_type_name) {
5601 concrete = local_type
5602 found_concrete = true
5603 }
5604 }
5605 }
5606 pos := arg.pos()
5607 if !found_concrete && pos.id != 0 {
5608 if env_type := g.env.get_expr_type(pos.id) {
5609 concrete = env_type
5610 found_concrete = true
5611 }
5612 }
5613 if !found_concrete {
5614 if raw := g.get_raw_type(arg) {
5615 if raw.name() != 'int' {
5616 concrete = raw
5617 found_concrete = true
5618 }
5619 }
5620 }
5621 if !found_concrete {
5622 continue
5623 }
5624 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
5625 concrete), generic_params, mut bindings)
5626 }
5627 for param_name in generic_params {
5628 concrete := bindings[param_name] or { return false }
5629 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
5630 return false
5631 }
5632 }
5633 return true
5634}
5635
5636fn (mut g Gen) try_specialize_generic_call_name(name string, call_args []ast.Expr) ?string {
5637 if name == '' {
5638 return none
5639 }
5640 if candidate := g.same_receiver_specialized_method_name(name) {
5641 return candidate
5642 }
5643 if name == 'copy' {
5644 return none
5645 }
5646 is_struct_field_should_encode :=
5647 name in ['struct_field_should_encode_T', 'json2__struct_field_should_encode_T', 'struct_field_should_encode', 'json2__struct_field_should_encode']
5648 || name.starts_with('struct_field_should_encode_T_')
5649 || name.starts_with('json2__struct_field_should_encode_T_')
5650 if is_struct_field_should_encode && call_args.len > 1 {
5651 arg_type_name := g.generic_specialization_arg_type_name(call_args[1])
5652 if arg_type_name != '' {
5653 g.record_late_single_generic_call_spec_from_c_type('struct_field_should_encode', 'T',
5654 arg_type_name)
5655 token := generic_specialization_token_from_c_type_name(arg_type_name)
5656 for base_name in [g.qualify_local_call_name('struct_field_should_encode'),
5657 'json2__struct_field_should_encode', 'struct_field_should_encode'] {
5658 if candidate := g.find_specialized_call_name(base_name, token) {
5659 return candidate
5660 }
5661 g.ensure_specialized_call_signature('${base_name}_T_${token}')
5662 if candidate := g.find_specialized_call_name(base_name, token) {
5663 return candidate
5664 }
5665 }
5666 }
5667 }
5668 is_encode_struct_field_value :=
5669 name in ['Encoder__encode_struct_field_value_T', 'json2__Encoder__encode_struct_field_value_T', 'Encoder__encode_struct_field_value', 'json2__Encoder__encode_struct_field_value']
5670 || name.starts_with('Encoder__encode_struct_field_value_T_')
5671 || name.starts_with('json2__Encoder__encode_struct_field_value_T_')
5672 if is_encode_struct_field_value && call_args.len > 1 {
5673 arg_type_name := g.generic_specialization_arg_type_name(call_args[1])
5674 if arg_type_name != '' {
5675 g.record_late_single_generic_call_spec_from_c_type('encode_struct_field_value', 'T',
5676 arg_type_name)
5677 token := generic_specialization_token_from_c_type_name(arg_type_name)
5678 for base_name in [
5679 g.qualify_local_call_name('Encoder__encode_struct_field_value'),
5680 'json2__Encoder__encode_struct_field_value',
5681 'Encoder__encode_struct_field_value',
5682 ] {
5683 if candidate := g.find_specialized_call_name(base_name, token) {
5684 return candidate
5685 }
5686 g.ensure_specialized_call_signature('${base_name}_T_${token}')
5687 if candidate := g.find_specialized_call_name(base_name, token) {
5688 return candidate
5689 }
5690 }
5691 }
5692 }
5693 if name.ends_with('_T') {
5694 base_name := generic_call_base_name_for_specialization(name)
5695 if base_name != '' {
5696 mut has_arg_bindings := false
5697 if generic_decl := g.find_generic_fn_decl_by_base_name(base_name) {
5698 has_arg_bindings = g.generic_call_has_concrete_arg_bindings(generic_decl,
5699 call_args, g.generic_fn_param_names(generic_decl))
5700 }
5701 cur_fn_specialized_name := if g.cur_fn_c_name != '' {
5702 g.cur_fn_c_name
5703 } else {
5704 g.cur_fn_name
5705 }
5706 if cur_fn_specialized_name.contains('_T_') && !has_arg_bindings {
5707 suffix := cur_fn_specialized_name.all_after('_T_')
5708 if suffix != '' {
5709 candidate := '${base_name}_T_${suffix}'
5710 g.ensure_specialized_call_signature(candidate)
5711 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5712 return candidate
5713 }
5714 if !base_name.contains('__') {
5715 qualified := g.qualify_local_call_name(candidate)
5716 g.ensure_specialized_call_signature(qualified)
5717 if qualified != candidate
5718 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5719 return qualified
5720 }
5721 }
5722 }
5723 }
5724 if g.active_generic_types.len > 0 && !has_arg_bindings {
5725 if concrete := g.active_generic_types['T'] {
5726 token := g.generic_specialization_token_from_type(concrete)
5727 candidate := '${base_name}_T_${token}'
5728 g.ensure_specialized_call_signature(candidate)
5729 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5730 return candidate
5731 }
5732 if !base_name.contains('__') {
5733 qualified := g.qualify_local_call_name(candidate)
5734 g.ensure_specialized_call_signature(qualified)
5735 if qualified != candidate
5736 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5737 return qualified
5738 }
5739 }
5740 }
5741 }
5742 if g.active_generic_types.len == 0 {
5743 if candidate := g.find_specialized_call_name(g.qualify_local_call_name(base_name),
5744 'f64')
5745 {
5746 return candidate
5747 }
5748 if candidate := g.find_specialized_call_name(base_name, 'f64') {
5749 return candidate
5750 }
5751 }
5752 }
5753 }
5754 if name in ['decode_value_T', 'json2__decode_value_T'] && call_args.len > 1 {
5755 base_arg := if call_args[1] is ast.ModifierExpr {
5756 (call_args[1] as ast.ModifierExpr).expr
5757 } else {
5758 call_args[1]
5759 }
5760 arg_type_name := g.get_expr_type(base_arg).trim_space().trim_right('*')
5761 if arg_type_name != '' && arg_type_name != 'int' {
5762 if candidate := g.find_specialized_call_name(g.qualify_local_call_name('decode_value'),
5763 sanitize_generic_token_part(arg_type_name))
5764 {
5765 return candidate
5766 }
5767 }
5768 }
5769 if name in ['Encoder__encode_value_T', 'json2__Encoder__encode_value_T', 'Encoder__encode_value', 'json2__Encoder__encode_value']
5770 && call_args.len > 1 {
5771 base_arg := if call_args[1] is ast.ModifierExpr {
5772 (call_args[1] as ast.ModifierExpr).expr
5773 } else {
5774 call_args[1]
5775 }
5776 mut arg_type_name := g.get_expr_type(base_arg).trim_space().trim_right('*')
5777 if (arg_type_name == '' || arg_type_name == 'int') && base_arg is ast.Ident {
5778 arg_type_name =
5779 (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space().trim_right('*')
5780 }
5781 if arg_type_name != '' && arg_type_name != 'int' {
5782 if candidate := g.find_specialized_call_name('json2__Encoder__encode_value',
5783 sanitize_generic_token_part(arg_type_name))
5784 {
5785 return candidate
5786 }
5787 }
5788 }
5789 if g.active_generic_types.len > 0 && name.contains('_T_') {
5790 base_name := name.all_before('_T_')
5791 mut parts := name.all_after('_T_').split('_')
5792 mut c_parts := parts.clone()
5793 mut changed := false
5794 for i, part in parts {
5795 if concrete := g.active_generic_types[part] {
5796 parts[i] = g.generic_specialization_token_from_type(concrete)
5797 c_type := g.types_type_to_c(concrete).trim_space().trim_right('*')
5798 c_parts[i] = if c_type == '' {
5799 parts[i]
5800 } else {
5801 sanitize_generic_token_part(c_type)
5802 }
5803 changed = true
5804 }
5805 }
5806 if changed {
5807 candidate := '${base_name}_T_${parts.join('_')}'
5808 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5809 return candidate
5810 }
5811 g.ensure_specialized_call_signature(candidate)
5812 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5813 return candidate
5814 }
5815 if !base_name.contains('__') {
5816 qualified := g.qualify_local_call_name(candidate)
5817 if qualified != candidate
5818 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5819 return qualified
5820 }
5821 g.ensure_specialized_call_signature(qualified)
5822 if qualified != candidate
5823 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5824 return qualified
5825 }
5826 }
5827 c_candidate := '${base_name}_T_${c_parts.join('_')}'
5828 if c_candidate != candidate
5829 && (c_candidate in g.fn_param_is_ptr || c_candidate in g.fn_return_types) {
5830 return c_candidate
5831 }
5832 if c_candidate != candidate {
5833 g.ensure_specialized_call_signature(c_candidate)
5834 if c_candidate in g.fn_param_is_ptr || c_candidate in g.fn_return_types {
5835 return c_candidate
5836 }
5837 }
5838 if c_candidate != candidate && !base_name.contains('__') {
5839 qualified := g.qualify_local_call_name(c_candidate)
5840 if qualified != c_candidate
5841 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5842 return qualified
5843 }
5844 g.ensure_specialized_call_signature(qualified)
5845 if qualified != c_candidate
5846 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
5847 return qualified
5848 }
5849 }
5850 }
5851 }
5852 if g.active_generic_types.len > 0 {
5853 mut parts := name.split('_')
5854 mut changed := false
5855 for i, part in parts {
5856 if concrete := g.active_generic_types[part] {
5857 parts[i] = 'T_${g.generic_specialization_token_from_type(concrete)}'
5858 changed = true
5859 }
5860 }
5861 if changed {
5862 candidate := parts.join('_')
5863 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5864 return candidate
5865 }
5866 }
5867 }
5868 if !name.contains('__') {
5869 qualified := g.qualify_local_call_name(name)
5870 if qualified != name && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types)
5871 && !g.has_generic_fn_decl_by_base_name(qualified) {
5872 return none
5873 }
5874 }
5875 mut skip_arg0_specialization := false
5876 arg0_lookup_name := if name.contains('_T_') {
5877 name.all_before('_T_')
5878 } else if name.ends_with('_T') {
5879 name[..name.len - 2]
5880 } else {
5881 name
5882 }
5883 if generic_decl := g.find_generic_fn_decl_by_base_name(arg0_lookup_name) {
5884 skip_arg0_specialization = generic_decl.is_method || generic_decl.typ.params.len == 0
5885 || !expr_has_generic_placeholder(generic_decl.typ.params[0].typ)
5886 }
5887 if call_args.len > 0 && !skip_arg0_specialization {
5888 base_arg := if call_args[0] is ast.ModifierExpr {
5889 (call_args[0] as ast.ModifierExpr).expr
5890 } else {
5891 call_args[0]
5892 }
5893 mut arg_type_name := ''
5894 if active_concrete := g.active_generic_concrete_from_arg(base_arg) {
5895 // If the argument is a parameter whose V type is the current generic T,
5896 // specialize by that V type. Its C ABI type may be erased to void*,
5897 // especially for function values.
5898 arg_type_name = g.generic_specialization_token_from_type(active_concrete)
5899 }
5900 if arg_type_name == '' && base_arg is ast.InitExpr {
5901 arg_type_name = g.expr_type_to_c(base_arg.typ).trim_space()
5902 }
5903 if arg_type_name == '' || arg_type_name == 'int' {
5904 arg_type_name = g.get_expr_type(base_arg).trim_space()
5905 }
5906 if (arg_type_name == '' || arg_type_name == 'int') && base_arg is ast.Ident {
5907 arg_type_name = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
5908 }
5909 if arg_type_name == '' || arg_type_name == 'int' {
5910 if raw := g.get_raw_type(base_arg) {
5911 arg_type_name = g.types_type_to_c(raw).trim_space()
5912 }
5913 }
5914 if arg_type_name != '' && arg_type_name != 'int' && arg_type_name.trim_space() != 'void*' {
5915 arg_type_name = g.resolve_sum_payload_arg_type_name(base_arg, arg_type_name)
5916 }
5917 arg_type_name = if arg_type_name.trim_space() == 'void*' {
5918 'voidptr'
5919 } else {
5920 arg_type_name.trim_right('*')
5921 }
5922 if arg_type_name != '' && arg_type_name != 'int' {
5923 mut candidate_types := [arg_type_name]
5924 if !arg_type_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
5925 && g.cur_module != 'builtin' {
5926 candidate_types << '${g.cur_module}__${arg_type_name}'
5927 if arg_type_name.starts_with('Array_') && arg_type_name.len > 'Array_'.len {
5928 elem_type_name := arg_type_name['Array_'.len..]
5929 if !elem_type_name.contains('__') {
5930 candidate_types << 'Array_${g.cur_module}__${elem_type_name}'
5931 }
5932 }
5933 }
5934 for concrete_type_name in candidate_types {
5935 if candidate := g.find_specialized_call_name(name,
5936 sanitize_generic_token_part(concrete_type_name))
5937 {
5938 return candidate
5939 }
5940 }
5941 }
5942 }
5943 if name in ['decode_value', 'json2__decode_value'] && g.cur_fn_ret_type.starts_with('_result_') {
5944 result_base := g.cur_fn_ret_type['_result_'.len..]
5945 if candidate := g.find_specialized_call_name(g.qualify_local_call_name('decode_value'),
5946 sanitize_generic_token_part(result_base))
5947 {
5948 return candidate
5949 }
5950 }
5951 mut generic_base_name := generic_call_base_name_for_specialization(name)
5952 if qualified_name := g.qualified_generic_fn_base_name(generic_base_name) {
5953 generic_base_name = qualified_name
5954 }
5955 decl := g.find_generic_fn_decl_by_base_name(generic_base_name) or {
5956 if generic_base_name.contains('__') {
5957 g.find_generic_fn_decl_by_base_name(generic_base_name.all_after_last('__')) or {
5958 return none
5959 }
5960 } else {
5961 return none
5962 }
5963 }
5964 generic_params := g.generic_fn_param_names(decl)
5965 if generic_params.len == 0 || g.env == unsafe { nil } {
5966 return none
5967 }
5968 if name.contains('_T_') {
5969 if active_bindings := g.active_generic_bindings_matching_name_suffix(name, generic_params) {
5970 mut suffixes := []string{cap: generic_params.len}
5971 for param_name in generic_params {
5972 concrete := active_bindings[param_name] or { return none }
5973 suffixes << g.generic_specialization_token_from_type(concrete)
5974 }
5975 candidate := '${generic_base_name}_T_${suffixes.join('_')}'
5976 g.ensure_specialized_call_signature(candidate)
5977 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5978 return candidate
5979 }
5980 specialized_candidate := g.specialized_fn_name(decl, active_bindings)
5981 if specialized_candidate != candidate {
5982 g.ensure_specialized_call_signature(specialized_candidate)
5983 if specialized_candidate in g.fn_param_is_ptr
5984 || specialized_candidate in g.fn_return_types {
5985 return specialized_candidate
5986 }
5987 }
5988 }
5989 }
5990 has_arg_bindings := g.generic_call_has_concrete_arg_bindings(decl, call_args, generic_params)
5991 cur_fn_specialized_name := if g.cur_fn_c_name != '' { g.cur_fn_c_name } else { g.cur_fn_name }
5992 if cur_fn_specialized_name.contains('_T_') && !has_arg_bindings {
5993 suffix := cur_fn_specialized_name.all_after('_T_')
5994 if suffix != '' {
5995 candidate := '${generic_base_name}_T_${suffix}'
5996 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
5997 return candidate
5998 }
5999 if !generic_base_name.contains('__') {
6000 qualified := g.qualify_local_call_name(candidate)
6001 if qualified != candidate
6002 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
6003 return qualified
6004 }
6005 }
6006 }
6007 }
6008 if g.active_generic_types.len > 0 && !has_arg_bindings {
6009 mut active_bindings := map[string]types.Type{}
6010 mut suffixes := []string{}
6011 for param_name in generic_params {
6012 concrete := g.active_generic_types[param_name] or { break }
6013 active_bindings[param_name] = concrete
6014 suffixes << g.generic_specialization_token_from_type(concrete)
6015 }
6016 if active_bindings.len == generic_params.len {
6017 candidate := g.specialized_fn_name(decl, active_bindings)
6018 g.ensure_specialized_call_signature(candidate)
6019 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
6020 return candidate
6021 }
6022 alt_candidate := '${generic_base_name}_T_${suffixes.join('_')}'
6023 g.ensure_specialized_call_signature(alt_candidate)
6024 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
6025 return alt_candidate
6026 }
6027 }
6028 }
6029 if g.generic_call_name_has_placeholder_suffix(name) {
6030 if return_bindings := g.infer_generic_bindings_from_current_return(decl, generic_params) {
6031 g.record_late_generic_call_spec(generic_base_name, return_bindings)
6032 candidate := g.specialized_fn_name(decl, return_bindings)
6033 g.ensure_specialized_call_signature(candidate)
6034 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
6035 return candidate
6036 }
6037 mut suffixes := []string{cap: generic_params.len}
6038 for param_name in generic_params {
6039 concrete := return_bindings[param_name] or { return none }
6040 suffixes << g.generic_specialization_token_from_type(concrete)
6041 }
6042 alt_candidate := '${generic_base_name}_T_${suffixes.join('_')}'
6043 g.ensure_specialized_call_signature(alt_candidate)
6044 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
6045 return alt_candidate
6046 }
6047 }
6048 }
6049 if generic_params.len == 1 {
6050 param_name := generic_params[0]
6051 arg_offset := if decl.is_method && call_args.len == decl.typ.params.len + 1 { 1 } else { 0 }
6052 for i, param in decl.typ.params {
6053 arg_idx := i + arg_offset
6054 if arg_idx >= call_args.len || !expr_has_generic_placeholder(param.typ) {
6055 continue
6056 }
6057 direct_placeholder := direct_generic_placeholder_name(param.typ)
6058 if direct_placeholder != param_name {
6059 continue
6060 }
6061 base_arg := if call_args[arg_idx] is ast.ModifierExpr {
6062 (call_args[arg_idx] as ast.ModifierExpr).expr
6063 } else {
6064 call_args[arg_idx]
6065 }
6066 mut arg_type_name := g.generic_specialization_arg_type_name(base_arg)
6067 if base_arg is ast.InitExpr {
6068 arg_type_name = g.expr_type_to_c(base_arg.typ).trim_space()
6069 }
6070 if arg_type_name == '' || arg_type_name == 'int' {
6071 mut found_local_arg_type := false
6072 if base_arg is ast.Ident {
6073 arg_type_name = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
6074 found_local_arg_type = arg_type_name != ''
6075 }
6076 if !found_local_arg_type && (arg_type_name == '' || arg_type_name == 'int') {
6077 arg_type_name = g.get_expr_type(base_arg).trim_space()
6078 }
6079 }
6080 if arg_type_name == '' && base_arg is ast.Ident {
6081 arg_type_name = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
6082 }
6083 if arg_type_name == '' || arg_type_name == 'int' {
6084 if raw := g.get_raw_type(base_arg) {
6085 arg_type_name = g.types_type_to_c(raw).trim_space()
6086 }
6087 }
6088 if arg_type_name != '' && arg_type_name != 'int'
6089 && arg_type_name.trim_space() != 'void*' {
6090 arg_type_name = g.resolve_sum_payload_arg_type_name(base_arg, arg_type_name)
6091 }
6092 arg_type_name = if arg_type_name.trim_space() == 'void*' {
6093 'voidptr'
6094 } else {
6095 arg_type_name.trim_space()
6096 }
6097 if arg_type_name == '' || arg_type_name == 'int' {
6098 continue
6099 }
6100 is_direct_array_param := param.typ is ast.Type && param.typ is ast.ArrayType
6101 mut candidate_types := [arg_type_name]
6102 if !arg_type_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
6103 && g.cur_module != 'builtin' {
6104 candidate_types << '${g.cur_module}__${arg_type_name}'
6105 if is_direct_array_param && arg_type_name.starts_with('Array_')
6106 && arg_type_name.len > 'Array_'.len {
6107 elem_type_name := arg_type_name['Array_'.len..]
6108 if !elem_type_name.contains('__') {
6109 candidate_types << 'Array_${g.cur_module}__${elem_type_name}'
6110 }
6111 }
6112 }
6113 for concrete_type_name in candidate_types {
6114 token := generic_specialization_token_for_direct_param(param.typ,
6115 concrete_type_name)
6116 if candidate := g.find_specialized_call_name(name, token) {
6117 return candidate
6118 }
6119 if !is_direct_array_param {
6120 continue
6121 }
6122 candidate_name := '${name}_T_${token}'
6123 g.ensure_specialized_call_signature(candidate_name)
6124 if candidate := g.find_specialized_call_name(name, token) {
6125 return candidate
6126 }
6127 if !name.contains('__') {
6128 qualified := g.qualify_local_call_name(candidate_name)
6129 g.ensure_specialized_call_signature(qualified)
6130 if candidate := g.find_specialized_call_name(name, token) {
6131 return candidate
6132 }
6133 }
6134 }
6135 }
6136 }
6137 mut bindings := map[string]types.Type{}
6138 mut receiver_bindings := map[string]types.Type{}
6139 if decl.is_method && call_args.len > 0 && expr_has_generic_placeholder(decl.receiver.typ) {
6140 recv_arg := if call_args[0] is ast.ModifierExpr {
6141 (call_args[0] as ast.ModifierExpr).expr
6142 } else {
6143 call_args[0]
6144 }
6145 if expr_has_valid_data(recv_arg) {
6146 if concrete := g.concrete_type_from_generic_call_arg(recv_arg) {
6147 g.infer_generic_type_bindings_from_param(decl.receiver.typ,
6148 g.concrete_type_with_active_generics(concrete),
6149 receiver_generic_param_names(decl), mut receiver_bindings)
6150 }
6151 }
6152 }
6153 arg_offset := if decl.is_method && call_args.len == decl.typ.params.len + 1 { 1 } else { 0 }
6154 for i, param in decl.typ.params {
6155 arg_idx := i + arg_offset
6156 if arg_idx >= call_args.len || !expr_has_generic_placeholder(param.typ) {
6157 continue
6158 }
6159 arg := if call_args[arg_idx] is ast.ModifierExpr {
6160 (call_args[arg_idx] as ast.ModifierExpr).expr
6161 } else {
6162 call_args[arg_idx]
6163 }
6164 if !expr_has_valid_data(arg) {
6165 continue
6166 }
6167 mut concrete := types.Type(types.void_)
6168 mut found_concrete := false
6169 if arg is ast.InitExpr {
6170 c_name := g.expr_type_to_c(arg.typ).trim_space()
6171 if concrete_type := g.concrete_type_from_call_arg_c_name(c_name) {
6172 concrete = concrete_type
6173 found_concrete = true
6174 }
6175 }
6176 if !found_concrete && arg is ast.CastExpr {
6177 c_name := g.expr_type_to_c(arg.typ).trim_space()
6178 if concrete_type := g.concrete_type_from_call_arg_c_name(c_name) {
6179 concrete = concrete_type
6180 found_concrete = true
6181 }
6182 }
6183 if !found_concrete {
6184 c_name := g.generic_specialization_arg_type_name(arg).trim_space()
6185 if c_name != '' && c_name != 'int' && c_name != 'void*' {
6186 if concrete_type := g.concrete_type_from_call_arg_c_name(c_name) {
6187 concrete = concrete_type
6188 found_concrete = true
6189 }
6190 }
6191 }
6192 if !found_concrete {
6193 if active_concrete := g.active_generic_concrete_from_arg(arg) {
6194 concrete = active_concrete
6195 found_concrete = true
6196 }
6197 }
6198 pos := arg.pos()
6199 if !found_concrete && arg is ast.Ident {
6200 arg_type_name := (g.get_local_var_c_type(arg.name) or { '' }).trim_space()
6201 if arg_type_name != '' {
6202 if local_type := g.concrete_type_from_call_arg_c_name(arg_type_name) {
6203 concrete = local_type
6204 found_concrete = true
6205 }
6206 }
6207 }
6208 if !found_concrete && pos.id != 0 {
6209 if env_type := g.env.get_expr_type(pos.id) {
6210 concrete = env_type
6211 found_concrete = true
6212 }
6213 }
6214 if !found_concrete {
6215 if raw := g.get_raw_type(arg) {
6216 if raw.name() != 'int' {
6217 concrete = raw
6218 found_concrete = true
6219 }
6220 }
6221 }
6222 if !found_concrete {
6223 continue
6224 }
6225 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
6226 concrete), generic_params, mut bindings)
6227 }
6228 if bindings.len != generic_params.len {
6229 return none
6230 }
6231 for _, concrete in bindings {
6232 if type_contains_generic_placeholder(concrete) {
6233 return none
6234 }
6235 }
6236 g.record_late_generic_call_spec(generic_base_name, bindings)
6237 mut suffixes := []string{}
6238 for param_name in generic_params {
6239 if concrete := bindings[param_name] {
6240 suffixes << g.generic_specialization_token_from_type(concrete)
6241 }
6242 }
6243 if suffixes.len > 0 {
6244 lookup_name := generic_base_name
6245 if candidate := g.find_specialized_call_name(lookup_name, suffixes.join('_')) {
6246 return candidate
6247 }
6248 suffix := suffixes.join('_')
6249 alt_candidate := '${lookup_name}_T_${suffix}'
6250 g.ensure_specialized_call_signature(alt_candidate)
6251 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
6252 return alt_candidate
6253 }
6254 }
6255 mut combined_bindings := receiver_bindings.clone()
6256 for param_name, concrete in bindings {
6257 combined_bindings[param_name] = concrete
6258 }
6259 candidate := g.specialized_fn_name(decl, combined_bindings)
6260 g.ensure_specialized_call_signature(candidate)
6261 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
6262 return candidate
6263 }
6264 if candidate.contains('__') {
6265 if known_candidate := g.find_known_qualified_fn_name(candidate.all_after_last('__')) {
6266 return known_candidate
6267 }
6268 }
6269 return candidate
6270}
6271
6272fn (mut g Gen) specialize_direct_method_call_name(name string, receiver ast.Expr, args []ast.Expr) string {
6273 if name == '' {
6274 return name
6275 }
6276 mut call_args := []ast.Expr{cap: args.len + 1}
6277 call_args << receiver
6278 call_args << args
6279 if specialized_name := g.try_specialize_generic_call_name(name, call_args) {
6280 if g.generic_specialization_should_replace_call_name(name, specialized_name) {
6281 return specialized_name
6282 }
6283 }
6284 return name
6285}
6286
6287// register_builder_methods ensures strings__Builder methods are known to
6288// fn_param_is_ptr so call sites emit &sb for the receiver. The Builder
6289// is a typedef for array and its methods take a mutable (pointer) receiver.
6290fn (mut g Gen) register_builder_methods() {
6291 builder_methods := [
6292 // name, has_extra_param, return_type
6293 ['strings__Builder__write_rune', 'true', 'void'],
6294 ['strings__Builder__write_u8', 'true', 'void'],
6295 ['strings__Builder__write_string', 'true', 'void'],
6296 ['strings__Builder__write_ptr', 'true', 'void'],
6297 ['strings__Builder__write_repeated_rune', 'true', 'void'],
6298 ['strings__Builder__spart', 'false', 'string'],
6299 ['strings__Builder__cut_last', 'true', 'void'],
6300 ['strings__Builder__str', 'false', 'string'],
6301 ]
6302 for entry in builder_methods {
6303 name := entry[0]
6304 has_extra := entry[1] == 'true'
6305 ret := entry[2]
6306 if name !in g.fn_param_is_ptr {
6307 if has_extra {
6308 g.fn_param_is_ptr[name] = [true, false]
6309 g.fn_param_types[name] = ['strings__Builder*', 'int']
6310 } else {
6311 g.fn_param_is_ptr[name] = [true]
6312 g.fn_param_types[name] = ['strings__Builder*']
6313 }
6314 g.fn_return_types[name] = ret
6315 }
6316 }
6317}
6318
6319fn (mut g Gen) emit_fixed_array_return_wrappers() {
6320 mut fixed_types := g.fixed_array_ret_wrappers.keys()
6321 fixed_types.sort()
6322 for fixed_type in fixed_types {
6323 wrapper_name := g.fixed_array_ret_wrappers[fixed_type] or {
6324 fixed_array_return_wrapper_name(fixed_type)
6325 }
6326 key := 'fixed_ret_wrapper_${wrapper_name}'
6327 if key in g.emitted_types {
6328 continue
6329 }
6330 g.emitted_types[key] = true
6331 g.sb.writeln('typedef struct ${wrapper_name} {')
6332 g.sb.writeln('\t${fixed_type} ret_arr;')
6333 g.sb.writeln('} ${wrapper_name};')
6334 g.sb.writeln('')
6335 }
6336}
6337
6338fn (mut g Gen) gen_fn_decl(node ast.FnDecl) {
6339 g.gen_fn_decl_ptr(&node)
6340}
6341
6342fn (mut g Gen) gen_fn_decl_ptr(node &ast.FnDecl) {
6343 if !g.should_emit_fn_decl_cached(g.cur_module, *node) {
6344 return
6345 }
6346 // All other functions are resolved from the host executable.
6347 if g.pref != unsafe { nil } && g.pref.is_shared_lib {
6348 if !node.attributes.has('live') {
6349 return
6350 }
6351 }
6352 // Generate V and .c.v function bodies, but skip JS and C extern declarations.
6353 if node.language == .js {
6354 return
6355 }
6356 // Header files carry declaration-only function nodes.
6357 if g.cur_file_name.ends_with('.vh') {
6358 return
6359 }
6360 if node.language == .c && node.stmts.len == 0 {
6361 return
6362 }
6363 if g.should_skip_backend_generic_fn(*node) {
6364 return
6365 }
6366 if g.generic_fn_param_names(*node).len > 0 {
6367 // maxof[T]/minof[T] are compile-time functions fully inlined by the transformer.
6368 // Skip emitting stub bodies (which contain invalid C).
6369 if node.name in ['maxof', 'minof'] {
6370 return
6371 }
6372 prev_generic_types := g.active_generic_types.clone()
6373 specs := g.generic_fn_specializations_for_emit_scope_with_receiver_bindings(*node)
6374 for spec in specs {
6375 g.active_generic_types = spec.generic_types.clone()
6376 g.gen_fn_decl_with_name_ptr(node, spec.name)
6377 }
6378 g.active_generic_types = prev_generic_types.clone()
6379 return
6380 }
6381
6382 // For methods on generic structs (e.g. LinkedList[T].push), resolve the
6383 // generic params from the receiver type using recorded struct bindings.
6384 // This ensures T resolves to the concrete type (e.g. ValueInfo) instead
6385 // of the default fallback (f64).
6386 recv_generic_params := receiver_generic_param_names(*node)
6387 if recv_generic_params.len > 0 {
6388 all_bindings := g.get_all_receiver_generic_bindings(*node)
6389 if all_bindings.len > 0 {
6390 prev_generic_types := g.active_generic_types.clone()
6391 for bi in all_bindings {
6392 g.active_generic_types = bi.clone()
6393 fn_name := g.get_fn_name(*node)
6394 if fn_name != '' {
6395 g.gen_fn_decl_with_name_ptr(node, fn_name)
6396 }
6397 }
6398 g.active_generic_types = prev_generic_types.clone()
6399 return
6400 } else if bindings := g.get_receiver_generic_bindings(*node) {
6401 prev_generic_types := g.active_generic_types.clone()
6402 g.active_generic_types = bindings.clone()
6403 fn_name := g.get_fn_name(*node)
6404 if fn_name != '' {
6405 g.gen_fn_decl_with_name_ptr(node, fn_name)
6406 }
6407 g.active_generic_types = prev_generic_types.clone()
6408 return
6409 }
6410 return
6411 }
6412 if node.is_method && node.receiver.typ !is ast.EmptyExpr {
6413 recv_c_name := g.decl_expr_type_to_c(node.receiver.typ).trim_right('*')
6414 recv_lookup_name := if recv_c_name.contains('__') {
6415 recv_c_name.all_after_last('__')
6416 } else {
6417 recv_c_name
6418 }
6419 recv_struct := g.lookup_struct_type(recv_lookup_name)
6420 if recv_struct.generic_params.len > 0 {
6421 body_key := 'body_${recv_c_name}'
6422 if body_key !in g.emitted_types && body_key !in g.pending_late_body_keys {
6423 return
6424 }
6425 }
6426 }
6427
6428 mut fn_name := g.get_fn_name(*node)
6429 if fn_name == '' {
6430 return
6431 }
6432 if specialized_name := g.called_specialized_name_for_base(fn_name) {
6433 g.copy_fn_signature(fn_name, specialized_name)
6434 fn_name = specialized_name
6435 }
6436 g.gen_fn_decl_with_name_ptr(node, fn_name)
6437}
6438
6439fn (mut g Gen) gen_fn_decl_with_name(node ast.FnDecl, fn_name string) {
6440 g.gen_fn_decl_with_name_ptr(&node, fn_name)
6441}
6442
6443fn (mut g Gen) remember_called_specialized_name(name string) {
6444 if name == '' || !name.contains('_T_') {
6445 return
6446 }
6447 base := name.all_before('_T_')
6448 if base == '' || base in g.called_specialized_names {
6449 return
6450 }
6451 g.called_specialized_names[base] = name
6452}
6453
6454fn (mut g Gen) ensure_called_specialized_name_index() {
6455 if g.called_specialized_names_indexed {
6456 return
6457 }
6458 g.called_specialized_names = map[string]string{}
6459 for name, _ in g.called_fn_names {
6460 g.remember_called_specialized_name(name)
6461 }
6462 g.called_specialized_names_indexed = true
6463}
6464
6465fn (g &Gen) called_specialized_name_for_base(base string) ?string {
6466 if base.contains('_T_') {
6467 return none
6468 }
6469 if g.called_specialized_names_indexed {
6470 if name := g.called_specialized_names[base] {
6471 return name
6472 }
6473 return none
6474 }
6475 prefix := '${base}_T_'
6476 for name, _ in g.called_fn_names {
6477 if name.starts_with(prefix) {
6478 return name
6479 }
6480 }
6481 return none
6482}
6483
6484fn (mut g Gen) copy_fn_signature(from_name string, to_name string) {
6485 if to_name in g.fn_return_types || to_name in g.fn_param_is_ptr {
6486 return
6487 }
6488 if ret := g.fn_return_types[from_name] {
6489 g.fn_return_types[to_name] = ret
6490 }
6491 if params := g.fn_param_is_ptr[from_name] {
6492 g.fn_param_is_ptr[to_name] = params.clone()
6493 }
6494 if param_types := g.fn_param_types[from_name] {
6495 g.fn_param_types[to_name] = param_types.clone()
6496 }
6497 g.remember_specialized_fn_base(to_name)
6498}
6499
6500// Keep pass 5 on pointer-based FnDecl access; large by-value copies here
6501// corrupt the self-hosted cleanc caller after the first emitted body.
6502fn (mut g Gen) gen_fn_decl_with_name_ptr(node &ast.FnDecl, fn_name string) {
6503 if g.should_skip_backend_generic_fn(*node) {
6504 return
6505 }
6506 fn_key := 'fn_${fn_name}'
6507 if fn_key in g.blocked_fn_keys && !g.explicit_slice_emit_allows(fn_key) {
6508 return
6509 }
6510 if g.should_skip_plain_v_fallback_fn(fn_key) {
6511 return
6512 }
6513 if fn_key in g.emitted_types {
6514 return
6515 }
6516 g.emitted_types[fn_key] = true
6517 saved_module := g.cur_module
6518 saved_import_modules := g.cur_import_modules.clone()
6519 defer {
6520 g.cur_module = saved_module
6521 g.cur_import_modules = saved_import_modules.clone()
6522 g.is_module_ident_cache.clear()
6523 g.resolved_module_names.clear()
6524 }
6525 fn_module := module_prefix_from_fn_name(fn_name)
6526 if fn_module.len > 0 && fn_module[0] >= `a` && fn_module[0] <= `z`
6527 && g.source_module_exists(fn_module) && g.cur_module != fn_module {
6528 g.cur_module = fn_module
6529 g.cur_import_modules.clear()
6530 g.is_module_ident_cache.clear()
6531 g.resolved_module_names.clear()
6532 }
6533
6534 // Set function scope for type lookups
6535 g.cur_fn_name = node.name
6536 g.cur_fn_c_name = fn_name
6537 g.runtime_local_types.clear()
6538 g.runtime_decl_types.clear()
6539 // Drop/RAII (Phase 1B): preload per-return drop snapshots for this fn
6540 // so the ReturnStmt handler can emit `Type__drop(&var)` in source order.
6541 g.cur_fn_return_drops = if g.env != unsafe { nil } {
6542 g.env.drop_at_returns[fn_name] or { [][]types.DropEntry{} }
6543 } else {
6544 [][]types.DropEntry{}
6545 }
6546 g.cur_fn_return_index = 0
6547 g.cur_fn_returned_idents = g.collect_returned_idents(node.stmts)
6548 g.is_module_ident_cache.clear()
6549 g.not_local_var_cache.clear()
6550 g.resolved_module_names.clear()
6551 is_program_main := g.is_program_main_fn(node)
6552 g.cur_fn_ret_type = if is_program_main {
6553 'int'
6554 } else if fn_ret := g.fn_return_types[fn_name] {
6555 fn_ret
6556 } else if node.typ.return_type !is ast.EmptyExpr {
6557 g.expr_type_to_c(node.typ.return_type)
6558 } else {
6559 'void'
6560 }
6561 g.cur_fn_ret_type = normalize_signature_type_name(g.cur_fn_ret_type, 'void')
6562 g.cur_fn_c_ret_type = g.c_fn_return_type_from_v(g.cur_fn_ret_type)
6563 if g.env != unsafe { nil } {
6564 // For methods, the checker stores scopes using V-style receiver type names
6565 // (e.g. "[]string__free"), while get_fn_name returns C-style names
6566 // (e.g. "Array_string__free"). Use V-style name for scope lookup.
6567 scope_fn_name := if node.is_method {
6568 // The checker stores scopes using V-style receiver type names
6569 // (e.g. "[]string__free", "Builder__go_back"). Convert the AST
6570 // receiver type expression to match.
6571 v_type_name := g.receiver_type_to_scope_name(node.receiver.typ)
6572 if v_type_name != '' {
6573 '${v_type_name}__${node.name}'
6574 } else {
6575 fn_name
6576 }
6577 } else {
6578 node.name
6579 }
6580 if fn_scope := g.env.get_fn_scope(g.cur_module, scope_fn_name) {
6581 g.cur_fn_scope = fn_scope
6582 } else if node.is_method {
6583 // Fallback: for type aliases (e.g. Builder = []u8), the checker resolves
6584 // the alias and uses the underlying type name. Try env-based resolution.
6585 mut found := false
6586 if expr_has_valid_data(node.receiver.typ) {
6587 receiver_pos := node.receiver.typ.pos()
6588 if receiver_pos.is_valid() {
6589 if recv_type := g.env.get_expr_type(receiver_pos.id) {
6590 base_type := recv_type.base_type()
6591 alt_scope_name := '${base_type.name()}__${node.name}'
6592 if fn_scope2 := g.env.get_fn_scope(g.cur_module, alt_scope_name) {
6593 g.cur_fn_scope = fn_scope2
6594 found = true
6595 }
6596 }
6597 }
6598 }
6599 if !found {
6600 g.cur_fn_scope = unsafe { nil }
6601 }
6602 } else {
6603 g.cur_fn_scope = unsafe { nil }
6604 }
6605 }
6606
6607 // Track mut parameter names for pointer detection
6608 g.cur_fn_mut_params.clear()
6609 g.cur_fn_generic_params.clear()
6610 if node.is_method && !node.is_static && node.receiver.name != '' {
6611 mut receiver_type := ''
6612 if sig_param_types := g.fn_param_types[fn_name] {
6613 if sig_param_types.len > 0 {
6614 receiver_type = normalize_signature_type_name(sig_param_types[0], '')
6615 }
6616 }
6617 if receiver_type == '' {
6618 receiver_type = g.expr_type_to_c(node.receiver.typ)
6619 }
6620 if node.receiver.is_mut || receiver_type.ends_with('*') {
6621 g.cur_fn_mut_params[node.receiver.name] = true
6622 }
6623 }
6624 // Register receiver type in local runtime type cache so chained selector
6625 // resolution (e.g. a.x.y) can infer field types reliably.
6626 if node.is_method && !node.is_static && node.receiver.name != '' {
6627 mut receiver_type := ''
6628 if sig_param_types := g.fn_param_types[fn_name] {
6629 if sig_param_types.len > 0 {
6630 receiver_type = normalize_signature_type_name(sig_param_types[0], '')
6631 }
6632 }
6633 if receiver_type == '' {
6634 receiver_type = g.expr_type_to_c(node.receiver.typ)
6635 }
6636 if receiver_type != '' {
6637 g.remember_runtime_local_type(node.receiver.name, receiver_type)
6638 }
6639 }
6640 if g.needs_implicit_veb_ctx_param(node, fn_name) {
6641 ctx_type := g.implicit_veb_ctx_c_type()
6642 g.cur_fn_mut_params['ctx'] = true
6643 g.remember_runtime_local_type('ctx', ctx_type)
6644 }
6645 mut param_ptr_offset := 0
6646 if node.is_method && node.receiver.name != '' {
6647 if sig_param_types := g.fn_param_types[fn_name] {
6648 if sig_param_types.len >= node.typ.params.len + 1 {
6649 param_ptr_offset = 1
6650 }
6651 } else if !node.is_static {
6652 param_ptr_offset = 1
6653 }
6654 }
6655 if g.needs_implicit_veb_ctx_param(node, fn_name) {
6656 param_ptr_offset++
6657 }
6658 for param_idx, param in node.typ.params {
6659 mut param_is_ptr := param.is_mut
6660 mut param_type := g.fn_param_signature_c_type(param)
6661 if sig_param_types := g.fn_param_types[fn_name] {
6662 sig_idx := param_idx + param_ptr_offset
6663 if sig_idx < sig_param_types.len {
6664 sig_type := normalize_signature_type_name(sig_param_types[sig_idx], '')
6665 if sig_type != '' {
6666 param_type = sig_type
6667 if sig_type.ends_with('*') {
6668 param_is_ptr = true
6669 }
6670 }
6671 }
6672 }
6673 if ptr_params := g.fn_param_is_ptr[fn_name] {
6674 ptr_idx := param_idx + param_ptr_offset
6675 if ptr_idx < ptr_params.len && ptr_params[ptr_idx] {
6676 param_is_ptr = true
6677 }
6678 }
6679 if param_is_ptr {
6680 g.cur_fn_mut_params[param.name] = true
6681 }
6682 // Register parameter types from the function declaration.
6683 // The declaration type is authoritative and ensures correct types
6684 // even when the env position lookup returns wrong results.
6685 if param_type != '' && param.name != '' {
6686 ptype := if param_is_ptr && !param_type.ends_with('*') {
6687 param_type + '*'
6688 } else {
6689 param_type
6690 }
6691 g.remember_runtime_local_type(param.name, ptype)
6692 // Also register under the C-renamed name (e.g. 'array' → '_v_array')
6693 // so that body references using the renamed identifier can find the type.
6694 if param.name == 'array' {
6695 g.remember_runtime_local_type('_v_array', ptype)
6696 }
6697 }
6698 placeholder := direct_generic_placeholder_name(param.typ)
6699 if placeholder != '' {
6700 g.cur_fn_generic_params[param.name] = placeholder
6701 }
6702 }
6703
6704 // Check for @[live] attribute
6705 is_live_fn := !is_program_main && node.attributes.has('live') && g.cache_bundle_name.len == 0
6706
6707 // Generate function header (with impl_live_ prefix for @[live] functions)
6708 if is_live_fn {
6709 g.gen_fn_head_live_ptr(node, fn_name)
6710 } else {
6711 g.gen_fn_head_with_name_ptr(node, fn_name)
6712 }
6713 g.sb.writeln(' {')
6714 g.indent++
6715
6716 if fn_name.starts_with('Array_') && fn_name.ends_with('_contains') && g.is_freestanding_target()
6717 && g.pref != unsafe { nil } && g.pref.skip_builtin {
6718 g.write_indent()
6719 g.sb.writeln('_Static_assert(0, "${freestanding_missing_heap_runtime_message}: ${fn_name}");')
6720 g.write_indent()
6721 g.sb.writeln('return false;')
6722 g.indent--
6723 g.sb.writeln('}')
6724 g.sb.writeln('')
6725 return
6726 }
6727
6728 if g.gen_json2_decode_string_body(node) {
6729 g.indent--
6730 g.sb.writeln('}')
6731 g.sb.writeln('')
6732 return
6733 }
6734
6735 if g.gen_json2_cached_struct_field_infos_body(node) {
6736 g.indent--
6737 g.sb.writeln('}')
6738 g.sb.writeln('')
6739 return
6740 }
6741
6742 // Main function: initialize argc/argv
6743 if is_program_main {
6744 g.write_indent()
6745 g.sb.writeln('g_main_argc = ___argc;')
6746 g.write_indent()
6747 g.sb.writeln('g_main_argv = (void*)___argv;')
6748 // Prealloc arena initialization (must run before any V allocations)
6749 if g.pref != unsafe { nil } && g.pref.prealloc {
6750 g.write_indent()
6751 g.sb.writeln('prealloc_vinit();')
6752 }
6753 // GC initialization (translated from Go's runtime GC init)
6754 if g.pref != unsafe { nil } && g.pref.gc_mode == .vgc {
6755 g.write_indent()
6756 g.sb.writeln('// VGC initialization (concurrent tri-color mark-and-sweep)')
6757 g.write_indent()
6758 g.sb.writeln('builtin__vgc_init();')
6759 }
6760 for init_call in g.cached_init_calls {
6761 g.write_indent()
6762 g.sb.writeln('${init_call}();')
6763 }
6764 g.write_indent()
6765 g.sb.writeln('g_main_argc = ___argc;')
6766 g.write_indent()
6767 g.sb.writeln('g_main_argv = (void*)___argv;')
6768 }
6769 // Live reload is only present when the program actually declares @[live] functions.
6770 if is_program_main && g.has_live_reload_functions() {
6771 g.write_indent()
6772 g.sb.writeln('__v_live_init();')
6773 }
6774 g.gen_stmts(node.stmts)
6775
6776 // Drop/RAII: invoke `Type__drop(&var)` for each still-owned binding
6777 // the checker scheduled. Only emitted at the natural fn exit (no
6778 // early-return support yet) and skipped when the body ends in an
6779 // explicit `return` (those drops would be unreachable). Phase 1B
6780 // will weave drops through every return point.
6781 g.emit_scheduled_drops(node.stmts, fn_name)
6782
6783 // Implicit return 0 for main
6784 if is_program_main {
6785 g.write_indent()
6786 g.sb.writeln('return 0;')
6787 } else if g.cur_fn_ret_type.starts_with('_result_') {
6788 g.write_indent()
6789 g.sb.writeln('return (${g.cur_fn_ret_type}){0};')
6790 } else if g.cur_fn_ret_type.starts_with('_option_') {
6791 g.write_indent()
6792 g.sb.writeln('return (${g.cur_fn_ret_type}){0};')
6793 }
6794
6795 g.indent--
6796 g.sb.writeln('}')
6797 g.sb.writeln('')
6798}
6799
6800// emit_scheduled_drops writes destructor calls for the bindings the
6801// checker recorded under `fn_name` in `env.drop_at_fn_exit`. Calls are
6802// emitted in reverse declaration order (LIFO: last declared, first
6803// dropped) and skipped entirely if the body's last statement is an
6804// explicit `return` — the drops would be unreachable code after such a
6805// return, and Phase 1B will handle per-return drop weaving instead.
6806fn (mut g Gen) emit_scheduled_drops(stmts []ast.Stmt, fn_name string) {
6807 if g.env == unsafe { nil } {
6808 return
6809 }
6810 entries := g.env.drop_at_fn_exit[fn_name] or { return }
6811 if entries.len == 0 {
6812 return
6813 }
6814 if cleanc_stmts_end_with_return(stmts) {
6815 return
6816 }
6817 for i := entries.len - 1; i >= 0; i-- {
6818 entry := entries[i]
6819 g.write_indent()
6820 g.sb.writeln('${entry.type_name}__drop(&${entry.var_name});')
6821 }
6822}
6823
6824// emit_scheduled_drops_at_return writes destructor calls for the bindings
6825// the checker recorded for the current ReturnStmt (Phase 1B). Snapshots
6826// are consumed positionally — the counter advances every call so the next
6827// ReturnStmt picks up the next snapshot. Drops emit in LIFO order
6828// (declaration-order reverse) for a single coherent scope. Bindings being
6829// returned (and thus moved out) were excluded by the checker.
6830fn (mut g Gen) emit_scheduled_drops_at_return() {
6831 if g.suppress_return_drop_emit {
6832 return
6833 }
6834 if g.cur_fn_return_index >= g.cur_fn_return_drops.len {
6835 return
6836 }
6837 entries := g.cur_fn_return_drops[g.cur_fn_return_index]
6838 g.cur_fn_return_index++
6839 for i := entries.len - 1; i >= 0; i-- {
6840 entry := entries[i]
6841 g.write_indent()
6842 g.sb.writeln('${entry.type_name}__drop(&${entry.var_name});')
6843 }
6844}
6845
6846fn cleanc_stmts_end_with_return(stmts []ast.Stmt) bool {
6847 for i := stmts.len - 1; i >= 0; i-- {
6848 stmt := stmts[i]
6849 if stmt is ast.EmptyStmt {
6850 continue
6851 }
6852 return stmt is ast.ReturnStmt
6853 }
6854 return false
6855}
6856
6857fn (mut g Gen) gen_json2_decode_string_body(node &ast.FnDecl) bool {
6858 if g.cur_module != 'json2' || !node.is_method || node.name != 'decode_string' {
6859 return false
6860 }
6861 concrete := g.active_generic_types['T'] or { return false }
6862 if concrete !is types.String && concrete.name() != 'string' {
6863 return false
6864 }
6865 g.write_indent()
6866 g.sb.writeln('json2__ValueInfo string_info = decoder->current_node->value;')
6867 g.write_indent()
6868 g.sb.writeln('if (string_info.value_kind == json2__ValueKind__string) {')
6869 g.indent++
6870 g.write_indent()
6871 g.sb.writeln('int string_start = string_info.position + 1;')
6872 g.write_indent()
6873 g.sb.writeln('int string_end = string_info.position + string_info.length - 1;')
6874 g.write_indent()
6875 g.sb.writeln('string string_body = string__substr(decoder->json, string_start, string_end);')
6876 g.write_indent()
6877 g.sb.writeln('*val = string_body;')
6878 g.write_indent()
6879 g.sb.writeln('return (_result_void){ .is_error=false };')
6880 g.indent--
6881 g.write_indent()
6882 g.sb.writeln('}')
6883 g.write_indent()
6884 g.sb.writeln('_result_void _err = json2__Decoder__decode_error(decoder, (string){.str = "Expected string", .len = sizeof("Expected string") - 1, .is_lit = 1});')
6885 g.write_indent()
6886 g.sb.writeln('if (_err.is_error) {')
6887 g.indent++
6888 g.write_indent()
6889 g.sb.writeln('IError err = _err.err;')
6890 g.write_indent()
6891 g.sb.writeln('return (_result_void){ .is_error=true, .err=err };')
6892 g.indent--
6893 g.write_indent()
6894 g.sb.writeln('}')
6895 g.write_indent()
6896 g.sb.writeln('return (_result_void){ .is_error=false };')
6897 return true
6898}
6899
6900fn (mut g Gen) gen_json2_cached_struct_field_infos_body(node &ast.FnDecl) bool {
6901 if g.cur_module != 'json2' || !node.is_method || node.name != 'cached_struct_field_infos' {
6902 return false
6903 }
6904 concrete := g.active_generic_types['T'] or { return false }
6905 mut fields := []types.Field{}
6906 if concrete is types.Struct {
6907 mut struct_type := concrete
6908 if struct_type.fields.len == 0 {
6909 struct_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
6910 full_struct_type := g.lookup_struct_type_by_c_name(struct_c_name)
6911 if full_struct_type.fields.len > 0 {
6912 struct_type = full_struct_type
6913 }
6914 }
6915 fields = struct_type.fields.clone()
6916 }
6917 g.write_indent()
6918 g.sb.writeln('Array_json2__StructFieldInfo field_infos = __new_array_with_default_noscan(0, ${fields.len}, sizeof(json2__StructFieldInfo), NULL);')
6919 for field in fields {
6920 json_name, is_skip_from_json := json2_field_json_name_and_skip(field)
6921 json_name_literal := c_string_literal_content_to_c(json_name)
6922 g.write_indent()
6923 g.sb.writeln('array__push((array*)&field_infos, &(json2__StructFieldInfo){.json_name_ptr = (void*)(${json_name_literal}), .json_name_len = ${json_name.len}, .is_omitempty = ${field_has_attr(field.attributes,
6924 'omitempty')}, .is_skip = ${field_has_attr(field.attributes, 'skip')
6925 || is_skip_from_json}, .is_required = ${field_has_attr(field.attributes, 'required')}, .is_raw = ${field_has_attr(field.attributes,
6926 'raw')}});')
6927 }
6928 g.write_indent()
6929 g.sb.writeln('return field_infos;')
6930 return true
6931}
6932
6933fn field_has_attr(attrs []ast.Attribute, name string) bool {
6934 for attr in attrs {
6935 if attr.name == name {
6936 return true
6937 }
6938 if attr.name == '' && attr.value.name().trim('\'"') == name {
6939 return true
6940 }
6941 }
6942 return false
6943}
6944
6945fn json2_field_json_name_and_skip(field types.Field) (string, bool) {
6946 for attr in field.attributes {
6947 if attr.name == 'json' && attr.value is ast.StringLiteral {
6948 value := attr.value.value.trim('\'"')
6949 if value == '-' {
6950 return field.name, true
6951 }
6952 if value != '' {
6953 return value, false
6954 }
6955 }
6956 }
6957 return field.name, false
6958}
6959
6960fn (mut g Gen) collect_returned_idents(stmts []ast.Stmt) map[string]bool {
6961 mut out := map[string]bool{}
6962 g.collect_returned_idents_from_stmts(stmts, mut out)
6963 return out
6964}
6965
6966fn (mut g Gen) collect_returned_idents_from_stmts(stmts []ast.Stmt, mut out map[string]bool) {
6967 for stmt in stmts {
6968 g.collect_returned_idents_from_stmt(stmt, mut out)
6969 }
6970}
6971
6972fn (mut g Gen) collect_returned_idents_from_stmt(stmt ast.Stmt, mut out map[string]bool) {
6973 match stmt {
6974 ast.ReturnStmt {
6975 if stmt.exprs.len == 1 {
6976 if name := returned_ident_name(stmt.exprs[0]) {
6977 out[name] = true
6978 }
6979 }
6980 }
6981 ast.BlockStmt {
6982 g.collect_returned_idents_from_stmts(stmt.stmts, mut out)
6983 }
6984 ast.ComptimeStmt {
6985 if stmt.stmt !is ast.EmptyStmt {
6986 g.collect_returned_idents_from_stmt(stmt.stmt, mut out)
6987 }
6988 }
6989 ast.DeferStmt {
6990 g.collect_returned_idents_from_stmts(stmt.stmts, mut out)
6991 }
6992 ast.ForStmt {
6993 if stmt.init !is ast.EmptyStmt {
6994 g.collect_returned_idents_from_stmt(stmt.init, mut out)
6995 }
6996 if stmt.post !is ast.EmptyStmt {
6997 g.collect_returned_idents_from_stmt(stmt.post, mut out)
6998 }
6999 g.collect_returned_idents_from_stmts(stmt.stmts, mut out)
7000 }
7001 ast.LabelStmt {
7002 if stmt.stmt !is ast.EmptyStmt {
7003 g.collect_returned_idents_from_stmt(stmt.stmt, mut out)
7004 }
7005 }
7006 ast.ExprStmt {
7007 g.collect_returned_idents_from_expr(stmt.expr, mut out)
7008 }
7009 else {}
7010 }
7011}
7012
7013fn (mut g Gen) collect_returned_idents_from_expr(expr ast.Expr, mut out map[string]bool) {
7014 match expr {
7015 ast.IfExpr {
7016 g.collect_returned_idents_from_stmts(expr.stmts, mut out)
7017 if expr.else_expr !is ast.EmptyExpr {
7018 g.collect_returned_idents_from_expr(expr.else_expr, mut out)
7019 }
7020 }
7021 ast.MatchExpr {
7022 for branch in expr.branches {
7023 g.collect_returned_idents_from_stmts(branch.stmts, mut out)
7024 }
7025 }
7026 ast.SelectExpr {
7027 g.collect_returned_idents_from_stmts(expr.stmts, mut out)
7028 if expr.next !is ast.EmptyExpr {
7029 g.collect_returned_idents_from_expr(expr.next, mut out)
7030 }
7031 }
7032 ast.UnsafeExpr {
7033 g.collect_returned_idents_from_stmts(expr.stmts, mut out)
7034 }
7035 ast.ParenExpr {
7036 g.collect_returned_idents_from_expr(expr.expr, mut out)
7037 }
7038 ast.ModifierExpr {
7039 g.collect_returned_idents_from_expr(expr.expr, mut out)
7040 }
7041 ast.CastExpr {
7042 g.collect_returned_idents_from_expr(expr.expr, mut out)
7043 }
7044 else {}
7045 }
7046}
7047
7048fn returned_ident_name(expr ast.Expr) ?string {
7049 match expr {
7050 ast.Ident {
7051 return expr.name
7052 }
7053 ast.ParenExpr {
7054 return returned_ident_name(expr.expr)
7055 }
7056 ast.ModifierExpr {
7057 return returned_ident_name(expr.expr)
7058 }
7059 ast.CastExpr {
7060 return returned_ident_name(expr.expr)
7061 }
7062 else {}
7063 }
7064
7065 return none
7066}
7067
7068fn (mut g Gen) gen_fn_head(node ast.FnDecl) {
7069 fn_name := g.get_fn_name(node)
7070 if fn_name == '' {
7071 return
7072 }
7073 g.gen_fn_head_with_name_ptr(&node, fn_name)
7074}
7075
7076fn (mut g Gen) gen_fn_head_with_name(node ast.FnDecl, fn_name string) {
7077 g.gen_fn_head_with_name_ptr(&node, fn_name)
7078}
7079
7080fn (mut g Gen) gen_fn_head_with_name_ptr(node &ast.FnDecl, fn_name string) {
7081 if g.should_skip_backend_generic_fn(*node) {
7082 return
7083 }
7084 g.declared_fn_names[fn_name] = true
7085 is_program_main := g.is_program_main_fn(node)
7086 mut ret := ''
7087 if fn_ret := g.fn_return_types[fn_name] {
7088 ret = fn_ret
7089 } else if node.typ.return_type !is ast.EmptyExpr {
7090 ret = g.expr_type_to_c(node.typ.return_type)
7091 } else {
7092 ret = 'void'
7093 }
7094 ret = normalize_signature_type_name(ret, 'void')
7095 mut c_ret := g.c_fn_return_type_from_v(ret)
7096 if ret.starts_with('Tuple_') {
7097 g.ensure_tuple_alias_for_fn_return(*node, ret)
7098 g.emit_tuple_aliases()
7099 }
7100 if is_program_main {
7101 ret = 'int'
7102 c_ret = 'int'
7103 }
7104 sig_param_types := g.fn_param_types[fn_name] or { []string{} }
7105
7106 // main takes argc/argv
7107 if is_program_main {
7108 g.sb.write_string(c_ret)
7109 g.sb.write_string(' ')
7110 g.sb.write_string(fn_name)
7111 g.sb.write_string('(int ___argc, char** ___argv)')
7112 return
7113 }
7114
7115 g.sb.write_string(c_ret)
7116 g.sb.write_string(' ')
7117 g.sb.write_string(fn_name)
7118 g.sb.write_string('(')
7119
7120 mut first := true
7121 mut sig_idx := 0
7122 // Receiver as first param for methods
7123 if node.is_method && node.receiver.name != '' {
7124 receiver_type := if sig_idx < sig_param_types.len {
7125 normalize_signature_type_name(sig_param_types[sig_idx], 'void*')
7126 } else if node.receiver.is_mut {
7127 rt := normalize_signature_type_name(g.decl_expr_type_to_c(node.receiver.typ), 'void*')
7128 if rt.ends_with('*') {
7129 rt
7130 } else {
7131 rt + '*'
7132 }
7133 } else {
7134 normalize_signature_type_name(g.decl_expr_type_to_c(node.receiver.typ), 'void*')
7135 }
7136 g.sb.write_string(receiver_type)
7137 g.sb.write_string(' ')
7138 g.sb.write_string(node.receiver.name)
7139 sig_idx++
7140 first = false
7141 }
7142 if g.needs_implicit_veb_ctx_param(node, fn_name) {
7143 if !first {
7144 g.sb.write_string(', ')
7145 }
7146 first = false
7147 g.sb.write_string(g.implicit_veb_ctx_c_type())
7148 g.sb.write_string(' ctx')
7149 sig_idx++
7150 }
7151
7152 for param_idx, param in node.typ.params {
7153 if !first {
7154 g.sb.write_string(', ')
7155 }
7156 first = false
7157 pname := c_fn_param_name(param.name, param_idx)
7158 if param.typ is ast.Type {
7159 if param.typ is ast.FnType {
7160 g.gen_fn_type_param_decl(param.typ as ast.FnType, pname)
7161 sig_idx++
7162 continue
7163 }
7164 }
7165 t := if sig_idx < sig_param_types.len {
7166 normalize_signature_type_name(sig_param_types[sig_idx], 'int')
7167 } else if param.is_mut {
7168 pt := normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7169 if pt.ends_with('*') {
7170 pt
7171 } else {
7172 pt + '*'
7173 }
7174 } else {
7175 normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7176 }
7177 if t.contains('(*)') {
7178 fn_ptr_decl := c_fn_pointer_type_with_name(t, pname)
7179 if fn_ptr_decl != '' {
7180 g.sb.write_string(fn_ptr_decl)
7181 } else {
7182 g.sb.write_string(t)
7183 g.sb.write_string(' ')
7184 g.sb.write_string(pname)
7185 }
7186 } else {
7187 g.sb.write_string(t)
7188 g.sb.write_string(' ')
7189 // Rename V variables that clash with C type names (matches expr.v Ident handler)
7190 g.sb.write_string(pname)
7191 }
7192 sig_idx++
7193 }
7194 g.sb.write_string(')')
7195}
7196
7197fn c_fn_pointer_type_with_name(fn_type string, name string) string {
7198 if !fn_type.contains('(*)') {
7199 return ''
7200 }
7201 return fn_type.replace_once('(*)', '(*${name})')
7202}
7203
7204fn (mut g Gen) fn_param_signature_c_type(param ast.Parameter) string {
7205 if param.typ is ast.Type {
7206 if param.typ is ast.FnType {
7207 return g.fn_type_c_type(param.typ as ast.FnType)
7208 }
7209 }
7210 return g.signature_expr_type_to_c(param.typ)
7211}
7212
7213fn (mut g Gen) fn_type_c_type(fn_type ast.FnType) string {
7214 mut ret_type := 'void'
7215 if fn_type.return_type !is ast.EmptyExpr {
7216 ret_type = g.expr_type_to_c(fn_type.return_type)
7217 }
7218 ret_type = normalize_signature_type_name(ret_type, 'void')
7219 ret_type = g.c_callback_return_type_from_v(ret_type)
7220 mut params := []string{cap: fn_type.params.len}
7221 for param in fn_type.params {
7222 mut param_type := normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7223 if param.is_mut && !param_type.ends_with('*') {
7224 param_type += '*'
7225 }
7226 params << param_type
7227 }
7228 if params.len == 0 {
7229 params << 'void'
7230 }
7231 return '${ret_type} (*)(${params.join(', ')})'
7232}
7233
7234fn (mut g Gen) gen_fn_type_param_decl(fn_type ast.FnType, name string) {
7235 decl := c_fn_pointer_type_with_name(g.fn_type_c_type(fn_type), name)
7236 if decl != '' {
7237 g.sb.write_string(decl)
7238 }
7239}
7240
7241// gen_c_extern_forward_decl emits a forward declaration for a C extern function.
7242// e.g., `fn C.macos_objc_msg_void1(obj Id, selector Sel, a0 voidptr)` emits:
7243// `void macos_objc_msg_void1(void* obj, void* selector, void* a0);`
7244fn (mut g Gen) gen_c_extern_forward_decl(node ast.FnDecl) {
7245 // Extract C function name: strip 'C.' prefix if present
7246 mut c_name := node.name
7247 if c_name.starts_with('C.') {
7248 c_name = c_name[2..]
7249 }
7250 if c_name == '' {
7251 return
7252 }
7253 // Skip C stdlib functions — they're already declared by system headers.
7254 if c_name in c_stdlib_fns || is_c_runtime_function(c_name) {
7255 return
7256 }
7257 // Only emit C extern forward declarations for user/third-party modules
7258 // (from .vmodules/ or the project directory). Skip all vlib modules since
7259 // they wrap system functions already declared by system headers.
7260 if !g.cur_file_name.contains('.vmodules/') && g.cur_module != 'main' {
7261 return
7262 }
7263 // Skip already-emitted declarations
7264 decl_key := 'c_extern_${c_name}'
7265 if decl_key in g.emitted_types {
7266 return
7267 }
7268 g.emitted_types[decl_key] = true
7269
7270 // Return type
7271 mut ret := if node.typ.return_type !is ast.EmptyExpr {
7272 g.expr_type_to_c(node.typ.return_type)
7273 } else {
7274 'void'
7275 }
7276 ret = normalize_signature_type_name(ret, 'void')
7277 c_ret := g.c_fn_return_type_from_v(ret)
7278
7279 g.sb.write_string(c_ret)
7280 g.sb.write_string(' ')
7281 g.sb.write_string(c_name)
7282 g.sb.write_string('(')
7283
7284 mut first := true
7285 for param_idx, param in node.typ.params {
7286 if !first {
7287 g.sb.write_string(', ')
7288 }
7289 first = false
7290 mut t := normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7291 if param.is_mut && !t.ends_with('*') {
7292 t = t + '*'
7293 }
7294 g.sb.write_string(t)
7295 if param.name != '' {
7296 pname := c_fn_param_name(param.name, param_idx)
7297 g.sb.write_string(' ')
7298 g.sb.write_string(pname)
7299 }
7300 }
7301 if first {
7302 g.sb.write_string('void')
7303 }
7304 g.sb.writeln(');')
7305}
7306
7307// gen_fn_head_live generates the function signature with `impl_live_` prefix for @[live]
7308// functions, and records the function info for generating wrappers and dlsym binding.
7309fn (mut g Gen) gen_fn_head_live(node ast.FnDecl, fn_name string) {
7310 g.gen_fn_head_live_ptr(&node, fn_name)
7311}
7312
7313// gen_fn_head_live_ptr mirrors gen_fn_head_live without copying ast.FnDecl in pass 5.
7314fn (mut g Gen) gen_fn_head_live_ptr(node &ast.FnDecl, fn_name string) {
7315 sig_param_types := g.fn_param_types[fn_name] or { []string{} }
7316
7317 mut c_ret := g.cur_fn_c_ret_type
7318 is_void := c_ret == 'void'
7319
7320 // Build parameter list string and arg forwarding string
7321 mut params_parts := []string{}
7322 mut args_parts := []string{}
7323 mut sig_idx := 0
7324
7325 // Receiver as first param for methods
7326 if node.is_method && node.receiver.name != '' {
7327 receiver_type := if sig_idx < sig_param_types.len {
7328 normalize_signature_type_name(sig_param_types[sig_idx], 'void*')
7329 } else if node.receiver.is_mut {
7330 rt := normalize_signature_type_name(g.decl_expr_type_to_c(node.receiver.typ), 'void*')
7331 if rt.ends_with('*') {
7332 rt
7333 } else {
7334 rt + '*'
7335 }
7336 } else {
7337 normalize_signature_type_name(g.decl_expr_type_to_c(node.receiver.typ), 'void*')
7338 }
7339 params_parts << '${receiver_type} ${node.receiver.name}'
7340 args_parts << node.receiver.name
7341 sig_idx++
7342 }
7343 if g.needs_implicit_veb_ctx_param(node, fn_name) {
7344 params_parts << '${g.implicit_veb_ctx_c_type()} ctx'
7345 args_parts << 'ctx'
7346 sig_idx++
7347 }
7348
7349 for param_idx, param in node.typ.params {
7350 t := if sig_idx < sig_param_types.len {
7351 normalize_signature_type_name(sig_param_types[sig_idx], 'int')
7352 } else if param.is_mut {
7353 pt := normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7354 if pt.ends_with('*') {
7355 pt
7356 } else {
7357 pt + '*'
7358 }
7359 } else {
7360 normalize_signature_type_name(g.expr_type_to_c(param.typ), 'int')
7361 }
7362 pname := c_fn_param_name(param.name, param_idx)
7363 params_parts << '${t} ${pname}'
7364 args_parts << pname
7365 sig_idx++
7366 }
7367
7368 params_str := params_parts.join(', ')
7369 args_str := args_parts.join(', ')
7370
7371 // Record live function info for later wrapper generation
7372 g.live_fns << LiveFnInfo{
7373 c_name: fn_name
7374 ret_type: c_ret
7375 params: params_str
7376 args: args_str
7377 is_void: is_void
7378 }
7379 if g.cur_file_name.len > 0 {
7380 g.live_source_file = os.real_path(g.cur_file_name)
7381 }
7382
7383 // Write function signature: ret_type impl_live_FUNC(params)
7384 // Mark with visibility("default") so it's exported from shared libraries
7385 // even when -fvisibility=hidden is used.
7386 g.sb.write_string('__attribute__((visibility("default"))) ')
7387 g.sb.write_string(c_ret)
7388 g.sb.write_string(' impl_live_')
7389 g.sb.write_string(fn_name)
7390 g.sb.write_string('(')
7391 g.sb.write_string(params_str)
7392 g.sb.write_string(')')
7393}
7394
7395fn (mut g Gen) get_fn_name(node ast.FnDecl) string {
7396 // Check for @[export: 'name'] attribute — use the export name as the C symbol.
7397 for attr in node.attributes {
7398 if attr.name == 'export' {
7399 if attr.value is ast.StringLiteral {
7400 export_name := attr.value.value.trim('\'"')
7401 if export_name.len > 0 {
7402 return export_name
7403 }
7404 }
7405 }
7406 }
7407 if g.is_program_main_fn(&node) {
7408 return 'main'
7409 }
7410 // Prevent collisions with libc symbols from builtin wrappers.
7411 if !node.is_method && (node.name in c_stdlib_fns || is_c_runtime_function(node.name))
7412 && (g.cur_module == '' || g.cur_module == 'main' || g.cur_module == 'builtin') {
7413 return ''
7414 }
7415 name := sanitize_fn_ident(node.name)
7416 // Methods: ReceiverType__method_name
7417 if node.is_method && node.receiver.name != '' {
7418 // For pointer alias types (byteptr, charptr), use the V name directly
7419 // to avoid collisions. e.g., byteptr.str() -> byteptr__str, not u8__str
7420 // which would conflict with the actual u8.str() method.
7421 if node.receiver.typ is ast.Ident && node.receiver.typ.name in ['byteptr', 'charptr'] {
7422 return node.receiver.typ.name + '__' + name
7423 }
7424 receiver_type := if concrete_receiver := g.receiver_generic_instance_c_name_for_active_bindings(node) {
7425 concrete_receiver
7426 } else {
7427 g.decl_expr_type_to_c(node.receiver.typ)
7428 }
7429 // Strip pointer suffix and 'struct ' prefix for method naming.
7430 // C struct types like 'struct sg_pipeline' must become 'sg_pipeline'
7431 // in function names to produce valid C identifiers.
7432 mut base_type := if receiver_type.ends_with('*') {
7433 receiver_type[..receiver_type.len - 1]
7434 } else {
7435 receiver_type
7436 }
7437 if base_type.starts_with('struct ') {
7438 base_type = base_type[7..]
7439 }
7440 recv_scope_name := g.receiver_type_to_scope_name(node.receiver.typ)
7441 if recv_scope_name != '' && base_type == recv_scope_name
7442 && g.is_module_local_type(base_type) {
7443 base_type = '${g.cur_module}__${base_type}'
7444 }
7445 if g.cur_module != 'builtin' && base_type in primitive_types {
7446 // Non-builtin primitive receiver methods are typically accidental generic
7447 // instantiations (e.g. int__multiply) that produce invalid scalar field access.
7448 return ''
7449 }
7450 method_name := if base_type == 'string' { legacy_operator_fn_ident(node.name) } else { name }
7451 return base_type + '__' + method_name
7452 }
7453 // Static methods: Type.method() -> Type__method
7454 if node.is_static {
7455 receiver_type := g.decl_expr_type_to_c(node.receiver.typ)
7456 mut base_type := if receiver_type.ends_with('*') {
7457 receiver_type[..receiver_type.len - 1]
7458 } else {
7459 receiver_type
7460 }
7461 if base_type.starts_with('struct ') {
7462 base_type = base_type[7..]
7463 }
7464 return base_type + '__' + name
7465 }
7466 if is_generated_global_helper_name(name) {
7467 return name
7468 }
7469 if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
7470 // Don't add module prefix if name already starts with it (e.g., generated
7471 // enum str functions placed back in their source module).
7472 if name.starts_with(g.cur_module + '__') {
7473 return name
7474 }
7475 fn_module := module_prefix_from_fn_name(name)
7476 if fn_module.len > 0 && fn_module[0] >= `a` && fn_module[0] <= `z`
7477 && g.source_module_exists(fn_module) {
7478 return name
7479 }
7480 return g.cur_module + '__' + name
7481 }
7482 // Rename builtin `panic` to avoid conflict with macOS mach/mach.h's
7483 // `extern void panic(const char *, ...)`.
7484 if name == 'panic' {
7485 return 'v_panic'
7486 }
7487 return name
7488}
7489
7490fn is_generated_global_helper_name(name string) bool {
7491 return name == 'map_map_eq' || name.starts_with('__sort_cmp_') || name.starts_with('Array_')
7492 || name.starts_with('Map_')
7493}
7494
7495fn (g &Gen) is_program_main_fn(node &ast.FnDecl) bool {
7496 return !node.is_method && !node.is_static && node.name == 'main'
7497 && (g.cur_module == '' || g.cur_module == 'main')
7498}
7499
7500fn c_fn_param_name(name string, idx int) string {
7501 if name == '' || name == '_' {
7502 return '_arg${idx}'
7503 }
7504 return c_local_name(name)
7505}
7506
7507fn (g &Gen) contains_call_expr(e ast.Expr) bool {
7508 if e is ast.CallExpr {
7509 return true
7510 }
7511 if e is ast.CallOrCastExpr {
7512 return true
7513 }
7514 if e is ast.CastExpr {
7515 return g.contains_call_expr(e.expr)
7516 }
7517 if e is ast.ParenExpr {
7518 return g.contains_call_expr(e.expr)
7519 }
7520 if e is ast.ArrayInitExpr {
7521 for elem in e.exprs {
7522 if g.contains_call_expr(elem) {
7523 return true
7524 }
7525 }
7526 }
7527 return false
7528}
7529
7530fn (g Gen) get_fn_type_from_expr(e ast.Expr) ?ast.FnType {
7531 if e is ast.Type {
7532 if e is ast.FnType {
7533 return e
7534 }
7535 }
7536 return none
7537}
7538
7539// Needed for self
7540fn (mut g Gen) expr_is_pointer(arg ast.Expr) bool {
7541 match arg {
7542 ast.ModifierExpr {
7543 return g.expr_is_pointer(arg.expr)
7544 }
7545 ast.ParenExpr {
7546 return g.expr_is_pointer(arg.expr)
7547 }
7548 ast.CastExpr {
7549 // The result type of a cast is determined by the target type, not the source.
7550 // e.g. u8(ptr) produces u8, not a pointer, even though ptr is a pointer.
7551 target_type := g.expr_type_to_c(arg.typ)
7552 if target_type.ends_with('*') {
7553 return true
7554 }
7555 // For C typedef struct casts like C.log__Logger(ptr), if the source is a pointer,
7556 // the result is also a pointer (type pun cast). The cast doesn't unwrap the pointer.
7557 if target_type.contains('__') && g.expr_is_pointer(arg.expr) {
7558 // Sum-type casts construct a sum value from the source, they are not
7559 // pointer-preserving aliases.
7560 if target_type in g.sum_type_variants {
7561 return false
7562 }
7563 return true
7564 }
7565 return false
7566 }
7567 ast.CallOrCastExpr {
7568 // When the LHS is a type (not a function), this is a type cast.
7569 // In V, C.Type(ptr) where ptr is a pointer means "treat as Type*",
7570 // so the result is still a pointer.
7571 if g.call_or_cast_lhs_is_type(arg.lhs) {
7572 return g.expr_is_pointer(arg.expr)
7573 }
7574 }
7575 ast.Ident {
7576 if arg.name == 'nil' {
7577 return true
7578 }
7579 // Prefer function-scope type information for identifiers.
7580 // Environment lookups can over-approximate loop variables as pointers.
7581 if local_type := g.get_local_var_c_type(arg.name) {
7582 if arg.name in g.cur_fn_mut_params {
7583 return true
7584 }
7585 return local_type.ends_with('*')
7586 || local_type in ['voidptr', 'charptr', 'byteptr', 'chan']
7587 }
7588 if arg.name in g.cur_fn_mut_params {
7589 return true
7590 }
7591 }
7592 ast.PrefixExpr {
7593 if arg.op == .amp {
7594 return true
7595 }
7596 if arg.op == .mul {
7597 arg_type := g.get_expr_type(ast.Expr(arg))
7598 return arg_type.ends_with('*')
7599 || arg_type in ['void*', 'char*', 'u8*', 'byteptr', 'charptr', 'voidptr', 'chan']
7600 }
7601 return false
7602 }
7603 ast.CallExpr {
7604 elem_type := g.infer_array_method_elem_type(ast.Expr(arg))
7605 if elem_type != '' {
7606 return is_type_name_pointer_like(elem_type)
7607 || elem_type in ['void*', 'char*', 'u8*', 'byteptr', 'charptr', 'voidptr', 'chan']
7608 }
7609 }
7610 ast.SelectorExpr {
7611 if arg.lhs is ast.Ident && arg.lhs.name == 'C'
7612 && arg.rhs.name in ['stdin', 'stdout', 'stderr', 'environ'] {
7613 return true
7614 }
7615 if arg.rhs.name == 'data' {
7616 lhs_type := g.get_expr_type(arg.lhs)
7617 if lhs_type == 'array' || lhs_type.starts_with('Array_') || lhs_type == 'map'
7618 || lhs_type.starts_with('Map_') || lhs_type == 'string'
7619 || lhs_type.starts_with('strings__Builder') {
7620 return true
7621 }
7622 }
7623 field_type := g.selector_field_type(arg)
7624 if field_type != '' {
7625 return field_type.ends_with('*')
7626 || field_type in ['void*', 'char*', 'u8*', 'byteptr', 'charptr', 'voidptr', 'chan']
7627 }
7628 }
7629 ast.UnsafeExpr {
7630 if arg.stmts.len > 0 {
7631 last := arg.stmts[arg.stmts.len - 1]
7632 if last is ast.ExprStmt {
7633 return g.expr_produces_pointer(last.expr)
7634 }
7635 }
7636 }
7637 else {}
7638 }
7639
7640 if raw_type := g.get_raw_type(arg) {
7641 if raw_type is types.Pointer || raw_type is types.Nil {
7642 return true
7643 }
7644 // Handle type aliases that resolve to pointers (e.g., voidptr, charptr, byteptr)
7645 if raw_type is types.Alias {
7646 if raw_type.base_type is types.Pointer {
7647 return true
7648 }
7649 }
7650 }
7651 return g.get_expr_type(arg).ends_with('*')
7652}
7653
7654fn (mut g Gen) expr_produces_pointer(arg ast.Expr) bool {
7655 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
7656 if g.expr_is_pointer(base_arg) {
7657 return true
7658 }
7659 match base_arg {
7660 ast.CastExpr {
7661 return g.is_pointer_type(base_arg.typ)
7662 }
7663 ast.InfixExpr {
7664 if base_arg.op == .plus || base_arg.op == .minus {
7665 return g.expr_produces_pointer(base_arg.lhs)
7666 || g.expr_produces_pointer(base_arg.rhs)
7667 }
7668 }
7669 ast.ParenExpr {
7670 return g.expr_produces_pointer(base_arg.expr)
7671 }
7672 ast.UnsafeExpr {
7673 if base_arg.stmts.len > 0 {
7674 last := base_arg.stmts[base_arg.stmts.len - 1]
7675 if last is ast.ExprStmt {
7676 return g.expr_produces_pointer(last.expr)
7677 }
7678 }
7679 }
7680 else {}
7681 }
7682
7683 return false
7684}
7685
7686// write_method_recv_addr emits a pointer to `recv` for a method call that wants
7687// `&Receiver`. When `recv` is a struct rvalue (e.g. a function call result),
7688// `&` cannot be taken directly; bind the value to a temporary first.
7689fn (mut g Gen) write_method_recv_addr(recv ast.Expr) {
7690 base := if recv is ast.ParenExpr { recv.expr } else { recv }
7691 is_rvalue := base is ast.CallExpr || base is ast.CallOrCastExpr || base is ast.AsCastExpr
7692 if is_rvalue {
7693 mut recv_type := g.get_expr_type(recv).trim_right('*')
7694 if recv_type == '' || recv_type == 'int' {
7695 recv_type = ''
7696 }
7697 if recv_type != '' {
7698 g.tmp_counter++
7699 tmp := '_recv${g.tmp_counter}'
7700 g.sb.write_string('({ ${recv_type} ${tmp} = ')
7701 g.expr(recv)
7702 g.sb.write_string('; &${tmp}; })')
7703 return
7704 }
7705 }
7706 g.sb.write_string('&(')
7707 g.expr(recv)
7708 g.sb.write_string(')')
7709}
7710
7711fn (mut g Gen) can_take_address(arg ast.Expr) bool {
7712 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
7713 match base_arg {
7714 ast.Ident {
7715 if _ := g.get_local_var_c_type(base_arg.name) {
7716 return true
7717 }
7718 return g.enum_member_ident_type(base_arg.name) == none
7719 }
7720 ast.IndexExpr {
7721 if g.index_expr_is_map_lookup(base_arg) {
7722 return false
7723 }
7724 return true
7725 }
7726 ast.ParenExpr {
7727 return g.can_take_address(base_arg.expr)
7728 }
7729 ast.SelectorExpr {
7730 // Enum values / module symbols / type-qualified selectors are rvalues.
7731 if base_arg.lhs is ast.EmptyExpr {
7732 return false
7733 }
7734 if base_arg.lhs is ast.Ident {
7735 if g.is_module_ident(base_arg.lhs.name) || g.is_type_name(base_arg.lhs.name) {
7736 return false
7737 }
7738 }
7739 if raw_type := g.get_raw_type(base_arg) {
7740 if raw_type is types.Enum {
7741 return false
7742 }
7743 }
7744 return g.selector_lhs_can_take_field_address(base_arg.lhs)
7745 }
7746 else {
7747 return false
7748 }
7749 }
7750}
7751
7752fn (mut g Gen) index_expr_is_map_lookup(expr ast.IndexExpr) bool {
7753 if lhs_type := g.get_raw_type(expr.lhs) {
7754 match lhs_type {
7755 types.Map {
7756 return true
7757 }
7758 types.Pointer {
7759 return lhs_type.base_type is types.Map
7760 }
7761 types.Alias {
7762 return lhs_type.base_type is types.Map
7763 }
7764 else {}
7765 }
7766 }
7767 lhs_c_type := g.get_expr_type(expr.lhs).trim_space().trim_right('*')
7768 return lhs_c_type == 'map' || lhs_c_type.starts_with('Map_')
7769}
7770
7771fn (mut g Gen) selector_lhs_can_take_field_address(lhs ast.Expr) bool {
7772 base_lhs := if lhs is ast.ModifierExpr { lhs.expr } else { lhs }
7773 if g.expr_is_pointer(base_lhs) || g.expr_produces_pointer(base_lhs) {
7774 return true
7775 }
7776 match base_lhs {
7777 ast.ParenExpr {
7778 return g.selector_lhs_can_take_field_address(base_lhs.expr)
7779 }
7780 ast.PrefixExpr {
7781 if base_lhs.op == .mul {
7782 return g.expr_is_pointer(base_lhs.expr) || g.expr_produces_pointer(base_lhs.expr)
7783 }
7784 }
7785 ast.InitExpr, ast.ArrayInitExpr {
7786 return true
7787 }
7788 else {}
7789 }
7790
7791 return g.can_take_address(base_lhs)
7792}
7793
7794fn (g &Gen) enum_member_ident_type(name string) ?string {
7795 if name == '' {
7796 return none
7797 }
7798 if enum_name := g.enum_value_to_enum[name] {
7799 return enum_name
7800 }
7801 for enum_name, fields in g.enum_type_fields {
7802 for field_name, _ in fields {
7803 if name == g.enum_member_c_name(enum_name, field_name) {
7804 return g.normalize_enum_name(enum_name)
7805 }
7806 }
7807 }
7808 return none
7809}
7810
7811// is_simple_addressable returns true if the expression is cheap to re-evaluate
7812// (e.g., an identifier or field access), unlike a function call which has side effects
7813// and generates significant code.
7814fn (g &Gen) is_simple_addressable(expr ast.Expr) bool {
7815 if expr is ast.Ident {
7816 return true
7817 }
7818 if expr is ast.SelectorExpr {
7819 return g.is_simple_addressable(expr.lhs)
7820 }
7821 if expr is ast.ParenExpr {
7822 return g.is_simple_addressable(expr.expr)
7823 }
7824 return false
7825}
7826
7827fn (mut g Gen) gen_addr_of_expr(arg ast.Expr, typ string) {
7828 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
7829 // nil is a null pointer — emit NULL directly, never wrap in compound literal
7830 if is_nil_like_expr(base_arg) {
7831 g.sb.write_string('NULL')
7832 return
7833 }
7834 if typ != '' && !typ.ends_with('*') && base_arg is ast.ArrayInitExpr && base_arg.exprs.len == 1 {
7835 elem_expr := base_arg.exprs[0]
7836 mut elem_type := g.get_expr_type(elem_expr).trim_space()
7837 if (elem_type == '' || elem_type == 'int') && elem_expr is ast.Ident {
7838 elem_type = (g.get_local_var_c_type(elem_expr.name) or { '' }).trim_space()
7839 }
7840 if elem_type.ends_with('*') && elem_type.trim_right('*') == typ {
7841 g.expr(elem_expr)
7842 return
7843 }
7844 }
7845 if typ != '' && !typ.ends_with('*') {
7846 mut arg_type := g.get_expr_type(base_arg).trim_space()
7847 if (arg_type == '' || arg_type == 'int') && base_arg is ast.Ident {
7848 arg_type = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
7849 }
7850 if base_arg !is ast.ArrayInitExpr && arg_type.ends_with('*')
7851 && arg_type.trim_right('*') == typ {
7852 g.expr(base_arg)
7853 return
7854 }
7855 }
7856 if g.can_take_address(base_arg) {
7857 g.sb.write_string('&')
7858 g.expr(base_arg)
7859 return
7860 }
7861 // StringLiteral already generates a compound literal string with `.is_lit = 1`
7862 // so just take its address directly to avoid double-wrapping
7863 if base_arg is ast.StringLiteral {
7864 g.sb.write_string('&')
7865 g.expr(base_arg)
7866 return
7867 }
7868 // InitExpr: check if it generates a compound literal or a function call
7869 if base_arg is ast.InitExpr {
7870 init_type := g.expr_type_to_c(base_arg.typ)
7871 if init_type.starts_with('Map_') || init_type == 'map' {
7872 // Map init lowers to a function call; wrap it in an array compound literal
7873 // so the produced pointer stays valid for the whole surrounding block.
7874 g.sb.write_string('&((${init_type}[1]){')
7875 g.expr(base_arg)
7876 g.sb.write_string('}[0])')
7877 return
7878 }
7879 // Non-map InitExpr generates a compound literal - just take address
7880 g.sb.write_string('&')
7881 g.expr(base_arg)
7882 return
7883 }
7884 // ArrayInitExpr already generates a compound literal
7885 if base_arg is ast.ArrayInitExpr {
7886 g.sb.write_string('&')
7887 g.expr(base_arg)
7888 return
7889 }
7890 raw_addr := if typ == '' { 'int' } else { typ }
7891 addr_type := if raw_addr.ends_with('*')
7892 && (raw_addr.starts_with('Array_') || raw_addr.starts_with('Map_')) {
7893 mangle_alias_component(raw_addr)
7894 } else {
7895 unmangle_c_ptr_type(raw_addr)
7896 }
7897 // For non-primitive aggregate values (array/map/string/struct/option/result),
7898 // use a 1-element array compound literal and take the first element's address.
7899 // This avoids statement-expression temporaries whose address can escape scope.
7900 is_nonprimitive_value := addr_type !in primitive_types && !addr_type.ends_with('*')
7901 if addr_type in ['array', 'map', 'string'] || addr_type.starts_with('Array_')
7902 || addr_type.starts_with('Map_') || addr_type.starts_with('_option_')
7903 || addr_type.starts_with('_result_') || is_nonprimitive_value {
7904 g.sb.write_string('&((${addr_type}[1]){')
7905 g.expr(base_arg)
7906 g.sb.write_string('}[0])')
7907 return
7908 }
7909 g.sb.write_string('&(${addr_type}){')
7910 g.expr(base_arg)
7911 g.sb.write_string('}')
7912}
7913
7914fn (mut g Gen) fn_pointer_return_type(expr ast.Expr) string {
7915 if expr is ast.Ident {
7916 ret := g.local_fn_pointer_return_type(expr.name)
7917 if ret != '' {
7918 return ret
7919 }
7920 if decl_type := g.get_runtime_decl_type(expr.name) {
7921 if raw_type := g.lookup_type_by_c_name(decl_type) {
7922 return g.fn_pointer_return_type_from_raw(raw_type)
7923 }
7924 }
7925 if cur_type := g.runtime_local_types[expr.name] {
7926 if raw_type := g.lookup_type_by_c_name(cur_type) {
7927 return g.fn_pointer_return_type_from_raw(raw_type)
7928 }
7929 }
7930 if raw_type := g.get_raw_type(expr) {
7931 return g.fn_pointer_return_type_from_raw(raw_type)
7932 }
7933 return ''
7934 }
7935 if expr is ast.SelectorExpr {
7936 if raw_type := g.selector_fn_pointer_raw_type(expr) {
7937 return g.fn_pointer_return_type_from_raw(raw_type)
7938 }
7939 }
7940 if raw_type := g.get_raw_type(expr) {
7941 return g.fn_pointer_return_type_from_raw(raw_type)
7942 }
7943 return ''
7944}
7945
7946fn (mut g Gen) local_fn_pointer_raw_type(name string) ?types.Type {
7947 if name == '' {
7948 return none
7949 }
7950 if placeholder := g.cur_fn_generic_params[name] {
7951 if concrete := g.active_generic_types[placeholder] {
7952 if _ := extract_fn_type(concrete) {
7953 return concrete
7954 }
7955 }
7956 }
7957 if raw_type := g.runtime_fn_pointer_types[name] {
7958 return raw_type
7959 }
7960 if decl_type := g.get_runtime_decl_type(name) {
7961 if raw_type := g.lookup_type_by_c_name(decl_type) {
7962 if _ := extract_fn_type(raw_type) {
7963 return raw_type
7964 }
7965 }
7966 }
7967 if cur_type := g.runtime_local_types[name] {
7968 if raw_type := g.lookup_type_by_c_name(cur_type) {
7969 if _ := extract_fn_type(raw_type) {
7970 return raw_type
7971 }
7972 }
7973 }
7974 if mut fn_scope := g.ensure_cur_fn_scope() {
7975 if found_scope, obj := fn_scope.lookup_parent_with_scope(name, 0) {
7976 if g.cur_module != '' {
7977 if module_scope := g.env_scope(g.cur_module) {
7978 if types.same_scope_ptr(found_scope, module_scope) {
7979 return none
7980 }
7981 }
7982 }
7983 if g.cur_module != 'builtin' {
7984 if builtin_scope := g.env_scope('builtin') {
7985 if types.same_scope_ptr(found_scope, builtin_scope) {
7986 return none
7987 }
7988 }
7989 }
7990 if obj is types.Module || obj is types.Const || obj is types.Global || obj is types.Fn {
7991 return none
7992 }
7993 raw_type := obj.typ()
7994 if _ := extract_fn_type(raw_type) {
7995 return raw_type
7996 }
7997 }
7998 }
7999 return none
8000}
8001
8002fn (mut g Gen) selector_fn_pointer_raw_type(expr ast.SelectorExpr) ?types.Type {
8003 lhs_struct_name := g.selector_struct_name(expr.lhs)
8004 if lhs_struct_name != '' {
8005 if field_type := g.lookup_struct_field_type_by_name(lhs_struct_name, expr.rhs.name) {
8006 if raw_type := g.lookup_fn_pointer_type_by_c_name(field_type) {
8007 return raw_type
8008 }
8009 }
8010 }
8011 if raw_type := g.get_raw_type(expr) {
8012 return raw_type
8013 }
8014 field_type := g.selector_field_type(expr).trim_space()
8015 return g.lookup_fn_pointer_type_by_c_name(field_type)
8016}
8017
8018fn (mut g Gen) lookup_fn_pointer_type_by_c_name(field_type string) ?types.Type {
8019 clean_type := strip_pointer_type_name(field_type.trim_space())
8020 if clean_type == '' || clean_type == 'int' || clean_type == 'void' {
8021 return none
8022 }
8023 raw_type := g.lookup_type_by_c_name(clean_type) or { return none }
8024 if g.fn_pointer_return_type_from_raw(raw_type) == '' {
8025 return none
8026 }
8027 return raw_type
8028}
8029
8030fn (mut g Gen) local_fn_pointer_return_type(name string) string {
8031 if local_type := g.runtime_local_types[name] {
8032 if local_type.contains('(*)') {
8033 return c_fn_pointer_return_type_from_type(local_type)
8034 }
8035 }
8036 raw_type := g.local_fn_pointer_raw_type(name) or { return '' }
8037 return g.fn_pointer_return_type_from_raw(raw_type)
8038}
8039
8040fn c_fn_pointer_return_type_from_type(fn_type string) string {
8041 if !fn_type.contains('(*)') {
8042 return ''
8043 }
8044 ret := fn_type.all_before('(*)').trim_space()
8045 if ret == '' {
8046 return 'void'
8047 }
8048 return ret
8049}
8050
8051fn (mut g Gen) fn_pointer_return_type_from_raw(raw_type types.Type) string {
8052 match raw_type {
8053 types.FnType {
8054 if rt := raw_type.get_return_type() {
8055 return g.fn_return_type_to_c(rt)
8056 }
8057 return 'void'
8058 }
8059 types.Alias {
8060 if raw_type.base_type is types.FnType {
8061 if rt := raw_type.base_type.get_return_type() {
8062 return g.fn_return_type_to_c(rt)
8063 }
8064 return 'void'
8065 }
8066 }
8067 types.Pointer {
8068 if raw_type.base_type is types.FnType {
8069 if rt := raw_type.base_type.get_return_type() {
8070 return g.fn_return_type_to_c(rt)
8071 }
8072 return 'void'
8073 }
8074 if raw_type.base_type is types.Alias && raw_type.base_type.base_type is types.FnType {
8075 if rt := raw_type.base_type.base_type.get_return_type() {
8076 return g.fn_return_type_to_c(rt)
8077 }
8078 return 'void'
8079 }
8080 }
8081 else {}
8082 }
8083
8084 return ''
8085}
8086
8087// fn_return_type_to_c converts a function return type to its C representation,
8088// handling Tuple types by registering the appropriate tuple alias.
8089fn (mut g Gen) fn_return_type_to_c(t types.Type) string {
8090 if t is types.Tuple {
8091 elem_types := t.get_types()
8092 mut c_types := []string{cap: elem_types.len}
8093 for et in elem_types {
8094 c_types << g.types_type_to_c(et)
8095 }
8096 return g.register_tuple_alias(c_types)
8097 }
8098 return g.types_type_to_c(t)
8099}
8100
8101fn (mut g Gen) is_fn_pointer_expr(expr ast.Expr) bool {
8102 return g.fn_pointer_return_type(expr) != ''
8103}
8104
8105fn (mut g Gen) selector_resolves_to_method_call(lhs ast.SelectorExpr) bool {
8106 if lhs.lhs is ast.Ident && (g.is_type_name(lhs.lhs.name) || g.is_module_ident(lhs.lhs.name)) {
8107 return true
8108 }
8109 method_name := sanitize_fn_ident(lhs.rhs.name)
8110 if receiver_type := g.get_receiver_expr_type_for_method(lhs.lhs) {
8111 if _ := g.resolve_method_on_concrete_type(receiver_type, method_name) {
8112 return true
8113 }
8114 }
8115 if _ := g.resolve_method_on_declared_receiver_type(lhs.lhs, method_name) {
8116 return true
8117 }
8118 mut base_type := g.method_receiver_base_type(lhs.lhs)
8119 if base_type == '' {
8120 base_type = g.get_expr_type(lhs.lhs).trim_space().trim_right('*')
8121 }
8122 if base_type == '' {
8123 base_type = g.receiver_local_base_type(lhs.lhs)
8124 }
8125 if base_type != '' {
8126 if _ := g.resolve_method_on_concrete_type(base_type, method_name) {
8127 return true
8128 }
8129 if _ := g.resolve_method_on_embedded_receiver(base_type, method_name) {
8130 return true
8131 }
8132 }
8133 return false
8134}
8135
8136fn (mut g Gen) receiver_local_base_type(expr ast.Expr) string {
8137 unwrapped := strip_expr_wrappers(expr)
8138 if unwrapped is ast.Ident {
8139 return (g.get_local_var_c_type(unwrapped.name) or { '' }).trim_space().trim_right('*')
8140 }
8141 return ''
8142}
8143
8144fn (mut g Gen) try_emit_resolved_selector_method_call(lhs ast.SelectorExpr, args []ast.Expr) bool {
8145 if lhs.lhs is ast.Ident && (g.is_type_name(lhs.lhs.name) || g.is_module_ident(lhs.lhs.name)) {
8146 return false
8147 }
8148 method_name := sanitize_fn_ident(lhs.rhs.name)
8149 mut name := ''
8150 mut embedded_owner := ''
8151 mut base_type := g.method_receiver_base_type(lhs.lhs)
8152 if base_type == '' {
8153 base_type = g.get_expr_type(lhs.lhs).trim_space().trim_right('*')
8154 }
8155 if base_type == '' {
8156 base_type = g.receiver_local_base_type(lhs.lhs)
8157 }
8158 if receiver_type := g.get_receiver_expr_type_for_method(lhs.lhs) {
8159 if concrete_method := g.resolve_method_on_concrete_type(receiver_type, method_name) {
8160 name = concrete_method
8161 }
8162 }
8163 if name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types) {
8164 if declared_method := g.resolve_method_on_declared_receiver_type(lhs.lhs, method_name) {
8165 name = declared_method
8166 }
8167 }
8168 if (name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types)) && base_type != '' {
8169 if concrete_method := g.resolve_method_on_concrete_type(base_type, method_name) {
8170 name = concrete_method
8171 }
8172 }
8173 if (name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types)) && base_type != '' {
8174 if embedded := g.resolve_method_on_embedded_receiver(base_type, method_name) {
8175 name = embedded.method_c_name
8176 embedded_owner = embedded.owner
8177 }
8178 }
8179 if name == '' {
8180 return false
8181 }
8182 mut call_args := []ast.Expr{cap: args.len + 1}
8183 call_args << lhs.lhs
8184 call_args << args
8185 if name !in g.fn_param_is_ptr && name !in g.fn_return_types {
8186 return false
8187 }
8188 g.mark_called_fn_name(name)
8189 g.sb.write_string('${name}(')
8190 if embedded_owner != '' {
8191 g.gen_embedded_receiver_arg(lhs.lhs, embedded_owner, name)
8192 } else {
8193 g.gen_call_arg(name, 0, lhs.lhs)
8194 }
8195 for i, arg in args {
8196 g.sb.write_string(', ')
8197 g.gen_call_arg(name, i + 1, arg)
8198 }
8199 g.sb.write_string(')')
8200 return true
8201}
8202
8203fn extract_fn_type(raw_type types.Type) ?types.FnType {
8204 match raw_type {
8205 types.FnType {
8206 return raw_type
8207 }
8208 types.Alias {
8209 if raw_type.base_type is types.FnType {
8210 return raw_type.base_type as types.FnType
8211 }
8212 }
8213 types.Pointer {
8214 if raw_type.base_type is types.FnType {
8215 return raw_type.base_type as types.FnType
8216 }
8217 if raw_type.base_type is types.Alias && raw_type.base_type.base_type is types.FnType {
8218 return raw_type.base_type.base_type as types.FnType
8219 }
8220 }
8221 else {}
8222 }
8223
8224 return none
8225}
8226
8227// fn_pointer_param_is_ptr extracts parameter pointer-ness from a fn-pointer type.
8228// Returns an array of bools (true = param expects pointer, false = by value).
8229fn (mut g Gen) fn_pointer_param_is_ptr(expr ast.Expr) []bool {
8230 if expr is ast.Ident {
8231 if local_type := g.runtime_local_types[expr.name] {
8232 if local_type.contains('(*)') {
8233 return c_fn_pointer_param_is_ptr_from_type(local_type)
8234 }
8235 }
8236 }
8237 raw_type := if expr is ast.SelectorExpr {
8238 g.selector_fn_pointer_raw_type(expr) or { return []bool{} }
8239 } else if expr is ast.Ident {
8240 g.local_fn_pointer_raw_type(expr.name) or { g.get_raw_type(expr) or { return []bool{} } }
8241 } else {
8242 g.get_raw_type(expr) or { return []bool{} }
8243 }
8244 fn_type := extract_fn_type(raw_type) or { return []bool{} }
8245 param_types := fn_type.get_param_types()
8246 mut result := []bool{cap: param_types.len}
8247 for pt in param_types {
8248 c_name := g.types_type_to_c(pt)
8249 result << (c_name.ends_with('*') || pt is types.Pointer)
8250 }
8251 return result
8252}
8253
8254fn c_fn_pointer_param_is_ptr_from_type(fn_type string) []bool {
8255 if !fn_type.contains('(*)') {
8256 return []bool{}
8257 }
8258 params_start := (fn_type.index('(*)(') or { return []bool{} }) + 4
8259 params_end := fn_type.last_index_u8(`)`)
8260 if params_end < 0 {
8261 return []bool{}
8262 }
8263 if params_end <= params_start {
8264 return []bool{}
8265 }
8266 params := fn_type[params_start..params_end].trim_space()
8267 if params == '' || params == 'void' {
8268 return []bool{}
8269 }
8270 mut out := []bool{}
8271 for param in split_c_fn_pointer_params(params) {
8272 p := param.trim_space()
8273 out << (p.ends_with('*') || p.contains('(*)'))
8274 }
8275 return out
8276}
8277
8278fn split_c_fn_pointer_params(params string) []string {
8279 mut out := []string{}
8280 mut start := 0
8281 mut paren_depth := 0
8282 for i := 0; i < params.len; i++ {
8283 match params[i] {
8284 `(` {
8285 paren_depth++
8286 }
8287 `)` {
8288 if paren_depth > 0 {
8289 paren_depth--
8290 }
8291 }
8292 `,` {
8293 if paren_depth == 0 {
8294 out << params[start..i]
8295 start = i + 1
8296 }
8297 }
8298 else {}
8299 }
8300 }
8301 out << params[start..]
8302 return out
8303}
8304
8305fn (mut g Gen) gen_direct_fn_pointer_call(lhs ast.Expr, call_args []ast.Expr) bool {
8306 if lhs is ast.Ident {
8307 if _ := g.generic_call_decl_from_lhs(lhs) {
8308 return false
8309 }
8310 }
8311 fnptr_param_is_ptr := g.fn_pointer_param_is_ptr(lhs)
8312 fnptr_ret := g.fn_pointer_return_type(lhs)
8313 if fnptr_param_is_ptr.len == 0 && fnptr_ret == '' {
8314 return false
8315 }
8316 g.expr(lhs)
8317 g.sb.write_u8(`(`)
8318 for i, arg in call_args {
8319 if i > 0 {
8320 g.sb.write_string(', ')
8321 }
8322 if i < fnptr_param_is_ptr.len && fnptr_param_is_ptr[i] && arg is ast.PrefixExpr
8323 && arg.op == .amp {
8324 inner := arg.expr
8325 if g.expr_is_pointer(inner) || (inner is ast.Ident && inner.name in g.cur_fn_mut_params) {
8326 g.expr(inner)
8327 continue
8328 }
8329 }
8330 if i < fnptr_param_is_ptr.len && fnptr_param_is_ptr[i] && arg is ast.ModifierExpr {
8331 inner := arg.expr
8332 if g.expr_is_pointer(inner) || (inner is ast.Ident && inner.name in g.cur_fn_mut_params) {
8333 g.expr(inner)
8334 continue
8335 }
8336 g.sb.write_u8(`&`)
8337 g.expr(inner)
8338 continue
8339 }
8340 g.expr(arg)
8341 }
8342 g.sb.write_u8(`)`)
8343 return true
8344}
8345
8346fn (mut g Gen) should_auto_deref(arg ast.Expr) bool {
8347 if local_type := g.local_var_c_type_for_expr(arg) {
8348 return local_type.ends_with('*')
8349 && local_type !in ['void*', 'char*', 'u8*', 'byteptr', 'charptr', 'voidptr', 'chan']
8350 }
8351 if raw_type := g.get_raw_type(arg) {
8352 if raw_type is types.Pointer {
8353 match raw_type.base_type {
8354 types.Array, types.Map, types.Struct, types.Interface, types.Alias, types.String {
8355 return true
8356 }
8357 else {}
8358 }
8359 }
8360 }
8361 t := g.get_expr_type(arg)
8362 return t.ends_with('*') && t !in ['void*', 'char*', 'byteptr', 'charptr']
8363}
8364
8365fn generic_token_to_c_type(token string) string {
8366 if token == '' {
8367 return ''
8368 }
8369 if token.starts_with('Array_') {
8370 elem := generic_token_to_c_type(token['Array_'.len..])
8371 return if elem == '' { token } else { 'Array_${elem}' }
8372 }
8373 if token in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool',
8374 'string', 'rune', 'byte', 'usize', 'isize'] {
8375 return token
8376 }
8377 sep := token.index_u8(`_`)
8378 if sep >= 0 {
8379 return '${token[..sep]}__${token[sep + 1..]}'
8380 }
8381 return token
8382}
8383
8384fn (mut g Gen) specialized_generic_call_param_type(fn_name string, idx int) ?string {
8385 if !fn_name.contains('_T_') {
8386 return none
8387 }
8388 base_name := fn_name.all_before('_T_')
8389 suffix := fn_name.all_after('_T_')
8390 if suffix == '' {
8391 return none
8392 }
8393 decl := g.find_generic_fn_decl_by_base_name(base_name) or { return none }
8394 mut param_idx := idx
8395 if decl.is_method {
8396 if idx == 0 {
8397 return none
8398 }
8399 param_idx = idx - 1
8400 }
8401 if param_idx < 0 || param_idx >= decl.typ.params.len {
8402 return none
8403 }
8404 generic_params := g.generic_fn_param_names(decl)
8405 if generic_params.len != 1 {
8406 return none
8407 }
8408 param := decl.typ.params[param_idx]
8409 if direct_generic_placeholder_name(param.typ) != generic_params[0] {
8410 return none
8411 }
8412 concrete_type := generic_token_to_c_type(suffix)
8413 if concrete_type == '' {
8414 return none
8415 }
8416 if param_type_is_array_expr(param.typ) {
8417 return if concrete_type.starts_with('Array_') {
8418 concrete_type
8419 } else {
8420 'Array_${concrete_type}'
8421 }
8422 }
8423
8424 return concrete_type
8425}
8426
8427fn sum_type_variant_for_arg_type(arg_type string, variants []string) (int, string) {
8428 for i, v in variants {
8429 if v == arg_type || arg_type.ends_with('__${v}') || v.ends_with('__${arg_type}') {
8430 return i, v
8431 }
8432 }
8433 return -1, ''
8434}
8435
8436fn (mut g Gen) gen_sum_type_wrap_call_arg(param_type string, field_name string, tag int, is_primitive bool, expr ast.Expr, inner_type string) {
8437 if param_type.ends_with('*') {
8438 sum_type := param_type.trim_right('*')
8439 if sum_type != '' && g.get_sum_type_variants_for(sum_type).len > 0 {
8440 g.sb.write_string('&((${sum_type}[1]){')
8441 g.gen_sum_type_wrap(sum_type, field_name, tag, is_primitive, expr, inner_type)
8442 g.sb.write_string('}[0])')
8443 return
8444 }
8445 }
8446 g.gen_sum_type_wrap(param_type, field_name, tag, is_primitive, expr, inner_type)
8447}
8448
8449fn (mut g Gen) gen_matching_value_or_pointer_arg(param_type string, base_arg ast.Expr) {
8450 if param_type.ends_with('*') {
8451 arg_type := g.get_expr_type(base_arg)
8452 if arg_type.ends_with('*') || g.expr_is_pointer(base_arg)
8453 || g.expr_produces_pointer(base_arg) {
8454 g.expr(base_arg)
8455 } else if !g.can_take_address(base_arg) {
8456 // CallExpr/cast rvalues can't have & taken directly.
8457 // Use a 1-element array compound literal to materialize an addressable lvalue.
8458 wrap_type := if arg_type != '' && arg_type != 'int' {
8459 arg_type
8460 } else {
8461 param_type.trim_right('*')
8462 }
8463 g.gen_addr_of_expr(base_arg, wrap_type)
8464 } else {
8465 g.sb.write_string('&')
8466 g.expr(base_arg)
8467 }
8468 return
8469 }
8470 g.expr(base_arg)
8471}
8472
8473fn (mut g Gen) gen_sum_type_call_arg(param_type string, base_arg ast.Expr) bool {
8474 param_base := param_type.trim_space().trim_right('*')
8475 if param_base == '' || param_base == 'array' || param_base == 'map'
8476 || param_base.starts_with('Array_') || param_base.starts_with('Array_fixed_')
8477 || param_base.starts_with('Map_') || param_base.starts_with('_option_')
8478 || param_base.starts_with('_result_') {
8479 return false
8480 }
8481 variants := g.get_sum_type_variants_for(param_base)
8482 if variants.len == 0 {
8483 return false
8484 }
8485 if base_arg is ast.Ident {
8486 if decl_type := g.get_runtime_decl_type(base_arg.name) {
8487 decl_base := decl_type.trim_space().trim_right('*')
8488 if decl_base == param_base
8489 || (decl_base != '' && short_type_name(decl_base) == short_type_name(param_base)) {
8490 g.gen_matching_value_or_pointer_arg(param_type, base_arg)
8491 return true
8492 }
8493 }
8494 }
8495 if base_arg is ast.SelectorExpr {
8496 declared_field_type := g.selector_declared_field_type(base_arg)
8497 declared_base := declared_field_type.trim_space().trim_right('*')
8498 if declared_base == param_base || (declared_base != ''
8499 && short_type_name(declared_base) == short_type_name(param_base)) {
8500 g.gen_matching_value_or_pointer_arg(param_type, base_arg)
8501 return true
8502 }
8503 }
8504 if raw := g.get_raw_type(base_arg) {
8505 raw_c := g.types_type_to_c(raw)
8506 raw_base := raw_c.trim_space().trim_right('*')
8507 if raw_base == param_base
8508 || (raw_base != '' && short_type_name(raw_base) == short_type_name(param_base)) {
8509 g.gen_matching_value_or_pointer_arg(param_type, base_arg)
8510 return true
8511 }
8512 }
8513 mut arg_type := g.get_expr_type(base_arg)
8514 mut arg_expr := base_arg
8515 if arg_type == 'int' || arg_type == '' {
8516 inner_arg := if base_arg is ast.ParenExpr { base_arg.expr } else { base_arg }
8517 if inner_arg is ast.PrefixExpr && inner_arg.op == .mul {
8518 if inner_arg.expr is ast.CastExpr {
8519 cast_type := g.expr_type_to_c(inner_arg.expr.typ)
8520 if cast_type.ends_with('*') {
8521 arg_type = cast_type[..cast_type.len - 1]
8522 }
8523 }
8524 }
8525 }
8526 mut tag, mut field_name := sum_type_variant_for_arg_type(arg_type, variants)
8527 if tag < 0 && arg_type.ends_with('*') {
8528 arg_base := arg_type.trim_right('*')
8529 tag, field_name = sum_type_variant_for_arg_type(arg_base, variants)
8530 if tag >= 0 {
8531 arg_type = arg_base
8532 arg_expr = ast.PrefixExpr{
8533 op: .mul
8534 expr: base_arg
8535 }
8536 }
8537 }
8538 if tag < 0 || arg_type == param_type || arg_type == '' {
8539 return false
8540 }
8541 is_primitive :=
8542 arg_type in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool', 'rune', 'byte', 'usize', 'isize']
8543 || arg_type in g.primitive_type_aliases
8544 g.gen_sum_type_wrap_call_arg(param_type, field_name, tag, is_primitive, arg_expr, arg_type)
8545 return true
8546}
8547
8548fn is_builtin_pointer_alias_type_name(name string) bool {
8549 return name in ['voidptr', 'charptr', 'byteptr', 'u8ptr', 'intptr']
8550}
8551
8552fn (mut g Gen) gen_auto_deref_value_param_arg(param_type string, base_arg ast.Expr) bool {
8553 expected_type := param_type.trim_space()
8554 if expected_type == '' || expected_type.ends_with('*')
8555 || is_builtin_pointer_alias_type_name(expected_type) {
8556 return false
8557 }
8558 expected_base := expected_type.trim_right('*')
8559 if expected_base == '' || g.is_interface_type(expected_base) {
8560 return false
8561 }
8562 unwrapped_arg := g.unwrap_parens(base_arg)
8563 if unwrapped_arg is ast.PrefixExpr && unwrapped_arg.op == .mul {
8564 deref_type := g.get_expr_type(ast.Expr(unwrapped_arg)).trim_space()
8565 if deref_type == expected_base
8566 || (deref_type != '' && short_type_name(deref_type) == short_type_name(expected_base)) {
8567 g.expr(base_arg)
8568 return true
8569 }
8570 }
8571 mut arg_type := (g.local_var_c_type_for_expr(base_arg) or { '' }).trim_space()
8572 if arg_type == '' || arg_type == 'int' {
8573 arg_type = g.get_expr_type(base_arg).trim_space()
8574 }
8575 if arg_type == '' || arg_type == 'int' {
8576 if raw := g.get_raw_type(base_arg) {
8577 arg_type = g.types_type_to_c(raw).trim_space()
8578 }
8579 }
8580 if !arg_type.ends_with('*') {
8581 return false
8582 }
8583 arg_base := arg_type.trim_right('*')
8584 if arg_base == '' {
8585 return false
8586 }
8587 if arg_base != expected_base && short_type_name(arg_base) != short_type_name(expected_base) {
8588 return false
8589 }
8590 if !g.should_auto_deref(base_arg) {
8591 return false
8592 }
8593 g.sb.write_string('(*')
8594 g.expr(base_arg)
8595 g.sb.write_string(')')
8596 return true
8597}
8598
8599fn field_init_param_value_type(param_type string) string {
8600 mut typ := param_type.trim_space()
8601 if typ.starts_with('const ') {
8602 typ = typ.all_after('const ').trim_space()
8603 }
8604 for typ.ends_with('*') {
8605 typ = typ[..typ.len - 1].trim_space()
8606 }
8607 if typ == ''
8608 || typ in ['void', 'bool', 'char', 'int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'usize', 'isize', 'f32', 'f64', 'string', 'void*', 'char*', 'u8*'] {
8609 return ''
8610 }
8611 if typ.contains('(*)') || typ.starts_with('fn ') {
8612 return ''
8613 }
8614 return typ
8615}
8616
8617fn (mut g Gen) field_init_param_is_known_struct(param_type string) bool {
8618 if param_type == '' {
8619 return false
8620 }
8621 if g.lookup_struct_type_by_c_name(param_type).fields.len > 0 {
8622 return true
8623 }
8624 if param_type in g.c_struct_types || param_type in g.typedef_c_types {
8625 return true
8626 }
8627 for _, instances in g.generic_struct_instances {
8628 for inst in instances {
8629 if inst.c_name == param_type {
8630 return true
8631 }
8632 }
8633 }
8634 return false
8635}
8636
8637fn (mut g Gen) lower_field_init_call_args_for_codegen(fn_name string, args []ast.Expr) []ast.Expr {
8638 params := g.fn_param_types[fn_name] or { return args }
8639 mut lowered := []ast.Expr{cap: args.len}
8640 mut i := 0
8641 for i < args.len {
8642 arg := args[i]
8643 if arg is ast.FieldInit {
8644 if i >= params.len {
8645 return args
8646 }
8647 struct_c_name := field_init_param_value_type(params[i])
8648 if !g.field_init_param_is_known_struct(struct_c_name) {
8649 return args
8650 }
8651 mut fields := []ast.FieldInit{}
8652 for i < args.len {
8653 next := args[i]
8654 if next is ast.FieldInit {
8655 fields << ast.FieldInit{
8656 name: next.name
8657 value: next.value
8658 }
8659 i++
8660 continue
8661 }
8662 break
8663 }
8664 lowered << ast.Expr(ast.InitExpr{
8665 typ: ast.Expr(ast.Ident{
8666 name: struct_c_name
8667 })
8668 fields: fields
8669 })
8670 continue
8671 }
8672 lowered << arg
8673 i++
8674 }
8675 return lowered
8676}
8677
8678fn call_args_have_field_init(args []ast.Expr) bool {
8679 for arg in args {
8680 if arg is ast.FieldInit {
8681 return true
8682 }
8683 }
8684 return false
8685}
8686
8687fn is_voidptr_like_c_type(typ string) bool {
8688 t := typ.trim_space()
8689 return t in ['void*', 'voidptr', 'builtin__voidptr'] || t.ends_with('__voidptr')
8690}
8691
8692fn canonical_map_runtime_fn_name(fn_name string) string {
8693 mut name := fn_name.trim_space()
8694 if name.starts_with('builtin__') {
8695 name = name['builtin__'.len..]
8696 }
8697 for runtime_name in ['map__exists', 'map__get', 'map__get_check', 'map__set', 'map__delete'] {
8698 if name == runtime_name || name.ends_with('__${runtime_name}') {
8699 return runtime_name
8700 }
8701 }
8702 return ''
8703}
8704
8705fn (mut g Gen) map_runtime_key_arg_for_codegen(base_arg ast.Expr) (ast.Expr, bool) {
8706 match base_arg {
8707 ast.CastExpr {
8708 if is_voidptr_like_c_type(g.expr_type_to_c(base_arg.typ)) {
8709 return base_arg.expr, true
8710 }
8711 }
8712 ast.CallOrCastExpr {
8713 if g.call_or_cast_lhs_is_type(base_arg.lhs)
8714 && is_voidptr_like_c_type(g.expr_type_to_c(base_arg.lhs)) {
8715 return base_arg.expr, true
8716 }
8717 }
8718 else {}
8719 }
8720
8721 return base_arg, false
8722}
8723
8724fn (mut g Gen) map_runtime_key_type_for_codegen(key_arg ast.Expr) string {
8725 typed_arg := strip_expr_wrappers(key_arg)
8726 mut key_type := g.get_expr_type(typed_arg).trim_space()
8727 if (key_type == '' || key_type == 'int') && typed_arg is ast.Ident {
8728 key_type = (g.get_local_var_c_type(typed_arg.name) or { '' }).trim_space()
8729 }
8730 if (key_type == '' || key_type == 'int') && typed_arg is ast.SelectorExpr {
8731 key_type = g.selector_declared_field_type(typed_arg).trim_space()
8732 if key_type == '' || key_type == 'int' {
8733 key_type = g.selector_field_type(typed_arg).trim_space()
8734 }
8735 if (key_type == '' || key_type == 'int') && typed_arg.rhs.name in ['name', 'lifetime'] {
8736 key_type = 'string'
8737 }
8738 }
8739 return key_type
8740}
8741
8742fn (mut g Gen) map_runtime_map_type_for_codegen(map_arg ast.Expr) string {
8743 typed_arg := strip_expr_wrappers(map_arg)
8744 mut map_type := g.get_expr_type(typed_arg).trim_space()
8745 if (map_type == '' || map_type == 'int') && typed_arg is ast.Ident {
8746 map_type = (g.get_local_var_c_type(typed_arg.name) or { '' }).trim_space()
8747 }
8748 if (map_type == '' || map_type == 'int') && typed_arg is ast.SelectorExpr {
8749 map_type = g.selector_declared_field_type(typed_arg).trim_space()
8750 if map_type == '' || map_type == 'int' {
8751 map_type = g.selector_field_type(typed_arg).trim_space()
8752 }
8753 if map_type == '' || map_type == 'int' {
8754 if narrowed_field_type := g.resolve_narrowed_selector_field_type(typed_arg) {
8755 map_type = g.types_type_to_c(narrowed_field_type).trim_space()
8756 }
8757 }
8758 }
8759 if map_type == '' || map_type == 'int' {
8760 if raw_type := g.get_raw_type(typed_arg) {
8761 map_type = g.types_type_to_c(raw_type).trim_space()
8762 }
8763 }
8764 return map_type
8765}
8766
8767fn map_runtime_key_code_needs_address(code string) bool {
8768 trimmed := code.trim_space()
8769 return trimmed.ends_with('->name') || trimmed.ends_with('.name')
8770 || trimmed.ends_with('->lifetime') || trimmed.ends_with('.lifetime')
8771}
8772
8773fn (mut g Gen) memdup_addr_arg_type(expr ast.Expr) string {
8774 match expr {
8775 ast.AsCastExpr {
8776 return g.expr_type_to_c(expr.typ).trim_space()
8777 }
8778 ast.CastExpr {
8779 return g.expr_type_to_c(expr.typ).trim_space()
8780 }
8781 ast.CallExpr {
8782 if typ := g.get_call_return_type(expr.lhs, expr.args) {
8783 return typ.trim_space()
8784 }
8785 }
8786 ast.SelectorExpr {
8787 mut typ := g.selector_declared_field_type(expr).trim_space()
8788 if typ == '' || typ == 'int' {
8789 typ = g.selector_field_type(expr).trim_space()
8790 }
8791 if typ != '' && typ != 'int' {
8792 return typ
8793 }
8794 }
8795 ast.ParenExpr {
8796 return g.memdup_addr_arg_type(expr.expr)
8797 }
8798 ast.ModifierExpr {
8799 return g.memdup_addr_arg_type(expr.expr)
8800 }
8801 else {}
8802 }
8803
8804 if raw_type := g.get_raw_type(expr) {
8805 typ := g.types_type_to_c(raw_type).trim_space()
8806 if typ != '' && typ != 'int' {
8807 return typ
8808 }
8809 }
8810 typ := g.get_expr_type(expr).trim_space()
8811 if typ == 'int' {
8812 return ''
8813 }
8814 return typ
8815}
8816
8817fn (mut g Gen) c_type_is_fn_pointer_value(type_name string) bool {
8818 name := type_name.trim_space()
8819 if name == '' || name == 'int' {
8820 return false
8821 }
8822 if g.c_type_is_fn_pointer_alias(name) {
8823 return true
8824 }
8825 if raw_type := g.lookup_type_by_c_name(name) {
8826 if _ := extract_fn_type(raw_type) {
8827 return true
8828 }
8829 }
8830 if raw_type := g.lookup_type_by_c_name_const(name) {
8831 if _ := extract_fn_type(raw_type) {
8832 return true
8833 }
8834 }
8835 return false
8836}
8837
8838fn (mut g Gen) selector_has_fn_pointer_value_type(sel ast.SelectorExpr) bool {
8839 if global_type := g.global_var_type_for_selector(sel) {
8840 if g.c_type_is_fn_pointer_value(global_type) {
8841 return true
8842 }
8843 }
8844 sel_expr := ast.Expr(sel)
8845 if raw_type := g.get_raw_type(sel_expr) {
8846 if _ := extract_fn_type(raw_type) {
8847 return true
8848 }
8849 }
8850 return g.c_type_is_fn_pointer_value(g.get_expr_type(sel_expr))
8851}
8852
8853fn (mut g Gen) selector_is_known_fn_value(sel ast.SelectorExpr) bool {
8854 lhs := sel.lhs
8855 if lhs !is ast.Ident || sel.rhs.name == '' {
8856 return false
8857 }
8858 mod_name := (lhs as ast.Ident).name
8859 if mod_name == '' {
8860 return false
8861 }
8862 if !g.is_module_ident(mod_name) {
8863 return false
8864 }
8865 c_mod_name := g.resolve_module_name(mod_name).replace('.', '_')
8866 c_name := '${c_mod_name}__${sel.rhs.name}'
8867 if c_name in g.fn_return_types || c_name in g.fn_param_is_ptr {
8868 return true
8869 }
8870 return g.selector_has_fn_pointer_value_type(sel)
8871}
8872
8873fn (mut g Gen) gen_sort_fnsortcb_arg(fn_name string, idx int, base_arg ast.Expr, expected_param_type string) bool {
8874 if idx != 1 || fn_name !in ['array__sort_with_compare', 'array__sorted_with_compare'] {
8875 return false
8876 }
8877 mut expected_sort_param_type := expected_param_type.trim_space()
8878 // Unregistered generated builtin sort helpers still expect FnSortCB; custom
8879 // same-name functions are declared and keep their real parameter type gate.
8880 if expected_sort_param_type == '' && fn_name !in g.fn_param_types {
8881 expected_sort_param_type = 'FnSortCB'
8882 }
8883 if expected_sort_param_type != 'FnSortCB' {
8884 return false
8885 }
8886 comparator := match base_arg {
8887 ast.ParenExpr {
8888 base_arg.expr
8889 }
8890 else {
8891 base_arg
8892 }
8893 }
8894
8895 match comparator {
8896 ast.Ident {
8897 if comparator.name == '' || comparator.name == 'nil' {
8898 return false
8899 }
8900 }
8901 ast.FnLiteral {}
8902 ast.SelectorExpr {
8903 if !g.selector_is_known_fn_value(comparator) {
8904 return false
8905 }
8906 }
8907 else {
8908 return false
8909 }
8910 }
8911
8912 g.sb.write_string('(FnSortCB)')
8913 g.expr(comparator)
8914 return true
8915}
8916
8917fn (mut g Gen) gen_call_arg(fn_name string, idx int, arg ast.Expr) {
8918 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
8919 map_runtime_name := canonical_map_runtime_fn_name(fn_name)
8920 if fn_name == 'memdup' && idx == 0 && base_arg is ast.PrefixExpr && base_arg.op == .amp {
8921 g.gen_addr_of_expr(base_arg.expr, g.memdup_addr_arg_type(base_arg.expr))
8922 return
8923 }
8924 if fn_name == 'json__decode' && idx == 0 {
8925 json_type_marker := if base_arg is ast.PrefixExpr && base_arg.op == .amp {
8926 base_arg.expr
8927 } else {
8928 base_arg
8929 }
8930 match json_type_marker {
8931 ast.Ident {
8932 if json_type_marker.name != '' && json_type_marker.name != 'nil' {
8933 if g.is_type_name(json_type_marker.name)
8934 || g.get_local_var_c_type(json_type_marker.name) == none {
8935 g.sb.write_string('NULL')
8936 return
8937 }
8938 }
8939 }
8940 ast.SelectorExpr, ast.Type {
8941 g.sb.write_string('NULL')
8942 return
8943 }
8944 else {
8945 if g.is_type_reference_expr(json_type_marker) {
8946 g.sb.write_string('NULL')
8947 return
8948 }
8949 }
8950 }
8951 }
8952 // Sum type variable narrowed via `is` check, passed to a function expecting the variant type.
8953 // Only extract when the parameter type matches the narrowed type (not the original sum type).
8954 mut expected_param_type := ''
8955 if param_types := g.fn_param_types[fn_name] {
8956 if idx < param_types.len {
8957 expected_param_type = param_types[idx]
8958 }
8959 }
8960 if fn_name.contains('_T_') {
8961 if derived_param_type := g.specialized_generic_call_param_type(fn_name, idx) {
8962 if expected_param_type == '' || expected_param_type == 'Array_T'
8963 || expected_param_type.contains('_T_')
8964 || (derived_param_type.starts_with('Array_')
8965 && expected_param_type == generic_token_to_c_type(fn_name.all_after('_T_'))) {
8966 expected_param_type = derived_param_type
8967 }
8968 }
8969 }
8970 if map_runtime_name != '' && idx == 0 {
8971 if base_arg is ast.PrefixExpr && base_arg.op == .amp {
8972 if g.expr_is_pointer(base_arg.expr) || g.expr_produces_pointer(base_arg.expr) {
8973 g.expr(base_arg.expr)
8974 return
8975 }
8976 g.sb.write_u8(`&`)
8977 g.expr(base_arg.expr)
8978 return
8979 }
8980 map_type := g.map_runtime_map_type_for_codegen(base_arg)
8981 if !g.expr_is_pointer(base_arg) && !g.expr_produces_pointer(base_arg)
8982 && (map_type == 'map' || map_type.starts_with('Map_')
8983 || g.can_take_address(base_arg)) {
8984 g.gen_addr_of_expr(base_arg, map_type)
8985 return
8986 }
8987 }
8988 if map_runtime_name != '' && idx == 1 {
8989 key_arg, cast_to_voidptr := g.map_runtime_key_arg_for_codegen(base_arg)
8990 if key_arg is ast.PrefixExpr && key_arg.op == .amp {
8991 if cast_to_voidptr {
8992 g.sb.write_string('(void*)')
8993 }
8994 g.sb.write_u8(`&`)
8995 g.expr(key_arg.expr)
8996 return
8997 }
8998 key_type := g.map_runtime_key_type_for_codegen(key_arg)
8999 if key_type != '' && key_type != 'void*' && key_type != 'voidptr'
9000 && !key_type.ends_with('*') {
9001 if cast_to_voidptr {
9002 g.sb.write_string('(void*)')
9003 }
9004 g.gen_addr_of_expr(key_arg, key_type)
9005 return
9006 }
9007 saved_sb := g.sb
9008 g.sb = strings.new_builder(256)
9009 g.expr(key_arg)
9010 key_code := g.sb.str()
9011 g.sb = saved_sb
9012 if map_runtime_key_code_needs_address(key_code) {
9013 if cast_to_voidptr {
9014 g.sb.write_string('(void*)')
9015 }
9016 g.sb.write_string('&${key_code}')
9017 return
9018 }
9019 }
9020 if g.gen_sort_fnsortcb_arg(fn_name, idx, base_arg, expected_param_type) {
9021 return
9022 }
9023 if expected_param_type != '' && g.gen_auto_deref_value_param_arg(expected_param_type, base_arg) {
9024 return
9025 }
9026 if expected_param_type != ''
9027 && (expected_param_type.contains('(*)') || g.is_fn_pointer_alias_type(expected_param_type))
9028 && base_arg is ast.FnLiteral {
9029 g.expr(base_arg)
9030 return
9031 }
9032 if expected_param_type.ends_with('*')
9033 && g.gen_sum_ident_payload_pointer_arg(base_arg, expected_param_type.trim_right('*')) {
9034 return
9035 }
9036 if expected_param_type != '' && g.gen_sum_type_call_arg(expected_param_type, base_arg) {
9037 return
9038 }
9039 if expected_param_type != '' {
9040 if sum_ident := sum_payload_ident_from_expr(base_arg) {
9041 decl_type := g.get_local_var_c_type(sum_ident.name) or { '' }
9042 if decl_type != '' && expected_param_type != decl_type
9043 && expected_param_type != decl_type + '*' {
9044 if g.gen_sum_ident_as_payload_type(sum_ident, expected_param_type) {
9045 return
9046 }
9047 if base_arg is ast.Ident && g.gen_sum_narrowed_ident(sum_ident) {
9048 return
9049 }
9050 }
9051 }
9052 }
9053 if fn_name == 'array__push' && idx == 1 && base_arg is ast.ArrayInitExpr
9054 && base_arg.exprs.len == 1 {
9055 elem_type := unmangle_c_ptr_type(g.extract_array_elem_type(base_arg.typ))
9056 elem_expr := base_arg.exprs[0]
9057 mut elem_expr_type := g.get_expr_type(elem_expr).trim_space()
9058 if (elem_expr_type == '' || elem_expr_type == 'int') && elem_expr is ast.Ident {
9059 elem_expr_type = (g.get_local_var_c_type(elem_expr.name) or { '' }).trim_space()
9060 }
9061 if elem_type != '' && elem_expr_type.ends_with('*')
9062 && elem_expr_type.trim_right('*') == elem_type {
9063 g.expr(elem_expr)
9064 return
9065 }
9066 }
9067 if fn_name in ['ui__EventMngr__add_receiver', 'ui__EventMngr__rm_receiver'] && idx == 1 {
9068 if g.gen_interface_cast('ui__Widget', base_arg) {
9069 return
9070 }
9071 }
9072 // nil is always a valid pointer value (NULL) — never wrap it. A literal
9073 // `0` (possibly cast-wrapped) is nil-like only for pointer parameters;
9074 // for value parameters it must stay a plain `0`.
9075 if is_nil_like_expr(base_arg) {
9076 if (base_arg is ast.Ident && base_arg.name == 'nil') || base_arg is ast.Type {
9077 g.sb.write_string('NULL')
9078 return
9079 }
9080 if ptr_params := g.fn_param_is_ptr[fn_name] {
9081 if idx < ptr_params.len && ptr_params[idx] {
9082 g.sb.write_string('NULL')
9083 return
9084 }
9085 }
9086 }
9087 if fn_name == 'new_map_init_noscan_value' && idx in [7, 8] {
9088 // new_map_init_noscan_value expects raw key/value element pointers.
9089 // Lowered dynamic arrays arrive as `array` values; pass their `.data`.
9090 // Raw fixed array literals should be emitted directly and decay to pointers.
9091 if base_arg is ast.ArrayInitExpr {
9092 g.expr(base_arg)
9093 } else {
9094 g.expr(base_arg)
9095 g.sb.write_string('.data')
9096 }
9097 return
9098 }
9099 if map_runtime_name == 'map__set' && idx in [1, 2] {
9100 if base_arg is ast.CastExpr && base_arg.expr is ast.PrefixExpr {
9101 prefix_expr := base_arg.expr as ast.PrefixExpr
9102 if prefix_expr.op == .amp {
9103 mut value_type := ''
9104 if prefix_expr.expr is ast.AsCastExpr {
9105 value_type = g.expr_type_to_c(prefix_expr.expr.typ).trim_space()
9106 }
9107 if value_type == '' || value_type == 'int' {
9108 value_type = g.get_expr_type(prefix_expr.expr).trim_space()
9109 }
9110 if value_type == '' || value_type == 'int' {
9111 if raw_type := g.get_raw_type(prefix_expr.expr) {
9112 value_type = g.types_type_to_c(raw_type).trim_space()
9113 }
9114 }
9115 g.sb.write_string('((void*)')
9116 g.gen_addr_of_expr(prefix_expr.expr, value_type)
9117 g.sb.write_string(')')
9118 return
9119 }
9120 }
9121 g.expr(base_arg)
9122 return
9123 }
9124 if param_types := g.fn_param_types[fn_name] {
9125 if idx < param_types.len {
9126 param_type := param_types[idx]
9127 param_base := param_type.trim_right('*')
9128 if g.is_interface_type(param_base) {
9129 mut iface_arg := base_arg
9130 mut want_iface_ptr := param_type.ends_with('*')
9131 if ptr_params := g.fn_param_is_ptr[fn_name] {
9132 if idx < ptr_params.len && ptr_params[idx] {
9133 want_iface_ptr = true
9134 }
9135 }
9136 if want_iface_ptr && base_arg is ast.PrefixExpr && base_arg.op == .amp {
9137 if g.expr_is_pointer(base_arg.expr)
9138 || g.expr_produces_pointer(base_arg.expr)
9139 || (base_arg.expr is ast.Ident && base_arg.expr.name in g.cur_fn_mut_params) {
9140 iface_arg = base_arg.expr
9141 }
9142 }
9143 mut arg_type := g.get_expr_type(iface_arg).trim_space()
9144 if (arg_type == '' || arg_type == 'int') && iface_arg is ast.Ident {
9145 arg_type = (g.get_local_var_c_type(iface_arg.name) or { '' }).trim_space()
9146 }
9147 mut raw_arg_type := ''
9148 if arg_type == '' || arg_type == 'int' {
9149 if raw := g.get_raw_type(iface_arg) {
9150 raw_arg_type = g.types_type_to_c(raw).trim_space()
9151 arg_type = raw_arg_type
9152 }
9153 } else if raw := g.get_raw_type(iface_arg) {
9154 raw_arg_type = g.types_type_to_c(raw).trim_space()
9155 }
9156 arg_base := arg_type.trim_right('*')
9157 raw_arg_base := raw_arg_type.trim_right('*')
9158 if is_ierror_c_type(param_base) && is_ierror_c_type(arg_base) && !want_iface_ptr {
9159 g.expr(iface_arg)
9160 return
9161 }
9162 needs_cast := if arg_base == param_base {
9163 raw_arg_base != '' && raw_arg_base != 'int' && raw_arg_base != param_base
9164 } else {
9165 arg_base != '' && arg_base != 'int'
9166 }
9167 if needs_cast {
9168 if want_iface_ptr {
9169 if g.gen_heap_interface_cast(param_base, iface_arg) {
9170 return
9171 }
9172 } else if g.gen_interface_cast(param_base, iface_arg) {
9173 return
9174 }
9175 }
9176 }
9177 }
9178 }
9179 mut has_pointer_param_info := false
9180 mut want_ptr := false
9181 if ptr_params := g.fn_param_is_ptr[fn_name] {
9182 if idx < ptr_params.len {
9183 has_pointer_param_info = true
9184 want_ptr = ptr_params[idx]
9185 }
9186 }
9187 if !has_pointer_param_info {
9188 if param_types := g.fn_param_types[fn_name] {
9189 if idx < param_types.len && param_types[idx].trim_space().ends_with('*') {
9190 has_pointer_param_info = true
9191 want_ptr = true
9192 }
9193 }
9194 }
9195 if has_pointer_param_info && want_ptr && base_arg is ast.FnLiteral {
9196 g.expr(base_arg)
9197 return
9198 }
9199 if has_pointer_param_info {
9200 if want_ptr && base_arg is ast.PrefixExpr && base_arg.op == .amp {
9201 inner := base_arg.expr
9202 if g.expr_is_pointer(inner) || g.expr_produces_pointer(inner) {
9203 g.expr(inner)
9204 return
9205 }
9206 if inner is ast.SelectorExpr && g.gen_sum_common_field_selector_addr(inner) {
9207 return
9208 }
9209 }
9210 // &*(ptr) cancels out to just ptr — avoids &(rvalue) when deref
9211 // gets null-guard expansion for sum type data pointers.
9212 if want_ptr {
9213 unwrapped := g.unwrap_parens(base_arg)
9214 if unwrapped is ast.PrefixExpr {
9215 if unwrapped.op == .mul {
9216 if g.expr_is_pointer(unwrapped.expr) || g.expr_produces_pointer(unwrapped.expr) {
9217 g.expr(unwrapped.expr)
9218 return
9219 }
9220 }
9221 }
9222 }
9223 got_ptr := g.expr_is_pointer(base_arg)
9224 // Also check for pointer-producing expressions (casts, pointer arithmetic)
9225 // that expr_is_pointer misses but that produce pointer values in C.
9226 if want_ptr && !got_ptr && g.expr_produces_pointer(base_arg) {
9227 g.expr(base_arg)
9228 return
9229 }
9230 if want_ptr && !got_ptr && base_arg is ast.SelectorExpr
9231 && g.gen_sum_common_field_selector_addr(base_arg) {
9232 return
9233 }
9234 if want_ptr && !got_ptr && g.can_take_address(base_arg) {
9235 // Don't take address of function pointer variables
9236 if base_arg is ast.Ident {
9237 if local_type := g.get_local_var_c_type(base_arg.name) {
9238 if local_type == 'fn_ptr' || local_type.ends_with('*') {
9239 g.expr(base_arg)
9240 return
9241 }
9242 }
9243 }
9244 // Generate inner code first to detect rvalue patterns
9245 // (null-guarded ternaries from smartcast produce rvalues in C)
9246 saved_sb2 := g.sb
9247 g.sb = strings.new_builder(256)
9248 g.expr(base_arg)
9249 arg_code := g.sb.str()
9250 g.sb = saved_sb2
9251 if arg_code.contains('){0})') {
9252 // Rvalue from null-guard — use temp variable
9253 arg_type := g.get_expr_type(base_arg)
9254 if arg_type != '' && arg_type != 'int' && arg_type != 'void' {
9255 tmp_name := '_addr_t${g.tmp_counter}'
9256 g.tmp_counter++
9257 g.sb.write_string('({ ${arg_type} ${tmp_name} = ${arg_code}; &${tmp_name}; })')
9258 } else {
9259 g.sb.write_string('&${arg_code}')
9260 }
9261 } else {
9262 g.sb.write_string('&${arg_code}')
9263 }
9264 return
9265 }
9266 if want_ptr && !got_ptr && !g.can_take_address(base_arg) {
9267 // Can't take address of expression (e.g., function call return value).
9268 // Use compound literal to make it addressable with proper type.
9269 if base_arg is ast.Ident {
9270 if enum_type := g.enum_member_ident_type(base_arg.name) {
9271 g.gen_addr_of_expr(base_arg, enum_type)
9272 return
9273 }
9274 }
9275 if raw := g.get_raw_type(base_arg) {
9276 if raw is types.Pointer {
9277 // Already a pointer at the type level, just emit
9278 g.expr(base_arg)
9279 return
9280 }
9281 c_type := g.types_type_to_c(raw)
9282 if c_type != '' {
9283 g.gen_addr_of_expr(base_arg, c_type)
9284 return
9285 }
9286 }
9287 // Fallback: use expression type or default to array
9288 wrap_type := g.get_expr_type(base_arg)
9289 if wrap_type != '' {
9290 g.gen_addr_of_expr(base_arg, wrap_type)
9291 return
9292 }
9293 if base_arg is ast.StringLiteral || base_arg is ast.StringInterLiteral {
9294 g.gen_addr_of_expr(base_arg, 'string')
9295 return
9296 }
9297 if base_arg is ast.BasicLiteral {
9298 lit_type := if base_arg.kind == .string {
9299 'string'
9300 } else {
9301 'int'
9302 }
9303 g.gen_addr_of_expr(base_arg, lit_type)
9304 return
9305 }
9306 g.gen_addr_of_expr(base_arg, 'int')
9307 return
9308 }
9309 should_deref := g.should_auto_deref(base_arg)
9310 || (base_arg is ast.Ident && base_arg.name in g.cur_fn_mut_params)
9311 if !want_ptr && got_ptr && should_deref {
9312 // Don't auto-deref when the param type is a pointer alias (e.g. Coordptr = Coord*)
9313 if param_types := g.fn_param_types[fn_name] {
9314 if idx < param_types.len {
9315 if is_builtin_pointer_alias_type_name(param_types[idx]) {
9316 g.expr(base_arg)
9317 return
9318 }
9319 param_base := param_types[idx].trim_right('*')
9320 if g.is_interface_type(param_base) {
9321 mut arg_type := g.get_expr_type(base_arg).trim_space()
9322 if (arg_type == '' || arg_type == 'int') && base_arg is ast.Ident {
9323 arg_type =
9324 (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
9325 }
9326 if arg_type == '' || arg_type == 'int' {
9327 if raw := g.get_raw_type(base_arg) {
9328 arg_type = g.types_type_to_c(raw).trim_space()
9329 }
9330 }
9331 if arg_type.trim_right('*') != param_base {
9332 if g.gen_interface_cast(param_base, base_arg) {
9333 return
9334 }
9335 g.expr(base_arg)
9336 return
9337 }
9338 }
9339 }
9340 }
9341 g.sb.write_string('(*')
9342 g.expr(base_arg)
9343 g.sb.write_string(')')
9344 return
9345 }
9346 }
9347 if param_types := g.fn_param_types[fn_name] {
9348 if idx < param_types.len {
9349 param_type := param_types[idx]
9350 param_base := param_type.trim_right('*')
9351 if g.is_interface_type(param_base) {
9352 mut arg_type := g.get_expr_type(base_arg).trim_space()
9353 if (arg_type == '' || arg_type == 'int') && base_arg is ast.Ident {
9354 arg_type = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
9355 }
9356 mut raw_arg_type := ''
9357 if arg_type == '' || arg_type == 'int' {
9358 if raw := g.get_raw_type(base_arg) {
9359 raw_arg_type = g.types_type_to_c(raw).trim_space()
9360 arg_type = raw_arg_type
9361 }
9362 } else if raw := g.get_raw_type(base_arg) {
9363 raw_arg_type = g.types_type_to_c(raw).trim_space()
9364 }
9365 arg_base := arg_type.trim_right('*')
9366 raw_arg_base := raw_arg_type.trim_right('*')
9367 if is_ierror_c_type(param_base) && is_ierror_c_type(arg_base) {
9368 if param_type.ends_with('*') {
9369 if arg_type.ends_with('*') {
9370 g.expr(base_arg)
9371 return
9372 }
9373 if g.can_take_address(base_arg) {
9374 g.sb.write_string('&')
9375 g.expr(base_arg)
9376 return
9377 }
9378 } else {
9379 g.expr(base_arg)
9380 return
9381 }
9382 }
9383 if arg_base == param_base {
9384 if raw_arg_base != '' && raw_arg_base != 'int' && raw_arg_base != param_base {
9385 if param_type.ends_with('*') {
9386 if g.gen_heap_interface_cast(param_base, base_arg) {
9387 return
9388 }
9389 } else {
9390 if g.gen_interface_cast(param_base, base_arg) {
9391 return
9392 }
9393 }
9394 }
9395 if param_type.ends_with('*') {
9396 if arg_type.ends_with('*') {
9397 g.expr(base_arg)
9398 return
9399 }
9400 if g.can_take_address(base_arg) {
9401 g.sb.write_string('&')
9402 g.expr(base_arg)
9403 return
9404 }
9405 } else if arg_type.ends_with('*') && g.should_auto_deref(base_arg) {
9406 g.sb.write_string('(*')
9407 g.expr(base_arg)
9408 g.sb.write_string(')')
9409 return
9410 }
9411 } else if arg_base != '' && arg_base != 'int' {
9412 if param_type.ends_with('*') {
9413 if g.gen_heap_interface_cast(param_base, base_arg) {
9414 return
9415 }
9416 } else {
9417 if g.gen_interface_cast(param_base, base_arg) {
9418 return
9419 }
9420 }
9421 }
9422 }
9423 }
9424 }
9425 // Check if argument needs sum type wrapping (variant passed where sum type expected)
9426 if param_types := g.fn_param_types[fn_name] {
9427 if idx < param_types.len {
9428 param_type := param_types[idx]
9429 variants := g.get_sum_type_variants_for(param_type)
9430 if variants.len > 0 {
9431 if base_arg is ast.Ident {
9432 if decl_type := g.get_runtime_decl_type(base_arg.name) {
9433 decl_base := decl_type.trim_space().trim_right('*')
9434 if decl_base == param_type || (decl_base != ''
9435 && short_type_name(decl_base) == short_type_name(param_type)) {
9436 g.expr(base_arg)
9437 return
9438 }
9439 }
9440 }
9441 if base_arg is ast.SelectorExpr {
9442 declared_field_type := g.selector_declared_field_type(base_arg)
9443 if declared_field_type == param_type
9444 || (declared_field_type != ''
9445 && short_type_name(declared_field_type) == short_type_name(param_type)) {
9446 g.expr(base_arg)
9447 return
9448 }
9449 }
9450 // Selector/index expressions can be smartcast-narrowed in the checker env,
9451 // while still evaluating to the original sum value representation.
9452 // If the raw (declared) type already matches the sum param type, pass it through.
9453 // But verify with get_expr_type: if it returns a known variant, the raw type
9454 // lookup hit the wrong scope entry (e.g. struct field vs. local variable).
9455 if raw := g.get_raw_type(base_arg) {
9456 raw_c := g.types_type_to_c(raw)
9457 if raw_c == param_type {
9458 expr_type := g.get_expr_type(base_arg)
9459 if expr_type == param_type || expr_type == '' || expr_type == 'int'
9460 || expr_type !in variants {
9461 g.expr(base_arg)
9462 return
9463 }
9464 // expr_type is a known variant but raw_type says sum type:
9465 // raw_type lookup was wrong, fall through to wrapping
9466 }
9467 }
9468 mut arg_type := g.get_expr_type(base_arg)
9469 // For smartcast dereference patterns (*(Type*)(expr._data._Type)),
9470 // extract the actual type from the cast
9471 if arg_type == 'int' || arg_type == '' {
9472 // Unwrap ParenExpr if present
9473 inner_arg := if base_arg is ast.ParenExpr {
9474 base_arg.expr
9475 } else {
9476 base_arg
9477 }
9478 if inner_arg is ast.PrefixExpr && inner_arg.op == .mul {
9479 if inner_arg.expr is ast.CastExpr {
9480 cast_type := g.expr_type_to_c(inner_arg.expr.typ)
9481 if cast_type.ends_with('*') {
9482 arg_type = cast_type[..cast_type.len - 1]
9483 }
9484 }
9485 }
9486 }
9487 if arg_type != param_type && arg_type != '' {
9488 mut tag := -1
9489 mut field_name := ''
9490 for i, v in variants {
9491 if v == arg_type || arg_type.ends_with('__${v}')
9492 || v.ends_with('__${arg_type}') {
9493 tag = i
9494 field_name = v
9495 break
9496 }
9497 }
9498 if tag >= 0 {
9499 is_primitive :=
9500 arg_type in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool', 'rune', 'byte', 'usize', 'isize']
9501 || arg_type in g.primitive_type_aliases
9502 g.gen_sum_type_wrap_call_arg(param_type, field_name, tag, is_primitive,
9503 base_arg, arg_type)
9504 return
9505 }
9506 }
9507 }
9508 }
9509 }
9510 if param_types := g.fn_param_types[fn_name] {
9511 if idx < param_types.len {
9512 param_type := param_types[idx]
9513 if param_type.starts_with('Array_fixed_') && base_arg is ast.ArrayInitExpr {
9514 g.sb.write_string('((${param_type})')
9515 g.expr(base_arg)
9516 g.sb.write_string(')')
9517 return
9518 }
9519 if param_type.starts_with('Array_fixed_') && base_arg is ast.CallExpr {
9520 if call_ret := g.get_call_return_type(base_arg.lhs, base_arg.args) {
9521 if call_ret == param_type {
9522 _, arr_len := parse_fixed_array_elem_type(param_type)
9523 if arr_len > 0 {
9524 wrapper_type := g.c_fn_return_type_from_v(param_type)
9525 g.tmp_counter++
9526 tmp_name := '_fixed_arg${g.tmp_counter}'
9527 g.sb.write_string('({ ${wrapper_type} ${tmp_name} = ')
9528 g.expr(base_arg)
9529 g.sb.write_string('; (${param_type}){')
9530 for fi in 0 .. arr_len {
9531 if fi > 0 {
9532 g.sb.write_string(', ')
9533 }
9534 g.sb.write_string('${tmp_name}.ret_arr[${fi}]')
9535 }
9536 g.sb.write_string('}; })')
9537 return
9538 }
9539 }
9540 }
9541 }
9542 if param_type == 'Array_string' && g.get_expr_type(base_arg) == 'string' {
9543 g.sb.write_string('((array){ .data = &(string[1]){')
9544 g.expr(base_arg)
9545 g.sb.write_string('}, .offset = 0, .len = 1, .cap = 1, .flags = 0, .element_size = sizeof(string) })')
9546 return
9547 }
9548 }
9549 }
9550 // Detect bound method references: b.method_name passed as a fn pointer arg.
9551 // Generate a static trampoline function + __thread capture variable.
9552 // Only applies when the expected parameter is a function pointer type.
9553 if base_arg is ast.SelectorExpr {
9554 receiver_type := g.get_expr_type(base_arg.lhs).trim_right('*')
9555 if receiver_type != '' && base_arg.rhs.name != '' {
9556 // Check if the expected parameter type is a function pointer.
9557 // If not, this is a struct field access (e.g., s.str, w.id), not a method value.
9558 mut param_is_fn_ptr := false
9559 if param_types := g.fn_param_types[fn_name] {
9560 if idx < param_types.len {
9561 pt := param_types[idx]
9562 param_is_fn_ptr = pt.contains('(*)') || g.is_fn_pointer_alias_type(pt)
9563 }
9564 }
9565 method_c_name := '${receiver_type}__${base_arg.rhs.name}'
9566 if param_is_fn_ptr
9567 && (method_c_name in g.fn_param_is_ptr || method_c_name in g.fn_return_types) {
9568 tramp_name := '__bound_method_${receiver_type}__${base_arg.rhs.name}'
9569 if tramp_name !in g.emitted_trampolines {
9570 g.emitted_trampolines[tramp_name] = true
9571 // Build trampoline function as a string and add to deferred defs
9572 ret_type := g.fn_return_types[method_c_name] or { 'void' }
9573 param_is_ptr := g.fn_param_is_ptr[method_c_name] or { []bool{} }
9574 param_types := g.fn_param_types[method_c_name] or { []string{} }
9575 recv_is_ptr := if param_is_ptr.len > 0 { param_is_ptr[0] } else { false }
9576 recv_var := '__bound_recv_${receiver_type}__${base_arg.rhs.name}'
9577 mut def := 'static __thread ${receiver_type}* ${recv_var};\n'
9578 def += 'static ${ret_type} ${tramp_name}('
9579 for pi in 1 .. param_types.len {
9580 if pi > 1 {
9581 def += ', '
9582 }
9583 def += '${param_types[pi]} _p${pi}'
9584 }
9585 def += ') {\n\t'
9586 if ret_type != 'void' {
9587 def += 'return '
9588 }
9589 if recv_is_ptr {
9590 def += '${method_c_name}(${recv_var}'
9591 } else {
9592 def += '${method_c_name}(*${recv_var}'
9593 }
9594 for pi in 1 .. param_types.len {
9595 def += ', _p${pi}'
9596 }
9597 def += ');\n}\n'
9598 g.trampoline_defs << def
9599 }
9600 // Set capture variable and pass trampoline
9601 recv_var := '__bound_recv_${receiver_type}__${base_arg.rhs.name}'
9602 g.sb.write_string('(${recv_var} = ')
9603 if g.expr_is_pointer(base_arg.lhs) {
9604 g.expr(base_arg.lhs)
9605 } else {
9606 g.sb.write_string('&')
9607 g.expr(base_arg.lhs)
9608 }
9609 g.sb.write_string(', ${tramp_name})')
9610 return
9611 }
9612 }
9613 }
9614 g.expr(base_arg)
9615}
9616
9617fn (mut g Gen) resolve_container_method_name(receiver ast.Expr, method_name string, expected_params int) string {
9618 mut candidates := []string{}
9619 if raw_type := g.get_raw_type(receiver) {
9620 match raw_type {
9621 types.Pointer {
9622 match raw_type.base_type {
9623 types.Array, types.ArrayFixed {
9624 candidates << 'array'
9625 }
9626 types.Map {
9627 candidates << 'map'
9628 }
9629 types.String {
9630 candidates << 'string'
9631 }
9632 types.Alias {
9633 match raw_type.base_type.base_type {
9634 types.Array, types.ArrayFixed {
9635 candidates << 'array'
9636 }
9637 types.Map {
9638 candidates << 'map'
9639 }
9640 types.String {
9641 candidates << 'string'
9642 }
9643 else {}
9644 }
9645 }
9646 else {}
9647 }
9648 }
9649 types.Array, types.ArrayFixed {
9650 candidates << 'array'
9651 }
9652 types.Map {
9653 candidates << 'map'
9654 }
9655 types.String {
9656 candidates << 'string'
9657 }
9658 types.Alias {
9659 match raw_type.base_type {
9660 types.Array, types.ArrayFixed {
9661 candidates << 'array'
9662 }
9663 types.Map {
9664 candidates << 'map'
9665 }
9666 types.String {
9667 candidates << 'string'
9668 }
9669 else {}
9670 }
9671 }
9672 else {}
9673 }
9674 }
9675 recv_name := g.method_receiver_base_type(receiver)
9676 if recv_name == 'array' || recv_name.starts_with('Array_') {
9677 candidates << 'array'
9678 }
9679 if recv_name == 'map' || recv_name.starts_with('Map_') {
9680 candidates << 'map'
9681 }
9682 if recv_name == 'string' {
9683 candidates << 'string'
9684 }
9685 for base in candidates {
9686 candidate := '${base}__${method_name}'
9687 if params := g.fn_param_is_ptr[candidate] {
9688 if params.len == expected_params {
9689 return candidate
9690 }
9691 } else if candidate in g.fn_return_types {
9692 return candidate
9693 }
9694 }
9695 return ''
9696}
9697
9698fn (mut g Gen) resolve_call_name(lhs ast.Expr, _arg_count int) string {
9699 mut name := ''
9700 if lhs is ast.GenericArgOrIndexExpr {
9701 if _ := g.fn_type_alias_cast_type(lhs) {
9702 return ''
9703 }
9704 return g.resolve_call_name(lhs.lhs, _arg_count)
9705 } else if lhs is ast.GenericArgs {
9706 if _ := g.fn_type_alias_cast_type(lhs) {
9707 return ''
9708 }
9709 return g.resolve_call_name(lhs.lhs, _arg_count)
9710 } else if lhs is ast.Ident {
9711 name = sanitize_fn_ident(lhs.name)
9712 } else if lhs is ast.SelectorExpr {
9713 if lhs.lhs is ast.Ident && lhs.lhs.name == 'C' {
9714 return lhs.rhs.name
9715 }
9716 // Static type method call: Type.method(...)
9717 if lhs.lhs is ast.Ident
9718 && (g.is_type_name(lhs.lhs.name) || g.selector_lhs_is_static_type_ident(lhs.lhs.name)) {
9719 return '${g.get_qualified_name(lhs.lhs.name)}__${sanitize_fn_ident(lhs.rhs.name)}'
9720 }
9721 if mod_call_name := g.resolve_selector_module_call_name(lhs) {
9722 name = mod_call_name
9723 } else {
9724 method_name := sanitize_fn_ident(lhs.rhs.name)
9725 if qualified_method_name := g.resolved_qualified_selector_method_name(method_name) {
9726 name = qualified_method_name
9727 } else {
9728 if method_name == 'bytestr' {
9729 elem_type := g.infer_array_elem_type_from_expr(lhs.lhs)
9730 if elem_type == 'u8' || elem_type == 'byte' {
9731 return 'Array_u8__bytestr'
9732 }
9733 }
9734 if method_name == 'bytestr' && lhs.lhs is ast.IndexExpr {
9735 index_lhs := lhs.lhs as ast.IndexExpr
9736 if index_lhs.expr is ast.RangeExpr {
9737 elem_type := g.infer_array_elem_type_from_expr(index_lhs.lhs)
9738 if elem_type == 'u8' || elem_type == 'byte' {
9739 return 'Array_u8__bytestr'
9740 }
9741 }
9742 }
9743 if method_name == 'bytestr' && lhs.lhs is ast.CallExpr {
9744 recv_call := lhs.lhs as ast.CallExpr
9745 if recv_call.lhs is ast.Ident
9746 && recv_call.lhs.name in ['array__slice', 'array__slice_ni', 'builtin__array__slice', 'builtin__array__slice_ni']
9747 && recv_call.args.len > 0 {
9748 elem_type := g.infer_array_elem_type_from_expr(recv_call.args[0])
9749 if elem_type == 'u8' || elem_type == 'byte' {
9750 return 'Array_u8__bytestr'
9751 }
9752 }
9753 }
9754 mut base_type := g.method_receiver_base_type(lhs.lhs)
9755 if base_type == '' {
9756 base_type = g.get_expr_type(lhs.lhs).trim_space().trim_right('*')
9757 }
9758 if base_type == '' {
9759 base_type = g.receiver_local_base_type(lhs.lhs)
9760 }
9761 name = '${base_type}__${method_name}'
9762 if receiver_type := g.get_receiver_expr_type_for_method(lhs.lhs) {
9763 if concrete_method := g.resolve_method_on_concrete_type(receiver_type,
9764 method_name)
9765 {
9766 name = concrete_method
9767 }
9768 }
9769 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
9770 if declared_method := g.resolve_method_on_declared_receiver_type(lhs.lhs,
9771 method_name)
9772 {
9773 name = declared_method
9774 }
9775 }
9776 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
9777 if raw_type := g.get_raw_type(lhs.lhs) {
9778 raw_c_type := strip_pointer_type_name(g.types_type_to_c(raw_type))
9779 if raw_c_type != '' && raw_c_type != base_type {
9780 if raw_method := g.resolve_method_on_concrete_type(raw_c_type,
9781 method_name)
9782 {
9783 name = raw_method
9784 }
9785 }
9786 }
9787 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
9788 if alias_base := g.alias_base_c_type(base_type) {
9789 alias_name := '${alias_base}__${method_name}'
9790 if alias_name in g.fn_return_types || alias_name in g.fn_param_is_ptr {
9791 name = alias_name
9792 }
9793 }
9794 }
9795 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
9796 if map_method := g.map_runtime_method_name(lhs.lhs, method_name) {
9797 name = map_method
9798 }
9799 }
9800 }
9801 }
9802 }
9803 }
9804 if name == 'builtin__new_array_from_c_array_noscan' {
9805 name = 'new_array_from_c_array'
9806 }
9807 if name == 'builtin__array_push_noscan' {
9808 name = 'array__push'
9809 }
9810 if name == 'panic' {
9811 name = 'v_panic'
9812 }
9813 if name == 'voidptr__vbytes' {
9814 name = 'void__vbytes'
9815 }
9816 if name == 'int__bytestr' {
9817 name = 'Array_u8__bytestr'
9818 }
9819 if name.ends_with('__bytes') && name !in g.fn_return_types && name !in g.fn_param_is_ptr {
9820 if 'string__bytes' in g.fn_return_types || 'string__bytes' in g.fn_param_is_ptr {
9821 name = 'string__bytes'
9822 }
9823 }
9824 if is_c_runtime_function(name) {
9825 return name
9826 }
9827 if name != '' && g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin'
9828 && !name.contains('__') {
9829 qualified := '${g.cur_module}__${name}'
9830 if qualified in g.fn_return_types || qualified in g.fn_param_is_ptr {
9831 return qualified
9832 }
9833 if name in g.fn_return_types || name in g.fn_param_is_ptr {
9834 return name
9835 }
9836 return qualified
9837 }
9838 return name
9839}
9840
9841fn (mut g Gen) resolved_qualified_selector_method_name(method_name string) ?string {
9842 if !method_name.contains('__') {
9843 return none
9844 }
9845 base_name := if method_name.contains('_T_') {
9846 method_name.all_before('_T_')
9847 } else if method_name.ends_with('_T') {
9848 method_name[..method_name.len - 2]
9849 } else {
9850 method_name
9851 }
9852 if base_name in g.fn_return_types || base_name in g.fn_param_is_ptr
9853 || g.has_specialized_fn_base(base_name) || g.has_generic_fn_decl_by_base_name(base_name) {
9854 return method_name
9855 }
9856 for candidate in generic_call_decl_candidates(base_name) {
9857 if candidate in g.generic_fn_decl_index {
9858 return method_name
9859 }
9860 }
9861 if _ := g.generic_call_decl_from_lhs(ast.Expr(ast.Ident{
9862 name: base_name
9863 }))
9864 {
9865 return method_name
9866 }
9867 return none
9868}
9869
9870fn (mut g Gen) selector_lhs_is_static_type_ident(name string) bool {
9871 if name.len == 0 || name[0] < `A` || name[0] > `Z` {
9872 return false
9873 }
9874 if _ := g.get_local_var_c_type(name) {
9875 return false
9876 }
9877 if g.is_module_ident(name) {
9878 return false
9879 }
9880 return true
9881}
9882
9883fn (mut g Gen) resolve_selector_module_call_name(lhs ast.SelectorExpr) ?string {
9884 if lhs.lhs !is ast.Ident {
9885 return none
9886 }
9887 lhs_ident := lhs.lhs as ast.Ident
9888 if lhs_ident.name == 'C' {
9889 return none
9890 }
9891 if _ := g.get_local_var_c_type(lhs_ident.name) {
9892 return none
9893 }
9894 mod_name := g.resolve_module_name(lhs_ident.name)
9895 name := '${mod_name}__${sanitize_fn_ident(lhs.rhs.name)}'
9896 if g.is_module_ident(lhs_ident.name) || name in g.fn_return_types || name in g.fn_param_is_ptr
9897 || g.is_module_local_fn(name) || g.has_specialized_fn_base(name)
9898 || g.has_generic_fn_decl_by_base_name(name) {
9899 return name
9900 }
9901 return none
9902}
9903
9904fn (mut g Gen) ident_call_name_is_plain_module_fn(name string) bool {
9905 if !name.contains('__') {
9906 return false
9907 }
9908 mod_name := name.all_before_last('__')
9909 if mod_name == '' || mod_name.contains('__') {
9910 return false
9911 }
9912 if mod_name == g.cur_module || mod_name in g.cur_import_modules {
9913 return true
9914 }
9915 if g.env != unsafe { nil } {
9916 if _ := g.env.get_scope(mod_name) {
9917 return true
9918 }
9919 }
9920 return false
9921}
9922
9923fn (mut g Gen) new_array_from_c_array_elem_type(call_args []ast.Expr) string {
9924 if call_args.len < 4 {
9925 return ''
9926 }
9927 array_data := strip_expr_wrappers(call_args[3])
9928 if array_data !is ast.ArrayInitExpr {
9929 return ''
9930 }
9931 elem_type := g.infer_array_elem_type_from_expr(array_data)
9932 if elem_type != '' && elem_type != 'array' && elem_type != 'int' {
9933 return elem_type
9934 }
9935 return ''
9936}
9937
9938fn (mut g Gen) get_receiver_expr_type_for_method(expr ast.Expr) ?string {
9939 unwrapped_expr := strip_expr_wrappers(expr)
9940 if unwrapped_expr is ast.PrefixExpr && unwrapped_expr.op in [.amp, .mul] {
9941 if receiver_type := g.get_receiver_expr_type_for_method(unwrapped_expr.expr) {
9942 return receiver_type
9943 }
9944 }
9945 if expr is ast.CallExpr {
9946 if ret := g.get_call_return_type(expr.lhs, expr.args) {
9947 if ret != '' && ret != 'int' {
9948 return ret.trim_right('*')
9949 }
9950 }
9951 }
9952 if active_concrete := g.active_generic_concrete_from_arg(expr) {
9953 active_type := g.types_type_to_c(active_concrete).trim_space()
9954 if active_type != '' && active_type != 'int' && active_type != 'void' {
9955 return active_type.trim_right('*')
9956 }
9957 }
9958 if expr is ast.Ident {
9959 local_type := (g.get_local_var_c_type(expr.name) or { '' }).trim_space()
9960 if local_type != '' && local_type != 'int' && local_type != 'void' {
9961 return local_type.trim_right('*')
9962 }
9963 }
9964 mut receiver_type := g.get_expr_type(expr).trim_space()
9965 if expr is ast.SelectorExpr {
9966 declared_type := g.selector_declared_field_type(expr).trim_space()
9967 declared_base := strip_pointer_type_name(declared_type)
9968 receiver_base := strip_pointer_type_name(receiver_type)
9969 if declared_type != '' && declared_type != 'int' && declared_type != 'void'
9970 && (receiver_type == '' || receiver_type == 'int'
9971 || (declared_type.contains('_T_') && (!receiver_type.contains('_T_')
9972 || declared_base != receiver_base))) {
9973 receiver_type = declared_type
9974 }
9975 }
9976 if (receiver_type == '' || receiver_type == 'int') && expr is ast.Ident {
9977 receiver_type = (g.get_local_var_c_type(expr.name) or { '' }).trim_space()
9978 }
9979 if receiver_type == '' || receiver_type == 'int' {
9980 if raw := g.get_raw_type(expr) {
9981 receiver_type = g.types_type_to_c(raw).trim_space()
9982 }
9983 }
9984 if receiver_type.ends_with('*') {
9985 receiver_type = receiver_type[..receiver_type.len - 1]
9986 }
9987 if receiver_type == '' || receiver_type == 'int' {
9988 return none
9989 }
9990 return receiver_type
9991}
9992
9993fn (mut g Gen) resolve_ident_receiver_method_call_name(name string, lhs ast.Expr, call_args []ast.Expr) string {
9994 if lhs !is ast.Ident || call_args.len == 0 || !name.contains('__')
9995 || g.ident_call_name_is_plain_module_fn(name) {
9996 return name
9997 }
9998 if name.ends_with('__str') && name in g.fn_return_types {
9999 return name
10000 }
10001 method_name := name.all_after_last('__')
10002 declared_owner := name.all_before_last('__')
10003 if declared_owner != '' {
10004 if param_types := g.fn_param_types[name] {
10005 if param_types.len == 0
10006 || !method_receiver_c_type_matches_owner(param_types[0], declared_owner) {
10007 return name
10008 }
10009 }
10010 }
10011 if receiver_type := g.get_receiver_expr_type_for_method(call_args[0]) {
10012 if concrete_method := g.resolve_method_on_concrete_type(receiver_type, method_name) {
10013 return concrete_method
10014 }
10015 if declared_owner != '' {
10016 if declared_method := g.resolve_method_on_concrete_type(declared_owner, method_name) {
10017 if declared_method == name
10018 && !method_receiver_c_type_matches_owner(receiver_type, declared_owner) {
10019 return name
10020 }
10021 }
10022 }
10023 }
10024 return name
10025}
10026
10027fn method_receiver_c_type_matches_owner(receiver_type string, owner string) bool {
10028 mut lhs := strip_pointer_type_name(unmangle_c_ptr_type(receiver_type.trim_space()))
10029 mut rhs := strip_pointer_type_name(unmangle_c_ptr_type(owner.trim_space()))
10030 if lhs == '' || rhs == '' {
10031 return false
10032 }
10033 if lhs == rhs {
10034 return true
10035 }
10036 if lhs.starts_with('${rhs}_T_') || rhs.starts_with('${lhs}_T_') {
10037 return true
10038 }
10039 return short_type_name(lhs) == short_type_name(rhs)
10040}
10041
10042fn (mut g Gen) get_call_return_type(lhs ast.Expr, call_args []ast.Expr) ?string {
10043 if ret := g.json_decode_result_c_type(lhs, call_args) {
10044 return ret
10045 }
10046 if lhs is ast.SelectorExpr {
10047 if lhs.lhs is ast.Ident && lhs.lhs.name == 'C' {
10048 if ret := g.fn_return_types[lhs.rhs.name] {
10049 return ret
10050 }
10051 // Fallbacks for common C symbols used by vlib/os where declaration
10052 // metadata can be missing in transformed environments.
10053 match lhs.rhs.name {
10054 'open', 'chdir', 'proc_pidpath' { return 'int' }
10055 'signal' { return 'void*' }
10056 else {}
10057 }
10058 }
10059 }
10060 if lhs is ast.SelectorExpr {
10061 if lhs.rhs.name in ['hash_fn', 'key_eq_fn', 'clone_fn', 'free_fn'] {
10062 base_type := g.method_receiver_base_type(lhs.lhs)
10063 if base_type == 'map' || base_type.starts_with('Map_') {
10064 return match lhs.rhs.name {
10065 'hash_fn' { 'u64' }
10066 'key_eq_fn' { 'bool' }
10067 else { 'void' }
10068 }
10069 }
10070 if ret := g.unique_v_method_return_type(sanitize_fn_ident(lhs.rhs.name)) {
10071 return ret
10072 }
10073 }
10074 }
10075 // Prefer explicit fn_return_types registration (includes result/option wrappers)
10076 // over fn_pointer_return_type (which may return the unwrapped type from the checker).
10077 if explicit_name := g.explicit_generic_call_name_for_lhs(lhs, call_args) {
10078 mut c_explicit_name := explicit_name
10079 g.ensure_specialized_call_signature(c_explicit_name)
10080 if c_explicit_name.contains('_T_') && !c_explicit_name.contains('__') {
10081 qualified := g.qualify_local_call_name(c_explicit_name)
10082 g.ensure_specialized_call_signature(qualified)
10083 if qualified in g.fn_return_types || qualified in g.fn_param_is_ptr {
10084 c_explicit_name = qualified
10085 }
10086 }
10087 if ret := g.fn_return_types[c_explicit_name] {
10088 return ret
10089 }
10090 }
10091 mut c_name := g.resolve_call_name(lhs, call_args.len)
10092 if c_name != '' {
10093 c_name = g.resolve_ident_receiver_method_call_name(c_name, lhs, call_args)
10094 if current_receiver_name := g.same_receiver_specialized_method_name(c_name) {
10095 c_name = current_receiver_name
10096 }
10097 // Generic function bodies can temporarily resolve ordinary methods as
10098 // specialized names; use the unspecialized base for well-known return types.
10099 c_name_base := if c_name.contains('_T_') { c_name.all_before('_T_') } else { c_name }
10100 if c_name_base == 'Array_u8__bytestr' || c_name_base.ends_with('__bytestr') {
10101 return 'string'
10102 }
10103 if c_name.contains('_T_') && c_name !in g.fn_return_types && c_name !in g.fn_param_is_ptr {
10104 base_name := c_name.all_before('_T_')
10105 if (base_name in g.fn_return_types || base_name in g.fn_param_is_ptr)
10106 && !g.has_generic_fn_decl_by_base_name(base_name) {
10107 c_name = base_name
10108 }
10109 }
10110 if ret := g.fn_decl_return_type_by_c_name(c_name) {
10111 return ret
10112 }
10113 if ret := g.fn_return_types[c_name] {
10114 return ret
10115 }
10116 if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin'
10117 && c_name.contains('__') && !c_name.starts_with(g.cur_module + '__') {
10118 qualified_c_name := g.cur_module + '__' + c_name
10119 if ret := g.fn_return_types[qualified_c_name] {
10120 return ret
10121 }
10122 if ret := g.fn_decl_return_type_by_c_name(qualified_c_name) {
10123 return ret
10124 }
10125 }
10126 if c_name.contains('__') {
10127 method_name := c_name.all_after_last('__')
10128 if ret := g.unique_v_method_return_type(method_name) {
10129 return ret
10130 }
10131 }
10132 }
10133 if lhs is ast.SelectorExpr {
10134 mut is_static_selector := false
10135 if lhs.lhs is ast.Ident {
10136 is_static_selector = g.is_module_ident(lhs.lhs.name) || g.is_type_name(lhs.lhs.name)
10137 || g.selector_lhs_is_static_type_ident(lhs.lhs.name)
10138 }
10139 if !is_static_selector {
10140 method_name := sanitize_fn_ident(lhs.rhs.name)
10141 if receiver_type := g.get_receiver_expr_type_for_method(lhs.lhs) {
10142 if concrete_method := g.resolve_method_on_concrete_type(receiver_type, method_name) {
10143 if ret := g.fn_decl_return_type_by_c_name(concrete_method) {
10144 return ret
10145 }
10146 if ret := g.fn_return_types[concrete_method] {
10147 return ret
10148 }
10149 }
10150 }
10151 base_type := g.method_receiver_base_type(lhs.lhs)
10152 if concrete_method := g.resolve_method_on_concrete_type(base_type, method_name) {
10153 if ret := g.fn_decl_return_type_by_c_name(concrete_method) {
10154 return ret
10155 }
10156 if ret := g.fn_return_types[concrete_method] {
10157 return ret
10158 }
10159 }
10160 if ret := g.unique_v_method_return_type(method_name) {
10161 return ret
10162 }
10163 if embedded := g.resolve_method_on_embedded_receiver(base_type, lhs.rhs.name) {
10164 if ret := g.fn_return_types[embedded.method_c_name] {
10165 return ret
10166 }
10167 }
10168 // For interface vtable method calls (e.g., w.size() where w is Widget),
10169 // look up the method's return type from interface_methods.
10170 if g.is_interface_type(base_type) {
10171 if method := g.interface_method_by_name(base_type, lhs.rhs.name) {
10172 if method.ret_type != '' {
10173 return method.ret_type
10174 }
10175 }
10176 }
10177 }
10178 }
10179 if lhs is ast.Ident {
10180 fn_ptr_ret := g.fn_pointer_return_type(lhs)
10181 if fn_ptr_ret != '' {
10182 return fn_ptr_ret
10183 }
10184 } else {
10185 mut allow_fn_ptr_lookup := true
10186 if lhs is ast.SelectorExpr {
10187 if lhs.lhs is ast.Ident
10188 && (g.is_module_ident(lhs.lhs.name) || g.is_type_name(lhs.lhs.name)
10189 || g.selector_lhs_is_static_type_ident(lhs.lhs.name)) {
10190 // Module/type selectors represent direct function/method calls, not
10191 // function-pointer values.
10192 allow_fn_ptr_lookup = false
10193 }
10194 }
10195 if allow_fn_ptr_lookup {
10196 fn_ptr_ret := g.fn_pointer_return_type(lhs)
10197 if fn_ptr_ret != '' {
10198 return fn_ptr_ret
10199 }
10200 }
10201 }
10202 if c_name == '' {
10203 if lhs is ast.Ident {
10204 match lhs.name {
10205 'open', 'chdir', 'proc_pidpath' { return 'int' }
10206 'signal' { return 'void*' }
10207 else {}
10208 }
10209 }
10210 return none
10211 }
10212 match c_name {
10213 'open', 'chdir', 'proc_pidpath' { return 'int' }
10214 'signal' { return 'void*' }
10215 else {}
10216 }
10217
10218 return none
10219}
10220
10221fn (mut g Gen) fn_decl_return_type_by_c_name(target_name string) ?string {
10222 if target_name == '' {
10223 return none
10224 }
10225 if ret := g.v_fn_return_types[target_name] {
10226 return ret
10227 }
10228 return none
10229}
10230
10231fn (g &Gen) knows_fn_signature(fn_name string) bool {
10232 return fn_name in g.fn_return_types || fn_name in g.v_fn_return_types
10233 || fn_name in g.fn_param_is_ptr || fn_name in g.fn_param_types
10234 || g.has_specialized_fn_base(fn_name)
10235}
10236
10237// v_method_ret_ambiguous marks an index entry whose method short-name maps to >1 distinct V
10238// return type (the original scan returned none for that case). A control char can't be a real type.
10239const v_method_ret_ambiguous = '\x02'
10240
10241// build_v_method_return_index indexes v_fn_return_types by method short-name (all_after_last('__')),
10242// replacing the per-call scan in unique_v_method_return_type. v_fn_return_types is final after
10243// collect_fn_signatures_to_fixed_point, so this is built once there.
10244fn (mut g Gen) build_v_method_return_index() {
10245 mut idx := map[string]string{}
10246 for fn_name, ret in g.v_fn_return_types {
10247 m := fn_name.all_after_last('__')
10248 if m == '' {
10249 continue
10250 }
10251 if existing := idx[m] {
10252 if existing != ret && existing != v_method_ret_ambiguous {
10253 idx[m] = v_method_ret_ambiguous
10254 }
10255 } else {
10256 idx[m] = ret
10257 }
10258 }
10259 g.v_method_return_index = idx.move()
10260}
10261
10262fn (g &Gen) unique_v_method_return_type(method_name string) ?string {
10263 if method_name == '' {
10264 return none
10265 }
10266 // Fast path: index lookup. The scan matched fn_name == method_name OR
10267 // fn_name.ends_with('__${method_name}') (ends_with('___...') is subsumed), which for a
10268 // method without '__' is exactly all_after_last('__') == method_name.
10269 if !method_name.contains('__') {
10270 if ret := g.v_method_return_index[method_name] {
10271 if ret != '' && ret != v_method_ret_ambiguous {
10272 return ret
10273 }
10274 }
10275 if g.v_method_return_index.len == 0 {
10276 return g.scan_unique_v_method_return_type(method_name)
10277 }
10278 return none
10279 }
10280 return g.scan_unique_v_method_return_type(method_name)
10281}
10282
10283fn (g &Gen) scan_unique_v_method_return_type(method_name string) ?string {
10284 // Rare fallback for method names containing '__'.
10285 mut found_name := ''
10286 mut found_ret := ''
10287 for fn_name, ret in g.v_fn_return_types {
10288 if fn_name != method_name && !fn_name.ends_with('__${method_name}')
10289 && !fn_name.ends_with('___${method_name}') {
10290 continue
10291 }
10292 if found_name != '' && found_name != fn_name && found_ret != ret {
10293 return none
10294 }
10295 found_name = fn_name
10296 found_ret = ret
10297 }
10298 if found_ret != '' {
10299 return found_ret
10300 }
10301 return none
10302}
10303
10304fn (mut g Gen) resolve_method_on_concrete_type(receiver_type string, method_name string) ?string {
10305 clean_type := strip_pointer_type_name(unmangle_c_ptr_type(receiver_type))
10306 if clean_type == '' || method_name == '' {
10307 return none
10308 }
10309 sanitized := sanitize_fn_ident(method_name)
10310 if g.cur_module != '' && clean_type.contains('__')
10311 && clean_type.all_before_last('__') == g.cur_module {
10312 candidate := '${clean_type}___${sanitized}'
10313 if g.knows_fn_signature(candidate) {
10314 return candidate
10315 }
10316 }
10317 if clean_type.contains('_T_') {
10318 specialized_candidate := '${clean_type}__${sanitized}'
10319 if g.knows_fn_signature(specialized_candidate) {
10320 return specialized_candidate
10321 }
10322 base_type := clean_type.all_before('_T_')
10323 base_method := '${base_type}__${sanitized}'
10324 if base_type in g.specialized_fn_bases || base_method in g.generic_fn_decl_index {
10325 return specialized_candidate
10326 }
10327 }
10328 candidate := '${clean_type}__${sanitized}'
10329 if g.knows_fn_signature(candidate) {
10330 return candidate
10331 }
10332 short_name := short_type_name(clean_type)
10333 if short_name != '' && short_name != clean_type {
10334 short_candidate := '${short_name}__${sanitized}'
10335 if g.knows_fn_signature(short_candidate) {
10336 return short_candidate
10337 }
10338 }
10339 if g.cur_module != '' && !clean_type.contains('__') {
10340 module_candidate := '${g.cur_module}__${clean_type}__${sanitized}'
10341 if g.knows_fn_signature(module_candidate) {
10342 return module_candidate
10343 }
10344 }
10345 g.ensure_specialized_receiver_method_index()
10346 if clean_type in g.specialized_fn_bases {
10347 if specialized := g.resolve_specialized_receiver_method(clean_type, sanitized) {
10348 return specialized
10349 }
10350 }
10351 if short_name != '' && short_name != clean_type && short_name in g.specialized_fn_bases {
10352 if specialized := g.resolve_specialized_receiver_method(short_name, sanitized) {
10353 return specialized
10354 }
10355 }
10356 return none
10357}
10358
10359fn (mut g Gen) resolve_method_on_declared_receiver_type(receiver ast.Expr, method_name string) ?string {
10360 raw_type := g.get_raw_type(receiver) or { return none }
10361 raw_c_type := strip_pointer_type_name(g.types_type_to_c(raw_type))
10362 if raw_c_type == '' || raw_c_type == 'int' || raw_c_type == 'void' {
10363 return none
10364 }
10365 if method := g.resolve_method_on_concrete_type(raw_c_type, method_name) {
10366 return method
10367 }
10368 return none
10369}
10370
10371fn (mut g Gen) resolve_specialized_receiver_method(receiver_type string, method_name string) ?string {
10372 if receiver_type == '' || method_name == '' {
10373 return none
10374 }
10375 if g.specialized_fn_bases.len > 0 && receiver_type !in g.specialized_fn_bases {
10376 return none
10377 }
10378 // Fast path: consult the incremental (base|method -> specialized fn) index that
10379 // remember_specialized_fn_base maintains on every registration. It replaces a per-key
10380 // linear scan over the whole fn_return_types + fn_param_is_ptr tables and, unlike a
10381 // once-built snapshot, stays correct when a second specialization is registered after the
10382 // first lookup (which turns a previously unambiguous (base, method) ambiguous). The scan it
10383 // replaces matched `candidate.starts_with('${receiver_type}_T_') &&
10384 // candidate.ends_with('__${method_name}')`, which for a base receiver_type (no `_T_`) and a
10385 // simple method (no `__`) is exactly (all_before('_T_') == base) && (all_after_last('__') ==
10386 // method) — i.e. the same keys remember_specialized_receiver_method records.
10387 if !receiver_type.contains('_T_') && !method_name.contains('__') {
10388 g.ensure_specialized_receiver_method_index()
10389 key := specialized_receiver_method_key(receiver_type, method_name)
10390 if key in g.specialized_receiver_method_miss {
10391 return none
10392 }
10393 if key in g.specialized_receiver_method_ambiguous {
10394 return none
10395 }
10396 if found := g.specialized_receiver_methods[key] {
10397 return found
10398 }
10399 g.specialized_receiver_method_miss[key] = true
10400 return none
10401 }
10402 // Rare fallback: receiver_type/method contains the `_T_`/`__` separator the index keys on.
10403 return g.scan_specialized_receiver_method(receiver_type, method_name)
10404}
10405
10406fn (g &Gen) scan_specialized_receiver_method(receiver_type string, method_name string) ?string {
10407 prefix := '${receiver_type}_T_'
10408 suffix := '__${method_name}'
10409 mut found := ''
10410 for candidate, _ in g.fn_return_types {
10411 if candidate.starts_with(prefix) && candidate.ends_with(suffix) {
10412 if found != '' && found != candidate {
10413 return none
10414 }
10415 found = candidate
10416 }
10417 }
10418 for candidate, _ in g.fn_param_is_ptr {
10419 if candidate.starts_with(prefix) && candidate.ends_with(suffix) {
10420 if found != '' && found != candidate {
10421 return none
10422 }
10423 found = candidate
10424 }
10425 }
10426 if found != '' {
10427 return found
10428 }
10429 return none
10430}
10431
10432struct EmbeddedMethodResolution {
10433 owner string
10434 method_c_name string
10435}
10436
10437fn (mut g Gen) resolve_method_on_embedded_decl(info StructDeclInfo, method_name string) ?EmbeddedMethodResolution {
10438 saved_module := g.cur_module
10439 saved_file_name := g.cur_file_name
10440 defer {
10441 g.cur_module = saved_module
10442 g.cur_file_name = saved_file_name
10443 }
10444 g.set_struct_info_context(info)
10445 for emb in info.decl.embedded {
10446 embedded_c_type := g.expr_type_to_c(emb)
10447 owner := embedded_owner_field_name(embedded_c_type)
10448 if method_c_name := g.resolve_method_on_concrete_type(embedded_c_type, method_name) {
10449 return EmbeddedMethodResolution{
10450 owner: owner
10451 method_c_name: method_c_name
10452 }
10453 }
10454 if embedded_info := g.find_struct_decl_info_by_c_name(embedded_c_type) {
10455 if nested := g.resolve_method_on_embedded_decl(embedded_info, method_name) {
10456 return EmbeddedMethodResolution{
10457 owner: owner
10458 method_c_name: nested.method_c_name
10459 }
10460 }
10461 g.set_struct_info_context(info)
10462 }
10463 }
10464 return none
10465}
10466
10467fn (mut g Gen) resolve_method_on_embedded_receiver(receiver_type string, method_name string) ?EmbeddedMethodResolution {
10468 clean_type := strip_pointer_type_name(unmangle_c_ptr_type(receiver_type))
10469 if clean_type == '' || method_name == '' {
10470 return none
10471 }
10472 mut lookup_name := clean_type
10473 mut struct_type := g.lookup_struct_type_by_c_name(lookup_name)
10474 if struct_type.fields.len == 0 && struct_type.embedded.len == 0 {
10475 if alias_base := g.alias_base_c_type(clean_type) {
10476 lookup_name = alias_base
10477 struct_type = g.lookup_struct_type_by_c_name(alias_base)
10478 }
10479 }
10480 if struct_type.fields.len == 0 && struct_type.embedded.len == 0 {
10481 if decl_info := g.find_struct_decl_info_by_c_name(lookup_name) {
10482 return g.resolve_method_on_embedded_decl(decl_info, method_name)
10483 }
10484 return none
10485 }
10486 for embedded in struct_type.embedded {
10487 embedded_c_type := g.types_type_to_c(embedded)
10488 owner := embedded_owner_field_name(embedded_c_type)
10489 if method_c_name := g.resolve_method_on_concrete_type(embedded_c_type, method_name) {
10490 return EmbeddedMethodResolution{
10491 owner: owner
10492 method_c_name: method_c_name
10493 }
10494 }
10495 if nested := g.resolve_method_on_embedded_receiver(embedded_c_type, method_name) {
10496 return EmbeddedMethodResolution{
10497 owner: owner
10498 method_c_name: nested.method_c_name
10499 }
10500 }
10501 }
10502 if decl_info := g.find_struct_decl_info_by_c_name(lookup_name) {
10503 return g.resolve_method_on_embedded_decl(decl_info, method_name)
10504 }
10505 return none
10506}
10507
10508fn (mut g Gen) gen_embedded_receiver_arg(receiver ast.Expr, owner string, callee_name string) {
10509 base_receiver := if receiver is ast.ParenExpr {
10510 receiver.expr
10511 } else {
10512 receiver
10513 }
10514 mut expect_ptr := false
10515 if ptr_params := g.fn_param_is_ptr[callee_name] {
10516 if ptr_params.len > 0 && ptr_params[0] {
10517 expect_ptr = true
10518 }
10519 }
10520 if expect_ptr {
10521 g.sb.write_string('&')
10522 }
10523 mut use_ptr := g.expr_is_pointer(base_receiver)
10524 if !use_ptr {
10525 receiver_type := g.get_expr_type(base_receiver)
10526 use_ptr = receiver_type.ends_with('*')
10527 }
10528 if !use_ptr && base_receiver is ast.Ident {
10529 if local_type := g.get_local_var_c_type(base_receiver.name) {
10530 use_ptr = local_type.ends_with('*')
10531 }
10532 if !use_ptr {
10533 if _ := g.cur_fn_mut_params[base_receiver.name] {
10534 use_ptr = true
10535 }
10536 }
10537 }
10538 g.expr(receiver)
10539 sep := if use_ptr { '->' } else { '.' }
10540 g.sb.write_string('${sep}${escape_c_keyword(owner)}')
10541}
10542
10543fn (mut g Gen) is_iface_object_arg_for_receiver(arg ast.Expr, receiver ast.Expr) bool {
10544 if arg !is ast.SelectorExpr {
10545 return false
10546 }
10547 sel := arg as ast.SelectorExpr
10548 if sel.rhs.name != '_object' {
10549 return false
10550 }
10551 receiver_src := g.expr_to_string(receiver)
10552 sel_lhs_src := g.expr_to_string(sel.lhs)
10553 return receiver_src != '' && receiver_src == sel_lhs_src
10554}
10555
10556fn (mut g Gen) interface_call_args_without_object(receiver ast.Expr, args []ast.Expr) []ast.Expr {
10557 if args.len > 0 && g.is_iface_object_arg_for_receiver(args[0], receiver) {
10558 mut out := []ast.Expr{cap: args.len - 1}
10559 for i in 1 .. args.len {
10560 out << args[i]
10561 }
10562 return out
10563 }
10564 return args
10565}
10566
10567fn (mut g Gen) gen_interface_call_arg(expected_type string, arg ast.Expr) {
10568 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
10569 mut arg_type := g.get_expr_type(base_arg).trim_space()
10570
10571 if (arg_type == '' || arg_type == 'int') && base_arg is ast.Ident {
10572 arg_type = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
10573 }
10574 // For Ident args, check if the local var C type includes pointer info (e.g. from mut params)
10575 // that get_expr_type doesn't provide
10576 if base_arg is ast.Ident && !arg_type.ends_with('*') {
10577 if local_c := g.get_local_var_c_type(base_arg.name) {
10578 if local_c.ends_with('*') {
10579 arg_type = local_c
10580 }
10581 }
10582 }
10583 is_pointer_arg := g.expr_is_pointer(base_arg) || arg_type.ends_with('*')
10584 // Check interface conversion FIRST before auto-deref/address-of, since
10585 // derefing a Stack* to Stack doesn't help when Layout is expected.
10586 expected_base := expected_type.trim_right('*')
10587 arg_base := arg_type.trim_right('*')
10588 if g.is_interface_type(expected_base) && arg_base != '' && arg_base != 'int'
10589 && arg_base != expected_base && !g.is_interface_type(arg_base)
10590 && !arg_base.starts_with('Array_') && arg_base != 'array' && !arg_base.starts_with('Map_')
10591 && arg_base != 'map' {
10592 if expected_type.ends_with('*') {
10593 if g.gen_heap_interface_cast(expected_base, base_arg) {
10594 return
10595 }
10596 } else {
10597 if g.gen_interface_cast(expected_base, base_arg) {
10598 return
10599 }
10600 }
10601 }
10602 if !expected_type.ends_with('*') && expected_type.starts_with('Array_')
10603 && arg_type.ends_with('*') {
10604 g.sb.write_string('(*')
10605 g.expr(base_arg)
10606 g.sb.write_string(')')
10607 return
10608 }
10609 if !expected_type.ends_with('*') && is_pointer_arg && g.should_auto_deref(base_arg) {
10610 g.sb.write_string('(*')
10611 g.expr(base_arg)
10612 g.sb.write_string(')')
10613 return
10614 }
10615 if expected_type.ends_with('*') && !is_pointer_arg {
10616 g.gen_addr_of_expr(base_arg, expected_type.trim_right('*'))
10617 return
10618 }
10619 g.expr(arg)
10620}
10621
10622fn (mut g Gen) smartcast_c_type_from_scope(expr ast.Expr) ?string {
10623 if mut fn_scope := g.ensure_cur_fn_scope() {
10624 expr_name := expr.name()
10625 if expr_name != '' {
10626 if cast_type := fn_scope.lookup_field_smartcast(expr_name) {
10627 c_type := g.types_type_to_c(cast_type)
10628 if c_type != '' && c_type != 'int' {
10629 return c_type
10630 }
10631 }
10632 }
10633 if expr is ast.SelectorExpr {
10634 lhs_name := expr.lhs.name()
10635 if lhs_name != '' {
10636 if cast_type := fn_scope.lookup_field_smartcast(lhs_name) {
10637 if field_type := selector_struct_field_type_from_type(cast_type, expr.rhs.name) {
10638 c_type := g.types_type_to_c(field_type)
10639 if c_type != '' && c_type != 'int' {
10640 return c_type
10641 }
10642 }
10643 }
10644 }
10645 }
10646 }
10647 return none
10648}
10649
10650fn (mut g Gen) gen_array_contains_call(name string, call_args []ast.Expr) bool {
10651 if call_args.len != 2 {
10652 return false
10653 }
10654 if name.starts_with('Array_fixed_') && name.ends_with('_contains') {
10655 fixed_type := name[..name.len - '_contains'.len]
10656 elem_type, fixed_len := parse_fixed_array_elem_type(fixed_type)
10657 if fixed_len <= 0 || elem_type == '' {
10658 return false
10659 }
10660 tmp := '_fixed_contains_${g.tmp_counter}'
10661 g.tmp_counter++
10662 g.sb.write_string('({ bool ${tmp} = false; for (int _i = 0; _i < ${fixed_len}; _i++) { if (')
10663 if elem_type == 'string' {
10664 g.sb.write_string('string__eq(')
10665 g.expr(call_args[0])
10666 g.sb.write_string('[_i], ')
10667 g.expr(call_args[1])
10668 g.sb.write_string(')')
10669 } else {
10670 g.expr(call_args[0])
10671 g.sb.write_string('[_i] == ')
10672 g.expr(call_args[1])
10673 }
10674 g.sb.write_string(') { ${tmp} = true; break; } } ${tmp}; })')
10675 return true
10676 }
10677 if !name.starts_with('array__contains_') {
10678 return false
10679 }
10680 elem_type := g.infer_array_contains_elem_type(name, call_args)
10681 g.sb.write_string('array__contains(')
10682 g.expr(call_args[0])
10683 g.sb.write_string(', ')
10684 g.gen_addr_of_expr(call_args[1], elem_type)
10685 g.sb.write_string(')')
10686 return true
10687}
10688
10689struct ArraySelectorReceiverInfo {
10690 elem_type string
10691 is_ptr bool
10692}
10693
10694fn (mut g Gen) array_selector_receiver_info(receiver ast.Expr) ?ArraySelectorReceiverInfo {
10695 mut receiver_type := g.method_receiver_base_type(receiver).trim_space()
10696 if receiver_type == '' || receiver_type == 'int' {
10697 receiver_type = g.get_expr_type(receiver).trim_space()
10698 }
10699 if receiver_type == '' || receiver_type == 'int' {
10700 if raw_type := g.get_raw_type(receiver) {
10701 receiver_type = g.types_type_to_c(raw_type).trim_space()
10702 }
10703 }
10704 mut is_ptr := receiver_type.ends_with('*')
10705 mut base_type := receiver_type.trim_right('*')
10706 if base_type == '' || base_type == 'int' {
10707 return none
10708 }
10709 mut array_type := base_type
10710 if !c_type_is_array_value(array_type) {
10711 if alias_base := g.alias_base_c_type(array_type) {
10712 array_type = alias_base.trim_right('*')
10713 }
10714 }
10715 if !c_type_is_array_value(array_type) {
10716 return none
10717 }
10718 is_ptr = is_ptr || g.expr_is_pointer(receiver)
10719 mut elem_type := array_alias_elem_type(array_type)
10720 if elem_type == '' || elem_type == 'int' {
10721 elem_type = g.infer_array_elem_type_from_expr(receiver).trim_right('*')
10722 }
10723 if elem_type == '' || elem_type == 'int' {
10724 elem_type = 'voidptr'
10725 }
10726 return ArraySelectorReceiverInfo{
10727 elem_type: elem_type
10728 is_ptr: is_ptr
10729 }
10730}
10731
10732fn (mut g Gen) gen_array_selector_receiver_ptr(receiver ast.Expr, info ArraySelectorReceiverInfo) {
10733 g.sb.write_string('(array*)')
10734 if info.is_ptr {
10735 g.expr(receiver)
10736 } else {
10737 g.sb.write_string('&(')
10738 g.expr(receiver)
10739 g.sb.write_string(')')
10740 }
10741}
10742
10743fn (mut g Gen) gen_array_selector_receiver_value(receiver ast.Expr, info ArraySelectorReceiverInfo) {
10744 if info.is_ptr {
10745 g.sb.write_string('(*')
10746 g.expr(receiver)
10747 g.sb.write_string(')')
10748 } else {
10749 g.expr(receiver)
10750 }
10751}
10752
10753fn (mut g Gen) gen_array_selector_method_call(lhs ast.SelectorExpr, args []ast.Expr) bool {
10754 method_name := lhs.rhs.name
10755 if method_name == 'clone' && args.len == 0 {
10756 info := g.array_selector_receiver_info(lhs.lhs) or { return false }
10757 if !info.is_ptr && !g.can_take_address(lhs.lhs) {
10758 mut arr_type := g.expr_array_runtime_type(lhs.lhs).trim_right('*')
10759 if arr_type == '' || arr_type == 'int' || arr_type == 'void' {
10760 arr_type = 'array'
10761 }
10762 tmp := '_arr_clone_tmp_${g.tmp_counter}'
10763 g.tmp_counter++
10764 g.sb.write_string('({ ${arr_type} ${tmp} = ')
10765 g.expr(lhs.lhs)
10766 g.sb.write_string('; array__clone_to_depth((array*)&${tmp}, 0); })')
10767 return true
10768 }
10769 g.sb.write_string('array__clone_to_depth(')
10770 g.gen_array_selector_receiver_ptr(lhs.lhs, info)
10771 g.sb.write_string(', 0)')
10772 return true
10773 }
10774 if method_name == 'contains' && args.len == 1 {
10775 info := g.array_selector_receiver_info(lhs.lhs) or { return false }
10776 g.sb.write_string('array__contains(')
10777 g.gen_array_selector_receiver_value(lhs.lhs, info)
10778 g.sb.write_string(', ')
10779 g.gen_addr_of_expr(args[0], info.elem_type)
10780 g.sb.write_string(')')
10781 return true
10782 }
10783 return false
10784}
10785
10786fn c_type_is_map_value(typ string) bool {
10787 clean := typ.trim_space().trim_right('*')
10788 return clean == 'map' || clean.starts_with('Map_')
10789}
10790
10791fn (mut g Gen) selector_receiver_is_map(receiver ast.Expr) bool {
10792 if raw_type := g.get_raw_type(receiver) {
10793 match raw_type {
10794 types.Map {
10795 return true
10796 }
10797 types.Pointer {
10798 match raw_type.base_type {
10799 types.Map {
10800 return true
10801 }
10802 types.Alias {
10803 return raw_type.base_type.base_type is types.Map
10804 }
10805 else {}
10806 }
10807 }
10808 types.Alias {
10809 return raw_type.base_type is types.Map
10810 }
10811 else {}
10812 }
10813 }
10814 base_type := g.method_receiver_base_type(receiver)
10815 if c_type_is_map_value(base_type) {
10816 return true
10817 }
10818 expr_type := g.get_expr_type(receiver)
10819 return c_type_is_map_value(expr_type)
10820}
10821
10822fn (mut g Gen) map_runtime_method_name(receiver ast.Expr, method_name string) ?string {
10823 if method_name !in ['clone', 'keys', 'values'] || !g.selector_receiver_is_map(receiver) {
10824 return none
10825 }
10826 return 'map__${method_name}'
10827}
10828
10829fn (mut g Gen) gen_builtin_string_method_call(lhs ast.SelectorExpr, args []ast.Expr) bool {
10830 method_name := lhs.rhs.name
10831 if method_name !in ['clone', 'contains', 'starts_with', 'ends_with', 'split', 'index', 'to_lower'] {
10832 return false
10833 }
10834 receiver_type := g.get_expr_type(lhs.lhs).trim_right('*')
10835 if receiver_type != 'string' {
10836 return false
10837 }
10838 g.sb.write_string('string__${sanitize_fn_ident(method_name)}(')
10839 g.expr(lhs.lhs)
10840 for arg in args {
10841 g.sb.write_string(', ')
10842 g.expr(arg)
10843 }
10844 g.sb.write_string(')')
10845 return true
10846}
10847
10848fn (mut g Gen) is_json_decode_call(lhs ast.Expr) bool {
10849 if lhs is ast.SelectorExpr {
10850 if lhs.rhs.name != 'decode' {
10851 return false
10852 }
10853 if lhs.lhs is ast.Ident {
10854 mod_name := g.resolve_module_name(lhs.lhs.name)
10855 return lhs.lhs.name == 'json' || mod_name == 'json'
10856 }
10857 }
10858 if lhs is ast.Ident {
10859 return lhs.name == 'json__decode' || (lhs.name == 'decode' && g.cur_module == 'json')
10860 }
10861 return false
10862}
10863
10864fn (mut g Gen) json_decode_target_c_type(call_args []ast.Expr) string {
10865 if call_args.len == 0 {
10866 return ''
10867 }
10868 mut type_marker := if call_args[0] is ast.ModifierExpr {
10869 (call_args[0] as ast.ModifierExpr).expr
10870 } else {
10871 call_args[0]
10872 }
10873 if type_marker is ast.PrefixExpr && type_marker.op == .amp {
10874 type_marker = type_marker.expr
10875 }
10876 target_type := g.expr_type_to_c(type_marker).trim_space().trim_right('*')
10877 if target_type in ['', 'int', 'void', 'void*', 'voidptr'] {
10878 return ''
10879 }
10880 return target_type
10881}
10882
10883fn (mut g Gen) json_decode_result_c_type(lhs ast.Expr, call_args []ast.Expr) ?string {
10884 if !g.is_json_decode_call(lhs) || call_args.len < 2 {
10885 return none
10886 }
10887 target_type := g.json_decode_target_c_type(call_args)
10888 if target_type == '' {
10889 return none
10890 }
10891 return '_result_${mangle_alias_component(target_type)}'
10892}
10893
10894fn (mut g Gen) gen_json_decode_call(lhs ast.Expr, args []ast.Expr) bool {
10895 result_type := g.json_decode_result_c_type(lhs, args) or { return false }
10896 target_type := g.json_decode_target_c_type(args)
10897 if target_type == '' {
10898 return false
10899 }
10900 tmp_id := g.tmp_counter
10901 g.tmp_counter++
10902 raw_tmp := '_json_decode_raw_${tmp_id}'
10903 res_tmp := '_json_decode_res_${tmp_id}'
10904 ptr_tmp := '_json_decode_ptr_${tmp_id}'
10905 g.mark_called_fn_name('json__decode')
10906 g.sb.write_string('({ _result_voidptr ${raw_tmp} = json__decode(NULL, ')
10907 g.gen_call_arg('json__decode', 1, args[1])
10908 g.sb.write_string('); ${result_type} ${res_tmp} = (${result_type}){ .is_error = ${raw_tmp}.is_error, .err = ${raw_tmp}.err }; if (!${raw_tmp}.is_error) { void* ${ptr_tmp} = (*(void**)(((u8*)(&${raw_tmp}.err)) + sizeof(IError))); if (${ptr_tmp} != NULL) { _result_ok(${ptr_tmp}, (_result*)&${res_tmp}, sizeof(${target_type})); } } ${res_tmp}; })')
10909 return true
10910}
10911
10912fn (mut g Gen) explicit_generic_arg_token(arg ast.Expr) string {
10913 if arg is ast.Ident {
10914 if concrete := g.active_generic_types[arg.name] {
10915 return g.generic_specialization_token_from_type(concrete)
10916 }
10917 }
10918 if raw := g.get_raw_type(arg) {
10919 concrete := g.concrete_type_with_active_generics(raw)
10920 if !type_contains_generic_placeholder(concrete) {
10921 return g.generic_specialization_token_from_type(concrete)
10922 }
10923 }
10924 mut type_name := g.expr_type_to_c(arg).trim_space().trim_right('*')
10925 if (type_name == '' || type_name == 'int') && arg is ast.Ident {
10926 if concrete := g.active_generic_types[arg.name] {
10927 return g.generic_specialization_token_from_type(concrete)
10928 }
10929 type_name = arg.name
10930 }
10931 if type_name == '' || type_name == 'int' {
10932 type_name = arg.name()
10933 }
10934 if concrete := g.active_generic_types[type_name] {
10935 return g.generic_specialization_token_from_type(concrete)
10936 }
10937 if type_name == '' || type_name == 'int' {
10938 return ''
10939 }
10940 return sanitize_generic_token_part(type_name)
10941}
10942
10943fn (mut g Gen) explicit_generic_arg_tokens(args []ast.Expr) []string {
10944 mut tokens := []string{cap: args.len}
10945 for arg in args {
10946 token := g.explicit_generic_arg_token(arg)
10947 if token == '' {
10948 return []string{}
10949 }
10950 tokens << token
10951 }
10952 return tokens
10953}
10954
10955fn explicit_generic_call_name(base_name string, tokens []string) string {
10956 if base_name == '' || tokens.len == 0 {
10957 return ''
10958 }
10959 name := if base_name.contains('_T_') {
10960 base_name.all_before('_T_')
10961 } else if base_name.ends_with('_T') {
10962 base_name[..base_name.len - 2]
10963 } else {
10964 base_name
10965 }
10966 return '${name}_T_${tokens.join('_')}'
10967}
10968
10969fn generic_call_base_name_for_specialization(name string) string {
10970 if name == '' {
10971 return ''
10972 }
10973 if name.ends_with('_T') {
10974 return name[..name.len - 2]
10975 }
10976 if !name.contains('_T_') {
10977 return name
10978 }
10979 method_sep := name.last_index('__') or { return name.all_before('_T_') }
10980 method_start := method_sep + 2
10981 method_part := name[method_start..]
10982 if idx := method_part.index('_T_') {
10983 return name[..method_start + idx]
10984 }
10985 return name
10986}
10987
10988fn (mut g Gen) explicit_generic_base_call_name(base_lhs ast.Expr, args []ast.Expr) ?string {
10989 match base_lhs {
10990 ast.Ident {
10991 return sanitize_fn_ident(base_lhs.name)
10992 }
10993 ast.SelectorExpr {
10994 if base_lhs.lhs is ast.Ident && base_lhs.lhs.name == 'C' {
10995 return none
10996 }
10997 if base_lhs.lhs is ast.Ident && g.is_type_name(base_lhs.lhs.name) {
10998 return '${g.get_qualified_name(base_lhs.lhs.name)}__${sanitize_fn_ident(base_lhs.rhs.name)}'
10999 }
11000 if mod_call_name := g.resolve_selector_module_call_name(base_lhs) {
11001 return mod_call_name
11002 }
11003 name := g.resolve_call_name(base_lhs, args.len)
11004 if name != '' {
11005 return name
11006 }
11007 method_name := sanitize_fn_ident(base_lhs.rhs.name)
11008 mut base_type := g.method_receiver_base_type(base_lhs.lhs)
11009 if base_type == '' {
11010 base_type = g.get_expr_type(base_lhs.lhs).trim_space().trim_right('*')
11011 }
11012 if base_type == '' {
11013 base_type = g.receiver_local_base_type(base_lhs.lhs)
11014 }
11015 if base_type == '' {
11016 return none
11017 }
11018 return '${base_type}__${method_name}'
11019 }
11020 else {
11021 return none
11022 }
11023 }
11024}
11025
11026fn (mut g Gen) explicit_generic_call_name_for_parts(base_lhs ast.Expr, generic_args []ast.Expr, args []ast.Expr) ?string {
11027 tokens := g.explicit_generic_arg_tokens(generic_args)
11028 if tokens.len != generic_args.len {
11029 return none
11030 }
11031 base_name := g.explicit_generic_base_call_name(base_lhs, args) or { return none }
11032 name := explicit_generic_call_name(base_name, tokens)
11033 if name == '' {
11034 return none
11035 }
11036 return name
11037}
11038
11039fn (mut g Gen) explicit_generic_call_name_for_lhs(lhs ast.Expr, args []ast.Expr) ?string {
11040 match lhs {
11041 ast.GenericArgOrIndexExpr {
11042 return g.explicit_generic_call_name_for_parts(lhs.lhs, [lhs.expr], args)
11043 }
11044 ast.GenericArgs {
11045 return g.explicit_generic_call_name_for_parts(lhs.lhs, lhs.args, args)
11046 }
11047 else {
11048 return none
11049 }
11050 }
11051}
11052
11053fn (mut g Gen) record_called_specialized_generic_name(name string) {
11054 if name == '' || !name.contains('_T_') {
11055 return
11056 }
11057 base_name := name.all_before('_T_')
11058 decl := g.find_generic_fn_decl_by_base_name(base_name) or { return }
11059 generic_params := g.generic_fn_param_names(decl)
11060 if generic_params.len == 0 {
11061 return
11062 }
11063 if bindings := g.active_generic_bindings_matching_name_suffix(name, generic_params) {
11064 g.record_late_generic_call_spec_unchecked(base_name, bindings)
11065 return
11066 }
11067 embedded_args := generic_call_embedded_type_arg_names_from_name(name, generic_params.len)
11068 if embedded_args.len != generic_params.len {
11069 return
11070 }
11071 mut bindings := map[string]types.Type{}
11072 for i, param_name in generic_params {
11073 c_type := generic_token_to_c_type(embedded_args[i])
11074 if c_type == '' {
11075 return
11076 }
11077 concrete := g.concrete_type_from_c_name(c_type) or { return }
11078 if !generic_concrete_type_is_runtime_specializable(concrete) {
11079 return
11080 }
11081 bindings[param_name] = concrete
11082 }
11083 g.record_late_generic_call_spec_unchecked(base_name, bindings)
11084}
11085
11086fn (mut g Gen) try_emit_explicit_generic_call(lhs ast.Expr, args []ast.Expr) bool {
11087 match lhs {
11088 ast.GenericArgOrIndexExpr {
11089 return g.try_emit_explicit_generic_call_parts(lhs.lhs, [lhs.expr], args)
11090 }
11091 ast.GenericArgs {
11092 return g.try_emit_explicit_generic_call_parts(lhs.lhs, lhs.args, args)
11093 }
11094 else {
11095 return false
11096 }
11097 }
11098}
11099
11100fn (mut g Gen) try_emit_explicit_generic_call_parts(base_lhs ast.Expr, generic_args []ast.Expr, args []ast.Expr) bool {
11101 tokens := g.explicit_generic_arg_tokens(generic_args)
11102 if tokens.len != generic_args.len {
11103 return false
11104 }
11105 mut name := ''
11106 mut call_args := []ast.Expr{cap: args.len + 1}
11107 match base_lhs {
11108 ast.Ident {
11109 name = sanitize_fn_ident(base_lhs.name)
11110 call_args << args
11111 }
11112 ast.SelectorExpr {
11113 if base_lhs.lhs is ast.Ident && base_lhs.lhs.name == 'C' {
11114 return false
11115 }
11116 if base_lhs.lhs is ast.Ident && g.is_type_name(base_lhs.lhs.name) {
11117 name = '${g.get_qualified_name(base_lhs.lhs.name)}__${sanitize_fn_ident(base_lhs.rhs.name)}'
11118 call_args << args
11119 } else if mod_call_name := g.resolve_selector_module_call_name(base_lhs) {
11120 name = mod_call_name
11121 call_args << args
11122 } else {
11123 name = g.resolve_call_name(base_lhs, args.len)
11124 if name == '' {
11125 method_name := sanitize_fn_ident(base_lhs.rhs.name)
11126 mut base_type := g.method_receiver_base_type(base_lhs.lhs)
11127 if base_type == '' {
11128 base_type = g.get_expr_type(base_lhs.lhs).trim_space().trim_right('*')
11129 }
11130 if base_type == '' {
11131 base_type = g.receiver_local_base_type(base_lhs.lhs)
11132 }
11133 if base_type == '' {
11134 return false
11135 }
11136 name = '${base_type}__${method_name}'
11137 }
11138 call_args << base_lhs.lhs
11139 call_args << args
11140 }
11141 }
11142 else {
11143 return false
11144 }
11145 }
11146
11147 name = explicit_generic_call_name(name, tokens)
11148 if name == '' {
11149 return false
11150 }
11151 if g.generic_call_name_has_placeholder_suffix(name) {
11152 if specialized_name := g.try_specialize_generic_call_name(name, call_args) {
11153 if g.generic_specialization_should_replace_call_name(name, specialized_name) {
11154 name = specialized_name
11155 }
11156 }
11157 }
11158 g.ensure_specialized_call_signature(name)
11159 g.record_called_specialized_generic_name(name)
11160 if name.contains('_T_') && !name.contains('__') {
11161 qualified := g.qualify_local_call_name(name)
11162 g.ensure_specialized_call_signature(qualified)
11163 if qualified != name && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
11164 g.record_called_specialized_generic_name(qualified)
11165 name = qualified
11166 }
11167 }
11168 g.mark_called_fn_name(name)
11169 g.sb.write_string('${name}(')
11170 for i, arg in call_args {
11171 if i > 0 {
11172 g.sb.write_string(', ')
11173 }
11174 g.gen_call_arg(name, i, arg)
11175 }
11176 g.sb.write_string(')')
11177 return true
11178}
11179
11180fn (mut g Gen) gen_array_push_many_for_lowered_array_arg(call_args []ast.Expr) bool {
11181 if call_args.len != 2 {
11182 return false
11183 }
11184 arg := strip_expr_wrappers(call_args[1])
11185 match arg {
11186 ast.ArrayInitExpr {
11187 if arg.exprs.len != 1 {
11188 return false
11189 }
11190 rhs := arg.exprs[0]
11191 if !g.expr_is_array_value(rhs) {
11192 return false
11193 }
11194 lhs_elem := g.infer_array_elem_type_from_expr(call_args[0])
11195 rhs_elem := g.infer_array_elem_type_from_expr(rhs)
11196 if lhs_elem == '' || rhs_elem == '' || lhs_elem != rhs_elem {
11197 return false
11198 }
11199 rhs_tmp := '_arr_push_many_tmp_${g.tmp_counter}'
11200 g.tmp_counter++
11201 arr_rhs_type := g.expr_array_runtime_type(rhs)
11202 g.sb.write_string('({ ${arr_rhs_type} ${rhs_tmp} = ')
11203 g.expr(rhs)
11204 g.sb.write_string('; array__push_many(')
11205 g.sb.write_string('((array*)')
11206 g.gen_array_append_target(call_args[0])
11207 g.sb.write_string(')')
11208 g.sb.write_string(', ${rhs_tmp}.data, ${rhs_tmp}.len); })')
11209 return true
11210 }
11211 else {
11212 return false
11213 }
11214 }
11215}
11216
11217fn (mut g Gen) gen_runtime_array_pointer_arg(arg ast.Expr) bool {
11218 mut base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
11219 if base_arg is ast.CastExpr {
11220 cast_type := g.expr_type_to_c(base_arg.typ).trim_space()
11221 if cast_type != '' && cast_type != 'array' && cast_type != 'array*' {
11222 return false
11223 }
11224 base_arg = base_arg.expr
11225 }
11226 if base_arg is ast.PrefixExpr && base_arg.op == .amp {
11227 inner := base_arg.expr
11228 if g.array_append_lhs_is_pointer(inner) {
11229 g.sb.write_string('((array*)(')
11230 g.expr(inner)
11231 g.sb.write_string('))')
11232 return true
11233 }
11234 if inner is ast.Ident {
11235 local_type := (g.get_local_var_c_type(inner.name) or { '' }).trim_space()
11236 if local_type.ends_with('*') {
11237 local_base := local_type.trim_right('*')
11238 if local_base == 'array' || local_base.starts_with('Array_') {
11239 g.sb.write_string('((array*)(')
11240 g.expr(inner)
11241 g.sb.write_string('))')
11242 return true
11243 }
11244 }
11245 }
11246 if inner is ast.SelectorExpr && g.selector_array_value_type(inner) != '' {
11247 g.sb.write_string('((array*)(&(')
11248 g.expr(inner)
11249 g.sb.write_string(')))')
11250 return true
11251 }
11252 }
11253 if base_arg is ast.Ident {
11254 if g.array_append_lhs_is_pointer(base_arg) {
11255 g.sb.write_string('((array*)(')
11256 g.expr(base_arg)
11257 g.sb.write_string('))')
11258 return true
11259 }
11260 local_type := (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
11261 if local_type.ends_with('*') {
11262 local_base := local_type.trim_right('*')
11263 if local_base == 'array' || local_base.starts_with('Array_') {
11264 g.sb.write_string('((array*)(')
11265 g.expr(base_arg)
11266 g.sb.write_string('))')
11267 return true
11268 }
11269 }
11270 }
11271 if base_arg is ast.SelectorExpr && g.selector_array_value_type(base_arg) != '' {
11272 g.sb.write_string('((array*)(&(')
11273 g.expr(base_arg)
11274 g.sb.write_string(')))')
11275 return true
11276 }
11277 return false
11278}
11279
11280fn (mut g Gen) call_expr(lhs ast.Expr, args []ast.Expr) {
11281 if args.len == 1 {
11282 if cast_type := g.fn_type_alias_cast_type(lhs) {
11283 g.gen_type_cast_expr(cast_type, args[0])
11284 return
11285 }
11286 }
11287 if lhs is ast.GenericArgOrIndexExpr {
11288 if g.try_emit_explicit_generic_call(lhs, args) {
11289 return
11290 }
11291 g.call_expr(lhs.lhs, args)
11292 return
11293 } else if lhs is ast.GenericArgs {
11294 if g.try_emit_explicit_generic_call(lhs, args) {
11295 return
11296 }
11297 g.call_expr(lhs.lhs, args)
11298 return
11299 }
11300 if args.len == 0 && lhs is ast.SelectorExpr && lhs.rhs.name == 'bytestr' {
11301 recv := lhs.lhs
11302 if recv is ast.IndexExpr && recv.expr is ast.RangeExpr {
11303 g.sb.write_string('Array_u8__bytestr(')
11304 g.expr(recv)
11305 g.sb.write_string(')')
11306 return
11307 }
11308 if recv is ast.CallExpr && recv.lhs is ast.Ident {
11309 call_lhs := recv.lhs as ast.Ident
11310 if call_lhs.name in ['array__slice', 'array__slice_ni', 'builtin__array__slice',
11311 'builtin__array__slice_ni'] {
11312 g.sb.write_string('Array_u8__bytestr(')
11313 g.expr(recv)
11314 g.sb.write_string(')')
11315 return
11316 }
11317 }
11318 elem_type := g.infer_array_elem_type_from_expr(recv).trim_right('*')
11319 if elem_type == 'u8' || elem_type == 'byte' {
11320 g.sb.write_string('Array_u8__bytestr(')
11321 g.expr(recv)
11322 g.sb.write_string(')')
11323 return
11324 }
11325 }
11326 for i in 0 .. args.len {
11327 if args[i] is ast.SelectorExpr {
11328 arg_sel := args[i] as ast.SelectorExpr
11329 if _ := g.plain_index_selector_type(arg_sel) {
11330 continue
11331 }
11332 if _ := g.plain_local_selector_type(arg_sel) {
11333 continue
11334 }
11335 }
11336 _ = g.get_expr_type(args[i])
11337 }
11338 // Comptime method dispatch: app.$method(args) inside `$for method in T.methods` loop.
11339 // Parser stores the rhs as Ident{name: '__comptime_selector__'}; the current
11340 // iteration's method name is in g.comptime_method_name.
11341 if g.comptime_method_var != '' && lhs is ast.SelectorExpr {
11342 rhs_name := lhs.rhs.name
11343 if rhs_name == '__comptime_selector__' || rhs_name == 'TODO: comptime selector' {
11344 g.gen_comptime_method_call(lhs.lhs, args)
11345 return
11346 }
11347 }
11348 if g.gen_json_decode_call(lhs, args) {
11349 return
11350 }
11351 if lhs is ast.Ident && lhs.name == 'array__slice' && args.len >= 3 {
11352 first_arg_type := g.get_expr_type(args[0]).trim_right('*')
11353 if g.expr_resolves_to_string(args[0], first_arg_type) {
11354 g.sb.write_string('string__substr(')
11355 g.expr(args[0])
11356 g.sb.write_string(', ')
11357 g.expr(args[1])
11358 g.sb.write_string(', ')
11359 g.expr(args[2])
11360 g.sb.write_string(')')
11361 return
11362 }
11363 }
11364 if lhs is ast.SelectorExpr {
11365 if lhs.rhs.name in ['str', 'name'] {
11366 if type_name := g.typeof_expr_type_name(lhs.lhs) {
11367 g.sb.write_string(c_static_v_string_expr(type_name))
11368 return
11369 }
11370 }
11371 if g.gen_builtin_string_method_call(lhs, args) {
11372 return
11373 }
11374 if g.gen_array_selector_method_call(lhs, args) {
11375 return
11376 }
11377 }
11378 // Intercept comptime field.attrs.contains("x") calls
11379 if g.comptime_field_var != '' && lhs is ast.SelectorExpr {
11380 if lhs.rhs.name == 'contains' && lhs.lhs is ast.SelectorExpr {
11381 inner := lhs.lhs as ast.SelectorExpr
11382 if inner.lhs is ast.Ident && inner.lhs.name == g.comptime_field_var
11383 && inner.rhs.name == 'attrs' {
11384 if args.len == 1 && args[0] is ast.BasicLiteral {
11385 lit := args[0] as ast.BasicLiteral
11386 attr_name := strip_literal_quotes(lit.value)
11387 result := attr_name in g.comptime_field_attrs
11388 g.sb.write_string(if result { 'true' } else { 'false' })
11389 return
11390 }
11391 g.sb.write_string('false')
11392 return
11393 }
11394 }
11395 }
11396 if lhs is ast.SelectorExpr {
11397 mut receiver_type := g.get_expr_type(lhs.lhs)
11398 if lhs.lhs is ast.Ident {
11399 if local_type := g.get_local_var_c_type(lhs.lhs.name) {
11400 local_base := local_type.trim_right('*')
11401 if local_type != '' && local_type != 'int' && !g.is_interface_type(local_base) {
11402 receiver_type = local_type
11403 }
11404 }
11405 }
11406 if (receiver_type == '' || receiver_type == 'int') && lhs.lhs is ast.SelectorExpr {
11407 receiver_type = g.selector_field_type(lhs.lhs)
11408 }
11409 base_receiver := receiver_type.trim_right('*')
11410 if lhs.lhs is ast.Ident && base_receiver != '' && base_receiver != 'int'
11411 && !g.is_interface_type(base_receiver) {
11412 method_name := lhs.rhs.name
11413 if concrete_method := g.resolve_method_on_concrete_type(base_receiver, method_name) {
11414 mut method_fn := concrete_method
11415 call_args2 := g.interface_call_args_without_object(lhs.lhs, args)
11416 method_fn = g.specialize_direct_method_call_name(method_fn, lhs.lhs, call_args2)
11417 g.mark_called_fn_name(method_fn)
11418 receiver_as_ptr := g.fn_param_wants_pointer(method_fn, 0)
11419 g.sb.write_string('${method_fn}(')
11420 if receiver_as_ptr {
11421 if !receiver_type.ends_with('*') {
11422 g.write_method_recv_addr(lhs.lhs)
11423 } else {
11424 g.expr(lhs.lhs)
11425 }
11426 } else if receiver_type.ends_with('*') {
11427 g.sb.write_string('(*')
11428 g.expr(lhs.lhs)
11429 g.sb.write_string(')')
11430 } else {
11431 g.expr(lhs.lhs)
11432 }
11433 for i in 0 .. call_args2.len {
11434 g.sb.write_string(', ')
11435 g.gen_call_arg(method_fn, i + 1, call_args2[i])
11436 }
11437 g.sb.write_string(')')
11438 return
11439 }
11440 }
11441 if g.is_interface_type(base_receiver) && lhs.rhs.name != '' {
11442 method_name := lhs.rhs.name
11443 if g.is_interface_vtable_method(base_receiver, method_name) {
11444 sep := if receiver_type.ends_with('*') { '->' } else { '.' }
11445 mut method_info := InterfaceMethodInfo{}
11446 mut has_method_info := false
11447 mut expected_total_params := 1
11448 if method := g.interface_method_by_name(base_receiver, method_name) {
11449 method_info = method
11450 has_method_info = true
11451 expected_total_params = method.param_types.len + 1
11452 }
11453 g.expr(lhs.lhs)
11454 g.sb.write_string('${sep}${method_name}(')
11455 mut emitted_args := 0
11456 mut param_offset := 0
11457 if args.len < expected_total_params {
11458 g.expr(lhs.lhs)
11459 g.sb.write_string('${sep}_object')
11460 emitted_args++
11461 } else {
11462 // Object is already in args — param_types start at args[1]
11463 param_offset = 1
11464 }
11465 for i in 0 .. args.len {
11466 arg := args[i]
11467 if emitted_args > 0 {
11468 g.sb.write_string(', ')
11469 }
11470 pi := i - param_offset
11471 if has_method_info && pi >= 0 && pi < method_info.param_types.len {
11472 g.gen_interface_call_arg(method_info.param_types[pi], arg)
11473 } else {
11474 g.expr(arg)
11475 }
11476 emitted_args++
11477 }
11478 g.sb.write_string(')')
11479 return
11480 }
11481 // Default interface method (not in vtable, has a body on the interface).
11482 // Call the default implementation directly: InterfaceName__method(receiver, args...)
11483 default_fn := '${base_receiver}__${method_name}'
11484 if default_fn in g.fn_param_is_ptr || default_fn in g.fn_return_types {
11485 call_args := g.interface_call_args_without_object(lhs.lhs, args)
11486 g.mark_called_fn_name(default_fn)
11487 g.sb.write_string('${default_fn}(')
11488 recv_wants_ptr := g.fn_param_wants_pointer(default_fn, 0)
11489 recv_is_ptr := receiver_type.ends_with('*')
11490 if recv_wants_ptr && !recv_is_ptr {
11491 g.sb.write_string('&')
11492 } else if !recv_wants_ptr && recv_is_ptr {
11493 g.sb.write_string('*')
11494 }
11495 g.expr(lhs.lhs)
11496 for i in 0 .. call_args.len {
11497 g.sb.write_string(', ')
11498 if param_types := g.fn_param_types[default_fn] {
11499 if i + 1 < param_types.len {
11500 g.gen_interface_call_arg(param_types[i + 1], call_args[i])
11501 } else {
11502 g.expr(call_args[i])
11503 }
11504 } else {
11505 g.expr(call_args[i])
11506 }
11507 }
11508 g.sb.write_string(')')
11509 return
11510 }
11511 // Interface-to-interface: method not on current interface but exists on
11512 // another interface (e.g., Widget variable inside `if w is Layout`).
11513 // Find a concrete type that has this method and call it directly via _object.
11514 if method_info := g.find_method_on_any_interface(method_name) {
11515 suffix := '__${method_name}'
11516 // Find a concrete function that ends with __method_name
11517 mut concrete_fn := ''
11518 for fn_name, _ in g.fn_return_types {
11519 if fn_name.ends_with(suffix) && fn_name != default_fn {
11520 // Verify it has the right number of params
11521 if fn_params := g.fn_param_types[fn_name] {
11522 if fn_params.len == method_info.param_types.len + 1 {
11523 concrete_fn = fn_name
11524 break
11525 }
11526 }
11527 }
11528 }
11529 if concrete_fn != '' {
11530 // Generate: ((ret_type (*)(void*, ...))(concrete_fn))(w->_object, args...)
11531 // Actually, emit a cast call through the method signature
11532 sep := if receiver_type.ends_with('*') { '->' } else { '.' }
11533 g.sb.write_string('((${method_info.cast_signature})(${concrete_fn}))(')
11534 g.expr(lhs.lhs)
11535 g.sb.write_string('${sep}_object')
11536 call_args2 := g.interface_call_args_without_object(lhs.lhs, args)
11537 for i in 0 .. call_args2.len {
11538 g.sb.write_string(', ')
11539 g.expr(call_args2[i])
11540 }
11541 g.sb.write_string(')')
11542 return
11543 }
11544 }
11545 }
11546 if lhs.lhs is ast.Ident {
11547 lhs_ident := lhs.lhs as ast.Ident
11548 if lhs_ident.name == 'C' && lhs.rhs.name in ['FD_ZERO', 'FD_SET', 'FD_ISSET'] {
11549 g.sb.write_string('${lhs.rhs.name}(')
11550 for i in 0 .. args.len {
11551 arg := args[i]
11552 if i > 0 {
11553 g.sb.write_string(', ')
11554 }
11555 g.expr(arg)
11556 }
11557 g.sb.write_string(')')
11558 return
11559 }
11560 }
11561 }
11562 if lhs is ast.SelectorExpr {
11563 if lhs.rhs.name in ['hash_fn', 'key_eq_fn', 'clone_fn', 'free_fn'] {
11564 base_type := g.method_receiver_base_type(lhs.lhs)
11565 if base_type == 'map' || base_type.starts_with('Map_') {
11566 g.expr(lhs)
11567 g.sb.write_string('(')
11568 for i in 0 .. args.len {
11569 arg := args[i]
11570 if i > 0 {
11571 g.sb.write_string(', ')
11572 }
11573 g.expr(arg)
11574 }
11575 g.sb.write_string(')')
11576 return
11577 }
11578 }
11579 }
11580 if lhs is ast.SelectorExpr && lhs.rhs.name == 'free' && !(lhs.lhs is ast.Ident
11581 && (lhs.lhs as ast.Ident).name == 'C') {
11582 receiver_type := g.get_expr_type(lhs.lhs).trim_right('*')
11583 if receiver_type != '' && receiver_type != 'int' && receiver_type != 'string'
11584 && receiver_type != 'array' && receiver_type != 'map'
11585 && !receiver_type.starts_with('Array_') && !receiver_type.starts_with('Map_') {
11586 g.sb.write_string('((void)(')
11587 g.expr(lhs.lhs)
11588 g.sb.write_string('))')
11589 return
11590 }
11591 }
11592 if lhs is ast.SelectorExpr && g.is_fn_pointer_expr(lhs) {
11593 if g.try_emit_resolved_selector_method_call(lhs, args) {
11594 return
11595 }
11596 // Let the regular method-call path emit concrete or promoted embedded methods.
11597 if !g.selector_resolves_to_method_call(lhs) {
11598 mut should_emit_fnptr_call := true
11599 resolved := g.resolve_call_name(lhs, args.len)
11600 if resolved != '' && (resolved in g.fn_param_is_ptr || resolved in g.fn_return_types) {
11601 should_emit_fnptr_call = false
11602 }
11603 // When there is a naming collision (e.g. `seed` is both a function and a
11604 // sub-module in the `rand` module), `is_module_ident` returns false and
11605 // `resolve_call_name` resolves to the function type. Try treating lhs.lhs
11606 // as a module name by checking if `lhs_name__rhs_name` exists as a known
11607 // function.
11608 if should_emit_fnptr_call && lhs.lhs is ast.Ident {
11609 alt := '${lhs.lhs.name}__${sanitize_fn_ident(lhs.rhs.name)}'
11610 if alt in g.fn_param_is_ptr || alt in g.fn_return_types {
11611 g.mark_called_fn_name(alt)
11612 g.sb.write_string('${alt}(')
11613 for i in 0 .. args.len {
11614 if i > 0 {
11615 g.sb.write_string(', ')
11616 }
11617 g.gen_call_arg(alt, i, args[i])
11618 }
11619 g.sb.write_string(')')
11620 return
11621 }
11622 }
11623 if should_emit_fnptr_call {
11624 // Function pointer fields are stored as void* in C.
11625 // We need to cast to the correct function pointer type before calling.
11626 ret_type := g.fn_pointer_return_type(lhs)
11627 c_ret := if ret_type == '' { 'void' } else { ret_type }
11628 // For pointer-to-interface types, the transformer doesn't add _object
11629 // (it only handles non-pointer interface vars like IError err).
11630 // Check if this is a vtable method call on an interface pointer
11631 // and if so, pass _object as the first argument.
11632 mut receiver_type := g.get_expr_type(lhs.lhs)
11633 if (receiver_type == '' || receiver_type == 'int') && lhs.lhs is ast.SelectorExpr {
11634 receiver_type = g.selector_field_type(lhs.lhs)
11635 }
11636 mut base_receiver := receiver_type.trim_right('*')
11637 mut is_iface_receiver := g.is_interface_type(base_receiver)
11638 if !is_iface_receiver {
11639 if raw := g.get_raw_type(lhs.lhs) {
11640 raw_name := g.types_type_to_c(raw).trim_right('*')
11641 if raw_name != '' && g.is_interface_type(raw_name) {
11642 base_receiver = raw_name
11643 if receiver_type == '' || receiver_type == 'int' {
11644 receiver_type = raw_name
11645 }
11646 is_iface_receiver = true
11647 }
11648 }
11649 }
11650 if !is_iface_receiver && lhs.lhs is ast.Ident {
11651 if local_type := g.get_local_var_c_type(lhs.lhs.name) {
11652 local_base := local_type.trim_right('*')
11653 if local_base != '' && g.is_interface_type(local_base) {
11654 base_receiver = local_base
11655 receiver_type = local_type
11656 is_iface_receiver = true
11657 }
11658 }
11659 }
11660 // Check global variables for interface type (e.g., __global default_rng &PRNG)
11661 if !is_iface_receiver && lhs.lhs is ast.Ident && g.env != unsafe { nil } {
11662 ident_name := lhs.lhs.name
11663 // Strip module prefix for scope lookup (e.g., 'rand__default_rng' → 'default_rng')
11664 short_name := if ident_name.contains('__') {
11665 ident_name.all_after_last('__')
11666 } else {
11667 ident_name
11668 }
11669 // Try looking up in the declaring module's scope
11670 global_mod := g.global_var_modules[short_name] or { '' }
11671 for mod_name in [global_mod, g.cur_module, 'builtin'] {
11672 if mod_name == '' {
11673 continue
11674 }
11675 if scope := g.env.get_scope(mod_name) {
11676 if obj := scope.lookup_parent(short_name, 0) {
11677 global_type := g.types_type_to_c(obj.typ())
11678 global_base := global_type.trim_right('*')
11679 if global_base != '' && g.is_interface_type(global_base) {
11680 base_receiver = global_base
11681 receiver_type = global_type
11682 is_iface_receiver = true
11683 break
11684 }
11685 }
11686 }
11687 }
11688 }
11689 if is_iface_receiver {
11690 sep := if receiver_type.ends_with('*') { '->' } else { '.' }
11691 method_name := lhs.rhs.name
11692 if g.is_interface_vtable_method(base_receiver, method_name) {
11693 mut cast_sig := '${c_ret} (*)(void*)'
11694 mut expected_total_params := 1
11695 if method := g.interface_method_by_name(base_receiver, method_name) {
11696 cast_sig = method.cast_signature
11697 expected_total_params = method.param_types.len + 1
11698 }
11699 g.sb.write_string('((${cast_sig})')
11700 g.expr(lhs)
11701 g.sb.write_string(')(')
11702 // If transformed code already passes all expected parameters
11703 // (including the receiver object), do not prepend `_object` again.
11704 mut prepend_object := args.len < expected_total_params
11705 mut emitted_args := 0
11706 if prepend_object {
11707 g.expr(lhs.lhs)
11708 g.sb.write_string('${sep}_object')
11709 emitted_args++
11710 }
11711 // When prepend_object is false, args[0] is _object (already provided
11712 // by the transformer), so param_types indices start at args[1].
11713 param_offset := if prepend_object { 0 } else { 1 }
11714 for i in 0 .. args.len {
11715 arg := args[i]
11716 if emitted_args > 0 {
11717 g.sb.write_string(', ')
11718 }
11719 param_idx := i - param_offset
11720 if method := g.interface_method_by_name(base_receiver, method_name) {
11721 if param_idx >= 0 && param_idx < method.param_types.len {
11722 g.gen_interface_call_arg(method.param_types[param_idx], arg)
11723 } else {
11724 g.expr(arg)
11725 }
11726 } else {
11727 g.expr(arg)
11728 }
11729 emitted_args++
11730 }
11731 g.sb.write_string(')')
11732 return
11733 }
11734 // Default interface method: not in vtable, has a body on the interface itself.
11735 // Call the default implementation directly: InterfaceName__method(receiver, args...)
11736 default_method_name := '${base_receiver}__${method_name}'
11737 if default_method_name in g.fn_param_is_ptr
11738 || default_method_name in g.fn_return_types {
11739 call_args := g.interface_call_args_without_object(lhs.lhs, args)
11740 g.mark_called_fn_name(default_method_name)
11741 g.sb.write_string('${default_method_name}(')
11742 // Pass receiver (interface pointer or value) as first arg
11743 receiver_as_ptr := g.fn_param_wants_pointer(default_method_name, 0)
11744 if receiver_as_ptr {
11745 if !receiver_type.ends_with('*') {
11746 g.sb.write_string('&')
11747 }
11748 g.expr(lhs.lhs)
11749 } else {
11750 if receiver_type.ends_with('*') {
11751 g.sb.write_string('*')
11752 }
11753 g.expr(lhs.lhs)
11754 }
11755 for i, arg in call_args {
11756 g.sb.write_string(', ')
11757 if param_types := g.fn_param_types[default_method_name] {
11758 if i + 1 < param_types.len {
11759 g.gen_interface_call_arg(param_types[i + 1], arg)
11760 } else {
11761 g.expr(arg)
11762 }
11763 } else {
11764 g.expr(arg)
11765 }
11766 }
11767 g.sb.write_string(')')
11768 return
11769 }
11770 mut narrowed_receiver := g.get_expr_type_from_env(lhs.lhs) or { '' }
11771 if narrowed_receiver == '' || narrowed_receiver == 'int' {
11772 narrowed_receiver = g.get_env_c_type(lhs.lhs) or { narrowed_receiver }
11773 }
11774 if narrowed_receiver == '' || narrowed_receiver == 'int' {
11775 narrowed_receiver = g.smartcast_c_type_from_scope(lhs.lhs) or {
11776 narrowed_receiver
11777 }
11778 }
11779 narrowed_receiver =
11780 strip_pointer_type_name(unmangle_c_ptr_type(narrowed_receiver))
11781 if narrowed_receiver != '' && narrowed_receiver != 'int'
11782 && !g.is_interface_type(narrowed_receiver) {
11783 if concrete_method := g.resolve_method_on_concrete_type(narrowed_receiver,
11784 method_name)
11785 {
11786 call_args := g.interface_call_args_without_object(lhs.lhs, args)
11787 g.mark_called_fn_name(concrete_method)
11788 g.sb.write_string('${concrete_method}(')
11789 receiver_as_ptr := g.fn_param_wants_pointer(concrete_method, 0)
11790 if receiver_as_ptr {
11791 g.sb.write_string('((${narrowed_receiver}*)(')
11792 g.expr(lhs.lhs)
11793 g.sb.write_string('${sep}_object))')
11794 } else {
11795 g.sb.write_string('(*(${narrowed_receiver}*)(')
11796 g.expr(lhs.lhs)
11797 g.sb.write_string('${sep}_object))')
11798 }
11799 for i in 0 .. call_args.len {
11800 arg := call_args[i]
11801 g.sb.write_string(', ')
11802 g.gen_call_arg(concrete_method, i + 1, arg)
11803 }
11804 g.sb.write_string(')')
11805 return
11806 }
11807 // Try embedded struct method resolution
11808 if emb := g.resolve_method_on_embedded_receiver(narrowed_receiver,
11809 method_name)
11810 {
11811 call_args := g.interface_call_args_without_object(lhs.lhs, args)
11812 g.mark_called_fn_name(emb.method_c_name)
11813 g.sb.write_string('${emb.method_c_name}(')
11814 receiver_as_ptr := g.fn_param_wants_pointer(emb.method_c_name, 0)
11815 if receiver_as_ptr {
11816 g.sb.write_string('&((${narrowed_receiver}*)(')
11817 g.expr(lhs.lhs)
11818 g.sb.write_string('${sep}_object))->${emb.owner}')
11819 } else {
11820 g.sb.write_string('((${narrowed_receiver}*)(')
11821 g.expr(lhs.lhs)
11822 g.sb.write_string('${sep}_object))->${emb.owner}')
11823 }
11824 for i in 0 .. call_args.len {
11825 arg := call_args[i]
11826 g.sb.write_string(', ')
11827 g.gen_call_arg(emb.method_c_name, i + 1, arg)
11828 }
11829 g.sb.write_string(')')
11830 return
11831 }
11832 }
11833 }
11834 // When receiver is a concrete (non-interface) type from smartcast,
11835 // resolve the method on the concrete type or its embedded structs.
11836 if !is_iface_receiver && base_receiver != '' && base_receiver != 'int'
11837 && !g.is_interface_type(base_receiver) {
11838 method_name := lhs.rhs.name
11839 if concrete_method := g.resolve_method_on_concrete_type(base_receiver,
11840 method_name)
11841 {
11842 receiver_as_ptr := g.fn_param_wants_pointer(concrete_method, 0)
11843 g.mark_called_fn_name(concrete_method)
11844 g.sb.write_string('${concrete_method}(')
11845 if receiver_as_ptr {
11846 g.write_method_recv_addr(lhs.lhs)
11847 } else {
11848 g.expr(lhs.lhs)
11849 }
11850 for i in 0 .. args.len {
11851 g.sb.write_string(', ')
11852 g.gen_call_arg(concrete_method, i + 1, args[i])
11853 }
11854 g.sb.write_string(')')
11855 return
11856 }
11857 if emb := g.resolve_method_on_embedded_receiver(base_receiver, method_name) {
11858 g.mark_called_fn_name(emb.method_c_name)
11859 g.sb.write_string('${emb.method_c_name}(')
11860 g.gen_embedded_receiver_arg(lhs.lhs, emb.owner, emb.method_c_name)
11861 for i in 0 .. args.len {
11862 g.sb.write_string(', ')
11863 g.gen_call_arg(emb.method_c_name, i + 1, args[i])
11864 }
11865 g.sb.write_string(')')
11866 return
11867 }
11868 }
11869 // Last resort: in generic method bodies, the receiver may be a generic type T.
11870 // Use active_generic_types to resolve the concrete type and find the method.
11871 if g.active_generic_types.len > 0 && lhs.lhs is ast.SelectorExpr {
11872 // Try to get the specialized type of lhs.lhs (e.g. current.value → StructFieldInfo)
11873 specialized_recv := g.resolve_generic_struct_field_name(lhs.lhs.lhs)
11874 if specialized_recv != '' {
11875 field_name := lhs.lhs.rhs.name
11876 if field_type := g.lookup_struct_field_type_by_name(specialized_recv,
11877 field_name)
11878 {
11879 concrete_type := field_type.trim_right('*')
11880 method_name := lhs.rhs.name
11881 if concrete_method := g.resolve_method_on_concrete_type(concrete_type,
11882 method_name)
11883 {
11884 receiver_as_ptr := g.fn_param_wants_pointer(concrete_method, 0)
11885 g.mark_called_fn_name(concrete_method)
11886 g.sb.write_string('${concrete_method}(')
11887 if receiver_as_ptr {
11888 g.sb.write_string('&(')
11889 g.expr(lhs.lhs)
11890 g.sb.write_string(')')
11891 } else {
11892 g.expr(lhs.lhs)
11893 }
11894 for i in 0 .. args.len {
11895 g.sb.write_string(', ')
11896 g.gen_call_arg(concrete_method, i + 1, args[i])
11897 }
11898 g.sb.write_string(')')
11899 return
11900 }
11901 }
11902 }
11903 }
11904 if g.gen_direct_fn_pointer_call(lhs, args) {
11905 return
11906 }
11907 g.sb.write_string('((${c_ret}(*)())')
11908 g.expr(lhs)
11909 g.sb.write_string(')(')
11910 for i in 0 .. args.len {
11911 arg := args[i]
11912 if i > 0 {
11913 g.sb.write_string(', ')
11914 }
11915 g.expr(arg)
11916 }
11917 g.sb.write_string(')')
11918 return
11919 }
11920 }
11921 }
11922 mut name := ''
11923 mut call_args := []ast.Expr{}
11924 mut embedded_receiver_owner := ''
11925 mut selector_is_module_or_static_call := false
11926 call_args << args
11927 if lhs is ast.Ident {
11928 name = sanitize_fn_ident(lhs.name)
11929 } else if lhs is ast.SelectorExpr {
11930 if lhs.rhs.name in ['hash_fn', 'key_eq_fn', 'clone_fn', 'free_fn'] {
11931 base_type := g.method_receiver_base_type(lhs.lhs)
11932 if base_type == 'map' || base_type.starts_with('Map_') {
11933 g.expr(lhs)
11934 g.sb.write_string('(')
11935 for i in 0 .. args.len {
11936 arg := args[i]
11937 if i > 0 {
11938 g.sb.write_string(', ')
11939 }
11940 g.expr(arg)
11941 }
11942 g.sb.write_string(')')
11943 return
11944 }
11945 }
11946 // Handle C.puts, C.putchar etc.
11947 if lhs.lhs is ast.Ident && lhs.lhs.name == 'C' {
11948 name = lhs.rhs.name
11949 if name == 'gettid' && args.len == 0 && g.should_emit_linux_gettid_compat() {
11950 name = 'v_cleanc_gettid'
11951 }
11952 // With prealloc, free() is redefined as a no-op macro.
11953 // C.free calls in prealloc_vcleanup need the real free via _v_cfree.
11954 if name == 'free' && g.pref != unsafe { nil } && g.pref.prealloc {
11955 name = '_v_cfree'
11956 }
11957 g.sb.write_string('${name}(')
11958 for i in 0 .. args.len {
11959 arg := args[i]
11960 if i > 0 {
11961 g.sb.write_string(', ')
11962 }
11963 g.expr(arg)
11964 }
11965 g.sb.write_string(')')
11966 return
11967 }
11968 // module.fn(...) => module__fn(...)
11969 if lhs.lhs is ast.Ident
11970 && (g.is_type_name(lhs.lhs.name) || g.selector_lhs_is_static_type_ident(lhs.lhs.name)) {
11971 // Static type method call: Type.method(...)
11972 name = '${g.get_qualified_name(lhs.lhs.name)}__${sanitize_fn_ident(lhs.rhs.name)}'
11973 selector_is_module_or_static_call = true
11974 } else if mod_call_name := g.resolve_selector_module_call_name(lhs) {
11975 name = mod_call_name
11976 selector_is_module_or_static_call = true
11977 } else {
11978 // value.method(args...) => ReceiverType__method(value, args...)
11979 name = g.resolve_call_name(lhs, args.len)
11980 method_name := sanitize_fn_ident(lhs.rhs.name)
11981 mut base_type := g.method_receiver_base_type(lhs.lhs)
11982 if base_type == '' {
11983 base_type = g.get_expr_type(lhs.lhs).trim_space().trim_right('*')
11984 }
11985 if base_type == '' {
11986 base_type = g.receiver_local_base_type(lhs.lhs)
11987 }
11988 if receiver_type := g.get_receiver_expr_type_for_method(lhs.lhs) {
11989 if concrete_method := g.resolve_method_on_concrete_type(receiver_type, method_name) {
11990 name = concrete_method
11991 }
11992 }
11993 if name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types) {
11994 if declared_method := g.resolve_method_on_declared_receiver_type(lhs.lhs,
11995 method_name)
11996 {
11997 name = declared_method
11998 }
11999 }
12000 if (name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types))
12001 && base_type != '' {
12002 if embedded := g.resolve_method_on_embedded_receiver(base_type, method_name) {
12003 name = embedded.method_c_name
12004 embedded_receiver_owner = embedded.owner
12005 }
12006 }
12007 if name == '' || (name !in g.fn_param_is_ptr && name !in g.fn_return_types) {
12008 if map_method := g.map_runtime_method_name(lhs.lhs, method_name) {
12009 name = map_method
12010 }
12011 }
12012 if name == '' {
12013 name = '${base_type}__${method_name}'
12014 }
12015 call_args = []ast.Expr{cap: args.len + 1}
12016 call_args << lhs.lhs
12017 call_args << args
12018 }
12019 }
12020
12021 // Transformer helper maps directly to builtin implementation name in vlib.
12022 if name == 'builtin__new_array_from_c_array_noscan' {
12023 name = 'new_array_from_c_array'
12024 }
12025 if name == 'builtin__array_push_noscan' {
12026 name = 'array__push'
12027 }
12028 if name == 'panic' {
12029 name = 'v_panic'
12030 }
12031 if name == 'array__bytestr'
12032 && ('Array_u8__bytestr' in g.fn_param_is_ptr || 'Array_u8__bytestr' in g.fn_return_types) {
12033 name = 'Array_u8__bytestr'
12034 }
12035 if name == 'int__eq_epsilon'
12036 && ('f32__eq_epsilon' in g.fn_param_is_ptr || 'f32__eq_epsilon' in g.fn_return_types) {
12037 name = 'f32__eq_epsilon'
12038 }
12039 if name == 'os__exit' {
12040 name = 'exit'
12041 }
12042 // chan type methods map to sync__Channel__ methods
12043 if name.starts_with('chan__') {
12044 name = 'sync__Channel__' + name['chan__'.len..]
12045 }
12046 if name in ['array__get_u64', 'array__get_u32', 'array__get_u16', 'array__get_u8']
12047 && call_args.len > 0 {
12048 receiver_expr := if call_args[0] is ast.PrefixExpr
12049 && (call_args[0] as ast.PrefixExpr).op == .mul {
12050 (call_args[0] as ast.PrefixExpr).expr
12051 } else {
12052 call_args[0]
12053 }
12054 receiver_type := g.get_expr_type(receiver_expr).trim_right('*')
12055 if receiver_type == 'binary__DecodeState' {
12056 method_name := name['array__'.len..]
12057 g.sb.write_string('binary__DecodeState__${method_name}(')
12058 if g.get_expr_type(receiver_expr).ends_with('*') {
12059 g.expr(receiver_expr)
12060 } else {
12061 g.sb.write_string('&')
12062 g.expr(receiver_expr)
12063 }
12064 g.sb.write_string(')')
12065 return
12066 }
12067 }
12068 // strings__Builder methods are emitted directly by cheaders.v;
12069 // do NOT fall back to array__ methods which have different signatures.
12070 if name.starts_with('array__') && call_args.len > 0 {
12071 method_name := name['array__'.len..]
12072 elem_type := g.infer_array_elem_type_from_expr(call_args[0]).trim_right('*')
12073 if elem_type != '' {
12074 specialized := 'Array_' + mangle_alias_component(elem_type) + '__' + method_name
12075 if specialized in g.fn_param_is_ptr || specialized in g.fn_return_types {
12076 name = specialized
12077 }
12078 }
12079 }
12080 if lhs is ast.SelectorExpr && sanitize_fn_ident(lhs.rhs.name) == 'bytestr' && call_args.len > 0 {
12081 elem_type := g.infer_array_elem_type_from_expr(call_args[0]).trim_right('*')
12082 if elem_type == 'u8' || elem_type == 'byte' {
12083 name = 'Array_u8__bytestr'
12084 }
12085 }
12086 if name.contains('_T_') && !name.contains('__') {
12087 if qualified := g.find_known_qualified_fn_name(name) {
12088 name = qualified
12089 } else if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
12090 base_name := name.all_before('_T_')
12091 suffix := name.all_after('_T_')
12092 local_candidate := '${g.cur_module}__${base_name}_T_${g.cur_module}_${suffix}'
12093 if local_candidate in g.fn_param_is_ptr || local_candidate in g.fn_return_types {
12094 name = local_candidate
12095 } else {
12096 name = '${g.cur_module}__${name}'
12097 }
12098 } else if lhs is ast.Ident && g.cur_module != '' && g.cur_module != 'main'
12099 && g.cur_module != 'builtin' {
12100 name = '${g.cur_module}__${name}'
12101 } else if lhs is ast.SelectorExpr && lhs.lhs is ast.Ident {
12102 lhs_mod := lhs.lhs as ast.Ident
12103 if g.is_module_ident(lhs_mod.name) {
12104 mod_name := g.resolve_module_name(lhs_mod.name)
12105 if mod_name != '' {
12106 name = '${mod_name}__${name}'
12107 }
12108 }
12109 }
12110 }
12111 if !name.contains('__') && name !in g.fn_return_types && name !in g.fn_param_is_ptr {
12112 if qualified := g.find_known_qualified_fn_name(name) {
12113 name = qualified
12114 }
12115 }
12116 if name.contains('_T_') {
12117 g.ensure_specialized_call_signature(name)
12118 if receiver_only_name := g.receiver_only_specialized_call_name(name) {
12119 name = receiver_only_name
12120 }
12121 }
12122 if name.contains('_T_') && name !in g.fn_return_types && name !in g.fn_param_is_ptr {
12123 base_name := name.all_before('_T_')
12124 if (base_name in g.fn_return_types || base_name in g.fn_param_is_ptr)
12125 && !g.has_generic_fn_decl_by_base_name(base_name) {
12126 name = base_name
12127 }
12128 }
12129 if lhs is ast.SelectorExpr && call_args.len > 0 && !selector_is_module_or_static_call
12130 && !name.contains('_T_') {
12131 method_name := sanitize_fn_ident(lhs.rhs.name)
12132 mut receiver_type := ''
12133 if call_args[0] is ast.CallExpr {
12134 recv_call := call_args[0] as ast.CallExpr
12135 if recv_call.lhs is ast.Ident {
12136 recv_name := sanitize_fn_ident((recv_call.lhs as ast.Ident).name)
12137 if ret := g.fn_return_types[recv_name] {
12138 receiver_type = ret.trim_right('*')
12139 }
12140 }
12141 if receiver_type == '' {
12142 if ret := g.get_call_return_type(recv_call.lhs, recv_call.args) {
12143 receiver_type = ret.trim_right('*')
12144 }
12145 }
12146 if receiver_type == '' {
12147 if ret := g.get_call_return_type(recv_call.lhs, recv_call.args) {
12148 receiver_type = ret.trim_right('*')
12149 }
12150 }
12151 }
12152 if receiver_type == '' && call_args[0] is ast.CallExpr {
12153 recv_call := call_args[0] as ast.CallExpr
12154 if ret := g.get_call_return_type(recv_call.lhs, recv_call.args) {
12155 receiver_type = ret.trim_right('*')
12156 }
12157 }
12158 if receiver_type == '' || receiver_type == 'int' {
12159 if recovered_type := g.get_receiver_expr_type_for_method(call_args[0]) {
12160 receiver_type = recovered_type
12161 }
12162 }
12163 if receiver_type == '' || receiver_type == 'int' {
12164 receiver_type = g.get_expr_type(call_args[0]).trim_right('*')
12165 }
12166 if receiver_type != '' && receiver_type != 'int' {
12167 if concrete_method := g.resolve_method_on_concrete_type(receiver_type, method_name) {
12168 name = concrete_method
12169 }
12170 }
12171 }
12172 name = g.resolve_ident_receiver_method_call_name(name, lhs, call_args)
12173 if current_receiver_name := g.same_receiver_specialized_method_name(name) {
12174 name = current_receiver_name
12175 }
12176 if name.contains('_T_') {
12177 g.ensure_specialized_call_signature(name)
12178 if receiver_only_name := g.receiver_only_specialized_call_name(name) {
12179 name = receiver_only_name
12180 }
12181 }
12182 if call_args_have_field_init(call_args) {
12183 call_args = g.lower_field_init_call_args_for_codegen(name, call_args)
12184 }
12185 for i in 0 .. call_args.len {
12186 arg := call_args[i]
12187 if arg is ast.FieldInit {
12188 panic('bug in v2 compiler: FieldInit call args should have been lowered in v2.transformer (${g.cur_file_name}:${g.cur_fn_name} call=${name} arg_idx=${i} field=${arg.name})')
12189 }
12190 }
12191 if name != '' {
12192 g.record_called_specialized_generic_name(name)
12193 g.mark_called_fn_name(name)
12194 }
12195 if name.contains('__new') && call_args.len > 0 {
12196 expected_ctor_type := name.all_before_last('__new')
12197 mut arg_type_name := ''
12198 first_arg := call_args[0]
12199 match first_arg {
12200 ast.Ident {
12201 arg_type_name = first_arg.name
12202 }
12203 ast.SelectorExpr {
12204 if first_arg.lhs is ast.Ident {
12205 lhs_ident := first_arg.lhs as ast.Ident
12206 arg_type_name = '${lhs_ident.name}__${first_arg.rhs.name}'
12207 } else {
12208 arg_type_name = first_arg.rhs.name
12209 }
12210 }
12211 ast.Type {
12212 arg_type_name = g.expr_type_to_c(first_arg)
12213 }
12214 else {}
12215 }
12216
12217 if arg_type_name == '' {
12218 arg_type_name = g.get_expr_type(first_arg)
12219 }
12220 arg_type_name = arg_type_name.trim_space().trim_left('&').trim_left('*')
12221 if arg_type_name != '' && (arg_type_name == expected_ctor_type
12222 || arg_type_name == expected_ctor_type.all_after_last('__')) {
12223 call_args.delete(0)
12224 }
12225 }
12226 if call_args.len == 1 && g.is_type_name(name) && !name.ends_with('__new') {
12227 g.gen_type_cast_expr(name, call_args[0])
12228 return
12229 }
12230 if lhs is ast.Ident && g.is_type_name(lhs.name) && call_args.len == 1 {
12231 type_name := g.expr_type_to_c(lhs)
12232 g.gen_type_cast_expr(type_name, call_args[0])
12233 return
12234 }
12235 if lhs is ast.SelectorExpr && call_args.len == 1 && lhs.lhs is ast.Ident {
12236 if lhs.lhs.name == 'C' && g.call_or_cast_lhs_is_type(lhs) {
12237 type_name := g.expr_type_to_c(lhs)
12238 g.gen_type_cast_expr(type_name, call_args[0])
12239 return
12240 }
12241 qualified_type := '${lhs.lhs.name}__${lhs.rhs.name}'
12242 if g.is_type_name(qualified_type) {
12243 type_name := g.expr_type_to_c(lhs)
12244 g.gen_type_cast_expr(type_name, call_args[0])
12245 return
12246 }
12247 }
12248 if call_args.len == 1 && name.starts_with('cgltf_') {
12249 arg := call_args[0]
12250 if is_none_like_expr(arg) || (arg is ast.BasicLiteral && arg.value == '0') {
12251 g.sb.write_string('((${name}){0})')
12252 return
12253 }
12254 }
12255 if name in ['panic', 'v_panic'] && call_args.len == 1 {
12256 arg := call_args[0]
12257 arg_type := g.get_expr_type(arg)
12258 if arg_type == 'IError' || (arg is ast.Ident && arg.name == 'err') {
12259 g.sb.write_string('v_panic(IError__str(')
12260 g.expr(arg)
12261 g.sb.write_string('))')
12262 return
12263 }
12264 }
12265 mut local_call_type := ''
12266 if lhs is ast.Ident {
12267 if lhs.name !in g.fn_param_is_ptr && lhs.name !in g.fn_return_types
12268 && name !in g.fn_param_is_ptr && name !in g.fn_return_types
12269 && !g.is_module_local_fn(lhs.name) {
12270 local_call_type = g.get_local_var_c_type(lhs.name) or { '' }
12271 }
12272 }
12273 known_ident_fn := lhs is ast.Ident && (lhs.name in g.fn_param_is_ptr
12274 || lhs.name in g.fn_return_types || g.is_module_local_fn(lhs.name)
12275 || name in g.fn_param_is_ptr || name in g.fn_return_types
12276 || g.is_module_local_fn(name))
12277 if lhs is ast.Ident {
12278 if _ := g.get_local_var_c_type(lhs.name) {
12279 if g.gen_direct_fn_pointer_call(lhs, call_args) {
12280 return
12281 }
12282 }
12283 }
12284 mut selector_call_resolved := false
12285 if lhs is ast.SelectorExpr {
12286 selector_call_resolved = embedded_receiver_owner != '' || name in g.fn_param_is_ptr
12287 || name in g.fn_return_types || g.is_module_local_fn(name)
12288 || g.selector_resolves_to_method_call(lhs)
12289 }
12290 if !known_ident_fn && !selector_call_resolved && g.gen_direct_fn_pointer_call(lhs, call_args) {
12291 return
12292 }
12293 if local_call_type in ['void*', 'voidptr'] {
12294 if name in ['FD_ZERO', 'FD_SET', 'FD_ISSET'] {
12295 g.sb.write_string('${name}(')
12296 for i in 0 .. call_args.len {
12297 arg := call_args[i]
12298 if i > 0 {
12299 g.sb.write_string(', ')
12300 }
12301 g.expr(arg)
12302 }
12303 g.sb.write_string(')')
12304 return
12305 }
12306 fnptr_param_is_ptr := g.fn_pointer_param_is_ptr(lhs)
12307 mut arg_types := []string{cap: call_args.len}
12308 for i in 0 .. call_args.len {
12309 arg := call_args[i]
12310 mut at := g.get_expr_type(arg)
12311 if arg is ast.PrefixExpr && arg.op == .amp && arg.expr is ast.Ident {
12312 inner := arg.expr as ast.Ident
12313 if inner.name in g.cur_fn_mut_params {
12314 if inner_type := g.get_local_var_c_type(inner.name) {
12315 at = inner_type
12316 }
12317 }
12318 }
12319 if at == '' || at == 'int_literal' || at == 'float_literal' {
12320 at = 'int'
12321 }
12322 if at == 'voidptr' {
12323 at = 'void*'
12324 }
12325 if i < fnptr_param_is_ptr.len && fnptr_param_is_ptr[i] && !at.ends_with('*') {
12326 at = ensure_ptr_suffix(at)
12327 }
12328 arg_types << at
12329 }
12330 ret_type := g.fn_pointer_return_type(lhs)
12331 c_ret := if ret_type == '' { 'void' } else { ret_type }
12332 g.sb.write_string('((${c_ret} (*)(')
12333 for i, at in arg_types {
12334 if i > 0 {
12335 g.sb.write_string(', ')
12336 }
12337 g.sb.write_string(at)
12338 }
12339 g.sb.write_string('))')
12340 g.expr(lhs)
12341 g.sb.write_string(')(')
12342 for i in 0 .. call_args.len {
12343 arg := call_args[i]
12344 if i > 0 {
12345 g.sb.write_string(', ')
12346 }
12347 if arg is ast.PrefixExpr && arg.op == .amp && arg.expr is ast.Ident {
12348 inner := arg.expr as ast.Ident
12349 if inner.name in g.cur_fn_mut_params {
12350 g.expr(inner)
12351 continue
12352 }
12353 }
12354 if i < fnptr_param_is_ptr.len && fnptr_param_is_ptr[i] && arg is ast.ModifierExpr {
12355 g.sb.write_u8(`&`)
12356 g.expr(arg.expr)
12357 continue
12358 }
12359 g.expr(arg)
12360 }
12361 g.sb.write_string(')')
12362 return
12363 }
12364 if g.gen_array_contains_call(name, call_args) {
12365 return
12366 }
12367 // Handle type-specific array equality: Array_T__eq(arr1, arr2)
12368 // Generated by transformer for arrays of structs/maps where memcmp won't work
12369 if name.starts_with('Array_') && name.ends_with('__eq') && !name.starts_with('Array_fixed_')
12370 && call_args.len == 2 {
12371 elem_type_name := name['Array_'.len..name.len - '__eq'.len]
12372 g.gen_typed_array_eq(elem_type_name, call_args)
12373 return
12374 }
12375 if name == 'array__eq' && call_args.len == 2 {
12376 // Check if either argument is a fixed array type
12377 arg0_type := g.get_expr_type(call_args[0])
12378 arg1_type := g.get_expr_type(call_args[1])
12379 if arg0_type.starts_with('Array_fixed_') || arg1_type.starts_with('Array_fixed_') {
12380 fixed_type := if arg0_type.starts_with('Array_fixed_') {
12381 arg0_type
12382 } else {
12383 arg1_type
12384 }
12385 // Parse element type from Array_fixed_T_N
12386 elem_type, arr_len := parse_fixed_array_elem_type(fixed_type)
12387 if arr_len > 0 && fixed_array_elem_needs_deep_eq(elem_type) {
12388 // Element-wise comparison for strings, arrays, maps, structs
12389 g.gen_fixed_array_deep_eq(fixed_type, elem_type, arr_len, call_args)
12390 return
12391 }
12392 g.sb.write_string('(memcmp(')
12393 g.gen_fixed_array_cmp_operand(call_args[0], fixed_type)
12394 g.sb.write_string(', ')
12395 g.gen_fixed_array_cmp_operand(call_args[1], fixed_type)
12396 g.sb.write_string(', sizeof(${fixed_type})) == 0)')
12397 return
12398 }
12399 g.sb.write_string('__v2_array_eq(')
12400 if g.expr_is_pointer(call_args[0]) {
12401 g.sb.write_string('*')
12402 }
12403 g.expr(call_args[0])
12404 g.sb.write_string(', ')
12405 if g.expr_is_pointer(call_args[1]) {
12406 g.sb.write_string('*')
12407 }
12408 g.expr(call_args[1])
12409 g.sb.write_string(')')
12410 return
12411 }
12412 if name.starts_with('Array_') && name.ends_with('__sort') && call_args.len == 1 {
12413 g.sb.write_string('array__sort((array*)')
12414 if g.expr_is_pointer(call_args[0]) {
12415 g.expr(call_args[0])
12416 } else {
12417 g.sb.write_string('&')
12418 g.expr(call_args[0])
12419 }
12420 g.sb.write_string(', NULL)')
12421 return
12422 }
12423 if name == 'os__join_path' && call_args.len == 2 && g.get_expr_type(call_args[1]) == 'string' {
12424 g.sb.write_string('os__join_path_single(')
12425 g.gen_call_arg('os__join_path_single', 0, call_args[0])
12426 g.sb.write_string(', ')
12427 g.gen_call_arg('os__join_path_single', 1, call_args[1])
12428 g.sb.write_string(')')
12429 return
12430 }
12431 // array__repeat → array__repeat_to_depth with automatic depth for deep clone
12432 if name == 'array__repeat' && call_args.len == 2 {
12433 elem_type := g.infer_array_elem_type_from_expr(call_args[0]).trim_right('*')
12434 if elem_type != '' && g.is_interface_type(elem_type) {
12435 g.sb.write_string('${array_interface_repeat_fn_name(elem_type)}(')
12436 g.gen_call_arg(name, 0, call_args[0])
12437 g.sb.write_string(', ')
12438 g.gen_call_arg(name, 1, call_args[1])
12439 g.sb.write_string(')')
12440 return
12441 }
12442 g.sb.write_string('array__repeat_to_depth(')
12443 g.gen_call_arg(name, 0, call_args[0])
12444 g.sb.write_string(', ')
12445 g.gen_call_arg(name, 1, call_args[1])
12446 g.sb.write_string(', 3)')
12447 return
12448 }
12449 // array__clone → array__clone_to_depth with depth 0 (shallow memcpy clone).
12450 // Depth > 0 uses element_size heuristics that misidentify non-string types
12451 // of the same size (e.g. tagged unions like ast.Expr are 16 bytes == sizeof(string)).
12452 if name == 'array__clone' && call_args.len == 1 {
12453 g.sb.write_string('array__clone_to_depth(')
12454 g.gen_call_arg(name, 0, call_args[0])
12455 g.sb.write_string(', 0)')
12456 return
12457 }
12458 // array__insert with array arg → array__insert_many
12459 // Only when arg's elem type matches receiver's elem type (not for nested arrays like [][]T.insert([]T))
12460 if name == 'array__insert' && call_args.len == 3 && g.expr_is_array_value(call_args[2])
12461 && g.infer_array_elem_type_from_expr(call_args[2]) == g.infer_array_elem_type_from_expr(call_args[0]) {
12462 rhs_tmp := '_ins_many_tmp_${g.tmp_counter}'
12463 g.tmp_counter++
12464 arr_rhs_type := g.expr_array_runtime_type(call_args[2])
12465 g.sb.write_string('({ ${arr_rhs_type} ${rhs_tmp} = ')
12466 g.expr(call_args[2])
12467 g.sb.write_string('; array__insert_many(')
12468 g.gen_call_arg(name, 0, call_args[0])
12469 g.sb.write_string(', ')
12470 g.gen_call_arg(name, 1, call_args[1])
12471 g.sb.write_string(', ${rhs_tmp}.data, ${rhs_tmp}.len); })')
12472 return
12473 }
12474 // array__prepend with array arg → array__prepend_many
12475 // Only when arg's elem type matches receiver's elem type (not for nested arrays like [][]T.prepend([]T))
12476 if name == 'array__prepend' && call_args.len == 2 && g.expr_is_array_value(call_args[1])
12477 && g.infer_array_elem_type_from_expr(call_args[1]) == g.infer_array_elem_type_from_expr(call_args[0]) {
12478 rhs_tmp := '_prep_many_tmp_${g.tmp_counter}'
12479 g.tmp_counter++
12480 arr_rhs_type := g.expr_array_runtime_type(call_args[1])
12481 g.sb.write_string('({ ${arr_rhs_type} ${rhs_tmp} = ')
12482 g.expr(call_args[1])
12483 g.sb.write_string('; array__prepend_many(')
12484 g.gen_call_arg(name, 0, call_args[0])
12485 g.sb.write_string(', ${rhs_tmp}.data, ${rhs_tmp}.len); })')
12486 return
12487 }
12488 if name in ['array__pop', 'array__pop_left'] && call_args.len == 1 {
12489 elem_type := g.infer_array_elem_type_from_expr(call_args[0])
12490 if elem_type != '' {
12491 g.sb.write_string('(*(${elem_type}*)${name}(')
12492 g.gen_call_arg(name, 0, call_args[0])
12493 g.sb.write_string('))')
12494 return
12495 }
12496 }
12497 if name in ['array__first', 'array__last'] && call_args.len == 1 {
12498 elem_type := g.infer_array_elem_type_from_expr(call_args[0])
12499 if elem_type != '' {
12500 g.sb.write_string('(*(${elem_type}*)${name}(')
12501 g.gen_call_arg(name, 0, call_args[0])
12502 g.sb.write_string('))')
12503 return
12504 }
12505 }
12506 if name == 'voidptr__vbytes' {
12507 name = 'void__vbytes'
12508 }
12509 if name == 'int__bytestr' {
12510 name = 'Array_u8__bytestr'
12511 }
12512 if name == 'signal' && call_args.len == 2 {
12513 g.sb.write_string('signal(')
12514 g.expr(call_args[0])
12515 g.sb.write_string(', ((void (*)(int))')
12516 g.expr(call_args[1])
12517 g.sb.write_string('))')
12518 return
12519 }
12520 // Handle builtin print functions with type-aware argument conversion
12521 if name in ['println', 'eprintln', 'print', 'eprint'] {
12522 if call_args.len == 1 {
12523 arg := call_args[0]
12524 mut arg_type := g.get_expr_type(arg)
12525
12526 mut c_name := name
12527 builtin_name := 'builtin__${name}'
12528 if builtin_name in g.fn_param_is_ptr || builtin_name in g.fn_return_types {
12529 c_name = builtin_name
12530 } else if name in g.fn_param_is_ptr || name in g.fn_return_types {
12531 c_name = name
12532 }
12533
12534 // String literals and interpolation strings are always strings,
12535 // regardless of what position-based type lookup returns.
12536 if arg is ast.StringLiteral || arg is ast.StringInterLiteral
12537 || (arg is ast.BasicLiteral && arg.kind == .string) {
12538 arg_type = 'string'
12539 }
12540
12541 // If argument is already a str-function call, treat as string to avoid double wrapping.
12542 // In freestanding mode, these calls are generated formatting paths and are not
12543 // supported by the raw output hook contract.
12544 mut arg_is_str_conversion_call := false
12545 if arg_type != 'string' {
12546 if arg is ast.CallExpr && arg.lhs is ast.Ident {
12547 called_fn := (arg.lhs as ast.Ident).name
12548 if called_fn.ends_with('_str') || called_fn.ends_with('__str') {
12549 arg_is_str_conversion_call = true
12550 arg_type = 'string'
12551 }
12552 }
12553 }
12554
12555 if g.is_freestanding_target() && (arg_type != 'string' || arg_is_str_conversion_call) {
12556 g.sb.write_string('${c_name}(${g.c_freestanding_missing_format_string_expr()})')
12557 return
12558 }
12559
12560 if arg_type == 'string' {
12561 g.sb.write_string('${c_name}(')
12562 g.expr(arg)
12563 g.sb.write_string(')')
12564 } else if arg is ast.Ident && arg.name == 'err' {
12565 g.sb.write_string('${c_name}(IError__str(')
12566 g.expr(arg)
12567 g.sb.write_string('))')
12568 } else if arg_type == 'IError' {
12569 g.sb.write_string('${c_name}(IError__str(')
12570 g.expr(arg)
12571 g.sb.write_string('))')
12572 } else if arg_type in ['int', 'i8', 'i16', 'i32'] {
12573 g.sb.write_string('${c_name}(int__str(')
12574 g.expr(arg)
12575 g.sb.write_string('))')
12576 } else if arg_type == 'i64' {
12577 g.sb.write_string('${c_name}(i64__str(')
12578 g.expr(arg)
12579 g.sb.write_string('))')
12580 } else if arg_type == 'u64' {
12581 g.sb.write_string('${c_name}(u64__str(')
12582 g.expr(arg)
12583 g.sb.write_string('))')
12584 } else if arg_type in ['f32', 'f64', 'float_literal'] {
12585 str_fn := if arg_type == 'f32' { 'f32__str' } else { 'f64__str' }
12586 g.sb.write_string('${c_name}(${str_fn}(')
12587 g.expr(arg)
12588 g.sb.write_string('))')
12589 } else if arg_type == 'bool' {
12590 g.sb.write_string('${c_name}(bool__str(')
12591 g.expr(arg)
12592 g.sb.write_string('))')
12593 } else {
12594 // Fallback
12595 g.sb.write_string('${c_name}(/* ${arg_type} */ int__str(')
12596 g.expr(arg)
12597 g.sb.write_string('))')
12598 }
12599 return
12600 }
12601 }
12602
12603 // Regular function call - mangle name based on module
12604 mut c_name := name
12605 is_local_call := local_call_type != ''
12606 if is_c_runtime_function(name) || is_generated_global_helper_name(name) {
12607 c_name = name
12608 } else if name != '' && g.cur_module != '' && g.cur_module != 'main'
12609 && g.cur_module != 'builtin' && !name.contains('__') && !is_local_call {
12610 qualified := '${g.cur_module}__${name}'
12611 if qualified in g.fn_param_is_ptr || qualified in g.fn_return_types {
12612 c_name = qualified
12613 } else if name in g.fn_param_is_ptr || name in g.fn_return_types {
12614 c_name = name
12615 } else {
12616 c_name = qualified
12617 }
12618 }
12619 if c_name in ['os__exit', 'builder__exit'] {
12620 c_name = 'exit'
12621 }
12622 canonical_map_runtime := canonical_map_runtime_fn_name(c_name)
12623 if canonical_map_runtime != '' {
12624 c_name = canonical_map_runtime
12625 }
12626 // Rename builtin `panic` to `v_panic` to avoid macOS mach.h conflict.
12627 if c_name == 'panic' {
12628 c_name = 'v_panic'
12629 }
12630 // Apply @[export] name mapping: V-qualified names → export C symbols.
12631 if export_name := g.export_fn_names[c_name] {
12632 c_name = export_name
12633 }
12634 if specialized_name := g.try_specialize_generic_call_name(c_name, call_args) {
12635 if g.generic_specialization_should_replace_call_name(c_name, specialized_name) {
12636 c_name = specialized_name
12637 }
12638 }
12639 if c_name == 'new_array_from_c_array' {
12640 elem_type := g.new_array_from_c_array_elem_type(call_args)
12641 if elem_type != '' {
12642 g.sb.write_string('${c_name}(')
12643 for i in 0 .. call_args.len {
12644 if i > 0 {
12645 g.sb.write_string(', ')
12646 }
12647 if i == 2 {
12648 g.sb.write_string('sizeof(${elem_type})')
12649 } else {
12650 g.gen_call_arg(c_name, i, call_args[i])
12651 }
12652 }
12653 g.sb.write_string(')')
12654 return
12655 }
12656 }
12657 if c_name == 'array__push' && g.gen_array_push_many_for_lowered_array_arg(call_args) {
12658 return
12659 }
12660 g.sb.write_string('${c_name}(')
12661 mut total_args := call_args.len
12662 if param_types := g.fn_param_types[c_name] {
12663 if param_types.len > total_args {
12664 total_args = param_types.len
12665 }
12666 } else if params := g.fn_param_is_ptr[c_name] {
12667 if params.len > total_args {
12668 total_args = params.len
12669 }
12670 }
12671 // For fn-pointer local variable calls, pre-compute param pointer info
12672 // so we can auto-deref mut params when the fn type expects by-value.
12673 mut fnptr_param_is_ptr := []bool{}
12674 if is_local_call && c_name !in g.fn_param_is_ptr {
12675 fnptr_param_is_ptr = g.fn_pointer_param_is_ptr(lhs)
12676 }
12677 for i in 0 .. total_args {
12678 if i > 0 {
12679 g.sb.write_string(', ')
12680 }
12681 if i < call_args.len {
12682 if i == 0 && embedded_receiver_owner != '' && lhs is ast.SelectorExpr {
12683 g.gen_embedded_receiver_arg(lhs.lhs, embedded_receiver_owner, c_name)
12684 continue
12685 }
12686 if i == 0
12687 && c_name in ['array__push', 'array__push_many', 'array__insert', 'array__insert_many', 'array__prepend', 'array__prepend_many', 'array__delete', 'array__delete_many', 'array__clear'] {
12688 if g.gen_runtime_array_pointer_arg(call_args[i]) {
12689 continue
12690 }
12691 }
12692 if c_name == 'array__push' && i == 1 && call_args.len > 0 {
12693 push_arg := call_args[i]
12694 mut handled_array_push_arg := false
12695 if push_arg is ast.PrefixExpr && push_arg.op == .amp {
12696 g.expr(push_arg)
12697 handled_array_push_arg = true
12698 } else if push_arg is ast.ArrayInitExpr {
12699 elem_type := unmangle_c_ptr_type(g.extract_array_elem_type(push_arg.typ))
12700 elem_expr := if push_arg.exprs.len == 1 {
12701 push_arg.exprs[0]
12702 } else {
12703 ast.empty_expr
12704 }
12705 mut elem_expr_type := g.get_expr_type(elem_expr).trim_space()
12706 if (elem_expr_type == '' || elem_expr_type == 'int') && elem_expr is ast.Ident {
12707 elem_expr_type =
12708 (g.get_local_var_c_type(elem_expr.name) or { '' }).trim_space()
12709 }
12710 if elem_type != '' && elem_expr_type.ends_with('*')
12711 && elem_expr_type.trim_right('*') == elem_type {
12712 g.expr(elem_expr)
12713 } else {
12714 g.sb.write_string('&')
12715 g.expr(push_arg)
12716 }
12717 handled_array_push_arg = true
12718 } else if push_arg !is ast.ArrayInitExpr {
12719 _, mut elem_type := g.array_append_elem_type(call_args[0], push_arg)
12720 if elem_type == '' {
12721 elem_type = g.get_expr_type(push_arg).trim_space()
12722 }
12723 if elem_type == '' {
12724 elem_type = 'int'
12725 }
12726 g.gen_array_push_elem_arg(push_arg, elem_type)
12727 handled_array_push_arg = true
12728 }
12729 if handled_array_push_arg {
12730 continue
12731 }
12732 }
12733 if c_name == 'signal' && i == 1 {
12734 g.sb.write_string('((void (*)(int))')
12735 g.expr(call_args[i])
12736 g.sb.write_string(')')
12737 continue
12738 }
12739 // For fn-pointer calls, auto-deref mut params when the fn type expects by-value
12740 if fnptr_param_is_ptr.len > 0 && i < fnptr_param_is_ptr.len && !fnptr_param_is_ptr[i] {
12741 arg := call_args[i]
12742 mut arg_ident_name := ''
12743 if arg is ast.ModifierExpr {
12744 if arg.expr is ast.Ident {
12745 arg_ident_name = arg.expr.name
12746 }
12747 } else if arg is ast.Ident {
12748 arg_ident_name = arg.name
12749 }
12750 if arg_ident_name != '' && arg_ident_name in g.cur_fn_mut_params {
12751 g.sb.write_string('(*')
12752 g.sb.write_string(arg_ident_name)
12753 g.sb.write_string(')')
12754 continue
12755 }
12756 }
12757 g.gen_call_arg(c_name, i, call_args[i])
12758 } else {
12759 // Default arguments should be lowered by transformer; keep C generation moving.
12760 if param_types := g.fn_param_types[c_name] {
12761 if i < param_types.len {
12762 g.sb.write_string(zero_value_for_type(param_types[i]))
12763 } else {
12764 g.sb.write_string('0')
12765 }
12766 } else {
12767 g.sb.write_string('0')
12768 }
12769 }
12770 }
12771 g.sb.write_string(')')
12772}
12773
12774fn (mut g Gen) generic_specialization_should_replace_call_name(current string, specialized string) bool {
12775 if specialized == '' || specialized == current {
12776 return true
12777 }
12778 if !current.contains('_T_') {
12779 if current.ends_with('_T') && specialized.contains('_T_') {
12780 return current[..current.len - 2] == specialized.all_before('_T_')
12781 }
12782 return true
12783 }
12784 if specialized.starts_with('${current}_T_') {
12785 return true
12786 }
12787 if specialized.contains('_T_') && current.all_before('_T_') == specialized.all_before('_T_')
12788 && g.generic_call_name_has_placeholder_suffix(current) {
12789 return true
12790 }
12791 return current.contains('int_literal') || current.contains('float_literal')
12792}
12793
12794fn (mut g Gen) generic_call_name_has_placeholder_suffix(name string) bool {
12795 if name.ends_with('_T') && !name.contains('_T_') {
12796 return true
12797 }
12798 if !name.contains('_T_') {
12799 return false
12800 }
12801 base_name := name.all_before('_T_')
12802 decl := g.find_generic_fn_decl_by_base_name(base_name) or { return false }
12803 generic_params := g.generic_fn_param_names(decl)
12804 embedded_args := generic_call_embedded_type_arg_names_from_name(name, generic_params.len)
12805 if embedded_args.len != generic_params.len {
12806 return false
12807 }
12808 for i, param_name in generic_params {
12809 if embedded_args[i] == param_name {
12810 return true
12811 }
12812 }
12813 return false
12814}
12815
12816fn (mut g Gen) ensure_cur_fn_scope() ?&types.Scope {
12817 if g.cur_fn_scope != unsafe { nil } {
12818 return g.cur_fn_scope
12819 }
12820 if g.env == unsafe { nil } || g.cur_fn_name == '' {
12821 return none
12822 }
12823 miss_key := '${g.cur_module}.${g.cur_fn_name}'
12824 if g.cur_fn_scope_miss_key == miss_key {
12825 return none
12826 }
12827 if fn_scope := g.env.get_fn_scope(g.cur_module, g.cur_fn_name) {
12828 g.cur_fn_scope = fn_scope
12829 g.cur_fn_scope_miss_key = ''
12830 return fn_scope
12831 }
12832 g.cur_fn_scope_miss_key = miss_key
12833 return none
12834}
12835
12836fn (g &Gen) env_fn_scope_by_key(key string) ?&types.Scope {
12837 if g.env == unsafe { nil } {
12838 return none
12839 }
12840 return g.env.get_fn_scope_by_key(key)
12841}
12842
12843fn (g &Gen) fn_param_wants_pointer(fn_name string, idx int) bool {
12844 if ptr_params := g.fn_param_is_ptr[fn_name] {
12845 if idx < ptr_params.len && ptr_params[idx] {
12846 return true
12847 }
12848 }
12849 if param_types := g.fn_param_types[fn_name] {
12850 if idx < param_types.len && param_types[idx].trim_space().ends_with('*') {
12851 return true
12852 }
12853 }
12854 return false
12855}
12856
12857fn ensure_ptr_suffix(typ string) string {
12858 if typ.ends_with('*') {
12859 return typ
12860 }
12861 mut sb := strings.new_builder(typ.len + 1)
12862 sb.write_string(typ)
12863 sb.write_u8(`*`)
12864 return sb.str()
12865}
12866
12867fn anon_fn_name(counter int) string {
12868 mut sb := strings.new_builder(20)
12869 sb.write_string('_anon_fn_')
12870 sb.write_string(counter.str())
12871 return sb.str()
12872}
12873
12874fn fn_literal_c_param_name(idx int) string {
12875 mut sb := strings.new_builder(16)
12876 sb.write_string('_fnlit_p')
12877 sb.write_string(idx.str())
12878 return sb.str()
12879}
12880
12881struct FnLiteralCaptureInfo {
12882 name string
12883 c_type string
12884 assign_expr ast.Expr
12885 is_mut bool
12886 storage_name string
12887 is_fn_pointer bool
12888 fn_pointer_raw_type types.Type = types.Type(types.void_)
12889}
12890
12891fn (mut g Gen) fn_pointer_c_type_from_raw(raw_type types.Type) string {
12892 fn_type := extract_fn_type(raw_type) or { return '' }
12893 ret_type := if rt := fn_type.get_return_type() {
12894 g.fn_return_type_to_c(rt)
12895 } else {
12896 'void'
12897 }
12898 param_types := fn_type.get_param_types()
12899 mut params := []string{cap: param_types.len}
12900 for param_type in param_types {
12901 params << g.types_type_to_c(param_type)
12902 }
12903 if params.len == 0 {
12904 params << 'void'
12905 }
12906 return '${ret_type} (*)(${params.join(', ')})'
12907}
12908
12909fn (mut g Gen) fn_pointer_c_decl_from_raw(raw_type types.Type, name string) string {
12910 fn_type := g.fn_pointer_c_type_from_raw(raw_type)
12911 if fn_type == '' {
12912 return ''
12913 }
12914 return c_fn_pointer_type_with_name(fn_type, name)
12915}
12916
12917fn (mut g Gen) fn_literal_capture_infos(anon_name string, captured_vars []ast.Expr) []FnLiteralCaptureInfo {
12918 mut infos := []FnLiteralCaptureInfo{cap: captured_vars.len}
12919 for i, captured in captured_vars {
12920 mut assign_expr := captured
12921 mut is_mut := false
12922 if captured is ast.ModifierExpr {
12923 assign_expr = captured.expr
12924 is_mut = true
12925 }
12926 if assign_expr !is ast.Ident {
12927 continue
12928 }
12929 name := (assign_expr as ast.Ident).name
12930 if name == '' {
12931 continue
12932 }
12933 if raw_fn_type := g.local_fn_pointer_raw_type(name) {
12934 infos << FnLiteralCaptureInfo{
12935 name: name
12936 c_type: 'fn_ptr'
12937 assign_expr: assign_expr
12938 is_mut: is_mut
12939 storage_name: '${anon_name}_capture_${i}'
12940 is_fn_pointer: true
12941 fn_pointer_raw_type: raw_fn_type
12942 }
12943 continue
12944 }
12945 mut c_type := g.local_var_c_type_for_expr(assign_expr) or { '' }
12946 if c_type == '' {
12947 c_type = g.get_expr_type(assign_expr)
12948 }
12949 if c_type == '' || c_type == 'void' {
12950 c_type = 'void*'
12951 }
12952 if is_mut && !c_type.ends_with('*') {
12953 c_type = ensure_ptr_suffix(c_type)
12954 }
12955 infos << FnLiteralCaptureInfo{
12956 name: name
12957 c_type: c_type
12958 assign_expr: assign_expr
12959 is_mut: is_mut
12960 storage_name: '${anon_name}_capture_${i}'
12961 }
12962 }
12963 return infos
12964}
12965
12966fn (mut g Gen) gen_fn_literal(node ast.FnLiteral) {
12967 anon_name := anon_fn_name(g.tmp_counter)
12968 g.tmp_counter++
12969 capture_infos := g.fn_literal_capture_infos(anon_name, node.captured_vars)
12970
12971 // Determine return type
12972 ret_type := if node.typ.return_type !is ast.EmptyExpr {
12973 g.expr_type_to_c(node.typ.return_type)
12974 } else {
12975 'void'
12976 }
12977
12978 // Save current state
12979 saved_sb := g.sb
12980 saved_indent := g.indent
12981 saved_fn_name := g.cur_fn_name
12982 saved_fn_ret := g.cur_fn_ret_type
12983 saved_fn_scope := g.cur_fn_scope
12984 saved_fn_mut_params := g.cur_fn_mut_params.clone()
12985 saved_local_types := g.runtime_local_types.clone()
12986 saved_decl_types := g.runtime_decl_types.clone()
12987 saved_runtime_fn_pointer_types := g.runtime_fn_pointer_types.clone()
12988 saved_returned_idents := g.cur_fn_returned_idents.clone()
12989
12990 // Use new builder for anon fn
12991 g.sb = strings.new_builder(1024)
12992 g.indent = 0
12993 g.cur_fn_name = anon_name
12994 g.cur_fn_ret_type = ret_type
12995 g.cur_fn_scope = unsafe { nil }
12996 g.cur_fn_mut_params.clear()
12997 g.runtime_local_types.clear()
12998 g.runtime_decl_types.clear()
12999 g.runtime_fn_pointer_types.clear()
13000 g.cur_fn_returned_idents = g.collect_returned_idents(node.stmts)
13001
13002 // Generate function definition
13003 for capture in capture_infos {
13004 if capture.is_fn_pointer {
13005 decl := g.fn_pointer_c_decl_from_raw(capture.fn_pointer_raw_type, capture.storage_name)
13006 if decl != '' {
13007 g.sb.writeln('static ${decl};')
13008 }
13009 } else {
13010 g.sb.writeln('static ${capture.c_type} ${capture.storage_name};')
13011 }
13012 }
13013 g.sb.write_string('static ')
13014 g.sb.write_string(ret_type)
13015 g.sb.write_u8(` `)
13016 g.sb.write_string(anon_name)
13017 g.sb.write_u8(`(`)
13018 for i, param in node.typ.params {
13019 if i > 0 {
13020 g.sb.write_string(', ')
13021 }
13022 param_type := g.expr_type_to_c(param.typ)
13023 // Use the original parameter name so body references match.
13024 param_name := if param.name != '' {
13025 param.name
13026 } else {
13027 fn_literal_c_param_name(i)
13028 }
13029 param_c_type := if param.is_mut && !param_type.ends_with('*') {
13030 ensure_ptr_suffix(param_type)
13031 } else {
13032 param_type
13033 }
13034 g.sb.write_string(param_c_type)
13035 g.sb.write_u8(` `)
13036 g.sb.write_string(param_name)
13037 if param.is_mut {
13038 g.cur_fn_mut_params[param_name] = true
13039 }
13040 // Register param type so expr_is_pointer and auto-deref work correctly
13041 ptype := if param.is_mut && !param_type.ends_with('*') {
13042 ensure_ptr_suffix(param_type)
13043 } else {
13044 param_c_type
13045 }
13046 g.remember_runtime_local_type(param_name, ptype)
13047 }
13048 if node.typ.params.len == 0 {
13049 g.sb.write_string('void')
13050 }
13051 g.sb.writeln(') {')
13052 g.indent = 1
13053 for capture in capture_infos {
13054 if capture.is_mut {
13055 g.cur_fn_mut_params[capture.name] = true
13056 }
13057 if capture.is_fn_pointer {
13058 g.runtime_fn_pointer_types[capture.name] = capture.fn_pointer_raw_type
13059 decl := g.fn_pointer_c_decl_from_raw(capture.fn_pointer_raw_type, capture.name)
13060 if decl != '' {
13061 g.sb.writeln('\t${decl} = ${capture.storage_name};')
13062 }
13063 } else {
13064 g.remember_runtime_local_type(capture.name, capture.c_type)
13065 g.sb.writeln('\t${capture.c_type} ${capture.name} = ${capture.storage_name};')
13066 }
13067 }
13068 g.gen_stmts(node.stmts)
13069 g.sb.writeln('}')
13070 g.sb.writeln('')
13071
13072 // Capture anon fn content
13073 anon_def := g.sb.str()
13074 g.anon_fn_defs << anon_def
13075
13076 // Restore state
13077 g.sb = saved_sb
13078 g.indent = saved_indent
13079 g.cur_fn_name = saved_fn_name
13080 g.cur_fn_ret_type = saved_fn_ret
13081 g.cur_fn_scope = saved_fn_scope
13082 g.cur_fn_mut_params = saved_fn_mut_params.clone()
13083 g.runtime_local_types = saved_local_types.clone()
13084 g.runtime_decl_types = saved_decl_types.clone()
13085 g.runtime_fn_pointer_types = saved_runtime_fn_pointer_types.clone()
13086 g.cur_fn_returned_idents = saved_returned_idents.clone()
13087
13088 // Emit function name at use site
13089 if capture_infos.len == 0 {
13090 g.sb.write_string(anon_name)
13091 return
13092 }
13093 g.sb.write_string('({ ')
13094 for capture in capture_infos {
13095 g.sb.write_string('${capture.storage_name} = ')
13096 if capture.is_mut && !g.expr_is_pointer(capture.assign_expr)
13097 && !g.expr_produces_pointer(capture.assign_expr) {
13098 g.sb.write_u8(`&`)
13099 g.expr(capture.assign_expr)
13100 } else {
13101 g.expr(capture.assign_expr)
13102 }
13103 g.sb.write_string('; ')
13104 }
13105 g.sb.write_string('${anon_name}; })')
13106}
13107
13108fn (g &Gen) alias_base_c_type(type_name string) ?string {
13109 if type_name == '' {
13110 return none
13111 }
13112 cache_key := '${g.cur_module}|${type_name}'
13113 if cached := g.alias_base_lookup_cache[cache_key] {
13114 return cached
13115 }
13116 if cache_key in g.alias_base_lookup_miss {
13117 return none
13118 }
13119 if base_name := g.alias_base_types[type_name] {
13120 if base_name != '' && base_name != type_name {
13121 unsafe {
13122 mut self := g
13123 self.alias_base_lookup_cache[cache_key] = base_name
13124 }
13125 return base_name
13126 }
13127 }
13128 short_name := type_name.all_after_last('__')
13129 if g.has_flat() {
13130 for i in 0 .. g.flat.files.len {
13131 fc := g.flat.file_cursor(i)
13132 mod_name := flat_file_module_name(fc)
13133 stmts := fc.stmts()
13134 for j in 0 .. stmts.len() {
13135 stmt := stmts.at(j)
13136 if stmt.kind() != .stmt_type_decl {
13137 continue
13138 }
13139 decl := stmt.type_decl()
13140 candidate := if mod_name != '' && mod_name != 'main' && mod_name != 'builtin' {
13141 '${mod_name}__${decl.name}'
13142 } else {
13143 decl.name
13144 }
13145 if candidate != type_name && decl.name != short_name {
13146 continue
13147 }
13148 base_name := decl.base_type.name().replace('.', '__')
13149 if base_name != '' && base_name != type_name {
13150 unsafe {
13151 mut self := g
13152 self.alias_base_lookup_cache[cache_key] = base_name
13153 }
13154 return base_name
13155 }
13156 }
13157 }
13158 } else {
13159 for file in g.files {
13160 mod_name := file.mod.replace('.', '_')
13161 for stmt in file.stmts {
13162 if stmt is ast.TypeDecl {
13163 candidate := if mod_name != '' && mod_name != 'main' && mod_name != 'builtin' {
13164 '${mod_name}__${stmt.name}'
13165 } else {
13166 stmt.name
13167 }
13168 if candidate != type_name && stmt.name != short_name {
13169 continue
13170 }
13171 base_name := stmt.base_type.name().replace('.', '__')
13172 if base_name != '' && base_name != type_name {
13173 unsafe {
13174 mut self := g
13175 self.alias_base_lookup_cache[cache_key] = base_name
13176 }
13177 return base_name
13178 }
13179 }
13180 }
13181 }
13182 }
13183 if g.env == unsafe { nil } {
13184 unsafe {
13185 mut self := g
13186 self.alias_base_lookup_miss[cache_key] = true
13187 }
13188 return none
13189 }
13190 mut modules := []string{}
13191 if type_name.contains('__') {
13192 modules << type_name.all_before_last('__')
13193 }
13194 if g.cur_module != '' && g.cur_module !in modules {
13195 modules << g.cur_module
13196 }
13197 if 'builtin' !in modules {
13198 modules << 'builtin'
13199 }
13200 for mod_name in modules {
13201 if mod_name == '' {
13202 continue
13203 }
13204 if mut scope := g.env_scope(mod_name) {
13205 for candidate in [type_name, short_name] {
13206 if candidate == '' {
13207 continue
13208 }
13209 if obj := scope.lookup_parent(candidate, 0) {
13210 if obj is types.Type && obj is types.Alias {
13211 alias_obj := obj as types.Alias
13212 base_name := g.types_type_to_c(alias_obj.base_type)
13213 if base_name != '' && base_name != type_name {
13214 unsafe {
13215 mut self := g
13216 self.alias_base_lookup_cache[cache_key] = base_name
13217 }
13218 return base_name
13219 }
13220 }
13221 }
13222 }
13223 }
13224 }
13225 if g.has_flat() {
13226 for i in 0 .. g.flat.files.len {
13227 mod_name := flat_file_module_name(g.flat.file_cursor(i))
13228 if mod_name == '' || mod_name in modules {
13229 continue
13230 }
13231 if mut scope := g.env_scope(mod_name) {
13232 if obj := scope.lookup_parent(short_name, 0) {
13233 if obj is types.Type && obj is types.Alias {
13234 alias_obj := obj as types.Alias
13235 base_name := g.types_type_to_c(alias_obj.base_type)
13236 if base_name != '' && base_name != type_name {
13237 unsafe {
13238 mut self := g
13239 self.alias_base_lookup_cache[cache_key] = base_name
13240 }
13241 return base_name
13242 }
13243 }
13244 }
13245 }
13246 }
13247 } else {
13248 for file in g.files {
13249 mod_name := file.mod.replace('.', '_')
13250 if mod_name == '' || mod_name in modules {
13251 continue
13252 }
13253 if mut scope := g.env_scope(mod_name) {
13254 if obj := scope.lookup_parent(short_name, 0) {
13255 if obj is types.Type && obj is types.Alias {
13256 alias_obj := obj as types.Alias
13257 base_name := g.types_type_to_c(alias_obj.base_type)
13258 if base_name != '' && base_name != type_name {
13259 unsafe {
13260 mut self := g
13261 self.alias_base_lookup_cache[cache_key] = base_name
13262 }
13263 return base_name
13264 }
13265 }
13266 }
13267 }
13268 }
13269 }
13270 unsafe {
13271 mut self := g
13272 self.alias_base_lookup_miss[cache_key] = true
13273 }
13274 return none
13275}
13276
13277fn (g &Gen) get_str_fn_for_type(expr_type string) ?string {
13278 return g.get_str_fn_for_type_seen(expr_type, []string{})
13279}
13280
13281fn (g &Gen) get_str_fn_for_type_seen(expr_type string, seen []string) ?string {
13282 if expr_type == ''
13283 || expr_type in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool', 'string', 'char', 'rune', 'voidptr', 'byteptr', 'charptr'] {
13284 return none
13285 }
13286 // Check for int__str, Array_int_str, etc.
13287 str_fn := '${expr_type}_str'
13288 if str_fn in g.fn_return_types || str_fn in g.fn_param_is_ptr {
13289 return str_fn
13290 }
13291 // Also check the __str variant
13292 str_fn2 := '${expr_type}__str'
13293 if str_fn2 in g.fn_return_types || str_fn2 in g.fn_param_is_ptr {
13294 return str_fn2
13295 }
13296 // For array types, try Array_ELEM_str / Array_fixed_ELEM_N_str
13297 if expr_type == 'array' || expr_type.starts_with('Array_') {
13298 if expr_type == 'array' {
13299 return 'Array_int_str'
13300 }
13301 return '${expr_type}_str'
13302 }
13303 // For Map types, try Map_K_V_str
13304 if expr_type.starts_with('Map_') {
13305 return '${expr_type}_str'
13306 }
13307 if expr_type in seen {
13308 return none
13309 }
13310 if base_type := g.alias_base_c_type(expr_type) {
13311 mut next_seen := seen.clone()
13312 next_seen << expr_type
13313 if base_str_fn := g.get_str_fn_for_type_seen(base_type, next_seen) {
13314 return base_str_fn
13315 }
13316 }
13317 return none
13318}
13319
13320fn expr_has_generic_placeholder(e ast.Expr) bool {
13321 match e {
13322 ast.Ident {
13323 return is_generic_placeholder_type_name(e.name)
13324 }
13325 ast.PrefixExpr {
13326 return expr_has_generic_placeholder(e.expr)
13327 }
13328 ast.ModifierExpr {
13329 return expr_has_generic_placeholder(e.expr)
13330 }
13331 ast.SelectorExpr {
13332 return expr_has_generic_placeholder(e.lhs)
13333 || is_generic_placeholder_type_name(e.rhs.name)
13334 }
13335 ast.GenericArgOrIndexExpr {
13336 return expr_has_generic_placeholder(e.lhs) || expr_has_generic_placeholder(e.expr)
13337 }
13338 ast.GenericArgs {
13339 if expr_has_generic_placeholder(e.lhs) {
13340 return true
13341 }
13342 for arg in e.args {
13343 if expr_has_generic_placeholder(arg) {
13344 return true
13345 }
13346 }
13347 return false
13348 }
13349 ast.Type {
13350 match e {
13351 ast.GenericType {
13352 if expr_has_generic_placeholder(e.name) {
13353 return true
13354 }
13355 for param in e.params {
13356 if expr_has_generic_placeholder(param) {
13357 return true
13358 }
13359 }
13360 return false
13361 }
13362 ast.ArrayType {
13363 return expr_has_generic_placeholder(e.elem_type)
13364 }
13365 ast.ArrayFixedType {
13366 return expr_has_generic_placeholder(e.elem_type)
13367 || expr_has_generic_placeholder(e.len)
13368 }
13369 ast.MapType {
13370 return expr_has_generic_placeholder(e.key_type)
13371 || expr_has_generic_placeholder(e.value_type)
13372 }
13373 ast.OptionType {
13374 return expr_has_generic_placeholder(e.base_type)
13375 }
13376 ast.ResultType {
13377 return expr_has_generic_placeholder(e.base_type)
13378 }
13379 ast.PointerType {
13380 return expr_has_generic_placeholder(e.base_type)
13381 }
13382 ast.FnType {
13383 return fn_type_has_generic_placeholder(e)
13384 }
13385 else {}
13386 }
13387
13388 return false
13389 }
13390 else {
13391 return false
13392 }
13393 }
13394}
13395
13396fn fn_type_has_generic_placeholder(t ast.FnType) bool {
13397 for p in t.params {
13398 if expr_has_generic_placeholder(p.typ) {
13399 return true
13400 }
13401 }
13402 if t.return_type !is ast.EmptyExpr {
13403 return expr_has_generic_placeholder(t.return_type)
13404 }
13405 return false
13406}
13407
13408fn module_prefix_from_fn_name(fn_name string) string {
13409 idx := fn_name.index('__') or { return '' }
13410 if idx <= 0 {
13411 return ''
13412 }
13413 return fn_name[..idx]
13414}
13415
13416fn (g &Gen) source_module_exists(module_name string) bool {
13417 if module_name == '' {
13418 return false
13419 }
13420 if g.source_module_names.len > 0 {
13421 if module_name in g.source_module_names {
13422 return true
13423 }
13424 }
13425 if g.env != unsafe { nil } {
13426 if _ := g.env.get_scope(module_name) {
13427 return true
13428 }
13429 }
13430 if g.has_flat() {
13431 for i in 0 .. g.flat.files.len {
13432 if flat_file_module_name(g.flat.file_cursor(i)) == module_name {
13433 return true
13434 }
13435 }
13436 return false
13437 }
13438 for file in g.files {
13439 if file_module_name(file) == module_name {
13440 return true
13441 }
13442 }
13443 return false
13444}
13445
13446fn (mut g Gen) emit_extract_attr_specialization(fn_name string, suffix string, elem_c_type string) {
13447 spec_fn := '${fn_name}_${suffix}'
13448 mod_prefix := module_prefix_from_fn_name(fn_name)
13449 mut result_type := g.fn_return_types[fn_name] or { '' }
13450 if result_type == '' || !result_type.starts_with('_result_') {
13451 result_type = '_result_Array_' + mangle_alias_component(elem_c_type)
13452 }
13453 mut array_type := g.result_value_type(result_type)
13454 if array_type == '' || !array_type.starts_with('Array_') {
13455 array_type = 'Array_' + mangle_alias_component(elem_c_type)
13456 }
13457 g.register_alias_type(array_type)
13458 g.register_alias_type(result_type)
13459 accessor_fn := if mod_prefix != '' {
13460 '${mod_prefix}__accessor_data_ptr'
13461 } else {
13462 'accessor_data_ptr'
13463 }
13464 parsed_data_sym := if mod_prefix != '' {
13465 '${mod_prefix}__parsed_data'
13466 } else {
13467 'parsed_data'
13468 }
13469 g.sb.writeln('${result_type} ${spec_fn}(cgltf_primitive prim, string attr_name, int attr_type, int attr_index, int expected_comp, int expected_count) {')
13470 g.sb.writeln('\textern struct cgltf_data* ${parsed_data_sym};')
13471 g.sb.writeln('\tfor (int a = 0; a < prim.attributes_count; a++) {')
13472 g.sb.writeln('\t\tcgltf_attribute attr = prim.attributes[a];')
13473 g.sb.writeln('\t\tbool matches = false;')
13474 g.sb.writeln('\t\tif (attr.name != NULL) {')
13475 g.sb.writeln('\t\t\tif (string__eq(attr_name, tos3(attr.name))) {')
13476 g.sb.writeln('\t\t\t\tmatches = true;')
13477 g.sb.writeln('\t\t\t}')
13478 g.sb.writeln('\t\t} else {')
13479 g.sb.writeln('\t\t\tif (attr_type == attr.type && (attr_index < 0 || attr.index == attr_index)) {')
13480 g.sb.writeln('\t\t\t\tmatches = true;')
13481 g.sb.writeln('\t\t\t}')
13482 g.sb.writeln('\t\t}')
13483 g.sb.writeln('\t\tif (!matches) {')
13484 g.sb.writeln('\t\t\tcontinue;')
13485 g.sb.writeln('\t\t}')
13486 g.sb.writeln('\t\tcgltf_accessor* acc = attr.data;')
13487 g.sb.writeln('\t\tif (acc->component_type != expected_comp || acc->type != expected_count) {')
13488 g.sb.writeln('\t\t\tcontinue;')
13489 g.sb.writeln('\t\t}')
13490 g.sb.writeln('\t\tvoid* ptr = ${accessor_fn}(${parsed_data_sym}, acc);')
13491 g.sb.writeln('\t\tint count = ((int)(acc->count)) * expected_count;')
13492 g.sb.writeln('\t\t${array_type} data = __new_array_with_default_noscan(count, 0, sizeof(${elem_c_type}), NULL);')
13493 g.sb.writeln('\t\tmemcpy(data.data, ptr, count * ((int)sizeof(${elem_c_type})));')
13494 g.sb.writeln('\t\treturn ({ ${result_type} _res = (${result_type}){0}; ${array_type} _val = data; _result_ok(&_val, (_result*)&_res, sizeof(_val)); _res; });')
13495 g.sb.writeln('\t}')
13496 g.sb.writeln('\t${array_type} empty_data = __new_array_with_default_noscan(0, 0, sizeof(${elem_c_type}), NULL);')
13497 g.sb.writeln('\treturn ({ ${result_type} _res = (${result_type}){0}; ${array_type} _val = empty_data; _result_ok(&_val, (_result*)&_res, sizeof(_val)); _res; });')
13498 g.sb.writeln('}')
13499}
13500
13501fn generic_fn_has_macro_fallback(node ast.FnDecl) bool {
13502 return (node.typ.params.len == 1 && node.name == 'abs')
13503 || (node.typ.params.len == 2 && node.name in ['min', 'max'])
13504 || (node.typ.params.len == 3 && node.name == 'clamp')
13505 || (node.typ.params.len == 0 && node.name in ['maxof', 'minof'])
13506}
13507
13508// emit_generic_fn_macro emits a C macro definition for known simple generic functions
13509// (abs, min, max, clamp, maxof, minof).
13510fn (mut g Gen) emit_generic_fn_macro(fn_name string, node ast.FnDecl) {
13511 macro_key := 'generic_macro_${fn_name}'
13512 if macro_key in g.emitted_types {
13513 return
13514 }
13515 g.emitted_types[macro_key] = true
13516 if node.typ.params.len == 1 && node.name == 'abs' {
13517 pname := node.typ.params[0].name
13518 g.sb.writeln('#define ${fn_name}(${pname}) ((${pname}) < 0 ? -(${pname}) : (${pname}))')
13519 } else if node.typ.params.len == 2 && node.name == 'min' {
13520 p0 := node.typ.params[0].name
13521 p1 := node.typ.params[1].name
13522 g.sb.writeln('#define ${fn_name}(${p0}, ${p1}) ((${p0}) < (${p1}) ? (${p0}) : (${p1}))')
13523 } else if node.typ.params.len == 2 && node.name == 'max' {
13524 p0 := node.typ.params[0].name
13525 p1 := node.typ.params[1].name
13526 g.sb.writeln('#define ${fn_name}(${p0}, ${p1}) ((${p0}) > (${p1}) ? (${p0}) : (${p1}))')
13527 } else if node.typ.params.len == 3 && node.name == 'clamp' {
13528 p0 := node.typ.params[0].name
13529 p1 := node.typ.params[1].name
13530 p2 := node.typ.params[2].name
13531 g.sb.writeln('#define ${fn_name}(${p0}, ${p1}, ${p2}) ((${p0}) < (${p1}) ? (${p1}) : ((${p0}) > (${p2}) ? (${p2}) : (${p0})))')
13532 } else if node.typ.params.len == 0 && node.name in ['maxof', 'minof'] {
13533 // Emit specialized macros for maxof[T]()/minof[T]() - comptime functions
13534 // that return the min/max value for each numeric type.
13535 prefix := if fn_name.contains('__') { fn_name.all_before_last('__') + '__' } else { '' }
13536 is_max := node.name == 'maxof'
13537 // max_f32/max_f64 are in the math module and emitted as math__max_f32/math__max_f64
13538 type_const_pairs := if is_max {
13539 [['i8', 'max_i8'], ['i16', 'max_i16'], ['i32', 'max_i32'],
13540 ['i64', 'max_i64'], ['int', 'max_int'], ['u8', 'max_u8'],
13541 ['u16', 'max_u16'], ['u32', 'max_u32'], ['u64', 'max_u64'],
13542 ['f32', 'math__max_f32'], ['f64', 'math__max_f64']]
13543 } else {
13544 [['i8', 'min_i8'], ['i16', 'min_i16'], ['i32', 'min_i32'],
13545 ['i64', 'min_i64'], ['int', 'min_int'], ['u8', '((u8)(0))'],
13546 ['u16', '((u16)(0))'], ['u32', '((u32)(0))'],
13547 ['u64', '((u64)(0))'], ['f32', '(-math__max_f32)'],
13548 ['f64', '(-math__max_f64)']]
13549 }
13550 for pair in type_const_pairs {
13551 g.sb.writeln('#define ${prefix}${node.name}_${pair[0]}() (${pair[1]})')
13552 }
13553 }
13554 // Emit typedef for generic type parameter T as f64 (default numeric type)
13555 for gp in node.typ.generic_params {
13556 if gp is ast.Ident {
13557 qualified := if fn_name.contains('__') {
13558 '${fn_name.all_before_last('__')}__${gp.name}'
13559 } else {
13560 gp.name
13561 }
13562 tdef_key := 'typedef_${qualified}'
13563 if tdef_key !in g.emitted_types {
13564 g.emitted_types[tdef_key] = true
13565 g.sb.writeln('typedef f64 ${qualified};')
13566 }
13567 }
13568 }
13569}
13570
13571fn (mut g Gen) emit_unresolved_generic_stub(fn_name string, stub_ret string) {
13572 g.sb.writeln('/* unresolved generic */ ${stub_ret} ${fn_name}() {')
13573 g.sb.writeln('#if !defined(__TINYC__)')
13574 g.sb.writeln('#warning "v2: unresolved generic stub: ${fn_name}"')
13575 g.sb.writeln('#endif')
13576 if g.is_freestanding_target() {
13577 if g.has_freestanding_hook_capability('panic') {
13578 msg := 'v2: unresolved generic call: ${fn_name}\n'
13579 msg_lit := c_string_literal_content_to_c(msg)
13580 g.sb.writeln('\tv_platform_panic((const u8*)${msg_lit}, ${msg.len});')
13581 g.sb.writeln('\tfor (;;) {}')
13582 } else {
13583 g.sb.writeln('#error "v2: unresolved generic stub requires freestanding panic platform hook"')
13584 g.sb.writeln('\tfor (;;) {}')
13585 }
13586 } else {
13587 g.sb.writeln('\tfputs("v2: unresolved generic call: ${fn_name}\\n", stderr);')
13588 g.sb.writeln('\tabort();')
13589 }
13590 if stub_ret != 'void' {
13591 // Unreachable, but keeps compilers that warn about missing return values happy.
13592 g.sb.writeln('\treturn ${zero_value_for_type(stub_ret)};')
13593 }
13594 g.sb.writeln('}')
13595}
13596
13597// gen_comptime_method_call emits a call to the current iteration's method:
13598// `app.$method(mut ctx)` -> `App__index(&app, &ctx)` (when method.name == 'index')
13599// The receiver type is taken from comptime_method_receiver_type (the struct being iterated).
13600fn (mut g Gen) gen_comptime_method_call(receiver ast.Expr, args []ast.Expr) {
13601 method_name := g.comptime_method_name
13602 recv_c_type := g.comptime_method_receiver_type
13603 fn_name := '${recv_c_type}__${method_name}'
13604
13605 // Determine receiver expr C-side: pass by pointer if the function expects pointer.
13606 ptr_params := g.fn_param_is_ptr[fn_name] or { []bool{} }
13607 recv_wants_ptr := ptr_params.len > 0 && ptr_params[0]
13608
13609 mut recv_type := g.get_expr_type(receiver)
13610 if receiver is ast.Ident {
13611 if local_type := g.get_local_var_c_type(receiver.name) {
13612 if local_type != '' && local_type != 'int' {
13613 recv_type = local_type
13614 }
13615 }
13616 }
13617 recv_is_ptr := recv_type.ends_with('*')
13618
13619 g.sb.write_string('${fn_name}(')
13620 if recv_wants_ptr && !recv_is_ptr {
13621 g.sb.write_string('&')
13622 g.expr(receiver)
13623 } else if !recv_wants_ptr && recv_is_ptr {
13624 g.sb.write_string('(*')
13625 g.expr(receiver)
13626 g.sb.write_string(')')
13627 } else {
13628 g.expr(receiver)
13629 }
13630
13631 // Emit remaining args. Unwraps `mut x` (KeywordOperator). For `...args`
13632 // spread, expand to one indexed access per remaining target param slot,
13633 // typed using the resolved fn's parameter types.
13634 param_types := g.fn_param_types[fn_name] or { []string{} }
13635 expected_extra := if param_types.len > 0 { param_types.len - 1 } else { 0 }
13636 mut emitted := 0
13637 for arg in args {
13638 if arg is ast.PrefixExpr && arg.op == .ellipsis {
13639 remaining := expected_extra - emitted
13640 for d_count in 0 .. remaining {
13641 slot_idx := 1 + emitted
13642 elem_type := if slot_idx < param_types.len {
13643 param_types[slot_idx]
13644 } else {
13645 'string'
13646 }
13647 g.sb.write_string(', ((${elem_type}*)(')
13648 g.expr(arg.expr)
13649 g.sb.write_string(').data)[${d_count}]')
13650 emitted++
13651 }
13652 continue
13653 }
13654 g.sb.write_string(', ')
13655 if arg is ast.KeywordOperator && arg.op == .key_mut && arg.exprs.len > 0 {
13656 inner := arg.exprs[0]
13657 mut inner_type := g.get_expr_type(inner)
13658 if inner is ast.Ident {
13659 if local_type := g.get_local_var_c_type(inner.name) {
13660 if local_type != '' && local_type != 'int' {
13661 inner_type = local_type
13662 }
13663 }
13664 }
13665 if !inner_type.ends_with('*') {
13666 g.sb.write_string('&')
13667 }
13668 g.expr(inner)
13669 } else {
13670 g.expr(arg)
13671 }
13672 emitted++
13673 }
13674 // Pad missing arg slots with zero-initialized values. The comptime
13675 // `$for method in T.methods` loop generates a call branch for every
13676 // method, even those whose source-level call site (e.g. an `else`
13677 // branch with no `...args`) passes fewer args than the method
13678 // expects. Such branches are dead at runtime, but C still type-checks
13679 // the call and needs the right arity.
13680 if emitted < expected_extra {
13681 for slot in (1 + emitted) .. param_types.len {
13682 pt := param_types[slot]
13683 g.sb.write_string(', (${pt}){0}')
13684 }
13685 }
13686 g.sb.write_string(')')
13687}
13688