vxx2 / 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