v / vlib / v2 / gen / cleanc / struct.v
8338 lines · 8070 sloc · 248.44 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 runtime
8import strings
9import time
10import v2.ast
11import v2.token
12import v2.types
13
14const max_generic_struct_scan_jobs = 4
15
16$if !windows {
17 struct GenericStructScanChunkArgs {
18 worker voidptr // &Gen - pre-cloned worker
19 file_indices_ptr voidptr // &[]int - worker-owned full-file indexes
20 }
21
22 @[typedef]
23 struct C.pthread_t {}
24
25 fn C.pthread_create(thread &C.pthread_t, attr voidptr, start_routine fn (voidptr) voidptr, arg voidptr) int
26 fn C.pthread_join(thread C.pthread_t, retval voidptr) int
27 fn C.pthread_attr_init(attr voidptr) int
28 fn C.pthread_attr_setstacksize(attr voidptr, stacksize usize) int
29 fn C.pthread_attr_destroy(attr voidptr) int
30
31 fn generic_struct_scan_chunk_thread(arg voidptr) voidptr {
32 a := unsafe { &GenericStructScanChunkArgs(arg) }
33 mut w := unsafe { &Gen(a.worker) }
34 indices := unsafe { &[]int(a.file_indices_ptr) }
35 w.collect_generic_struct_bindings_file_indices(*indices)
36 return unsafe { nil }
37 }
38}
39
40fn (mut g Gen) has_explicit_str_method_for_c_type(c_type_name string) bool {
41 old_file := g.cur_file_name
42 old_module := g.cur_module
43 old_import_modules := g.cur_import_modules.clone()
44 if g.has_flat() {
45 for i in 0 .. g.flat.files.len {
46 fc := g.flat.file_cursor(i)
47 g.set_file_cursor_module(fc)
48 stmts := fc.stmts()
49 for j in 0 .. stmts.len() {
50 stmt := stmts.at(j)
51 if stmt.kind() == .stmt_fn_decl && stmt.name() == 'str' {
52 decl := stmt.fn_decl_signature()
53 if decl.is_method
54 && g.method_decl_c_name_for_module(g.cur_module, decl) == '${c_type_name}__str' {
55 g.cur_file_name = old_file
56 g.cur_module = old_module
57 g.cur_import_modules = old_import_modules.clone()
58 return true
59 }
60 }
61 }
62 }
63 } else {
64 for file in g.files {
65 g.set_file_module(file)
66 for stmt in file.stmts {
67 if stmt is ast.FnDecl {
68 if stmt.is_method && stmt.name == 'str'
69 && g.method_decl_c_name_for_module(g.cur_module, stmt) == '${c_type_name}__str' {
70 g.cur_file_name = old_file
71 g.cur_module = old_module
72 g.cur_import_modules = old_import_modules.clone()
73 return true
74 }
75 }
76 }
77 }
78 }
79 g.cur_file_name = old_file
80 g.cur_module = old_module
81 g.cur_import_modules = old_import_modules.clone()
82 return false
83}
84
85// collect_generic_struct_bindings scans all struct fields for GenericType
86// instantiations (e.g. LinkedList[ValueInfo]) and records the concrete type
87// bindings so that methods on generic structs can resolve their generic params.
88fn (mut g Gen) collect_generic_struct_bindings() {
89 stats_enabled := g.cgen_stats_enabled()
90 stats_scope := g.cgen_stats_scope_label()
91 mut stats_sw := time.new_stopwatch()
92 mut stage_start := stats_sw.elapsed()
93 prev_active_generic_types := g.active_generic_types.clone()
94 prev_runtime_local_types := g.runtime_local_types.clone()
95 prev_runtime_decl_types := g.runtime_decl_types.clone()
96 prev_not_local_var_cache := g.not_local_var_cache.clone()
97 prev_is_module_ident_cache := g.is_module_ident_cache.clone()
98 prev_resolved_module_names := g.resolved_module_names.clone()
99 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
100 prev_fn_name := g.cur_fn_name
101 prev_fn_c_name := g.cur_fn_c_name
102 g.active_generic_types = map[string]types.Type{}
103 g.runtime_local_types = map[string]string{}
104 g.runtime_decl_types = map[string]string{}
105 g.not_local_var_cache = map[string]bool{}
106 g.is_module_ident_cache = map[string]bool{}
107 g.resolved_module_names = map[string]string{}
108 g.cur_fn_generic_params = map[string]string{}
109 g.cur_fn_name = ''
110 g.cur_fn_c_name = ''
111 defer {
112 g.active_generic_types = prev_active_generic_types.clone()
113 g.runtime_local_types = prev_runtime_local_types.clone()
114 g.runtime_decl_types = prev_runtime_decl_types.clone()
115 g.not_local_var_cache = prev_not_local_var_cache.clone()
116 g.is_module_ident_cache = prev_is_module_ident_cache.clone()
117 g.resolved_module_names = prev_resolved_module_names.clone()
118 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
119 g.cur_fn_name = prev_fn_name
120 g.cur_fn_c_name = prev_fn_c_name
121 }
122 if g.has_flat() {
123 g.collect_generic_struct_bindings_flat()
124 } else if !g.collect_generic_struct_bindings_file_scan_parallel() {
125 g.collect_generic_struct_bindings_file_scan(g.files)
126 }
127 stage_start = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
128 'setup.generic_struct_bindings.file_scan')
129 if g.has_flat() {
130 g.propagate_generic_struct_bindings_flat()
131 } else {
132 g.propagate_generic_struct_bindings_for_files(g.files)
133 }
134 stage_start = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
135 'setup.generic_struct_bindings.propagate')
136 if g.has_flat() {
137 g.scan_receiver_generic_struct_bindings_flat()
138 } else {
139 g.scan_receiver_generic_struct_bindings_for_files(g.files)
140 }
141 _ = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
142 'setup.generic_struct_bindings.receiver_scan')
143}
144
145fn (mut g Gen) collect_generic_struct_bindings_flat() {
146 for i in 0 .. g.flat.files.len {
147 fc := g.flat.file_cursor(i)
148 g.set_file_cursor_module(fc)
149 stmts := fc.stmts()
150 for j in 0 .. stmts.len() {
151 stmt := stmts.at(j)
152 match stmt.kind() {
153 .stmt_struct_decl {
154 decl := stmt.struct_decl()
155 for field in decl.fields {
156 g.scan_expr_for_generic_types(field.typ)
157 }
158 }
159 .stmt_fn_decl {
160 decl := stmt.fn_decl_signature()
161 if decl.receiver.typ !is ast.EmptyExpr {
162 g.scan_expr_for_generic_types(decl.receiver.typ)
163 }
164 for param in decl.typ.params {
165 g.scan_expr_for_generic_types(param.typ)
166 }
167 if decl.typ.return_type !is ast.EmptyExpr {
168 g.scan_expr_for_generic_types(decl.typ.return_type)
169 }
170 if stmt.list_at(3).len() > 0 {
171 g.scan_fn_body_cursor_for_generic_types_with_clean_locals(stmt, decl, '')
172 }
173 }
174 else {}
175 }
176 }
177 }
178}
179
180fn (mut g Gen) collect_generic_struct_bindings_file_scan(files []ast.File) {
181 for file in files {
182 g.collect_generic_struct_bindings_file(file)
183 }
184}
185
186fn (mut g Gen) collect_generic_struct_bindings_file_indices(file_indices []int) {
187 for file_idx in file_indices {
188 if file_idx < 0 || file_idx >= g.files.len {
189 continue
190 }
191 g.collect_generic_struct_bindings_file(g.files[file_idx])
192 }
193}
194
195fn (mut g Gen) collect_generic_struct_bindings_file(file ast.File) {
196 g.set_file_module(file)
197 for stmt in file.stmts {
198 if stmt is ast.StructDecl {
199 for field in stmt.fields {
200 g.scan_expr_for_generic_types(field.typ)
201 }
202 }
203 // Also scan function bodies for generic type references
204 // (e.g. LinkedList[StructFieldInfo]{} in decode_value)
205 if stmt is ast.FnDecl {
206 if stmt.receiver.typ !is ast.EmptyExpr {
207 g.scan_expr_for_generic_types(stmt.receiver.typ)
208 }
209 for param in stmt.typ.params {
210 g.scan_expr_for_generic_types(param.typ)
211 }
212 if stmt.typ.return_type !is ast.EmptyExpr {
213 g.scan_expr_for_generic_types(stmt.typ.return_type)
214 }
215 g.scan_fn_body_for_generic_types_with_clean_locals(stmt, '')
216 }
217 }
218}
219
220fn (mut g Gen) propagate_generic_struct_bindings_for_files(files []ast.File) {
221 // Propagation pass: for each generic struct with recorded bindings,
222 // look for nested generic type references (e.g. Node[T] inside
223 // LinkedList[T]) and record concrete bindings by substituting the
224 // parent struct's known bindings for placeholder params.
225 for file in files {
226 g.set_file_module(file)
227 for stmt in file.stmts {
228 if stmt is ast.StructDecl {
229 if stmt.generic_params.len == 0 {
230 continue
231 }
232 struct_c_name := g.get_struct_name(stmt)
233 if struct_c_name !in g.generic_struct_bindings {
234 continue
235 }
236 // Propagate for ALL instances (not just the primary binding)
237 instances := g.generic_struct_instances[struct_c_name]
238 for inst in instances {
239 for field in stmt.fields {
240 g.propagate_generic_bindings(field.typ, inst.bindings)
241 }
242 }
243 }
244 }
245 }
246}
247
248fn (mut g Gen) propagate_generic_struct_bindings_flat() {
249 for i in 0 .. g.flat.files.len {
250 fc := g.flat.file_cursor(i)
251 g.set_file_cursor_module(fc)
252 stmts := fc.stmts()
253 for j in 0 .. stmts.len() {
254 stmt := stmts.at(j)
255 if stmt.kind() != .stmt_struct_decl {
256 continue
257 }
258 decl := stmt.struct_decl()
259 if decl.generic_params.len == 0 {
260 continue
261 }
262 struct_c_name := g.get_struct_name(decl)
263 if struct_c_name !in g.generic_struct_bindings {
264 continue
265 }
266 instances := g.generic_struct_instances[struct_c_name]
267 for inst in instances {
268 for field in decl.fields {
269 g.propagate_generic_bindings(field.typ, inst.bindings)
270 }
271 }
272 }
273 }
274}
275
276fn (mut g Gen) scan_receiver_generic_struct_bindings_for_files(files []ast.File) {
277 // Generic receiver methods can contain explicit calls like
278 // `helper[T](...)` even when the receiver itself is emitted with v2's
279 // fallback placeholder type. Rescan those bodies with concrete receiver
280 // bindings. Uninstantiated receiver-generic methods should not invent
281 // concrete helper calls from unrelated generic bindings.
282 for file in files {
283 g.set_file_module(file)
284 for stmt in file.stmts {
285 if stmt is ast.FnDecl {
286 recv_params := receiver_generic_param_names(stmt)
287 if recv_params.len == 0 {
288 continue
289 }
290 mut binding_sets := []map[string]types.Type{}
291 all_bindings := g.get_all_receiver_generic_bindings(stmt)
292 if all_bindings.len > 0 {
293 binding_sets << all_bindings
294 } else if bindings := g.get_receiver_generic_bindings(stmt) {
295 binding_sets << bindings
296 }
297 if binding_sets.len == 0 {
298 continue
299 }
300 prev_generic_types := g.active_generic_types.clone()
301 for bindings in binding_sets {
302 g.active_generic_types = bindings.clone()
303 g.scan_fn_body_for_generic_types_with_clean_locals(stmt, '')
304 }
305 g.active_generic_types = prev_generic_types.clone()
306 }
307 }
308 }
309}
310
311fn (mut g Gen) scan_receiver_generic_struct_bindings_flat() {
312 for i in 0 .. g.flat.files.len {
313 fc := g.flat.file_cursor(i)
314 g.set_file_cursor_module(fc)
315 stmts := fc.stmts()
316 for j in 0 .. stmts.len() {
317 stmt := stmts.at(j)
318 if stmt.kind() != .stmt_fn_decl {
319 continue
320 }
321 decl := stmt.fn_decl_signature()
322 recv_params := receiver_generic_param_names(decl)
323 if recv_params.len == 0 {
324 continue
325 }
326 mut binding_sets := []map[string]types.Type{}
327 all_bindings := g.get_all_receiver_generic_bindings(decl)
328 if all_bindings.len > 0 {
329 binding_sets << all_bindings
330 } else if bindings := g.get_receiver_generic_bindings(decl) {
331 binding_sets << bindings
332 }
333 if binding_sets.len == 0 {
334 continue
335 }
336 prev_generic_types := g.active_generic_types.clone()
337 for bindings in binding_sets {
338 g.active_generic_types = bindings.clone()
339 g.scan_fn_body_cursor_for_generic_types_with_clean_locals(stmt, decl, '')
340 }
341 g.active_generic_types = prev_generic_types.clone()
342 }
343 }
344}
345
346fn (mut g Gen) collect_generic_struct_bindings_file_scan_parallel() bool {
347 if g.pref == unsafe { nil } || g.pref.no_parallel || g.files.len < 2 {
348 return false
349 }
350 $if windows {
351 return false
352 } $else {
353 stats_enabled := g.cgen_stats_enabled()
354 stats_scope := g.cgen_stats_scope_label()
355 mut stats_sw := time.new_stopwatch()
356 mut stage_start := stats_sw.elapsed()
357 n_files := g.files.len
358 n_runtime_jobs := runtime.nr_jobs()
359 n_jobs := generic_struct_scan_job_count(n_runtime_jobs, n_files)
360 if n_jobs <= 1 {
361 return false
362 }
363
364 mut thread_ids := []C.pthread_t{len: n_jobs}
365 mut args := []GenericStructScanChunkArgs{cap: n_jobs}
366 mut workers := []voidptr{cap: n_jobs}
367 mut chunk_indices := [][]int{cap: n_jobs}
368 for ci := 0; ci < n_jobs; ci++ {
369 start := ci * n_files / n_jobs
370 end := (ci + 1) * n_files / n_jobs
371 mut indices := []int{cap: end - start}
372 for file_idx := start; file_idx < end; file_idx++ {
373 indices << file_idx
374 }
375 chunk_indices << indices
376 w := g.new_generic_struct_scan_worker(ci)
377 workers << voidptr(w)
378 }
379 stage_start = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
380 'setup.generic_struct_bindings.worker_setup')
381 for ci := 0; ci < n_jobs; ci++ {
382 args << GenericStructScanChunkArgs{
383 worker: workers[ci]
384 file_indices_ptr: unsafe { voidptr(&chunk_indices[ci]) }
385 }
386 }
387
388 attr_buf := [64]u8{}
389 attr := unsafe { voidptr(&attr_buf[0]) }
390 C.pthread_attr_init(attr)
391 C.pthread_attr_setstacksize(attr, 64 * 1024 * 1024)
392 for ci := 0; ci < n_jobs; ci++ {
393 C.pthread_create(unsafe { &thread_ids[ci] }, attr, generic_struct_scan_chunk_thread,
394 unsafe { voidptr(&args[ci]) })
395 }
396 C.pthread_attr_destroy(attr)
397 for ci := 0; ci < n_jobs; ci++ {
398 C.pthread_join(thread_ids[ci], unsafe { nil })
399 }
400 stage_start = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
401 'setup.generic_struct_bindings.worker_run')
402 for ci := 0; ci < n_jobs; ci++ {
403 w := unsafe { &Gen(workers[ci]) }
404 g.merge_generic_struct_scan_worker(w)
405 }
406 _ = g.mark_cgen_step(stats_enabled, stats_scope, mut stats_sw, stage_start,
407 'setup.generic_struct_bindings.worker_merge')
408 return true
409 }
410}
411
412fn generic_struct_scan_job_count(n_runtime_jobs int, n_files int) int {
413 if n_runtime_jobs <= 0 || n_files <= 0 {
414 return 0
415 }
416 mut n_jobs := n_runtime_jobs
417 if n_jobs > max_generic_struct_scan_jobs {
418 n_jobs = max_generic_struct_scan_jobs
419 }
420 if n_jobs > n_files {
421 n_jobs = n_files
422 }
423 // Worker Gen clones are sizeable; avoid spending more time cloning than scanning
424 // when the current bundle is already small because .vh caches did most work.
425 if n_files < n_jobs * 4 {
426 n_jobs = if n_files >= 4 { n_files / 4 } else { 1 }
427 }
428 return n_jobs
429}
430
431fn (g &Gen) new_generic_struct_scan_worker(worker_id int) &Gen {
432 mut w := g.new_pass5_worker([], worker_id)
433 w.generic_body_scan_cache = g.generic_body_scan_cache.clone()
434 w.generic_spec_index = clone_generic_spec_index(g.generic_spec_index)
435 w.late_generic_specs = clone_late_generic_specs(g.late_generic_specs)
436 w.generic_struct_bindings = clone_generic_struct_bindings(g.generic_struct_bindings)
437 w.generic_struct_instances = clone_generic_struct_instances(g.generic_struct_instances)
438 w.active_generic_types = map[string]types.Type{}
439 w.runtime_local_types = map[string]string{}
440 w.runtime_decl_types = map[string]string{}
441 w.not_local_var_cache = map[string]bool{}
442 w.is_module_ident_cache = map[string]bool{}
443 w.resolved_module_names = map[string]string{}
444 w.cur_fn_generic_params = map[string]string{}
445 w.cur_fn_name = ''
446 w.cur_fn_c_name = ''
447 return w
448}
449
450fn (mut g Gen) merge_generic_struct_scan_worker(w &Gen) {
451 for struct_c_name, instances in w.generic_struct_instances {
452 for inst in instances {
453 g.merge_generic_struct_scan_instance(struct_c_name, inst)
454 }
455 }
456 for key, bindings in w.generic_struct_bindings {
457 if key !in g.generic_struct_bindings && key !in g.generic_struct_instances {
458 g.generic_struct_bindings[key] = bindings.clone()
459 }
460 }
461 for key, specs in w.late_generic_specs {
462 for bindings in specs {
463 g.record_late_generic_call_spec_unchecked(key, bindings)
464 }
465 }
466 for key, scanned in w.generic_body_scan_cache {
467 if scanned {
468 g.generic_body_scan_cache[key] = true
469 }
470 }
471 g.merge_generic_setup_alias_maps(w)
472}
473
474fn (mut g Gen) merge_generic_struct_scan_instance(struct_c_name string, inst GenericStructInstance) {
475 mut instances := g.generic_struct_instances[struct_c_name]
476 struct_base := if struct_c_name.contains('__') {
477 struct_c_name.all_after_last('__')
478 } else {
479 struct_c_name
480 }
481 param_names := g.generic_struct_runtime_param_names(struct_base, struct_c_name)
482 bindings_key := g.generic_struct_bindings_key(param_names, inst.bindings)
483 for existing in instances {
484 if g.generic_struct_instance_matches(existing, inst.params_key, bindings_key, param_names) {
485 return
486 }
487 }
488 mut c_name := inst.c_name
489 if instances.len > 0 && c_name == struct_c_name {
490 c_name = '${struct_c_name}_T_${inst.params_key}'
491 }
492 merged := GenericStructInstance{
493 params_key: inst.params_key
494 bindings: inst.bindings.clone()
495 c_name: c_name
496 }
497 instances << merged
498 g.generic_struct_instances[struct_c_name] = instances
499 if struct_c_name !in g.generic_struct_bindings {
500 g.generic_struct_bindings[struct_c_name] = merged.bindings.clone()
501 }
502}
503
504fn (mut g Gen) merge_generic_setup_alias_maps(w &Gen) {
505 for key, values in w.tuple_aliases {
506 if key !in g.tuple_aliases {
507 g.tuple_aliases[key] = values.clone()
508 }
509 }
510 for key, value in w.array_aliases {
511 if value {
512 g.array_aliases[key] = true
513 }
514 }
515 for key, value in w.map_aliases {
516 if value {
517 g.map_aliases[key] = true
518 }
519 }
520 for key, value in w.result_aliases {
521 if value {
522 g.result_aliases[key] = true
523 }
524 }
525 for key, value in w.option_aliases {
526 if value {
527 g.option_aliases[key] = true
528 }
529 }
530 for key, value in w.collected_fixed_array_types {
531 if key !in g.collected_fixed_array_types {
532 g.collected_fixed_array_types[key] = value
533 }
534 }
535 for key, value in w.collected_map_types {
536 if key !in g.collected_map_types {
537 g.collected_map_types[key] = value
538 }
539 }
540}
541
542fn (mut g Gen) scan_fn_body_for_generic_types_with_clean_locals(node ast.FnDecl, spec_name string) {
543 prev_runtime_local_types := g.runtime_local_types.clone()
544 prev_runtime_decl_types := g.runtime_decl_types.clone()
545 prev_not_local_var_cache := g.not_local_var_cache.clone()
546 prev_is_module_ident_cache := g.is_module_ident_cache.clone()
547 prev_resolved_module_names := g.resolved_module_names.clone()
548 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
549 prev_fn_name := g.cur_fn_name
550 prev_fn_c_name := g.cur_fn_c_name
551 g.runtime_local_types = map[string]string{}
552 g.runtime_decl_types = map[string]string{}
553 g.not_local_var_cache = map[string]bool{}
554 g.is_module_ident_cache = map[string]bool{}
555 g.resolved_module_names = map[string]string{}
556 g.cur_fn_generic_params = map[string]string{}
557 g.cur_fn_name = ''
558 g.cur_fn_c_name = ''
559 g.scan_fn_body_for_generic_types(node, spec_name)
560 g.runtime_local_types = prev_runtime_local_types.clone()
561 g.runtime_decl_types = prev_runtime_decl_types.clone()
562 g.not_local_var_cache = prev_not_local_var_cache.clone()
563 g.is_module_ident_cache = prev_is_module_ident_cache.clone()
564 g.resolved_module_names = prev_resolved_module_names.clone()
565 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
566 g.cur_fn_name = prev_fn_name
567 g.cur_fn_c_name = prev_fn_c_name
568}
569
570fn (mut g Gen) scan_fn_body_cursor_for_generic_types_with_clean_locals(stmt ast.Cursor, decl ast.FnDecl, spec_name string) {
571 prev_runtime_local_types := g.runtime_local_types.clone()
572 prev_runtime_decl_types := g.runtime_decl_types.clone()
573 prev_not_local_var_cache := g.not_local_var_cache.clone()
574 prev_is_module_ident_cache := g.is_module_ident_cache.clone()
575 prev_resolved_module_names := g.resolved_module_names.clone()
576 prev_cur_fn_generic_params := g.cur_fn_generic_params.clone()
577 prev_fn_name := g.cur_fn_name
578 prev_fn_c_name := g.cur_fn_c_name
579 g.runtime_local_types = map[string]string{}
580 g.runtime_decl_types = map[string]string{}
581 g.not_local_var_cache = map[string]bool{}
582 g.is_module_ident_cache = map[string]bool{}
583 g.resolved_module_names = map[string]string{}
584 g.cur_fn_generic_params = map[string]string{}
585 g.cur_fn_name = ''
586 g.cur_fn_c_name = ''
587 g.scan_fn_body_cursor_for_generic_types(stmt, decl, spec_name)
588 g.runtime_local_types = prev_runtime_local_types.clone()
589 g.runtime_decl_types = prev_runtime_decl_types.clone()
590 g.not_local_var_cache = prev_not_local_var_cache.clone()
591 g.is_module_ident_cache = prev_is_module_ident_cache.clone()
592 g.resolved_module_names = prev_resolved_module_names.clone()
593 g.cur_fn_generic_params = prev_cur_fn_generic_params.clone()
594 g.cur_fn_name = prev_fn_name
595 g.cur_fn_c_name = prev_fn_c_name
596}
597
598fn clone_tuple_aliases(src map[string][]string) map[string][]string {
599 mut out := map[string][]string{}
600 for key, values in src {
601 out[key] = values.clone()
602 }
603 return out
604}
605
606fn clone_generic_spec_index(src map[string][]string) map[string][]string {
607 mut out := map[string][]string{}
608 for key, values in src {
609 out[key] = values.clone()
610 }
611 return out
612}
613
614fn clone_late_generic_specs(src map[string][]map[string]types.Type) map[string][]map[string]types.Type {
615 mut out := map[string][]map[string]types.Type{}
616 for key, specs in src {
617 mut cloned_specs := []map[string]types.Type{cap: specs.len}
618 for spec in specs {
619 cloned_specs << spec.clone()
620 }
621 out[key] = cloned_specs
622 }
623 return out
624}
625
626fn clone_generic_struct_bindings(src map[string]map[string]types.Type) map[string]map[string]types.Type {
627 mut out := map[string]map[string]types.Type{}
628 for key, bindings in src {
629 out[key] = bindings.clone()
630 }
631 return out
632}
633
634fn clone_generic_struct_instances(src map[string][]GenericStructInstance) map[string][]GenericStructInstance {
635 mut out := map[string][]GenericStructInstance{}
636 for key, instances in src {
637 mut cloned_instances := []GenericStructInstance{cap: instances.len}
638 for inst in instances {
639 cloned_instances << GenericStructInstance{
640 params_key: inst.params_key
641 bindings: inst.bindings.clone()
642 c_name: inst.c_name
643 }
644 }
645 out[key] = cloned_instances
646 }
647 return out
648}
649
650fn (s &GenericSetupSnapshot) clone() GenericSetupSnapshot {
651 return GenericSetupSnapshot{
652 ready: s.ready
653 tuple_aliases: clone_tuple_aliases(s.tuple_aliases)
654 array_aliases: s.array_aliases.clone()
655 map_aliases: s.map_aliases.clone()
656 result_aliases: s.result_aliases.clone()
657 option_aliases: s.option_aliases.clone()
658 collected_fixed_array_types: s.collected_fixed_array_types.clone()
659 collected_map_types: s.collected_map_types.clone()
660 generic_body_scan_cache: s.generic_body_scan_cache.clone()
661 generic_spec_index: clone_generic_spec_index(s.generic_spec_index)
662 late_generic_specs: clone_late_generic_specs(s.late_generic_specs)
663 generic_struct_bindings: clone_generic_struct_bindings(s.generic_struct_bindings)
664 generic_struct_instances: clone_generic_struct_instances(s.generic_struct_instances)
665 }
666}
667
668pub fn (mut g Gen) use_generic_setup_snapshot(snapshot GenericSetupSnapshot) {
669 g.generic_setup_snapshot = snapshot.clone()
670 g.has_generic_setup_snapshot = snapshot.ready
671}
672
673pub fn (g &Gen) generic_setup_snapshot() GenericSetupSnapshot {
674 return GenericSetupSnapshot{
675 ready: true
676 tuple_aliases: clone_tuple_aliases(g.tuple_aliases)
677 array_aliases: g.array_aliases.clone()
678 map_aliases: g.map_aliases.clone()
679 result_aliases: g.result_aliases.clone()
680 option_aliases: g.option_aliases.clone()
681 collected_fixed_array_types: g.collected_fixed_array_types.clone()
682 collected_map_types: g.collected_map_types.clone()
683 generic_body_scan_cache: g.generic_body_scan_cache.clone()
684 generic_spec_index: clone_generic_spec_index(g.generic_spec_index)
685 late_generic_specs: clone_late_generic_specs(g.late_generic_specs)
686 generic_struct_bindings: clone_generic_struct_bindings(g.generic_struct_bindings)
687 generic_struct_instances: clone_generic_struct_instances(g.generic_struct_instances)
688 }
689}
690
691fn (mut g Gen) apply_generic_setup_snapshot() {
692 snapshot := g.generic_setup_snapshot
693 g.tuple_aliases = clone_tuple_aliases(snapshot.tuple_aliases)
694 g.array_aliases = snapshot.array_aliases.clone()
695 g.map_aliases = snapshot.map_aliases.clone()
696 g.result_aliases = snapshot.result_aliases.clone()
697 g.option_aliases = snapshot.option_aliases.clone()
698 g.collected_fixed_array_types = snapshot.collected_fixed_array_types.clone()
699 g.collected_map_types = snapshot.collected_map_types.clone()
700 g.generic_body_scan_cache = snapshot.generic_body_scan_cache.clone()
701 g.generic_spec_index = clone_generic_spec_index(snapshot.generic_spec_index)
702 g.late_generic_specs = clone_late_generic_specs(snapshot.late_generic_specs)
703 g.generic_struct_bindings = clone_generic_struct_bindings(snapshot.generic_struct_bindings)
704 g.generic_struct_instances = clone_generic_struct_instances(snapshot.generic_struct_instances)
705}
706
707// propagate_generic_bindings finds nested generic type references and records
708// concrete bindings by substituting placeholder params from parent bindings.
709fn (mut g Gen) propagate_generic_bindings(e ast.Expr, parent_bindings map[string]types.Type) {
710 match e {
711 ast.Type {
712 if e is ast.GenericType {
713 gt := e as ast.GenericType
714 base_name := g.expr_type_to_c(gt.name)
715 if gt.params.len > 0 {
716 // Check if any param is a placeholder that can be resolved
717 // via parent_bindings.
718 mut all_concrete := true
719 for param in gt.params {
720 pname := param.name()
721 if is_generic_placeholder_type_name(pname) && pname !in parent_bindings {
722 all_concrete = false
723 break
724 }
725 }
726 if all_concrete && !g.generic_call_spec_scan_only {
727 struct_base := if base_name.contains('__') {
728 base_name.all_after_last('__')
729 } else {
730 base_name
731 }
732 g.record_generic_struct_bindings_with_parent(struct_base, base_name,
733 gt.params, parent_bindings)
734 }
735 }
736 // Recurse into params
737 for param in gt.params {
738 g.propagate_generic_bindings(param, parent_bindings)
739 }
740 }
741 if e is ast.PointerType {
742 g.propagate_generic_bindings(e.base_type, parent_bindings)
743 }
744 }
745 ast.ModifierExpr {
746 g.propagate_generic_bindings(e.expr, parent_bindings)
747 }
748 ast.PrefixExpr {
749 g.propagate_generic_bindings(e.expr, parent_bindings)
750 }
751 ast.GenericArgOrIndexExpr {
752 base_name := g.expr_type_to_c(e.lhs)
753 arg_name := e.expr.name()
754 if !g.generic_call_spec_scan_only {
755 if is_generic_placeholder_type_name(arg_name) && arg_name in parent_bindings {
756 struct_base := if base_name.contains('__') {
757 base_name.all_after_last('__')
758 } else {
759 base_name
760 }
761 g.record_generic_struct_bindings_with_parent(struct_base, base_name, [
762 e.expr,
763 ], parent_bindings)
764 } else if !is_generic_placeholder_type_name(arg_name) {
765 struct_base := if base_name.contains('__') {
766 base_name.all_after_last('__')
767 } else {
768 base_name
769 }
770 g.record_generic_struct_bindings(struct_base, base_name, [e.expr])
771 }
772 }
773 }
774 ast.GenericArgs {
775 base_name := g.expr_type_to_c(e.lhs)
776 if e.args.len > 0 && !g.generic_call_spec_scan_only {
777 struct_base := if base_name.contains('__') {
778 base_name.all_after_last('__')
779 } else {
780 base_name
781 }
782 g.record_generic_struct_bindings_with_parent(struct_base, base_name, e.args,
783 parent_bindings)
784 }
785 }
786 else {}
787 }
788}
789
790fn (mut g Gen) scan_expr_for_generic_types(e ast.Expr) {
791 match e {
792 ast.Ident {
793 if e.name.contains('_T_') || e.name.ends_with('_T') {
794 g.scan_generic_fn_value_for_specs(e)
795 g.record_generic_struct_bindings_from_specialized_name(e.name)
796 }
797 }
798 ast.Type {
799 if e is ast.GenericType {
800 gt := e as ast.GenericType
801 base_name := g.expr_type_to_c(gt.name)
802 if gt.params.len > 0 && !g.generic_call_spec_scan_only {
803 struct_base := if base_name.contains('__') {
804 base_name.all_after_last('__')
805 } else {
806 base_name
807 }
808 g.record_generic_struct_bindings(struct_base, base_name, gt.params)
809 }
810 // Also scan params recursively (e.g. Node[T] inside LinkedList[ValueInfo])
811 for param in gt.params {
812 g.scan_expr_for_generic_types(param)
813 }
814 }
815 if e is ast.ArrayType {
816 g.scan_expr_for_generic_types(e.elem_type)
817 }
818 if e is ast.MapType {
819 g.scan_expr_for_generic_types(e.key_type)
820 g.scan_expr_for_generic_types(e.value_type)
821 }
822 if e is ast.OptionType {
823 g.scan_expr_for_generic_types(e.base_type)
824 }
825 if e is ast.ResultType {
826 g.scan_expr_for_generic_types(e.base_type)
827 }
828 if e is ast.PointerType {
829 g.scan_expr_for_generic_types(e.base_type)
830 }
831 }
832 ast.ModifierExpr {
833 g.scan_expr_for_generic_types(e.expr)
834 }
835 ast.PrefixExpr {
836 g.scan_expr_for_generic_types(e.expr)
837 }
838 ast.GenericArgOrIndexExpr {
839 // e.g. &Node[ValueInfo] → PrefixExpr { GenericArgOrIndexExpr { Ident("Node"), Ident("ValueInfo") } }
840 g.scan_generic_fn_value_for_specs(e)
841 base_name := g.expr_type_to_c(e.lhs)
842 arg_name := e.expr.name()
843 if !g.generic_call_spec_scan_only {
844 struct_base := if base_name.contains('__') {
845 base_name.all_after_last('__')
846 } else {
847 base_name
848 }
849 if is_generic_placeholder_type_name(arg_name) {
850 if concrete := g.active_generic_types[arg_name] {
851 g.record_generic_struct_bindings(struct_base, base_name, [
852 ast.Expr(ast.Ident{
853 name: g.types_type_to_c(concrete)
854 }),
855 ])
856 }
857 } else {
858 g.record_generic_struct_bindings(struct_base, base_name, [e.expr])
859 }
860 }
861 }
862 ast.GenericArgs {
863 g.scan_generic_fn_value_for_specs(e)
864 base_name := g.expr_type_to_c(e.lhs)
865 if e.args.len > 0 && !g.generic_call_spec_scan_only {
866 struct_base := if base_name.contains('__') {
867 base_name.all_after_last('__')
868 } else {
869 base_name
870 }
871 mut concrete_args := []ast.Expr{cap: e.args.len}
872 mut all_concrete := true
873 for arg in e.args {
874 arg_name := arg.name()
875 if is_generic_placeholder_type_name(arg_name) {
876 if concrete := g.active_generic_types[arg_name] {
877 concrete_args << ast.Expr(ast.Ident{
878 name: g.types_type_to_c(concrete)
879 })
880 } else {
881 all_concrete = false
882 break
883 }
884 } else {
885 concrete_args << arg
886 }
887 }
888 if all_concrete {
889 g.record_generic_struct_bindings(struct_base, base_name, concrete_args)
890 }
891 }
892 }
893 ast.CallOrCastExpr {
894 // LinkedList[StructFieldInfo]{} is parsed as CallOrCastExpr with
895 // lhs = GenericArgOrIndexExpr or GenericArgs
896 g.scan_expr_for_generic_types(e.lhs)
897 g.scan_expr_for_generic_types(e.expr)
898 }
899 ast.CallExpr {
900 g.scan_expr_for_generic_types(e.lhs)
901 for arg in e.args {
902 g.scan_expr_for_generic_types(arg)
903 g.scan_expr_stmts_for_generic_types(arg)
904 }
905 g.scan_call_for_generic_fn_specs(e)
906 }
907 ast.InfixExpr {
908 g.scan_expr_for_generic_types(e.lhs)
909 g.scan_expr_for_generic_types(e.rhs)
910 if e.op == .left_shift {
911 g.remember_array_append_lhs_type_for_generic_scan(e.lhs, e.rhs)
912 }
913 }
914 ast.OrExpr {
915 g.scan_expr_for_generic_types(e.expr)
916 }
917 ast.ComptimeExpr {
918 if e.expr is ast.IfExpr {
919 g.scan_comptime_if_for_generic_types(e.expr)
920 return
921 }
922 g.scan_expr_for_generic_types(e.expr)
923 }
924 ast.IfExpr {
925 g.scan_expr_for_generic_types(e.cond)
926 }
927 ast.MatchExpr {
928 g.scan_expr_for_generic_types(e.expr)
929 for branch in e.branches {
930 for cond in branch.cond {
931 g.scan_expr_for_generic_types(cond)
932 }
933 }
934 }
935 ast.Tuple {
936 mut elem_types := []string{cap: e.exprs.len}
937 for expr in e.exprs {
938 g.scan_expr_for_generic_types(expr)
939 g.scan_expr_stmts_for_generic_types(expr)
940 mut elem_type := g.get_expr_type(expr)
941 if elem_type == '' || elem_type == 'int' {
942 if raw := g.get_raw_type(expr) {
943 elem_type = g.types_type_to_c(raw)
944 }
945 }
946 if elem_type == '' {
947 elem_type = 'int'
948 }
949 elem_types << elem_type
950 }
951 g.register_tuple_alias(elem_types)
952 }
953 ast.InitExpr {
954 // e.g. LinkedList[StructFieldInfo]{} has .typ = GenericArgs
955 g.scan_expr_for_generic_types(e.typ)
956 for field in e.fields {
957 g.scan_expr_for_generic_types(field.value)
958 }
959 }
960 else {}
961 }
962}
963
964fn (mut g Gen) record_generic_struct_bindings_from_specialized_name(raw_name string) {
965 if !raw_name.contains('_T_') {
966 return
967 }
968 struct_c_name := raw_name.all_before('_T_')
969 if struct_c_name == '' {
970 return
971 }
972 runtime_param_names := g.generic_struct_runtime_param_names(struct_c_name, struct_c_name)
973 if runtime_param_names.len == 0 {
974 return
975 }
976 arg_tokens := generic_call_embedded_type_arg_names_from_name(raw_name, runtime_param_names.len)
977 if arg_tokens.len != runtime_param_names.len {
978 return
979 }
980 mut bindings := map[string]types.Type{}
981 mut param_c_names := []string{cap: arg_tokens.len}
982 for i, param_name in runtime_param_names {
983 concrete_c_name := generic_token_to_c_type(arg_tokens[i])
984 if concrete_c_name == '' {
985 return
986 }
987 concrete_type := g.concrete_type_from_call_arg_c_name(concrete_c_name) or { return }
988 if type_contains_generic_placeholder(concrete_type)
989 || !g.generic_concrete_type_is_runtime_specializable(concrete_type) {
990 return
991 }
992 bindings[param_name] = concrete_type
993 param_c_names << mangle_alias_component(concrete_c_name)
994 }
995 if bindings.len != runtime_param_names.len
996 || !g.generic_specialization_belongs_to_emit_modules(bindings) {
997 return
998 }
999 params_key := param_c_names.join('_')
1000 bindings_key := g.generic_struct_bindings_key(runtime_param_names, bindings)
1001 mut instances := g.generic_struct_instances[struct_c_name]
1002 for inst in instances {
1003 if inst.c_name == raw_name
1004 || g.generic_struct_instance_matches(inst, params_key, bindings_key, runtime_param_names)
1005 || g.generic_struct_instance_bindings_match(inst, bindings, runtime_param_names) {
1006 return
1007 }
1008 }
1009 instances << GenericStructInstance{
1010 params_key: params_key
1011 bindings: bindings.clone()
1012 c_name: raw_name
1013 }
1014 g.generic_struct_instances[struct_c_name] = instances
1015 if struct_c_name !in g.generic_struct_bindings {
1016 g.generic_struct_bindings[struct_c_name] = bindings.clone()
1017 }
1018}
1019
1020fn (mut g Gen) generic_call_decl_from_lhs(lhs ast.Expr) ?ast.FnDecl {
1021 mut call_name := match lhs {
1022 ast.Ident {
1023 lhs.name
1024 }
1025 ast.SelectorExpr {
1026 lhs.rhs.name
1027 }
1028 ast.GenericArgOrIndexExpr {
1029 return g.generic_call_decl_from_lhs(lhs.lhs)
1030 }
1031 ast.GenericArgs {
1032 return g.generic_call_decl_from_lhs(lhs.lhs)
1033 }
1034 else {
1035 ''
1036 }
1037 }
1038
1039 if call_name.contains('_T_') {
1040 call_name = call_name.all_before('_T_')
1041 } else if call_name.ends_with('_T') {
1042 call_name = call_name[..call_name.len - 2]
1043 }
1044
1045 if call_name == '' {
1046 return none
1047 }
1048 for candidate in generic_call_decl_candidates(call_name) {
1049 if info := g.generic_fn_decl_index[candidate] {
1050 if g.has_flat() {
1051 if info.file_idx < 0 || info.file_idx >= g.flat.files.len {
1052 continue
1053 }
1054 stmts := g.flat.file_cursor(info.file_idx).stmts()
1055 if info.stmt_idx < 0 || info.stmt_idx >= stmts.len() {
1056 continue
1057 }
1058 stmt := stmts.at(info.stmt_idx)
1059 if stmt.kind() == .stmt_fn_decl {
1060 return stmt.fn_decl_signature()
1061 }
1062 } else if info.file_idx >= 0 && info.file_idx < g.files.len {
1063 file := g.files[info.file_idx]
1064 if info.stmt_idx >= 0 && info.stmt_idx < file.stmts.len {
1065 stmt := file.stmts[info.stmt_idx]
1066 if stmt is ast.FnDecl {
1067 return stmt
1068 }
1069 }
1070 }
1071 }
1072 }
1073 if g.generic_fn_decl_index.len > 0 {
1074 return none
1075 }
1076 prev_module := g.cur_module
1077 prev_file_name := g.cur_file_name
1078 prev_active_generic_types := g.active_generic_types.clone()
1079 g.active_generic_types = map[string]types.Type{}
1080 defer {
1081 g.cur_module = prev_module
1082 g.cur_file_name = prev_file_name
1083 g.active_generic_types = prev_active_generic_types.clone()
1084 }
1085 if g.has_flat() {
1086 for i in 0 .. g.flat.files.len {
1087 fc := g.flat.file_cursor(i)
1088 g.set_file_cursor_module(fc)
1089 stmts := fc.stmts()
1090 for j in 0 .. stmts.len() {
1091 stmt := stmts.at(j)
1092 if stmt.kind() != .stmt_fn_decl || stmt.name() != call_name {
1093 continue
1094 }
1095 decl := stmt.fn_decl_signature()
1096 if g.generic_fn_param_names(decl).len > 0 {
1097 return decl
1098 }
1099 }
1100 }
1101 return none
1102 }
1103 for file in g.files {
1104 g.set_file_module(file)
1105 for stmt in file.stmts {
1106 if stmt is ast.FnDecl && stmt.name == call_name
1107 && g.generic_fn_param_names(stmt).len > 0 {
1108 return stmt
1109 }
1110 }
1111 }
1112 return none
1113}
1114
1115fn (mut g Gen) generic_call_decl_from_lhs_cursor(lhs ast.Cursor) ?ast.FnDecl {
1116 mut call_name := generic_call_short_name_cursor(lhs)
1117 if call_name == '' {
1118 return none
1119 }
1120 for candidate in generic_call_decl_candidates(call_name) {
1121 if info := g.generic_fn_decl_index[candidate] {
1122 if g.has_flat() {
1123 if info.file_idx < 0 || info.file_idx >= g.flat.files.len {
1124 continue
1125 }
1126 stmts := g.flat.file_cursor(info.file_idx).stmts()
1127 if info.stmt_idx < 0 || info.stmt_idx >= stmts.len() {
1128 continue
1129 }
1130 stmt := stmts.at(info.stmt_idx)
1131 if stmt.kind() == .stmt_fn_decl {
1132 return stmt.fn_decl_signature()
1133 }
1134 } else if info.file_idx >= 0 && info.file_idx < g.files.len {
1135 file := g.files[info.file_idx]
1136 if info.stmt_idx >= 0 && info.stmt_idx < file.stmts.len {
1137 stmt := file.stmts[info.stmt_idx]
1138 if stmt is ast.FnDecl {
1139 return stmt
1140 }
1141 }
1142 }
1143 }
1144 }
1145 if g.generic_fn_decl_index.len > 0 {
1146 return none
1147 }
1148 prev_module := g.cur_module
1149 prev_file_name := g.cur_file_name
1150 prev_active_generic_types := g.active_generic_types.clone()
1151 g.active_generic_types = map[string]types.Type{}
1152 defer {
1153 g.cur_module = prev_module
1154 g.cur_file_name = prev_file_name
1155 g.active_generic_types = prev_active_generic_types.clone()
1156 }
1157 if g.has_flat() {
1158 for i in 0 .. g.flat.files.len {
1159 fc := g.flat.file_cursor(i)
1160 g.set_file_cursor_module(fc)
1161 stmts := fc.stmts()
1162 for j in 0 .. stmts.len() {
1163 stmt := stmts.at(j)
1164 if stmt.kind() != .stmt_fn_decl || stmt.name() != call_name {
1165 continue
1166 }
1167 decl := stmt.fn_decl_signature()
1168 if g.generic_fn_param_names(decl).len > 0 {
1169 return decl
1170 }
1171 }
1172 }
1173 return none
1174 }
1175 for file in g.files {
1176 g.set_file_module(file)
1177 for stmt in file.stmts {
1178 if stmt is ast.FnDecl && stmt.name == call_name
1179 && g.generic_fn_param_names(stmt).len > 0 {
1180 return stmt
1181 }
1182 }
1183 }
1184 return none
1185}
1186
1187fn generic_call_decl_candidates(call_name string) []string {
1188 mut candidates := []string{cap: 3}
1189 if call_name != '' {
1190 candidates << call_name
1191 }
1192 sanitized := sanitize_fn_ident(call_name)
1193 if sanitized != '' && sanitized !in candidates {
1194 candidates << sanitized
1195 }
1196 if call_name.contains('__') {
1197 short_name := call_name.all_after_last('__')
1198 if short_name != '' && short_name !in candidates {
1199 candidates << short_name
1200 }
1201 }
1202 return candidates
1203}
1204
1205fn generic_call_short_name(lhs ast.Expr) string {
1206 mut name := match lhs {
1207 ast.Ident {
1208 lhs.name
1209 }
1210 ast.SelectorExpr {
1211 lhs.rhs.name
1212 }
1213 ast.GenericArgOrIndexExpr {
1214 generic_call_short_name(lhs.lhs)
1215 }
1216 ast.GenericArgs {
1217 generic_call_short_name(lhs.lhs)
1218 }
1219 else {
1220 ''
1221 }
1222 }
1223
1224 if name.contains('_T_') {
1225 name = name.all_before('_T_')
1226 } else if name.ends_with('_T') {
1227 name = name[..name.len - 2]
1228 }
1229 return name
1230}
1231
1232fn generic_call_short_name_cursor(lhs ast.Cursor) string {
1233 mut name := match lhs.kind() {
1234 .expr_ident {
1235 lhs.name()
1236 }
1237 .expr_selector {
1238 lhs.edge(1).name()
1239 }
1240 .expr_generic_arg_or_index, .expr_generic_args {
1241 generic_call_short_name_cursor(lhs.edge(0))
1242 }
1243 else {
1244 ''
1245 }
1246 }
1247
1248 if name.contains('_T_') {
1249 name = name.all_before('_T_')
1250 } else if name.ends_with('_T') {
1251 name = name[..name.len - 2]
1252 }
1253 return name
1254}
1255
1256fn generic_call_raw_name(lhs ast.Expr) string {
1257 return match lhs {
1258 ast.Ident {
1259 lhs.name
1260 }
1261 ast.SelectorExpr {
1262 lhs.rhs.name
1263 }
1264 ast.GenericArgOrIndexExpr {
1265 generic_call_raw_name(lhs.lhs)
1266 }
1267 ast.GenericArgs {
1268 generic_call_raw_name(lhs.lhs)
1269 }
1270 else {
1271 ''
1272 }
1273 }
1274}
1275
1276fn generic_call_raw_name_cursor(lhs ast.Cursor) string {
1277 return match lhs.kind() {
1278 .expr_ident {
1279 lhs.name()
1280 }
1281 .expr_selector {
1282 lhs.edge(1).name()
1283 }
1284 .expr_generic_arg_or_index, .expr_generic_args {
1285 generic_call_raw_name_cursor(lhs.edge(0))
1286 }
1287 else {
1288 ''
1289 }
1290 }
1291}
1292
1293fn generic_call_type_args(lhs ast.Expr) []ast.Expr {
1294 return match lhs {
1295 ast.GenericArgOrIndexExpr {
1296 if lhs.expr is ast.LifetimeExpr {
1297 []ast.Expr{}
1298 } else {
1299 [lhs.expr]
1300 }
1301 }
1302 ast.GenericArgs {
1303 runtime_generic_args(lhs.args)
1304 }
1305 else {
1306 []ast.Expr{}
1307 }
1308 }
1309}
1310
1311fn generic_call_type_args_cursor(lhs ast.Cursor) []ast.Expr {
1312 match lhs.kind() {
1313 .expr_generic_arg_or_index {
1314 arg := lhs.edge(1)
1315 if arg.kind() == .expr_lifetime {
1316 return []ast.Expr{}
1317 }
1318 return [arg.type_expr()]
1319 }
1320 .expr_generic_args {
1321 mut args := []ast.Expr{cap: lhs.edge_count() - 1}
1322 for i in 1 .. lhs.edge_count() {
1323 arg := lhs.edge(i)
1324 if arg.kind() == .expr_lifetime {
1325 continue
1326 }
1327 args << arg.type_expr()
1328 }
1329 return args
1330 }
1331 else {
1332 return []ast.Expr{}
1333 }
1334 }
1335}
1336
1337fn generic_call_embedded_type_arg_names_from_name(raw_name string, expected_count int) []string {
1338 if expected_count <= 0 {
1339 return []string{}
1340 }
1341 if !raw_name.contains('_T_') {
1342 return []string{}
1343 }
1344 suffix := raw_name.all_after('_T_')
1345 if suffix == '' {
1346 return []string{}
1347 }
1348 if expected_count == 1 {
1349 return [suffix]
1350 }
1351 parts := suffix.split('_')
1352 if parts.len != expected_count {
1353 return []string{}
1354 }
1355 return parts
1356}
1357
1358fn generic_call_embedded_type_arg_names(lhs ast.Expr, expected_count int) []string {
1359 raw_name := match lhs {
1360 ast.Ident {
1361 lhs.name
1362 }
1363 ast.SelectorExpr {
1364 lhs.rhs.name
1365 }
1366 ast.GenericArgOrIndexExpr {
1367 return generic_call_embedded_type_arg_names(lhs.lhs, expected_count)
1368 }
1369 ast.GenericArgs {
1370 return generic_call_embedded_type_arg_names(lhs.lhs, expected_count)
1371 }
1372 else {
1373 ''
1374 }
1375 }
1376
1377 return generic_call_embedded_type_arg_names_from_name(raw_name, expected_count)
1378}
1379
1380fn generic_call_embedded_type_arg_names_cursor(lhs ast.Cursor, expected_count int) []string {
1381 raw_name := generic_call_raw_name_cursor(lhs)
1382 return generic_call_embedded_type_arg_names_from_name(raw_name, expected_count)
1383}
1384
1385fn (g &Gen) active_generic_bindings_matching_embedded_args(embedded_args []string, generic_params []string) ?map[string]types.Type {
1386 if embedded_args.len != generic_params.len || g.active_generic_types.len == 0 {
1387 return none
1388 }
1389 mut bindings := map[string]types.Type{}
1390 for i, param_name in generic_params {
1391 arg_name := embedded_args[i]
1392 concrete := if is_generic_placeholder_type_name(arg_name) {
1393 g.active_generic_types[arg_name] or { return none }
1394 } else {
1395 active := g.active_generic_types[param_name] or { return none }
1396 spec_token := g.generic_specialization_token_from_type(active)
1397 if !generic_token_matches_short_name(spec_token, arg_name) {
1398 return none
1399 }
1400 active
1401 }
1402 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
1403 return none
1404 }
1405 bindings[param_name] = concrete
1406 }
1407 return bindings
1408}
1409
1410fn (g &Gen) generic_concrete_type_is_runtime_specializable(typ types.Type) bool {
1411 if !generic_concrete_type_is_runtime_specializable(typ) {
1412 return false
1413 }
1414 concrete := normalize_generic_concrete_type(typ)
1415 c_name := g.types_type_to_c(concrete)
1416 if c_name != '' && g.generic_struct_primary_instance_is_concrete(c_name) {
1417 return true
1418 }
1419 if c_name != '' {
1420 if struct_typ := g.lookup_type_by_c_name_const(c_name) {
1421 if struct_typ is types.Struct
1422 && type_contains_generic_placeholder(types.Type(struct_typ)) {
1423 return false
1424 }
1425 }
1426 }
1427 if c_name != '' && c_name in g.generic_struct_instances && c_name !in g.generic_struct_bindings {
1428 return false
1429 }
1430 return true
1431}
1432
1433fn (g &Gen) active_generic_bindings_matching_embedded_suffix(lhs ast.Expr, generic_params []string) ?map[string]types.Type {
1434 embedded_args := generic_call_embedded_type_arg_names(lhs, generic_params.len)
1435 return g.active_generic_bindings_matching_embedded_args(embedded_args, generic_params)
1436}
1437
1438fn (g &Gen) active_generic_bindings_matching_embedded_suffix_cursor(lhs ast.Cursor, generic_params []string) ?map[string]types.Type {
1439 embedded_args := generic_call_embedded_type_arg_names_cursor(lhs, generic_params.len)
1440 return g.active_generic_bindings_matching_embedded_args(embedded_args, generic_params)
1441}
1442
1443fn (g &Gen) active_generic_bindings_matching_name_suffix(name string, generic_params []string) ?map[string]types.Type {
1444 embedded_args := generic_call_embedded_type_arg_names_from_name(name, generic_params.len)
1445 return g.active_generic_bindings_matching_embedded_args(embedded_args, generic_params)
1446}
1447
1448fn (mut g Gen) bind_embedded_generic_type_args(lhs ast.Expr, generic_params []string, mut bindings map[string]types.Type) bool {
1449 embedded_args := generic_call_embedded_type_arg_names(lhs, generic_params.len)
1450 if embedded_args.len == 0 {
1451 return true
1452 }
1453 if embedded_args.len != generic_params.len {
1454 return bindings.len == generic_params.len
1455 }
1456 for i, param_name in generic_params {
1457 if param_name in bindings {
1458 continue
1459 }
1460 arg_name := embedded_args[i]
1461 if is_generic_placeholder_type_name(arg_name) {
1462 concrete := g.active_generic_types[arg_name] or { return false }
1463 bindings[param_name] = concrete
1464 continue
1465 }
1466 concrete := g.concrete_type_from_c_name(arg_name) or { return false }
1467 bindings[param_name] = concrete
1468 }
1469 return true
1470}
1471
1472fn (mut g Gen) bind_embedded_generic_type_args_cursor(lhs ast.Cursor, generic_params []string, mut bindings map[string]types.Type) bool {
1473 embedded_args := generic_call_embedded_type_arg_names_cursor(lhs, generic_params.len)
1474 if embedded_args.len == 0 {
1475 return true
1476 }
1477 if embedded_args.len != generic_params.len {
1478 return bindings.len == generic_params.len
1479 }
1480 for i, param_name in generic_params {
1481 if param_name in bindings {
1482 continue
1483 }
1484 arg_name := embedded_args[i]
1485 if is_generic_placeholder_type_name(arg_name) {
1486 concrete := g.active_generic_types[arg_name] or { return false }
1487 bindings[param_name] = concrete
1488 continue
1489 }
1490 concrete := g.concrete_type_from_c_name(arg_name) or { return false }
1491 bindings[param_name] = concrete
1492 }
1493 return true
1494}
1495
1496fn (mut g Gen) generic_type_arg_concrete_type(arg ast.Expr) ?types.Type {
1497 arg_name := arg.name()
1498 if is_generic_placeholder_type_name(arg_name) {
1499 if concrete := g.active_generic_types[arg_name] {
1500 resolved_concrete := g.concrete_type_with_active_generics(concrete)
1501 if !generic_concrete_type_is_runtime_specializable(resolved_concrete) {
1502 return none
1503 }
1504 return resolved_concrete
1505 }
1506 return none
1507 }
1508 c_name := g.expr_type_to_c(arg).trim_space().trim_right('*')
1509 concrete := g.concrete_type_from_c_name(c_name) or { return none }
1510 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
1511 return none
1512 }
1513 return concrete
1514}
1515
1516fn (mut g Gen) concrete_type_from_generic_call_arg(arg ast.Expr) ?types.Type {
1517 base_arg := if arg is ast.ModifierExpr { arg.expr } else { arg }
1518 if is_comptime_field_metadata_expr(base_arg, g.comptime_field_var) {
1519 return none
1520 }
1521 if base_arg is ast.SelectorExpr && is_comptime_selector_rhs_name(base_arg.rhs.name)
1522 && type_has_valid_data(g.comptime_field_raw_type) {
1523 return g.comptime_field_raw_type
1524 }
1525 if active_concrete := g.active_generic_concrete_from_arg(base_arg) {
1526 return active_concrete
1527 }
1528 if base_arg is ast.Ident {
1529 if placeholder := g.cur_fn_generic_params[base_arg.name] {
1530 if placeholder !in g.active_generic_types {
1531 return none
1532 }
1533 }
1534 }
1535 mut c_name := ''
1536 if base_arg is ast.InitExpr {
1537 c_name = g.expr_type_to_c(base_arg.typ).trim_space()
1538 }
1539 if c_name == '' && base_arg is ast.CastExpr {
1540 c_name = g.expr_type_to_c(base_arg.typ).trim_space()
1541 }
1542 if (c_name == '' || c_name == 'int' || c_name == 'array' || c_name == 'map')
1543 && base_arg is ast.SelectorExpr {
1544 c_name = g.selector_field_type(base_arg).trim_space()
1545 if c_name == '' || c_name == 'int' || c_name == 'array' || c_name == 'map' {
1546 c_name = g.selector_declared_field_type(base_arg).trim_space()
1547 }
1548 }
1549 mut found_local_arg_type := false
1550 if c_name == '' || c_name == 'int' {
1551 if base_arg is ast.Ident {
1552 c_name = (g.get_local_var_c_type(base_arg.name) or { '' }).trim_space()
1553 found_local_arg_type = c_name != ''
1554 }
1555 }
1556 if !found_local_arg_type && (c_name == '' || c_name == 'int') {
1557 c_name = g.get_expr_type(base_arg).trim_space()
1558 }
1559 if c_name == '' || c_name == 'int' {
1560 if raw := g.get_raw_type(base_arg) {
1561 return raw
1562 }
1563 }
1564 if c_name == '' || c_name == 'int' {
1565 return none
1566 }
1567 if concrete := g.active_generic_types[c_name] {
1568 return concrete
1569 }
1570 return g.concrete_type_from_call_arg_c_name(c_name)
1571}
1572
1573fn (mut g Gen) concrete_type_from_generic_call_arg_cursor(arg ast.Cursor) ?types.Type {
1574 base_arg := generic_call_base_arg_cursor(arg)
1575 if is_comptime_field_metadata_expr_cursor(base_arg, g.comptime_field_var) {
1576 return none
1577 }
1578 if base_arg.kind() == .expr_selector && is_comptime_selector_rhs_name(base_arg.edge(1).name())
1579 && type_has_valid_data(g.comptime_field_raw_type) {
1580 return g.comptime_field_raw_type
1581 }
1582 if active_concrete := g.active_generic_concrete_from_arg_cursor(base_arg) {
1583 return active_concrete
1584 }
1585 if base_arg.kind() == .expr_ident {
1586 if placeholder := g.cur_fn_generic_params[base_arg.name()] {
1587 if placeholder !in g.active_generic_types {
1588 return none
1589 }
1590 }
1591 }
1592 mut c_name := ''
1593 match base_arg.kind() {
1594 .expr_init {
1595 c_name = g.expr_type_to_c(base_arg.edge(0).type_expr()).trim_space()
1596 }
1597 .expr_cast {
1598 c_name = g.expr_type_to_c(base_arg.edge(0).type_expr()).trim_space()
1599 }
1600 .expr_as_cast {
1601 c_name = g.expr_type_to_c(base_arg.edge(1).type_expr()).trim_space()
1602 }
1603 else {}
1604 }
1605
1606 if (c_name == '' || c_name == 'int' || c_name == 'array' || c_name == 'map')
1607 && base_arg.kind() == .expr_selector {
1608 c_name = g.selector_declared_field_type_cursor_for_generic_scan(base_arg).trim_space()
1609 }
1610
1611 mut found_local_arg_type := false
1612 if c_name == '' || c_name == 'int' {
1613 if base_arg.kind() == .expr_ident {
1614 c_name = (g.get_local_var_c_type(base_arg.name()) or { '' }).trim_space()
1615 found_local_arg_type = c_name != ''
1616 }
1617 }
1618 if !found_local_arg_type && (c_name == '' || c_name == 'int') {
1619 c_name = g.generic_scan_expr_cursor_c_type(base_arg).trim_space()
1620 }
1621 if !found_local_arg_type && (c_name == '' || c_name == 'int') {
1622 if raw := g.raw_type_from_generic_call_arg_cursor(base_arg) {
1623 return raw
1624 }
1625 }
1626 if c_name == '' || c_name == 'int' {
1627 return none
1628 }
1629 if concrete := g.active_generic_types[c_name] {
1630 return concrete
1631 }
1632 return g.concrete_type_from_call_arg_c_name(c_name)
1633}
1634
1635fn generic_call_base_arg_cursor(arg ast.Cursor) ast.Cursor {
1636 if arg.kind() == .expr_modifier {
1637 return arg.edge(0)
1638 }
1639 return arg
1640}
1641
1642fn (mut g Gen) active_generic_concrete_from_arg_cursor(arg ast.Cursor) ?types.Type {
1643 if g.active_generic_types.len == 0 {
1644 return none
1645 }
1646 if arg.kind() == .expr_prefix && unsafe { token.Token(int(arg.aux())) } == .amp {
1647 if concrete := g.active_generic_concrete_from_arg_cursor(arg.edge(0)) {
1648 return types.Type(types.Pointer{
1649 base_type: concrete
1650 })
1651 }
1652 }
1653 if arg.kind() == .expr_ident {
1654 if param_name := g.cur_fn_generic_params[arg.name()] {
1655 if concrete := g.active_generic_types[param_name] {
1656 return normalize_generic_concrete_type(concrete)
1657 }
1658 }
1659 }
1660 raw := g.raw_type_from_generic_call_arg_cursor(arg) or { return none }
1661 concrete := g.concrete_type_with_active_generics(raw)
1662 if type_contains_generic_placeholder(concrete) || concrete.name() == raw.name() {
1663 return none
1664 }
1665 return normalize_generic_concrete_type(concrete)
1666}
1667
1668fn (mut g Gen) raw_type_from_generic_call_arg_cursor(arg ast.Cursor) ?types.Type {
1669 if g.env == unsafe { nil } || !arg.is_valid() {
1670 return none
1671 }
1672 if arg.kind() == .expr_ident {
1673 if cached := g.is_module_ident_cache[arg.name()] {
1674 if cached {
1675 return none
1676 }
1677 }
1678 if local_type := g.runtime_local_types[arg.name()] {
1679 if resolved := g.resolve_c_type_to_raw(local_type) {
1680 return resolved
1681 }
1682 }
1683 if mut fn_scope := g.ensure_cur_fn_scope() {
1684 if obj := fn_scope.lookup_parent(arg.name(), 0) {
1685 if obj is types.Module {
1686 return none
1687 }
1688 return obj.typ()
1689 }
1690 }
1691 }
1692 pos := arg.pos()
1693 if pos.is_valid() {
1694 if typ := g.env.get_expr_type(pos.id) {
1695 if type_has_valid_data(typ) {
1696 return typ
1697 }
1698 }
1699 }
1700 return none
1701}
1702
1703fn is_comptime_field_metadata_expr(expr ast.Expr, field_var string) bool {
1704 if field_var == '' {
1705 return false
1706 }
1707 base_expr := if expr is ast.ModifierExpr { expr.expr } else { expr }
1708 if base_expr is ast.SelectorExpr && base_expr.lhs is ast.Ident
1709 && (base_expr.lhs as ast.Ident).name == field_var
1710 && !is_comptime_selector_rhs_name(base_expr.rhs.name) {
1711 return true
1712 }
1713 return false
1714}
1715
1716fn is_comptime_field_metadata_expr_cursor(expr ast.Cursor, field_var string) bool {
1717 if field_var == '' {
1718 return false
1719 }
1720 base_expr := generic_call_base_arg_cursor(expr)
1721 if base_expr.kind() != .expr_selector {
1722 return false
1723 }
1724 lhs := base_expr.edge(0)
1725 rhs := base_expr.edge(1)
1726 return lhs.kind() == .expr_ident && lhs.name() == field_var
1727 && !is_comptime_selector_rhs_name(rhs.name())
1728}
1729
1730fn (mut g Gen) record_late_generic_call_spec_for_key(key string, bindings map[string]types.Type) {
1731 if key == '' || bindings.len == 0 {
1732 return
1733 }
1734 for _, concrete in bindings {
1735 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
1736 return
1737 }
1738 }
1739 for existing in g.late_generic_specs[key] {
1740 if existing == bindings {
1741 return
1742 }
1743 }
1744 g.late_generic_specs[key] << bindings.clone()
1745 g.index_late_generic_spec_key(key)
1746}
1747
1748fn (mut g Gen) record_late_generic_call_spec_unchecked(key string, bindings map[string]types.Type) {
1749 if key == '' || bindings.len == 0 {
1750 return
1751 }
1752 for existing in g.late_generic_specs[key] {
1753 if existing == bindings {
1754 return
1755 }
1756 }
1757 g.late_generic_specs[key] << bindings.clone()
1758 g.index_late_generic_spec_key(key)
1759}
1760
1761fn (mut g Gen) record_late_generic_call_spec(key string, bindings map[string]types.Type) {
1762 g.record_late_generic_call_spec_for_key(key, bindings)
1763 dot_pos := key.last_index_u8(`.`)
1764 if dot_pos > 0 && dot_pos < key.len - 1 {
1765 short_key := key[dot_pos + 1..]
1766 if short_key != key {
1767 g.record_late_generic_call_spec_for_key(short_key, bindings)
1768 }
1769 }
1770}
1771
1772fn (mut g Gen) record_generic_scan_call_name(lhs ast.Expr, arg_count int, generic_params []string, bindings map[string]types.Type) {
1773 base_name := g.resolve_call_name(lhs, arg_count)
1774 g.record_generic_scan_call_base_name(base_name, generic_params, bindings)
1775}
1776
1777fn (mut g Gen) record_generic_scan_call_name_cursor(lhs ast.Cursor, arg_count int, generic_params []string, bindings map[string]types.Type) {
1778 base_name := g.resolve_call_name_cursor_for_generic_scan(lhs, arg_count)
1779 g.record_generic_scan_call_base_name(base_name, generic_params, bindings)
1780}
1781
1782fn (mut g Gen) record_generic_scan_call_base_name(name string, generic_params []string, bindings map[string]types.Type) {
1783 if !g.collect_generic_scan_calls {
1784 return
1785 }
1786 if generic_params.len == 0 || bindings.len != generic_params.len {
1787 return
1788 }
1789 mut base_name := name
1790 if base_name == '' {
1791 return
1792 }
1793 if base_name.contains('_T_') {
1794 base_name = base_name.all_before('_T_')
1795 } else if base_name.ends_with('_T') {
1796 base_name = base_name[..base_name.len - 2]
1797 }
1798 base_name = normalize_duplicate_qualified_method_prefix(base_name)
1799 mut suffixes := []string{cap: generic_params.len}
1800 for param_name in generic_params {
1801 concrete := bindings[param_name] or { return }
1802 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
1803 return
1804 }
1805 suffixes << g.generic_specialization_token_from_type(concrete)
1806 }
1807 if suffixes.len == generic_params.len {
1808 specialized_name := '${base_name}_T_${suffixes.join('_')}'
1809 g.generic_scan_called_names[specialized_name] = true
1810 if !base_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
1811 && g.cur_module != 'builtin' {
1812 g.generic_scan_called_names['${g.cur_module}__${specialized_name}'] = true
1813 }
1814 }
1815}
1816
1817fn (mut g Gen) resolve_call_name_cursor_for_generic_scan(lhs ast.Cursor, arg_count int) string {
1818 mut name := ''
1819 match lhs.kind() {
1820 .expr_generic_arg_or_index, .expr_generic_args {
1821 return g.resolve_call_name_cursor_for_generic_scan(lhs.edge(0), arg_count)
1822 }
1823 .expr_ident {
1824 name = sanitize_fn_ident(lhs.name())
1825 }
1826 .expr_selector {
1827 lhs_expr := lhs.edge(0)
1828 method_name := sanitize_fn_ident(lhs.edge(1).name())
1829 if lhs_expr.kind() == .expr_ident && lhs_expr.name() == 'C' {
1830 return method_name
1831 }
1832 if lhs_expr.kind() == .expr_ident && (g.is_type_name(lhs_expr.name())
1833 || g.selector_lhs_is_static_type_ident(lhs_expr.name())) {
1834 return '${g.get_qualified_name(lhs_expr.name())}__${method_name}'
1835 }
1836 if mod_call_name := g.resolve_selector_module_call_name_cursor(lhs) {
1837 name = mod_call_name
1838 } else if qualified_method_name := g.resolved_qualified_selector_method_name(method_name) {
1839 name = qualified_method_name
1840 } else {
1841 mut base_type := g.method_receiver_base_type_cursor_for_generic_scan(lhs_expr)
1842 if base_type == '' {
1843 base_type =
1844 g.generic_scan_expr_cursor_c_type(lhs_expr).trim_space().trim_right('*')
1845 }
1846 name = '${base_type}__${method_name}'
1847 if receiver_type := g.get_receiver_expr_type_for_method_cursor(lhs_expr) {
1848 if concrete_method := g.resolve_method_on_concrete_type(receiver_type,
1849 method_name)
1850 {
1851 name = concrete_method
1852 }
1853 }
1854 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
1855 if raw_type := g.raw_type_from_generic_call_arg_cursor(lhs_expr) {
1856 raw_c_type := strip_pointer_type_name(g.types_type_to_c(raw_type))
1857 if raw_c_type != '' && raw_c_type != base_type {
1858 if raw_method := g.resolve_method_on_concrete_type(raw_c_type,
1859 method_name)
1860 {
1861 name = raw_method
1862 }
1863 }
1864 }
1865 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
1866 if alias_base := g.alias_base_c_type(base_type) {
1867 alias_name := '${alias_base}__${method_name}'
1868 if alias_name in g.fn_return_types || alias_name in g.fn_param_is_ptr {
1869 name = alias_name
1870 }
1871 }
1872 }
1873 if name !in g.fn_return_types && name !in g.fn_param_is_ptr {
1874 if map_method := g.map_runtime_method_name_cursor(lhs_expr, method_name) {
1875 name = map_method
1876 }
1877 }
1878 }
1879 }
1880 }
1881 else {}
1882 }
1883
1884 if name == 'builtin__new_array_from_c_array_noscan' {
1885 name = 'new_array_from_c_array'
1886 }
1887 if name == 'builtin__array_push_noscan' {
1888 name = 'array__push'
1889 }
1890 if name == 'panic' {
1891 name = 'v_panic'
1892 }
1893 if name == 'voidptr__vbytes' {
1894 name = 'void__vbytes'
1895 }
1896 if name == 'int__bytestr' {
1897 name = 'Array_u8__bytestr'
1898 }
1899 if name.ends_with('__bytes') && name !in g.fn_return_types && name !in g.fn_param_is_ptr {
1900 if 'string__bytes' in g.fn_return_types || 'string__bytes' in g.fn_param_is_ptr {
1901 name = 'string__bytes'
1902 }
1903 }
1904 if is_c_runtime_function(name) {
1905 return name
1906 }
1907 if name != '' && g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin'
1908 && !name.contains('__') {
1909 qualified := '${g.cur_module}__${name}'
1910 if qualified in g.fn_return_types || qualified in g.fn_param_is_ptr {
1911 return qualified
1912 }
1913 if name in g.fn_return_types || name in g.fn_param_is_ptr {
1914 return name
1915 }
1916 return qualified
1917 }
1918 return name
1919}
1920
1921fn (mut g Gen) resolve_selector_module_call_name_cursor(lhs ast.Cursor) ?string {
1922 if lhs.kind() != .expr_selector {
1923 return none
1924 }
1925 lhs_ident := lhs.edge(0)
1926 if lhs_ident.kind() != .expr_ident || lhs_ident.name() == 'C' {
1927 return none
1928 }
1929 if _ := g.get_local_var_c_type(lhs_ident.name()) {
1930 return none
1931 }
1932 mod_name := g.resolve_module_name(lhs_ident.name())
1933 name := '${mod_name}__${sanitize_fn_ident(lhs.edge(1).name())}'
1934 if g.is_module_ident(lhs_ident.name()) || name in g.fn_return_types || name in g.fn_param_is_ptr
1935 || g.is_module_local_fn(name) || g.has_specialized_fn_base(name)
1936 || g.has_generic_fn_decl_by_base_name(name) {
1937 return name
1938 }
1939 return none
1940}
1941
1942fn (mut g Gen) direct_known_c_type_for_expr_cursor(expr ast.Cursor) string {
1943 if !expr.is_valid() {
1944 return ''
1945 }
1946 match expr.kind() {
1947 .expr_paren, .expr_modifier {
1948 return g.direct_known_c_type_for_expr_cursor(expr.edge(0))
1949 }
1950 .expr_prefix {
1951 if unsafe { token.Token(int(expr.aux())) } == .mul {
1952 ptr_type := g.direct_known_c_type_for_expr_cursor(expr.edge(0))
1953 if ptr_type != '' {
1954 return strip_one_pointer_type_name(ptr_type)
1955 }
1956 }
1957 }
1958 .expr_cast {
1959 return g.expr_type_to_c(expr.edge(0).type_expr())
1960 }
1961 .expr_as_cast {
1962 return g.expr_type_to_c(expr.edge(1).type_expr())
1963 }
1964 .expr_ident {
1965 name := expr.name()
1966 if local_type := g.get_local_var_c_type(name) {
1967 return local_type
1968 }
1969 if const_type := g.const_types[name] {
1970 return const_type
1971 }
1972 if global_type := g.global_var_types[name] {
1973 return global_type
1974 }
1975 if g.cur_module != '' {
1976 qualified := '${g.cur_module}__${name}'
1977 if const_type := g.const_types[qualified] {
1978 return const_type
1979 }
1980 if global_type := g.global_var_types[qualified] {
1981 return global_type
1982 }
1983 }
1984 }
1985 else {}
1986 }
1987
1988 return ''
1989}
1990
1991fn (mut g Gen) selector_struct_name_cursor_for_generic_scan(expr ast.Cursor) string {
1992 direct_type := g.direct_known_c_type_for_expr_cursor(expr)
1993 if direct_type != '' {
1994 base := strip_pointer_type_name(direct_type)
1995 if base != '' && base != 'int' && base !in ['void', 'void*', 'voidptr'] {
1996 return base
1997 }
1998 }
1999 if expr.kind() == .expr_ident {
2000 if local_type := g.get_local_var_c_type(expr.name()) {
2001 base := strip_pointer_type_name(local_type)
2002 if base != '' && base != 'int' && base !in ['void', 'void*', 'voidptr'] {
2003 return base
2004 }
2005 }
2006 }
2007 if raw_type := g.raw_type_from_generic_call_arg_cursor(expr) {
2008 if !type_has_valid_data(raw_type) {
2009 return g.generic_scan_expr_cursor_c_type(expr).trim_right('*')
2010 }
2011 match raw_type {
2012 types.Pointer {
2013 if raw_type.base_type is types.Struct {
2014 return raw_type.base_type.name
2015 }
2016 if raw_type.base_type is types.Alias {
2017 return raw_type.base_type.name
2018 }
2019 }
2020 types.Struct {
2021 return raw_type.name
2022 }
2023 types.Alias {
2024 return raw_type.name
2025 }
2026 else {}
2027 }
2028 }
2029 return g.generic_scan_expr_cursor_c_type(expr).trim_right('*')
2030}
2031
2032fn (mut g Gen) selector_declared_field_type_cursor_for_generic_scan(sel ast.Cursor) string {
2033 if sel.kind() != .expr_selector {
2034 return ''
2035 }
2036 rhs := sel.edge(1).name()
2037 if rhs == '' {
2038 return ''
2039 }
2040 lhs_expr := sel.edge(0)
2041 lhs_struct_name := g.selector_struct_name_cursor_for_generic_scan(lhs_expr)
2042 if lhs_struct_name != '' {
2043 if g.active_generic_types.len > 0 || lhs_struct_name in g.generic_struct_bindings
2044 || lhs_struct_name in g.generic_struct_instances || lhs_struct_name.contains('_T_') {
2045 if field_type := g.lookup_struct_field_type_by_name(lhs_struct_name, rhs) {
2046 return field_type
2047 }
2048 }
2049 if field_type := g.lookup_struct_decl_field_type_by_name(lhs_struct_name, rhs) {
2050 return field_type
2051 }
2052 if field_type := g.lookup_struct_field_type_by_name(lhs_struct_name, rhs) {
2053 return field_type
2054 }
2055 }
2056 lhs_expr_type := g.generic_scan_expr_cursor_c_type(lhs_expr)
2057 if lhs_expr_type != '' && lhs_expr_type != 'int' {
2058 if field_type := g.lookup_struct_field_type_by_name(lhs_expr_type, rhs) {
2059 return field_type
2060 }
2061 }
2062 mut raw_resolved := ''
2063 if lhs_raw := g.raw_type_from_generic_call_arg_cursor(lhs_expr) {
2064 if field_type := selector_struct_field_type_from_type(lhs_raw, rhs) {
2065 resolved := g.types_type_to_c(field_type)
2066 if resolved != '' && resolved !in ['int', 'array'] {
2067 return resolved
2068 }
2069 raw_resolved = resolved
2070 }
2071 }
2072 return raw_resolved
2073}
2074
2075fn (mut g Gen) method_receiver_base_type_cursor_for_generic_scan(expr ast.Cursor) string {
2076 mut base_expr := expr
2077 for base_expr.is_valid() && base_expr.kind() in [.expr_paren, .expr_modifier] {
2078 base_expr = base_expr.edge(0)
2079 }
2080 if !base_expr.is_valid() {
2081 return ''
2082 }
2083 if base_expr.kind() == .expr_prefix
2084 && unsafe { token.Token(int(base_expr.aux())) } in [.amp, .mul] {
2085 return g.method_receiver_base_type_cursor_for_generic_scan(base_expr.edge(0))
2086 }
2087 direct_type := g.direct_known_c_type_for_expr_cursor(base_expr)
2088 if direct_type != '' && direct_type != 'int' {
2089 base := strip_pointer_type_name(direct_type)
2090 if base != '' && base != 'int' && base !in ['void', 'void*', 'voidptr'] {
2091 return base
2092 }
2093 }
2094 if base_expr.kind() == .expr_init {
2095 init_type := g.expr_type_to_c(base_expr.edge(0).type_expr())
2096 if init_type != '' && init_type != 'int' {
2097 return init_type
2098 }
2099 }
2100 if base_expr.kind() == .expr_ident {
2101 if local_type := g.get_local_var_c_type(base_expr.name()) {
2102 base := strip_pointer_type_name(local_type)
2103 if base != '' && base != 'int' {
2104 return base
2105 }
2106 }
2107 }
2108 if base_expr.kind() == .expr_selector {
2109 lhs_struct_name := g.selector_struct_name_cursor_for_generic_scan(base_expr.edge(0))
2110 if lhs_struct_name != '' {
2111 if declared_field_type := g.lookup_struct_field_type_by_name(lhs_struct_name,
2112 base_expr.edge(1).name())
2113 {
2114 base := strip_pointer_type_name(declared_field_type)
2115 if base != '' && base != 'int' && base !in ['void', 'void*', 'voidptr'] {
2116 return base
2117 }
2118 }
2119 }
2120 field_type := g.selector_declared_field_type_cursor_for_generic_scan(base_expr)
2121 if field_type != '' && field_type != 'int' {
2122 base := strip_pointer_type_name(field_type)
2123 if base in ['voidptr', 'void*'] {
2124 return 'void'
2125 }
2126 return base
2127 }
2128 }
2129 if raw_type := g.raw_type_from_generic_call_arg_cursor(base_expr) {
2130 match raw_type {
2131 types.Pointer {
2132 return g.types_type_to_c(raw_type.base_type)
2133 }
2134 else {
2135 raw_c_type := g.types_type_to_c(raw_type)
2136 if raw_c_type != 'void' {
2137 return raw_c_type
2138 }
2139 }
2140 }
2141 }
2142 mut receiver_type := g.generic_scan_expr_cursor_c_type(base_expr)
2143 if receiver_type.ends_with('*') {
2144 receiver_type = receiver_type[..receiver_type.len - 1]
2145 }
2146 if receiver_type in ['voidptr', 'void*'] {
2147 return 'void'
2148 }
2149 return receiver_type
2150}
2151
2152fn (mut g Gen) get_receiver_expr_type_for_method_cursor(expr ast.Cursor) ?string {
2153 mut base_expr := expr
2154 for base_expr.is_valid() && base_expr.kind() in [.expr_paren, .expr_modifier] {
2155 base_expr = base_expr.edge(0)
2156 }
2157 if !base_expr.is_valid() {
2158 return none
2159 }
2160 if base_expr.kind() == .expr_prefix
2161 && unsafe { token.Token(int(base_expr.aux())) } in [.amp, .mul] {
2162 if receiver_type := g.get_receiver_expr_type_for_method_cursor(base_expr.edge(0)) {
2163 return receiver_type
2164 }
2165 }
2166 if active_concrete := g.active_generic_concrete_from_arg_cursor(base_expr) {
2167 active_type := g.types_type_to_c(active_concrete).trim_space()
2168 if active_type != '' && active_type != 'int' && active_type != 'void' {
2169 return active_type.trim_right('*')
2170 }
2171 }
2172 if base_expr.kind() == .expr_ident {
2173 local_type := (g.get_local_var_c_type(base_expr.name()) or { '' }).trim_space()
2174 if local_type != '' && local_type != 'int' && local_type != 'void' {
2175 return local_type.trim_right('*')
2176 }
2177 }
2178 mut receiver_type := g.generic_scan_expr_cursor_c_type(base_expr).trim_space()
2179 if base_expr.kind() == .expr_selector {
2180 declared_type :=
2181 g.selector_declared_field_type_cursor_for_generic_scan(base_expr).trim_space()
2182 declared_base := strip_pointer_type_name(declared_type)
2183 receiver_base := strip_pointer_type_name(receiver_type)
2184 if declared_type != '' && declared_type != 'int' && declared_type != 'void'
2185 && (receiver_type == '' || receiver_type == 'int'
2186 || (declared_type.contains('_T_') && (!receiver_type.contains('_T_')
2187 || declared_base != receiver_base))) {
2188 receiver_type = declared_type
2189 }
2190 }
2191 if (receiver_type == '' || receiver_type == 'int') && base_expr.kind() == .expr_ident {
2192 receiver_type = (g.get_local_var_c_type(base_expr.name()) or { '' }).trim_space()
2193 }
2194 if receiver_type == '' || receiver_type == 'int' {
2195 if raw := g.raw_type_from_generic_call_arg_cursor(base_expr) {
2196 receiver_type = g.types_type_to_c(raw).trim_space()
2197 }
2198 }
2199 if receiver_type.ends_with('*') {
2200 receiver_type = receiver_type[..receiver_type.len - 1]
2201 }
2202 if receiver_type == '' || receiver_type == 'int' {
2203 return none
2204 }
2205 return receiver_type
2206}
2207
2208fn (mut g Gen) selector_receiver_is_map_cursor(receiver ast.Cursor) bool {
2209 if raw_type := g.raw_type_from_generic_call_arg_cursor(receiver) {
2210 match raw_type {
2211 types.Map {
2212 return true
2213 }
2214 types.Pointer {
2215 match raw_type.base_type {
2216 types.Map {
2217 return true
2218 }
2219 types.Alias {
2220 return raw_type.base_type.base_type is types.Map
2221 }
2222 else {}
2223 }
2224 }
2225 types.Alias {
2226 return raw_type.base_type is types.Map
2227 }
2228 else {}
2229 }
2230 }
2231 base_type := g.method_receiver_base_type_cursor_for_generic_scan(receiver)
2232 if c_type_is_map_value(base_type) {
2233 return true
2234 }
2235 expr_type := g.generic_scan_expr_cursor_c_type(receiver)
2236 return c_type_is_map_value(expr_type)
2237}
2238
2239fn (mut g Gen) map_runtime_method_name_cursor(receiver ast.Cursor, method_name string) ?string {
2240 if method_name !in ['clone', 'keys', 'values'] || !g.selector_receiver_is_map_cursor(receiver) {
2241 return none
2242 }
2243 return 'map__${method_name}'
2244}
2245
2246fn normalize_duplicate_qualified_method_prefix(name string) string {
2247 parts := name.split('__')
2248 if parts.len < 5 {
2249 return name
2250 }
2251 max_prefix_parts := parts.len / 2
2252 for prefix_parts := max_prefix_parts; prefix_parts >= 2; prefix_parts-- {
2253 mut matches := true
2254 for i := 0; i < prefix_parts; i++ {
2255 if parts[i] != parts[prefix_parts + i] {
2256 matches = false
2257 break
2258 }
2259 }
2260 if matches {
2261 return parts[prefix_parts..].join('__')
2262 }
2263 }
2264 return name
2265}
2266
2267fn (mut g Gen) index_late_generic_spec_key(key string) {
2268 // Some generic function values are first discovered while emitting an earlier
2269 // function body. Keep the specialization index in sync immediately so a later
2270 // declaration in the same generation pass can emit the concrete body.
2271 mut fn_name := key
2272 bracket_idx := key.index_u8(`[`)
2273 if bracket_idx > 0 {
2274 fn_name = key[..bracket_idx]
2275 }
2276 dot_idx := fn_name.last_index_u8(`.`)
2277 if dot_idx > 0 && dot_idx < fn_name.len - 1 {
2278 short_name := fn_name[dot_idx + 1..]
2279 if short_name.len > 0 && key !in g.generic_spec_index[short_name] {
2280 g.generic_spec_index[short_name] << key
2281 }
2282 }
2283 double_underscore_idx := fn_name.last_index('__') or { -1 }
2284 if double_underscore_idx > 0 && double_underscore_idx < fn_name.len - 2 {
2285 short_name := fn_name[double_underscore_idx + 2..]
2286 if short_name.len > 0 && key !in g.generic_spec_index[short_name] {
2287 g.generic_spec_index[short_name] << key
2288 }
2289 }
2290 if fn_name.len > 0 && key !in g.generic_spec_index[fn_name] {
2291 g.generic_spec_index[fn_name] << key
2292 }
2293}
2294
2295fn (mut g Gen) scan_call_for_generic_fn_specs(call ast.CallExpr) {
2296 decl := g.generic_call_decl_from_lhs(call.lhs) or { return }
2297 generic_params := g.generic_fn_param_names(decl)
2298 if generic_params.len == 0 {
2299 return
2300 }
2301 mut bindings := map[string]types.Type{}
2302 mut metadata_params := map[string]bool{}
2303 type_args := generic_call_type_args(call.lhs)
2304 mut has_unresolved_explicit_type_arg := false
2305 for i, param_name in generic_params {
2306 if i >= type_args.len {
2307 break
2308 }
2309 concrete := g.generic_type_arg_concrete_type(type_args[i]) or {
2310 has_unresolved_explicit_type_arg = true
2311 continue
2312 }
2313 bindings[param_name] = concrete
2314 }
2315 if has_unresolved_explicit_type_arg {
2316 return
2317 }
2318 if type_args.len == 0 {
2319 if active_bindings := g.active_generic_bindings_matching_embedded_suffix(call.lhs,
2320 generic_params)
2321 {
2322 for param_name, concrete in active_bindings {
2323 bindings[param_name] = concrete
2324 }
2325 }
2326 }
2327 if type_args.len == 0 && g.active_generic_types.len == 0
2328 && !g.bind_embedded_generic_type_args(call.lhs, generic_params, mut bindings) {
2329 return
2330 }
2331 arg_offset := if decl.is_method && call.args.len == decl.typ.params.len + 1 { 1 } else { 0 }
2332 for i, param in decl.typ.params {
2333 arg_idx := i + arg_offset
2334 if arg_idx >= call.args.len || !expr_has_generic_placeholder(param.typ) {
2335 continue
2336 }
2337 arg := call.args[arg_idx]
2338 if is_comptime_field_metadata_expr(arg, g.comptime_field_var) {
2339 mut seen := map[string]bool{}
2340 mut names := []string{}
2341 collect_generic_placeholder_names_from_expr(param.typ, mut seen, mut names)
2342 for name in names {
2343 metadata_params[name] = true
2344 }
2345 continue
2346 }
2347 concrete := g.concrete_type_from_generic_call_arg(arg) or { continue }
2348 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
2349 concrete), generic_params, mut bindings)
2350 }
2351 for param_name in generic_params {
2352 if param_name in bindings {
2353 continue
2354 }
2355 if param_name in metadata_params {
2356 continue
2357 }
2358 if concrete := g.active_generic_types[param_name] {
2359 bindings[param_name] = concrete
2360 }
2361 }
2362 if type_args.len == 0 && bindings.len != generic_params.len
2363 && !g.bind_embedded_generic_type_args(call.lhs, generic_params, mut bindings) {
2364 raw_name := generic_call_raw_name(call.lhs)
2365 if !g.generic_call_name_has_placeholder_suffix(raw_name) {
2366 if specialized_name := g.try_specialize_generic_call_name(raw_name, call.args) {
2367 g.record_called_specialized_generic_name(specialized_name)
2368 }
2369 return
2370 }
2371 return_bindings := g.infer_generic_bindings_from_current_return(decl, generic_params) or {
2372 return
2373 }
2374 for param_name, concrete in return_bindings {
2375 if param_name !in bindings {
2376 bindings[param_name] = concrete
2377 }
2378 }
2379 }
2380 if bindings.len != generic_params.len {
2381 return
2382 }
2383 for _, concrete in bindings {
2384 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
2385 return
2386 }
2387 }
2388 short_name := generic_call_short_name(call.lhs)
2389 keys := if short_name == '' {
2390 []string{}
2391 } else {
2392 [short_name]
2393 }
2394
2395 for key in keys {
2396 g.record_late_generic_call_spec(key, bindings)
2397 }
2398 g.record_generic_scan_call_name(call.lhs, call.args.len, generic_params, bindings)
2399}
2400
2401fn generic_fn_value_base_cursor(expr ast.Cursor) ast.Cursor {
2402 if expr.kind() in [.expr_generic_args, .expr_generic_arg_or_index] {
2403 return expr.edge(0)
2404 }
2405 return ast.Cursor{}
2406}
2407
2408fn (mut g Gen) scan_generic_struct_bindings_cursor(expr ast.Cursor) {
2409 if g.generic_call_spec_scan_only {
2410 return
2411 }
2412 match expr.kind() {
2413 .expr_generic_arg_or_index {
2414 base_name := g.expr_type_to_c(expr.edge(0).type_expr())
2415 arg := expr.edge(1)
2416 arg_name := arg.name()
2417 struct_base := if base_name.contains('__') {
2418 base_name.all_after_last('__')
2419 } else {
2420 base_name
2421 }
2422 if is_generic_placeholder_type_name(arg_name) {
2423 if concrete := g.active_generic_types[arg_name] {
2424 g.record_generic_struct_bindings(struct_base, base_name, [
2425 ast.Expr(ast.Ident{
2426 name: g.types_type_to_c(concrete)
2427 }),
2428 ])
2429 }
2430 } else {
2431 g.record_generic_struct_bindings(struct_base, base_name, [
2432 arg.type_expr(),
2433 ])
2434 }
2435 }
2436 .expr_generic_args {
2437 args := generic_call_type_args_cursor(expr)
2438 if args.len == 0 {
2439 return
2440 }
2441 base_name := g.expr_type_to_c(expr.edge(0).type_expr())
2442 struct_base := if base_name.contains('__') {
2443 base_name.all_after_last('__')
2444 } else {
2445 base_name
2446 }
2447 mut concrete_args := []ast.Expr{cap: args.len}
2448 mut all_concrete := true
2449 for arg in args {
2450 arg_name := arg.name()
2451 if is_generic_placeholder_type_name(arg_name) {
2452 if concrete := g.active_generic_types[arg_name] {
2453 concrete_args << ast.Expr(ast.Ident{
2454 name: g.types_type_to_c(concrete)
2455 })
2456 } else {
2457 all_concrete = false
2458 break
2459 }
2460 } else {
2461 concrete_args << arg
2462 }
2463 }
2464 if all_concrete {
2465 g.record_generic_struct_bindings(struct_base, base_name, concrete_args)
2466 }
2467 }
2468 else {}
2469 }
2470}
2471
2472fn (mut g Gen) scan_generic_fn_value_cursor_for_specs(expr ast.Cursor) {
2473 decl := g.generic_call_decl_from_lhs_cursor(expr) or { return }
2474 generic_params := g.generic_fn_param_names(decl)
2475 if generic_params.len == 0 {
2476 return
2477 }
2478 mut bindings := map[string]types.Type{}
2479 type_args := generic_call_type_args_cursor(expr)
2480 mut has_unresolved_explicit_type_arg := false
2481 for i, param_name in generic_params {
2482 if i >= type_args.len {
2483 break
2484 }
2485 concrete := g.generic_type_arg_concrete_type(type_args[i]) or {
2486 has_unresolved_explicit_type_arg = true
2487 continue
2488 }
2489 bindings[param_name] = concrete
2490 }
2491 if has_unresolved_explicit_type_arg {
2492 return
2493 }
2494 for param_name in generic_params {
2495 if param_name in bindings {
2496 continue
2497 }
2498 if concrete := g.active_generic_types[param_name] {
2499 bindings[param_name] = concrete
2500 }
2501 }
2502 if type_args.len == 0 && bindings.len != generic_params.len
2503 && !g.bind_embedded_generic_type_args_cursor(expr, generic_params, mut bindings) {
2504 return
2505 }
2506 if bindings.len != generic_params.len {
2507 return
2508 }
2509 for _, concrete in bindings {
2510 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
2511 return
2512 }
2513 }
2514 short_name := generic_call_short_name_cursor(expr)
2515 if short_name == '' {
2516 return
2517 }
2518 g.record_late_generic_call_spec(short_name, bindings)
2519 g.record_generic_scan_call_name_cursor(generic_fn_value_base_cursor(expr), 0, generic_params,
2520 bindings)
2521}
2522
2523fn (mut g Gen) scan_generic_fn_value_for_specs(expr ast.Expr) {
2524 decl := g.generic_call_decl_from_lhs(expr) or { return }
2525 generic_params := g.generic_fn_param_names(decl)
2526 if generic_params.len == 0 {
2527 return
2528 }
2529 mut bindings := map[string]types.Type{}
2530 type_args := generic_call_type_args(expr)
2531 mut has_unresolved_explicit_type_arg := false
2532 for i, param_name in generic_params {
2533 if i >= type_args.len {
2534 break
2535 }
2536 concrete := g.generic_type_arg_concrete_type(type_args[i]) or {
2537 has_unresolved_explicit_type_arg = true
2538 continue
2539 }
2540 bindings[param_name] = concrete
2541 }
2542 if has_unresolved_explicit_type_arg {
2543 return
2544 }
2545 // Generic function values inside generic functions do not have call
2546 // arguments to infer from. Substitute the surrounding function's concrete
2547 // bindings so a value like handler[A, X] emits handler_T_App_Context.
2548 for param_name in generic_params {
2549 if param_name in bindings {
2550 continue
2551 }
2552 if concrete := g.active_generic_types[param_name] {
2553 bindings[param_name] = concrete
2554 }
2555 }
2556 if type_args.len == 0 && bindings.len != generic_params.len
2557 && !g.bind_embedded_generic_type_args(expr, generic_params, mut bindings) {
2558 return
2559 }
2560 if bindings.len != generic_params.len {
2561 return
2562 }
2563 for _, concrete in bindings {
2564 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
2565 return
2566 }
2567 }
2568 short_name := generic_call_short_name(expr)
2569 if short_name == '' {
2570 return
2571 }
2572 g.record_late_generic_call_spec(short_name, bindings)
2573 g.record_generic_scan_call_name(generic_fn_value_base_expr(expr), 0, generic_params, bindings)
2574}
2575
2576fn (mut g Gen) scan_comptime_for_for_generic_types(node ast.ForStmt) bool {
2577 if node.init !is ast.ForInStmt {
2578 return false
2579 }
2580 for_in := node.init as ast.ForInStmt
2581 if for_in.expr !is ast.SelectorExpr {
2582 return false
2583 }
2584 sel := for_in.expr as ast.SelectorExpr
2585 if sel.rhs.name != 'fields' {
2586 return false
2587 }
2588 type_name := sel.lhs.name()
2589 concrete := g.active_generic_types[type_name] or { return false }
2590 if concrete !is types.Struct {
2591 return false
2592 }
2593 struct_type := g.comptime_for_struct_type(concrete, concrete as types.Struct)
2594 prev_field_var := g.comptime_field_var
2595 prev_field_name := g.comptime_field_name
2596 prev_field_type := g.comptime_field_type
2597 prev_field_raw_type := g.comptime_field_raw_type
2598 prev_field_attrs := g.comptime_field_attrs
2599 prev_field_idx := g.comptime_field_idx
2600 defer {
2601 g.comptime_field_var = prev_field_var
2602 g.comptime_field_name = prev_field_name
2603 g.comptime_field_type = prev_field_type
2604 g.comptime_field_raw_type = prev_field_raw_type
2605 g.comptime_field_attrs = prev_field_attrs
2606 g.comptime_field_idx = prev_field_idx
2607 }
2608 g.comptime_field_var = for_in.value.name()
2609 for i, field in struct_type.fields {
2610 g.comptime_field_name = field.name
2611 g.comptime_field_type = g.types_type_to_c(field.typ)
2612 g.comptime_field_raw_type = field.typ
2613 g.comptime_field_attrs = g.comptime_field_attribute_strings(struct_type.name, field)
2614 g.comptime_field_idx = i
2615 g.scan_stmts_for_generic_types(node.stmts)
2616 }
2617 return true
2618}
2619
2620// scan_stmts_for_generic_types walks statements to find generic
2621// type instantiations (e.g. LinkedList[StructFieldInfo]{} in function bodies).
2622fn generic_body_scan_bindings_key(bindings map[string]types.Type) string {
2623 if bindings.len == 0 {
2624 return ''
2625 }
2626 mut keys := bindings.keys()
2627 keys.sort()
2628 mut parts := []string{cap: keys.len}
2629 for key in keys {
2630 concrete := bindings[key] or { continue }
2631 parts << '${key}=${concrete.name()}'
2632 }
2633 return parts.join(',')
2634}
2635
2636fn (mut g Gen) scan_fn_body_for_generic_types(node ast.FnDecl, spec_name string) {
2637 prev_cur_fn_ret_type := g.cur_fn_ret_type
2638 if ret_type := g.scan_fn_return_c_type(node, spec_name) {
2639 g.cur_fn_ret_type = ret_type
2640 }
2641 defer {
2642 g.cur_fn_ret_type = prev_cur_fn_ret_type
2643 }
2644 if node.pos.id <= 0 {
2645 if !g.stmts_may_need_generic_scan(node.stmts) {
2646 return
2647 }
2648 g.scan_stmts_for_generic_types(node.stmts)
2649 return
2650 }
2651 fn_key := node.pos.id.str()
2652 bindings_key := if spec_name.len > 0 {
2653 spec_name
2654 } else {
2655 generic_body_scan_bindings_key(g.active_generic_types)
2656 }
2657 mode_key := if g.generic_call_spec_scan_only { 'calls' } else { 'all' }
2658 cache_key := '${g.cur_module}:${fn_key}:${bindings_key}:${mode_key}'
2659 if cache_key in g.generic_body_scan_cache {
2660 if !g.collect_generic_scan_calls {
2661 return
2662 }
2663 } else {
2664 g.generic_body_scan_cache[cache_key] = true
2665 }
2666 if !g.stmts_may_need_generic_scan(node.stmts) {
2667 return
2668 }
2669 g.scan_stmts_for_generic_types(node.stmts)
2670}
2671
2672fn (mut g Gen) scan_fn_body_cursor_for_generic_types(stmt ast.Cursor, decl ast.FnDecl, spec_name string) {
2673 prev_cur_fn_ret_type := g.cur_fn_ret_type
2674 if ret_type := g.scan_fn_return_c_type(decl, spec_name) {
2675 g.cur_fn_ret_type = ret_type
2676 }
2677 defer {
2678 g.cur_fn_ret_type = prev_cur_fn_ret_type
2679 }
2680 body := stmt.list_at(3)
2681 if decl.pos.id <= 0 {
2682 if !g.cursor_stmts_may_need_generic_scan(body) {
2683 return
2684 }
2685 g.scan_cursor_stmts_for_generic_types(body)
2686 return
2687 }
2688 fn_key := decl.pos.id.str()
2689 bindings_key := if spec_name.len > 0 {
2690 spec_name
2691 } else {
2692 generic_body_scan_bindings_key(g.active_generic_types)
2693 }
2694 mode_key := if g.generic_call_spec_scan_only { 'calls' } else { 'all' }
2695 cache_key := '${g.cur_module}:${fn_key}:${bindings_key}:${mode_key}'
2696 if cache_key in g.generic_body_scan_cache {
2697 if !g.collect_generic_scan_calls {
2698 return
2699 }
2700 } else {
2701 g.generic_body_scan_cache[cache_key] = true
2702 }
2703 if !g.cursor_stmts_may_need_generic_scan(body) {
2704 return
2705 }
2706 g.scan_cursor_stmts_for_generic_types(body)
2707}
2708
2709fn (mut g Gen) scan_fn_return_c_type(node ast.FnDecl, spec_name string) ?string {
2710 if spec_name != '' {
2711 if ret := g.fn_return_types[spec_name] {
2712 return normalize_signature_type_name(ret, 'void')
2713 }
2714 }
2715 if node.typ.return_type !is ast.EmptyExpr {
2716 return normalize_signature_type_name(g.expr_type_to_c(node.typ.return_type), 'void')
2717 }
2718 return 'void'
2719}
2720
2721fn (g &Gen) stmts_may_need_generic_scan(stmts []ast.Stmt) bool {
2722 for stmt in stmts {
2723 match stmt {
2724 ast.AssignStmt {
2725 for expr in stmt.lhs {
2726 if g.expr_may_need_generic_scan(expr) {
2727 return true
2728 }
2729 }
2730 for expr in stmt.rhs {
2731 if g.expr_may_need_generic_scan(expr) {
2732 return true
2733 }
2734 }
2735 }
2736 ast.ReturnStmt {
2737 for expr in stmt.exprs {
2738 if g.expr_may_need_generic_scan(expr) {
2739 return true
2740 }
2741 }
2742 }
2743 ast.ExprStmt {
2744 if g.expr_may_need_generic_scan(stmt.expr) {
2745 return true
2746 }
2747 }
2748 ast.ComptimeStmt {
2749 if g.stmts_may_need_generic_scan([stmt.stmt]) {
2750 return true
2751 }
2752 }
2753 ast.ForStmt {
2754 if g.expr_may_need_generic_scan(stmt.cond)
2755 || g.stmts_may_need_generic_scan(stmt.stmts) {
2756 return true
2757 }
2758 }
2759 ast.ForInStmt {
2760 if g.expr_may_need_generic_scan(stmt.key)
2761 || g.expr_may_need_generic_scan(stmt.value)
2762 || g.expr_may_need_generic_scan(stmt.expr) {
2763 return true
2764 }
2765 }
2766 ast.BlockStmt {
2767 if g.stmts_may_need_generic_scan(stmt.stmts) {
2768 return true
2769 }
2770 }
2771 else {}
2772 }
2773 }
2774 return false
2775}
2776
2777fn (g &Gen) cursor_stmts_may_need_generic_scan(stmts ast.CursorList) bool {
2778 for i in 0 .. stmts.len() {
2779 if g.cursor_stmt_may_need_generic_scan(stmts.at(i)) {
2780 return true
2781 }
2782 }
2783 return false
2784}
2785
2786fn (g &Gen) cursor_stmt_may_need_generic_scan(stmt ast.Cursor) bool {
2787 if !stmt.is_valid() {
2788 return false
2789 }
2790 match stmt.kind() {
2791 .stmt_assign {
2792 lhs_len := stmt.extra_int()
2793 for i in 0 .. lhs_len {
2794 if g.cursor_expr_may_need_generic_scan(stmt.edge(i)) {
2795 return true
2796 }
2797 }
2798 for i in lhs_len .. stmt.edge_count() {
2799 if g.cursor_expr_may_need_generic_scan(stmt.edge(i)) {
2800 return true
2801 }
2802 }
2803 }
2804 .stmt_return {
2805 for i in 0 .. stmt.edge_count() {
2806 if g.cursor_expr_may_need_generic_scan(stmt.edge(i)) {
2807 return true
2808 }
2809 }
2810 }
2811 .stmt_expr {
2812 return g.cursor_expr_may_need_generic_scan(stmt.edge(0))
2813 }
2814 .stmt_comptime {
2815 return g.cursor_stmt_may_need_generic_scan(stmt.edge(0))
2816 }
2817 .stmt_for {
2818 init := stmt.edge(0)
2819 if init.kind() == .stmt_for_in && g.cursor_stmt_may_need_generic_scan(init) {
2820 return true
2821 }
2822 if g.cursor_expr_may_need_generic_scan(stmt.edge(1)) {
2823 return true
2824 }
2825 return g.cursor_stmts_may_need_generic_scan(stmt.for_body_list())
2826 }
2827 .stmt_for_in {
2828 return g.cursor_expr_may_need_generic_scan(stmt.edge(0))
2829 || g.cursor_expr_may_need_generic_scan(stmt.edge(1))
2830 || g.cursor_expr_may_need_generic_scan(stmt.edge(2))
2831 }
2832 .stmt_block, .stmt_defer {
2833 for i in 0 .. stmt.edge_count() {
2834 if g.cursor_stmt_may_need_generic_scan(stmt.edge(i)) {
2835 return true
2836 }
2837 }
2838 }
2839 else {}
2840 }
2841
2842 return false
2843}
2844
2845fn (g &Gen) cursor_call_lhs_may_need_generic_scan(lhs ast.Cursor) bool {
2846 call_name := generic_call_short_name_cursor(lhs)
2847 if call_name != '' {
2848 for candidate in generic_call_decl_candidates(call_name) {
2849 if candidate in g.generic_fn_decl_index {
2850 return true
2851 }
2852 }
2853 }
2854 return g.cursor_expr_may_need_generic_scan(lhs)
2855}
2856
2857fn (g &Gen) cursor_expr_may_need_generic_scan(expr ast.Cursor) bool {
2858 if !expr.is_valid() {
2859 return false
2860 }
2861 match expr.kind() {
2862 .expr_ident {
2863 name := expr.name()
2864 return name.contains('_T_') || name.ends_with('_T')
2865 }
2866 .typ_anon_struct, .typ_array_fixed, .typ_array, .typ_channel, .typ_fn, .typ_generic,
2867 .typ_map, .typ_option, .typ_pointer, .typ_result, .typ_thread, .typ_tuple {
2868 return g.type_cursor_may_need_generic_scan(expr)
2869 }
2870 .expr_generic_arg_or_index, .expr_generic_args, .expr_tuple {
2871 return true
2872 }
2873 .expr_call {
2874 if g.cursor_call_lhs_may_need_generic_scan(expr.edge(0)) {
2875 return true
2876 }
2877 for i in 1 .. expr.edge_count() {
2878 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
2879 return true
2880 }
2881 }
2882 }
2883 .expr_call_or_cast {
2884 return g.cursor_call_lhs_may_need_generic_scan(expr.edge(0))
2885 || g.cursor_expr_may_need_generic_scan(expr.edge(1))
2886 }
2887 .expr_infix {
2888 return g.cursor_expr_may_need_generic_scan(expr.edge(0))
2889 || g.cursor_expr_may_need_generic_scan(expr.edge(1))
2890 }
2891 .expr_postfix, .expr_prefix, .expr_paren, .expr_modifier, .expr_lambda, .expr_comptime {
2892 return g.cursor_expr_may_need_generic_scan(expr.edge(0))
2893 }
2894 .expr_assoc {
2895 if g.type_cursor_may_need_generic_scan(expr.edge(0))
2896 || g.cursor_expr_may_need_generic_scan(expr.edge(1)) {
2897 return true
2898 }
2899 for i in 2 .. expr.edge_count() {
2900 if g.cursor_expr_may_need_generic_scan(expr.edge(i).edge(0)) {
2901 return true
2902 }
2903 }
2904 }
2905 .expr_if_guard {
2906 return g.cursor_stmt_may_need_generic_scan(expr.edge(0))
2907 }
2908 .expr_or {
2909 if g.cursor_expr_may_need_generic_scan(expr.edge(0)) {
2910 return true
2911 }
2912 for i in 1 .. expr.edge_count() {
2913 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
2914 return true
2915 }
2916 }
2917 }
2918 .expr_unsafe {
2919 for i in 0 .. expr.edge_count() {
2920 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
2921 return true
2922 }
2923 }
2924 }
2925 .expr_lock {
2926 packed := u32(expr.extra_int())
2927 lock_len := int(packed & 0xFFFF)
2928 rlock_len := int((packed >> 16) & 0xFFFF)
2929 for i in 0 .. (lock_len + rlock_len) {
2930 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
2931 return true
2932 }
2933 }
2934 for i in (lock_len + rlock_len) .. expr.edge_count() {
2935 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
2936 return true
2937 }
2938 }
2939 }
2940 .expr_if {
2941 if g.cursor_expr_may_need_generic_scan(expr.edge(0)) {
2942 return true
2943 }
2944 for i in 2 .. expr.edge_count() {
2945 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
2946 return true
2947 }
2948 }
2949 return g.cursor_expr_may_need_generic_scan(expr.edge(1))
2950 }
2951 .expr_match {
2952 if g.cursor_expr_may_need_generic_scan(expr.edge(0)) {
2953 return true
2954 }
2955 for i in 1 .. expr.edge_count() {
2956 branch := expr.edge(i)
2957 conds := branch.list_at(0)
2958 for ci in 0 .. conds.len() {
2959 if g.cursor_expr_may_need_generic_scan(conds.at(ci)) {
2960 return true
2961 }
2962 }
2963 if g.cursor_stmts_may_need_generic_scan(branch.list_at(1)) {
2964 return true
2965 }
2966 }
2967 }
2968 .expr_select {
2969 if g.cursor_stmt_may_need_generic_scan(expr.edge(0))
2970 || g.cursor_expr_may_need_generic_scan(expr.edge(1)) {
2971 return true
2972 }
2973 for i in 2 .. expr.edge_count() {
2974 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
2975 return true
2976 }
2977 }
2978 }
2979 .expr_string_inter {
2980 inters := expr.list_at(1)
2981 for i in 0 .. inters.len() {
2982 inter := inters.at(i)
2983 if g.cursor_expr_may_need_generic_scan(inter.edge(0))
2984 || g.cursor_expr_may_need_generic_scan(inter.edge(1)) {
2985 return true
2986 }
2987 }
2988 }
2989 .expr_init {
2990 if g.type_cursor_may_need_generic_scan(expr.edge(0)) {
2991 return true
2992 }
2993 for i in 1 .. expr.edge_count() {
2994 if g.cursor_expr_may_need_generic_scan(expr.edge(i).edge(0)) {
2995 return true
2996 }
2997 }
2998 }
2999 .expr_array_init {
3000 if g.type_cursor_may_need_generic_scan(expr.edge(0)) {
3001 return true
3002 }
3003 for i in 1 .. expr.edge_count() {
3004 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
3005 return true
3006 }
3007 }
3008 }
3009 .expr_map_init {
3010 if g.type_cursor_may_need_generic_scan(expr.edge(0)) {
3011 return true
3012 }
3013 for i in 1 .. expr.edge_count() {
3014 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
3015 return true
3016 }
3017 }
3018 }
3019 .expr_index, .expr_cast, .expr_as_cast, .expr_range {
3020 for i in 0 .. expr.edge_count() {
3021 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
3022 return true
3023 }
3024 }
3025 }
3026 .expr_keyword_operator {
3027 for i in 0 .. expr.edge_count() {
3028 if g.cursor_expr_may_need_generic_scan(expr.edge(i)) {
3029 return true
3030 }
3031 }
3032 }
3033 .expr_fn_literal {
3034 captured_len := expr.extra_int()
3035 for i in 0 .. captured_len {
3036 if g.cursor_expr_may_need_generic_scan(expr.edge(1 + i)) {
3037 return true
3038 }
3039 }
3040 for i in (1 + captured_len) .. expr.edge_count() {
3041 if g.cursor_stmt_may_need_generic_scan(expr.edge(i)) {
3042 return true
3043 }
3044 }
3045 }
3046 .expr_sql, .expr_selector {
3047 return g.cursor_expr_may_need_generic_scan(expr.edge(0))
3048 }
3049 else {}
3050 }
3051
3052 return false
3053}
3054
3055fn (g &Gen) type_cursor_may_need_generic_scan(typ ast.Cursor) bool {
3056 if !typ.is_valid() {
3057 return false
3058 }
3059 match typ.kind() {
3060 .typ_generic, .expr_generic_arg_or_index, .expr_generic_args {
3061 return true
3062 }
3063 .expr_ident {
3064 name := typ.name()
3065 return name.contains('_T_') || name.ends_with('_T')
3066 }
3067 .expr_modifier, .expr_prefix, .expr_paren {
3068 return g.type_cursor_may_need_generic_scan(typ.edge(0))
3069 }
3070 .typ_anon_struct {
3071 generic_params := typ.list_at(0)
3072 for i in 0 .. generic_params.len() {
3073 if g.cursor_expr_may_need_generic_scan(generic_params.at(i)) {
3074 return true
3075 }
3076 }
3077 embedded := typ.list_at(1)
3078 for i in 0 .. embedded.len() {
3079 if g.cursor_expr_may_need_generic_scan(embedded.at(i)) {
3080 return true
3081 }
3082 }
3083 fields := typ.list_at(2)
3084 for i in 0 .. fields.len() {
3085 field := fields.at(i)
3086 if g.cursor_expr_may_need_generic_scan(field.edge(0))
3087 || g.cursor_expr_may_need_generic_scan(field.edge(1)) {
3088 return true
3089 }
3090 }
3091 }
3092 .typ_array_fixed {
3093 return g.type_cursor_may_need_generic_scan(typ.edge(0))
3094 || g.type_cursor_may_need_generic_scan(typ.edge(1))
3095 }
3096 .typ_array, .typ_channel, .typ_option, .typ_pointer, .typ_result, .typ_thread {
3097 return g.type_cursor_may_need_generic_scan(typ.edge(0))
3098 }
3099 .typ_fn {
3100 generic_params := typ.list_at(0)
3101 for i in 0 .. generic_params.len() {
3102 if g.cursor_expr_may_need_generic_scan(generic_params.at(i)) {
3103 return true
3104 }
3105 }
3106 params := typ.list_at(1)
3107 for i in 0 .. params.len() {
3108 if g.cursor_expr_may_need_generic_scan(params.at(i).edge(0)) {
3109 return true
3110 }
3111 }
3112 return g.type_cursor_may_need_generic_scan(typ.edge(2))
3113 }
3114 .typ_map {
3115 return g.type_cursor_may_need_generic_scan(typ.edge(0))
3116 || g.type_cursor_may_need_generic_scan(typ.edge(1))
3117 }
3118 .typ_tuple {
3119 for i in 0 .. typ.edge_count() {
3120 if g.type_cursor_may_need_generic_scan(typ.edge(i)) {
3121 return true
3122 }
3123 }
3124 }
3125 else {}
3126 }
3127
3128 return false
3129}
3130
3131fn (g &Gen) call_lhs_may_need_generic_scan(lhs ast.Expr) bool {
3132 call_name := generic_call_short_name(lhs)
3133 if call_name != '' {
3134 for candidate in generic_call_decl_candidates(call_name) {
3135 if candidate in g.generic_fn_decl_index {
3136 return true
3137 }
3138 }
3139 }
3140 return g.expr_may_need_generic_scan(lhs)
3141}
3142
3143fn (g &Gen) expr_may_need_generic_scan(expr ast.Expr) bool {
3144 match expr {
3145 ast.Ident {
3146 return expr.name.contains('_T_') || expr.name.ends_with('_T')
3147 }
3148 ast.Type {
3149 return type_expr_may_need_generic_scan(expr)
3150 }
3151 ast.GenericArgOrIndexExpr {
3152 return true
3153 }
3154 ast.GenericArgs {
3155 return true
3156 }
3157 ast.CallOrCastExpr {
3158 return g.expr_may_need_generic_scan(expr.lhs) || g.expr_may_need_generic_scan(expr.expr)
3159 }
3160 ast.CallExpr {
3161 if g.call_lhs_may_need_generic_scan(expr.lhs) {
3162 return true
3163 }
3164 for arg in expr.args {
3165 if g.expr_may_need_generic_scan(arg) {
3166 return true
3167 }
3168 }
3169 }
3170 ast.InfixExpr {
3171 return g.expr_may_need_generic_scan(expr.lhs) || g.expr_may_need_generic_scan(expr.rhs)
3172 }
3173 ast.PostfixExpr {
3174 return g.expr_may_need_generic_scan(expr.expr)
3175 }
3176 ast.PrefixExpr {
3177 return g.expr_may_need_generic_scan(expr.expr)
3178 }
3179 ast.ParenExpr {
3180 return g.expr_may_need_generic_scan(expr.expr)
3181 }
3182 ast.ModifierExpr {
3183 return g.expr_may_need_generic_scan(expr.expr)
3184 }
3185 ast.AssocExpr {
3186 if type_expr_may_need_generic_scan(expr.typ) || g.expr_may_need_generic_scan(expr.expr) {
3187 return true
3188 }
3189 for field in expr.fields {
3190 if g.expr_may_need_generic_scan(field.value) {
3191 return true
3192 }
3193 }
3194 }
3195 ast.IfGuardExpr {
3196 return g.stmts_may_need_generic_scan([ast.Stmt(expr.stmt)])
3197 }
3198 ast.OrExpr {
3199 return g.expr_may_need_generic_scan(expr.expr)
3200 || g.stmts_may_need_generic_scan(expr.stmts)
3201 }
3202 ast.UnsafeExpr {
3203 return g.stmts_may_need_generic_scan(expr.stmts)
3204 }
3205 ast.LockExpr {
3206 for lock_expr in expr.lock_exprs {
3207 if g.expr_may_need_generic_scan(lock_expr) {
3208 return true
3209 }
3210 }
3211 for lock_expr in expr.rlock_exprs {
3212 if g.expr_may_need_generic_scan(lock_expr) {
3213 return true
3214 }
3215 }
3216 return g.stmts_may_need_generic_scan(expr.stmts)
3217 }
3218 ast.IfExpr {
3219 if g.expr_may_need_generic_scan(expr.cond) || g.stmts_may_need_generic_scan(expr.stmts) {
3220 return true
3221 }
3222 return expr.else_expr !is ast.EmptyExpr && g.expr_may_need_generic_scan(expr.else_expr)
3223 }
3224 ast.MatchExpr {
3225 if g.expr_may_need_generic_scan(expr.expr) {
3226 return true
3227 }
3228 for branch in expr.branches {
3229 for cond in branch.cond {
3230 if g.expr_may_need_generic_scan(cond) {
3231 return true
3232 }
3233 }
3234 if g.stmts_may_need_generic_scan(branch.stmts) {
3235 return true
3236 }
3237 }
3238 }
3239 ast.ComptimeExpr {
3240 return g.expr_may_need_generic_scan(expr.expr)
3241 }
3242 ast.SelectExpr {
3243 return g.stmts_may_need_generic_scan([expr.stmt])
3244 || g.stmts_may_need_generic_scan(expr.stmts)
3245 || g.expr_may_need_generic_scan(expr.next)
3246 }
3247 ast.StringInterLiteral {
3248 for item in expr.inters {
3249 if g.expr_may_need_generic_scan(item.expr)
3250 || g.expr_may_need_generic_scan(item.format_expr) {
3251 return true
3252 }
3253 }
3254 }
3255 ast.InitExpr {
3256 if type_expr_may_need_generic_scan(expr.typ) {
3257 return true
3258 }
3259 for field in expr.fields {
3260 if g.expr_may_need_generic_scan(field.value) {
3261 return true
3262 }
3263 }
3264 }
3265 ast.ArrayInitExpr {
3266 if type_expr_may_need_generic_scan(expr.typ) || g.expr_may_need_generic_scan(expr.len) {
3267 return true
3268 }
3269 if g.expr_may_need_generic_scan(expr.init) || g.expr_may_need_generic_scan(expr.cap)
3270 || g.expr_may_need_generic_scan(expr.update_expr) {
3271 return true
3272 }
3273 for item in expr.exprs {
3274 if g.expr_may_need_generic_scan(item) {
3275 return true
3276 }
3277 }
3278 }
3279 ast.MapInitExpr {
3280 if type_expr_may_need_generic_scan(expr.typ) {
3281 return true
3282 }
3283 for key in expr.keys {
3284 if g.expr_may_need_generic_scan(key) {
3285 return true
3286 }
3287 }
3288 for value in expr.vals {
3289 if g.expr_may_need_generic_scan(value) {
3290 return true
3291 }
3292 }
3293 }
3294 ast.IndexExpr {
3295 return g.expr_may_need_generic_scan(expr.lhs) || g.expr_may_need_generic_scan(expr.expr)
3296 }
3297 ast.CastExpr {
3298 return type_expr_may_need_generic_scan(expr.typ)
3299 || g.expr_may_need_generic_scan(expr.expr)
3300 }
3301 ast.AsCastExpr {
3302 return type_expr_may_need_generic_scan(expr.typ)
3303 || g.expr_may_need_generic_scan(expr.expr)
3304 }
3305 ast.Tuple {
3306 return true
3307 }
3308 ast.FieldInit {
3309 return g.expr_may_need_generic_scan(expr.value)
3310 }
3311 ast.KeywordOperator {
3312 for arg in expr.exprs {
3313 if g.expr_may_need_generic_scan(arg) {
3314 return true
3315 }
3316 }
3317 }
3318 ast.RangeExpr {
3319 return g.expr_may_need_generic_scan(expr.start)
3320 || g.expr_may_need_generic_scan(expr.end)
3321 }
3322 ast.FnLiteral {
3323 for captured in expr.captured_vars {
3324 if g.expr_may_need_generic_scan(captured) {
3325 return true
3326 }
3327 }
3328 return g.stmts_may_need_generic_scan(expr.stmts)
3329 }
3330 ast.LambdaExpr {
3331 return g.expr_may_need_generic_scan(expr.expr)
3332 }
3333 ast.SqlExpr {
3334 return g.expr_may_need_generic_scan(expr.expr)
3335 }
3336 else {}
3337 }
3338
3339 return false
3340}
3341
3342fn type_expr_may_need_generic_scan(expr ast.Expr) bool {
3343 match expr {
3344 ast.Type {
3345 if expr is ast.GenericType {
3346 return true
3347 }
3348 if expr is ast.ArrayType {
3349 return type_expr_may_need_generic_scan(expr.elem_type)
3350 }
3351 if expr is ast.ArrayFixedType {
3352 return type_expr_may_need_generic_scan(expr.elem_type)
3353 || type_expr_may_need_generic_scan(expr.len)
3354 }
3355 if expr is ast.MapType {
3356 return type_expr_may_need_generic_scan(expr.key_type)
3357 || type_expr_may_need_generic_scan(expr.value_type)
3358 }
3359 if expr is ast.OptionType {
3360 return type_expr_may_need_generic_scan(expr.base_type)
3361 }
3362 if expr is ast.ResultType {
3363 return type_expr_may_need_generic_scan(expr.base_type)
3364 }
3365 if expr is ast.PointerType {
3366 return type_expr_may_need_generic_scan(expr.base_type)
3367 }
3368 if expr is ast.FnType {
3369 for param in expr.params {
3370 if type_expr_may_need_generic_scan(param.typ) {
3371 return true
3372 }
3373 }
3374 return type_expr_may_need_generic_scan(expr.return_type)
3375 }
3376 return false
3377 }
3378 ast.GenericArgOrIndexExpr {
3379 return true
3380 }
3381 ast.GenericArgs {
3382 return true
3383 }
3384 ast.Ident {
3385 return expr.name.contains('_T_') || expr.name.ends_with('_T')
3386 }
3387 ast.ModifierExpr {
3388 return type_expr_may_need_generic_scan(expr.expr)
3389 }
3390 ast.PrefixExpr {
3391 return type_expr_may_need_generic_scan(expr.expr)
3392 }
3393 ast.ParenExpr {
3394 return type_expr_may_need_generic_scan(expr.expr)
3395 }
3396 else {
3397 return false
3398 }
3399 }
3400}
3401
3402fn (mut g Gen) scan_stmts_for_generic_types(stmts []ast.Stmt) {
3403 for stmt in stmts {
3404 if stmt is ast.AssignStmt {
3405 for rhs in stmt.rhs {
3406 g.scan_expr_for_generic_types(rhs)
3407 g.scan_expr_stmts_for_generic_types(rhs)
3408 }
3409 for i, lhs in stmt.lhs {
3410 if i >= stmt.rhs.len {
3411 continue
3412 }
3413 lhs_name := generic_wrapped_ident_name(lhs)
3414 if lhs_name == '' {
3415 continue
3416 }
3417
3418 mut rhs_type := g.get_expr_type(stmt.rhs[i]).trim_space()
3419 if generic_scan_type_is_unresolved(rhs_type) {
3420 if raw := g.get_raw_type(stmt.rhs[i]) {
3421 rhs_type = g.types_type_to_c(raw).trim_space()
3422 }
3423 }
3424 if generic_scan_type_is_unresolved(rhs_type) {
3425 rhs_type = (g.get_local_var_c_type(lhs_name) or { '' }).trim_space()
3426 }
3427 if generic_scan_type_is_unresolved(rhs_type) && stmt.rhs[i] is ast.ArrayInitExpr {
3428 array_init := stmt.rhs[i] as ast.ArrayInitExpr
3429 rhs_type = g.expr_type_to_c(array_init.typ).trim_space()
3430 }
3431 if !generic_scan_type_is_unresolved(rhs_type) {
3432 g.remember_runtime_local_type(lhs_name, rhs_type)
3433 } else if stmt.rhs[i] is ast.ArrayInitExpr {
3434 g.remember_runtime_current_local_type(lhs_name, 'array')
3435 }
3436 }
3437 } else if stmt is ast.ReturnStmt {
3438 for expr in stmt.exprs {
3439 g.scan_expr_for_generic_types(expr)
3440 g.scan_expr_stmts_for_generic_types(expr)
3441 }
3442 } else if stmt is ast.ExprStmt {
3443 g.scan_expr_for_generic_types(stmt.expr)
3444 // Recurse into IfExpr/MatchExpr bodies
3445 g.scan_expr_stmts_for_generic_types(stmt.expr)
3446 } else if stmt is ast.ComptimeStmt {
3447 if stmt.stmt is ast.ForStmt && g.scan_comptime_for_for_generic_types(stmt.stmt) {
3448 continue
3449 }
3450 if stmt.stmt is ast.ExprStmt && stmt.stmt.expr is ast.ComptimeExpr
3451 && (stmt.stmt.expr as ast.ComptimeExpr).expr is ast.IfExpr {
3452 g.scan_comptime_if_for_generic_types((stmt.stmt.expr as ast.ComptimeExpr).expr as ast.IfExpr)
3453 continue
3454 }
3455 // Recurse into comptime $if/$for bodies (wraps a single stmt)
3456 g.scan_stmts_for_generic_types([stmt.stmt])
3457 } else if stmt is ast.ForStmt {
3458 g.scan_stmts_for_generic_types(stmt.stmts)
3459 } else if stmt is ast.BlockStmt {
3460 g.scan_stmts_for_generic_types(stmt.stmts)
3461 }
3462 }
3463}
3464
3465fn (mut g Gen) scan_cursor_stmts_for_generic_types(stmts ast.CursorList) {
3466 for i in 0 .. stmts.len() {
3467 g.scan_cursor_stmt_for_generic_types(stmts.at(i))
3468 }
3469}
3470
3471fn (mut g Gen) scan_cursor_stmt_for_generic_types(stmt ast.Cursor) {
3472 if !stmt.is_valid() {
3473 return
3474 }
3475 match stmt.kind() {
3476 .stmt_assign {
3477 lhs_len := stmt.extra_int()
3478 for i in lhs_len .. stmt.edge_count() {
3479 g.scan_expr_cursor_for_generic_types(stmt.edge(i))
3480 }
3481 for i in 0 .. lhs_len {
3482 rhs_idx := lhs_len + i
3483 if rhs_idx >= stmt.edge_count() {
3484 continue
3485 }
3486 g.remember_assign_cursor_runtime_local_type(stmt.edge(i), stmt.edge(rhs_idx))
3487 }
3488 }
3489 .stmt_return {
3490 for i in 0 .. stmt.edge_count() {
3491 g.scan_expr_cursor_for_generic_types(stmt.edge(i))
3492 }
3493 }
3494 .stmt_expr {
3495 g.scan_expr_cursor_for_generic_types(stmt.edge(0))
3496 }
3497 .stmt_comptime {
3498 inner := stmt.edge(0)
3499 if inner.kind() == .stmt_for && g.scan_comptime_for_cursor_for_generic_types(inner) {
3500 return
3501 }
3502 if inner.kind() == .stmt_expr {
3503 expr := inner.edge(0)
3504 if expr.kind() == .expr_comptime && expr.edge(0).kind() == .expr_if {
3505 g.scan_comptime_if_cursor_for_generic_types(expr.edge(0))
3506 return
3507 }
3508 }
3509 g.scan_cursor_stmt_for_generic_types(inner)
3510 }
3511 .stmt_for {
3512 g.scan_cursor_stmts_for_generic_types(stmt.for_body_list())
3513 }
3514 .stmt_block, .stmt_defer {
3515 for i in 0 .. stmt.edge_count() {
3516 g.scan_cursor_stmt_for_generic_types(stmt.edge(i))
3517 }
3518 }
3519 else {}
3520 }
3521}
3522
3523fn generic_call_cursor_arg_count(call ast.Cursor) int {
3524 return if call.edge_count() > 1 { call.edge_count() - 1 } else { 0 }
3525}
3526
3527fn generic_call_cursor_arg(call ast.Cursor, idx int) ast.Cursor {
3528 edge_idx := idx + 1
3529 if idx < 0 || edge_idx >= call.edge_count() {
3530 return ast.Cursor{}
3531 }
3532 return call.edge(edge_idx)
3533}
3534
3535fn sum_payload_ident_name_from_cursor(expr ast.Cursor) ?string {
3536 if !expr.is_valid() {
3537 return none
3538 }
3539 match expr.kind() {
3540 .expr_ident {
3541 return expr.name()
3542 }
3543 .expr_as_cast, .expr_modifier, .expr_paren {
3544 return sum_payload_ident_name_from_cursor(expr.edge(0))
3545 }
3546 .expr_cast {
3547 return sum_payload_ident_name_from_cursor(expr.edge(1))
3548 }
3549 .expr_prefix {
3550 if unsafe { token.Token(int(expr.aux())) } == .mul {
3551 return sum_payload_ident_name_from_cursor(expr.edge(0))
3552 }
3553 }
3554 .expr_selector {
3555 lhs := expr.edge(0)
3556 rhs := expr.edge(1)
3557 if lhs.kind() == .expr_selector && lhs.edge(1).name() == '_data' {
3558 return sum_payload_ident_name_from_cursor(lhs.edge(0))
3559 }
3560 if rhs.name() == '_data' {
3561 return sum_payload_ident_name_from_cursor(lhs)
3562 }
3563 }
3564 else {}
3565 }
3566
3567 return none
3568}
3569
3570fn (mut g Gen) resolve_sum_payload_arg_type_name_cursor(arg ast.Cursor, candidate_type string) string {
3571 mut type_name := candidate_type.trim_space()
3572 if type_name == '' || type_name == 'int' {
3573 return type_name
3574 }
3575 ident_name := sum_payload_ident_name_from_cursor(arg) or { return type_name }
3576 decl_type :=
3577 (g.get_local_var_c_type(ident_name) or { return type_name }).trim_space().trim_right('*')
3578 if decl_type == '' || type_name == decl_type || type_name == decl_type + '*' {
3579 return type_name
3580 }
3581 mut ptr_suffix := ''
3582 for type_name.ends_with('*') {
3583 ptr_suffix += '*'
3584 type_name = type_name[..type_name.len - 1].trim_space()
3585 }
3586 variants := g.get_sum_type_variants_for(decl_type)
3587 if variants.len == 0 {
3588 return type_name + ptr_suffix
3589 }
3590 mut variant_field := g.sum_type_variant_field_name(decl_type, type_name)
3591 if variant_field !in variants {
3592 for variant in variants {
3593 if sum_type_variant_matches(variant, type_name) {
3594 variant_field = variant
3595 break
3596 }
3597 }
3598 }
3599 if variant_field !in variants {
3600 return type_name + ptr_suffix
3601 }
3602 payload_type := g.sum_type_variant_payload_type(decl_type, type_name, variant_field)
3603 if payload_type == '' {
3604 return type_name + ptr_suffix
3605 }
3606 return payload_type + ptr_suffix
3607}
3608
3609fn (mut g Gen) get_comptime_selector_type_cursor(node ast.Cursor) string {
3610 if node.kind() != .expr_selector {
3611 return ''
3612 }
3613 lhs := node.edge(0)
3614 rhs_name := node.edge(1).name()
3615 if is_comptime_selector_rhs_name(rhs_name) {
3616 return g.comptime_field_type
3617 }
3618 if lhs.kind() == .expr_ident && lhs.name() == g.comptime_field_var {
3619 match rhs_name {
3620 'name' {
3621 return 'string'
3622 }
3623 'typ', 'unaliased_typ' {
3624 return 'int'
3625 }
3626 'attrs' {
3627 return 'Array_string'
3628 }
3629 'is_pub', 'is_mut', 'is_embed', 'is_shared', 'is_atomic', 'is_option', 'is_array',
3630 'is_map', 'is_chan', 'is_enum', 'is_struct', 'is_alias' {
3631 return 'bool'
3632 }
3633 else {}
3634 }
3635 }
3636 if lhs.kind() == .expr_selector && lhs.edge(0).kind() == .expr_ident
3637 && lhs.edge(0).name() == g.comptime_field_var && lhs.edge(1).name() == 'name' {
3638 match rhs_name {
3639 'str' {
3640 return 'char*'
3641 }
3642 'len' {
3643 return 'int'
3644 }
3645 else {}
3646 }
3647 }
3648 return ''
3649}
3650
3651fn (mut g Gen) generic_specialization_arg_type_name_cursor(arg ast.Cursor) string {
3652 mut base_arg := generic_call_base_arg_cursor(arg)
3653 for base_arg.is_valid() && base_arg.kind() == .expr_paren {
3654 base_arg = base_arg.edge(0)
3655 }
3656 if !base_arg.is_valid() {
3657 return ''
3658 }
3659 if base_arg.kind() == .expr_prefix && unsafe { token.Token(int(base_arg.aux())) } == .amp {
3660 inner_type_name := g.generic_specialization_arg_type_name_cursor(base_arg.edge(0))
3661 if inner_type_name != '' && inner_type_name != 'int' {
3662 return inner_type_name.trim_right('*') + '*'
3663 }
3664 }
3665 mut arg_type_name := ''
3666 if base_arg.kind() == .expr_selector {
3667 rhs := base_arg.edge(1)
3668 if is_comptime_selector_rhs_name(rhs.name()) {
3669 comptime_type := g.get_comptime_selector_type_cursor(base_arg).trim_space()
3670 if comptime_type != '' && comptime_type != 'int' {
3671 return comptime_type
3672 }
3673 }
3674 arg_type_name =
3675 g.selector_declared_field_type_cursor_for_generic_scan(base_arg).trim_space()
3676 if arg_type_name == '' || arg_type_name == 'array' {
3677 arg_type_name = g.generic_scan_expr_cursor_c_type(base_arg).trim_space()
3678 }
3679 lhs := base_arg.edge(0)
3680 if lhs.kind() == .expr_ident {
3681 if placeholder := g.cur_fn_generic_params[lhs.name()] {
3682 if concrete := g.active_generic_types[placeholder] {
3683 mut struct_type := g.resolve_type_to_struct(concrete)
3684 if struct_type.fields.len == 0 {
3685 struct_c_name := g.types_type_to_c(concrete).trim_space().trim_right('*')
3686 struct_type = g.lookup_struct_type_by_c_name(struct_c_name)
3687 }
3688 for field in struct_type.fields {
3689 if field.name == rhs.name() {
3690 arg_type_name = g.types_type_to_c(field.typ)
3691 break
3692 }
3693 }
3694 }
3695 }
3696 }
3697 }
3698 if arg_type_name == '' {
3699 if active_concrete := g.active_generic_concrete_from_arg_cursor(base_arg) {
3700 arg_type_name = g.generic_specialization_token_from_type(active_concrete)
3701 }
3702 }
3703 if arg_type_name == '' && base_arg.kind() == .expr_init {
3704 arg_type_name = g.expr_type_to_c(base_arg.edge(0).type_expr()).trim_space()
3705 }
3706 if arg_type_name == '' && base_arg.kind() == .expr_cast {
3707 arg_type_name = g.expr_type_to_c(base_arg.edge(0).type_expr()).trim_space()
3708 }
3709 if arg_type_name == '' || arg_type_name == 'int' {
3710 arg_type_name = g.generic_scan_expr_cursor_c_type(base_arg).trim_space()
3711 }
3712 if (arg_type_name == '' || arg_type_name == 'int') && base_arg.kind() == .expr_ident {
3713 arg_type_name = (g.get_local_var_c_type(base_arg.name()) or { '' }).trim_space()
3714 }
3715 if arg_type_name == '' || arg_type_name == 'int' {
3716 if raw := g.raw_type_from_generic_call_arg_cursor(base_arg) {
3717 arg_type_name = g.types_type_to_c(raw).trim_space()
3718 }
3719 }
3720 return if arg_type_name.trim_space() == 'void*' {
3721 'voidptr'
3722 } else {
3723 g.resolve_sum_payload_arg_type_name_cursor(base_arg, arg_type_name).trim_space()
3724 }
3725}
3726
3727fn (mut g Gen) generic_call_has_concrete_arg_bindings_cursor(decl ast.FnDecl, call ast.Cursor, generic_params []string) bool {
3728 if generic_params.len == 0 || g.env == unsafe { nil } {
3729 return false
3730 }
3731 arg_count := generic_call_cursor_arg_count(call)
3732 mut bindings := map[string]types.Type{}
3733 if decl.is_method && arg_count > 0 && expr_has_generic_placeholder(decl.receiver.typ) {
3734 recv_arg := generic_call_base_arg_cursor(generic_call_cursor_arg(call, 0))
3735 if recv_arg.is_valid() {
3736 if concrete := g.concrete_type_from_generic_call_arg_cursor(recv_arg) {
3737 g.infer_generic_type_bindings_from_param(decl.receiver.typ,
3738 g.concrete_type_with_active_generics(concrete), generic_params, mut bindings)
3739 }
3740 }
3741 }
3742 arg_offset := if decl.is_method && arg_count == decl.typ.params.len + 1 { 1 } else { 0 }
3743 for i, param in decl.typ.params {
3744 arg_idx := i + arg_offset
3745 if arg_idx >= arg_count || !expr_has_generic_placeholder(param.typ) {
3746 continue
3747 }
3748 arg := generic_call_base_arg_cursor(generic_call_cursor_arg(call, arg_idx))
3749 if !arg.is_valid() {
3750 continue
3751 }
3752 concrete := g.concrete_type_from_generic_call_arg_cursor(arg) or { continue }
3753 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
3754 concrete), generic_params, mut bindings)
3755 }
3756 for param_name in generic_params {
3757 concrete := bindings[param_name] or { return false }
3758 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
3759 return false
3760 }
3761 }
3762 return true
3763}
3764
3765fn (mut g Gen) try_specialize_generic_call_name_cursor(name string, call ast.Cursor) ?string {
3766 if name == '' {
3767 return none
3768 }
3769 if candidate := g.same_receiver_specialized_method_name(name) {
3770 return candidate
3771 }
3772 if name == 'copy' {
3773 return none
3774 }
3775 arg_count := generic_call_cursor_arg_count(call)
3776 is_struct_field_should_encode :=
3777 name in ['struct_field_should_encode_T', 'json2__struct_field_should_encode_T', 'struct_field_should_encode', 'json2__struct_field_should_encode']
3778 || name.starts_with('struct_field_should_encode_T_')
3779 || name.starts_with('json2__struct_field_should_encode_T_')
3780 if is_struct_field_should_encode && arg_count > 1 {
3781 arg_type_name :=
3782 g.generic_specialization_arg_type_name_cursor(generic_call_cursor_arg(call, 1))
3783 if arg_type_name != '' {
3784 g.record_late_single_generic_call_spec_from_c_type('struct_field_should_encode', 'T',
3785 arg_type_name)
3786 spec_token := generic_specialization_token_from_c_type_name(arg_type_name)
3787 for base_name in [g.qualify_local_call_name('struct_field_should_encode'),
3788 'json2__struct_field_should_encode', 'struct_field_should_encode'] {
3789 if candidate := g.find_specialized_call_name(base_name, spec_token) {
3790 return candidate
3791 }
3792 g.ensure_specialized_call_signature('${base_name}_T_${spec_token}')
3793 if candidate := g.find_specialized_call_name(base_name, spec_token) {
3794 return candidate
3795 }
3796 }
3797 }
3798 }
3799 is_encode_struct_field_value :=
3800 name in ['Encoder__encode_struct_field_value_T', 'json2__Encoder__encode_struct_field_value_T', 'Encoder__encode_struct_field_value', 'json2__Encoder__encode_struct_field_value']
3801 || name.starts_with('Encoder__encode_struct_field_value_T_')
3802 || name.starts_with('json2__Encoder__encode_struct_field_value_T_')
3803 if is_encode_struct_field_value && arg_count > 1 {
3804 arg_type_name :=
3805 g.generic_specialization_arg_type_name_cursor(generic_call_cursor_arg(call, 1))
3806 if arg_type_name != '' {
3807 g.record_late_single_generic_call_spec_from_c_type('encode_struct_field_value', 'T',
3808 arg_type_name)
3809 spec_token := generic_specialization_token_from_c_type_name(arg_type_name)
3810 for base_name in [
3811 g.qualify_local_call_name('Encoder__encode_struct_field_value'),
3812 'json2__Encoder__encode_struct_field_value',
3813 'Encoder__encode_struct_field_value',
3814 ] {
3815 if candidate := g.find_specialized_call_name(base_name, spec_token) {
3816 return candidate
3817 }
3818 g.ensure_specialized_call_signature('${base_name}_T_${spec_token}')
3819 if candidate := g.find_specialized_call_name(base_name, spec_token) {
3820 return candidate
3821 }
3822 }
3823 }
3824 }
3825 if name.ends_with('_T') {
3826 base_name := generic_call_base_name_for_specialization(name)
3827 if base_name != '' {
3828 mut has_arg_bindings := false
3829 if generic_decl := g.find_generic_fn_decl_by_base_name(base_name) {
3830 has_arg_bindings = g.generic_call_has_concrete_arg_bindings_cursor(generic_decl,
3831 call, g.generic_fn_param_names(generic_decl))
3832 }
3833 cur_fn_specialized_name := if g.cur_fn_c_name != '' {
3834 g.cur_fn_c_name
3835 } else {
3836 g.cur_fn_name
3837 }
3838 if cur_fn_specialized_name.contains('_T_') && !has_arg_bindings {
3839 suffix := cur_fn_specialized_name.all_after('_T_')
3840 if suffix != '' {
3841 candidate := '${base_name}_T_${suffix}'
3842 g.ensure_specialized_call_signature(candidate)
3843 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
3844 return candidate
3845 }
3846 if !base_name.contains('__') {
3847 qualified := g.qualify_local_call_name(candidate)
3848 g.ensure_specialized_call_signature(qualified)
3849 if qualified != candidate
3850 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3851 return qualified
3852 }
3853 }
3854 }
3855 }
3856 if g.active_generic_types.len > 0 && !has_arg_bindings {
3857 if concrete := g.active_generic_types['T'] {
3858 spec_token := g.generic_specialization_token_from_type(concrete)
3859 candidate := '${base_name}_T_${spec_token}'
3860 g.ensure_specialized_call_signature(candidate)
3861 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
3862 return candidate
3863 }
3864 if !base_name.contains('__') {
3865 qualified := g.qualify_local_call_name(candidate)
3866 g.ensure_specialized_call_signature(qualified)
3867 if qualified != candidate
3868 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3869 return qualified
3870 }
3871 }
3872 }
3873 }
3874 if g.active_generic_types.len == 0 {
3875 if candidate := g.find_specialized_call_name(g.qualify_local_call_name(base_name),
3876 'f64')
3877 {
3878 return candidate
3879 }
3880 if candidate := g.find_specialized_call_name(base_name, 'f64') {
3881 return candidate
3882 }
3883 }
3884 }
3885 }
3886 if name in ['decode_value_T', 'json2__decode_value_T'] && arg_count > 1 {
3887 arg_type_name :=
3888 g.generic_specialization_arg_type_name_cursor(generic_call_cursor_arg(call, 1)).trim_space().trim_right('*')
3889 if arg_type_name != '' && arg_type_name != 'int' {
3890 if candidate := g.find_specialized_call_name(g.qualify_local_call_name('decode_value'),
3891 sanitize_generic_token_part(arg_type_name))
3892 {
3893 return candidate
3894 }
3895 }
3896 }
3897 if name in ['Encoder__encode_value_T', 'json2__Encoder__encode_value_T', 'Encoder__encode_value', 'json2__Encoder__encode_value']
3898 && arg_count > 1 {
3899 arg_type_name :=
3900 g.generic_specialization_arg_type_name_cursor(generic_call_cursor_arg(call, 1)).trim_space().trim_right('*')
3901 if arg_type_name != '' && arg_type_name != 'int' {
3902 if candidate := g.find_specialized_call_name('json2__Encoder__encode_value',
3903 sanitize_generic_token_part(arg_type_name))
3904 {
3905 return candidate
3906 }
3907 }
3908 }
3909 if g.active_generic_types.len > 0 && name.contains('_T_') {
3910 base_name := name.all_before('_T_')
3911 mut parts := name.all_after('_T_').split('_')
3912 mut c_parts := parts.clone()
3913 mut changed := false
3914 for i, part in parts {
3915 if concrete := g.active_generic_types[part] {
3916 parts[i] = g.generic_specialization_token_from_type(concrete)
3917 c_type := g.types_type_to_c(concrete).trim_space().trim_right('*')
3918 c_parts[i] = if c_type == '' {
3919 parts[i]
3920 } else {
3921 sanitize_generic_token_part(c_type)
3922 }
3923 changed = true
3924 }
3925 }
3926 if changed {
3927 candidate := '${base_name}_T_${parts.join('_')}'
3928 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
3929 return candidate
3930 }
3931 g.ensure_specialized_call_signature(candidate)
3932 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
3933 return candidate
3934 }
3935 if !base_name.contains('__') {
3936 qualified := g.qualify_local_call_name(candidate)
3937 if qualified != candidate
3938 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3939 return qualified
3940 }
3941 g.ensure_specialized_call_signature(qualified)
3942 if qualified != candidate
3943 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3944 return qualified
3945 }
3946 }
3947 c_candidate := '${base_name}_T_${c_parts.join('_')}'
3948 if c_candidate != candidate
3949 && (c_candidate in g.fn_param_is_ptr || c_candidate in g.fn_return_types) {
3950 return c_candidate
3951 }
3952 if c_candidate != candidate {
3953 g.ensure_specialized_call_signature(c_candidate)
3954 if c_candidate in g.fn_param_is_ptr || c_candidate in g.fn_return_types {
3955 return c_candidate
3956 }
3957 }
3958 if c_candidate != candidate && !base_name.contains('__') {
3959 qualified := g.qualify_local_call_name(c_candidate)
3960 if qualified != c_candidate
3961 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3962 return qualified
3963 }
3964 g.ensure_specialized_call_signature(qualified)
3965 if qualified != c_candidate
3966 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
3967 return qualified
3968 }
3969 }
3970 }
3971 }
3972 if g.active_generic_types.len > 0 {
3973 mut parts := name.split('_')
3974 mut changed := false
3975 for i, part in parts {
3976 if concrete := g.active_generic_types[part] {
3977 parts[i] = 'T_${g.generic_specialization_token_from_type(concrete)}'
3978 changed = true
3979 }
3980 }
3981 if changed {
3982 candidate := parts.join('_')
3983 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
3984 return candidate
3985 }
3986 }
3987 }
3988 if !name.contains('__') {
3989 qualified := g.qualify_local_call_name(name)
3990 if qualified != name && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types)
3991 && !g.has_generic_fn_decl_by_base_name(qualified) {
3992 return none
3993 }
3994 }
3995 mut skip_arg0_specialization := false
3996 arg0_lookup_name := if name.contains('_T_') {
3997 name.all_before('_T_')
3998 } else if name.ends_with('_T') {
3999 name[..name.len - 2]
4000 } else {
4001 name
4002 }
4003 if generic_decl := g.find_generic_fn_decl_by_base_name(arg0_lookup_name) {
4004 skip_arg0_specialization = generic_decl.is_method || generic_decl.typ.params.len == 0
4005 || !expr_has_generic_placeholder(generic_decl.typ.params[0].typ)
4006 }
4007 if arg_count > 0 && !skip_arg0_specialization {
4008 mut arg_type_name :=
4009 g.generic_specialization_arg_type_name_cursor(generic_call_cursor_arg(call, 0))
4010 if arg_type_name != '' && arg_type_name != 'int' && arg_type_name.trim_space() != 'void*' {
4011 arg_type_name = g.resolve_sum_payload_arg_type_name_cursor(generic_call_cursor_arg(call, 0),
4012 arg_type_name)
4013 }
4014 arg_type_name = if arg_type_name.trim_space() == 'void*' {
4015 'voidptr'
4016 } else {
4017 arg_type_name.trim_right('*')
4018 }
4019 if arg_type_name != '' && arg_type_name != 'int' {
4020 mut candidate_types := [arg_type_name]
4021 if !arg_type_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
4022 && g.cur_module != 'builtin' {
4023 candidate_types << '${g.cur_module}__${arg_type_name}'
4024 if arg_type_name.starts_with('Array_') && arg_type_name.len > 'Array_'.len {
4025 elem_type_name := arg_type_name['Array_'.len..]
4026 if !elem_type_name.contains('__') {
4027 candidate_types << 'Array_${g.cur_module}__${elem_type_name}'
4028 }
4029 }
4030 }
4031 for concrete_type_name in candidate_types {
4032 if candidate := g.find_specialized_call_name(name,
4033 sanitize_generic_token_part(concrete_type_name))
4034 {
4035 return candidate
4036 }
4037 }
4038 }
4039 }
4040 if name in ['decode_value', 'json2__decode_value'] && g.cur_fn_ret_type.starts_with('_result_') {
4041 result_base := g.cur_fn_ret_type['_result_'.len..]
4042 if candidate := g.find_specialized_call_name(g.qualify_local_call_name('decode_value'),
4043 sanitize_generic_token_part(result_base))
4044 {
4045 return candidate
4046 }
4047 }
4048 mut generic_base_name := generic_call_base_name_for_specialization(name)
4049 if qualified_name := g.qualified_generic_fn_base_name(generic_base_name) {
4050 generic_base_name = qualified_name
4051 }
4052 decl := g.find_generic_fn_decl_by_base_name(generic_base_name) or {
4053 if generic_base_name.contains('__') {
4054 g.find_generic_fn_decl_by_base_name(generic_base_name.all_after_last('__')) or {
4055 return none
4056 }
4057 } else {
4058 return none
4059 }
4060 }
4061 generic_params := g.generic_fn_param_names(decl)
4062 if generic_params.len == 0 || g.env == unsafe { nil } {
4063 return none
4064 }
4065 if name.contains('_T_') {
4066 if active_bindings := g.active_generic_bindings_matching_name_suffix(name, generic_params) {
4067 mut suffixes := []string{cap: generic_params.len}
4068 for param_name in generic_params {
4069 concrete := active_bindings[param_name] or { return none }
4070 suffixes << g.generic_specialization_token_from_type(concrete)
4071 }
4072 candidate := '${generic_base_name}_T_${suffixes.join('_')}'
4073 g.ensure_specialized_call_signature(candidate)
4074 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
4075 return candidate
4076 }
4077 specialized_candidate := g.specialized_fn_name(decl, active_bindings)
4078 if specialized_candidate != candidate {
4079 g.ensure_specialized