v4 / vlib / v2_toberemoved / gen / cleanc / struct.v
8338 lines · 8070 sloc · 248.44 KB · c0624b274a458fe3e487d89ae9554c2e8c25bdb6
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module cleanc
6
7import 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_call_signature(specialized_candidate)
4080 if specialized_candidate in g.fn_param_is_ptr
4081 || specialized_candidate in g.fn_return_types {
4082 return specialized_candidate
4083 }
4084 }
4085 }
4086 }
4087 has_arg_bindings := g.generic_call_has_concrete_arg_bindings_cursor(decl, call, generic_params)
4088 cur_fn_specialized_name := if g.cur_fn_c_name != '' { g.cur_fn_c_name } else { g.cur_fn_name }
4089 if cur_fn_specialized_name.contains('_T_') && !has_arg_bindings {
4090 suffix := cur_fn_specialized_name.all_after('_T_')
4091 if suffix != '' {
4092 candidate := '${generic_base_name}_T_${suffix}'
4093 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
4094 return candidate
4095 }
4096 if !generic_base_name.contains('__') {
4097 qualified := g.qualify_local_call_name(candidate)
4098 if qualified != candidate
4099 && (qualified in g.fn_param_is_ptr || qualified in g.fn_return_types) {
4100 return qualified
4101 }
4102 }
4103 }
4104 }
4105 if g.active_generic_types.len > 0 && !has_arg_bindings {
4106 mut active_bindings := map[string]types.Type{}
4107 mut suffixes := []string{}
4108 for param_name in generic_params {
4109 concrete := g.active_generic_types[param_name] or { break }
4110 active_bindings[param_name] = concrete
4111 suffixes << g.generic_specialization_token_from_type(concrete)
4112 }
4113 if active_bindings.len == generic_params.len {
4114 candidate := g.specialized_fn_name(decl, active_bindings)
4115 g.ensure_specialized_call_signature(candidate)
4116 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
4117 return candidate
4118 }
4119 alt_candidate := '${generic_base_name}_T_${suffixes.join('_')}'
4120 g.ensure_specialized_call_signature(alt_candidate)
4121 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
4122 return alt_candidate
4123 }
4124 }
4125 }
4126 if g.generic_call_name_has_placeholder_suffix(name) {
4127 if return_bindings := g.infer_generic_bindings_from_current_return(decl, generic_params) {
4128 g.record_late_generic_call_spec(generic_base_name, return_bindings)
4129 candidate := g.specialized_fn_name(decl, return_bindings)
4130 g.ensure_specialized_call_signature(candidate)
4131 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
4132 return candidate
4133 }
4134 mut suffixes := []string{cap: generic_params.len}
4135 for param_name in generic_params {
4136 concrete := return_bindings[param_name] or { return none }
4137 suffixes << g.generic_specialization_token_from_type(concrete)
4138 }
4139 alt_candidate := '${generic_base_name}_T_${suffixes.join('_')}'
4140 g.ensure_specialized_call_signature(alt_candidate)
4141 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
4142 return alt_candidate
4143 }
4144 }
4145 }
4146 if generic_params.len == 1 {
4147 param_name := generic_params[0]
4148 arg_offset := if decl.is_method && arg_count == decl.typ.params.len + 1 { 1 } else { 0 }
4149 for i, param in decl.typ.params {
4150 arg_idx := i + arg_offset
4151 if arg_idx >= arg_count || !expr_has_generic_placeholder(param.typ) {
4152 continue
4153 }
4154 direct_placeholder := direct_generic_placeholder_name(param.typ)
4155 if direct_placeholder != param_name {
4156 continue
4157 }
4158 base_arg := generic_call_base_arg_cursor(generic_call_cursor_arg(call, arg_idx))
4159 mut arg_type_name := g.generic_specialization_arg_type_name_cursor(base_arg)
4160 if base_arg.kind() == .expr_init {
4161 arg_type_name = g.expr_type_to_c(base_arg.edge(0).type_expr()).trim_space()
4162 }
4163 if arg_type_name != '' && arg_type_name != 'int'
4164 && arg_type_name.trim_space() != 'void*' {
4165 arg_type_name = g.resolve_sum_payload_arg_type_name_cursor(base_arg, arg_type_name)
4166 }
4167 arg_type_name = if arg_type_name.trim_space() == 'void*' {
4168 'voidptr'
4169 } else {
4170 arg_type_name.trim_space()
4171 }
4172 if arg_type_name == '' || arg_type_name == 'int' {
4173 continue
4174 }
4175 is_direct_array_param := param.typ is ast.Type && param.typ is ast.ArrayType
4176 mut candidate_types := [arg_type_name]
4177 if !arg_type_name.contains('__') && g.cur_module != '' && g.cur_module != 'main'
4178 && g.cur_module != 'builtin' {
4179 candidate_types << '${g.cur_module}__${arg_type_name}'
4180 if is_direct_array_param && arg_type_name.starts_with('Array_')
4181 && arg_type_name.len > 'Array_'.len {
4182 elem_type_name := arg_type_name['Array_'.len..]
4183 if !elem_type_name.contains('__') {
4184 candidate_types << 'Array_${g.cur_module}__${elem_type_name}'
4185 }
4186 }
4187 }
4188 for concrete_type_name in candidate_types {
4189 spec_token := generic_specialization_token_for_direct_param(param.typ,
4190 concrete_type_name)
4191 if candidate := g.find_specialized_call_name(name, spec_token) {
4192 return candidate
4193 }
4194 if !is_direct_array_param {
4195 continue
4196 }
4197 candidate_name := '${name}_T_${spec_token}'
4198 g.ensure_specialized_call_signature(candidate_name)
4199 if candidate := g.find_specialized_call_name(name, spec_token) {
4200 return candidate
4201 }
4202 if !name.contains('__') {
4203 qualified := g.qualify_local_call_name(candidate_name)
4204 g.ensure_specialized_call_signature(qualified)
4205 if candidate := g.find_specialized_call_name(name, spec_token) {
4206 return candidate
4207 }
4208 }
4209 }
4210 }
4211 }
4212 mut bindings := map[string]types.Type{}
4213 mut receiver_bindings := map[string]types.Type{}
4214 if decl.is_method && arg_count > 0 && expr_has_generic_placeholder(decl.receiver.typ) {
4215 recv_arg := generic_call_base_arg_cursor(generic_call_cursor_arg(call, 0))
4216 if recv_arg.is_valid() {
4217 if concrete := g.concrete_type_from_generic_call_arg_cursor(recv_arg) {
4218 g.infer_generic_type_bindings_from_param(decl.receiver.typ,
4219 g.concrete_type_with_active_generics(concrete),
4220 receiver_generic_param_names(decl), mut receiver_bindings)
4221 }
4222 }
4223 }
4224 arg_offset := if decl.is_method && arg_count == decl.typ.params.len + 1 { 1 } else { 0 }
4225 for i, param in decl.typ.params {
4226 arg_idx := i + arg_offset
4227 if arg_idx >= arg_count || !expr_has_generic_placeholder(param.typ) {
4228 continue
4229 }
4230 arg := generic_call_base_arg_cursor(generic_call_cursor_arg(call, arg_idx))
4231 if !arg.is_valid() {
4232 continue
4233 }
4234 concrete := g.concrete_type_from_generic_call_arg_cursor(arg) or { continue }
4235 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
4236 concrete), generic_params, mut bindings)
4237 }
4238 if bindings.len != generic_params.len {
4239 return none
4240 }
4241 for _, concrete in bindings {
4242 if type_contains_generic_placeholder(concrete) {
4243 return none
4244 }
4245 }
4246 g.record_late_generic_call_spec(generic_base_name, bindings)
4247 mut suffixes := []string{}
4248 for param_name in generic_params {
4249 if concrete := bindings[param_name] {
4250 suffixes << g.generic_specialization_token_from_type(concrete)
4251 }
4252 }
4253 if suffixes.len > 0 {
4254 lookup_name := generic_base_name
4255 if candidate := g.find_specialized_call_name(lookup_name, suffixes.join('_')) {
4256 return candidate
4257 }
4258 suffix := suffixes.join('_')
4259 alt_candidate := '${lookup_name}_T_${suffix}'
4260 g.ensure_specialized_call_signature(alt_candidate)
4261 if alt_candidate in g.fn_param_is_ptr || alt_candidate in g.fn_return_types {
4262 return alt_candidate
4263 }
4264 }
4265 mut combined_bindings := receiver_bindings.clone()
4266 for param_name, concrete in bindings {
4267 combined_bindings[param_name] = concrete
4268 }
4269 candidate := g.specialized_fn_name(decl, combined_bindings)
4270 g.ensure_specialized_call_signature(candidate)
4271 if candidate in g.fn_param_is_ptr || candidate in g.fn_return_types {
4272 return candidate
4273 }
4274 if candidate.contains('__') {
4275 if known_candidate := g.find_known_qualified_fn_name(candidate.all_after_last('__')) {
4276 return known_candidate
4277 }
4278 }
4279 return candidate
4280}
4281
4282fn (mut g Gen) scan_call_cursor_for_generic_fn_specs(call ast.Cursor) {
4283 lhs := call.edge(0)
4284 decl := g.generic_call_decl_from_lhs_cursor(lhs) or { return }
4285 generic_params := g.generic_fn_param_names(decl)
4286 if generic_params.len == 0 {
4287 return
4288 }
4289 mut bindings := map[string]types.Type{}
4290 mut metadata_params := map[string]bool{}
4291 type_args := generic_call_type_args_cursor(lhs)
4292 mut has_unresolved_explicit_type_arg := false
4293 for i, param_name in generic_params {
4294 if i >= type_args.len {
4295 break
4296 }
4297 concrete := g.generic_type_arg_concrete_type(type_args[i]) or {
4298 has_unresolved_explicit_type_arg = true
4299 continue
4300 }
4301 bindings[param_name] = concrete
4302 }
4303 if has_unresolved_explicit_type_arg {
4304 return
4305 }
4306 if type_args.len == 0 {
4307 if active_bindings := g.active_generic_bindings_matching_embedded_suffix_cursor(lhs,
4308 generic_params)
4309 {
4310 for param_name, concrete in active_bindings {
4311 bindings[param_name] = concrete
4312 }
4313 }
4314 }
4315 if type_args.len == 0 && g.active_generic_types.len == 0
4316 && !g.bind_embedded_generic_type_args_cursor(lhs, generic_params, mut bindings) {
4317 return
4318 }
4319 arg_count := if call.edge_count() > 1 { call.edge_count() - 1 } else { 0 }
4320 arg_offset := if decl.is_method && arg_count == decl.typ.params.len + 1 { 1 } else { 0 }
4321 for i, param in decl.typ.params {
4322 arg_idx := i + arg_offset
4323 if arg_idx >= arg_count || !expr_has_generic_placeholder(param.typ) {
4324 continue
4325 }
4326 arg := call.edge(1 + arg_idx)
4327 if is_comptime_field_metadata_expr_cursor(arg, g.comptime_field_var) {
4328 mut seen := map[string]bool{}
4329 mut names := []string{}
4330 collect_generic_placeholder_names_from_expr(param.typ, mut seen, mut names)
4331 for name in names {
4332 metadata_params[name] = true
4333 }
4334 continue
4335 }
4336 concrete := g.concrete_type_from_generic_call_arg_cursor(arg) or { continue }
4337 g.infer_generic_type_bindings_from_param(param.typ, g.concrete_type_for_generic_param(param,
4338 concrete), generic_params, mut bindings)
4339 }
4340 for param_name in generic_params {
4341 if param_name in bindings {
4342 continue
4343 }
4344 if param_name in metadata_params {
4345 continue
4346 }
4347 if concrete := g.active_generic_types[param_name] {
4348 bindings[param_name] = concrete
4349 }
4350 }
4351 if type_args.len == 0 && bindings.len != generic_params.len
4352 && !g.bind_embedded_generic_type_args_cursor(lhs, generic_params, mut bindings) {
4353 raw_name := generic_call_raw_name_cursor(lhs)
4354 if !g.generic_call_name_has_placeholder_suffix(raw_name) {
4355 if specialized_name := g.try_specialize_generic_call_name_cursor(raw_name, call) {
4356 g.record_called_specialized_generic_name(specialized_name)
4357 }
4358 return
4359 }
4360 return_bindings := g.infer_generic_bindings_from_current_return(decl, generic_params) or {
4361 return
4362 }
4363 for param_name, concrete in return_bindings {
4364 if param_name !in bindings {
4365 bindings[param_name] = concrete
4366 }
4367 }
4368 }
4369 if bindings.len != generic_params.len {
4370 return
4371 }
4372 for _, concrete in bindings {
4373 if !g.generic_concrete_type_is_runtime_specializable(concrete) {
4374 return
4375 }
4376 }
4377 short_name := generic_call_short_name_cursor(lhs)
4378 if short_name == '' {
4379 return
4380 }
4381 g.record_late_generic_call_spec(short_name, bindings)
4382 g.record_generic_scan_call_name_cursor(lhs, arg_count, generic_params, bindings)
4383}
4384
4385fn (mut g Gen) scan_type_cursor_for_generic_types(expr ast.Cursor) {
4386 if !expr.is_valid() || !g.type_cursor_may_need_generic_scan(expr) {
4387 return
4388 }
4389 g.scan_expr_for_generic_types(expr.type_expr())
4390}
4391
4392fn (mut g Gen) scan_if_expr_cursor_for_generic_types(expr ast.Cursor) {
4393 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4394 narrow_name, narrow_type := g.generic_scan_narrowing_from_cond_cursor(expr.edge(0))
4395 mut prev_runtime_local_types := g.runtime_local_types.clone()
4396 if narrow_name != '' && narrow_type != '' {
4397 g.remember_runtime_local_type(narrow_name, narrow_type)
4398 }
4399 for i in 2 .. expr.edge_count() {
4400 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4401 }
4402 g.runtime_local_types = prev_runtime_local_types.move()
4403 else_expr := expr.edge(1)
4404 if else_expr.is_valid() && else_expr.kind() != .expr_empty {
4405 g.scan_expr_cursor_for_generic_types(else_expr)
4406 }
4407}
4408
4409fn (mut g Gen) scan_match_expr_cursor_for_generic_types(expr ast.Cursor) {
4410 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4411 for i in 1 .. expr.edge_count() {
4412 branch := expr.edge(i)
4413 conds := branch.list_at(0)
4414 for ci in 0 .. conds.len() {
4415 g.scan_expr_cursor_for_generic_types(conds.at(ci))
4416 }
4417 g.scan_cursor_stmts_for_generic_types(branch.list_at(1))
4418 }
4419}
4420
4421fn (mut g Gen) scan_tuple_expr_cursor_for_generic_types(expr ast.Cursor) {
4422 mut elem_types := []string{cap: expr.edge_count()}
4423 for i in 0 .. expr.edge_count() {
4424 elem := expr.edge(i)
4425 g.scan_expr_cursor_for_generic_types(elem)
4426 mut elem_type := g.generic_scan_expr_cursor_c_type(elem)
4427 if elem_type == '' || elem_type == 'int' {
4428 if raw := g.get_expr_cursor_type_from_env(elem) {
4429 elem_type = raw
4430 }
4431 }
4432 if elem_type == '' {
4433 elem_type = 'int'
4434 }
4435 elem_types << elem_type
4436 }
4437 g.register_tuple_alias(elem_types)
4438}
4439
4440fn (mut g Gen) scan_expr_cursor_for_generic_types(expr ast.Cursor) {
4441 if !expr.is_valid() || !g.cursor_expr_may_need_generic_scan(expr) {
4442 return
4443 }
4444 if expr.kind() == .expr_comptime && expr.edge(0).kind() == .expr_if {
4445 g.scan_comptime_if_cursor_for_generic_types(expr.edge(0))
4446 return
4447 }
4448 match expr.kind() {
4449 .expr_ident {
4450 name := expr.name()
4451 if name.contains('_T_') || name.ends_with('_T') {
4452 g.scan_generic_fn_value_for_specs(ast.Expr(ast.Ident{
4453 name: name
4454 pos: expr.pos()
4455 }))
4456 g.record_generic_struct_bindings_from_specialized_name(name)
4457 }
4458 }
4459 .typ_anon_struct, .typ_array_fixed, .typ_array, .typ_channel, .typ_fn, .typ_generic,
4460 .typ_map, .typ_option, .typ_pointer, .typ_result, .typ_thread, .typ_tuple {
4461 g.scan_type_cursor_for_generic_types(expr)
4462 }
4463 .expr_generic_arg_or_index, .expr_generic_args {
4464 g.scan_generic_fn_value_cursor_for_specs(expr)
4465 g.scan_generic_struct_bindings_cursor(expr)
4466 for arg in generic_call_type_args_cursor(expr) {
4467 g.scan_expr_for_generic_types(arg)
4468 }
4469 }
4470 .expr_call {
4471 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4472 for i in 1 .. expr.edge_count() {
4473 g.scan_expr_cursor_for_generic_types(expr.edge(i))
4474 }
4475 g.scan_call_cursor_for_generic_fn_specs(expr)
4476 }
4477 .expr_call_or_cast {
4478 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4479 g.scan_expr_cursor_for_generic_types(expr.edge(1))
4480 }
4481 .expr_infix {
4482 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4483 g.scan_expr_cursor_for_generic_types(expr.edge(1))
4484 op := unsafe { token.Token(int(expr.aux())) }
4485 if op == .left_shift {
4486 g.remember_array_append_lhs_type_for_generic_scan_cursor(expr.edge(0), expr.edge(1))
4487 }
4488 }
4489 .expr_postfix, .expr_prefix, .expr_paren, .expr_modifier, .expr_lambda, .expr_sql,
4490 .expr_selector, .expr_comptime {
4491 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4492 }
4493 .expr_assoc {
4494 g.scan_type_cursor_for_generic_types(expr.edge(0))
4495 g.scan_expr_cursor_for_generic_types(expr.edge(1))
4496 for i in 2 .. expr.edge_count() {
4497 g.scan_expr_cursor_for_generic_types(expr.edge(i).edge(0))
4498 }
4499 }
4500 .expr_if_guard {
4501 g.scan_cursor_stmt_for_generic_types(expr.edge(0))
4502 }
4503 .expr_or {
4504 g.scan_expr_cursor_for_generic_types(expr.edge(0))
4505 for i in 1 .. expr.edge_count() {
4506 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4507 }
4508 }
4509 .expr_unsafe {
4510 for i in 0 .. expr.edge_count() {
4511 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4512 }
4513 }
4514 .expr_lock {
4515 packed := u32(expr.extra_int())
4516 lock_len := int(packed & 0xFFFF)
4517 rlock_len := int((packed >> 16) & 0xFFFF)
4518 for i in 0 .. (lock_len + rlock_len) {
4519 g.scan_expr_cursor_for_generic_types(expr.edge(i))
4520 }
4521 for i in (lock_len + rlock_len) .. expr.edge_count() {
4522 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4523 }
4524 }
4525 .expr_if {
4526 g.scan_if_expr_cursor_for_generic_types(expr)
4527 }
4528 .expr_match {
4529 g.scan_match_expr_cursor_for_generic_types(expr)
4530 }
4531 .expr_select {
4532 g.scan_cursor_stmt_for_generic_types(expr.edge(0))
4533 g.scan_expr_cursor_for_generic_types(expr.edge(1))
4534 for i in 2 .. expr.edge_count() {
4535 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4536 }
4537 }
4538 .expr_string_inter {
4539 inters := expr.list_at(1)
4540 for i in 0 .. inters.len() {
4541 inter := inters.at(i)
4542 g.scan_expr_cursor_for_generic_types(inter.edge(0))
4543 g.scan_expr_cursor_for_generic_types(inter.edge(1))
4544 }
4545 }
4546 .expr_init {
4547 g.scan_type_cursor_for_generic_types(expr.edge(0))
4548 for i in 1 .. expr.edge_count() {
4549 g.scan_expr_cursor_for_generic_types(expr.edge(i).edge(0))
4550 }
4551 }
4552 .expr_array_init {
4553 g.scan_type_cursor_for_generic_types(expr.edge(0))
4554 for i in 1 .. expr.edge_count() {
4555 g.scan_expr_cursor_for_generic_types(expr.edge(i))
4556 }
4557 }
4558 .expr_map_init {
4559 g.scan_type_cursor_for_generic_types(expr.edge(0))
4560 for i in 1 .. expr.edge_count() {
4561 g.scan_expr_cursor_for_generic_types(expr.edge(i))
4562 }
4563 }
4564 .expr_index, .expr_cast, .expr_as_cast, .expr_range, .expr_keyword_operator {
4565 for i in 0 .. expr.edge_count() {
4566 g.scan_expr_cursor_for_generic_types(expr.edge(i))
4567 }
4568 }
4569 .expr_fn_literal {
4570 captured_len := expr.extra_int()
4571 for i in 0 .. captured_len {
4572 g.scan_expr_cursor_for_generic_types(expr.edge(1 + i))
4573 }
4574 for i in (1 + captured_len) .. expr.edge_count() {
4575 g.scan_cursor_stmt_for_generic_types(expr.edge(i))
4576 }
4577 }
4578 .expr_tuple {
4579 g.scan_tuple_expr_cursor_for_generic_types(expr)
4580 }
4581 else {}
4582 }
4583}
4584
4585fn (mut g Gen) scan_comptime_for_cursor_for_generic_types(node ast.Cursor) bool {
4586 if node.kind() != .stmt_for {
4587 return false
4588 }
4589 for_in := node.edge(0)
4590 if for_in.kind() != .stmt_for_in {
4591 return false
4592 }
4593 iter := for_in.edge(2)
4594 if iter.kind() != .expr_selector || iter.edge(1).name() != 'fields' {
4595 return false
4596 }
4597 type_name := iter.edge(0).name()
4598 concrete := g.active_generic_types[type_name] or { return false }
4599 if concrete !is types.Struct {
4600 return false
4601 }
4602 struct_type := g.comptime_for_struct_type(concrete, concrete as types.Struct)
4603 prev_field_var := g.comptime_field_var
4604 prev_field_name := g.comptime_field_name
4605 prev_field_type := g.comptime_field_type
4606 prev_field_raw_type := g.comptime_field_raw_type
4607 prev_field_attrs := g.comptime_field_attrs
4608 prev_field_idx := g.comptime_field_idx
4609 defer {
4610 g.comptime_field_var = prev_field_var
4611 g.comptime_field_name = prev_field_name
4612 g.comptime_field_type = prev_field_type
4613 g.comptime_field_raw_type = prev_field_raw_type
4614 g.comptime_field_attrs = prev_field_attrs
4615 g.comptime_field_idx = prev_field_idx
4616 }
4617 g.comptime_field_var = for_in.edge(1).name()
4618 for i, field in struct_type.fields {
4619 g.comptime_field_name = field.name
4620 g.comptime_field_type = g.types_type_to_c(field.typ)
4621 g.comptime_field_raw_type = field.typ
4622 g.comptime_field_attrs = g.comptime_field_attribute_strings(struct_type.name, field)
4623 g.comptime_field_idx = i
4624 g.scan_cursor_stmts_for_generic_types(node.for_body_list())
4625 }
4626 return true
4627}
4628
4629// scan_expr_stmts_for_generic_types recurses into IfExpr/MatchExpr/ComptimeExpr bodies.
4630fn (mut g Gen) scan_expr_stmts_for_generic_types(e ast.Expr) {
4631 if e is ast.IfExpr {
4632 g.scan_expr_for_generic_types(e.cond)
4633 narrow_name, narrow_type := g.generic_scan_narrowing_from_cond(e.cond)
4634 mut prev_runtime_local_types := g.runtime_local_types.clone()
4635 if narrow_name != '' && narrow_type != '' {
4636 g.remember_runtime_local_type(narrow_name, narrow_type)
4637 }
4638 g.scan_stmts_for_generic_types(e.stmts)
4639 g.runtime_local_types = prev_runtime_local_types.move()
4640 if e.else_expr !is ast.EmptyExpr {
4641 g.scan_expr_for_generic_types(e.else_expr)
4642 g.scan_expr_stmts_for_generic_types(e.else_expr)
4643 }
4644 } else if e is ast.MatchExpr {
4645 g.scan_expr_for_generic_types(e.expr)
4646 for branch in e.branches {
4647 for cond in branch.cond {
4648 g.scan_expr_for_generic_types(cond)
4649 }
4650 g.scan_stmts_for_generic_types(branch.stmts)
4651 }
4652 } else if e is ast.ComptimeExpr {
4653 if e.expr is ast.IfExpr {
4654 g.scan_comptime_if_for_generic_types(e.expr)
4655 return
4656 }
4657 // $if / $for wrapped in ComptimeExpr
4658 g.scan_expr_for_generic_types(e.expr)
4659 g.scan_expr_stmts_for_generic_types(e.expr)
4660 } else if e is ast.OrExpr {
4661 g.scan_expr_for_generic_types(e.expr)
4662 g.scan_stmts_for_generic_types(e.stmts)
4663 } else if e is ast.UnsafeExpr {
4664 g.scan_stmts_for_generic_types(e.stmts)
4665 }
4666}
4667
4668fn (mut g Gen) scan_comptime_if_cursor_for_generic_types(node ast.Cursor) {
4669 if node.kind() != .expr_if {
4670 return
4671 }
4672 if g.eval_comptime_cond_cursor(node.edge(0)) {
4673 g.scan_if_expr_cursor_body_for_generic_types(node)
4674 return
4675 }
4676 else_expr := node.edge(1)
4677 if else_expr.kind() == .expr_if {
4678 if else_expr.edge(0).kind() == .expr_empty {
4679 g.scan_if_expr_cursor_body_for_generic_types(else_expr)
4680 } else {
4681 g.scan_comptime_if_cursor_for_generic_types(else_expr)
4682 }
4683 return
4684 }
4685 if else_expr.is_valid() && else_expr.kind() != .expr_empty {
4686 g.scan_expr_cursor_for_generic_types(else_expr)
4687 }
4688}
4689
4690fn (mut g Gen) scan_if_expr_cursor_body_for_generic_types(node ast.Cursor) {
4691 for i in 2 .. node.edge_count() {
4692 g.scan_cursor_stmt_for_generic_types(node.edge(i))
4693 }
4694}
4695
4696fn (mut g Gen) generic_scan_narrowing_from_cond(cond ast.Expr) (string, string) {
4697 if cond is ast.InfixExpr && cond.op == .key_is {
4698 name := cond.lhs.name()
4699 if name == '' {
4700 return '', ''
4701 }
4702 c_type := g.expr_type_to_c(cond.rhs).trim_space()
4703 if c_type == '' || c_type == 'int' {
4704 return '', ''
4705 }
4706 return name, c_type
4707 }
4708 return '', ''
4709}
4710
4711fn (mut g Gen) generic_scan_narrowing_from_cond_cursor(cond ast.Cursor) (string, string) {
4712 if !cond.is_valid() || cond.kind() != .expr_infix {
4713 return '', ''
4714 }
4715 op := unsafe { token.Token(int(cond.aux())) }
4716 if op != .key_is {
4717 return '', ''
4718 }
4719 name := generic_wrapped_ident_name_cursor(cond.edge(0))
4720 if name == '' {
4721 return '', ''
4722 }
4723 c_type := g.expr_type_to_c(cond.edge(1).type_expr()).trim_space()
4724 if c_type == '' || c_type == 'int' {
4725 return '', ''
4726 }
4727 return name, c_type
4728}
4729
4730fn generic_scan_type_is_unresolved(typ string) bool {
4731 return typ == '' || typ == 'int' || typ == 'void' || typ == 'array' || typ == 'map'
4732}
4733
4734fn (mut g Gen) generic_scan_expr_c_type(expr ast.Expr) string {
4735 base_expr := if expr is ast.ModifierExpr { expr.expr } else { expr }
4736 if base_expr is ast.StringLiteral || base_expr is ast.StringInterLiteral {
4737 return 'string'
4738 }
4739 if base_expr is ast.Ident {
4740 if local_type := g.get_local_var_c_type(base_expr.name) {
4741 return local_type.trim_space()
4742 }
4743 }
4744 if base_expr is ast.ArrayInitExpr {
4745 array_type := g.expr_type_to_c(base_expr.typ).trim_space()
4746 if !generic_scan_type_is_unresolved(array_type) {
4747 return array_type
4748 }
4749 }
4750 mut typ := g.get_expr_type(base_expr).trim_space()
4751 if generic_scan_type_is_unresolved(typ) {
4752 if raw := g.get_raw_type(base_expr) {
4753 typ = g.types_type_to_c(raw).trim_space()
4754 }
4755 }
4756 return typ
4757}
4758
4759fn (mut g Gen) generic_scan_expr_cursor_c_type(expr ast.Cursor) string {
4760 mut base_expr := expr
4761 for base_expr.is_valid() && base_expr.kind() in [.expr_modifier, .expr_paren] {
4762 base_expr = base_expr.edge(0)
4763 }
4764 if !base_expr.is_valid() {
4765 return ''
4766 }
4767 match base_expr.kind() {
4768 .expr_string, .expr_string_inter {
4769 return 'string'
4770 }
4771 .expr_ident {
4772 if local_type := g.get_local_var_c_type(base_expr.name()) {
4773 return local_type.trim_space()
4774 }
4775 }
4776 .expr_array_init {
4777 array_type := g.expr_type_to_c(base_expr.edge(0).type_expr()).trim_space()
4778 if !generic_scan_type_is_unresolved(array_type) {
4779 return array_type
4780 }
4781 }
4782 .expr_cast {
4783 cast_type := g.expr_type_to_c(base_expr.edge(0).type_expr()).trim_space()
4784 if !generic_scan_type_is_unresolved(cast_type) {
4785 return cast_type
4786 }
4787 }
4788 .expr_as_cast {
4789 cast_type := g.expr_type_to_c(base_expr.edge(1).type_expr()).trim_space()
4790 if !generic_scan_type_is_unresolved(cast_type) {
4791 return cast_type
4792 }
4793 }
4794 else {}
4795 }
4796
4797 if typ := g.get_expr_cursor_type_from_env(base_expr) {
4798 return typ.trim_space()
4799 }
4800 return ''
4801}
4802
4803fn (mut g Gen) remember_assign_cursor_runtime_local_type(lhs ast.Cursor, rhs ast.Cursor) {
4804 if !rhs.is_valid() {
4805 return
4806 }
4807 lhs_name := generic_wrapped_ident_name_cursor(lhs)
4808 if lhs_name == '' {
4809 return
4810 }
4811 mut rhs_type := g.generic_scan_expr_cursor_c_type(rhs).trim_space()
4812 if generic_scan_type_is_unresolved(rhs_type) {
4813 rhs_type = (g.get_local_var_c_type(lhs_name) or { '' }).trim_space()
4814 }
4815 if generic_scan_type_is_unresolved(rhs_type) && rhs.kind() == .expr_array_init {
4816 rhs_type = g.expr_type_to_c(rhs.edge(0).type_expr()).trim_space()
4817 }
4818 if !generic_scan_type_is_unresolved(rhs_type) {
4819 g.remember_runtime_local_type(lhs_name, rhs_type)
4820 } else if rhs.kind() == .expr_array_init {
4821 g.remember_runtime_current_local_type(lhs_name, 'array')
4822 }
4823}
4824
4825fn (mut g Gen) remember_array_append_lhs_type_for_generic_scan(lhs ast.Expr, rhs ast.Expr) {
4826 lhs_name := generic_wrapped_ident_name(lhs)
4827 if lhs_name == '' {
4828 return
4829 }
4830 lhs_type := g.generic_scan_expr_c_type(lhs).trim_space().trim_right('*')
4831 if lhs_type != 'array' && !lhs_type.starts_with('Array_') {
4832 return
4833 }
4834 elem_type := g.generic_scan_expr_c_type(rhs).trim_space().trim_right('*')
4835 if generic_scan_type_is_unresolved(elem_type) {
4836 return
4837 }
4838 g.remember_runtime_local_type(lhs_name, 'Array_' + mangle_alias_component(elem_type))
4839}
4840
4841fn (mut g Gen) remember_array_append_lhs_type_for_generic_scan_cursor(lhs ast.Cursor, rhs ast.Cursor) {
4842 lhs_name := generic_wrapped_ident_name_cursor(lhs)
4843 if lhs_name == '' {
4844 return
4845 }
4846 lhs_type := g.generic_scan_expr_cursor_c_type(lhs).trim_space().trim_right('*')
4847 if lhs_type != 'array' && !lhs_type.starts_with('Array_') {
4848 return
4849 }
4850 elem_type := g.generic_scan_expr_cursor_c_type(rhs).trim_space().trim_right('*')
4851 if generic_scan_type_is_unresolved(elem_type) {
4852 return
4853 }
4854 g.remember_runtime_local_type(lhs_name, 'Array_' + mangle_alias_component(elem_type))
4855}
4856
4857fn (mut g Gen) scan_comptime_if_for_generic_types(node ast.IfExpr) {
4858 if g.eval_comptime_cond(node.cond) {
4859 g.scan_stmts_for_generic_types(node.stmts)
4860 return
4861 }
4862 if node.else_expr is ast.IfExpr {
4863 else_if := node.else_expr as ast.IfExpr
4864 if else_if.cond is ast.EmptyExpr {
4865 g.scan_stmts_for_generic_types(else_if.stmts)
4866 } else {
4867 g.scan_comptime_if_for_generic_types(else_if)
4868 }
4869 return
4870 }
4871 if node.else_expr !is ast.EmptyExpr {
4872 g.scan_expr_for_generic_types(node.else_expr)
4873 g.scan_expr_stmts_for_generic_types(node.else_expr)
4874 }
4875}
4876
4877fn (mut g Gen) struct_is_leaf(node ast.StructDecl) bool {
4878 if node.embedded.len > 0 {
4879 return false
4880 }
4881 env_struct := g.lookup_struct_type(node.name)
4882 if env_struct.fields.len == node.fields.len {
4883 for field in env_struct.fields {
4884 if g.struct_leaf_field_type(field.typ) {
4885 continue
4886 }
4887 return false
4888 }
4889 return true
4890 }
4891 for field in node.fields {
4892 if g.is_pointer_type(field.typ) {
4893 continue
4894 }
4895 match field.typ {
4896 ast.Ident {
4897 if field.typ.name in primitive_types {
4898 continue
4899 }
4900 return false
4901 }
4902 ast.Type {
4903 if field.typ is ast.ArrayFixedType {
4904 if field.typ.elem_type is ast.Ident
4905 && (field.typ.elem_type as ast.Ident).name in primitive_types {
4906 continue
4907 }
4908 }
4909 return false
4910 }
4911 else {
4912 return false
4913 }
4914 }
4915 }
4916 return true
4917}
4918
4919fn (mut g Gen) struct_leaf_field_type(t types.Type) bool {
4920 if !type_has_valid_data(t) {
4921 return false
4922 }
4923 if g.struct_leaf_pointer_type(t) {
4924 return true
4925 }
4926 match t {
4927 types.Primitive, types.Char, types.Rune, types.ISize, types.USize, types.Enum {
4928 return true
4929 }
4930 types.ArrayFixed {
4931 return g.struct_leaf_field_type(t.elem_type)
4932 }
4933 types.Alias {
4934 type_name := g.types_type_to_c(t)
4935 if type_name in primitive_types || type_name == 'bool' {
4936 return true
4937 }
4938 return g.struct_leaf_field_type(t.base_type)
4939 }
4940 else {
4941 return false
4942 }
4943 }
4944}
4945
4946fn (g &Gen) struct_leaf_pointer_type(t types.Type) bool {
4947 if !type_has_valid_data(t) {
4948 return false
4949 }
4950 match t {
4951 types.Pointer {
4952 return true
4953 }
4954 types.Alias {
4955 return g.struct_leaf_pointer_type(t.base_type)
4956 }
4957 else {
4958 return false
4959 }
4960 }
4961}
4962
4963fn (mut g Gen) emit_ready_option_result_structs() bool {
4964 mut emitted_any := false
4965 for _, ret_type in g.fn_return_types {
4966 if ret_type.starts_with('_option_') || ret_type.starts_with('_result_') {
4967 g.register_alias_type(ret_type)
4968 }
4969 }
4970 mut option_names := g.option_aliases.keys()
4971 option_names.sort()
4972 for name in option_names {
4973 if g.should_skip_shadowed_option_result_alias(name) {
4974 continue
4975 }
4976 if name in g.emitted_option_structs {
4977 continue
4978 }
4979 val_type := option_value_type(name)
4980 if !g.option_result_payload_ready(val_type) {
4981 continue
4982 }
4983 payload_type := g.option_result_payload_c_type(val_type)
4984 g.emit_option_result_forward_decl(name, val_type)
4985 g.sb.writeln('struct ${name} { u8 state; IError err; u8 data[sizeof(${payload_type}) > 1 ? sizeof(${payload_type}) : 1]; };')
4986 g.emitted_option_structs[name] = true
4987 emitted_any = true
4988 }
4989 mut result_names := g.result_aliases.keys()
4990 result_names.sort()
4991 for name in result_names {
4992 if g.should_skip_shadowed_option_result_alias(name) {
4993 continue
4994 }
4995 if name in g.emitted_result_structs {
4996 continue
4997 }
4998 val_type := g.result_value_type(name)
4999 if !g.option_result_payload_ready(val_type) {
5000 continue
5001 }
5002 payload_type := g.option_result_payload_c_type(val_type)
5003 g.emit_option_result_forward_decl(name, val_type)
5004 g.sb.writeln('struct ${name} { bool is_error; IError err; u8 data[sizeof(${payload_type}) > 1 ? sizeof(${payload_type}) : 1]; };')
5005 g.emitted_result_structs[name] = true
5006 emitted_any = true
5007 }
5008 return emitted_any
5009}
5010
5011fn (mut g Gen) emit_option_result_structs() {
5012 for g.emit_ready_option_result_structs() {}
5013}
5014
5015fn (mut g Gen) struct_fields_resolved(node ast.StructDecl) bool {
5016 real_generic_params := generic_param_names(node.generic_params)
5017 owner_name := g.get_struct_name(node)
5018 prev_active_generic_types := g.active_generic_types.clone()
5019 mut set_generic_context := false
5020 if real_generic_params.len > 0 && g.active_generic_types.len == 0 {
5021 bindings := g.generic_bindings_for_struct_decl(node) or { return false }
5022 g.active_generic_types = bindings.clone()
5023 set_generic_context = true
5024 }
5025 defer {
5026 if set_generic_context {
5027 g.active_generic_types = prev_active_generic_types.clone()
5028 }
5029 }
5030
5031 // Check embedded types (used by value, not pointer)
5032 for emb in node.embedded {
5033 emb_name := g.field_type_name(emb)
5034 if emb_name != '' && emb_name !in primitive_types {
5035 emb_body_key := 'body_${emb_name}'
5036 emb_enum_key := 'enum_${emb_name}'
5037 emb_alias_key := 'alias_${emb_name}'
5038 if emb_body_key !in g.emitted_types && emb_enum_key !in g.emitted_types
5039 && emb_alias_key !in g.emitted_types {
5040 return false
5041 }
5042 }
5043 }
5044 for field in node.fields {
5045 mut typ_name := if g.active_generic_types.len > 0 {
5046 g.expr_type_to_c(field.typ)
5047 } else {
5048 g.field_type_name(field.typ)
5049 }
5050 typ_name = g.qualify_owner_local_field_type(owner_name, typ_name)
5051 if typ_name == '' {
5052 continue
5053 }
5054 if raw_type := g.get_raw_type(field.typ) {
5055 match raw_type {
5056 types.Interface {
5057 if typ_name !in g.emitted_interface_bodies {
5058 return false
5059 }
5060 continue
5061 }
5062 types.Alias {
5063 if raw_type.base_type is types.Interface
5064 && typ_name !in g.emitted_interface_bodies {
5065 return false
5066 }
5067 }
5068 else {}
5069 }
5070 }
5071 field_typ := field.typ
5072 if field_typ is ast.Type {
5073 if field_typ is ast.ArrayFixedType {
5074 fixed_typ := field_typ as ast.ArrayFixedType
5075 if !g.struct_field_fixed_array_elem_resolved(fixed_typ.elem_type) {
5076 return false
5077 }
5078 continue
5079 }
5080 if field_typ is ast.OptionType {
5081 opt_typ := field_typ as ast.OptionType
5082 mut base_name := if g.active_generic_types.len > 0 {
5083 g.expr_type_to_c(opt_typ.base_type)
5084 } else {
5085 g.field_type_name(opt_typ.base_type)
5086 }
5087 base_name = g.qualify_owner_local_field_type(owner_name, base_name)
5088 if base_name != '' && base_name != 'void'
5089 && !g.option_result_payload_invalid(base_name) {
5090 wrapper_name := '_option_' + g.option_result_payload_alias_component(base_name)
5091 if wrapper_name !in g.emitted_option_structs {
5092 return false
5093 }
5094 }
5095 } else if field_typ is ast.ResultType {
5096 res_typ := field_typ as ast.ResultType
5097 mut base_name := if g.active_generic_types.len > 0 {
5098 g.expr_type_to_c(res_typ.base_type)
5099 } else {
5100 g.field_type_name(res_typ.base_type)
5101 }
5102 base_name = g.qualify_owner_local_field_type(owner_name, base_name)
5103 if base_name != '' && base_name != 'void'
5104 && !g.option_result_payload_invalid(base_name) {
5105 wrapper_name := '_result_' + g.option_result_payload_alias_component(base_name)
5106 if wrapper_name !in g.emitted_result_structs {
5107 return false
5108 }
5109 }
5110 }
5111 }
5112 if typ_name.starts_with('_option_') {
5113 if typ_name !in g.emitted_option_structs {
5114 return false
5115 }
5116 continue
5117 }
5118 if typ_name.starts_with('_result_') {
5119 if typ_name !in g.emitted_result_structs {
5120 return false
5121 }
5122 continue
5123 }
5124 // Pointer types are fine with forward declarations
5125 if g.is_pointer_type(field.typ) {
5126 continue
5127 }
5128 // Primitive types are always resolved
5129 if typ_name in primitive_types {
5130 continue
5131 }
5132 if g.is_c_type_name(typ_name) {
5133 continue
5134 }
5135 if typ_name == 'string' || typ_name == 'builtin__string' {
5136 string_body_key := 'body_string'
5137 builtin_string_body_key := 'body_builtin__string'
5138 if string_body_key !in g.emitted_types && builtin_string_body_key !in g.emitted_types {
5139 return false
5140 }
5141 continue
5142 }
5143 if typ_name.starts_with('Array_fixed_') {
5144 typ_body_key := 'body_${typ_name}'
5145 typ_alias_key := 'alias_${typ_name}'
5146 if typ_body_key !in g.emitted_types && typ_alias_key !in g.emitted_types {
5147 return false
5148 }
5149 continue
5150 }
5151 if typ_name == 'array' || typ_name.starts_with('Array_') || typ_name in g.array_aliases {
5152 if 'body_array' !in g.emitted_types {
5153 return false
5154 }
5155 continue
5156 }
5157 if typ_name == 'map' || typ_name.starts_with('Map_') || typ_name in g.map_aliases {
5158 if 'body_map' !in g.emitted_types {
5159 return false
5160 }
5161 continue
5162 }
5163 // Check if this type's body has been emitted
5164 typ_body_key := 'body_${typ_name}'
5165 typ_enum_key := 'enum_${typ_name}'
5166 typ_alias_key := 'alias_${typ_name}'
5167 if typ_body_key !in g.emitted_types && typ_enum_key !in g.emitted_types
5168 && typ_alias_key !in g.emitted_types {
5169 return false
5170 }
5171 }
5172 return true
5173}
5174
5175fn (mut g Gen) struct_field_fixed_array_elem_resolved(elem_type ast.Expr) bool {
5176 elem_name := if g.active_generic_types.len > 0 {
5177 g.expr_type_to_c(elem_type)
5178 } else {
5179 g.field_type_name(elem_type)
5180 }
5181 if elem_name == '' || elem_name in primitive_types || elem_name == 'bool' || elem_name == 'void'
5182 || elem_name == 'void*' || elem_name == 'voidptr' {
5183 return true
5184 }
5185 if g.is_pointer_type(elem_type) || g.is_c_type_name(elem_name) {
5186 return true
5187 }
5188 if elem_name == 'string' || elem_name == 'builtin__string' {
5189 return 'body_string' in g.emitted_types || 'body_builtin__string' in g.emitted_types
5190 }
5191 if elem_name.starts_with('Array_') || elem_name == 'array' || elem_name in g.array_aliases {
5192 return 'body_array' in g.emitted_types
5193 }
5194 if elem_name.starts_with('Map_') || elem_name == 'map' || elem_name in g.map_aliases {
5195 return 'body_map' in g.emitted_types
5196 }
5197 return 'body_${elem_name}' in g.emitted_types || 'enum_${elem_name}' in g.emitted_types
5198 || 'alias_${elem_name}' in g.emitted_types
5199}
5200
5201fn (mut g Gen) generic_bindings_for_struct_decl(node ast.StructDecl) ?map[string]types.Type {
5202 struct_c_name := g.get_struct_name(node)
5203 if bindings := g.generic_struct_bindings[struct_c_name] {
5204 return bindings.clone()
5205 }
5206 if instances := g.generic_struct_instances[struct_c_name] {
5207 if instances.len > 0 {
5208 return instances[0].bindings.clone()
5209 }
5210 }
5211 return none
5212}
5213
5214fn (mut g Gen) get_struct_name(node ast.StructDecl) string {
5215 if node.name.contains('__') {
5216 return node.name
5217 }
5218 if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
5219 return '${g.cur_module}__${node.name}'
5220 }
5221 return node.name
5222}
5223
5224fn (g &Gen) qualify_owner_local_field_type(owner_type_name string, field_type string) string {
5225 if owner_type_name == '' || !owner_type_name.contains('__') || field_type == '' {
5226 return field_type
5227 }
5228 owner_mod := owner_type_name.all_before('__')
5229 if owner_mod == '' || owner_mod == 'main' || owner_mod == 'builtin' {
5230 return field_type
5231 }
5232 mut base := field_type.trim_space()
5233 mut suffix := ''
5234 for base.ends_with('*') {
5235 base = base[..base.len - 1]
5236 suffix += '*'
5237 }
5238 if base == '' || base.contains('.') || base in primitive_types
5239 || base in ['bool', 'string', 'void*', 'voidptr', 'char*', 'charptr', 'u8*', 'byteptr'] {
5240 return field_type
5241 }
5242 if base.starts_with('Array_') || base.starts_with('Array_fixed_') || base.starts_with('Map_')
5243 || base.starts_with('_option_') || base.starts_with('_result_') {
5244 return field_type
5245 }
5246 mut lookup_name := base
5247 if base.contains('_T_') {
5248 lookup_name = base.all_before('_T_')
5249 }
5250 if lookup_name.contains('__') {
5251 return field_type
5252 }
5253 if scope := g.env_scope(owner_mod) {
5254 if _ := scope.lookup_type(lookup_name) {
5255 return '${owner_mod}__${base}${suffix}'
5256 }
5257 if obj := scope.lookup(lookup_name) {
5258 if obj is types.Type {
5259 return '${owner_mod}__${base}${suffix}'
5260 }
5261 }
5262 }
5263 return field_type
5264}
5265
5266fn (mut g Gen) gen_struct_decl(node ast.StructDecl) {
5267 // Skip C extern struct declarations
5268 if node.language == .c {
5269 return
5270 }
5271 // Generic structs are emitted only from concrete bindings recorded from
5272 // GenericType instantiations.
5273 prev_generic_types := g.active_generic_types.clone()
5274 real_generic_params := generic_param_names(node.generic_params)
5275 if real_generic_params.len > 0 {
5276 g.active_generic_types = g.generic_bindings_for_struct_decl(node) or { return }
5277 }
5278 defer {
5279 g.active_generic_types = prev_generic_types.clone()
5280 }
5281
5282 name := g.get_struct_name(node)
5283 body_key := 'body_${name}'
5284 if body_key in g.emitted_types || body_key in g.pending_late_body_keys {
5285 return
5286 }
5287 if (name.contains('_T_') || name.contains('__T_'))
5288 && g.struct_has_unresolved_generic_value_dependency(node, name) {
5289 g.emit_late_concrete_struct_decl(node, name)
5290 return
5291 }
5292 // For generic structs with active bindings, verify that all field types
5293 // are already emitted. This prevents the last-resort pass from emitting
5294 // a generic struct body before its concrete field types are defined.
5295 if real_generic_params.len > 0 && g.active_generic_types.len > 0 {
5296 if !g.struct_fields_resolved(node)
5297 || g.struct_has_unresolved_generic_value_dependency(node, name) {
5298 return
5299 }
5300 }
5301 g.emitted_types[body_key] = true
5302 keyword := if node.is_union { 'union' } else { 'struct' }
5303 // Try to get the resolved struct type from the Environment
5304 env_struct := g.lookup_struct_type(node.name)
5305 for field in node.fields {
5306 field_type := g.struct_field_type_for_emit(name, field)
5307 if field_type.starts_with('Array_') {
5308 g.emit_array_alias_decl(field_type)
5309 }
5310 if field_type.starts_with('Map_') {
5311 g.emit_map_alias_decl(field_type)
5312 }
5313 field_base_type := field_type.trim_right('*')
5314 if field_base_type.contains('_T_') && !field_base_type.starts_with('Array_')
5315 && !field_base_type.starts_with('Map_') && field_base_type !in g.emitted_types {
5316 g.emitted_types[field_base_type] = true
5317 g.sb.writeln('typedef struct ${field_base_type} ${field_base_type};')
5318 }
5319 }
5320
5321 // Use named struct to match the forward declaration: typedef struct name name;
5322 is_packed := node.attributes.has('_pack')
5323 if is_packed {
5324 g.sb.writeln('#pragma pack(push, 1)')
5325 }
5326 g.sb.writeln('${keyword} ${name} {')
5327 // Embedded structs as fields
5328 for i, emb in node.embedded {
5329 emb_type := g.expr_type_to_c(emb)
5330 emb_field_name := if emb_type.contains('__') {
5331 emb_type.all_after_last('__')
5332 } else {
5333 emb_type
5334 }
5335 g.sb.writeln('\t${emb_type} ${emb_field_name};')
5336 if i < env_struct.embedded.len {
5337 mut embedded := env_struct.embedded[i]
5338 // For unions, the checker stores placeholder Struct objects with empty
5339 // fields. Look up the actual struct type from the environment,
5340 // following alias chains across modules.
5341 if embedded.fields.len == 0 && node.is_union {
5342 if embedded.name != '' {
5343 resolved := g.lookup_union_variant_struct(embedded.name)
5344 if resolved.fields.len > 0 {
5345 embedded = resolved
5346 }
5347 }
5348 if embedded.fields.len == 0 {
5349 // Try with the C type name from the AST
5350 resolved2 := g.lookup_union_variant_struct(emb_type)
5351 if resolved2.fields.len > 0 {
5352 embedded = resolved2
5353 }
5354 }
5355 }
5356 // Collect direct field names to avoid overriding with embedded fields
5357 mut direct_field_names := map[string]bool{}
5358 for field in node.fields {
5359 direct_field_names[field.name] = true
5360 }
5361 for ef in embedded.fields {
5362 if ef.name in direct_field_names {
5363 continue
5364 }
5365 key := name + '.' + ef.name
5366 g.embedded_field_owner[key] = emb_field_name
5367 embedded_field_type := g.types_type_to_c(ef.typ)
5368 g.struct_field_types[key] = embedded_field_type
5369 if name.contains('__') {
5370 short_key := name.all_after_last('__') + '.' + ef.name
5371 g.embedded_field_owner[short_key] = emb_field_name
5372 g.struct_field_types[short_key] = embedded_field_type
5373 }
5374 }
5375 // Recursively register fields from nested embedded structs.
5376 // E.g., if A embeds B and B embeds C with field f, register
5377 // A.f → B_owner.C_owner.f so that a.f generates a.B.C.f in C.
5378 emb_struct_info := g.lookup_struct_type_by_c_name(emb_type)
5379 for sub_emb in emb_struct_info.embedded {
5380 sub_emb_c_name := g.types_type_to_c(types.Type(sub_emb))
5381 sub_emb_field := if sub_emb_c_name.contains('__') {
5382 sub_emb_c_name.all_after_last('__')
5383 } else {
5384 sub_emb_c_name
5385 }
5386 // The embedded copy may have stale (empty) fields if the sub-struct
5387 // was processed after the parent by the checker. Re-lookup live type.
5388 mut live_sub := sub_emb
5389 if sub_emb.fields.len == 0 && sub_emb_c_name != '' {
5390 live_sub = g.lookup_struct_type_by_c_name(sub_emb_c_name)
5391 }
5392 for sf in live_sub.fields {
5393 if sf.name in direct_field_names {
5394 continue
5395 }
5396 sub_key := name + '.' + sf.name
5397 if sub_key !in g.embedded_field_owner {
5398 g.embedded_field_owner[sub_key] = '${emb_field_name}.${sub_emb_field}'
5399 sub_field_type := g.types_type_to_c(sf.typ)
5400 g.struct_field_types[sub_key] = sub_field_type
5401 if name.contains('__') {
5402 short_sub_key := name.all_after_last('__') + '.' + sf.name
5403 if short_sub_key !in g.embedded_field_owner {
5404 g.embedded_field_owner[short_sub_key] = '${emb_field_name}.${sub_emb_field}'
5405 g.struct_field_types[short_sub_key] = sub_field_type
5406 }
5407 }
5408 }
5409 }
5410 }
5411 }
5412 }
5413 // Regular fields
5414 mut has_shared_fields := false
5415 for field in node.fields {
5416 mut field_lookup_type := g.struct_field_type_for_emit(name, field)
5417 if fn_field_type := g.struct_fn_field_lookup_type_for_emit(field) {
5418 field_lookup_type = fn_field_type
5419 }
5420 field_v_name := if node.is_union && field_lookup_type.contains('__')
5421 && (field.name == '' || field.name.contains('__')
5422 || field.name == field_lookup_type
5423 || field.name.all_after_last('__') == field_lookup_type.all_after_last('__')) {
5424 field_lookup_type.all_after_last('__')
5425 } else {
5426 field.name
5427 }
5428 field_name := escape_c_keyword(field_v_name)
5429 field_key := '${name}.${field_v_name}'
5430 g.struct_field_types[field_key] = field_lookup_type
5431 if name.contains('__') {
5432 short_field_key := '${name.all_after_last('__')}.${field_v_name}'
5433 g.struct_field_types[short_field_key] = field_lookup_type
5434 }
5435 if field.typ is ast.Type && field.typ is ast.ArrayFixedType {
5436 fixed_typ := field.typ as ast.ArrayFixedType
5437 elem_type := g.expr_type_to_c(fixed_typ.elem_type)
5438 // Use the resolved array size from the Environment if available
5439 mut resolved_len := -1
5440 for ef in env_struct.fields {
5441 if ef.name == field.name || ef.name == field_v_name {
5442 if ef.typ is types.ArrayFixed {
5443 resolved_len = ef.typ.len
5444 }
5445 break
5446 }
5447 }
5448 g.sb.write_string('\t${elem_type} ${field_name}[')
5449 if resolved_len > 0 {
5450 g.sb.write_string('${resolved_len}')
5451 } else if len_expr := g.const_expr_c_value_for_header(fixed_typ.len) {
5452 g.sb.write_string(len_expr)
5453 } else {
5454 g.expr(fixed_typ.len)
5455 }
5456 g.sb.writeln('];')
5457 continue
5458 }
5459 if field.typ is ast.Type {
5460 if field.typ is ast.FnType {
5461 if decl := g.struct_fn_field_decl_for_emit(field, field_name) {
5462 g.sb.writeln('\t${decl};')
5463 }
5464 continue
5465 }
5466 }
5467 // Check for shared modifier
5468 if field.typ is ast.ModifierExpr && field.typ.kind == .key_shared {
5469 has_shared_fields = true
5470 }
5471 field_type := field_lookup_type
5472 g.sb.writeln('\t${field_type} ${field_name};')
5473 // For union types, register the variant's sub-fields in embedded_field_owner
5474 // so that `box.x` resolves to `box.GgRect.x` via embedded_owner_for().
5475 if node.is_union {
5476 variant_struct := g.lookup_union_variant_struct(field_lookup_type)
5477 for vf in variant_struct.fields {
5478 vf_key := name + '.' + vf.name
5479 g.embedded_field_owner[vf_key] = field_name
5480 vf_type := g.types_type_to_c(vf.typ)
5481 g.struct_field_types[vf_key] = vf_type
5482 if name.contains('__') {
5483 vf_short_key := name.all_after_last('__') + '.' + vf.name
5484 g.embedded_field_owner[vf_short_key] = field_name
5485 g.struct_field_types[vf_short_key] = vf_type
5486 }
5487 }
5488 }
5489 }
5490 // Add mutex field for shared fields
5491 if has_shared_fields {
5492 g.sb.writeln('\tsync__RwMutex mtx;')
5493 }
5494 if node.embedded.len == 0 && node.fields.len == 0 {
5495 g.sb.writeln('\tu8 _dummy;')
5496 }
5497 g.sb.writeln('};')
5498 if is_packed {
5499 g.sb.writeln('#pragma pack(pop)')
5500 }
5501 // Emit fallback str macros for structs without explicit str() methods
5502 struct_str_fn := '${name}__str'
5503 has_explicit_str := g.has_explicit_str_method_for_c_type(name)
5504 if g.cache_bundle_name == 'virtuals' && g.should_emit_current_file() && !has_explicit_str {
5505 label := '${name}{}'
5506 g.late_struct_defs << 'string ${name}__str(${name} v) { return (string){.str = "${label}", .len = ${label.len}, .is_lit = 1}; }\n#define ${name}_str(v) ${name}__str(v)\n'
5507 g.fn_return_types[struct_str_fn] = 'string'
5508 } else if !has_explicit_str && struct_str_fn !in g.fn_return_types {
5509 label := '${name}{}'
5510 g.sb.writeln('#define ${name}__str(v) ((string){.str = "${label}", .len = ${label.len}, .is_lit = 1})')
5511 // Register the macro in fn_return_types so method resolution can find it
5512 g.fn_return_types[struct_str_fn] = 'string'
5513 struct_short_str_fn := '${name}_str'
5514 if struct_short_str_fn !in g.fn_return_types {
5515 g.sb.writeln('#define ${name}_str(v) ${name}__str(v)')
5516 }
5517 }
5518 g.sb.writeln('')
5519 // Generate SoA (Structure of Arrays) companion struct and helpers for @[soa] structs
5520 if env_struct.is_soa && env_struct.fields.len > 0 {
5521 g.gen_soa_companion(name, env_struct)
5522 }
5523 // Emit additional generic struct instantiations (e.g. Node[StructFieldInfo]
5524 // when Node[ValueInfo] was the primary binding).
5525 if node.generic_params.len > 0 {
5526 instances := g.generic_struct_instances[name]
5527 for inst in instances {
5528 if inst.c_name == name {
5529 continue // already emitted as primary
5530 }
5531 inst_body_key := 'body_${inst.c_name}'
5532 if inst_body_key in g.emitted_types || inst_body_key in g.pending_late_body_keys {
5533 continue
5534 }
5535 // Set active generic types to this instantiation's bindings
5536 prev_active := g.active_generic_types.clone()
5537 g.active_generic_types = inst.bindings.clone()
5538 if !g.struct_fields_resolved(node)
5539 || g.struct_has_unresolved_generic_value_dependency(node, inst.c_name) {
5540 g.active_generic_types = prev_active.clone()
5541 g.emit_late_generic_struct(name, inst)
5542 continue
5543 }
5544 g.emitted_types[inst_body_key] = true
5545 // Emit typedef forward declarations for self-references and other
5546 // generic-instance field types (e.g. `next` field in linked-list
5547 // generic structs references the instance itself).
5548 // Note: don't mark these in g.emitted_types — per-bundle workers
5549 // inherit the map and would skip their own pass 1 typedef otherwise.
5550 g.sb.writeln('typedef struct ${inst.c_name} ${inst.c_name};')
5551 mut seen_field_typedef := map[string]bool{}
5552 for field in node.fields {
5553 ft := g.struct_field_type_for_emit(inst.c_name, field)
5554 fbase := ft.trim_right('*')
5555 if ft.starts_with('Array_') {
5556 g.emit_array_alias_decl(ft)
5557 }
5558 if fbase != inst.c_name && fbase.contains('_T_') && !fbase.starts_with('Array_')
5559 && !fbase.starts_with('Map_') && fbase !in seen_field_typedef {
5560 seen_field_typedef[fbase] = true
5561 g.sb.writeln('typedef struct ${fbase} ${fbase};')
5562 }
5563 if ft.starts_with('Map_') {
5564 g.emit_map_alias_decl(ft)
5565 }
5566 }
5567 g.sb.writeln('${keyword} ${inst.c_name} {')
5568 for field in node.fields {
5569 field_type := g.struct_field_type_for_emit(inst.c_name, field)
5570 field_name := if field.name.len > 0 { field.name } else { 'value' }
5571 if decl := g.struct_fn_field_decl_for_emit(field, field_name) {
5572 g.sb.writeln('\t${decl};')
5573 g.struct_field_types['${inst.c_name}.${field_name}'] = g.struct_fn_field_lookup_type_for_emit(field) or {
5574 'void*'
5575 }
5576 continue
5577 }
5578 g.sb.writeln('\t${field_type} ${field_name};')
5579 // Register field types for this instantiation
5580 g.struct_field_types['${inst.c_name}.${field_name}'] = field_type
5581 }
5582 g.struct_field_lookup_cache = map[string]string{}
5583 g.struct_field_lookup_miss = map[string]bool{}
5584 if node.fields.len == 0 {
5585 g.sb.writeln('\tu8 _dummy;')
5586 }
5587 g.sb.writeln('};')
5588 // str macros — only emit fallback if no explicit str() method exists
5589 inst_str_fn := '${inst.c_name}__str'
5590 if inst_str_fn !in g.fn_return_types {
5591 inst_label := '${inst.c_name}{}'
5592 g.sb.writeln('#define ${inst.c_name}__str(v) ((string){.str = "${inst_label}", .len = ${inst_label.len}, .is_lit = 1})')
5593 g.sb.writeln('#define ${inst.c_name}_str(v) ${inst.c_name}__str(v)')
5594 }
5595 g.sb.writeln('')
5596 g.active_generic_types = prev_active.clone()
5597 }
5598 }
5599}
5600
5601fn (mut g Gen) emit_late_concrete_struct_decl(node ast.StructDecl, name string) {
5602 body_key := 'body_${name}'
5603 if body_key in g.emitted_types || body_key in g.pending_late_body_keys {
5604 return
5605 }
5606 g.pending_late_body_keys[body_key] = true
5607 keyword := if node.is_union { 'union' } else { 'struct' }
5608 mut field_types := []string{cap: node.fields.len}
5609 for field in node.fields {
5610 field_type := g.struct_field_type_for_emit(name, field)
5611 field_types << field_type
5612 g.emit_late_generic_struct_dependency_for_type(field_type)
5613 }
5614 mut def := strings.new_builder(256)
5615 def.writeln('typedef ${keyword} ${name} ${name};')
5616 mut seen_field_typedef := map[string]bool{}
5617 for field_type in field_types {
5618 fbase := field_type.trim_right('*')
5619 if fbase != name && fbase.contains('_T_') && !fbase.starts_with('Array_')
5620 && !fbase.starts_with('Map_') && fbase !in seen_field_typedef {
5621 seen_field_typedef[fbase] = true
5622 def.writeln('typedef struct ${fbase} ${fbase};')
5623 }
5624 }
5625 def.writeln('${keyword} ${name} {')
5626 for i, field in node.fields {
5627 field_type := if i < field_types.len {
5628 field_types[i]
5629 } else {
5630 g.struct_field_type_for_emit(name, field)
5631 }
5632 field_name := if field.name.len > 0 { escape_c_keyword(field.name) } else { 'value' }
5633 if decl := g.struct_fn_field_decl_for_emit(field, field_name) {
5634 def.writeln('\t${decl};')
5635 g.struct_field_types['${name}.${field.name}'] = g.struct_fn_field_lookup_type_for_emit(field) or {
5636 'void*'
5637 }
5638 continue
5639 }
5640 def.writeln('\t${field_type} ${field_name};')
5641 g.struct_field_types['${name}.${field.name}'] = field_type
5642 }
5643 if node.fields.len == 0 {
5644 def.writeln('\tu8 _dummy;')
5645 }
5646 def.writeln('};')
5647 label := '${name}{}'
5648 def.writeln('#define ${name}__str(v) ((string){.str = "${label}", .len = ${label.len}, .is_lit = 1})')
5649 def.writeln('#define ${name}_str(v) ${name}__str(v)')
5650 def.writeln('')
5651 g.late_struct_defs << def.str()
5652}
5653
5654fn (mut g Gen) struct_has_unresolved_generic_value_dependency(node ast.StructDecl, owner_name string) bool {
5655 for field in node.fields {
5656 if g.is_pointer_type(field.typ) {
5657 continue
5658 }
5659 field_type := g.struct_field_type_for_emit(owner_name, field)
5660 if g.generic_struct_value_dependency_unresolved(field_type) {
5661 return true
5662 }
5663 }
5664 return false
5665}
5666
5667fn (mut g Gen) struct_fn_field_decl_for_emit(field ast.FieldDecl, field_name string) ?string {
5668 if field.typ is ast.Type {
5669 if field.typ is ast.FnType {
5670 fn_type := field.typ as ast.FnType
5671 if g.fn_type_has_unresolved_generic_placeholder(fn_type) {
5672 return 'void* ${field_name}'
5673 }
5674 decl := c_fn_pointer_type_with_name(g.fn_type_c_type(fn_type), field_name)
5675 if decl != '' {
5676 return decl
5677 }
5678 }
5679 }
5680 return none
5681}
5682
5683fn (mut g Gen) struct_fn_field_lookup_type_for_emit(field ast.FieldDecl) ?string {
5684 if field.typ is ast.Type {
5685 if field.typ is ast.FnType {
5686 fn_type := field.typ as ast.FnType
5687 if g.fn_type_has_unresolved_generic_placeholder(fn_type) {
5688 return 'void*'
5689 }
5690 return g.fn_type_c_type(fn_type)
5691 }
5692 }
5693 return none
5694}
5695
5696fn (mut g Gen) fn_type_has_unresolved_generic_placeholder(fn_type ast.FnType) bool {
5697 for param in fn_type.params {
5698 if g.expr_has_unresolved_generic_placeholder(param.typ) {
5699 return true
5700 }
5701 }
5702 if fn_type.return_type !is ast.EmptyExpr {
5703 return g.expr_has_unresolved_generic_placeholder(fn_type.return_type)
5704 }
5705 return false
5706}
5707
5708fn (mut g Gen) expr_has_unresolved_generic_placeholder(expr ast.Expr) bool {
5709 match expr {
5710 ast.Ident {
5711 return is_generic_placeholder_type_name(expr.name)
5712 && expr.name !in g.active_generic_types
5713 }
5714 ast.PrefixExpr {
5715 return g.expr_has_unresolved_generic_placeholder(expr.expr)
5716 }
5717 ast.ModifierExpr {
5718 return g.expr_has_unresolved_generic_placeholder(expr.expr)
5719 }
5720 ast.GenericArgOrIndexExpr {
5721 return g.expr_has_unresolved_generic_placeholder(expr.lhs)
5722 || g.expr_has_unresolved_generic_placeholder(expr.expr)
5723 }
5724 ast.GenericArgs {
5725 if g.expr_has_unresolved_generic_placeholder(expr.lhs) {
5726 return true
5727 }
5728 for arg in expr.args {
5729 if g.expr_has_unresolved_generic_placeholder(arg) {
5730 return true
5731 }
5732 }
5733 return false
5734 }
5735 ast.Type {
5736 match expr {
5737 ast.ArrayType {
5738 return g.expr_has_unresolved_generic_placeholder(expr.elem_type)
5739 }
5740 ast.ArrayFixedType {
5741 return g.expr_has_unresolved_generic_placeholder(expr.elem_type)
5742 || g.expr_has_unresolved_generic_placeholder(expr.len)
5743 }
5744 ast.MapType {
5745 return g.expr_has_unresolved_generic_placeholder(expr.key_type)
5746 || g.expr_has_unresolved_generic_placeholder(expr.value_type)
5747 }
5748 ast.OptionType {
5749 return g.expr_has_unresolved_generic_placeholder(expr.base_type)
5750 }
5751 ast.ResultType {
5752 return g.expr_has_unresolved_generic_placeholder(expr.base_type)
5753 }
5754 ast.PointerType {
5755 return g.expr_has_unresolved_generic_placeholder(expr.base_type)
5756 }
5757 ast.GenericType {
5758 if g.expr_has_unresolved_generic_placeholder(expr.name) {
5759 return true
5760 }
5761 for param in expr.params {
5762 if g.expr_has_unresolved_generic_placeholder(param) {
5763 return true
5764 }
5765 }
5766 }
5767 ast.FnType {
5768 return g.fn_type_has_unresolved_generic_placeholder(expr)
5769 }
5770 else {}
5771 }
5772
5773 return false
5774 }
5775 else {
5776 return false
5777 }
5778 }
5779}
5780
5781fn (mut g Gen) struct_field_type_for_emit(owner_name string, field ast.FieldDecl) string {
5782 first := g.qualify_owner_local_field_type(owner_name, g.expr_type_to_c(field.typ))
5783 second := g.qualify_owner_local_field_type(owner_name, g.expr_type_to_c(field.typ))
5784 if second != '' && second != first {
5785 return g.canonical_generic_struct_type_for_emit(second)
5786 }
5787 return g.canonical_generic_struct_type_for_emit(first)
5788}
5789
5790fn (mut g Gen) canonical_generic_struct_type_for_emit(type_name string) string {
5791 mut base := type_name.trim_space()
5792 mut suffix := ''
5793 for base.ends_with('*') {
5794 base = base[..base.len - 1].trim_space()
5795 suffix += '*'
5796 }
5797 if base == '' || !base.contains('__T_') {
5798 return type_name
5799 }
5800 canonical := g.canonical_generic_struct_c_name_for_emit(base)
5801 if canonical == base {
5802 return type_name
5803 }
5804 return canonical + suffix
5805}
5806
5807fn (mut g Gen) canonical_generic_struct_c_name_for_emit(c_name string) string {
5808 if c_name == '' || g.generic_struct_c_name_known_for_emit(c_name) {
5809 return c_name
5810 }
5811 if !c_name.contains('__T_') {
5812 return c_name
5813 }
5814 alt := c_name.replace('__T_', '_T_')
5815 if alt != c_name && g.generic_struct_c_name_known_for_emit(alt) {
5816 return alt
5817 }
5818 return c_name
5819}
5820
5821fn (mut g Gen) generic_struct_c_name_known_for_emit(c_name string) bool {
5822 if c_name == '' {
5823 return false
5824 }
5825 if c_name in g.emitted_types || 'body_${c_name}' in g.emitted_types
5826 || 'body_${c_name}' in g.pending_late_body_keys {
5827 return true
5828 }
5829 for _, instances in g.generic_struct_instances {
5830 for inst in instances {
5831 if inst.c_name == c_name {
5832 return true
5833 }
5834 }
5835 }
5836 if _ := g.find_struct_node_by_c_name(c_name) {
5837 return true
5838 }
5839 return false
5840}
5841
5842fn (mut g Gen) generic_struct_value_dependency_unresolved(type_name string) bool {
5843 mut base := g.canonical_generic_struct_type_for_emit(type_name).trim_space()
5844 for base.ends_with('*') {
5845 base = base[..base.len - 1].trim_space()
5846 }
5847 if base == '' || !base.contains('_T_') || base.starts_with('Array_') || base.starts_with('Map_')
5848 || base.starts_with('_option_') || base.starts_with('_result_') {
5849 return false
5850 }
5851 return 'body_${base}' !in g.emitted_types
5852}
5853
5854fn (mut g Gen) gen_sum_type_decl(node ast.TypeDecl) {
5855 name := g.get_type_decl_name(node)
5856 body_key := 'body_${name}'
5857 if body_key in g.emitted_types {
5858 return
5859 }
5860 g.emitted_types[body_key] = true
5861
5862 // Track variant names for sum type cast generation.
5863 mut variant_field_names := []string{cap: node.variants.len}
5864 if raw := g.lookup_type_by_c_name(name) {
5865 if raw is types.SumType {
5866 for variant_type in raw.variants {
5867 variant_c_name := g.types_type_to_c(variant_type)
5868 if variant_c_name != '' && variant_c_name != 'int' {
5869 variant_field_names << '_${variant_c_name}'
5870 }
5871 }
5872 }
5873 }
5874 if variant_field_names.len != node.variants.len {
5875 variant_field_names = []string{cap: node.variants.len}
5876 for i, variant in node.variants {
5877 variant_field_names << g.get_variant_field_name(variant, i)
5878 }
5879 }
5880 mut variant_names := []string{cap: variant_field_names.len}
5881 for vname in variant_field_names {
5882 variant_names << if vname.len > 1 && vname[0] == `_` { vname[1..] } else { vname }
5883 }
5884 g.sum_type_variants[name] = variant_names
5885
5886 g.sb.writeln('struct ${name} {')
5887 g.sb.writeln('\tint _tag;')
5888 g.sb.writeln('\tunion {')
5889 for variant_name in variant_field_names {
5890 g.sb.writeln('\t\tvoid* ${variant_name};')
5891 }
5892 g.sb.writeln('\t} _data;')
5893 g.sb.writeln('};')
5894 g.sb.writeln('')
5895}
5896
5897fn (mut g Gen) get_variant_field_name(variant ast.Expr, idx int) string {
5898 if variant is ast.Ident {
5899 return '_${variant.name}'
5900 } else if variant is ast.SelectorExpr {
5901 if variant.lhs is ast.Ident {
5902 return '_${variant.lhs.name}__${variant.rhs.name}'
5903 }
5904 return '_${variant.rhs.name}'
5905 } else if variant is ast.GenericArgOrIndexExpr {
5906 base := g.expr_type_to_c(variant.lhs)
5907 if base != '' && base != 'int' {
5908 return '_${base}'
5909 }
5910 } else if variant is ast.GenericArgs {
5911 base := g.expr_type_to_c(variant.lhs)
5912 if base != '' && base != 'int' {
5913 return '_${base}'
5914 }
5915 } else if variant is ast.Type {
5916 typ := variant as ast.Type
5917 if typ is ast.GenericType {
5918 base := g.expr_type_to_c(typ.name)
5919 if base != '' && base != 'int' {
5920 return '_${base}'
5921 }
5922 }
5923 if typ is ast.ArrayType {
5924 elem := mangle_alias_component(g.field_type_name(typ.elem_type))
5925 return '_Array_${elem}'
5926 }
5927 if typ is ast.MapType {
5928 key := mangle_alias_component(g.field_type_name(typ.key_type))
5929 val := mangle_alias_component(g.field_type_name(typ.value_type))
5930 return '_Map_${key}_${val}'
5931 }
5932 }
5933 return '_v${idx}'
5934}
5935
5936fn (mut g Gen) infer_sum_variant_from_expr(type_name string, variants []string, expr ast.Expr) SumVariantMatch {
5937 // Handle module constants used as sum variants in selfhosted checker code
5938 // (e.g. char_, string_, void_, nil_, none_).
5939 if expr is ast.Ident {
5940 variant_hint := match expr.name {
5941 'char_' {
5942 'Char'
5943 }
5944 'string_' {
5945 'String'
5946 }
5947 'void_' {
5948 'Void'
5949 }
5950 'nil_' {
5951 'Nil'
5952 }
5953 'none_' {
5954 'None'
5955 }
5956 'rune_' {
5957 'Rune'
5958 }
5959 'usize_' {
5960 'USize'
5961 }
5962 'isize_' {
5963 'ISize'
5964 }
5965 'thread_' {
5966 'Thread'
5967 }
5968 'chan_' {
5969 'Channel'
5970 }
5971 'bool_', 'i8_', 'i16_', 'i32_', 'int_', 'i64_', 'u8_', 'u16_', 'u32_', 'u64_', 'f32_',
5972 'f64_', 'int_literal_', 'float_literal_' {
5973 'Primitive'
5974 }
5975 else {
5976 ''
5977 }
5978 }
5979
5980 if variant_hint != '' {
5981 for i, v in variants {
5982 if sum_type_variant_matches(v, variant_hint) {
5983 return SumVariantMatch{
5984 tag: i
5985 field_name: v
5986 is_primitive: variant_hint == 'Primitive'
5987 inner_type: ''
5988 }
5989 }
5990 }
5991 }
5992 }
5993
5994 // For InitExpr, infer from the struct type name
5995 if expr is ast.InitExpr {
5996 init_type := g.expr_type_to_c(expr.typ)
5997 mut resolved_init_type := init_type
5998 if (resolved_init_type == '' || resolved_init_type == 'int') && expr.typ is ast.Ident {
5999 resolved_init_type = expr.typ.name.replace('.', '__')
6000 }
6001 if resolved_init_type != '' {
6002 init_short := if resolved_init_type.contains('__') {
6003 resolved_init_type.all_after_last('__')
6004 } else {
6005 resolved_init_type
6006 }
6007 for i, v in variants {
6008 if v == init_short || v == resolved_init_type
6009 || resolved_init_type.ends_with('__${v}')
6010 || v.ends_with('__${resolved_init_type}') {
6011 return SumVariantMatch{
6012 tag: i
6013 field_name: v
6014 is_primitive: false
6015 inner_type: resolved_init_type
6016 }
6017 }
6018 }
6019 }
6020 }
6021 // Generic fallback: if the expression is already a concrete AST sum variant
6022 // (e.g. ast.BasicLiteral being cast to ast.Expr), map it by runtime variant name.
6023 expr_variant := expr.type_name()
6024 if expr_variant != '' {
6025 expr_variant_c := expr_variant.replace('.', '__')
6026 expr_variant_short := if expr_variant.contains('.') {
6027 expr_variant.all_after_last('.')
6028 } else if expr_variant.contains('__') {
6029 expr_variant.all_after_last('__')
6030 } else {
6031 expr_variant
6032 }
6033 for i, v in variants {
6034 if sum_type_variant_matches(v, expr_variant_c)
6035 || sum_type_variant_matches(v, expr_variant_short) {
6036 inner_type := if g.is_scalar_sum_payload_type(v)
6037 || v in ['string', 'bool', 'voidptr', 'charptr', 'byteptr'] {
6038 v
6039 } else if type_name.contains('__') {
6040 '${type_name.all_before_last('__')}__${v}'
6041 } else {
6042 v
6043 }
6044 return SumVariantMatch{
6045 tag: i
6046 field_name: v
6047 is_primitive: g.is_scalar_sum_payload_type(v)
6048 inner_type: inner_type
6049 }
6050 }
6051 }
6052 }
6053 return SumVariantMatch{
6054 tag: -1
6055 }
6056}
6057
6058fn (g &Gen) is_scalar_sum_payload_type(type_name string) bool {
6059 return type_name in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'bool', 'rune',
6060 'byte', 'usize', 'isize']
6061}
6062
6063// gen_sum_wrapped_init_field checks if an InitExpr is a variant of the given
6064// sum type and wraps it accordingly. Returns true if wrapping was emitted.
6065fn (mut g Gen) gen_sum_wrapped_init_field(sum_type_name string, init_expr ast.InitExpr) bool {
6066 variants := g.get_sum_type_variants_for(sum_type_name)
6067 if variants.len == 0 {
6068 return false
6069 }
6070 init_type := g.expr_type_to_c(init_expr.typ)
6071 mut resolved := init_type
6072 if (resolved == '' || resolved == 'int') && init_expr.typ is ast.Ident {
6073 resolved = (init_expr.typ as ast.Ident).name.replace('.', '__')
6074 }
6075 if resolved == '' || resolved == sum_type_name {
6076 return false
6077 }
6078 init_short := if resolved.contains('__') { resolved.all_after_last('__') } else { resolved }
6079 for i, v in variants {
6080 if v == init_short || v == resolved || resolved.ends_with('__${v}')
6081 || v.ends_with('__${resolved}') {
6082 g.gen_sum_type_wrap(sum_type_name, v, i, false, ast.Expr(init_expr), resolved)
6083 return true
6084 }
6085 }
6086 return false
6087}
6088
6089fn (mut g Gen) gen_sum_wrapped_init_value_field(sum_type_name string, value ast.Expr) bool {
6090 if g.get_sum_type_variants_for(sum_type_name).len == 0 {
6091 return false
6092 }
6093 mut value_type := g.get_expr_type(value)
6094 if value is ast.Ident {
6095 if local_type := g.get_local_var_c_type(value.name) {
6096 if local_type != '' && local_type != 'int' {
6097 value_type = local_type
6098 }
6099 }
6100 }
6101 value_base := value_type.trim_right('*')
6102 if value_base == '' || value_base == 'int' || value_base == 'void'
6103 || value_base == sum_type_name {
6104 return false
6105 }
6106 g.gen_type_cast_expr(sum_type_name, value)
6107 return true
6108}
6109
6110fn (mut g Gen) gen_sum_type_wrap(type_name string, field_name string, tag int, is_primitive bool, expr ast.Expr, inner_type string) {
6111 _ = is_primitive
6112 g.sb.write_string('((${type_name}){._tag = ${tag}, ._data._${field_name} = ')
6113 resolved_type := g.resolve_sum_payload_storage_type(type_name, field_name, inner_type)
6114 if g.is_scalar_sum_payload_type(resolved_type) {
6115 // Keep scalar payloads encoded in pointer-size space. Smartcast extraction expects this.
6116 g.sb.write_string('((void*)((intptr_t)(')
6117 g.expr(expr)
6118 g.sb.write_string(')))')
6119 } else if inner_type == 'void*' {
6120 // The transformer pre-encodes payloads as void*-cast expressions:
6121 // `(voidptr)(intptr_t)(scalar)` for direct scalar aliases, or
6122 // `(voidptr)memdup(&struct, sizeof(...))` for boxed structs.
6123 // Emit them as-is rather than re-wrapping (double memdup leaks for
6124 // structs and dereferences NULL for scalars whose value is 0).
6125 if g.expr_is_voidptr_cast(expr) {
6126 g.expr(expr)
6127 } else if inner_expr := g.unwrap_addr_of_value_expr(expr) {
6128 // `&fn_call()` lowers to a statement-expression pointer to a temporary.
6129 // Copy by-value from the inner expression to avoid dangling addresses.
6130 g.tmp_counter++
6131 tmp_name := '_st${g.tmp_counter}'
6132 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
6133 g.expr(inner_expr)
6134 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
6135 } else if g.sum_payload_expr_needs_temp(expr, resolved_type) {
6136 // Inner expr is a struct value (e.g. another sum type wrap or InitExpr).
6137 // memdup expects a pointer, so route through a temporary.
6138 g.tmp_counter++
6139 tmp_name := '_st${g.tmp_counter}'
6140 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
6141 g.expr(expr)
6142 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
6143 } else {
6144 g.sb.write_string('((void*)memdup(')
6145 g.expr(expr)
6146 g.sb.write_string(', sizeof(${resolved_type})))')
6147 }
6148 } else {
6149 // Must heap-allocate (memdup) non-primitive sum type variants to avoid
6150 // dangling pointers to local variables that go out of scope.
6151 g.tmp_counter++
6152 tmp_name := '_st${g.tmp_counter}'
6153 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
6154 g.expr(expr)
6155 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
6156 }
6157 g.sb.write_string('})')
6158}
6159
6160fn (mut g Gen) sum_payload_expr_needs_temp(expr ast.Expr, resolved_type string) bool {
6161 unwrapped := strip_expr_wrappers(expr)
6162 return g.expr_yields_struct_value(expr) || g.expr_type_matches_value_type(expr, resolved_type)
6163 || unwrapped is ast.SelectorExpr || unwrapped is ast.CallExpr
6164 || g.sum_payload_expr_code_needs_temp(expr, resolved_type)
6165}
6166
6167fn (mut g Gen) sum_payload_expr_code_needs_temp(expr ast.Expr, resolved_type string) bool {
6168 if resolved_type == '' || resolved_type.ends_with('*') || resolved_type in primitive_types {
6169 return false
6170 }
6171 saved_sb := g.sb
6172 g.sb = strings.new_builder(256)
6173 g.expr(expr)
6174 code := g.sb.str().trim_space()
6175 g.sb = saved_sb
6176 if code == '' || code.starts_with('&') {
6177 return false
6178 }
6179 return code.contains(')->') || code.contains(').')
6180}
6181
6182fn (g &Gen) resolve_sum_payload_storage_type(sum_type_name string, variant_field string, inner_type string) string {
6183 mut resolved_type := inner_type
6184 if resolved_type.ends_with('__f32') {
6185 resolved_type = 'f32'
6186 } else if resolved_type.ends_with('__f64') {
6187 resolved_type = 'f64'
6188 }
6189 if resolved_type == '' || resolved_type == 'void*' || resolved_type == 'int'
6190 || resolved_type == sum_type_name {
6191 return g.sum_type_variant_payload_type(sum_type_name, resolved_type, variant_field)
6192 }
6193 if !g.is_scalar_sum_payload_type(resolved_type)
6194 && resolved_type !in ['string', 'bool', 'f32', 'f64', 'voidptr', 'charptr', 'byteptr', 'void*', 'char*', 'u8*'] {
6195 return g.sum_type_variant_payload_type(sum_type_name, resolved_type, variant_field)
6196 }
6197 return resolved_type
6198}
6199
6200fn (g &Gen) get_sum_type_variants_for(type_name string) []string {
6201 sum_type := g.resolve_sum_type_name(type_name)
6202 if sum_type != '' {
6203 if variants := g.sum_type_variants[sum_type] {
6204 return variants
6205 }
6206 }
6207 lookup_name := if sum_type != '' { sum_type } else { type_name.trim_space().trim_right('*') }
6208 if raw := g.lookup_type_by_c_name_const(lookup_name) {
6209 if raw is types.SumType {
6210 mut variants := []string{cap: raw.variants.len}
6211 for variant in raw.variants {
6212 variants << g.types_type_to_c(variant)
6213 }
6214 return variants
6215 }
6216 }
6217 if lookup_name != type_name {
6218 if raw := g.lookup_type_by_c_name_const(type_name.trim_space().trim_right('*')) {
6219 if raw is types.SumType {
6220 mut variants := []string{cap: raw.variants.len}
6221 for variant in raw.variants {
6222 variants << g.types_type_to_c(variant)
6223 }
6224 return variants
6225 }
6226 }
6227 }
6228 return []string{}
6229}
6230
6231fn (g &Gen) lookup_type_by_c_name_const(c_name string) ?types.Type {
6232 if g.env == unsafe { nil } || c_name == '' {
6233 return none
6234 }
6235 if c_name.contains('__') {
6236 parts := c_name.split('__')
6237 if parts.len == 2 {
6238 mod_name := parts[0]
6239 type_name := parts[1]
6240 if scope := g.env_scope(mod_name) {
6241 if typ := scope.lookup_type(type_name) {
6242 return typ
6243 }
6244 }
6245 }
6246 }
6247 mod_name := if g.cur_module != '' { g.cur_module } else { 'main' }
6248 short_name := if c_name.contains('__') { c_name.all_after_last('__') } else { c_name }
6249 mut modules_to_try := []string{}
6250 if !c_name.contains('__') && mod_name != 'main' && mod_name != 'builtin' {
6251 modules_to_try << 'main'
6252 modules_to_try << 'builtin'
6253 }
6254 modules_to_try << mod_name
6255 if 'main' !in modules_to_try {
6256 modules_to_try << 'main'
6257 }
6258 if 'builtin' !in modules_to_try {
6259 modules_to_try << 'builtin'
6260 }
6261 mut tried := map[string]bool{}
6262 for try_mod in modules_to_try {
6263 if tried[try_mod] {
6264 continue
6265 }
6266 tried[try_mod] = true
6267 if scope := g.env_scope(try_mod) {
6268 if typ := scope.lookup_type(short_name) {
6269 return typ
6270 }
6271 }
6272 }
6273 return none
6274}
6275
6276fn (mut g Gen) sum_type_has_direct_variant(sum_type_name string, target_name string) bool {
6277 variants := g.get_sum_type_variants_for(sum_type_name)
6278 for variant in variants {
6279 if sum_type_variant_matches(variant, target_name) {
6280 return true
6281 }
6282 }
6283 if raw := g.lookup_type_by_c_name(sum_type_name) {
6284 if raw is types.SumType {
6285 for variant_type in raw.variants {
6286 variant_c_name := g.types_type_to_c(variant_type)
6287 if sum_type_variant_matches(variant_c_name, target_name) {
6288 return true
6289 }
6290 }
6291 }
6292 }
6293 return false
6294}
6295
6296fn (g &Gen) resolve_sum_type_name(type_name string) string {
6297 sum_type := type_name.trim_space().trim_right('*')
6298 if sum_type == '' {
6299 return ''
6300 }
6301 if sum_type in g.sum_type_variants {
6302 return sum_type
6303 }
6304 // Composite array/map types are never themselves sum types — never fall
6305 // back to short-name matching, which would erroneously resolve e.g.
6306 // `Array_orm__Primitive` to the `orm__Primitive` sum type because of the
6307 // trailing `Primitive` segment.
6308 if sum_type.starts_with('Array_') || sum_type.starts_with('Map_') {
6309 return ''
6310 }
6311 if !sum_type.contains('__') {
6312 qualified_sum := g.get_qualified_name(sum_type)
6313 if qualified_sum in g.sum_type_variants {
6314 return qualified_sum
6315 }
6316 }
6317 short_sum := if sum_type.contains('__') { sum_type.all_after_last('__') } else { sum_type }
6318 if short_sum in g.sum_type_variants {
6319 return short_sum
6320 }
6321 mut found := ''
6322 for candidate, _ in g.sum_type_variants {
6323 if candidate.all_after_last('__') != short_sum {
6324 continue
6325 }
6326 if found != '' && found != candidate {
6327 return ''
6328 }
6329 found = candidate
6330 }
6331 return found
6332}
6333
6334fn (g &Gen) sum_type_variant_field_name(sum_type_name string, variant_name string) string {
6335 mut resolved_sum_type := g.resolve_sum_type_name(sum_type_name)
6336 if resolved_sum_type == '' {
6337 resolved_sum_type = sum_type_name
6338 }
6339 variants := g.get_sum_type_variants_for(resolved_sum_type)
6340 qualified_variant := g.qualify_sum_type_variant_name(resolved_sum_type, variant_name)
6341 c_variant_name := variant_name.replace('.', '__')
6342 c_qualified_variant := qualified_variant.replace('.', '__')
6343 for variant in variants {
6344 c_variant := variant.replace('.', '__')
6345 if c_variant == c_variant_name || c_variant == c_qualified_variant {
6346 return variant
6347 }
6348 }
6349 for variant in variants {
6350 if sum_type_variant_matches(variant, variant_name)
6351 || sum_type_variant_matches(variant, qualified_variant) {
6352 return variant
6353 }
6354 }
6355 if !variant_name.contains('__') && resolved_sum_type.contains('__')
6356 && !g.is_scalar_sum_payload_type(variant_name)
6357 && variant_name !in ['string', 'bool', 'voidptr', 'charptr', 'byteptr'] {
6358 prefix := resolved_sum_type.all_before_last('__')
6359 if prefix != '' {
6360 return '${prefix}__${variant_name}'
6361 }
6362 }
6363 return variant_name
6364}
6365
6366fn sum_type_variant_match_name(name string) string {
6367 c_name := name.replace('.', '__')
6368 if c_name.starts_with('Array_') || c_name.starts_with('Array_fixed_')
6369 || c_name.starts_with('Map_') || c_name.starts_with('_option_')
6370 || c_name.starts_with('_result_') {
6371 return c_name
6372 }
6373 if c_name.contains('__') {
6374 return c_name.all_after_last('__')
6375 }
6376 return c_name
6377}
6378
6379fn sum_type_variant_matches(variant string, target string) bool {
6380 c_variant := variant.replace('.', '__')
6381 c_target := target.replace('.', '__')
6382 if c_variant == c_target {
6383 return true
6384 }
6385 if c_variant == '' || c_target == '' {
6386 return false
6387 }
6388 if c_variant.contains('__') && c_target.contains('__') {
6389 return false
6390 }
6391 variant_short := sum_type_variant_match_name(c_variant)
6392 target_short := sum_type_variant_match_name(c_target)
6393 return variant_short == target_short || c_target.ends_with('__${variant_short}')
6394}
6395
6396fn (g &Gen) qualify_sum_type_variant_name(sum_type_name string, variant_name string) string {
6397 if variant_name == '' || variant_name.contains('__') || !sum_type_name.contains('__') {
6398 return variant_name
6399 }
6400 prefix := sum_type_name.all_before_last('__')
6401 if prefix == '' {
6402 return variant_name
6403 }
6404 if variant_name.starts_with('Array_') && variant_name.len > 'Array_'.len {
6405 elem := variant_name['Array_'.len..]
6406 if !g.is_scalar_sum_payload_type(elem)
6407 && elem !in ['string', 'bool', 'voidptr', 'charptr', 'byteptr'] {
6408 return 'Array_${prefix}__${elem}'
6409 }
6410 }
6411 if variant_name.starts_with('Map_') && variant_name.len > 'Map_'.len {
6412 rest := variant_name['Map_'.len..]
6413 split := rest.index_u8(`_`)
6414 if split >= 0 {
6415 key := rest[..split]
6416 value := rest[split + 1..]
6417 if value != '' && !value.contains('__') && !g.is_scalar_sum_payload_type(value)
6418 && value !in ['string', 'bool', 'voidptr', 'charptr', 'byteptr'] {
6419 return 'Map_${key}_${prefix}__${value}'
6420 }
6421 }
6422 }
6423 return '${prefix}__${variant_name}'
6424}
6425
6426fn (g &Gen) sum_type_variant_payload_type(sum_type_name string, target_type string, variant_field string) string {
6427 if variant_field == '' {
6428 return target_type
6429 }
6430 resolved_sum_type := g.resolve_sum_type_name(sum_type_name)
6431 sum_type := if resolved_sum_type != '' { resolved_sum_type } else { sum_type_name }
6432 if g.is_scalar_sum_payload_type(variant_field)
6433 || variant_field in ['string', 'bool', 'f32', 'f64', 'voidptr', 'charptr', 'byteptr'] {
6434 return variant_field
6435 }
6436 if variant_field.starts_with('Array_') || variant_field.starts_with('Array_fixed_')
6437 || variant_field.starts_with('Map_') || variant_field.starts_with('_option_')
6438 || variant_field.starts_with('_result_') || variant_field.contains('__') {
6439 return variant_field
6440 }
6441 if target_type != '' && target_type != 'int' && target_type != 'void*'
6442 && target_type != sum_type && target_type.contains('__') {
6443 return target_type
6444 }
6445 if sum_type.contains('__') {
6446 prefix := sum_type.all_before_last('__')
6447 if prefix != '' {
6448 return '${prefix}__${variant_field}'
6449 }
6450 }
6451 return variant_field
6452}
6453
6454struct SumVariantTagStep {
6455 tag int
6456 variant_field string
6457 payload_type string
6458}
6459
6460fn (mut g Gen) sum_variant_tag_path(sum_type_name string, target_name string, seen []string) ?[]SumVariantTagStep {
6461 sum_type := g.resolve_sum_type_name_for_common_field(sum_type_name)
6462 if sum_type == '' || sum_type in seen {
6463 return none
6464 }
6465 variants := g.get_sum_type_variants_for(sum_type)
6466 if variants.len == 0 {
6467 return none
6468 }
6469 for i, variant in variants {
6470 if sum_type_variant_matches(variant, target_name) {
6471 return [
6472 SumVariantTagStep{
6473 tag: i
6474 variant_field: variant
6475 payload_type: g.sum_type_variant_payload_type(sum_type, variant, variant)
6476 },
6477 ]
6478 }
6479 }
6480 mut next_seen := seen.clone()
6481 next_seen << sum_type
6482 for i, variant in variants {
6483 payload_type := g.sum_type_variant_payload_type(sum_type, variant, variant)
6484 nested_sum_type := g.resolve_sum_type_name_for_common_field(payload_type)
6485 if nested_sum_type == '' {
6486 continue
6487 }
6488 mut child_path := g.sum_variant_tag_path(nested_sum_type, target_name, next_seen) or {
6489 continue
6490 }
6491 mut path := []SumVariantTagStep{cap: child_path.len + 1}
6492 path << SumVariantTagStep{
6493 tag: i
6494 variant_field: variant
6495 payload_type: nested_sum_type
6496 }
6497 path << child_path
6498 return path
6499 }
6500 return none
6501}
6502
6503fn (mut g Gen) sum_data_variant_selector_field(sel ast.SelectorExpr) ?string {
6504 if !sel.rhs.name.starts_with('_') || sel.lhs !is ast.SelectorExpr {
6505 return none
6506 }
6507 lhs_sel := sel.lhs as ast.SelectorExpr
6508 if lhs_sel.rhs.name != '_data' {
6509 return none
6510 }
6511 mut sum_type := ''
6512 if raw_type := g.get_raw_type(lhs_sel.lhs) {
6513 match raw_type {
6514 types.SumType {
6515 sum_type = g.types_type_to_c(raw_type)
6516 }
6517 types.Pointer {
6518 if raw_type.base_type is types.SumType {
6519 sum_type = g.types_type_to_c(raw_type.base_type)
6520 }
6521 }
6522 types.Alias {
6523 if raw_type.base_type is types.SumType {
6524 sum_type = g.types_type_to_c(raw_type.base_type)
6525 }
6526 }
6527 else {}
6528 }
6529 }
6530 if sum_type == '' {
6531 sum_type = g.get_expr_type(lhs_sel.lhs).trim_space().trim_right('*')
6532 }
6533 if g.get_sum_type_variants_for(sum_type).len == 0 {
6534 return none
6535 }
6536 raw_variant := sel.rhs.name[1..]
6537 return '_${g.sum_type_variant_field_name(sum_type, raw_variant)}'
6538}
6539
6540fn (g &Gen) is_sum_payload_expr(node ast.Expr, variant string) bool {
6541 match node {
6542 ast.SelectorExpr {
6543 return node.rhs.name == variant || node.rhs.name == '_${variant}'
6544 }
6545 ast.CastExpr {
6546 return g.is_sum_payload_expr(node.expr, variant)
6547 }
6548 ast.ParenExpr {
6549 return g.is_sum_payload_expr(node.expr, variant)
6550 }
6551 ast.PrefixExpr {
6552 return g.is_sum_payload_expr(node.expr, variant)
6553 }
6554 ast.ModifierExpr {
6555 return g.is_sum_payload_expr(node.expr, variant)
6556 }
6557 else {}
6558 }
6559
6560 return false
6561}
6562
6563fn (mut g Gen) gen_sum_variant_field_selector(node ast.SelectorExpr) bool {
6564 if node.rhs.name.starts_with('_') {
6565 return false
6566 }
6567 // If LHS is an as-cast, the cast already handles narrowing - skip double unwrap
6568 if node.lhs is ast.AsCastExpr {
6569 return false
6570 }
6571 mut lhs_sum_type := ''
6572 if node.lhs is ast.SelectorExpr {
6573 declared_lhs := g.selector_declared_field_type(node.lhs).trim_space().trim_right('*')
6574 if declared_lhs != '' && g.get_sum_type_variants_for(declared_lhs).len > 0 {
6575 lhs_sum_type = declared_lhs
6576 }
6577 }
6578 if raw_lhs := g.get_raw_type(node.lhs) {
6579 match raw_lhs {
6580 types.SumType {
6581 if lhs_sum_type == '' {
6582 lhs_sum_type = g.types_type_to_c(raw_lhs)
6583 }
6584 }
6585 types.Pointer {
6586 if lhs_sum_type == '' && raw_lhs.base_type is types.SumType {
6587 lhs_sum_type = g.types_type_to_c(raw_lhs.base_type)
6588 }
6589 }
6590 types.Alias {
6591 if lhs_sum_type == '' && raw_lhs.base_type is types.SumType {
6592 lhs_sum_type = g.types_type_to_c(raw_lhs.base_type)
6593 }
6594 }
6595 else {}
6596 }
6597 }
6598 if lhs_sum_type == '' {
6599 lhs_sum_type = g.get_expr_type(node.lhs)
6600 }
6601 mut variants := []string{}
6602 if vs := g.sum_type_variants[lhs_sum_type] {
6603 variants = vs.clone()
6604 } else if lhs_sum_type.contains('__') {
6605 short_sum := lhs_sum_type.all_after_last('__')
6606 if vs := g.sum_type_variants[short_sum] {
6607 variants = vs.clone()
6608 }
6609 } else {
6610 qualified_sum := g.get_qualified_name(lhs_sum_type)
6611 if vs := g.sum_type_variants[qualified_sum] {
6612 variants = vs.clone()
6613 lhs_sum_type = qualified_sum
6614 }
6615 }
6616 if variants.len == 0 {
6617 return false
6618 }
6619 mut matched_full := ''
6620 mut matched_field := ''
6621 for variant in variants {
6622 variant_short := sum_type_variant_match_name(variant)
6623 mut variant_full := variant
6624 if !variant_full.contains('__') && lhs_sum_type.contains('__')
6625 && !g.is_scalar_sum_payload_type(variant_short)
6626 && variant_short !in ['string', 'bool', 'voidptr', 'charptr', 'byteptr'] {
6627 prefix := lhs_sum_type.all_before_last('__')
6628 if prefix != '' {
6629 variant_full = '${prefix}__${variant_short}'
6630 }
6631 }
6632 key_full := '${variant_full}.${node.rhs.name}'
6633 key_short := '${variant_short}.${node.rhs.name}'
6634 has_field := key_full in g.struct_field_types || key_short in g.struct_field_types
6635 || g.lookup_struct_field_type_by_name(variant_full, node.rhs.name) != none
6636 || g.lookup_struct_field_type_by_name(variant_short, node.rhs.name) != none
6637 if has_field {
6638 if matched_full != '' && matched_full != variant_full {
6639 // Ambiguous field across multiple variants.
6640 return false
6641 }
6642 matched_full = variant_full
6643 matched_field = variant
6644 }
6645 }
6646 if matched_full == '' {
6647 return false
6648 }
6649 field_name := escape_c_keyword(node.rhs.name)
6650 owner := g.embedded_owner_for(matched_full, node.rhs.name)
6651 sep := if g.expr_is_pointer(node.lhs) { '->' } else { '.' }
6652 g.sb.write_string('(((${matched_full}*)(((')
6653 g.expr(node.lhs)
6654 g.sb.write_string(')${sep}_data._${matched_field})))')
6655 if owner != '' {
6656 g.sb.write_string('->${escape_c_keyword(owner)}.${field_name}')
6657 } else {
6658 g.sb.write_string('->${field_name}')
6659 }
6660 g.sb.write_string(')')
6661 return true
6662}
6663
6664struct SumCommonFieldVariant {
6665 variant_field string
6666 payload_type string
6667 field_type string
6668 owner string
6669 nested_sum_type string
6670}
6671
6672struct SumCommonFieldSelectorTarget {
6673 sum_type string
6674 field_name string
6675 sum_expr string
6676 is_ptr bool
6677 infos []SumCommonFieldVariant
6678}
6679
6680struct SumCommonFieldAddressTarget {
6681 sum_type string
6682 field_name string
6683 sum_expr string
6684 is_ptr bool
6685 infos []SumCommonFieldVariant
6686 tail []string
6687 value_type string
6688}
6689
6690fn (mut g Gen) resolve_sum_type_name_for_common_field(type_name string) string {
6691 return g.resolve_sum_type_name(type_name)
6692}
6693
6694fn (mut g Gen) sum_common_field_infos(sum_type_name string, field_name string, seen []string) ?[]SumCommonFieldVariant {
6695 sum_type := g.resolve_sum_type_name_for_common_field(sum_type_name)
6696 if sum_type == '' || sum_type in seen {
6697 return none
6698 }
6699 variants := g.get_sum_type_variants_for(sum_type)
6700 if variants.len == 0 {
6701 return none
6702 }
6703 mut next_seen := seen.clone()
6704 next_seen << sum_type
6705 mut infos := []SumCommonFieldVariant{cap: variants.len}
6706 mut common_field_type := ''
6707 for variant in variants {
6708 info := g.sum_common_field_info_for_variant(sum_type, variant, field_name, next_seen) or {
6709 return none
6710 }
6711 if common_field_type == '' {
6712 common_field_type = info.field_type
6713 } else if common_field_type != info.field_type {
6714 return none
6715 }
6716 infos << info
6717 }
6718 return infos
6719}
6720
6721fn (mut g Gen) sum_common_field_info_for_variant(sum_type_name string, variant string, field_name string, seen []string) ?SumCommonFieldVariant {
6722 payload_type := g.sum_type_variant_payload_type(sum_type_name, variant, variant)
6723 if payload_type == '' || g.is_scalar_sum_payload_type(payload_type) {
6724 return none
6725 }
6726 if field_type := g.lookup_struct_field_type_by_name(payload_type, field_name) {
6727 return SumCommonFieldVariant{
6728 variant_field: variant
6729 payload_type: payload_type
6730 field_type: field_type
6731 owner: g.embedded_owner_for(payload_type, field_name)
6732 }
6733 }
6734 nested_sum_type := g.resolve_sum_type_name_for_common_field(payload_type)
6735 if nested_sum_type != '' {
6736 nested_infos := g.sum_common_field_infos(nested_sum_type, field_name, seen) or {
6737 return none
6738 }
6739 if nested_infos.len == 0 {
6740 return none
6741 }
6742 return SumCommonFieldVariant{
6743 variant_field: variant
6744 payload_type: nested_sum_type
6745 field_type: nested_infos[0].field_type
6746 nested_sum_type: nested_sum_type
6747 }
6748 }
6749 return none
6750}
6751
6752fn (mut g Gen) sum_common_field_selector_target(node ast.SelectorExpr) ?SumCommonFieldSelectorTarget {
6753 if node.rhs.name.starts_with('_') {
6754 return none
6755 }
6756 if node.lhs is ast.AsCastExpr {
6757 return none
6758 }
6759 mut lhs_sum_type := ''
6760 if local_type := g.local_var_c_type_for_expr(node.lhs) {
6761 lhs_sum_type = g.resolve_sum_type_name_for_common_field(local_type)
6762 if lhs_sum_type == '' {
6763 return none
6764 }
6765 } else {
6766 if raw_lhs := g.get_raw_type(node.lhs) {
6767 match raw_lhs {
6768 types.SumType {
6769 lhs_sum_type = g.types_type_to_c(raw_lhs)
6770 }
6771 types.Pointer {
6772 if raw_lhs.base_type is types.SumType {
6773 lhs_sum_type = g.types_type_to_c(raw_lhs.base_type)
6774 }
6775 }
6776 types.Alias {
6777 if raw_lhs.base_type is types.SumType {
6778 lhs_sum_type = g.types_type_to_c(raw_lhs.base_type)
6779 }
6780 }
6781 else {}
6782 }
6783 }
6784 if lhs_sum_type == '' {
6785 lhs_sum_type = g.get_expr_type(node.lhs)
6786 }
6787 }
6788 if lhs_sum_type == '' {
6789 return none
6790 }
6791 lhs_sum_type = g.resolve_sum_type_name_for_common_field(lhs_sum_type)
6792 if lhs_sum_type == '' {
6793 return none
6794 }
6795 infos := g.sum_common_field_infos(lhs_sum_type, node.rhs.name, []string{}) or { return none }
6796 if infos.len == 0 {
6797 return none
6798 }
6799 lhs_code := g.expr_to_string(node.lhs)
6800 return SumCommonFieldSelectorTarget{
6801 sum_type: lhs_sum_type
6802 field_name: node.rhs.name
6803 sum_expr: lhs_code
6804 is_ptr: g.expr_is_pointer(node.lhs)
6805 infos: infos
6806 }
6807}
6808
6809fn (mut g Gen) gen_sum_common_field_selector(node ast.SelectorExpr) bool {
6810 target := g.sum_common_field_selector_target(node) or { return false }
6811 g.emit_sum_common_field_expr(target.sum_type, target.field_name, target.sum_expr,
6812 target.is_ptr, target.infos)
6813 return true
6814}
6815
6816fn (mut g Gen) emit_sum_common_field_expr(sum_type string, field_name string, sum_expr string, is_ptr bool, infos []SumCommonFieldVariant) {
6817 if infos.len == 0 {
6818 g.sb.write_string(zero_value_for_type('int'))
6819 return
6820 }
6821 field_type := infos[0].field_type
6822 g.tmp_counter++
6823 sum_tmp := '_sum_cf${g.tmp_counter}'
6824 value_tmp := '_field_cf${g.tmp_counter}'
6825 sum_decl_type := if is_ptr { '${sum_type}*' } else { sum_type }
6826 sep := if is_ptr { '->' } else { '.' }
6827 g.sb.write_string('({ ${sum_decl_type} ${sum_tmp} = ${sum_expr}; ${field_type} ${value_tmp} = ${zero_value_for_type(field_type)}; switch (${sum_tmp}${sep}_tag) {')
6828 for i, info in infos {
6829 g.sb.write_string('case ${i}: ${value_tmp} = ')
6830 g.emit_sum_common_field_variant_expr(info, field_name, sum_tmp, sep)
6831 g.sb.write_string('; break;')
6832 }
6833 g.sb.write_string('default: break;} ${value_tmp}; })')
6834}
6835
6836fn (mut g Gen) gen_sum_common_field_assign(lhs ast.SelectorExpr, rhs ast.Expr) bool {
6837 target := g.sum_common_field_selector_target(lhs) or { return false }
6838 g.emit_sum_common_field_assign(target.sum_type, target.field_name, target.sum_expr,
6839 target.is_ptr, target.infos, rhs)
6840 return true
6841}
6842
6843fn (mut g Gen) emit_sum_common_field_assign(sum_type string, field_name string, sum_expr string, is_ptr bool, infos []SumCommonFieldVariant, rhs ast.Expr) {
6844 field_type := infos[0].field_type
6845 g.tmp_counter++
6846 sum_tmp := '_sum_cf${g.tmp_counter}'
6847 value_tmp := '_field_cf${g.tmp_counter}'
6848 sum_decl_type := if is_ptr { '${sum_type}*' } else { sum_type }
6849 sep := if is_ptr { '->' } else { '.' }
6850 g.write_indent()
6851 g.sb.write_string('{ ${sum_decl_type} ${sum_tmp} = ${sum_expr}; ${field_type} ${value_tmp} = ')
6852 g.expr(rhs)
6853 g.sb.write_string('; switch (${sum_tmp}${sep}_tag) {')
6854 for i, info in infos {
6855 g.sb.write_string('case ${i}: ')
6856 g.emit_sum_common_field_variant_expr(info, field_name, sum_tmp, sep)
6857 g.sb.write_string(' = ${value_tmp}; break;')
6858 }
6859 g.sb.writeln('default: break;} }')
6860}
6861
6862fn (mut g Gen) emit_sum_common_field_variant_expr(info SumCommonFieldVariant, field_name string, sum_tmp string, sep string) {
6863 payload_expr := '(((${info.payload_type}*)(${sum_tmp}${sep}_data._${info.variant_field})))'
6864 if info.nested_sum_type != '' {
6865 nested_infos := g.sum_common_field_infos(info.nested_sum_type, field_name, []string{}) or {
6866 g.sb.write_string(zero_value_for_type(info.field_type))
6867 return
6868 }
6869 g.emit_sum_common_field_expr(info.nested_sum_type, field_name, payload_expr, true,
6870 nested_infos)
6871 return
6872 }
6873 field_path := sum_common_field_path(info, field_name, [])
6874 g.sb.write_string('${payload_expr}->${field_path}')
6875}
6876
6877fn (mut g Gen) gen_sum_common_field_selector_addr(node ast.SelectorExpr) bool {
6878 target := g.sum_common_field_address_target(node) or { return false }
6879 g.emit_sum_common_field_ptr_expr(target.sum_type, target.field_name, target.sum_expr,
6880 target.is_ptr, target.infos, target.tail, target.value_type)
6881 return true
6882}
6883
6884fn (mut g Gen) sum_common_field_address_target(node ast.SelectorExpr) ?SumCommonFieldAddressTarget {
6885 mut tail := []string{}
6886 mut current := ast.Expr(node)
6887 for {
6888 if current is ast.SelectorExpr {
6889 if target := g.sum_common_field_selector_target(current) {
6890 mut value_type := g.selector_field_type(node)
6891 if value_type == '' {
6892 value_type = g.get_expr_type(ast.Expr(node))
6893 }
6894 if value_type == '' && tail.len == 0 && target.infos.len > 0 {
6895 value_type = target.infos[0].field_type
6896 }
6897 if value_type == '' {
6898 return none
6899 }
6900 return SumCommonFieldAddressTarget{
6901 sum_type: target.sum_type
6902 field_name: target.field_name
6903 sum_expr: target.sum_expr
6904 is_ptr: target.is_ptr
6905 infos: target.infos
6906 tail: tail
6907 value_type: value_type
6908 }
6909 }
6910 tail.insert(0, current.rhs.name)
6911 current = unwrap_sum_common_field_address_base(current.lhs)
6912 continue
6913 }
6914 return none
6915 }
6916 return none
6917}
6918
6919fn unwrap_sum_common_field_address_base(expr ast.Expr) ast.Expr {
6920 return match expr {
6921 ast.ParenExpr {
6922 unwrap_sum_common_field_address_base(expr.expr)
6923 }
6924 ast.ModifierExpr {
6925 unwrap_sum_common_field_address_base(expr.expr)
6926 }
6927 ast.CastExpr {
6928 unwrap_sum_common_field_address_base(expr.expr)
6929 }
6930 else {
6931 expr
6932 }
6933 }
6934}
6935
6936fn (mut g Gen) emit_sum_common_field_ptr_expr(sum_type string, field_name string, sum_expr string, is_ptr bool, infos []SumCommonFieldVariant, tail []string, value_type string) {
6937 if infos.len == 0 {
6938 g.sb.write_string('NULL')
6939 return
6940 }
6941 g.tmp_counter++
6942 sum_tmp := '_sum_cf${g.tmp_counter}'
6943 value_tmp := '_field_cf${g.tmp_counter}'
6944 sum_decl_type := if is_ptr { '${sum_type}*' } else { sum_type }
6945 sep := if is_ptr { '->' } else { '.' }
6946 g.sb.write_string('({ ${sum_decl_type} ${sum_tmp} = ${sum_expr}; ${value_type}* ${value_tmp} = NULL; switch (${sum_tmp}${sep}_tag) {')
6947 for i, info in infos {
6948 g.sb.write_string('case ${i}: ${value_tmp} = ')
6949 g.emit_sum_common_field_variant_ptr_expr(info, field_name, sum_tmp, sep, tail, value_type)
6950 g.sb.write_string('; break;')
6951 }
6952 g.sb.write_string('default: break;} ${value_tmp}; })')
6953}
6954
6955fn (mut g Gen) emit_sum_common_field_variant_ptr_expr(info SumCommonFieldVariant, field_name string, sum_tmp string, sep string, tail []string, value_type string) {
6956 payload_expr := '(((${info.payload_type}*)(${sum_tmp}${sep}_data._${info.variant_field})))'
6957 if info.nested_sum_type != '' {
6958 nested_infos := g.sum_common_field_infos(info.nested_sum_type, field_name, []string{}) or {
6959 g.sb.write_string('NULL')
6960 return
6961 }
6962 g.emit_sum_common_field_ptr_expr(info.nested_sum_type, field_name, payload_expr, true,
6963 nested_infos, tail, value_type)
6964 return
6965 }
6966 field_path := sum_common_field_path(info, field_name, tail)
6967 g.sb.write_string('&(${payload_expr}->${field_path})')
6968}
6969
6970fn sum_common_field_path(info SumCommonFieldVariant, field_name string, tail []string) string {
6971 mut parts := []string{}
6972 if info.owner != '' {
6973 parts << escape_c_keyword(info.owner)
6974 }
6975 parts << escape_c_keyword(field_name)
6976 for part in tail {
6977 parts << escape_c_keyword(part)
6978 }
6979 return parts.join('.')
6980}
6981
6982fn (mut g Gen) embedded_owner_for(struct_name string, field_name string) string {
6983 if struct_name == '' {
6984 return ''
6985 }
6986 mut lookup_name := struct_name
6987 if concrete := g.resolve_active_generic_type(strip_pointer_type_name(struct_name)) {
6988 // Generic veb helpers are emitted in the framework module, where a short
6989 // concrete name like `Context` can collide with `veb.Context`. Prefer the
6990 // active generic's concrete struct metadata before doing module-relative
6991 // name lookup, so promoted fields resolve through the app context embed.
6992 if concrete is types.Struct {
6993 for field in concrete.fields {
6994 if field.name == field_name {
6995 return ''
6996 }
6997 }
6998 if info := g.lookup_embedded_field_info_in_struct(concrete, field_name) {
6999 return info.owner
7000 }
7001 }
7002 lookup_name = g.types_type_to_c(concrete)
7003 }
7004 if active_concrete := g.active_generic_concrete_struct_for_c_name(lookup_name) {
7005 for field in active_concrete.fields {
7006 if field.name == field_name {
7007 return ''
7008 }
7009 }
7010 if info := g.lookup_embedded_field_info_in_struct(active_concrete, field_name) {
7011 return info.owner
7012 }
7013 }
7014 key := '${lookup_name}.${field_name}'
7015 if owner := g.embedded_field_owner[key] {
7016 return owner
7017 }
7018 if info := g.lookup_embedded_field_info(lookup_name, field_name) {
7019 return info.owner
7020 }
7021 return ''
7022}
7023
7024fn (mut g Gen) active_generic_concrete_struct_for_c_name(c_name string) ?types.Struct {
7025 lookup_name := strip_pointer_type_name(c_name)
7026 if lookup_name == '' || g.active_generic_types.len == 0 {
7027 return none
7028 }
7029 for _, concrete in g.active_generic_types {
7030 concrete_c_name := strip_pointer_type_name(g.types_type_to_c(concrete))
7031 if concrete_c_name != lookup_name && short_type_name(concrete_c_name) != lookup_name {
7032 continue
7033 }
7034 if concrete is types.Struct {
7035 return concrete
7036 }
7037 }
7038 return none
7039}
7040
7041fn (mut g Gen) is_fn_pointer_alias_type(type_name string) bool {
7042 if type_name == '' {
7043 return false
7044 }
7045 if type_name.contains('(*)') {
7046 return true
7047 }
7048 if raw_type := g.lookup_type_by_c_name(type_name) {
7049 if raw_type is types.Alias && raw_type.base_type is types.FnType {
7050 return true
7051 }
7052 }
7053 if g.env == unsafe { nil } {
7054 return type_name.ends_with('Fn')
7055 }
7056 mut candidates := []string{cap: 3}
7057 candidates << type_name
7058 if type_name.contains('__') {
7059 candidates << type_name.all_after_last('__')
7060 }
7061 for cand in candidates {
7062 if mut scope := g.env_scope(g.cur_module) {
7063 if obj := scope.lookup_parent(cand, 0) {
7064 typ := obj.typ()
7065 if typ is types.Alias {
7066 if typ.base_type is types.FnType {
7067 return true
7068 }
7069 }
7070 }
7071 }
7072 if mut scope := g.env_scope('builtin') {
7073 if obj := scope.lookup_parent(cand, 0) {
7074 typ := obj.typ()
7075 if typ is types.Alias {
7076 if typ.base_type is types.FnType {
7077 return true
7078 }
7079 }
7080 }
7081 }
7082 if cand.contains('__') {
7083 mod_name := cand.all_before('__')
7084 short_name := cand.all_after('__')
7085 if mut scope := g.env_scope(mod_name) {
7086 if obj := scope.lookup_parent(short_name, 0) {
7087 typ := obj.typ()
7088 if typ is types.Alias {
7089 if typ.base_type is types.FnType {
7090 return true
7091 }
7092 }
7093 }
7094 }
7095 }
7096 }
7097 return type_name.ends_with('Fn')
7098}
7099
7100fn simd_vector_field_order(type_name string) []string {
7101 if type_name.ends_with('SimdFloat4') || type_name.ends_with('SimdInt4')
7102 || type_name.ends_with('SimdU32_4') || type_name.ends_with('Vec4') {
7103 return ['x', 'y', 'z', 'w']
7104 }
7105 if type_name.ends_with('SimdFloat2') || type_name.ends_with('SimdUint2')
7106 || type_name.ends_with('SimdI32_2') || type_name.ends_with('Vec2') {
7107 return ['x', 'y']
7108 }
7109 return []string{}
7110}
7111
7112fn (mut g Gen) gen_simd_vector_init_expr(type_name string, fields []ast.FieldInit) bool {
7113 order := simd_vector_field_order(type_name)
7114 if order.len == 0 {
7115 return false
7116 }
7117 mut values := map[string]ast.Expr{}
7118 for field in fields {
7119 if field.name == '' {
7120 return false
7121 }
7122 values[field.name] = field.value
7123 }
7124 g.sb.write_string('((${type_name}){')
7125 for i, field_name in order {
7126 if i > 0 {
7127 g.sb.write_string(', ')
7128 }
7129 if value := values[field_name] {
7130 g.expr(value)
7131 } else {
7132 g.sb.write_string('0')
7133 }
7134 }
7135 g.sb.write_string('})')
7136 return true
7137}
7138
7139fn unwrap_alias_type(typ types.Type) types.Type {
7140 if !type_has_valid_data(typ) {
7141 return typ
7142 }
7143 mut cur := typ
7144 for {
7145 if !type_has_valid_data(cur) {
7146 break
7147 }
7148 match cur {
7149 types.Alias {
7150 alias_type := cur as types.Alias
7151 cur = alias_type.base_type
7152 }
7153 else {
7154 break
7155 }
7156 }
7157 }
7158 return cur
7159}
7160
7161fn struct_field_needs_explicit_default(field types.Field) bool {
7162 if field.default_expr !is ast.EmptyExpr {
7163 return true
7164 }
7165 field_type := unwrap_alias_type(field.typ)
7166 match field_type {
7167 types.Array, types.Map, types.OptionType, types.String {
7168 return true
7169 }
7170 types.Struct {
7171 return struct_type_needs_explicit_default(field_type)
7172 }
7173 else {}
7174 }
7175
7176 return false
7177}
7178
7179fn struct_type_needs_explicit_default(struct_type types.Struct) bool {
7180 for field in struct_type.fields {
7181 if struct_field_needs_explicit_default(field) {
7182 return true
7183 }
7184 }
7185 for emb in struct_type.embedded {
7186 if struct_type_needs_explicit_default(emb) {
7187 return true
7188 }
7189 }
7190 return false
7191}
7192
7193fn map_int_key_width_from_c_type(type_name string) int {
7194 return match type_name {
7195 'i8', 'u8', 'byte', 'bool', 'char' { 1 }
7196 'i16', 'u16' { 2 }
7197 'i64', 'u64', 'f64', 'usize', 'isize' { 8 }
7198 else { 4 }
7199 }
7200}
7201
7202fn map_runtime_key_fns_from_c_type(key_type_name string) (string, string, string, string) {
7203 if key_type_name == 'string' {
7204 return 'map_hash_string', 'map_eq_string', 'map_clone_string', 'map_free_string'
7205 }
7206 width := map_int_key_width_from_c_type(key_type_name)
7207 return 'map_hash_int_${width}', 'map_eq_int_${width}', 'map_clone_int_${width}', 'map_free_nop'
7208}
7209
7210fn (mut g Gen) struct_default_field_is_direct(type_name string, field_name string) bool {
7211 if decl_info := g.find_struct_decl_info_by_c_name(type_name) {
7212 for field in decl_info.decl.fields {
7213 if field.name == field_name {
7214 return true
7215 }
7216 }
7217 return false
7218 }
7219 return true
7220}
7221
7222fn embedded_struct_field_name(emb types.Struct) string {
7223 mut name := emb.name
7224 if name.contains('__') {
7225 name = name.all_after_last('__')
7226 }
7227 if name.contains('.') {
7228 name = name.all_after_last('.')
7229 }
7230 return name
7231}
7232
7233fn (mut g Gen) resolve_embedded_struct(emb types.Struct) types.Struct {
7234 if emb.fields.len > 0 || emb.embedded.len > 0 || emb.name == '' {
7235 return emb
7236 }
7237 for lookup_name in [emb.name, emb.name.replace('.', '__')] {
7238 resolved := g.lookup_struct_type_by_c_name(lookup_name)
7239 if resolved.fields.len > 0 || resolved.embedded.len > 0 {
7240 return resolved
7241 }
7242 }
7243 return emb
7244}
7245
7246fn (mut g Gen) resolve_struct_for_default_literal(struct_type types.Struct, type_name string) types.Struct {
7247 if struct_type.fields.len > 0 || struct_type.embedded.len > 0 {
7248 return struct_type
7249 }
7250 mut lookup_names := []string{}
7251 if type_name != '' {
7252 lookup_names << type_name
7253 }
7254 if struct_type.name != '' {
7255 lookup_names << struct_type.name
7256 lookup_names << struct_type.name.replace('.', '__')
7257 }
7258 for lookup_name in lookup_names {
7259 resolved := g.lookup_struct_type_by_c_name(lookup_name)
7260 if resolved.fields.len > 0 || resolved.embedded.len > 0 {
7261 return resolved
7262 }
7263 }
7264 return struct_type
7265}
7266
7267fn (mut g Gen) write_struct_default_literal(struct_type types.Struct, type_name string) bool {
7268 resolved := g.resolve_struct_for_default_literal(struct_type, type_name)
7269 if resolved.fields.len == 0 && resolved.embedded.len == 0 {
7270 return false
7271 }
7272 g.sb.write_string('((${type_name}){')
7273 mut wrote_defaults := 0
7274 for field in resolved.fields {
7275 if !g.struct_default_field_is_direct(type_name, field.name) {
7276 continue
7277 }
7278 if !struct_field_needs_explicit_default(field) {
7279 continue
7280 }
7281 if wrote_defaults > 0 {
7282 g.sb.write_string(',')
7283 }
7284 g.sb.write_string('.${escape_c_keyword(field.name)} = ')
7285 if !g.write_struct_field_default_value(field, type_name) {
7286 g.sb.write_string('0')
7287 }
7288 wrote_defaults++
7289 }
7290 for emb in resolved.embedded {
7291 resolved_emb := g.resolve_embedded_struct(emb)
7292 emb_name := embedded_struct_field_name(resolved_emb)
7293 for field in resolved_emb.fields {
7294 if !struct_field_needs_explicit_default(field) {
7295 continue
7296 }
7297 if wrote_defaults > 0 {
7298 g.sb.write_string(',')
7299 }
7300 g.sb.write_string('.${escape_c_keyword(emb_name)}.${escape_c_keyword(field.name)} = ')
7301 emb_type_name := g.types_type_to_c(types.Type(resolved_emb))
7302 if !g.write_struct_field_default_value(field, emb_type_name) {
7303 g.sb.write_string('0')
7304 }
7305 wrote_defaults++
7306 }
7307 }
7308 if wrote_defaults == 0 {
7309 g.sb.write_string('0')
7310 }
7311 g.sb.write_string('})')
7312 return true
7313}
7314
7315fn (mut g Gen) write_struct_field_default_value(field types.Field, owner_type_name string) bool {
7316 if field.default_expr !is ast.EmptyExpr {
7317 saved_module := g.cur_module
7318 owner_module := if owner_type_name.contains('__') {
7319 owner_type_name.all_before_last('__')
7320 } else {
7321 ''
7322 }
7323 if owner_module != '' && owner_module != 'main' && owner_module != 'builtin' {
7324 g.cur_module = owner_module
7325 }
7326 defer {
7327 g.cur_module = saved_module
7328 }
7329 field_c_type := g.types_type_to_c(field.typ)
7330 if g.is_interface_type(field_c_type)
7331 && g.gen_interface_cast(field_c_type, field.default_expr) {
7332 return true
7333 }
7334 g.expr(field.default_expr)
7335 return true
7336 }
7337 field_type := unwrap_alias_type(field.typ)
7338 if field_type is types.Array {
7339 array_type := field_type as types.Array
7340 elem_type := g.types_type_to_c(array_type.elem_type)
7341 g.sb.write_string('__new_array_with_default_noscan(0, 0, sizeof(${elem_type}), NULL)')
7342 return true
7343 }
7344 if field_type is types.Map {
7345 map_type := field_type as types.Map
7346 key_type := unwrap_alias_type(map_type.key_type)
7347 key_c_type := g.types_type_to_c(key_type)
7348 value_c_type := g.types_type_to_c(map_type.value_type)
7349 hash_fn, eq_fn, clone_fn, free_fn := map_runtime_key_fns_from_c_type(key_c_type)
7350 g.sb.write_string('new_map(sizeof(${key_c_type}), sizeof(${value_c_type}), (*&${hash_fn}), (*&${eq_fn}), (*&${clone_fn}), (*&${free_fn}))')
7351 return true
7352 }
7353 if field_type is types.String {
7354 g.sb.write_string(c_empty_v_string_expr())
7355 return true
7356 }
7357 if field_type is types.OptionType {
7358 option_type := g.known_unqualified_struct_literal_type(g.types_type_to_c(field.typ))
7359 if option_type.starts_with('_option_') {
7360 g.sb.write_string('(${option_type}){ .state = 2 }')
7361 return true
7362 }
7363 }
7364 if field_type is types.Struct {
7365 struct_type := field_type as types.Struct
7366 type_name := g.types_type_to_c(field_type)
7367 return g.write_struct_default_literal(struct_type, type_name)
7368 }
7369 return false
7370}
7371
7372fn (mut g Gen) gen_none_literal_for_type(type_name string) bool {
7373 trimmed := type_name.trim_space()
7374 if trimmed == '' {
7375 return false
7376 }
7377 if trimmed.starts_with('_option_') {
7378 g.sb.write_string('(${trimmed}){ .state = 2 }')
7379 return true
7380 }
7381 if is_ierror_interface_name(trimmed) {
7382 g.sb.write_string('none__')
7383 return true
7384 }
7385 if is_type_name_pointer_like(trimmed) || trimmed in ['void*', 'voidptr', 'byteptr', 'charptr'] {
7386 g.sb.write_string('NULL')
7387 return true
7388 }
7389 return false
7390}
7391
7392fn (g &Gen) unique_qualified_option_type(type_name string) string {
7393 if !type_name.starts_with('_option_') {
7394 return type_name
7395 }
7396 payload := type_name['_option_'.len..]
7397 if payload == '' || payload.contains('__') {
7398 return type_name
7399 }
7400 suffix := '__${payload}'
7401 mut matches := []string{}
7402 for name in g.option_aliases.keys() {
7403 if name.starts_with('_option_') && name['_option_'.len..].ends_with(suffix) {
7404 matches << name
7405 }
7406 }
7407 for name in g.emitted_option_structs.keys() {
7408 if name.starts_with('_option_') && name['_option_'.len..].ends_with(suffix)
7409 && name !in matches {
7410 matches << name
7411 }
7412 }
7413 if matches.len == 0 {
7414 for key in g.emitted_types.keys() {
7415 if !key.starts_with('alias_') {
7416 continue
7417 }
7418 alias_name := key['alias_'.len..]
7419 if alias_name.ends_with(suffix) {
7420 matches << '_option_' + mangle_alias_component(alias_name)
7421 }
7422 }
7423 }
7424 if matches.len == 0 && g.cur_module != '' && g.cur_module !in ['main', 'builtin'] {
7425 qualified_payload := '${g.cur_module}__${payload}'
7426 if 'alias_${qualified_payload}' in g.emitted_types {
7427 matches << '_option_' + mangle_alias_component(qualified_payload)
7428 }
7429 }
7430 if matches.len == 1 {
7431 return matches[0]
7432 }
7433 return g.qualify_module_local_type_name(type_name)
7434}
7435
7436fn option_payload_expr_can_be_contextually_typed_as_option(value ast.Expr) bool {
7437 unwrapped := strip_expr_wrappers(value)
7438 return match unwrapped {
7439 ast.BasicLiteral, ast.StringLiteral, ast.StringInterLiteral, ast.ArrayInitExpr {
7440 true
7441 }
7442 ast.SelectorExpr {
7443 unwrapped.lhs is ast.EmptyExpr
7444 }
7445 else {
7446 false
7447 }
7448 }
7449}
7450
7451fn (mut g Gen) gen_option_wrapped_value_expr(option_type string, value ast.Expr) bool {
7452 expected_type := g.known_unqualified_struct_literal_type(option_type)
7453 if !expected_type.starts_with('_option_') {
7454 return false
7455 }
7456 if is_none_like_expr(value) {
7457 return g.gen_none_literal_for_type(expected_type)
7458 }
7459 value_type := option_value_type(expected_type)
7460 if value_type == '' || value_type == 'void' {
7461 return false
7462 }
7463 rhs_type := g.get_expr_type(value)
7464 contextual_payload := option_payload_expr_can_be_contextually_typed_as_option(value)
7465 if expected_type == '_option_string' && option_value_expr_is_builtin_option_string_clone(value) {
7466 g.expr(value)
7467 return true
7468 }
7469 if rhs_type == expected_type && !contextual_payload {
7470 g.expr(value)
7471 return true
7472 }
7473 if call_ret_type := g.option_result_value_expr_return_type(value) {
7474 if call_ret_type == expected_type && !contextual_payload {
7475 g.expr(value)
7476 return true
7477 }
7478 if (call_ret_type.starts_with('_option_') || call_ret_type.starts_with('_result_'))
7479 && !contextual_payload {
7480 return false
7481 }
7482 }
7483 if (rhs_type.starts_with('_option_') || rhs_type.starts_with('_result_')) && !contextual_payload {
7484 return false
7485 }
7486 if value is ast.InitExpr && g.expr_type_to_c(value.typ) == expected_type {
7487 g.expr(value)
7488 return true
7489 }
7490 g.sb.write_string('({ ${expected_type} _opt = (${expected_type}){ .state = 2 }; ${value_type} _val = ')
7491 if value is ast.SelectorExpr {
7492 sel := value as ast.SelectorExpr
7493 if sel.lhs is ast.EmptyExpr && g.is_enum_type(value_type) {
7494 g.sb.write_string(g.enum_member_c_name(value_type, sel.rhs.name))
7495 } else if g.is_interface_type(value_type) && g.gen_interface_cast(value_type, value) {
7496 } else if !g.gen_auto_deref_value_param_arg(value_type, value) {
7497 g.expr(value)
7498 }
7499 } else if g.is_interface_type(value_type) && g.gen_interface_cast(value_type, value) {
7500 } else if !g.gen_auto_deref_value_param_arg(value_type, value) {
7501 g.expr(value)
7502 }
7503 g.sb.write_string('; _option_ok(&_val, (_option*)&_opt, sizeof(_val)); _opt; })')
7504 return true
7505}
7506
7507fn option_string_clone_call_name(name string) bool {
7508 return name == 'builtin__Option_string__clone' || name == 'builtin____Option_string__clone'
7509 || name.ends_with('__Option_string__clone')
7510}
7511
7512fn option_value_expr_is_builtin_option_string_clone(value ast.Expr) bool {
7513 unwrapped := strip_expr_wrappers(value)
7514 match unwrapped {
7515 ast.CallExpr {
7516 if unwrapped.lhs is ast.Ident {
7517 return option_string_clone_call_name(unwrapped.lhs.name)
7518 }
7519 }
7520 ast.CallOrCastExpr {
7521 if unwrapped.lhs is ast.Ident {
7522 return option_string_clone_call_name(unwrapped.lhs.name)
7523 }
7524 }
7525 else {}
7526 }
7527
7528 return false
7529}
7530
7531fn (mut g Gen) option_result_value_expr_return_type(value ast.Expr) ?string {
7532 match value {
7533 ast.CallExpr {
7534 if ret := g.get_call_return_type(value.lhs, value.args) {
7535 if ret != '' && ret != 'int' {
7536 return ret
7537 }
7538 }
7539 if value.lhs is ast.Ident {
7540 name := sanitize_fn_ident(value.lhs.name)
7541 if option_string_clone_call_name(name) {
7542 return '_option_string'
7543 }
7544 if ret := g.fn_return_types[name] {
7545 return ret
7546 }
7547 }
7548 }
7549 ast.CallOrCastExpr {
7550 if value.lhs is ast.Ident {
7551 name := sanitize_fn_ident(value.lhs.name)
7552 if option_string_clone_call_name(name) {
7553 return '_option_string'
7554 }
7555 if ret := g.fn_return_types[name] {
7556 return ret
7557 }
7558 }
7559 }
7560 else {}
7561 }
7562
7563 return none
7564}
7565
7566fn (mut g Gen) gen_fixed_array_field_init_from_expr(fixed_type string, expr ast.Expr) bool {
7567 _, arr_len := parse_fixed_array_elem_type(fixed_type)
7568 if arr_len <= 0 {
7569 return false
7570 }
7571 if expr is ast.ArrayInitExpr && expr.exprs.len > 0 {
7572 g.sb.write_u8(`{`)
7573 for i in 0 .. arr_len {
7574 if i > 0 {
7575 g.sb.write_string(', ')
7576 }
7577 if i < expr.exprs.len {
7578 g.expr(expr.exprs[i])
7579 } else {
7580 g.sb.write_string('0')
7581 }
7582 }
7583 g.sb.write_u8(`}`)
7584 return true
7585 }
7586 g.sb.write_u8(`{`)
7587 for i in 0 .. arr_len {
7588 if i > 0 {
7589 g.sb.write_string(', ')
7590 }
7591 g.expr(expr)
7592 g.sb.write_string('[${i}]')
7593 }
7594 g.sb.write_u8(`}`)
7595 return true
7596}
7597
7598fn (mut g Gen) init_field_expected_type(type_name string, env_struct types.Struct, field_name string) string {
7599 expected_key := '${type_name}.${field_name}'
7600 mut expected_field_type := g.struct_field_types[expected_key] or { '' }
7601 if expected_field_type == '' && type_name.contains('__') {
7602 short_type := type_name.all_after_last('__')
7603 short_expected_key := '${short_type}.${field_name}'
7604 expected_field_type = g.struct_field_types[short_expected_key] or { '' }
7605 }
7606 if expected_field_type != '' {
7607 return expected_field_type
7608 }
7609 if generic_field_type := g.generic_instance_field_type_for_init(type_name, field_name) {
7610 return generic_field_type
7611 }
7612 for field in env_struct.fields {
7613 if field.name == field_name {
7614 return g.types_type_to_c(field.typ)
7615 }
7616 }
7617 for emb in env_struct.embedded {
7618 resolved_emb := g.resolve_embedded_struct(emb)
7619 emb_name := embedded_struct_field_name(resolved_emb)
7620 if emb_name == field_name {
7621 return g.types_type_to_c(types.Type(resolved_emb))
7622 }
7623 }
7624 if info := g.lookup_embedded_field_info(type_name, field_name) {
7625 if info.field_type != '' {
7626 return info.field_type
7627 }
7628 }
7629 return ''
7630}
7631
7632fn (mut g Gen) generic_instance_field_type_for_init(type_name string, field_name string) ?string {
7633 if type_name == '' || field_name == '' {
7634 return none
7635 }
7636 old_module := g.cur_module
7637 old_file := g.cur_file_name
7638 prev_active := g.active_generic_types.clone()
7639 defer {
7640 g.cur_module = old_module
7641 g.cur_file_name = old_file
7642 g.active_generic_types = prev_active.clone()
7643 }
7644 for struct_base, instances in g.generic_struct_instances {
7645 for inst in instances {
7646 if inst.c_name != type_name {
7647 continue
7648 }
7649 node := g.find_generic_struct_node(struct_base) or { return none }
7650 g.active_generic_types = inst.bindings.clone()
7651 for field in node.fields {
7652 if field.name != field_name {
7653 continue
7654 }
7655 if fn_field_type := g.struct_fn_field_lookup_type_for_emit(field) {
7656 return fn_field_type
7657 }
7658 return g.struct_field_type_for_emit(inst.c_name, field)
7659 }
7660 }
7661 }
7662 return none
7663}
7664
7665fn (mut g Gen) gen_channel_init_expr(node ast.InitExpr) bool {
7666 mut is_channel := false
7667 mut elem_c_type := ''
7668 if node.typ is ast.Type && node.typ is ast.ChannelType {
7669 is_channel = true
7670 ch_type := node.typ as ast.ChannelType
7671 elem_c_type = g.expr_type_to_c(ch_type.elem_type)
7672 }
7673 if raw_type := g.get_raw_type(node.typ) {
7674 unwrapped := unwrap_alias_type(raw_type)
7675 if unwrapped is types.Channel {
7676 is_channel = true
7677 if elem_type := unwrapped.elem_type {
7678 elem_c_type = g.types_type_to_c(elem_type)
7679 }
7680 }
7681 }
7682 if !is_channel && g.expr_type_to_c(node.typ) != 'chan' {
7683 return false
7684 }
7685 if elem_c_type == '' {
7686 elem_c_type = 'void*'
7687 }
7688 g.force_emit_fn_names['sync__new_channel_st'] = true
7689 g.mark_called_fn_name('sync__new_channel_st')
7690 g.sb.write_string('sync__new_channel_st(')
7691 mut wrote_cap := false
7692 for field in node.fields {
7693 if field.name == 'cap' {
7694 g.expr(field.value)
7695 wrote_cap = true
7696 break
7697 }
7698 }
7699 if !wrote_cap && node.typ is ast.Type && node.typ is ast.ChannelType {
7700 ch_type := node.typ as ast.ChannelType
7701 if ch_type.cap !is ast.EmptyExpr {
7702 g.expr(ch_type.cap)
7703 wrote_cap = true
7704 }
7705 }
7706 if !wrote_cap {
7707 g.sb.write_string('0')
7708 }
7709 g.sb.write_string(', sizeof(${elem_c_type}) > 0 ? sizeof(${elem_c_type}) : 1)')
7710 return true
7711}
7712
7713fn (mut g Gen) known_unqualified_struct_literal_type(type_name string) string {
7714 normalized_builtin := g.normalize_builtin_qualified_c_type(type_name)
7715 if normalized_builtin != type_name {
7716 return normalized_builtin
7717 }
7718 if type_name.starts_with('_option_') {
7719 return g.unique_qualified_option_type(type_name)
7720 }
7721 if type_name == '' || type_name.contains('__') || type_name.starts_with('Array_')
7722 || type_name.starts_with('Map_') || type_name.starts_with('_result_')
7723 || type_name in primitive_types || type_name in ['bool', 'string', 'void*', 'voidptr'] {
7724 return type_name
7725 }
7726 if type_name.contains('_T_') && !type_name.contains('__') {
7727 mod_name := if g.cur_module != '' && g.cur_module != 'main' && g.cur_module != 'builtin' {
7728 g.cur_module
7729 } else if g.cur_fn_c_name.contains('__') {
7730 g.cur_fn_c_name.all_before('__')
7731 } else {
7732 ''
7733 }
7734 if mod_name != '' {
7735 base_name := type_name.all_before('_T_')
7736 if base_name != '' {
7737 if scope := g.env_scope(mod_name) {
7738 if _ := scope.lookup_type(base_name) {
7739 return '${mod_name}__${type_name}'
7740 }
7741 }
7742 }
7743 }
7744 }
7745 if 'body_${type_name}' in g.emitted_types {
7746 return type_name
7747 }
7748 if 'body_${type_name}' in g.pending_late_body_keys {
7749 return type_name
7750 }
7751 if 'alias_${type_name}' in g.emitted_types {
7752 return type_name
7753 }
7754 mut matched := ''
7755 mut match_count := 0
7756 suffix := '__${type_name}'
7757 for key, _ in g.emitted_types {
7758 if key.starts_with('body_') || key.starts_with('alias_') {
7759 prefix_len := if key.starts_with('body_') { 'body_'.len } else { 'alias_'.len }
7760 candidate := key[prefix_len..]
7761 if candidate.ends_with(suffix) {
7762 matched = candidate
7763 match_count++
7764 if match_count > 1 {
7765 return g.qualify_module_local_type_name(type_name)
7766 }
7767 }
7768 }
7769 }
7770 for key, _ in g.pending_late_body_keys {
7771 if key.starts_with('body_') {
7772 candidate := key['body_'.len..]
7773 if candidate.ends_with(suffix) {
7774 matched = candidate
7775 match_count++
7776 if match_count > 1 {
7777 return g.qualify_module_local_type_name(type_name)
7778 }
7779 }
7780 }
7781 }
7782 if match_count == 1 {
7783 return matched
7784 }
7785 return g.qualify_module_local_type_name(type_name)
7786}
7787
7788fn (g &Gen) contextual_specialized_init_type(type_name string) string {
7789 if type_name == '' || g.cur_fn_ret_type == '' || type_name.starts_with('Array_')
7790 || type_name.starts_with('Map_') || type_name.starts_with('_option_')
7791 || type_name.starts_with('_result_') || type_name in primitive_types
7792 || type_name in ['bool', 'string', 'void*', 'voidptr'] {
7793 return type_name
7794 }
7795 mut context_type := g.cur_fn_ret_type.trim_space()
7796 if context_type.starts_with('_option_') {
7797 context_type = option_value_type(context_type)
7798 } else if context_type.starts_with('_result_') {
7799 context_type = g.result_value_c_type(context_type)
7800 }
7801 context_type = strip_pointer_type_name(context_type)
7802 if context_type == '' {
7803 return type_name
7804 }
7805 if context_type.starts_with('Array_') || context_type.starts_with('Map_')
7806 || context_type.starts_with('Tuple_') {
7807 return type_name
7808 }
7809 context_base := context_type.all_before('_T_')
7810 if context_type.contains('__') && !context_type.contains('_T_')
7811 && short_type_name(context_type) == short_type_name(type_name) {
7812 return context_type
7813 }
7814 if !context_type.contains('_T_') {
7815 return type_name
7816 }
7817 type_base := if type_name.contains('_T_') { type_name.all_before('_T_') } else { type_name }
7818 if context_base == type_base || short_type_name(context_base) == short_type_name(type_base) {
7819 return context_type
7820 }
7821 return type_name
7822}
7823
7824fn expected_init_expr_type_for_expr(expr ast.Expr, expected_type string) ?string {
7825 if expected_type == '' {
7826 return none
7827 }
7828 match expr {
7829 ast.InitExpr {
7830 return expected_type
7831 }
7832 ast.ParenExpr {
7833 return expected_init_expr_type_for_expr(expr.expr, expected_type)
7834 }
7835 ast.ModifierExpr {
7836 return expected_init_expr_type_for_expr(expr.expr, expected_type)
7837 }
7838 ast.CastExpr {
7839 return expected_init_expr_type_for_expr(expr.expr, expected_type)
7840 }
7841 ast.PrefixExpr {
7842 if expr.op == .amp {
7843 base_type := strip_pointer_type_name(expected_type)
7844 if base_type != '' && base_type != expected_type {
7845 return expected_init_expr_type_for_expr(expr.expr, base_type)
7846 }
7847 }
7848 }
7849 else {}
7850 }
7851
7852 return none
7853}
7854
7855fn (mut g Gen) expr_with_expected_init_type(expr ast.Expr, expected_type string) {
7856 init_expected_type := expected_init_expr_type_for_expr(expr, expected_type) or {
7857 g.expr(expr)
7858 return
7859 }
7860 saved_expected_type := g.expected_init_expr_type
7861 g.expected_init_expr_type = init_expected_type
7862 g.expr(expr)
7863 g.expected_init_expr_type = saved_expected_type
7864}
7865
7866fn init_expr_can_use_expected_type(node ast.InitExpr, type_name string, expected_type string) bool {
7867 if expected_type == '' || expected_type.starts_with('Array_')
7868 || expected_type.starts_with('Map_') || expected_type.starts_with('_option_')
7869 || expected_type.starts_with('_result_') || expected_type in primitive_types
7870 || expected_type in ['bool', 'string', 'void*', 'voidptr'] {
7871 return false
7872 }
7873 if node.typ !is ast.Ident {
7874 return false
7875 }
7876 ident := node.typ as ast.Ident
7877 ident_name := ident.name
7878 if ident_name == '' || ident_name.contains('.') || ident_name.contains('__') {
7879 return false
7880 }
7881 expected_base := strip_pointer_type_name(expected_type)
7882 type_base := strip_pointer_type_name(type_name)
7883 return short_type_name(expected_base) == ident_name
7884 || is_generic_placeholder_type_name(ident_name)
7885 || (type_base != '' && short_type_name(expected_base) == short_type_name(type_base))
7886}
7887
7888fn generic_init_type_base_matches(resolved_type string, base_name string) bool {
7889 if resolved_type == '' || base_name == '' {
7890 return false
7891 }
7892 resolved_base := if resolved_type.contains('_T_') {
7893 resolved_type.all_before('_T_')
7894 } else {
7895 resolved_type
7896 }
7897 return resolved_base == base_name
7898 || short_type_name(resolved_base) == short_type_name(base_name)
7899}
7900
7901fn (mut g Gen) canonical_init_generic_type_name(typ ast.Expr, type_name string) string {
7902 if type_name == '' || !type_name.contains('_T_') {
7903 return type_name
7904 }
7905 match typ {
7906 ast.GenericArgOrIndexExpr {
7907 base_name := g.generic_type_lhs_to_c(typ.lhs)
7908 if generic_init_type_base_matches(type_name, base_name) {
7909 return g.resolve_generic_struct_c_name(base_name, [typ.expr])
7910 }
7911 }
7912 ast.GenericArgs {
7913 base_name := g.generic_type_lhs_to_c(typ.lhs)
7914 if generic_init_type_base_matches(type_name, base_name) {
7915 return g.resolve_generic_struct_c_name(base_name, typ.args)
7916 }
7917 }
7918 ast.Type {
7919 if typ is ast.GenericType {
7920 base_name := g.generic_type_lhs_to_c(typ.name)
7921 if generic_init_type_base_matches(type_name, base_name) {
7922 return g.resolve_generic_struct_c_name(base_name, typ.params)
7923 }
7924 }
7925 }
7926 else {}
7927 }
7928
7929 return type_name
7930}
7931
7932fn (mut g Gen) canonical_recorded_generic_type_name(type_name string) string {
7933 if type_name == '' || !type_name.contains('_T_') {
7934 return type_name
7935 }
7936 base_name := type_name.all_before('_T_')
7937 params_key := type_name.all_after('_T_')
7938 mut instances := g.generic_struct_instances[base_name]
7939 if instances.len == 0 && base_name.contains('__') {
7940 instances = g.generic_struct_instances[base_name.all_after_last('__')]
7941 } else if instances.len == 0 && !base_name.contains('__') && g.cur_module != ''
7942 && g.cur_module != 'main' && g.cur_module != 'builtin' {
7943 instances = g.generic_struct_instances['${g.cur_module}__${base_name}']
7944 }
7945 for inst in instances {
7946 if inst.params_key == params_key {
7947 return inst.c_name
7948 }
7949 }
7950 return type_name
7951}
7952
7953fn (mut g Gen) gen_init_expr(node ast.InitExpr) {
7954 expected_type := g.expected_init_expr_type
7955 g.expected_init_expr_type = ''
7956 mut type_name := g.expr_type_to_c(node.typ)
7957 type_name = g.canonical_init_generic_type_name(node.typ, type_name)
7958 type_name = g.canonical_recorded_generic_type_name(type_name)
7959 if node.typ is ast.Ident {
7960 if g.type_expr_has_metadata(node.typ) {
7961 // Position metadata is authoritative for synthesized type expressions.
7962 } else if fn_local_type := g.current_fn_module_local_type_name(node.typ.name) {
7963 type_name = fn_local_type
7964 }
7965 }
7966 mut used_expected_type := false
7967 if init_expr_can_use_expected_type(node, type_name, expected_type) {
7968 type_name = expected_type
7969 used_expected_type = true
7970 }
7971 if !used_expected_type {
7972 type_name = g.known_unqualified_struct_literal_type(type_name)
7973 type_name = g.contextual_specialized_init_type(type_name)
7974 }
7975 if g.gen_channel_init_expr(node) {
7976 return
7977 }
7978 if type_name.starts_with('_option_') && gen_init_expr_is_none_option(node) {
7979 g.sb.write_string('((${type_name}){ .state = 2 })')
7980 return
7981 }
7982 mut env_struct := types.Struct{}
7983 mut has_struct_defaults := false
7984 if raw_type := g.get_raw_type(node.typ) {
7985 unwrapped := unwrap_alias_type(raw_type)
7986 if unwrapped is types.Struct {
7987 env_struct = unwrapped
7988 has_struct_defaults = true
7989 }
7990 }
7991 if !has_struct_defaults && !type_name.starts_with('Array_') && !type_name.starts_with('Map_') {
7992 resolved := g.lookup_struct_type_by_c_name(type_name)
7993 if resolved.fields.len > 0 {
7994 env_struct = resolved
7995 has_struct_defaults = true
7996 }
7997 }
7998 if node.fields.len == 0 {
7999 if type_name.ends_with('*') {
8000 g.sb.write_string('0')
8001 return
8002 }
8003 if has_struct_defaults && (env_struct.fields.len > 0 || env_struct.embedded.len > 0) {
8004 if g.write_struct_default_literal(env_struct, type_name) {
8005 return
8006 }
8007 }
8008 if type_name.starts_with('Array_') {
8009 elem_c_type := type_name[6..] // e.g. Array_ast__Stmt → ast__Stmt
8010 g.sb.write_string('__new_array_with_default_noscan(0, 0, sizeof(${elem_c_type}), NULL)')
8011 return
8012 }
8013 g.sb.write_string('((${type_name}){0})')
8014 return
8015 }
8016 if g.gen_simd_vector_init_expr(type_name, node.fields) {
8017 return
8018 }
8019 if g.gen_specialized_sum_init_expr(type_name, node) {
8020 return
8021 }
8022 g.sb.write_string('((${type_name}){')
8023 mut wrote_fields := 0
8024 for field in node.fields {
8025 if wrote_fields > 0 {
8026 g.sb.write_string(',')
8027 }
8028 wrote_fields++
8029 if field.name == '' {
8030 g.expr(field.value)
8031 continue
8032 }
8033 if type_name in g.sum_type_variants && field.name.starts_with('_data._') {
8034 raw_variant_name := field.name.all_after('_data._')
8035 variant_name := g.sum_type_variant_field_name(type_name, raw_variant_name)
8036 field_name := '_data._${variant_name}'
8037 g.sb.write_string('.${field_name} = ')
8038 mut inner_type := g.get_expr_type(field.value)
8039 resolved_type := g.resolve_sum_payload_storage_type(type_name, variant_name, inner_type)
8040 if g.is_scalar_sum_payload_type(resolved_type) {
8041 // Keep scalar payloads encoded in pointer-size space.
8042 g.sb.write_string('((void*)((intptr_t)(')
8043 g.expr(field.value)
8044 g.sb.write_string(')))')
8045 } else if inner_type == 'void*' {
8046 // The transformer already lowered the payload to a void* expression
8047 // (either `(voidptr)(intptr_t)(scalar)` for direct scalar aliases or
8048 // `(voidptr)memdup(&struct, sizeof(...))` for boxed payloads). Trust
8049 // that encoding and emit it as-is. Re-wrapping with another `memdup`
8050 // would double-allocate for structs and dereference NULL for scalars.
8051 if g.expr_is_voidptr_cast(field.value) {
8052 g.expr(field.value)
8053 } else if inner_expr := g.unwrap_addr_of_value_expr(field.value) {
8054 // `&fn_call()` can point to a temporary; copy by-value from inner expr.
8055 g.tmp_counter++
8056 tmp_name := '_st${g.tmp_counter}'
8057 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
8058 g.expr(inner_expr)
8059 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
8060 } else if g.sum_payload_expr_needs_temp(field.value, resolved_type) {
8061 g.tmp_counter++
8062 tmp_name := '_st${g.tmp_counter}'
8063 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
8064 g.expr(field.value)
8065 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
8066 } else {
8067 g.sb.write_string('((void*)memdup(')
8068 g.expr(field.value)
8069 g.sb.write_string(', sizeof(${resolved_type})))')
8070 }
8071 } else {
8072 g.tmp_counter++
8073 tmp_name := '_st${g.tmp_counter}'
8074 g.sb.write_string('((void*)({ ${resolved_type} ${tmp_name} = ')
8075 g.expr(field.value)
8076 g.sb.write_string('; memdup(&${tmp_name}, sizeof(${resolved_type})); }))')
8077 }
8078 continue
8079 }
8080 owner := g.embedded_owner_for(type_name, field.name)
8081 field_name := escape_c_keyword(field.name)
8082 if owner != '' {
8083 g.sb.write_string('.${escape_c_keyword(owner)}.${field_name} = ')
8084 } else {
8085 g.sb.write_string('.${field_name} = ')
8086 }
8087 mut expected_field_type := g.init_field_expected_type(type_name, env_struct, field.name)
8088 expected_field_type = g.known_unqualified_struct_literal_type(expected_field_type)
8089 if expected_field_type.starts_with('Array_fixed_')
8090 && g.gen_fixed_array_field_init_from_expr(expected_field_type, field.value) {
8091 continue
8092 }
8093 if is_none_like_expr(field.value) && g.gen_none_literal_for_type(expected_field_type) {
8094 continue
8095 }
8096 if expected_field_type.starts_with('_option_')
8097 && g.gen_option_wrapped_value_expr(expected_field_type, field.value) {
8098 continue
8099 }
8100 if g.is_fn_pointer_alias_type(expected_field_type) {
8101 if field.value is ast.SelectorExpr {
8102 sel := field.value as ast.SelectorExpr
8103 if g.gen_bound_method_value_expr(sel, expected_field_type) {
8104 continue
8105 }
8106 if method_value_name := g.selector_method_value_name(sel) {
8107 g.sb.write_string('((${expected_field_type})${method_value_name})')
8108 continue
8109 }
8110 }
8111 if field.value is ast.Ident {
8112 g.sb.write_string('((${expected_field_type})')
8113 g.expr(field.value)
8114 g.sb.write_string(')')
8115 continue
8116 }
8117 }
8118 if expected_field_type in ['void*', 'voidptr'] && field.value is ast.SelectorExpr {
8119 sel := field.value as ast.SelectorExpr
8120 if g.gen_bound_method_value_expr(sel, 'void*') {
8121 continue
8122 }
8123 if method_value_name := g.selector_method_value_name(sel) {
8124 g.sb.write_string('((void*)${method_value_name})')
8125 continue
8126 }
8127 }
8128 // Disambiguate shorthand enum values in struct field initializers
8129 // using the field's declared enum type.
8130 if field.value is ast.SelectorExpr {
8131 sel := field.value as ast.SelectorExpr
8132 if sel.lhs is ast.EmptyExpr {
8133 expected_enum := expected_field_type
8134 if expected_enum != '' && g.is_enum_type(expected_enum) {
8135 g.sb.write_string(g.enum_member_c_name(expected_enum, sel.rhs.name))
8136 continue
8137 }
8138 }
8139 }
8140 if g.should_deref_init_field_value(type_name, field.name, expected_field_type, field.value) {
8141 g.sb.write_string('(*(')
8142 g.expr(field.value)
8143 g.sb.write_string('))')
8144 continue
8145 }
8146 // Auto-wrap concrete types into interface structs for interface-typed fields
8147 // (e.g. output_stream: log__stderr where output_stream is io__Writer and stderr is os__File)
8148 if expected_field_type != '' && g.is_interface_type(expected_field_type)
8149 && g.gen_interface_cast(expected_field_type, field.value) {
8150 continue
8151 }
8152 // Pointer-to-interface fields (e.g. dd: &DrawDevice = &DrawDeviceContext{...})
8153 if expected_field_type != '' && expected_field_type.ends_with('*') {
8154 iface_base := expected_field_type.trim_right('*')
8155 if g.is_interface_type(iface_base) {
8156 rhs_type := g.get_expr_type(field.value)
8157 rhs_base := rhs_type.trim_right('*')
8158 if rhs_base != '' && rhs_base != 'int' && rhs_base != iface_base
8159 && !g.is_interface_type(rhs_base) {
8160 if !g.gen_heap_interface_cast(iface_base, field.value) {
8161 g.expr(field.value)
8162 }
8163 continue
8164 }
8165 }
8166 }
8167 // Auto-wrap variant struct literals into sum type wrapping
8168 // (e.g. types.Struct{} -> types__Type{._tag = N, ._data._Struct = ...})
8169 // Only for InitExpr values (struct literals that are a variant of the sum type).
8170 if expected_field_type != '' && field.value is ast.InitExpr
8171 && g.gen_sum_wrapped_init_field(expected_field_type, field.value) {
8172 continue
8173 }
8174 if expected_field_type != ''
8175 && g.gen_sum_wrapped_init_value_field(expected_field_type, field.value) {
8176 continue
8177 }
8178 g.expr(field.value)
8179 }
8180 g.sb.write_string('})')
8181}
8182
8183fn (mut g Gen) gen_specialized_sum_init_expr(type_name string, node ast.InitExpr) bool {
8184 variants := g.get_sum_type_variants_for(type_name)
8185 if variants.len == 0 {
8186 return false
8187 }
8188 if node.fields.len == 0 {
8189 return false
8190 }
8191 mut value := node.fields[0].value
8192 mut found_data_field := false
8193 for field in node.fields {
8194 if field.name.starts_with('_data._') {
8195 value = field.value
8196 found_data_field = true
8197 break
8198 }
8199 }
8200 if !found_data_field {
8201 return false
8202 }
8203 payload_value := g.unwrap_sum_init_payload_value(value)
8204 mut value_type := g.get_expr_type(payload_value)
8205 if local_type := g.local_var_c_type_for_expr(payload_value) {
8206 if local_type != '' && local_type != 'int' {
8207 value_type = local_type
8208 }
8209 }
8210 if value_type == '' {
8211 return false
8212 }
8213 mut wrap_value := payload_value
8214 mut tag, mut field_name := sum_type_variant_for_arg_type(value_type, variants)
8215 if tag < 0 && value_type.ends_with('*') {
8216 value_base := value_type.trim_right('*')
8217 tag, field_name = sum_type_variant_for_arg_type(value_base, variants)
8218 if tag >= 0 {
8219 value_type = value_base
8220 wrap_value = ast.PrefixExpr{
8221 op: .mul
8222 expr: payload_value
8223 }
8224 }
8225 }
8226 if tag < 0 || value_type == type_name {
8227 return false
8228 }
8229 is_primitive :=
8230 value_type in ['int', 'i8', 'i16', 'i32', 'i64', 'u8', 'u16', 'u32', 'u64', 'f32', 'f64', 'bool', 'rune', 'byte', 'usize', 'isize']
8231 || value_type in g.primitive_type_aliases
8232 g.gen_sum_type_wrap(type_name, field_name, tag, is_primitive, wrap_value, value_type)
8233 return true
8234}
8235
8236fn (mut g Gen) unwrap_sum_init_payload_value(expr ast.Expr) ast.Expr {
8237 match expr {
8238 ast.ParenExpr {
8239 return g.unwrap_sum_init_payload_value(expr.expr)
8240 }
8241 ast.ModifierExpr {
8242 return g.unwrap_sum_init_payload_value(expr.expr)
8243 }
8244 ast.CastExpr {
8245 cast_type := g.expr_type_to_c(expr.typ)
8246 if cast_type in ['void*', 'voidptr', 'intptr', 'intptr_t', 'isize', 'usize', 'int',
8247 'i64'] {
8248 return g.unwrap_sum_init_payload_value(expr.expr)
8249 }
8250 }
8251 else {}
8252 }
8253
8254 return expr
8255}
8256
8257fn gen_init_expr_is_none_option(node ast.InitExpr) bool {
8258 if node.fields.len != 1 {
8259 return false
8260 }
8261 field := node.fields[0]
8262 if field.name != 'state' {
8263 return false
8264 }
8265 if field.value is ast.BasicLiteral {
8266 return field.value.value == '2'
8267 }
8268 return false
8269}
8270
8271fn (mut g Gen) should_deref_init_field_value(struct_type string, field_name string, expected_field_type string, value ast.Expr) bool {
8272 expected_key := '${struct_type}.${field_name}'
8273 mut expected := expected_field_type
8274 if expected == '' {
8275 expected = g.struct_field_types[expected_key] or { '' }
8276 }
8277 if expected == '' && struct_type.contains('__') {
8278 short_struct := struct_type.all_after_last('__')
8279 short_expected_key := '${short_struct}.${field_name}'
8280 expected = g.struct_field_types[short_expected_key] or { '' }
8281 }
8282 if expected == '' || is_type_name_pointer_like(expected) {
8283 return false
8284 }
8285 mut value_type := g.get_expr_type(value)
8286 if value is ast.Ident {
8287 if local_type := g.get_local_var_c_type(value.name) {
8288 if local_type != '' && local_type != 'int' {
8289 value_type = local_type
8290 }
8291 }
8292 }
8293 // When the value is a SelectorExpr with .data on a result/option variable
8294 // (e.g., _or_t48.data where _or_t48 is _result_SomeTypeptr), the generated
8295 // C code extracts the pointer from the result data area. The V-level type
8296 // says SomeType (non-pointer), but the actual C output is SomeType*.
8297 if !is_type_name_pointer_like(value_type) && value is ast.SelectorExpr {
8298 sel := value as ast.SelectorExpr
8299 if sel.rhs.name == 'data' {
8300 lhs_type := g.get_expr_type(sel.lhs)
8301 if lhs_type.starts_with('_result_') {
8302 inner := g.result_value_type(lhs_type)
8303 if is_type_name_pointer_like(inner) {
8304 value_type = inner
8305 }
8306 } else if lhs_type.starts_with('_option_') {
8307 inner := option_value_type(lhs_type)
8308 if is_type_name_pointer_like(inner) {
8309 value_type = inner
8310 }
8311 }
8312 }
8313 }
8314 // When the value is a CastExpr whose target type is a pointer (e.g., from
8315 // result/option unwrap like `new_socket()!` producing `&Socket`), the generated
8316 // C code will yield a pointer.
8317 if !is_type_name_pointer_like(value_type) && value is ast.CastExpr {
8318 cast_type := g.expr_type_to_c((value as ast.CastExpr).typ)
8319 if is_type_name_pointer_like(cast_type) {
8320 value_type = cast_type
8321 }
8322 }
8323 if !is_type_name_pointer_like(value_type) {
8324 call_ret_type := g.expr_pointer_return_type(value)
8325 if call_ret_type != '' {
8326 value_type = call_ret_type
8327 }
8328 }
8329 if !is_type_name_pointer_like(value_type) {
8330 return false
8331 }
8332 expected_base := strip_pointer_type_name(expected)
8333 value_base := strip_pointer_type_name(value_type)
8334 if expected_base == value_base {
8335 return true
8336 }
8337 return short_type_name(expected_base) == short_type_name(value_base)
8338}
8339