v / vlib / v2 / gen / cleanc / fn.v
13687 lines · 13253 sloc · 408.89 KB
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 }