v / vlib / v2 / transformer / monomorphize.v
10855 lines · 10488 sloc · 319.77 KB
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module transformer
5
6// Generic monomorphization belongs in the transformer so every backend receives
7// concrete FnDecls and call names.
8import v2.ast
9import v2.token
10import v2.types
11import os
12import time
13
14struct CloneComptimeFieldCtx {
15 var_name string
16 field types.Field
17 field_idx int
18 struct_type types.Struct
19 attrs []string
20 is_embed bool
21 continue_label string
22 break_label string
23}
24
25struct DeferredGenericCallSpec {
26 base_name string
27 bindings map[string]types.Type
28 file_idx int
29}
30
31struct GenericStructSpec {
32 base_c_name string
33 concrete_c_name string
34 concrete_short_name string
35 module_name string
36 file_idx int
37 bindings map[string]types.Type
38}
39
40struct GenericTypeArgCursorBindings {
41 bindings map[string]types.Type
42 concrete_args []types.Type
43}
44
45struct StructDefaultDeclInfo {
46 decl ast.StructDecl
47 module_name string
48}
49
50// needs_full_files_for_transform reports whether transform needs the whole
51// legacy file set before per-file workers can run.
52pub fn (t &Transformer) needs_full_files_for_transform() bool {
53 return true
54}
55
56// prepare_files_for_transform performs whole-program preparation required
57// before per-file transformation. Generic monomorphization lives here so
58// sequential and parallel builders feed identical concrete ASTs to every
59// backend.
60pub fn (mut t Transformer) prepare_files_for_transform(files []ast.File) []ast.File {
61 // The checker records generic bindings while checking generic bodies, which
62 // can include speculative branches from unused library code. Build the
63 // transformer worklist from concrete call sites instead, then scan concrete
64 // clones to discover transitive generic calls.
65 t.env.generic_types = map[string][]map[string]types.Type{}
66 mut prepared := files.clone()
67 t.collect_declared_method_fns(prepared)
68 t.collect_struct_field_generic_decl_types(prepared)
69 timing := os.getenv('V2_TTIME') != ''
70 mut sw := time.new_stopwatch()
71 // prepared_dirty tracks whether `prepared` changed since it was last scanned
72 // by collect_generic_call_specs. collect is a pure (idempotent) function of
73 // the program: scanning an unchanged file set rediscovers the exact same
74 // specs. So the leading full-program scan is only needed when the program
75 // actually changed since the previous scan — i.e. when the previous
76 // iteration's inject step appended new struct specializations. The
77 // post-mono_pass scan below already covers the clones mono_pass adds, so the
78 // only unscanned source of change between iterations is inject. This removes
79 // the fixpoint's redundant confirmation scan (~6s / ~700MB for v2 self-host).
80 mut prepared_dirty := true
81 for iter in 0 .. 64 {
82 spec_count := t.monomorphized_specs.len
83 generic_count := t.generic_types_spec_count()
84 struct_count := t.generic_struct_specs.len
85 ts_start := sw.elapsed().milliseconds()
86 // collect1: full-program scan, only when the program changed since the
87 // last scan (initial pass, or inject appended structs last iteration).
88 if prepared_dirty {
89 t.collect_generic_call_specs(prepared)
90 }
91 ts_collect1 := sw.elapsed().milliseconds()
92 before_mono := t.monomorphized_specs.len
93 prepared = t.monomorphize_pass(prepared)
94 ts_mono := sw.elapsed().milliseconds()
95 // collect2: only when monomorphize_pass materialized new clones. Those
96 // clones are the only statements the previous scan has not seen — every
97 // other statement is byte-for-byte unchanged and collect is idempotent —
98 // so rescan just the new clones instead of re-walking all files. This
99 // turns the dominant ~6s/~700MB full rescan into a walk of a few dozen
100 // functions.
101 if t.monomorphized_specs.len != before_mono {
102 t.collect_generic_call_specs_in_new_clones(prepared)
103 }
104 ts_collect2 := sw.elapsed().milliseconds()
105 prepared = t.inject_generic_struct_specializations(prepared)
106 ts_inject := sw.elapsed().milliseconds()
107 if t.inject_changed_files {
108 t.collect_generic_call_specs_in_new_structs(prepared)
109 }
110 ts_collect3 := sw.elapsed().milliseconds()
111 // collect2/collect3 rescan every statement appended by this iteration,
112 // so the next iteration does not need another full-program scan.
113 prepared_dirty = false
114 if timing {
115 eprintln(' [ttime] iter ${iter}: collect1=${ts_collect1 - ts_start}ms mono_pass=${ts_mono - ts_collect1}ms collect2=${ts_collect2 - ts_mono}ms inject=${ts_inject - ts_collect2}ms collect3=${ts_collect3 - ts_inject}ms files=${prepared.len}')
116 }
117 t_print_mem('monomorphize iter ${iter}')
118 if t.monomorphized_specs.len == spec_count && t.generic_types_spec_count() == generic_count
119 && t.generic_struct_specs.len == struct_count {
120 break
121 }
122 }
123 if dump_path := os.getenv_opt('V2_TDUMP') {
124 t.dump_monomorphize_specs(dump_path, prepared)
125 }
126 t.collect_struct_default_decl_infos(prepared)
127 t.collect_concrete_embedded_owner_names(prepared)
128 return prepared
129}
130
131// prepare_flat_for_transform performs the same whole-program generic
132// preparation as prepare_files_for_transform, but keeps the original source in
133// FlatAst form. Monomorphization and generic struct specialization are
134// append-only, so the flat path stores just the per-file appended statements and
135// lets the caller stream each original file through a one-file decode.
136pub fn (mut t Transformer) prepare_flat_for_transform(flat &ast.FlatAst) map[int][]ast.Stmt {
137 t.env.generic_types = map[string][]map[string]types.Type{}
138 mut extra_stmts := map[int][]ast.Stmt{}
139 t.collect_declared_method_fns_from_flat(flat)
140 t.collect_struct_field_generic_decl_types_from_flat(flat)
141 timing := os.getenv('V2_TTIME') != ''
142 mut sw := time.new_stopwatch()
143 mut prepared_dirty := true
144 for iter in 0 .. 64 {
145 spec_count := t.monomorphized_specs.len
146 generic_count := t.generic_types_spec_count()
147 struct_count := t.generic_struct_specs.len
148 ts_start := sw.elapsed().milliseconds()
149 if prepared_dirty {
150 t.collect_generic_call_specs_from_flat(flat, extra_stmts)
151 }
152 ts_collect1 := sw.elapsed().milliseconds()
153 before_mono := t.monomorphized_specs.len
154 t.monomorphize_pass_from_flat(flat, mut extra_stmts)
155 ts_mono := sw.elapsed().milliseconds()
156 if t.monomorphized_specs.len != before_mono {
157 t.collect_generic_call_specs_in_new_clones_from_flat(flat, extra_stmts)
158 }
159 ts_collect2 := sw.elapsed().milliseconds()
160 t.inject_generic_struct_specializations_from_flat(flat, mut extra_stmts)
161 ts_inject := sw.elapsed().milliseconds()
162 if t.inject_changed_files {
163 t.collect_generic_call_specs_in_new_structs_from_flat(flat, extra_stmts)
164 }
165 ts_collect3 := sw.elapsed().milliseconds()
166 prepared_dirty = false
167 if timing {
168 eprintln(' [ttime] iter ${iter}: collect1=${ts_collect1 - ts_start}ms mono_pass=${ts_mono - ts_collect1}ms collect2=${ts_collect2 - ts_mono}ms inject=${ts_inject - ts_collect2}ms collect3=${ts_collect3 - ts_inject}ms files=${flat.files.len}')
169 }
170 t_print_mem('monomorphize iter ${iter}')
171 if t.monomorphized_specs.len == spec_count && t.generic_types_spec_count() == generic_count
172 && t.generic_struct_specs.len == struct_count {
173 break
174 }
175 }
176 if dump_path := os.getenv_opt('V2_TDUMP') {
177 t.dump_flat_monomorphize_specs(dump_path, flat, extra_stmts)
178 }
179 t.collect_struct_default_decl_infos_from_flat(flat, extra_stmts)
180 t.collect_concrete_embedded_owner_names_from_flat(flat, extra_stmts)
181 return extra_stmts
182}
183
184// dump_monomorphize_specs writes a deterministic snapshot of the fixpoint's
185// result (all monomorphized fn spec keys, generic struct spec keys, generic
186// binding signatures, and per-file appended-stmt counts) to `path`. Used only
187// for correctness validation: the file must be byte-identical before and after
188// any optimization to the fixpoint loop. Gated on V2_TDUMP.
189fn (t &Transformer) dump_monomorphize_specs(path string, prepared []ast.File) {
190 mut lines := []string{}
191 mut mspecs := t.monomorphized_specs.keys()
192 mspecs.sort()
193 lines << '# monomorphized_specs (${mspecs.len})'
194 for k in mspecs {
195 lines << 'M ${k}'
196 }
197 mut sspecs := t.generic_struct_specs.keys()
198 sspecs.sort()
199 lines << '# generic_struct_specs (${sspecs.len})'
200 for k in sspecs {
201 lines << 'S ${k}'
202 }
203 mut gkeys := t.env.generic_types.keys()
204 gkeys.sort()
205 lines << '# generic_types'
206 for k in gkeys {
207 blist := t.env.generic_types[k] or { continue }
208 mut sigs := []string{}
209 for b in blist {
210 sigs << generic_bindings_signature(b)
211 }
212 sigs.sort()
213 for sig in sigs {
214 lines << 'G ${k} :: ${sig}'
215 }
216 }
217 // Per-file fingerprint: file name + stmt count, in input order.
218 lines << '# files (${prepared.len})'
219 for f in prepared {
220 lines << 'F ${f.mod}/${f.name} stmts=${f.stmts.len}'
221 }
222 os.write_file(path, lines.join('\n')) or {}
223}
224
225fn (t &Transformer) dump_flat_monomorphize_specs(path string, flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
226 mut lines := []string{}
227 mut mspecs := t.monomorphized_specs.keys()
228 mspecs.sort()
229 lines << '# monomorphized_specs (${mspecs.len})'
230 for k in mspecs {
231 lines << 'M ${k}'
232 }
233 mut sspecs := t.generic_struct_specs.keys()
234 sspecs.sort()
235 lines << '# generic_struct_specs (${sspecs.len})'
236 for k in sspecs {
237 lines << 'S ${k}'
238 }
239 mut gkeys := t.env.generic_types.keys()
240 gkeys.sort()
241 lines << '# generic_types'
242 for k in gkeys {
243 blist := t.env.generic_types[k] or { continue }
244 mut sigs := []string{}
245 for b in blist {
246 sigs << generic_bindings_signature(b)
247 }
248 sigs.sort()
249 for sig in sigs {
250 lines << 'G ${k} :: ${sig}'
251 }
252 }
253 lines << '# files (${flat.files.len})'
254 for i, ff in flat.files {
255 extra := extra_stmts[i] or { []ast.Stmt{} }
256 stmt_count := flat_file_stmt_count(flat, i) + extra.len
257 lines << 'F ${flat.file_mod(ff)}/${flat.file_name(ff)} stmts=${stmt_count}'
258 }
259 os.write_file(path, lines.join('\n')) or {}
260}
261
262fn flat_file_stmt_count(flat &ast.FlatAst, fi int) int {
263 if fi < 0 || fi >= flat.files.len {
264 return 0
265 }
266 return flat.file_cursor(fi).stmts().len()
267}
268
269fn (t &Transformer) generic_types_spec_count() int {
270 mut count := 0
271 for _, bindings_list in t.env.generic_types {
272 count += bindings_list.len
273 }
274 return count
275}
276
277fn (mut t Transformer) collect_declared_method_fns(files []ast.File) {
278 t.declared_method_fns = map[string]bool{}
279 for file in files {
280 for stmt in file.stmts {
281 if stmt is ast.FnDecl && stmt.is_method {
282 t.register_declared_method_fn(stmt, file.mod)
283 }
284 }
285 }
286}
287
288fn (mut t Transformer) collect_declared_method_fns_from_flat(flat &ast.FlatAst) {
289 t.declared_method_fns = map[string]bool{}
290 for fi in 0 .. flat.files.len {
291 module_name := flat.file_mod(flat.files[fi])
292 stmts := flat.file_cursor(fi).stmts()
293 for si in 0 .. stmts.len() {
294 c := stmts.at(si)
295 if c.kind() != .stmt_fn_decl || !c.flag(ast.flag_is_method) {
296 continue
297 }
298 t.register_declared_method_fn_cursor(c, module_name)
299 }
300 }
301}
302
303fn (mut t Transformer) register_declared_method_fn(decl ast.FnDecl, module_name string) {
304 recv_name := t.get_receiver_type_name(decl.receiver.typ)
305 if recv_name == '' || decl.name == '' {
306 return
307 }
308 t.declared_method_fns['${recv_name}__${decl.name}'] = true
309 if module_name != '' && module_name != 'builtin' && !recv_name.contains('__') {
310 qualified := '${module_name.replace('.', '__')}__${recv_name}__${decl.name}'
311 t.declared_method_fns[qualified] = true
312 call_prefix := module_call_c_prefix(module_name)
313 if call_prefix != '' && call_prefix != module_name.replace('.', '__') {
314 t.declared_method_fns['${call_prefix}__${recv_name}__${decl.name}'] = true
315 }
316 }
317}
318
319fn (mut t Transformer) register_declared_method_fn_cursor(decl ast.Cursor, module_name string) {
320 recv := decl.edge(0)
321 recv_name := t.get_receiver_type_name_cursor(recv.edge(0))
322 if recv_name == '' || decl.name() == '' {
323 return
324 }
325 t.declared_method_fns['${recv_name}__${decl.name()}'] = true
326 if module_name != '' && module_name != 'builtin' && !recv_name.contains('__') {
327 qualified := '${module_name.replace('.', '__')}__${recv_name}__${decl.name()}'
328 t.declared_method_fns[qualified] = true
329 call_prefix := module_call_c_prefix(module_name)
330 if call_prefix != '' && call_prefix != module_name.replace('.', '__') {
331 t.declared_method_fns['${call_prefix}__${recv_name}__${decl.name()}'] = true
332 }
333 }
334}
335
336fn (mut t Transformer) collect_struct_field_generic_decl_types(files []ast.File) {
337 old_module := t.cur_module
338 old_scope := t.scope
339 t.struct_field_generic_decl_types = map[string]types.Type{}
340 t.struct_field_generic_decl_bindings = map[string]map[string]types.Type{}
341 for file in files {
342 t.cur_module = file.mod
343 if scope := t.get_module_scope(file.mod) {
344 t.scope = scope
345 } else {
346 t.scope = unsafe { nil }
347 }
348 for stmt in file.stmts {
349 if stmt is ast.StructDecl {
350 t.collect_struct_decl_generic_field_types(stmt, file.mod)
351 }
352 }
353 }
354 t.cur_module = old_module
355 t.scope = old_scope
356}
357
358fn (mut t Transformer) collect_struct_field_generic_decl_types_from_flat(flat &ast.FlatAst) {
359 old_module := t.cur_module
360 old_scope := t.scope
361 t.struct_field_generic_decl_types = map[string]types.Type{}
362 t.struct_field_generic_decl_bindings = map[string]map[string]types.Type{}
363 for fi in 0 .. flat.files.len {
364 module_name := flat.file_mod(flat.files[fi])
365 t.cur_module = module_name
366 if scope := t.get_module_scope(module_name) {
367 t.scope = scope
368 } else {
369 t.scope = unsafe { nil }
370 }
371 stmts := flat.file_cursor(fi).stmts()
372 for si in 0 .. stmts.len() {
373 c := stmts.at(si)
374 if c.kind() != .stmt_struct_decl {
375 continue
376 }
377 t.collect_struct_decl_generic_field_types_cursor(c, module_name)
378 }
379 }
380 t.cur_module = old_module
381 t.scope = old_scope
382}
383
384fn (mut t Transformer) collect_struct_decl_generic_field_types(decl ast.StructDecl, module_name string) {
385 parent_names := struct_decl_field_lookup_names(decl.name, module_name)
386 if parent_names.len == 0 {
387 return
388 }
389 for field in decl.fields {
390 if field.name == '' || !field_type_expr_has_generic_args(field.typ) {
391 continue
392 }
393 field_type := t.lookup_type_from_expr(field.typ) or { continue }
394 if !types.type_has_valid_payload(field_type) {
395 continue
396 }
397 for parent_name in parent_names {
398 t.struct_field_generic_decl_types[struct_field_generic_decl_key(parent_name, field.name)] = field_type
399 if bindings := t.generic_bindings_from_type_expr(field.typ) {
400 t.struct_field_generic_decl_bindings[struct_field_generic_decl_key(parent_name,
401 field.name)] = bindings.clone()
402 }
403 }
404 }
405 for embedded in decl.embedded {
406 if !field_type_expr_has_generic_args(embedded) {
407 continue
408 }
409 field_name := embedded_field_name_from_type_expr(embedded)
410 if field_name == '' {
411 continue
412 }
413 field_type := t.lookup_type_from_expr(embedded) or { continue }
414 if !types.type_has_valid_payload(field_type) {
415 continue
416 }
417 for parent_name in parent_names {
418 t.struct_field_generic_decl_types[struct_field_generic_decl_key(parent_name, field_name)] = field_type
419 if bindings := t.generic_bindings_from_type_expr(embedded) {
420 t.struct_field_generic_decl_bindings[struct_field_generic_decl_key(parent_name,
421 field_name)] = bindings.clone()
422 }
423 }
424 }
425}
426
427fn (mut t Transformer) collect_struct_decl_generic_field_types_cursor(decl ast.Cursor, module_name string) {
428 parent_names := struct_decl_field_lookup_names(decl.name(), module_name)
429 if parent_names.len == 0 {
430 return
431 }
432 fields := decl.list_at(4)
433 for i in 0 .. fields.len() {
434 field := fields.at(i)
435 field_name := field.name()
436 if field_name == '' {
437 continue
438 }
439 field_type_expr := field.edge(0)
440 if !field_type_cursor_has_generic_args(field_type_expr) {
441 continue
442 }
443 field_type := t.lookup_type_from_expr_cursor(field_type_expr) or { continue }
444 if !types.type_has_valid_payload(field_type) {
445 continue
446 }
447 for parent_name in parent_names {
448 t.struct_field_generic_decl_types[struct_field_generic_decl_key(parent_name, field_name)] = field_type
449 if bindings := t.generic_bindings_from_type_expr_cursor(field_type_expr) {
450 t.struct_field_generic_decl_bindings[struct_field_generic_decl_key(parent_name,
451 field_name)] = bindings.clone()
452 }
453 }
454 }
455 embedded := decl.list_at(2)
456 for i in 0 .. embedded.len() {
457 embedded_expr := embedded.at(i)
458 if !field_type_cursor_has_generic_args(embedded_expr) {
459 continue
460 }
461 field_name := embedded_field_name_from_type_expr_cursor(embedded_expr)
462 if field_name == '' {
463 continue
464 }
465 field_type := t.lookup_type_from_expr_cursor(embedded_expr) or { continue }
466 if !types.type_has_valid_payload(field_type) {
467 continue
468 }
469 for parent_name in parent_names {
470 t.struct_field_generic_decl_types[struct_field_generic_decl_key(parent_name, field_name)] = field_type
471 if bindings := t.generic_bindings_from_type_expr_cursor(embedded_expr) {
472 t.struct_field_generic_decl_bindings[struct_field_generic_decl_key(parent_name,
473 field_name)] = bindings.clone()
474 }
475 }
476 }
477}
478
479fn (mut t Transformer) collect_concrete_embedded_owner_names(files []ast.File) {
480 old_module := t.cur_module
481 old_file := t.cur_file_name
482 old_scope := t.scope
483 old_import_aliases := t.cur_import_aliases.clone()
484 t.concrete_embedded_owner_names = map[string]map[string]string{}
485 for file in files {
486 t.cur_file_name = file.name
487 t.cur_module = file.mod
488 t.cur_import_aliases = import_aliases_for_generic_collect(file.imports)
489 if scope := t.get_module_scope(file.mod) {
490 t.scope = scope
491 } else {
492 t.scope = unsafe { nil }
493 }
494 for stmt in file.stmts {
495 if stmt is ast.StructDecl {
496 mut embedded := []ast.Expr{cap: stmt.embedded.len}
497 for item in stmt.embedded {
498 embedded << t.rewrite_concrete_generic_struct_type_expr(item)
499 }
500 t.register_concrete_embedded_owner_names(stmt, embedded)
501 }
502 }
503 }
504 t.cur_module = old_module
505 t.cur_file_name = old_file
506 t.scope = old_scope
507 t.cur_import_aliases = old_import_aliases.clone()
508}
509
510fn (mut t Transformer) collect_concrete_embedded_owner_names_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
511 old_module := t.cur_module
512 old_file := t.cur_file_name
513 old_scope := t.scope
514 old_import_aliases := t.cur_import_aliases.clone()
515 t.concrete_embedded_owner_names = map[string]map[string]string{}
516 for fi in 0 .. flat.files.len {
517 module_name := flat.file_mod(flat.files[fi])
518 t.cur_file_name = flat.file_name(flat.files[fi])
519 t.cur_module = module_name
520 t.cur_import_aliases = flat_import_aliases_for_generic_collect(flat, fi)
521 if scope := t.get_module_scope(module_name) {
522 t.scope = scope
523 } else {
524 t.scope = unsafe { nil }
525 }
526 for stmt in flat_file_struct_decls_with_extra(flat, extra_stmts, fi) {
527 mut embedded := []ast.Expr{cap: stmt.embedded.len}
528 for item in stmt.embedded {
529 embedded << t.rewrite_concrete_generic_struct_type_expr(item)
530 }
531 t.register_concrete_embedded_owner_names(stmt, embedded)
532 }
533 }
534 t.cur_module = old_module
535 t.cur_file_name = old_file
536 t.scope = old_scope
537 t.cur_import_aliases = old_import_aliases.clone()
538}
539
540fn (mut t Transformer) collect_struct_default_decl_infos(files []ast.File) {
541 t.struct_default_decl_infos = map[string]StructDefaultDeclInfo{}
542 for file in files {
543 for stmt in file.stmts {
544 if stmt is ast.StructDecl {
545 info := StructDefaultDeclInfo{
546 decl: stmt
547 module_name: file.mod
548 }
549 c_name := generic_struct_decl_c_name(stmt, file.mod)
550 t.struct_default_decl_infos[c_name] = info
551 if c_name.contains('__') {
552 t.struct_default_decl_infos[c_name.all_after_last('__')] = info
553 }
554 t.struct_default_decl_infos[stmt.name] = info
555 }
556 }
557 }
558}
559
560fn (mut t Transformer) collect_struct_default_decl_infos_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
561 t.struct_default_decl_infos = map[string]StructDefaultDeclInfo{}
562 for fi in 0 .. flat.files.len {
563 module_name := flat.file_mod(flat.files[fi])
564 for stmt in flat_file_struct_decls_with_extra(flat, extra_stmts, fi) {
565 info := StructDefaultDeclInfo{
566 decl: stmt
567 module_name: module_name
568 }
569 c_name := generic_struct_decl_c_name(stmt, module_name)
570 t.struct_default_decl_infos[c_name] = info
571 if c_name.contains('__') {
572 t.struct_default_decl_infos[c_name.all_after_last('__')] = info
573 }
574 t.struct_default_decl_infos[stmt.name] = info
575 }
576 }
577}
578
579fn generic_struct_runtime_args(args []ast.Expr) []ast.Expr {
580 mut out := []ast.Expr{cap: args.len}
581 for arg in args {
582 if arg is ast.LifetimeExpr {
583 continue
584 }
585 out << arg
586 }
587 return out
588}
589
590fn generic_struct_module_from_c_name(c_name string, fallback string) string {
591 if c_name.contains('__') {
592 return c_name.all_before_last('__')
593 }
594 return fallback
595}
596
597fn generic_struct_short_name_from_c_name(c_name string) string {
598 if c_name.contains('__') {
599 return c_name.all_after_last('__')
600 }
601 return c_name
602}
603
604fn generic_struct_decl_c_name(decl ast.StructDecl, module_name string) string {
605 name := decl.name.replace('.', '__')
606 if module_name != '' && module_name != 'main' && module_name != 'builtin'
607 && !name.contains('__') {
608 return '${module_name.replace('.', '__')}__${name}'
609 }
610 return name
611}
612
613fn (mut t Transformer) collect_generic_struct_spec_from_type_expr(expr ast.Expr) {
614 mut lhs := ast.empty_expr
615 mut args := []ast.Expr{}
616 match expr {
617 ast.GenericArgs {
618 lhs = expr.lhs
619 args = expr.args.clone()
620 }
621 ast.GenericArgOrIndexExpr {
622 lhs = expr.lhs
623 args = [expr.expr]
624 }
625 ast.IndexExpr {
626 lhs = expr.lhs
627 args = [expr.expr]
628 }
629 ast.Type {
630 if expr is ast.GenericType {
631 lhs = expr.name
632 args = expr.params.clone()
633 } else {
634 return
635 }
636 }
637 ast.ModifierExpr {
638 t.collect_generic_struct_spec_from_type_expr(expr.expr)
639 return
640 }
641 ast.PrefixExpr {
642 t.collect_generic_struct_spec_from_type_expr(expr.expr)
643 return
644 }
645 else {
646 return
647 }
648 }
649
650 t.register_generic_struct_spec(lhs, args)
651}
652
653fn (mut t Transformer) collect_generic_struct_spec_from_type_expr_cursor(expr ast.Cursor) {
654 if !expr.is_valid() {
655 return
656 }
657 mut lhs := ast.Cursor{}
658 mut args := []ast.Cursor{}
659 match expr.kind() {
660 .expr_generic_args {
661 lhs = expr.edge(0)
662 for i in 1 .. expr.edge_count() {
663 args << expr.edge(i)
664 }
665 }
666 .expr_generic_arg_or_index, .expr_index {
667 lhs = expr.edge(0)
668 args = [expr.edge(1)]
669 }
670 .typ_generic {
671 lhs = expr.edge(0)
672 for i in 1 .. expr.edge_count() {
673 args << expr.edge(i)
674 }
675 }
676 .expr_modifier, .expr_prefix {
677 t.collect_generic_struct_spec_from_type_expr_cursor(expr.edge(0))
678 return
679 }
680 else {
681 return
682 }
683 }
684
685 t.register_generic_struct_spec_cursor(lhs, args)
686}
687
688fn (mut t Transformer) register_generic_struct_spec(lhs ast.Expr, args []ast.Expr) {
689 runtime_args := generic_struct_runtime_args(args)
690 if runtime_args.len == 0 {
691 return
692 }
693 base_type := t.lookup_type_from_expr(lhs) or { return }
694 if base_type !is types.Struct {
695 return
696 }
697 base_struct := base_type as types.Struct
698 if base_struct.generic_params.len == 0 {
699 return
700 }
701 bindings := t.generic_type_arg_bindings(base_struct.generic_params, runtime_args) or { return }
702 if bindings.len != base_struct.generic_params.len {
703 return
704 }
705 for _, concrete in bindings {
706 if clone_type_contains_generic_placeholder(concrete) {
707 return
708 }
709 }
710 base_c_name := t.type_to_c_name(types.Type(base_struct))
711 suffix := t.generic_specialization_suffix(runtime_args)
712 if base_c_name == '' || suffix == '' || !suffix.starts_with('_T_') {
713 return
714 }
715 concrete_c_name := base_c_name + suffix
716 if concrete_c_name in t.generic_struct_specs {
717 return
718 }
719 module_name := generic_struct_module_from_c_name(base_c_name, t.cur_module)
720 file_idx := if t.generic_bindings_visible_in_module(module_name, bindings) {
721 -1
722 } else {
723 t.cur_generic_call_file_idx
724 }
725 spec := GenericStructSpec{
726 base_c_name: base_c_name
727 concrete_c_name: concrete_c_name
728 concrete_short_name: generic_struct_short_name_from_c_name(concrete_c_name)
729 module_name: module_name
730 file_idx: file_idx
731 bindings: bindings.clone()
732 }
733 t.generic_struct_specs[concrete_c_name] = spec
734 t.register_generic_struct_env_type(base_struct, spec)
735}
736
737fn (mut t Transformer) register_generic_struct_spec_cursor(lhs ast.Cursor, args []ast.Cursor) {
738 runtime_args := generic_struct_runtime_arg_cursors(args)
739 if runtime_args.len == 0 {
740 return
741 }
742 base_type := t.lookup_type_from_expr_cursor(lhs) or { return }
743 if base_type !is types.Struct {
744 return
745 }
746 base_struct := base_type as types.Struct
747 if base_struct.generic_params.len == 0 {
748 return
749 }
750 arg_bindings := t.generic_type_arg_bindings_cursor(base_struct.generic_params, runtime_args) or {
751 return
752 }
753 bindings := arg_bindings.bindings.clone()
754 if bindings.len != base_struct.generic_params.len {
755 return
756 }
757 for _, concrete in bindings {
758 if clone_type_contains_generic_placeholder(concrete) {
759 return
760 }
761 }
762 base_c_name := t.type_to_c_name(types.Type(base_struct))
763 suffix := t.generic_specialization_suffix_from_types(arg_bindings.concrete_args)
764 if base_c_name == '' || suffix == '' || !suffix.starts_with('_T_') {
765 return
766 }
767 concrete_c_name := base_c_name + suffix
768 if concrete_c_name in t.generic_struct_specs {
769 return
770 }
771 module_name := generic_struct_module_from_c_name(base_c_name, t.cur_module)
772 file_idx := if t.generic_bindings_visible_in_module(module_name, bindings) {
773 -1
774 } else {
775 t.cur_generic_call_file_idx
776 }
777 spec := GenericStructSpec{
778 base_c_name: base_c_name
779 concrete_c_name: concrete_c_name
780 concrete_short_name: generic_struct_short_name_from_c_name(concrete_c_name)
781 module_name: module_name
782 file_idx: file_idx
783 bindings: bindings.clone()
784 }
785 t.generic_struct_specs[concrete_c_name] = spec
786 t.register_generic_struct_env_type(base_struct, spec)
787}
788
789fn generic_struct_runtime_arg_cursors(args []ast.Cursor) []ast.Cursor {
790 mut out := []ast.Cursor{cap: args.len}
791 for arg in args {
792 if arg.kind() == .expr_lifetime {
793 continue
794 }
795 out << arg
796 }
797 return out
798}
799
800fn (t &Transformer) generic_type_arg_bindings_cursor(generic_params []string, args []ast.Cursor) ?GenericTypeArgCursorBindings {
801 if generic_params.len == 0 || args.len == 0 {
802 return none
803 }
804 mut bindings := map[string]types.Type{}
805 mut concrete_args := []types.Type{cap: args.len}
806 for i, param_name in generic_params {
807 if i >= args.len {
808 break
809 }
810 arg := args[i]
811 mut found := false
812 mut concrete := types.Type(types.void_)
813 if arg.kind() == .expr_ident {
814 if bound_type := t.cur_monomorphized_fn_bindings[arg.name()] {
815 concrete = t.qualify_generic_concrete_type_from_cursor(bound_type, arg)
816 found = true
817 }
818 }
819 if !found {
820 if synth_type := t.get_synth_type(arg.pos()) {
821 concrete = t.qualify_generic_concrete_type_from_cursor(synth_type, arg)
822 found = true
823 }
824 }
825 if !found {
826 if typ := t.lookup_type_from_expr_cursor(arg) {
827 concrete = t.qualify_generic_concrete_type_from_cursor(typ, arg)
828 found = true
829 }
830 }
831 if !found {
832 if typ := t.get_expr_type_cursor(arg) {
833 concrete = t.qualify_generic_concrete_type_from_cursor(typ, arg)
834 found = true
835 }
836 }
837 if found {
838 bindings[param_name] = concrete
839 concrete_args << concrete
840 }
841 }
842 if bindings.len == 0 {
843 return none
844 }
845 return GenericTypeArgCursorBindings{
846 bindings: bindings
847 concrete_args: concrete_args
848 }
849}
850
851fn (t &Transformer) generic_specialization_suffix_from_types(args []types.Type) string {
852 if args.len == 0 {
853 return ''
854 }
855 mut parts := []string{cap: args.len}
856 for arg in args {
857 parts << t.generic_specialization_token_from_type(arg)
858 }
859 return '_T_' + parts.join('_')
860}
861
862fn (t &Transformer) lookup_type_from_expr_cursor(expr ast.Cursor) ?types.Type {
863 if !expr.is_valid() || expr.kind() == .expr_empty {
864 return none
865 }
866 if typ := t.get_synth_type(expr.pos()) {
867 return typ
868 }
869 match expr.kind() {
870 .expr_ident {
871 name := expr.name()
872 if typ := t.lookup_type(name) {
873 return typ
874 }
875 if typ := t.c_name_to_type(name) {
876 return typ
877 }
878 c_name := t.v_type_name_to_c_name(name)
879 if c_name != '' && c_name != name {
880 if typ := t.c_name_to_type(c_name) {
881 return typ
882 }
883 }
884 }
885 .expr_selector {
886 lhs := expr.edge(0)
887 rhs_name := selector_rhs_name_cursor(expr)
888 if lhs.kind() == .expr_ident && rhs_name != '' {
889 lhs_name := lhs.name()
890 mut module_names := []string{}
891 if full_name := t.cur_import_aliases[lhs_name] {
892 module_names << module_call_c_prefix(full_name)
893 module_names << full_name.replace('.', '__')
894 }
895 if prefix := t.resolve_module_call_prefix(lhs_name) {
896 module_names << prefix
897 }
898 module_names << lhs_name.replace('.', '__')
899 mut seen := map[string]bool{}
900 for module_name in module_names {
901 if module_name == '' || module_name in seen {
902 continue
903 }
904 seen[module_name] = true
905 qualified := '${module_name}__${rhs_name}'
906 if typ := t.lookup_type(qualified) {
907 return typ
908 }
909 }
910 return t.lookup_type(rhs_name)
911 }
912 }
913 .expr_prefix {
914 op := unsafe { token.Token(int(expr.aux())) }
915 if op == .amp {
916 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
917 return types.Type(types.Pointer{
918 base_type: base
919 })
920 }
921 }
922 .expr_modifier {
923 return t.lookup_type_from_expr_cursor(expr.edge(0))
924 }
925 .expr_generic_args {
926 return t.lookup_generic_type_from_expr_cursor(expr.edge(0), cursor_edges(expr, 1))
927 }
928 .expr_generic_arg_or_index, .expr_index {
929 return t.lookup_generic_type_from_expr_cursor(expr.edge(0), [
930 expr.edge(1),
931 ])
932 }
933 .typ_pointer {
934 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
935 return types.Type(types.Pointer{
936 base_type: base
937 })
938 }
939 .typ_option {
940 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
941 return types.Type(types.OptionType{
942 base_type: base
943 })
944 }
945 .typ_result {
946 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
947 return types.Type(types.ResultType{
948 base_type: base
949 })
950 }
951 .typ_map {
952 key := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
953 value := t.lookup_type_from_expr_cursor(expr.edge(1)) or { return none }
954 return types.Type(types.Map{
955 key_type: key
956 value_type: value
957 })
958 }
959 .typ_array {
960 elem := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
961 return types.Type(types.Array{
962 elem_type: elem
963 })
964 }
965 .typ_array_fixed {
966 len_expr := expr.edge(0)
967 mut len := 0
968 if len_expr.kind() == .expr_basic_literal {
969 len = len_expr.name().int()
970 }
971 elem := t.lookup_type_from_expr_cursor(expr.edge(1)) or { return none }
972 return types.Type(types.ArrayFixed{
973 len: len
974 elem_type: elem
975 })
976 }
977 .typ_generic {
978 return t.lookup_generic_type_from_expr_cursor(expr.edge(0), cursor_edges(expr, 1))
979 }
980 else {}
981 }
982
983 return none
984}
985
986fn cursor_edges(expr ast.Cursor, start int) []ast.Cursor {
987 mut out := []ast.Cursor{cap: expr.edge_count() - start}
988 for i in start .. expr.edge_count() {
989 out << expr.edge(i)
990 }
991 return out
992}
993
994fn (t &Transformer) lookup_generic_type_from_expr_cursor(lhs ast.Cursor, args []ast.Cursor) ?types.Type {
995 base := t.lookup_type_from_expr_cursor(lhs) or { return none }
996 if base is types.Struct {
997 arg_bindings := t.generic_type_arg_bindings_cursor(base.generic_params, args) or {
998 return base
999 }
1000 return substitute_type(base, arg_bindings.bindings)
1001 }
1002 return base
1003}
1004
1005fn (t &Transformer) qualify_generic_concrete_type_from_cursor(concrete types.Type, arg ast.Cursor) types.Type {
1006 match concrete {
1007 types.Struct {
1008 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1009 return types.Type(types.Struct{
1010 name: name
1011 generic_params: concrete.generic_params
1012 implements: concrete.implements
1013 embedded: concrete.embedded
1014 fields: concrete.fields
1015 is_soa: concrete.is_soa
1016 })
1017 }
1018 }
1019 types.Enum {
1020 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1021 return types.Type(types.Enum{
1022 is_flag: concrete.is_flag
1023 name: name
1024 fields: concrete.fields
1025 })
1026 }
1027 }
1028 types.Interface {
1029 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1030 return types.Type(types.Interface{
1031 name: name
1032 fields: concrete.fields
1033 })
1034 }
1035 }
1036 types.SumType {
1037 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1038 return types.Type(types.SumType{
1039 name: name
1040 generic_params: concrete.generic_params
1041 variants: concrete.variants
1042 })
1043 }
1044 }
1045 types.Alias {
1046 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1047 return types.Type(types.Alias{
1048 name: name
1049 base_type: concrete.base_type
1050 })
1051 }
1052 }
1053 types.NamedType {
1054 if name := t.generic_concrete_type_arg_c_name_cursor(types.Type(concrete), arg) {
1055 return types.Type(types.NamedType(name))
1056 }
1057 }
1058 types.Pointer {
1059 if base_arg := pointer_generic_type_arg_base_cursor(arg) {
1060 return types.Type(types.Pointer{
1061 lifetime: concrete.lifetime
1062 base_type: t.qualify_generic_concrete_type_from_cursor(concrete.base_type,
1063 base_arg)
1064 })
1065 }
1066 }
1067 types.Array {
1068 if elem_arg := array_generic_type_arg_elem_cursor(arg) {
1069 return types.Type(types.Array{
1070 elem_type: t.qualify_generic_concrete_type_from_cursor(concrete.elem_type,
1071 elem_arg)
1072 })
1073 }
1074 }
1075 types.ArrayFixed {
1076 if elem_arg := array_fixed_generic_type_arg_elem_cursor(arg) {
1077 return types.Type(types.ArrayFixed{
1078 len: concrete.len
1079 elem_type: t.qualify_generic_concrete_type_from_cursor(concrete.elem_type,
1080 elem_arg)
1081 })
1082 }
1083 }
1084 types.Map {
1085 if parts := map_generic_type_arg_parts_cursor(arg) {
1086 return types.Type(types.Map{
1087 key_type: t.qualify_generic_concrete_type_from_cursor(concrete.key_type,
1088 parts.key)
1089 value_type: t.qualify_generic_concrete_type_from_cursor(concrete.value_type,
1090 parts.value)
1091 })
1092 }
1093 }
1094 types.OptionType {
1095 if base_arg := option_generic_type_arg_base_cursor(arg) {
1096 return types.Type(types.OptionType{
1097 base_type: t.qualify_generic_concrete_type_from_cursor(concrete.base_type,
1098 base_arg)
1099 })
1100 }
1101 }
1102 types.ResultType {
1103 if base_arg := result_generic_type_arg_base_cursor(arg) {
1104 return types.Type(types.ResultType{
1105 base_type: t.qualify_generic_concrete_type_from_cursor(concrete.base_type,
1106 base_arg)
1107 })
1108 }
1109 }
1110 else {}
1111 }
1112
1113 return concrete
1114}
1115
1116fn (t &Transformer) generic_concrete_type_arg_c_name_cursor(concrete types.Type, arg ast.Cursor) ?string {
1117 if arg.kind() == .expr_ident {
1118 name := arg.name()
1119 if bound_type := t.cur_monomorphized_fn_bindings[name] {
1120 c_name := t.type_to_c_name(bound_type)
1121 if c_name != '' {
1122 return c_name
1123 }
1124 }
1125 if name.contains('__') {
1126 return name
1127 }
1128 if concrete.name() == name {
1129 return t.generic_ident_type_arg_c_name(name)
1130 }
1131 }
1132 if arg.kind() == .typ_generic {
1133 base_name := t.expr_to_type_name_cursor_direct(arg.edge(0))
1134 suffix := t.generic_specialization_suffix_from_type_cursors(cursor_edges(arg, 1))
1135 if base_name != '' && suffix != '' {
1136 return base_name + suffix
1137 }
1138 }
1139 if name := t.generic_init_type_name_cursor_direct(arg) {
1140 return name
1141 }
1142 name := t.expr_to_type_name_cursor_direct(arg)
1143 if name == '' {
1144 return none
1145 }
1146 return name
1147}
1148
1149fn (t &Transformer) generic_specialization_suffix_from_type_cursors(args []ast.Cursor) string {
1150 runtime_args := generic_struct_runtime_arg_cursors(args)
1151 if runtime_args.len == 0 {
1152 return ''
1153 }
1154 arg_bindings := t.generic_type_arg_bindings_cursor(runtime_arg_names(runtime_args),
1155 runtime_args) or { return '' }
1156 if arg_bindings.concrete_args.len != runtime_args.len {
1157 return ''
1158 }
1159 for concrete in arg_bindings.concrete_args {
1160 if clone_type_contains_generic_placeholder(concrete) {
1161 return ''
1162 }
1163 }
1164 return t.generic_specialization_suffix_from_types(arg_bindings.concrete_args)
1165}
1166
1167fn runtime_arg_names(args []ast.Cursor) []string {
1168 mut names := []string{cap: args.len}
1169 for i in 0 .. args.len {
1170 names << 'T${i}'
1171 }
1172 return names
1173}
1174
1175fn (t &Transformer) generic_init_type_name_cursor_direct(expr ast.Cursor) ?string {
1176 match expr.kind() {
1177 .expr_generic_args {
1178 base_name := t.expr_to_type_name_cursor_direct(expr.edge(0))
1179 suffix := t.generic_specialization_suffix_from_type_cursors(cursor_edges(expr, 1))
1180 if base_name != '' && suffix != '' {
1181 return base_name + suffix
1182 }
1183 }
1184 .expr_generic_arg_or_index, .expr_index {
1185 base_name := t.expr_to_type_name_cursor_direct(expr.edge(0))
1186 suffix := t.generic_specialization_suffix_from_type_cursors([
1187 expr.edge(1),
1188 ])
1189 if base_name != '' && suffix != '' {
1190 return base_name + suffix
1191 }
1192 }
1193 else {}
1194 }
1195
1196 return none
1197}
1198
1199fn (t &Transformer) expr_to_type_name_cursor_direct(expr ast.Cursor) string {
1200 match expr.kind() {
1201 .expr_ident {
1202 if typ := t.get_synth_type(expr.pos()) {
1203 c_name := t.type_to_c_name(typ)
1204 if c_name != '' {
1205 return c_name
1206 }
1207 }
1208 return expr.name()
1209 }
1210 .expr_selector {
1211 return selector_type_name_cursor(expr, true)
1212 }
1213 .typ_array, .typ_array_fixed, .typ_map, .typ_option, .typ_pointer, .typ_result,
1214 .typ_generic {
1215 if typ := t.lookup_type_from_expr_cursor(expr) {
1216 c_name := t.type_to_c_name(typ)
1217 if c_name != '' {
1218 return c_name
1219 }
1220 }
1221 }
1222 else {}
1223 }
1224
1225 return ''
1226}
1227
1228fn pointer_generic_type_arg_base_cursor(arg ast.Cursor) ?ast.Cursor {
1229 match arg.kind() {
1230 .expr_prefix {
1231 op := unsafe { token.Token(int(arg.aux())) }
1232 if op in [.amp, .mul] {
1233 return arg.edge(0)
1234 }
1235 }
1236 .typ_pointer {
1237 return arg.edge(0)
1238 }
1239 else {}
1240 }
1241
1242 return none
1243}
1244
1245fn array_generic_type_arg_elem_cursor(arg ast.Cursor) ?ast.Cursor {
1246 if arg.kind() == .typ_array {
1247 return arg.edge(0)
1248 }
1249 return none
1250}
1251
1252fn array_fixed_generic_type_arg_elem_cursor(arg ast.Cursor) ?ast.Cursor {
1253 if arg.kind() == .typ_array_fixed {
1254 return arg.edge(1)
1255 }
1256 return none
1257}
1258
1259struct GenericMapTypeArgCursorParts {
1260 key ast.Cursor
1261 value ast.Cursor
1262}
1263
1264fn map_generic_type_arg_parts_cursor(arg ast.Cursor) ?GenericMapTypeArgCursorParts {
1265 if arg.kind() == .typ_map {
1266 return GenericMapTypeArgCursorParts{
1267 key: arg.edge(0)
1268 value: arg.edge(1)
1269 }
1270 }
1271 return none
1272}
1273
1274fn option_generic_type_arg_base_cursor(arg ast.Cursor) ?ast.Cursor {
1275 if arg.kind() == .typ_option {
1276 return arg.edge(0)
1277 }
1278 return none
1279}
1280
1281fn result_generic_type_arg_base_cursor(arg ast.Cursor) ?ast.Cursor {
1282 if arg.kind() == .typ_result {
1283 return arg.edge(0)
1284 }
1285 return none
1286}
1287
1288fn (t &Transformer) generic_bindings_visible_in_module(module_name string, bindings map[string]types.Type) bool {
1289 for _, typ in bindings {
1290 if !t.generic_type_visible_in_module(module_name, typ) {
1291 return false
1292 }
1293 }
1294 return true
1295}
1296
1297fn (t &Transformer) generic_type_visible_in_module(module_name string, typ types.Type) bool {
1298 match typ {
1299 types.Primitive, types.String, types.Char, types.Rune, types.Void, types.Nil, types.None,
1300 types.ISize, types.USize {
1301 return true
1302 }
1303 types.Struct {
1304 return t.generic_type_name_visible_in_module(module_name, typ.name)
1305 }
1306 types.Enum {
1307 return t.generic_type_name_visible_in_module(module_name, typ.name)
1308 }
1309 types.Interface {
1310 return t.generic_type_name_visible_in_module(module_name, typ.name)
1311 }
1312 types.SumType {
1313 return t.generic_type_name_visible_in_module(module_name, types.sum_type_name(typ))
1314 }
1315 types.Alias {
1316 return t.generic_type_name_visible_in_module(module_name, typ.name)
1317 }
1318 types.NamedType {
1319 return t.generic_type_name_visible_in_module(module_name, string(typ))
1320 }
1321 types.Pointer {
1322 return t.generic_type_visible_in_module(module_name, typ.base_type)
1323 }
1324 types.Array {
1325 return t.generic_type_visible_in_module(module_name, typ.elem_type)
1326 }
1327 types.ArrayFixed {
1328 return t.generic_type_visible_in_module(module_name, typ.elem_type)
1329 }
1330 types.Map {
1331 return t.generic_type_visible_in_module(module_name, typ.key_type)
1332 && t.generic_type_visible_in_module(module_name, typ.value_type)
1333 }
1334 types.OptionType {
1335 return t.generic_type_visible_in_module(module_name, typ.base_type)
1336 }
1337 types.ResultType {
1338 return t.generic_type_visible_in_module(module_name, typ.base_type)
1339 }
1340 types.FnType {
1341 for param_type in typ.get_param_types() {
1342 if !t.generic_type_visible_in_module(module_name, param_type) {
1343 return false
1344 }
1345 }
1346 if ret_type := typ.get_return_type() {
1347 return t.generic_type_visible_in_module(module_name, ret_type)
1348 }
1349 return true
1350 }
1351 else {
1352 return true
1353 }
1354 }
1355}
1356
1357fn (t &Transformer) generic_type_name_visible_in_module(module_name string, type_name string) bool {
1358 if type_name == '' || t.is_builtin_type_name(type_name) {
1359 return true
1360 }
1361 name := type_name.replace('.', '__')
1362 if name.contains('__') {
1363 mod_prefix := module_name.replace('.', '__')
1364 return name.starts_with('${mod_prefix}__')
1365 }
1366 if module_name == '' || module_name == 'main' {
1367 return true
1368 }
1369 if scope := t.get_module_scope(module_name) {
1370 if obj := scope.objects[name] {
1371 if _ := transformer_object_type(obj) {
1372 return true
1373 }
1374 }
1375 }
1376 return false
1377}
1378
1379fn (mut t Transformer) register_generic_struct_env_type(base_struct types.Struct, spec GenericStructSpec) {
1380 substituted := substitute_type(types.Type(base_struct), spec.bindings)
1381 mut struct_info := if substituted is types.Struct {
1382 substituted as types.Struct
1383 } else {
1384 base_struct
1385 }
1386 concrete_type := types.Type(types.Struct{
1387 name: spec.concrete_c_name
1388 generic_params: []string{}
1389 implements: struct_info.implements
1390 embedded: struct_info.embedded
1391 fields: struct_info.fields
1392 is_soa: struct_info.is_soa
1393 })
1394 if mut scope := t.get_module_scope(spec.module_name) {
1395 scope.insert_type(spec.concrete_short_name, concrete_type)
1396 scope.objects[spec.concrete_short_name] = types.TypeObject{
1397 typ: concrete_type
1398 }
1399 }
1400}
1401
1402fn (t &Transformer) generic_struct_spec_key_from_type_expr(expr ast.Expr) ?string {
1403 mut lhs := ast.empty_expr
1404 mut args := []ast.Expr{}
1405 match expr {
1406 ast.GenericArgs {
1407 lhs = expr.lhs
1408 args = expr.args.clone()
1409 }
1410 ast.GenericArgOrIndexExpr {
1411 lhs = expr.lhs
1412 args = [expr.expr]
1413 }
1414 ast.IndexExpr {
1415 lhs = expr.lhs
1416 args = [expr.expr]
1417 }
1418 ast.Type {
1419 if expr is ast.GenericType {
1420 lhs = expr.name
1421 args = expr.params.clone()
1422 } else {
1423 return none
1424 }
1425 }
1426 ast.ModifierExpr {
1427 return t.generic_struct_spec_key_from_type_expr(expr.expr)
1428 }
1429 ast.PrefixExpr {
1430 return t.generic_struct_spec_key_from_type_expr(expr.expr)
1431 }
1432 else {
1433 return none
1434 }
1435 }
1436
1437 base_type := t.lookup_type_from_expr(lhs) or { return none }
1438 if base_type !is types.Struct {
1439 return none
1440 }
1441 base_struct := base_type as types.Struct
1442 if base_struct.generic_params.len == 0 {
1443 return none
1444 }
1445 runtime_args := generic_struct_runtime_args(args)
1446 if runtime_args.len == 0 {
1447 return none
1448 }
1449 base_c_name := t.type_to_c_name(types.Type(base_struct))
1450 suffix := t.generic_specialization_suffix(runtime_args)
1451 if base_c_name == '' || suffix == '' {
1452 return none
1453 }
1454 return base_c_name + suffix
1455}
1456
1457fn (t &Transformer) generic_struct_spec_from_type_expr(expr ast.Expr) ?GenericStructSpec {
1458 key := t.generic_struct_spec_key_from_type_expr(expr) or { return none }
1459 return t.generic_struct_specs[key] or { return none }
1460}
1461
1462fn (mut t Transformer) concrete_generic_struct_type_expr(expr ast.Expr) ?ast.Expr {
1463 spec := t.generic_struct_spec_from_type_expr(expr) or { return none }
1464 mut pos := expr.pos()
1465 if !pos.is_valid() {
1466 pos = t.next_synth_pos()
1467 }
1468 concrete_type := types.Type(types.Struct{
1469 name: spec.concrete_c_name
1470 })
1471 t.register_synth_type(pos, concrete_type)
1472 return ast.Expr(ast.Ident{
1473 name: spec.concrete_c_name
1474 pos: pos
1475 })
1476}
1477
1478fn (mut t Transformer) clone_generic_struct_decl(decl ast.StructDecl, spec GenericStructSpec, target_module string) ast.StructDecl {
1479 old_module := t.cur_module
1480 old_scope := t.scope
1481 t.cur_module = spec.module_name
1482 if scope := t.get_module_scope(spec.module_name) {
1483 t.scope = scope
1484 }
1485 should_qualify_source_types := target_module != spec.module_name && spec.module_name != ''
1486 && spec.module_name != 'main' && spec.module_name != 'builtin'
1487 mut fields := []ast.FieldDecl{cap: decl.fields.len}
1488 for field in decl.fields {
1489 mut field_typ := t.substitute_type_in_expr(field.typ, spec.bindings)
1490 mut field_value := t.clone_expr_with_bindings(field.value, spec.bindings)
1491 if should_qualify_source_types {
1492 field_typ = t.qualify_moved_clone_type_expr(field_typ, spec.module_name)
1493 field_value = t.qualify_moved_clone_expr_type_positions(field_value, spec.module_name)
1494 }
1495 fields << ast.FieldDecl{
1496 name: field.name
1497 typ: field_typ
1498 value: field_value
1499 attributes: field.attributes
1500 is_public: field.is_public
1501 is_mut: field.is_mut
1502 is_module_mut: field.is_module_mut
1503 is_interface_method: field.is_interface_method
1504 }
1505 }
1506 mut embedded := []ast.Expr{cap: decl.embedded.len}
1507 for item in decl.embedded {
1508 mut embedded_type := t.substitute_type_in_expr(item, spec.bindings)
1509 if should_qualify_source_types {
1510 embedded_type = t.qualify_moved_clone_type_expr(embedded_type, spec.module_name)
1511 }
1512 embedded << embedded_type
1513 }
1514 mut implemented_types := []ast.Expr{cap: decl.implements.len}
1515 for item in decl.implements {
1516 mut implemented_type := t.substitute_type_in_expr(item, spec.bindings)
1517 if should_qualify_source_types {
1518 implemented_type = t.qualify_moved_clone_type_expr(implemented_type, spec.module_name)
1519 }
1520 implemented_types << implemented_type
1521 }
1522 cloned := ast.StructDecl{
1523 attributes: decl.attributes
1524 is_public: decl.is_public
1525 is_union: decl.is_union
1526 implements: implemented_types
1527 embedded: embedded
1528 language: decl.language
1529 name: if target_module == spec.module_name {
1530 spec.concrete_short_name
1531 } else {
1532 spec.concrete_c_name
1533 }
1534 generic_params: []ast.Expr{}
1535 fields: fields
1536 pos: decl.pos
1537 }
1538 t.cur_module = old_module
1539 t.scope = old_scope
1540 return cloned
1541}
1542
1543fn (mut t Transformer) inject_generic_struct_specializations(files []ast.File) []ast.File {
1544 t.inject_changed_files = false
1545 t.last_struct_clones = map[int][]ast.Stmt{}
1546 if t.generic_struct_specs.len == 0 {
1547 return files
1548 }
1549 mut existing := map[string]bool{}
1550 mut base_decls := map[string]ast.StructDecl{}
1551 for file in files {
1552 for stmt in file.stmts {
1553 if stmt is ast.StructDecl {
1554 c_name := generic_struct_decl_c_name(stmt, file.mod)
1555 existing[c_name] = true
1556 if stmt.generic_params.len > 0 {
1557 base_decls[c_name] = stmt
1558 }
1559 }
1560 }
1561 }
1562 spec_keys := t.generic_struct_specs.keys()
1563 mut sorted_keys := spec_keys.clone()
1564 sorted_keys.sort()
1565 // Fast path: if every struct spec is already present in `files`, nothing
1566 // will be injected and the loop below would only rebuild an identical file
1567 // set. Return the input unchanged to avoid duplicating every file's stmt
1568 // list (~1.5GB under -gc none on the fixpoint loop's confirmation pass).
1569 // Mirrors monomorphize_pass's `per_file_clones.len == 0 { return files }`.
1570 mut any_pending := false
1571 for key in sorted_keys {
1572 spec := t.generic_struct_specs[key] or { continue }
1573 if spec.concrete_c_name !in existing {
1574 any_pending = true
1575 break
1576 }
1577 }
1578 if !any_pending {
1579 return files
1580 }
1581 mut out := []ast.File{cap: files.len}
1582 for file in files {
1583 mut stmts := []ast.Stmt{cap: file.stmts.len}
1584 for stmt in file.stmts {
1585 stmts << stmt
1586 }
1587 mut injected := []ast.Stmt{}
1588 for key in sorted_keys {
1589 spec := t.generic_struct_specs[key] or { continue }
1590 if spec.file_idx < 0 || spec.file_idx >= files.len || spec.file_idx != out.len
1591 || spec.concrete_c_name in existing {
1592 continue
1593 }
1594 struct_decl := base_decls[spec.base_c_name] or { continue }
1595 cloned := ast.Stmt(t.clone_generic_struct_decl(struct_decl, spec, file.mod))
1596 stmts << cloned
1597 injected << cloned
1598 existing[spec.concrete_c_name] = true
1599 }
1600 for key in sorted_keys {
1601 spec := t.generic_struct_specs[key] or { continue }
1602 if (spec.file_idx >= 0 && spec.file_idx < files.len)
1603 || spec.module_name != file.mod || spec.concrete_c_name in existing {
1604 continue
1605 }
1606 struct_decl := base_decls[spec.base_c_name] or { continue }
1607 cloned := ast.Stmt(t.clone_generic_struct_decl(struct_decl, spec, file.mod))
1608 stmts << cloned
1609 injected << cloned
1610 existing[spec.concrete_c_name] = true
1611 }
1612 if injected.len > 0 {
1613 t.last_struct_clones[out.len] = injected
1614 }
1615 out << ast.File{
1616 attributes: file.attributes
1617 mod: file.mod
1618 name: file.name
1619 stmts: stmts
1620 imports: file.imports
1621 selector_names: file.selector_names
1622 }
1623 }
1624 t.inject_changed_files = true
1625 return out
1626}
1627
1628fn (t &Transformer) generic_bindings_from_type_expr(expr ast.Expr) ?map[string]types.Type {
1629 match expr {
1630 ast.GenericArgs {
1631 return t.generic_bindings_from_generic_type_parts(expr.lhs, expr.args)
1632 }
1633 ast.GenericArgOrIndexExpr {
1634 return t.generic_bindings_from_generic_type_parts(expr.lhs, [expr.expr])
1635 }
1636 ast.ModifierExpr {
1637 return t.generic_bindings_from_type_expr(expr.expr)
1638 }
1639 ast.PrefixExpr {
1640 return t.generic_bindings_from_type_expr(expr.expr)
1641 }
1642 ast.Type {
1643 match expr {
1644 ast.GenericType {
1645 return t.generic_bindings_from_generic_type_parts(expr.name, expr.params)
1646 }
1647 ast.PointerType {
1648 return t.generic_bindings_from_type_expr(expr.base_type)
1649 }
1650 else {}
1651 }
1652 }
1653 else {}
1654 }
1655
1656 return none
1657}
1658
1659fn (t &Transformer) generic_bindings_from_generic_type_parts(lhs ast.Expr, args []ast.Expr) ?map[string]types.Type {
1660 generic_params := t.generic_template_type_param_names_from_type_lhs(lhs) or { return none }
1661 return t.generic_type_arg_bindings(generic_params, args)
1662}
1663
1664fn (t &Transformer) generic_template_type_param_names_from_type_lhs(lhs ast.Expr) ?[]string {
1665 if base := t.generic_template_struct_from_type_lhs(lhs) {
1666 return base.generic_params.clone()
1667 }
1668 match lhs {
1669 ast.Ident {
1670 return t.generic_template_type_param_names_from_type_name(lhs.name)
1671 }
1672 ast.ModifierExpr {
1673 return t.generic_template_type_param_names_from_type_lhs(lhs.expr)
1674 }
1675 ast.PrefixExpr {
1676 return t.generic_template_type_param_names_from_type_lhs(lhs.expr)
1677 }
1678 else {}
1679 }
1680
1681 return none
1682}
1683
1684fn (t &Transformer) generic_template_type_param_names_from_type_name(name string) ?[]string {
1685 if name == '' {
1686 return none
1687 }
1688 if typ := t.lookup_type(name) {
1689 params := generic_template_type_param_names_from_type(typ)
1690 if params.len > 0 {
1691 return params
1692 }
1693 }
1694 if name.contains('_T_') {
1695 return t.generic_template_type_param_names_from_type_name(name.all_before('_T_'))
1696 }
1697 return none
1698}
1699
1700fn generic_template_type_param_names_from_type(typ types.Type) []string {
1701 mut seen := map[string]bool{}
1702 mut structural_seen := map[string]bool{}
1703 mut names := []string{}
1704 collect_declared_generic_template_type_param_names(typ, mut seen, mut structural_seen, mut
1705 names)
1706 return names
1707}
1708
1709fn generic_template_structural_seen_key(kind string, name string, generic_params []string) string {
1710 if name == '' {
1711 return ''
1712 }
1713 return '${kind}:${name}:${generic_params.join(',')}'
1714}
1715
1716fn collect_declared_generic_template_type_param_names(typ types.Type, mut seen map[string]bool, mut structural_seen map[string]bool, mut names []string) {
1717 match typ {
1718 types.Alias {
1719 collect_declared_generic_template_type_param_names(typ.base_type, mut seen, mut
1720 structural_seen, mut names)
1721 }
1722 types.Array {
1723 collect_declared_generic_template_type_param_names(typ.elem_type, mut seen, mut
1724 structural_seen, mut names)
1725 }
1726 types.ArrayFixed {
1727 collect_declared_generic_template_type_param_names(typ.elem_type, mut seen, mut
1728 structural_seen, mut names)
1729 }
1730 types.Channel {
1731 if elem_type := typ.elem_type {
1732 collect_declared_generic_template_type_param_names(elem_type, mut seen, mut
1733 structural_seen, mut names)
1734 }
1735 }
1736 types.Map {
1737 collect_declared_generic_template_type_param_names(typ.key_type, mut seen, mut
1738 structural_seen, mut names)
1739 collect_declared_generic_template_type_param_names(typ.value_type, mut seen, mut
1740 structural_seen, mut names)
1741 }
1742 types.NamedType {
1743 name := string(typ)
1744 if is_generic_placeholder_ident(name) && name !in seen {
1745 seen[name] = true
1746 names << name
1747 }
1748 }
1749 types.OptionType {
1750 collect_declared_generic_template_type_param_names(typ.base_type, mut seen, mut
1751 structural_seen, mut names)
1752 }
1753 types.Pointer {
1754 collect_declared_generic_template_type_param_names(typ.base_type, mut seen, mut
1755 structural_seen, mut names)
1756 }
1757 types.ResultType {
1758 collect_declared_generic_template_type_param_names(typ.base_type, mut seen, mut
1759 structural_seen, mut names)
1760 }
1761 types.Struct {
1762 for param in typ.generic_params {
1763 if is_generic_placeholder_ident(param) && param !in seen {
1764 seen[param] = true
1765 names << param
1766 }
1767 }
1768 struct_key := generic_template_structural_seen_key('struct', typ.name,
1769 typ.generic_params)
1770 if struct_key != '' {
1771 if structural_seen[struct_key] {
1772 return
1773 }
1774 structural_seen[struct_key] = true
1775 }
1776 for field in typ.fields {
1777 collect_declared_generic_template_type_param_names(field.typ, mut seen, mut
1778 structural_seen, mut names)
1779 }
1780 for embedded in typ.embedded {
1781 collect_declared_generic_template_type_param_names(types.Type(embedded), mut seen, mut
1782 structural_seen, mut names)
1783 }
1784 }
1785 types.SumType {
1786 for param in typ.generic_params {
1787 if is_generic_placeholder_ident(param) && param !in seen {
1788 seen[param] = true
1789 names << param
1790 }
1791 }
1792 sumtype_key := generic_template_structural_seen_key('sumtype', typ.name,
1793 typ.generic_params)
1794 if sumtype_key != '' {
1795 if structural_seen[sumtype_key] {
1796 return
1797 }
1798 structural_seen[sumtype_key] = true
1799 }
1800 if typ.generic_params.len > 0 {
1801 return
1802 }
1803 for variant in typ.variants {
1804 collect_declared_generic_template_type_param_names(variant, mut seen, mut
1805 structural_seen, mut names)
1806 }
1807 }
1808 else {}
1809 }
1810}
1811
1812fn (t &Transformer) generic_template_struct_from_type_lhs(lhs ast.Expr) ?types.Struct {
1813 match lhs {
1814 ast.Ident {
1815 return t.generic_template_struct_from_type_name(lhs.name)
1816 }
1817 ast.SelectorExpr {
1818 if lhs.lhs is ast.Ident {
1819 lhs_ident := lhs.lhs as ast.Ident
1820 mut module_names := []string{}
1821 if full_name := t.cur_import_aliases[lhs_ident.name] {
1822 module_names << module_call_c_prefix(full_name)
1823 module_names << full_name.replace('.', '__')
1824 }
1825 if prefix := t.resolve_module_call_prefix(lhs_ident.name) {
1826 module_names << prefix
1827 }
1828 module_names << lhs_ident.name.replace('.', '__')
1829 mut seen := map[string]bool{}
1830 for module_name in module_names {
1831 if module_name == '' || module_name in seen {
1832 continue
1833 }
1834 seen[module_name] = true
1835 if base := t.generic_template_struct_from_type_name('${module_name}__${lhs.rhs.name}') {
1836 return base
1837 }
1838 }
1839 }
1840 return t.generic_template_struct_from_type_name(lhs.rhs.name)
1841 }
1842 ast.ModifierExpr {
1843 return t.generic_template_struct_from_type_lhs(lhs.expr)
1844 }
1845 ast.PrefixExpr {
1846 return t.generic_template_struct_from_type_lhs(lhs.expr)
1847 }
1848 else {}
1849 }
1850
1851 return none
1852}
1853
1854fn (t &Transformer) generic_template_struct_from_type_name(name string) ?types.Struct {
1855 if name == '' {
1856 return none
1857 }
1858 if typ := t.lookup_type(name) {
1859 if typ is types.Struct && typ.generic_params.len > 0 {
1860 return typ
1861 }
1862 }
1863 if name.contains('_T_') {
1864 return t.generic_template_struct_from_type_name(name.all_before('_T_'))
1865 }
1866 return none
1867}
1868
1869fn struct_decl_field_lookup_names(name string, module_name string) []string {
1870 if name == '' {
1871 return []string{}
1872 }
1873 mut names := [name]
1874 if module_name != '' && module_name != 'main' && module_name != 'builtin' {
1875 names << '${module_name.replace('.', '__')}__${name}'
1876 }
1877 return names
1878}
1879
1880fn embedded_field_name_from_type_expr(expr ast.Expr) string {
1881 match expr {
1882 ast.Ident {
1883 return embedded_field_name_from_type_name(expr.name)
1884 }
1885 ast.SelectorExpr {
1886 return embedded_field_name_from_type_name(expr.rhs.name)
1887 }
1888 ast.GenericArgOrIndexExpr {
1889 return embedded_field_name_from_type_expr(expr.lhs)
1890 }
1891 ast.GenericArgs {
1892 return embedded_field_name_from_type_expr(expr.lhs)
1893 }
1894 ast.ModifierExpr {
1895 return embedded_field_name_from_type_expr(expr.expr)
1896 }
1897 ast.PrefixExpr {
1898 return embedded_field_name_from_type_expr(expr.expr)
1899 }
1900 ast.Type {
1901 match expr {
1902 ast.GenericType {
1903 return embedded_field_name_from_type_expr(expr.name)
1904 }
1905 ast.PointerType {
1906 return embedded_field_name_from_type_expr(expr.base_type)
1907 }
1908 else {
1909 return ''
1910 }
1911 }
1912 }
1913 else {
1914 return ''
1915 }
1916 }
1917}
1918
1919fn embedded_field_name_from_type_expr_cursor(expr ast.Cursor) string {
1920 match expr.kind() {
1921 .expr_ident {
1922 return embedded_field_name_from_type_name(expr.name())
1923 }
1924 .expr_selector {
1925 return embedded_field_name_from_type_name(selector_rhs_name_cursor(expr))
1926 }
1927 .expr_generic_arg_or_index, .expr_generic_args, .expr_index {
1928 return embedded_field_name_from_type_expr_cursor(expr.edge(0))
1929 }
1930 .expr_modifier, .expr_prefix {
1931 return embedded_field_name_from_type_expr_cursor(expr.edge(0))
1932 }
1933 .typ_generic {
1934 return embedded_field_name_from_type_expr_cursor(expr.edge(0))
1935 }
1936 .typ_pointer {
1937 return embedded_field_name_from_type_expr_cursor(expr.edge(0))
1938 }
1939 else {
1940 return ''
1941 }
1942 }
1943}
1944
1945fn embedded_field_name_from_type_name(name string) string {
1946 if name == '' {
1947 return ''
1948 }
1949 mut field_name := if name.contains('__') { name.all_after_last('__') } else { name }
1950 if field_name.contains('.') {
1951 field_name = field_name.all_after_last('.')
1952 }
1953 if field_name.contains('_T_') {
1954 field_name = field_name.all_before('_T_')
1955 }
1956 return field_name
1957}
1958
1959fn field_type_expr_has_generic_args(expr ast.Expr) bool {
1960 match expr {
1961 ast.GenericArgOrIndexExpr, ast.GenericArgs {
1962 return true
1963 }
1964 ast.ModifierExpr {
1965 return field_type_expr_has_generic_args(expr.expr)
1966 }
1967 ast.PrefixExpr {
1968 return field_type_expr_has_generic_args(expr.expr)
1969 }
1970 ast.Type {
1971 match expr {
1972 ast.ArrayType {
1973 return field_type_expr_has_generic_args(expr.elem_type)
1974 }
1975 ast.ArrayFixedType {
1976 return field_type_expr_has_generic_args(expr.elem_type)
1977 }
1978 ast.ChannelType {
1979 return field_type_expr_has_generic_args(expr.elem_type)
1980 }
1981 ast.FnType {
1982 for param in expr.params {
1983 if field_type_expr_has_generic_args(param.typ) {
1984 return true
1985 }
1986 }
1987 return field_type_expr_has_generic_args(expr.return_type)
1988 }
1989 ast.GenericType {
1990 return true
1991 }
1992 ast.MapType {
1993 return field_type_expr_has_generic_args(expr.key_type)
1994 || field_type_expr_has_generic_args(expr.value_type)
1995 }
1996 ast.OptionType {
1997 return field_type_expr_has_generic_args(expr.base_type)
1998 }
1999 ast.PointerType {
2000 return field_type_expr_has_generic_args(expr.base_type)
2001 }
2002 ast.ResultType {
2003 return field_type_expr_has_generic_args(expr.base_type)
2004 }
2005 ast.ThreadType {
2006 return field_type_expr_has_generic_args(expr.elem_type)
2007 }
2008 else {
2009 return false
2010 }
2011 }
2012 }
2013 else {
2014 return false
2015 }
2016 }
2017}
2018
2019fn field_type_cursor_has_generic_args(expr ast.Cursor) bool {
2020 match expr.kind() {
2021 .expr_generic_arg_or_index, .expr_generic_args {
2022 return true
2023 }
2024 .expr_modifier, .expr_prefix {
2025 return field_type_cursor_has_generic_args(expr.edge(0))
2026 }
2027 .typ_array {
2028 return field_type_cursor_has_generic_args(expr.edge(0))
2029 }
2030 .typ_array_fixed {
2031 return field_type_cursor_has_generic_args(expr.edge(1))
2032 }
2033 .typ_channel {
2034 return field_type_cursor_has_generic_args(expr.edge(1))
2035 }
2036 .typ_fn {
2037 params := expr.list_at(1)
2038 for i in 0 .. params.len() {
2039 if field_type_cursor_has_generic_args(params.at(i).edge(0)) {
2040 return true
2041 }
2042 }
2043 return field_type_cursor_has_generic_args(expr.edge(2))
2044 }
2045 .typ_generic {
2046 return true
2047 }
2048 .typ_map {
2049 return field_type_cursor_has_generic_args(expr.edge(0))
2050 || field_type_cursor_has_generic_args(expr.edge(1))
2051 }
2052 .typ_option, .typ_pointer, .typ_result, .typ_thread {
2053 return field_type_cursor_has_generic_args(expr.edge(0))
2054 }
2055 .typ_tuple {
2056 for i in 0 .. expr.edge_count() {
2057 if field_type_cursor_has_generic_args(expr.edge(i)) {
2058 return true
2059 }
2060 }
2061 return false
2062 }
2063 else {
2064 return false
2065 }
2066 }
2067}
2068
2069fn (t &Transformer) generic_bindings_from_type_expr_cursor(expr ast.Cursor) ?map[string]types.Type {
2070 match expr.kind() {
2071 .expr_generic_args {
2072 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
2073 return t.generic_bindings_from_template_type_cursor(base, cursor_edges(expr, 1))
2074 }
2075 .expr_generic_arg_or_index {
2076 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
2077 return t.generic_bindings_from_template_type_cursor(base, [
2078 expr.edge(1),
2079 ])
2080 }
2081 .expr_modifier, .expr_prefix {
2082 return t.generic_bindings_from_type_expr_cursor(expr.edge(0))
2083 }
2084 .typ_generic {
2085 base := t.lookup_type_from_expr_cursor(expr.edge(0)) or { return none }
2086 return t.generic_bindings_from_template_type_cursor(base, cursor_edges(expr, 1))
2087 }
2088 .typ_pointer {
2089 return t.generic_bindings_from_type_expr_cursor(expr.edge(0))
2090 }
2091 else {}
2092 }
2093
2094 return none
2095}
2096
2097fn (t &Transformer) generic_bindings_from_template_type_cursor(base types.Type, args []ast.Cursor) ?map[string]types.Type {
2098 generic_params := generic_template_type_param_names_from_type(base)
2099 if generic_params.len == 0 {
2100 return none
2101 }
2102 arg_bindings := t.generic_type_arg_bindings_cursor(generic_params, args) or { return none }
2103 return arg_bindings.bindings.clone()
2104}
2105
2106fn struct_field_generic_decl_key(struct_name string, field_name string) string {
2107 return '${struct_name}.${field_name}'
2108}
2109
2110// monomorphize_pass walks env.generic_types, clones each generic FnDecl per
2111// binding map with concrete types substituted, and appends the clones to the
2112// owning file's stmts.
2113pub fn (mut t Transformer) monomorphize_pass(files []ast.File) []ast.File {
2114 // Index generic FnDecls by names that can appear in env.generic_types,
2115 // including module-qualified functions and method receiver keys.
2116 mut decl_owner := map[string]int{} // fn key -> file index
2117 mut decl_node := map[string]ast.FnDecl{}
2118 for fi, file in files {
2119 for stmt in file.stmts {
2120 if stmt is ast.FnDecl {
2121 if decl_generic_param_names(stmt).len == 0 {
2122 continue
2123 }
2124 t.index_generic_fn_decl_for_monomorphize(mut decl_owner, mut decl_node, stmt, fi,
2125 file.mod)
2126 }
2127 }
2128 }
2129 // Per-file accumulator for cloned stmts (keyed by file index).
2130 mut per_file_clones := map[int][]ast.Stmt{}
2131 old_deferred_specs := t.deferred_generic_call_specs.clone()
2132 t.deferred_generic_call_specs = []DeferredGenericCallSpec{}
2133 for fn_key, bindings_list in t.env.generic_types {
2134 lookup_key := t.resolve_monomorphize_decl_key(fn_key, decl_node) or { continue }
2135 decl := decl_node[lookup_key] or { continue }
2136 fi := decl_owner[lookup_key] or { continue }
2137 for bindings in bindings_list {
2138 spec_name := t.specialized_fn_name(decl, bindings).clone()
2139 if spec_name == decl.name {
2140 continue
2141 }
2142 clone_name := t.monomorphized_clone_name(lookup_key, decl, spec_name, bindings).clone()
2143 clone_ast_name := t.monomorphized_clone_ast_name(decl, clone_name, bindings).clone()
2144 spec_key := '${lookup_key}:${clone_name}'.clone()
2145 if spec_key in t.monomorphized_specs {
2146 continue
2147 }
2148 t.monomorphized_specs[spec_key] = true
2149 owner_key := generic_spec_owner_key(fn_key, bindings)
2150 owner_file := t.generic_spec_owner_file[owner_key] or { fi }
2151 nested_struct_file := if t.generic_bindings_visible_in_module(files[fi].mod, bindings) {
2152 fi
2153 } else {
2154 owner_file
2155 }
2156 if nested_struct_file < 0 || nested_struct_file >= files.len {
2157 continue
2158 }
2159 clone_file := nested_struct_file
2160 t.register_monomorphized_fn_bindings(files[fi].mod, clone_name, bindings)
2161 old_owner_file := t.cur_generic_call_file_idx
2162 old_import_aliases := t.cur_import_aliases.clone()
2163 t.cur_generic_call_file_idx = clone_file
2164 t.cur_import_aliases = import_aliases_for_generic_collect(files[clone_file].imports)
2165 mut cloned := t.clone_fn_decl_with_substitutions(decl, bindings, clone_ast_name,
2166 files[fi].mod, files[clone_file].mod)
2167 if files[clone_file].mod != files[fi].mod {
2168 cloned = t.qualify_moved_clone_source_module_types(cloned, files[fi].mod)
2169 }
2170 t.cur_generic_call_file_idx = old_owner_file
2171 t.cur_import_aliases = old_import_aliases.clone()
2172 mut bucket := []ast.Stmt{}
2173 if clone_file in per_file_clones {
2174 bucket = per_file_clones[clone_file]
2175 }
2176 bucket << ast.Stmt(cloned)
2177 per_file_clones[clone_file] = bucket
2178 }
2179 }
2180 deferred_specs := t.deferred_generic_call_specs.clone()
2181 t.deferred_generic_call_specs = old_deferred_specs.clone()
2182 t.flush_deferred_generic_call_specs(deferred_specs)
2183 // Record the freshly-materialized clones so the fixpoint can rescan only
2184 // them (collect_generic_call_specs_in_new_clones) instead of all files.
2185 t.last_mono_clones = per_file_clones.clone()
2186 if per_file_clones.len == 0 {
2187 return files
2188 }
2189 mut new_files := []ast.File{cap: files.len}
2190 for fi, file in files {
2191 if fi !in per_file_clones {
2192 new_files << file
2193 continue
2194 }
2195 extra := per_file_clones[fi]
2196 mut stmts := file.stmts.clone()
2197 stmts << extra
2198 new_files << ast.File{
2199 attributes: file.attributes
2200 mod: file.mod
2201 name: file.name
2202 stmts: stmts
2203 imports: file.imports
2204 selector_names: file.selector_names
2205 }
2206 }
2207 return new_files
2208}
2209
2210fn (mut t Transformer) monomorphize_pass_from_flat(flat &ast.FlatAst, mut extra_stmts map[int][]ast.Stmt) {
2211 mut decl_owner := map[string]int{}
2212 mut decl_node := map[string]ast.FnDecl{}
2213 mut decl_cursor_ids := map[string]ast.FlatNodeId{}
2214 mut decl_extra_nodes := map[string]ast.FnDecl{}
2215 for fi in 0 .. flat.files.len {
2216 module_name := flat.file_mod(flat.files[fi])
2217 stmts := flat.file_cursor(fi).stmts()
2218 for si in 0 .. stmts.len() {
2219 stmt_c := stmts.at(si)
2220 if stmt_c.kind() != .stmt_fn_decl {
2221 continue
2222 }
2223 decl := stmt_c.fn_decl_signature()
2224 if decl_generic_param_names(decl).len == 0 {
2225 continue
2226 }
2227 for key in t.generic_fn_decl_monomorphize_keys(decl, module_name) {
2228 if key == '' || key in decl_node {
2229 continue
2230 }
2231 decl_owner[key] = fi
2232 decl_node[key] = decl
2233 decl_cursor_ids[key] = stmt_c.id
2234 }
2235 }
2236 extra := extra_stmts[fi] or { []ast.Stmt{} }
2237 for stmt in extra {
2238 if stmt !is ast.FnDecl {
2239 continue
2240 }
2241 decl := stmt as ast.FnDecl
2242 if decl_generic_param_names(decl).len == 0 {
2243 continue
2244 }
2245 for key in t.generic_fn_decl_monomorphize_keys(decl, module_name) {
2246 if key == '' || key in decl_node {
2247 continue
2248 }
2249 decl_owner[key] = fi
2250 decl_node[key] = decl
2251 decl_extra_nodes[key] = decl
2252 }
2253 }
2254 }
2255 mut per_file_clones := map[int][]ast.Stmt{}
2256 mut decl_full_cache := map[string]ast.FnDecl{}
2257 old_deferred_specs := t.deferred_generic_call_specs.clone()
2258 t.deferred_generic_call_specs = []DeferredGenericCallSpec{}
2259 for fn_key, bindings_list in t.env.generic_types {
2260 lookup_key := t.resolve_monomorphize_decl_key(fn_key, decl_node) or { continue }
2261 decl_sig := decl_node[lookup_key] or { continue }
2262 fi := decl_owner[lookup_key] or { continue }
2263 decl_mod := flat.file_mod(flat.files[fi])
2264 for bindings in bindings_list {
2265 spec_name := t.specialized_fn_name(decl_sig, bindings).clone()
2266 if spec_name == decl_sig.name {
2267 continue
2268 }
2269 clone_name :=
2270 t.monomorphized_clone_name(lookup_key, decl_sig, spec_name, bindings).clone()
2271 clone_ast_name := t.monomorphized_clone_ast_name(decl_sig, clone_name, bindings).clone()
2272 spec_key := '${lookup_key}:${clone_name}'.clone()
2273 if spec_key in t.monomorphized_specs {
2274 continue
2275 }
2276 t.monomorphized_specs[spec_key] = true
2277 owner_key := generic_spec_owner_key(fn_key, bindings)
2278 owner_file := t.generic_spec_owner_file[owner_key] or { fi }
2279 nested_struct_file := if t.generic_bindings_visible_in_module(decl_mod, bindings) {
2280 fi
2281 } else {
2282 owner_file
2283 }
2284 if nested_struct_file < 0 || nested_struct_file >= flat.files.len {
2285 continue
2286 }
2287 clone_file := nested_struct_file
2288 clone_mod := flat.file_mod(flat.files[clone_file])
2289 t.register_monomorphized_fn_bindings(decl_mod, clone_name, bindings)
2290 old_owner_file := t.cur_generic_call_file_idx
2291 old_import_aliases := t.cur_import_aliases.clone()
2292 t.cur_generic_call_file_idx = clone_file
2293 t.cur_import_aliases = flat_import_aliases_for_generic_collect(flat, clone_file)
2294 decl := decl_full_cache[lookup_key] or {
2295 full_decl := decl_extra_nodes[lookup_key] or {
2296 cursor_id := decl_cursor_ids[lookup_key] or { ast.invalid_flat_node_id }
2297 if cursor_id < 0 {
2298 continue
2299 }
2300 decl_cursor := ast.Cursor{
2301 flat: unsafe { flat }
2302 id: cursor_id
2303 }
2304 fn_decl_signature_with_body_cursor(decl_sig, decl_cursor)
2305 }
2306 decl_full_cache[lookup_key] = full_decl
2307 full_decl
2308 }
2309 mut cloned := t.clone_fn_decl_with_substitutions(decl, bindings, clone_ast_name,
2310 decl_mod, clone_mod)
2311 if clone_mod != decl_mod {
2312 cloned = t.qualify_moved_clone_source_module_types(cloned, decl_mod)
2313 }
2314 t.cur_generic_call_file_idx = old_owner_file
2315 t.cur_import_aliases = old_import_aliases.clone()
2316 mut bucket := per_file_clones[clone_file] or { []ast.Stmt{} }
2317 bucket << ast.Stmt(cloned)
2318 per_file_clones[clone_file] = bucket
2319 }
2320 }
2321 deferred_specs := t.deferred_generic_call_specs.clone()
2322 t.deferred_generic_call_specs = old_deferred_specs.clone()
2323 t.flush_deferred_generic_call_specs(deferred_specs)
2324 t.last_mono_clones = per_file_clones.clone()
2325 for fi, clones in per_file_clones {
2326 if clones.len == 0 {
2327 continue
2328 }
2329 append_flat_extra_stmts(mut extra_stmts, fi, clones)
2330 }
2331}
2332
2333fn fn_decl_signature_with_body_cursor(signature ast.FnDecl, decl_cursor ast.Cursor) ast.FnDecl {
2334 return ast.FnDecl{
2335 attributes: signature.attributes
2336 is_public: signature.is_public
2337 is_method: signature.is_method
2338 is_static: signature.is_static
2339 receiver: signature.receiver
2340 language: signature.language
2341 name: signature.name
2342 typ: signature.typ
2343 stmts: decl_cursor.list_at(3).stmts()
2344 pos: signature.pos
2345 }
2346}
2347
2348fn (mut t Transformer) inject_generic_struct_specializations_from_flat(flat &ast.FlatAst, mut extra_stmts map[int][]ast.Stmt) {
2349 t.inject_changed_files = false
2350 t.last_struct_clones = map[int][]ast.Stmt{}
2351 if t.generic_struct_specs.len == 0 {
2352 return
2353 }
2354 mut existing := map[string]bool{}
2355 mut base_decls := map[string]ast.StructDecl{}
2356 for fi in 0 .. flat.files.len {
2357 module_name := flat.file_mod(flat.files[fi])
2358 for stmt in flat_file_struct_decls_with_extra(flat, extra_stmts, fi) {
2359 c_name := generic_struct_decl_c_name(stmt, module_name)
2360 existing[c_name] = true
2361 if stmt.generic_params.len > 0 {
2362 base_decls[c_name] = stmt
2363 }
2364 }
2365 }
2366 spec_keys := t.generic_struct_specs.keys()
2367 mut sorted_keys := spec_keys.clone()
2368 sorted_keys.sort()
2369 mut any_pending := false
2370 for key in sorted_keys {
2371 spec := t.generic_struct_specs[key] or { continue }
2372 if spec.concrete_c_name !in existing {
2373 any_pending = true
2374 break
2375 }
2376 }
2377 if !any_pending {
2378 return
2379 }
2380 for fi in 0 .. flat.files.len {
2381 module_name := flat.file_mod(flat.files[fi])
2382 mut injected := []ast.Stmt{}
2383 for key in sorted_keys {
2384 spec := t.generic_struct_specs[key] or { continue }
2385 if spec.file_idx < 0 || spec.file_idx >= flat.files.len || spec.file_idx != fi
2386 || spec.concrete_c_name in existing {
2387 continue
2388 }
2389 struct_decl := base_decls[spec.base_c_name] or { continue }
2390 cloned := ast.Stmt(t.clone_generic_struct_decl(struct_decl, spec, module_name))
2391 injected << cloned
2392 existing[spec.concrete_c_name] = true
2393 }
2394 for key in sorted_keys {
2395 spec := t.generic_struct_specs[key] or { continue }
2396 if (spec.file_idx >= 0 && spec.file_idx < flat.files.len)
2397 || spec.module_name != module_name
2398 || spec.concrete_c_name in existing {
2399 continue
2400 }
2401 struct_decl := base_decls[spec.base_c_name] or { continue }
2402 cloned := ast.Stmt(t.clone_generic_struct_decl(struct_decl, spec, module_name))
2403 injected << cloned
2404 existing[spec.concrete_c_name] = true
2405 }
2406 if injected.len > 0 {
2407 t.last_struct_clones[fi] = injected.clone()
2408 append_flat_extra_stmts(mut extra_stmts, fi, injected)
2409 }
2410 }
2411 t.inject_changed_files = true
2412}
2413
2414fn (mut t Transformer) flush_deferred_generic_call_specs(specs []DeferredGenericCallSpec) {
2415 old_file_idx := t.cur_generic_call_file_idx
2416 for spec in specs {
2417 if spec.file_idx >= 0 {
2418 t.cur_generic_call_file_idx = spec.file_idx
2419 }
2420 t.register_generic_bindings(spec.base_name, spec.bindings)
2421 }
2422 t.cur_generic_call_file_idx = old_file_idx
2423}
2424
2425fn (t &Transformer) qualify_moved_clone_source_module_types(decl ast.FnDecl, source_mod string) ast.FnDecl {
2426 if source_mod == '' || source_mod == 'main' || source_mod == 'builtin' {
2427 return decl
2428 }
2429 mut receiver := decl.receiver
2430 receiver.typ = t.qualify_moved_clone_type_expr(receiver.typ, source_mod)
2431 mut params := []ast.Parameter{cap: decl.typ.params.len}
2432 for param in decl.typ.params {
2433 params << ast.Parameter{
2434 name: param.name
2435 typ: t.qualify_moved_clone_type_expr(param.typ, source_mod)
2436 is_mut: param.is_mut
2437 pos: param.pos
2438 }
2439 }
2440 mut stmts := []ast.Stmt{cap: decl.stmts.len}
2441 for stmt in decl.stmts {
2442 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
2443 }
2444 return ast.FnDecl{
2445 attributes: decl.attributes
2446 is_public: decl.is_public
2447 is_method: decl.is_method
2448 is_static: decl.is_static
2449 receiver: receiver
2450 language: decl.language
2451 name: decl.name
2452 typ: ast.FnType{
2453 generic_params: decl.typ.generic_params
2454 params: params
2455 return_type: t.qualify_moved_clone_type_expr(decl.typ.return_type, source_mod)
2456 }
2457 stmts: stmts
2458 pos: decl.pos
2459 }
2460}
2461
2462fn (t &Transformer) qualify_moved_clone_type_expr(expr ast.Expr, source_mod string) ast.Expr {
2463 match expr {
2464 ast.Ident {
2465 if _ := t.get_synth_type(expr.pos) {
2466 return ast.Expr(expr)
2467 }
2468 if !t.should_qualify_moved_clone_type_name(expr.name, source_mod) {
2469 return ast.Expr(expr)
2470 }
2471 return ast.Expr(ast.Ident{
2472 name: '${source_mod}__${expr.name}'
2473 pos: expr.pos
2474 })
2475 }
2476 ast.GenericArgOrIndexExpr {
2477 return ast.Expr(ast.GenericArgOrIndexExpr{
2478 lhs: t.qualify_moved_clone_type_expr(expr.lhs, source_mod)
2479 expr: t.qualify_moved_clone_type_expr(expr.expr, source_mod)
2480 pos: expr.pos
2481 })
2482 }
2483 ast.GenericArgs {
2484 mut args := []ast.Expr{cap: expr.args.len}
2485 for arg in expr.args {
2486 args << t.qualify_moved_clone_type_expr(arg, source_mod)
2487 }
2488 return ast.Expr(ast.GenericArgs{
2489 lhs: t.qualify_moved_clone_type_expr(expr.lhs, source_mod)
2490 args: args
2491 pos: expr.pos
2492 })
2493 }
2494 ast.ModifierExpr {
2495 return ast.Expr(ast.ModifierExpr{
2496 kind: expr.kind
2497 expr: t.qualify_moved_clone_type_expr(expr.expr, source_mod)
2498 pos: expr.pos
2499 })
2500 }
2501 ast.PrefixExpr {
2502 return ast.Expr(ast.PrefixExpr{
2503 op: expr.op
2504 expr: t.qualify_moved_clone_type_expr(expr.expr, source_mod)
2505 pos: expr.pos
2506 })
2507 }
2508 ast.Type {
2509 return ast.Expr(t.qualify_moved_clone_type_node(expr, source_mod))
2510 }
2511 else {
2512 return expr
2513 }
2514 }
2515}
2516
2517fn (t &Transformer) should_qualify_moved_clone_type_name(name string, source_mod string) bool {
2518 if name == '' || name.contains('__') || name.contains('.')
2519 || name in ['void', 'voidptr', 'byteptr', 'charptr', 'i8', 'i16', 'i32', 'int', 'i64', 'isize', 'u8', 'byte', 'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'char', 'rune', 'string', 'bool'] {
2520 return false
2521 }
2522 qualified := '${source_mod}__${name}'
2523 if _ := t.lookup_type(qualified) {
2524 return true
2525 }
2526 return false
2527}
2528
2529fn (t &Transformer) should_qualify_moved_clone_fn_name(name string, source_mod string) bool {
2530 if name == '' || source_mod == '' || source_mod == 'main' || source_mod == 'builtin'
2531 || name.contains('__') || name.contains('.') {
2532 return false
2533 }
2534 base_name := generic_base_name_without_specialization(name)
2535 if base_name == '' {
2536 return false
2537 }
2538 if _ := t.lookup_fn_cached(source_mod, base_name) {
2539 return true
2540 }
2541 if _ := t.generic_fn_decl_for_call('${source_mod}__${base_name}') {
2542 return true
2543 }
2544 if _ := t.generic_fn_decl_for_call('${source_mod}.${base_name}') {
2545 return true
2546 }
2547 return false
2548}
2549
2550fn (t &Transformer) qualify_moved_clone_type_node(typ ast.Type, source_mod string) ast.Type {
2551 match typ {
2552 ast.ArrayType {
2553 return ast.Type(ast.ArrayType{
2554 elem_type: t.qualify_moved_clone_type_expr(typ.elem_type, source_mod)
2555 })
2556 }
2557 ast.ArrayFixedType {
2558 return ast.Type(ast.ArrayFixedType{
2559 len: typ.len
2560 elem_type: t.qualify_moved_clone_type_expr(typ.elem_type, source_mod)
2561 })
2562 }
2563 ast.ChannelType {
2564 return ast.Type(ast.ChannelType{
2565 cap: typ.cap
2566 elem_type: t.qualify_moved_clone_type_expr(typ.elem_type, source_mod)
2567 })
2568 }
2569 ast.FnType {
2570 mut params := []ast.Parameter{cap: typ.params.len}
2571 for param in typ.params {
2572 params << ast.Parameter{
2573 name: param.name
2574 typ: t.qualify_moved_clone_type_expr(param.typ, source_mod)
2575 is_mut: param.is_mut
2576 pos: param.pos
2577 }
2578 }
2579 return ast.Type(ast.FnType{
2580 generic_params: typ.generic_params
2581 params: params
2582 return_type: t.qualify_moved_clone_type_expr(typ.return_type, source_mod)
2583 })
2584 }
2585 ast.GenericType {
2586 mut params := []ast.Expr{cap: typ.params.len}
2587 for param in typ.params {
2588 params << t.qualify_moved_clone_type_expr(param, source_mod)
2589 }
2590 return ast.Type(ast.GenericType{
2591 name: t.qualify_moved_clone_type_expr(typ.name, source_mod)
2592 params: params
2593 })
2594 }
2595 ast.MapType {
2596 return ast.Type(ast.MapType{
2597 key_type: t.qualify_moved_clone_type_expr(typ.key_type, source_mod)
2598 value_type: t.qualify_moved_clone_type_expr(typ.value_type, source_mod)
2599 })
2600 }
2601 ast.OptionType {
2602 return ast.Type(ast.OptionType{
2603 base_type: t.qualify_moved_clone_type_expr(typ.base_type, source_mod)
2604 })
2605 }
2606 ast.PointerType {
2607 return ast.Type(ast.PointerType{
2608 base_type: t.qualify_moved_clone_type_expr(typ.base_type, source_mod)
2609 lifetime: typ.lifetime
2610 })
2611 }
2612 ast.ResultType {
2613 return ast.Type(ast.ResultType{
2614 base_type: t.qualify_moved_clone_type_expr(typ.base_type, source_mod)
2615 })
2616 }
2617 ast.ThreadType {
2618 return ast.Type(ast.ThreadType{
2619 elem_type: t.qualify_moved_clone_type_expr(typ.elem_type, source_mod)
2620 })
2621 }
2622 else {
2623 return typ
2624 }
2625 }
2626}
2627
2628fn (t &Transformer) qualify_moved_clone_stmt_type_positions(stmt ast.Stmt, source_mod string) ast.Stmt {
2629 match stmt {
2630 ast.AssertStmt {
2631 return ast.Stmt(ast.AssertStmt{
2632 expr: t.qualify_moved_clone_expr_type_positions(stmt.expr, source_mod)
2633 extra: t.qualify_moved_clone_expr_type_positions(stmt.extra, source_mod)
2634 })
2635 }
2636 ast.AssignStmt {
2637 mut lhs := []ast.Expr{cap: stmt.lhs.len}
2638 for expr in stmt.lhs {
2639 lhs << t.qualify_moved_clone_expr_type_positions(expr, source_mod)
2640 }
2641 mut rhs := []ast.Expr{cap: stmt.rhs.len}
2642 for expr in stmt.rhs {
2643 rhs << t.qualify_moved_clone_expr_type_positions(expr, source_mod)
2644 }
2645 return ast.Stmt(ast.AssignStmt{
2646 op: stmt.op
2647 lhs: lhs
2648 rhs: rhs
2649 pos: stmt.pos
2650 })
2651 }
2652 ast.BlockStmt {
2653 mut stmts := []ast.Stmt{cap: stmt.stmts.len}
2654 for child in stmt.stmts {
2655 stmts << t.qualify_moved_clone_stmt_type_positions(child, source_mod)
2656 }
2657 return ast.Stmt(ast.BlockStmt{
2658 stmts: stmts
2659 })
2660 }
2661 ast.ComptimeStmt {
2662 return ast.Stmt(ast.ComptimeStmt{
2663 stmt: t.qualify_moved_clone_stmt_type_positions(stmt.stmt, source_mod)
2664 })
2665 }
2666 ast.ConstDecl {
2667 mut fields := []ast.FieldInit{cap: stmt.fields.len}
2668 for field in stmt.fields {
2669 fields << ast.FieldInit{
2670 name: field.name
2671 value: t.qualify_moved_clone_expr_type_positions(field.value, source_mod)
2672 }
2673 }
2674 return ast.Stmt(ast.ConstDecl{
2675 fields: fields
2676 })
2677 }
2678 ast.DeferStmt {
2679 mut stmts := []ast.Stmt{cap: stmt.stmts.len}
2680 for child in stmt.stmts {
2681 stmts << t.qualify_moved_clone_stmt_type_positions(child, source_mod)
2682 }
2683 return ast.Stmt(ast.DeferStmt{
2684 mode: stmt.mode
2685 stmts: stmts
2686 })
2687 }
2688 ast.ExprStmt {
2689 return ast.Stmt(ast.ExprStmt{
2690 expr: t.qualify_moved_clone_expr_type_positions(stmt.expr, source_mod)
2691 })
2692 }
2693 ast.ForInStmt {
2694 return ast.Stmt(ast.ForInStmt{
2695 key: t.qualify_moved_clone_expr_type_positions(stmt.key, source_mod)
2696 value: t.qualify_moved_clone_expr_type_positions(stmt.value, source_mod)
2697 expr: t.qualify_moved_clone_expr_type_positions(stmt.expr, source_mod)
2698 })
2699 }
2700 ast.ForStmt {
2701 mut stmts := []ast.Stmt{cap: stmt.stmts.len}
2702 for child in stmt.stmts {
2703 stmts << t.qualify_moved_clone_stmt_type_positions(child, source_mod)
2704 }
2705 return ast.Stmt(ast.ForStmt{
2706 init: t.qualify_moved_clone_stmt_type_positions(stmt.init, source_mod)
2707 cond: t.qualify_moved_clone_expr_type_positions(stmt.cond, source_mod)
2708 post: t.qualify_moved_clone_stmt_type_positions(stmt.post, source_mod)
2709 stmts: stmts
2710 })
2711 }
2712 ast.GlobalDecl {
2713 mut fields := []ast.FieldDecl{cap: stmt.fields.len}
2714 for field in stmt.fields {
2715 fields << ast.FieldDecl{
2716 name: field.name
2717 typ: t.qualify_moved_clone_type_expr(field.typ, source_mod)
2718 value: t.qualify_moved_clone_expr_type_positions(field.value,
2719 source_mod)
2720 attributes: field.attributes
2721 is_public: field.is_public
2722 is_mut: field.is_mut
2723 is_module_mut: field.is_module_mut
2724 is_interface_method: field.is_interface_method
2725 }
2726 }
2727 return ast.Stmt(ast.GlobalDecl{
2728 attributes: stmt.attributes
2729 fields: fields
2730 is_public: stmt.is_public
2731 })
2732 }
2733 ast.LabelStmt {
2734 return ast.Stmt(ast.LabelStmt{
2735 name: stmt.name
2736 stmt: t.qualify_moved_clone_stmt_type_positions(stmt.stmt, source_mod)
2737 })
2738 }
2739 ast.ReturnStmt {
2740 mut exprs := []ast.Expr{cap: stmt.exprs.len}
2741 for expr in stmt.exprs {
2742 exprs << t.qualify_moved_clone_expr_type_positions(expr, source_mod)
2743 }
2744 return ast.Stmt(ast.ReturnStmt{
2745 exprs: exprs
2746 })
2747 }
2748 else {
2749 return stmt
2750 }
2751 }
2752}
2753
2754fn (t &Transformer) qualify_moved_clone_expr_type_positions(expr ast.Expr, source_mod string) ast.Expr {
2755 match expr {
2756 ast.Ident {
2757 if expr.name.contains('_T_')
2758 && t.should_qualify_moved_clone_fn_name(expr.name, source_mod) {
2759 return ast.Expr(ast.Ident{
2760 name: '${source_mod}__${expr.name}'
2761 pos: expr.pos
2762 })
2763 }
2764 return ast.Expr(expr)
2765 }
2766 ast.ArrayInitExpr {
2767 mut exprs := []ast.Expr{cap: expr.exprs.len}
2768 for item in expr.exprs {
2769 exprs << t.qualify_moved_clone_expr_type_positions(item, source_mod)
2770 }
2771 return ast.Expr(ast.ArrayInitExpr{
2772 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2773 exprs: exprs
2774 init: t.qualify_moved_clone_expr_type_positions(expr.init, source_mod)
2775 cap: t.qualify_moved_clone_expr_type_positions(expr.cap, source_mod)
2776 len: t.qualify_moved_clone_expr_type_positions(expr.len, source_mod)
2777 update_expr: t.qualify_moved_clone_expr_type_positions(expr.update_expr, source_mod)
2778 pos: expr.pos
2779 })
2780 }
2781 ast.AsCastExpr {
2782 return ast.Expr(ast.AsCastExpr{
2783 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2784 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2785 pos: expr.pos
2786 })
2787 }
2788 ast.AssocExpr {
2789 mut fields := []ast.FieldInit{cap: expr.fields.len}
2790 for field in expr.fields {
2791 fields << ast.FieldInit{
2792 name: field.name
2793 value: t.qualify_moved_clone_expr_type_positions(field.value, source_mod)
2794 }
2795 }
2796 return ast.Expr(ast.AssocExpr{
2797 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2798 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2799 fields: fields
2800 pos: expr.pos
2801 })
2802 }
2803 ast.CallExpr {
2804 mut args := []ast.Expr{cap: expr.args.len}
2805 for arg in expr.args {
2806 args << t.qualify_moved_clone_expr_type_positions(arg, source_mod)
2807 }
2808 return ast.Expr(ast.CallExpr{
2809 lhs: t.qualify_moved_clone_call_lhs_type_positions(expr.lhs, source_mod)
2810 args: args
2811 pos: expr.pos
2812 })
2813 }
2814 ast.CallOrCastExpr {
2815 return ast.Expr(ast.CallOrCastExpr{
2816 lhs: t.qualify_moved_clone_call_lhs_type_positions(expr.lhs, source_mod)
2817 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2818 pos: expr.pos
2819 })
2820 }
2821 ast.CastExpr {
2822 return ast.Expr(ast.CastExpr{
2823 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2824 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2825 pos: expr.pos
2826 })
2827 }
2828 ast.ComptimeExpr {
2829 return ast.Expr(ast.ComptimeExpr{
2830 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2831 pos: expr.pos
2832 })
2833 }
2834 ast.FieldInit {
2835 return ast.Expr(ast.FieldInit{
2836 name: expr.name
2837 value: t.qualify_moved_clone_expr_type_positions(expr.value, source_mod)
2838 })
2839 }
2840 ast.IfExpr {
2841 mut stmts := []ast.Stmt{cap: expr.stmts.len}
2842 for stmt in expr.stmts {
2843 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
2844 }
2845 return ast.Expr(ast.IfExpr{
2846 cond: t.qualify_moved_clone_expr_type_positions(expr.cond, source_mod)
2847 stmts: stmts
2848 else_expr: t.qualify_moved_clone_expr_type_positions(expr.else_expr, source_mod)
2849 pos: expr.pos
2850 })
2851 }
2852 ast.IfGuardExpr {
2853 stmt := t.qualify_moved_clone_stmt_type_positions(ast.Stmt(expr.stmt), source_mod)
2854 if stmt is ast.AssignStmt {
2855 return ast.Expr(ast.IfGuardExpr{
2856 stmt: stmt
2857 pos: expr.pos
2858 })
2859 }
2860 return expr
2861 }
2862 ast.IndexExpr {
2863 return ast.Expr(ast.IndexExpr{
2864 lhs: t.qualify_moved_clone_expr_type_positions(expr.lhs, source_mod)
2865 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2866 is_gated: expr.is_gated
2867 pos: expr.pos
2868 })
2869 }
2870 ast.InfixExpr {
2871 return ast.Expr(ast.InfixExpr{
2872 op: expr.op
2873 lhs: t.qualify_moved_clone_expr_type_positions(expr.lhs, source_mod)
2874 rhs: t.qualify_moved_clone_expr_type_positions(expr.rhs, source_mod)
2875 pos: expr.pos
2876 })
2877 }
2878 ast.InitExpr {
2879 mut fields := []ast.FieldInit{cap: expr.fields.len}
2880 for field in expr.fields {
2881 fields << ast.FieldInit{
2882 name: field.name
2883 value: t.qualify_moved_clone_expr_type_positions(field.value, source_mod)
2884 }
2885 }
2886 return ast.Expr(ast.InitExpr{
2887 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2888 fields: fields
2889 pos: expr.pos
2890 })
2891 }
2892 ast.KeywordOperator {
2893 mut exprs := []ast.Expr{cap: expr.exprs.len}
2894 for item in expr.exprs {
2895 exprs << t.qualify_moved_clone_expr_type_positions(item, source_mod)
2896 }
2897 return ast.Expr(ast.KeywordOperator{
2898 op: expr.op
2899 exprs: exprs
2900 pos: expr.pos
2901 })
2902 }
2903 ast.LockExpr {
2904 mut lock_exprs := []ast.Expr{cap: expr.lock_exprs.len}
2905 for item in expr.lock_exprs {
2906 lock_exprs << t.qualify_moved_clone_expr_type_positions(item, source_mod)
2907 }
2908 mut rlock_exprs := []ast.Expr{cap: expr.rlock_exprs.len}
2909 for item in expr.rlock_exprs {
2910 rlock_exprs << t.qualify_moved_clone_expr_type_positions(item, source_mod)
2911 }
2912 mut stmts := []ast.Stmt{cap: expr.stmts.len}
2913 for stmt in expr.stmts {
2914 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
2915 }
2916 return ast.Expr(ast.LockExpr{
2917 lock_exprs: lock_exprs
2918 rlock_exprs: rlock_exprs
2919 stmts: stmts
2920 pos: expr.pos
2921 })
2922 }
2923 ast.MapInitExpr {
2924 mut keys := []ast.Expr{cap: expr.keys.len}
2925 for key in expr.keys {
2926 keys << t.qualify_moved_clone_expr_type_positions(key, source_mod)
2927 }
2928 mut vals := []ast.Expr{cap: expr.vals.len}
2929 for val in expr.vals {
2930 vals << t.qualify_moved_clone_expr_type_positions(val, source_mod)
2931 }
2932 return ast.Expr(ast.MapInitExpr{
2933 typ: t.qualify_moved_clone_type_expr(expr.typ, source_mod)
2934 keys: keys
2935 vals: vals
2936 pos: expr.pos
2937 })
2938 }
2939 ast.MatchExpr {
2940 mut branches := []ast.MatchBranch{cap: expr.branches.len}
2941 for branch in expr.branches {
2942 mut conds := []ast.Expr{cap: branch.cond.len}
2943 for cond in branch.cond {
2944 conds << t.qualify_moved_clone_type_expr(cond, source_mod)
2945 }
2946 mut stmts := []ast.Stmt{cap: branch.stmts.len}
2947 for stmt in branch.stmts {
2948 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
2949 }
2950 branches << ast.MatchBranch{
2951 cond: conds
2952 stmts: stmts
2953 pos: branch.pos
2954 }
2955 }
2956 return ast.Expr(ast.MatchExpr{
2957 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2958 branches: branches
2959 pos: expr.pos
2960 })
2961 }
2962 ast.ModifierExpr {
2963 return ast.Expr(ast.ModifierExpr{
2964 kind: expr.kind
2965 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2966 pos: expr.pos
2967 })
2968 }
2969 ast.OrExpr {
2970 mut stmts := []ast.Stmt{cap: expr.stmts.len}
2971 for stmt in expr.stmts {
2972 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
2973 }
2974 return ast.Expr(ast.OrExpr{
2975 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2976 stmts: stmts
2977 pos: expr.pos
2978 })
2979 }
2980 ast.ParenExpr {
2981 return ast.Expr(ast.ParenExpr{
2982 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2983 pos: expr.pos
2984 })
2985 }
2986 ast.PostfixExpr {
2987 return ast.Expr(ast.PostfixExpr{
2988 op: expr.op
2989 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2990 pos: expr.pos
2991 })
2992 }
2993 ast.PrefixExpr {
2994 return ast.Expr(ast.PrefixExpr{
2995 op: expr.op
2996 expr: t.qualify_moved_clone_expr_type_positions(expr.expr, source_mod)
2997 pos: expr.pos
2998 })
2999 }
3000 ast.RangeExpr {
3001 return ast.Expr(ast.RangeExpr{
3002 op: expr.op
3003 start: t.qualify_moved_clone_expr_type_positions(expr.start, source_mod)
3004 end: t.qualify_moved_clone_expr_type_positions(expr.end, source_mod)
3005 pos: expr.pos
3006 })
3007 }
3008 ast.SelectExpr {
3009 mut stmts := []ast.Stmt{cap: expr.stmts.len}
3010 for stmt in expr.stmts {
3011 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
3012 }
3013 return ast.Expr(ast.SelectExpr{
3014 stmt: t.qualify_moved_clone_stmt_type_positions(expr.stmt, source_mod)
3015 stmts: stmts
3016 next: t.qualify_moved_clone_expr_type_positions(expr.next, source_mod)
3017 pos: expr.pos
3018 })
3019 }
3020 ast.SelectorExpr {
3021 return ast.Expr(ast.SelectorExpr{
3022 lhs: t.qualify_moved_clone_expr_type_positions(expr.lhs, source_mod)
3023 rhs: expr.rhs
3024 pos: expr.pos
3025 })
3026 }
3027 ast.StringInterLiteral {
3028 mut inters := []ast.StringInter{cap: expr.inters.len}
3029 for inter in expr.inters {
3030 inters << ast.StringInter{
3031 format: inter.format
3032 width: inter.width
3033 precision: inter.precision
3034 expr: t.qualify_moved_clone_expr_type_positions(inter.expr, source_mod)
3035 format_expr: t.qualify_moved_clone_expr_type_positions(inter.format_expr,
3036 source_mod)
3037 resolved_fmt: inter.resolved_fmt
3038 }
3039 }
3040 return ast.Expr(ast.StringInterLiteral{
3041 kind: expr.kind
3042 values: expr.values
3043 inters: inters
3044 pos: expr.pos
3045 })
3046 }
3047 ast.Tuple {
3048 mut exprs := []ast.Expr{cap: expr.exprs.len}
3049 for item in expr.exprs {
3050 exprs << t.qualify_moved_clone_expr_type_positions(item, source_mod)
3051 }
3052 return ast.Expr(ast.Tuple{
3053 exprs: exprs
3054 pos: expr.pos
3055 })
3056 }
3057 ast.Type {
3058 return ast.Expr(t.qualify_moved_clone_type_node(expr, source_mod))
3059 }
3060 ast.UnsafeExpr {
3061 mut stmts := []ast.Stmt{cap: expr.stmts.len}
3062 for stmt in expr.stmts {
3063 stmts << t.qualify_moved_clone_stmt_type_positions(stmt, source_mod)
3064 }
3065 return ast.Expr(ast.UnsafeExpr{
3066 stmts: stmts
3067 pos: expr.pos
3068 })
3069 }
3070 else {
3071 return expr
3072 }
3073 }
3074}
3075
3076fn (t &Transformer) qualify_moved_clone_call_lhs_type_positions(expr ast.Expr, source_mod string) ast.Expr {
3077 match expr {
3078 ast.Ident {
3079 if t.should_qualify_moved_clone_fn_name(expr.name, source_mod) {
3080 return ast.Expr(ast.Ident{
3081 name: '${source_mod}__${expr.name}'
3082 pos: expr.pos
3083 })
3084 }
3085 return ast.Expr(expr)
3086 }
3087 ast.GenericArgOrIndexExpr {
3088 return ast.Expr(ast.GenericArgOrIndexExpr{
3089 lhs: t.qualify_moved_clone_call_lhs_type_positions(expr.lhs, source_mod)
3090 expr: t.qualify_moved_clone_type_expr(expr.expr, source_mod)
3091 pos: expr.pos
3092 })
3093 }
3094 ast.GenericArgs {
3095 mut args := []ast.Expr{cap: expr.args.len}
3096 for arg in expr.args {
3097 args << t.qualify_moved_clone_type_expr(arg, source_mod)
3098 }
3099 return ast.Expr(ast.GenericArgs{
3100 lhs: t.qualify_moved_clone_call_lhs_type_positions(expr.lhs, source_mod)
3101 args: args
3102 pos: expr.pos
3103 })
3104 }
3105 else {
3106 return t.qualify_moved_clone_expr_type_positions(expr, source_mod)
3107 }
3108 }
3109}
3110
3111fn (t &Transformer) monomorphized_clone_name(fn_key string, decl ast.FnDecl, spec_name string, bindings map[string]types.Type) string {
3112 if decl.is_method {
3113 short_name := t.specialized_receiver_method_name(decl, bindings) or { spec_name }
3114 return t.qualify_receiver_generic_method_call_name(fn_key, decl, short_name) or {
3115 short_name
3116 }
3117 }
3118 base_name := generic_base_name_without_specialization(fn_key)
3119 if base_name.ends_with('__${decl.name}') {
3120 return '${base_name.all_before_last('__')}__${spec_name}'
3121 }
3122 if base_name.ends_with('.${decl.name}') {
3123 return '${base_name.all_before_last('.').replace('.', '__')}__${spec_name}'
3124 }
3125 return spec_name
3126}
3127
3128fn (t &Transformer) monomorphized_clone_ast_name(decl ast.FnDecl, clone_name string, bindings map[string]types.Type) string {
3129 if decl.is_method {
3130 return t.specialized_method_name_without_receiver(decl, bindings)
3131 }
3132 return clone_name
3133}
3134
3135fn (t &Transformer) specialized_receiver_method_name(decl ast.FnDecl, bindings map[string]types.Type) ?string {
3136 receiver_name := t.specialized_receiver_type_name(decl.receiver.typ, bindings) or {
3137 return none
3138 }
3139 method_name := t.specialized_method_name_without_receiver(decl, bindings)
3140 if method_name == '' {
3141 return none
3142 }
3143 return '${receiver_name}__${method_name}'
3144}
3145
3146fn (t &Transformer) specialized_method_name_without_receiver(decl ast.FnDecl, bindings map[string]types.Type) string {
3147 method_params := generic_param_names(decl.typ.generic_params)
3148 if method_params.len == 0 {
3149 return decl.name
3150 }
3151 mut all_placeholders := true
3152 mut concrete_parts := []string{cap: method_params.len}
3153 for gp_name in method_params {
3154 concrete := bindings[gp_name] or {
3155 concrete_parts << gp_name
3156 continue
3157 }
3158 concrete_parts << t.generic_specialization_token_from_type(concrete)
3159 if concrete.name() != gp_name {
3160 all_placeholders = false
3161 }
3162 }
3163 if all_placeholders {
3164 return decl.name + '_' + method_params.join('_')
3165 }
3166 return decl.name + '_T_' + concrete_parts.join('_')
3167}
3168
3169fn (t &Transformer) specialized_receiver_type_name(expr ast.Expr, bindings map[string]types.Type) ?string {
3170 match expr {
3171 ast.GenericArgs {
3172 return t.specialized_receiver_type_name_from_parts(expr.lhs, expr.args, bindings)
3173 }
3174 ast.GenericArgOrIndexExpr {
3175 return t.specialized_receiver_type_name_from_parts(expr.lhs, [expr.expr], bindings)
3176 }
3177 ast.ModifierExpr {
3178 return t.specialized_receiver_type_name(expr.expr, bindings)
3179 }
3180 ast.PrefixExpr {
3181 return t.specialized_receiver_type_name(expr.expr, bindings)
3182 }
3183 ast.Type {
3184 match expr {
3185 ast.GenericType {
3186 return t.specialized_receiver_type_name_from_parts(expr.name, expr.params,
3187 bindings)
3188 }
3189 ast.PointerType {
3190 return t.specialized_receiver_type_name(expr.base_type, bindings)
3191 }
3192 else {}
3193 }
3194 }
3195 else {}
3196 }
3197
3198 return none
3199}
3200
3201fn (t &Transformer) specialized_receiver_type_name_from_parts(lhs ast.Expr, args []ast.Expr, bindings map[string]types.Type) ?string {
3202 base_name := t.get_receiver_type_name(lhs)
3203 if base_name == '' || args.len == 0 {
3204 return none
3205 }
3206 mut parts := []string{cap: args.len}
3207 for arg in args {
3208 concrete := t.concrete_type_from_receiver_generic_arg(arg, bindings) or { return none }
3209 parts << t.generic_specialization_token_from_type(concrete)
3210 }
3211 return '${base_name}_T_${parts.join('_')}'
3212}
3213
3214fn (t &Transformer) concrete_type_from_receiver_generic_arg(arg ast.Expr, bindings map[string]types.Type) ?types.Type {
3215 if arg is ast.Ident {
3216 if concrete := bindings[arg.name] {
3217 return concrete
3218 }
3219 }
3220 if typ := t.type_from_param_type_expr(arg, []) {
3221 return substitute_type(typ, bindings)
3222 }
3223 if typ := t.get_expr_type(arg) {
3224 return substitute_type(typ, bindings)
3225 }
3226 return none
3227}
3228
3229fn generic_base_name_without_specialization(name string) string {
3230 // Hot path: called per-method per-call-site during generic collection.
3231 // Hand-rolled byte scans replace string.contains/.index/.all_before, each of
3232 // which builds a fresh KMP failure-table heap allocation on every call. With
3233 // millions of calls (and most names having neither `[` nor `_T_`) that
3234 // allocation churn dominated the whole transform stage. This is behaviour-
3235 // identical to the previous index_u8(`[`)>0 / contains('_T_') / ends_with('_T')
3236 // version, just without the per-call allocations.
3237 mut end := name.len
3238 for i in 0 .. name.len {
3239 if name[i] == `[` {
3240 if i > 0 {
3241 end = i
3242 }
3243 break
3244 }
3245 }
3246 for i := 0; i + 3 <= end; i++ {
3247 if name[i] == `_` && name[i + 1] == `T` && name[i + 2] == `_` {
3248 return name[..i]
3249 }
3250 }
3251 if end >= 2 && name[end - 1] == `T` && name[end - 2] == `_` {
3252 return name[..end - 2]
3253 }
3254 if end == name.len {
3255 return name
3256 }
3257 return name[..end]
3258}
3259
3260// method_short_name returns the final `__`-separated segment of s (after a
3261// `.`->`__` normalization). It matches the short form method_key_matches_type_name
3262// compares, so it can be used to bucket method keys for fast candidate lookup.
3263fn method_short_name(s string) string {
3264 norm := if s.index_u8(`.`) >= 0 { s.replace('.', '__') } else { s }
3265 d := last_double_underscore(norm)
3266 return if d >= 0 { norm[d + 2..] } else { norm }
3267}
3268
3269// last_double_underscore returns the index of the last `__` in s, or -1.
3270// Hand-rolled (no allocation) replacement for s.contains('__') / .all_after_last('__'),
3271// which build KMP tables / allocate; used in hot per-call-site name matching.
3272fn last_double_underscore(s string) int {
3273 mut i := s.len - 2
3274 for i >= 0 {
3275 if s[i] == `_` && s[i + 1] == `_` {
3276 return i
3277 }
3278 i--
3279 }
3280 return -1
3281}
3282
3283fn (mut t Transformer) generic_fn_decl_monomorphize_keys(decl ast.FnDecl, module_name string) []string {
3284 mut keys := []string{}
3285 if !decl.is_method {
3286 keys << decl.name
3287 if module_name != '' {
3288 keys << '${module_name}.${decl.name}'
3289 keys << '${module_name.replace('.', '__')}__${decl.name}'
3290 call_prefix := module_call_c_prefix(module_name)
3291 if call_prefix != '' {
3292 keys << '${call_prefix}__${decl.name}'
3293 }
3294 }
3295 } else {
3296 recv_name := t.get_receiver_type_name(decl.receiver.typ)
3297 if recv_name != '' {
3298 keys << '${recv_name}__${decl.name}'
3299 if module_name != '' && !recv_name.contains('__') {
3300 keys << '${module_name.replace('.', '__')}__${recv_name}__${decl.name}'
3301 call_prefix := module_call_c_prefix(module_name)
3302 if call_prefix != '' {
3303 keys << '${call_prefix}__${recv_name}__${decl.name}'
3304 }
3305 }
3306 }
3307 }
3308 return keys
3309}
3310
3311fn (mut t Transformer) index_generic_fn_decl_for_monomorphize(mut decl_owner map[string]int, mut decl_node map[string]ast.FnDecl, decl ast.FnDecl, file_idx int, module_name string) {
3312 keys := t.generic_fn_decl_monomorphize_keys(decl, module_name)
3313 for key in keys {
3314 if key == '' {
3315 continue
3316 }
3317 if key in decl_node {
3318 continue
3319 }
3320 decl_owner[key] = file_idx
3321 decl_node[key] = decl
3322 }
3323}
3324
3325fn (t &Transformer) resolve_monomorphize_decl_key(fn_key string, decl_node map[string]ast.FnDecl) ?string {
3326 if fn_key in decl_node {
3327 return fn_key
3328 }
3329 mut base_name := fn_key
3330 bracket_pos := base_name.index_u8(`[`)
3331 if bracket_pos > 0 {
3332 base_name = base_name[..bracket_pos]
3333 }
3334 if base_name in decl_node {
3335 return base_name
3336 }
3337 if base_name.contains('.') {
3338 c_name := base_name.replace('.', '__')
3339 if c_name in decl_node {
3340 return c_name
3341 }
3342 }
3343 if base_name.contains('__') {
3344 mut suffix_name := base_name.all_after('__')
3345 for suffix_name.contains('__') {
3346 if suffix_name in decl_node {
3347 return suffix_name
3348 }
3349 suffix_name = suffix_name.all_after('__')
3350 }
3351 }
3352 return none
3353}
3354
3355fn (t &Transformer) resolve_generic_decl_key_for_call(base_name string, decl_node map[string]ast.FnDecl) ?string {
3356 if receiver_template := receiver_specialized_method_template_name(base_name) {
3357 if key := t.resolve_monomorphize_decl_key(receiver_template, decl_node) {
3358 return key
3359 }
3360 }
3361 mut name := generic_base_name_without_specialization(base_name)
3362 bracket_pos := name.index_u8(`[`)
3363 if bracket_pos > 0 {
3364 name = name[..bracket_pos]
3365 }
3366 if name == '' {
3367 return none
3368 }
3369 if !name.contains('__') && !name.contains('.') && t.cur_module != ''
3370 && t.cur_module != 'builtin' {
3371 if t.cur_module != 'main' {
3372 call_prefix := module_call_c_prefix(t.cur_module)
3373 if call_prefix != '' {
3374 candidate := '${call_prefix}__${name}'
3375 if candidate in decl_node {
3376 return candidate
3377 }
3378 }
3379 module_prefix := t.cur_module.replace('.', '__')
3380 if module_prefix != '' {
3381 candidate := '${module_prefix}__${name}'
3382 if candidate in decl_node {
3383 return candidate
3384 }
3385 }
3386 candidate := '${t.cur_module}.${name}'
3387 if candidate in decl_node {
3388 return candidate
3389 }
3390 }
3391 if t.has_current_module_concrete_fn(name) {
3392 return none
3393 }
3394 }
3395 return t.resolve_monomorphize_decl_key(name, decl_node)
3396}
3397
3398fn receiver_specialized_method_template_name(name string) ?string {
3399 if name == '' {
3400 return none
3401 }
3402 mut base := name
3403 bracket_pos := base.index_u8(`[`)
3404 if bracket_pos > 0 {
3405 base = base[..bracket_pos]
3406 }
3407 t_idx := base.index('_T_') or { return none }
3408 method_sep := first_double_underscore_after(base, t_idx + 3)
3409 if method_sep < 0 || method_sep + 2 >= base.len {
3410 return none
3411 }
3412 receiver_base := base[..t_idx]
3413 if receiver_base == '' {
3414 return none
3415 }
3416 return receiver_base + base[method_sep..]
3417}
3418
3419fn first_double_underscore_after(s string, start int) int {
3420 mut i := if start < 0 { 0 } else { start }
3421 for i + 1 < s.len {
3422 if s[i] == `_` && s[i + 1] == `_` {
3423 return i
3424 }
3425 i++
3426 }
3427 return -1
3428}
3429
3430fn (t &Transformer) has_current_module_concrete_fn(name string) bool {
3431 return t.has_concrete_fn_in_module(t.cur_module, name)
3432 || (t.cur_module == 'main' && t.has_concrete_fn_in_module('', name))
3433}
3434
3435fn (t &Transformer) has_concrete_fn_in_module(module_name string, name string) bool {
3436 if fn_type := t.lookup_fn_cached(module_name, name) {
3437 return fn_type.get_generic_params().len == 0
3438 }
3439 scope := t.get_module_scope(module_name) or { return false }
3440 obj := scope.objects[name] or { return false }
3441 if obj is types.Fn {
3442 typ := obj.get_typ()
3443 if typ is types.FnType {
3444 return typ.get_generic_params().len == 0
3445 }
3446 }
3447 return false
3448}
3449
3450fn (mut t Transformer) collect_generic_call_specs(files []ast.File) {
3451 old_module := t.cur_module
3452 old_file := t.cur_file_name
3453 old_scope := t.scope
3454 old_file_idx := t.cur_generic_call_file_idx
3455 old_import_aliases := t.cur_import_aliases.clone()
3456 t.build_generic_fn_decl_index(files)
3457 for fi, file in files {
3458 t.cur_file_name = file.name
3459 t.cur_module = file.mod
3460 t.cur_generic_call_file_idx = fi
3461 t.cur_import_aliases = import_aliases_for_generic_collect(file.imports)
3462 if scope := t.get_module_scope(file.mod) {
3463 t.scope = scope
3464 } else {
3465 t.scope = unsafe { nil }
3466 }
3467 for stmt in file.stmts {
3468 t.collect_generic_call_specs_in_stmt(stmt)
3469 }
3470 }
3471 t.cur_module = old_module
3472 t.cur_file_name = old_file
3473 t.scope = old_scope
3474 t.cur_generic_call_file_idx = old_file_idx
3475 t.cur_import_aliases = old_import_aliases.clone()
3476}
3477
3478fn (mut t Transformer) collect_generic_call_specs_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
3479 old_module := t.cur_module
3480 old_file := t.cur_file_name
3481 old_scope := t.scope
3482 old_file_idx := t.cur_generic_call_file_idx
3483 old_import_aliases := t.cur_import_aliases.clone()
3484 t.build_generic_fn_decl_index_from_flat(flat, extra_stmts)
3485 for fi in 0 .. flat.files.len {
3486 module_name := flat.file_mod(flat.files[fi])
3487 t.cur_file_name = flat.file_name(flat.files[fi])
3488 t.cur_module = module_name
3489 t.cur_generic_call_file_idx = fi
3490 t.cur_import_aliases = flat_import_aliases_for_generic_collect(flat, fi)
3491 if scope := t.get_module_scope(module_name) {
3492 t.scope = scope
3493 } else {
3494 t.scope = unsafe { nil }
3495 }
3496 stmts := flat.file_cursor(fi).stmts()
3497 for si in 0 .. stmts.len() {
3498 t.collect_generic_call_specs_in_stmt_cursor(stmts.at(si))
3499 }
3500 extra := extra_stmts[fi] or { []ast.Stmt{} }
3501 for stmt in extra {
3502 t.collect_generic_call_specs_in_stmt(stmt)
3503 }
3504 }
3505 t.cur_module = old_module
3506 t.cur_file_name = old_file
3507 t.scope = old_scope
3508 t.cur_generic_call_file_idx = old_file_idx
3509 t.cur_import_aliases = old_import_aliases.clone()
3510}
3511
3512// build_generic_fn_decl_index (re)builds t.generic_fn_decl_index from every
3513// generic FnDecl across all files. Generic declarations never change during the
3514// fixpoint (monomorphize_pass only appends concrete clones), but the index maps
3515// keys to file indices, so it is rebuilt against the current file set.
3516fn (mut t Transformer) build_generic_fn_decl_index(files []ast.File) {
3517 t.generic_fn_decl_index = map[string]ast.FnDecl{}
3518 t.generic_call_candidate_names = map[string]bool{}
3519 mut dummy_owner := map[string]int{}
3520 for fi, file in files {
3521 for stmt in file.stmts {
3522 if stmt is ast.FnDecl {
3523 if decl_generic_param_names(stmt).len == 0 {
3524 continue
3525 }
3526 t.index_generic_fn_decl_for_monomorphize(mut dummy_owner, mut
3527 t.generic_fn_decl_index, stmt, fi, file.mod)
3528 }
3529 }
3530 }
3531 for key, _ in t.generic_fn_decl_index {
3532 t.register_generic_call_candidate_name(key)
3533 }
3534 for name, _ in t.generic_fn_value_names {
3535 t.register_generic_call_candidate_name(name)
3536 }
3537}
3538
3539fn (mut t Transformer) build_generic_fn_decl_index_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
3540 t.generic_fn_decl_index = map[string]ast.FnDecl{}
3541 t.generic_call_candidate_names = map[string]bool{}
3542 mut dummy_owner := map[string]int{}
3543 for fi in 0 .. flat.files.len {
3544 module_name := flat.file_mod(flat.files[fi])
3545 stmts := flat.file_cursor(fi).stmts()
3546 for si in 0 .. stmts.len() {
3547 stmt_c := stmts.at(si)
3548 if stmt_c.kind() != .stmt_fn_decl {
3549 continue
3550 }
3551 stmt := stmt_c.fn_decl_signature()
3552 if decl_generic_param_names(stmt).len == 0 {
3553 continue
3554 }
3555 t.index_generic_fn_decl_for_monomorphize(mut dummy_owner, mut t.generic_fn_decl_index,
3556 stmt, fi, module_name)
3557 }
3558 extra := extra_stmts[fi] or { []ast.Stmt{} }
3559 for stmt in extra {
3560 if stmt !is ast.FnDecl {
3561 continue
3562 }
3563 decl := stmt as ast.FnDecl
3564 if decl_generic_param_names(decl).len == 0 {
3565 continue
3566 }
3567 t.index_generic_fn_decl_for_monomorphize(mut dummy_owner, mut t.generic_fn_decl_index,
3568 decl, fi, module_name)
3569 }
3570 }
3571 for key, _ in t.generic_fn_decl_index {
3572 t.register_generic_call_candidate_name(key)
3573 }
3574 for name, _ in t.generic_fn_value_names {
3575 t.register_generic_call_candidate_name(name)
3576 }
3577}
3578
3579fn (mut t Transformer) register_generic_call_candidate_name(name string) {
3580 base := generic_base_name_without_specialization(name)
3581 if base == '' {
3582 return
3583 }
3584 t.generic_call_candidate_names[base] = true
3585 dot := base.last_index_u8(`.`)
3586 if dot >= 0 {
3587 if dot + 1 < base.len {
3588 t.generic_call_candidate_names[base[dot + 1..]] = true
3589 }
3590 }
3591 dunder := last_double_underscore(base)
3592 if dunder >= 0 {
3593 if dunder + 2 < base.len {
3594 t.generic_call_candidate_names[base[dunder + 2..]] = true
3595 }
3596 }
3597}
3598
3599fn (t &Transformer) may_target_generic_call_name(name string) bool {
3600 if name == '' {
3601 return false
3602 }
3603 base := generic_base_name_without_specialization(name)
3604 return base in t.generic_call_candidate_names
3605}
3606
3607// collect_generic_call_specs_in_new_clones is the incremental counterpart of
3608// collect_generic_call_specs. After monomorphize_pass appends concrete clones,
3609// only those clones can introduce generic calls the previous scan did not see —
3610// every other statement is byte-for-byte unchanged and was already walked. So
3611// this rebuilds the generic-decl index (cheap, shallow) but deep-walks only the
3612// clones recorded in t.last_mono_clones, each under its owning file's module,
3613// scope and import context. For v2 self-host this replaces a full ~6s/~700MB
3614// rescan with a walk of a few dozen functions.
3615fn (mut t Transformer) collect_generic_call_specs_in_new_clones(files []ast.File) {
3616 if t.last_mono_clones.len == 0 {
3617 return
3618 }
3619 old_module := t.cur_module
3620 old_file := t.cur_file_name
3621 old_scope := t.scope
3622 old_file_idx := t.cur_generic_call_file_idx
3623 old_import_aliases := t.cur_import_aliases.clone()
3624 t.build_generic_fn_decl_index(files)
3625 for fi, file in files {
3626 if fi !in t.last_mono_clones {
3627 continue
3628 }
3629 clones := t.last_mono_clones[fi]
3630 if clones.len == 0 {
3631 continue
3632 }
3633 t.cur_file_name = file.name
3634 t.cur_module = file.mod
3635 t.cur_generic_call_file_idx = fi
3636 t.cur_import_aliases = import_aliases_for_generic_collect(file.imports)
3637 if scope := t.get_module_scope(file.mod) {
3638 t.scope = scope
3639 } else {
3640 t.scope = unsafe { nil }
3641 }
3642 for stmt in clones {
3643 t.collect_generic_call_specs_in_stmt(stmt)
3644 }
3645 }
3646 t.cur_module = old_module
3647 t.cur_file_name = old_file
3648 t.scope = old_scope
3649 t.cur_generic_call_file_idx = old_file_idx
3650 t.cur_import_aliases = old_import_aliases.clone()
3651}
3652
3653fn (mut t Transformer) collect_generic_call_specs_in_new_clones_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
3654 if t.last_mono_clones.len == 0 {
3655 return
3656 }
3657 old_module := t.cur_module
3658 old_file := t.cur_file_name
3659 old_scope := t.scope
3660 old_file_idx := t.cur_generic_call_file_idx
3661 old_import_aliases := t.cur_import_aliases.clone()
3662 t.build_generic_fn_decl_index_from_flat(flat, extra_stmts)
3663 for fi in 0 .. flat.files.len {
3664 clones := t.last_mono_clones[fi] or { continue }
3665 if clones.len == 0 {
3666 continue
3667 }
3668 module_name := flat.file_mod(flat.files[fi])
3669 t.cur_file_name = flat.file_name(flat.files[fi])
3670 t.cur_module = module_name
3671 t.cur_generic_call_file_idx = fi
3672 t.cur_import_aliases = flat_import_aliases_for_generic_collect(flat, fi)
3673 if scope := t.get_module_scope(module_name) {
3674 t.scope = scope
3675 } else {
3676 t.scope = unsafe { nil }
3677 }
3678 for stmt in clones {
3679 t.collect_generic_call_specs_in_stmt(stmt)
3680 }
3681 }
3682 t.cur_module = old_module
3683 t.cur_file_name = old_file
3684 t.scope = old_scope
3685 t.cur_generic_call_file_idx = old_file_idx
3686 t.cur_import_aliases = old_import_aliases.clone()
3687}
3688
3689fn (mut t Transformer) collect_generic_call_specs_in_new_structs(files []ast.File) {
3690 if t.last_struct_clones.len == 0 {
3691 return
3692 }
3693 old_module := t.cur_module
3694 old_file := t.cur_file_name
3695 old_scope := t.scope
3696 old_file_idx := t.cur_generic_call_file_idx
3697 old_import_aliases := t.cur_import_aliases.clone()
3698 t.build_generic_fn_decl_index(files)
3699 for fi, file in files {
3700 clones := t.last_struct_clones[fi] or { continue }
3701 if clones.len == 0 {
3702 continue
3703 }
3704 t.cur_file_name = file.name
3705 t.cur_module = file.mod
3706 t.cur_generic_call_file_idx = fi
3707 t.cur_import_aliases = import_aliases_for_generic_collect(file.imports)
3708 if scope := t.get_module_scope(file.mod) {
3709 t.scope = scope
3710 } else {
3711 t.scope = unsafe { nil }
3712 }
3713 for stmt in clones {
3714 t.collect_generic_call_specs_in_stmt(stmt)
3715 }
3716 }
3717 t.cur_module = old_module
3718 t.cur_file_name = old_file
3719 t.scope = old_scope
3720 t.cur_generic_call_file_idx = old_file_idx
3721 t.cur_import_aliases = old_import_aliases.clone()
3722}
3723
3724fn (mut t Transformer) collect_generic_call_specs_in_new_structs_from_flat(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt) {
3725 if t.last_struct_clones.len == 0 {
3726 return
3727 }
3728 old_module := t.cur_module
3729 old_file := t.cur_file_name
3730 old_scope := t.scope
3731 old_file_idx := t.cur_generic_call_file_idx
3732 old_import_aliases := t.cur_import_aliases.clone()
3733 t.build_generic_fn_decl_index_from_flat(flat, extra_stmts)
3734 for fi in 0 .. flat.files.len {
3735 clones := t.last_struct_clones[fi] or { continue }
3736 if clones.len == 0 {
3737 continue
3738 }
3739 module_name := flat.file_mod(flat.files[fi])
3740 t.cur_file_name = flat.file_name(flat.files[fi])
3741 t.cur_module = module_name
3742 t.cur_generic_call_file_idx = fi
3743 t.cur_import_aliases = flat_import_aliases_for_generic_collect(flat, fi)
3744 if scope := t.get_module_scope(module_name) {
3745 t.scope = scope
3746 } else {
3747 t.scope = unsafe { nil }
3748 }
3749 for stmt in clones {
3750 t.collect_generic_call_specs_in_stmt(stmt)
3751 }
3752 }
3753 t.cur_module = old_module
3754 t.cur_file_name = old_file
3755 t.scope = old_scope
3756 t.cur_generic_call_file_idx = old_file_idx
3757 t.cur_import_aliases = old_import_aliases.clone()
3758}
3759
3760fn import_aliases_for_generic_collect(imports []ast.ImportStmt) map[string]string {
3761 mut aliases := map[string]string{}
3762 for imp in imports {
3763 alias := if imp.alias != '' { imp.alias } else { imp.name.all_after_last('.') }
3764 if alias == '' || imp.name == '' {
3765 continue
3766 }
3767 aliases[alias] = imp.name
3768 }
3769 return aliases
3770}
3771
3772fn flat_import_aliases_for_generic_collect(flat &ast.FlatAst, fi int) map[string]string {
3773 if fi < 0 || fi >= flat.files.len {
3774 return map[string]string{}
3775 }
3776 imports := flat.file_cursor(fi).imports()
3777 mut aliases := map[string]string{}
3778 for i in 0 .. imports.len() {
3779 imp := imports.at(i)
3780 if imp.kind() != .stmt_import {
3781 continue
3782 }
3783 name := imp.name()
3784 alias := if imp.extra_str() != '' { imp.extra_str() } else { name.all_after_last('.') }
3785 if alias == '' || name == '' {
3786 continue
3787 }
3788 aliases[alias] = name
3789 }
3790 return aliases
3791}
3792
3793fn flat_file_struct_decls_with_extra(flat &ast.FlatAst, extra_stmts map[int][]ast.Stmt, fi int) []ast.StructDecl {
3794 if fi < 0 || fi >= flat.files.len {
3795 return []ast.StructDecl{}
3796 }
3797 stmt_cursors := flat.file_cursor(fi).stmts()
3798 extra := extra_stmts[fi] or { []ast.Stmt{} }
3799 mut decls := []ast.StructDecl{cap: stmt_cursors.len() + extra.len}
3800 for i in 0 .. stmt_cursors.len() {
3801 stmt_c := stmt_cursors.at(i)
3802 if stmt_c.kind() == .stmt_struct_decl {
3803 decls << stmt_c.struct_decl()
3804 }
3805 }
3806 for stmt in extra {
3807 if stmt is ast.StructDecl {
3808 decls << stmt
3809 }
3810 }
3811 return decls
3812}
3813
3814fn append_flat_extra_stmts(mut extra_stmts map[int][]ast.Stmt, fi int, stmts []ast.Stmt) {
3815 if stmts.len == 0 {
3816 return
3817 }
3818 mut bucket := extra_stmts[fi] or { []ast.Stmt{} }
3819 bucket << stmts
3820 extra_stmts[fi] = bucket
3821}
3822
3823fn (mut t Transformer) collect_generic_call_specs_in_stmts(stmts []ast.Stmt) {
3824 for stmt in stmts {
3825 t.collect_generic_call_specs_in_stmt(stmt)
3826 }
3827}
3828
3829fn (mut t Transformer) collect_generic_call_specs_in_cursor_list(stmts ast.CursorList) {
3830 for i in 0 .. stmts.len() {
3831 t.collect_generic_call_specs_in_stmt_cursor(stmts.at(i))
3832 }
3833}
3834
3835fn (mut t Transformer) collect_generic_call_specs_in_stmt_cursor(stmt ast.Cursor) {
3836 if !stmt.is_valid() {
3837 return
3838 }
3839 match stmt.kind() {
3840 .stmt_assert {
3841 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(0))
3842 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(1))
3843 }
3844 .stmt_assign {
3845 lhs_len := stmt.extra_int()
3846 for i in 0 .. lhs_len {
3847 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(i))
3848 }
3849 for i in lhs_len .. stmt.edge_count() {
3850 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(i))
3851 }
3852 t.collect_generic_scan_decl_assign_types_cursor(stmt)
3853 }
3854 .stmt_block {
3855 for i in 0 .. stmt.edge_count() {
3856 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(i))
3857 }
3858 }
3859 .stmt_comptime {
3860 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(0))
3861 }
3862 .stmt_const_decl {
3863 fields := stmt.list_at(0)
3864 for i in 0 .. fields.len() {
3865 t.collect_generic_call_specs_in_expr_cursor(fields.at(i).edge(0))
3866 }
3867 }
3868 .stmt_defer {
3869 for i in 0 .. stmt.edge_count() {
3870 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(i))
3871 }
3872 }
3873 .stmt_enum_decl {
3874 fields := stmt.list_at(2)
3875 for i in 0 .. fields.len() {
3876 t.collect_generic_call_specs_in_expr_cursor(fields.at(i).edge(1))
3877 }
3878 }
3879 .stmt_expr {
3880 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(0))
3881 }
3882 .stmt_for_in {
3883 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(0))
3884 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(1))
3885 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(2))
3886 }
3887 .stmt_for {
3888 init := stmt.edge(0)
3889 if init.kind() == .stmt_for_in {
3890 t.collect_generic_call_specs_in_for_stmt_cursor(stmt, init)
3891 return
3892 }
3893 t.collect_generic_call_specs_in_stmt_cursor(init)
3894 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(1))
3895 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(2))
3896 for i in 3 .. stmt.edge_count() {
3897 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(i))
3898 }
3899 }
3900 .stmt_fn_decl {
3901 signature := stmt.fn_decl_signature()
3902 if decl_generic_param_names(signature).len == 0 {
3903 t.collect_generic_call_specs_in_fn_decl_cursor(stmt, signature)
3904 }
3905 }
3906 .stmt_global_decl {
3907 fields := stmt.list_at(1)
3908 for i in 0 .. fields.len() {
3909 t.collect_generic_call_specs_in_expr_cursor(fields.at(i).edge(1))
3910 }
3911 }
3912 .stmt_import, .stmt_module {}
3913 .stmt_return {
3914 for i in 0 .. stmt.edge_count() {
3915 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(i))
3916 }
3917 }
3918 .stmt_struct_decl {
3919 fields := stmt.list_at(4)
3920 for i in 0 .. fields.len() {
3921 field := fields.at(i)
3922 t.collect_generic_call_specs_in_expr_cursor(field.edge(0))
3923 t.collect_generic_call_specs_in_expr_cursor(field.edge(1))
3924 }
3925 embedded := stmt.list_at(2)
3926 for i in 0 .. embedded.len() {
3927 t.collect_generic_call_specs_in_expr_cursor(embedded.at(i))
3928 }
3929 implemented := stmt.list_at(1)
3930 for i in 0 .. implemented.len() {
3931 t.collect_generic_call_specs_in_expr_cursor(implemented.at(i))
3932 }
3933 }
3934 else {}
3935 }
3936}
3937
3938fn (mut t Transformer) collect_generic_call_specs_in_stmt(stmt ast.Stmt) {
3939 match stmt {
3940 ast.AssertStmt {
3941 t.collect_generic_call_specs_in_expr(stmt.expr)
3942 t.collect_generic_call_specs_in_expr(stmt.extra)
3943 }
3944 ast.AssignStmt {
3945 for expr in stmt.lhs {
3946 t.collect_generic_call_specs_in_expr(expr)
3947 }
3948 for expr in stmt.rhs {
3949 t.collect_generic_call_specs_in_expr(expr)
3950 }
3951 t.collect_generic_scan_decl_assign_types(stmt)
3952 }
3953 ast.BlockStmt {
3954 t.collect_generic_call_specs_in_stmts(stmt.stmts)
3955 }
3956 ast.ComptimeStmt {
3957 t.collect_generic_call_specs_in_stmt(stmt.stmt)
3958 }
3959 ast.ConstDecl {
3960 for field in stmt.fields {
3961 t.collect_generic_call_specs_in_expr(field.value)
3962 }
3963 }
3964 ast.DeferStmt {
3965 t.collect_generic_call_specs_in_stmts(stmt.stmts)
3966 }
3967 ast.EnumDecl {
3968 for field in stmt.fields {
3969 t.collect_generic_call_specs_in_expr(field.value)
3970 }
3971 }
3972 ast.ExprStmt {
3973 t.collect_generic_call_specs_in_expr(stmt.expr)
3974 }
3975 ast.ForInStmt {
3976 t.collect_generic_call_specs_in_expr(stmt.key)
3977 t.collect_generic_call_specs_in_expr(stmt.value)
3978 t.collect_generic_call_specs_in_expr(stmt.expr)
3979 }
3980 ast.ForStmt {
3981 if stmt.init is ast.ForInStmt {
3982 t.collect_generic_call_specs_in_for_stmt(stmt, stmt.init as ast.ForInStmt)
3983 return
3984 }
3985 t.collect_generic_call_specs_in_stmt(stmt.init)
3986 t.collect_generic_call_specs_in_expr(stmt.cond)
3987 t.collect_generic_call_specs_in_stmt(stmt.post)
3988 t.collect_generic_call_specs_in_stmts(stmt.stmts)
3989 }
3990 ast.FnDecl {
3991 if decl_generic_param_names(stmt).len == 0 {
3992 t.collect_generic_call_specs_in_fn_decl(stmt)
3993 }
3994 }
3995 ast.GlobalDecl {
3996 for field in stmt.fields {
3997 t.collect_generic_call_specs_in_expr(field.value)
3998 }
3999 }
4000 ast.ImportStmt {}
4001 ast.ModuleStmt {}
4002 ast.ReturnStmt {
4003 for expr in stmt.exprs {
4004 t.collect_generic_call_specs_in_expr(expr)
4005 }
4006 }
4007 ast.StructDecl {
4008 for field in stmt.fields {
4009 t.collect_generic_call_specs_in_expr(field.typ)
4010 t.collect_generic_call_specs_in_expr(field.value)
4011 }
4012 for embedded in stmt.embedded {
4013 t.collect_generic_call_specs_in_expr(embedded)
4014 }
4015 for implemented in stmt.implements {
4016 t.collect_generic_call_specs_in_expr(implemented)
4017 }
4018 }
4019 else {}
4020 }
4021}
4022
4023fn (mut t Transformer) collect_generic_call_specs_in_for_stmt_cursor(stmt ast.Cursor, for_in ast.Cursor) {
4024 t.collect_generic_call_specs_in_expr_cursor(for_in.edge(0))
4025 t.collect_generic_call_specs_in_expr_cursor(for_in.edge(1))
4026 t.collect_generic_call_specs_in_expr_cursor(for_in.edge(2))
4027 t.collect_generic_call_specs_in_expr_cursor(stmt.edge(1))
4028 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(2))
4029 old_local_decl_types := t.local_decl_types.clone()
4030 t.seed_generic_scan_for_in_var_types_cursor(for_in)
4031 for i in 3 .. stmt.edge_count() {
4032 t.collect_generic_call_specs_in_stmt_cursor(stmt.edge(i))
4033 }
4034 t.local_decl_types = old_local_decl_types.clone()
4035}
4036
4037fn (mut t Transformer) collect_generic_call_specs_in_for_stmt(stmt ast.ForStmt, for_in ast.ForInStmt) {
4038 t.collect_generic_call_specs_in_expr(for_in.key)
4039 t.collect_generic_call_specs_in_expr(for_in.value)
4040 t.collect_generic_call_specs_in_expr(for_in.expr)
4041 t.collect_generic_call_specs_in_expr(stmt.cond)
4042 t.collect_generic_call_specs_in_stmt(stmt.post)
4043 old_local_decl_types := t.local_decl_types.clone()
4044 t.seed_generic_scan_for_in_var_types(for_in)
4045 t.collect_generic_call_specs_in_stmts(stmt.stmts)
4046 t.local_decl_types = old_local_decl_types.clone()
4047}
4048
4049fn (mut t Transformer) seed_generic