v4 / vlib / v3 / types / checker.v
9632 lines · 9282 sloc · 270.64 KB · bd87753646a4c2ae1384399584858dc14cb90b28
Raw
1module types
2
3import v3.flat
4
5const export_c_reserved_words = {
6 'auto': true
7 'break': true
8 'case': true
9 'char': true
10 'const': true
11 'continue': true
12 'default': true
13 'do': true
14 'double': true
15 'else': true
16 'enum': true
17 'extern': true
18 'float': true
19 'for': true
20 'goto': true
21 'if': true
22 'inline': true
23 'int': true
24 'long': true
25 'register': true
26 'restrict': true
27 'return': true
28 'short': true
29 'signed': true
30 'sizeof': true
31 'static': true
32 'struct': true
33 'switch': true
34 'typedef': true
35 'union': true
36 'unsigned': true
37 'void': true
38 'volatile': true
39 'while': true
40}
41
42const export_v3_reserved_c_symbols = {
43 'i8': true
44 'i16': true
45 'i32': true
46 'i64': true
47 'u8': true
48 'byte': true
49 'u16': true
50 'u32': true
51 'u64': true
52 'bool': true
53 'voidptr': true
54 'int_literal': true
55 'float_literal': true
56 'chan': true
57 'string': true
58 'array': true
59 'Array': true
60 'map': true
61 'mapnode': true
62 'DenseArray': true
63 'SortedMap': true
64 'Optional': true
65 'IError': true
66 'true': true
67 'false': true
68 'elem_size': true
69 'c_name': true
70}
71
72const export_c_libc_collision_symbols = {
73 'rint': true
74 'y0': true
75 'y1': true
76 'yn': true
77 'j0': true
78 'j1': true
79 'jn': true
80 'drem': true
81 'scalb': true
82}
83
84// tarr1 supports tarr1 handling for types.
85fn tarr1(a Type) []Type {
86 mut r := []Type{}
87 r << a
88 return r
89}
90
91// tarr2 supports tarr2 handling for types.
92fn tarr2(a Type, b Type) []Type {
93 mut r := []Type{}
94 r << a
95 r << b
96 return r
97}
98
99// tarr3 supports tarr3 handling for types.
100fn tarr3(a Type, b Type, c Type) []Type {
101 mut r := []Type{}
102 r << a
103 r << b
104 r << c
105 return r
106}
107
108// unknown_type supports unknown type handling for types.
109fn unknown_type(reason string) Type {
110 return Type(Unknown{
111 reason: reason
112 })
113}
114
115// TypeError represents type error data used by types.
116pub struct TypeError {
117pub:
118 msg string
119 kind TypeErrorKind
120 node flat.NodeId
121}
122
123// TypeErrorKind lists type error kind values used by types.
124pub enum TypeErrorKind {
125 unknown_ident
126 unknown_type
127 unknown_fn
128 unknown_field
129 cannot_index
130 if_branch_mismatch
131 assignment_mismatch
132 return_mismatch
133 call_arg_mismatch
134 condition_mismatch
135 unhandled_node
136 unsupported_generic
137}
138
139// CallInfo stores call info metadata used by types.
140pub struct CallInfo {
141pub:
142 name string
143 params []Type
144 return_type Type
145 has_receiver bool
146 is_variadic bool
147 is_c_variadic bool
148 params_known bool
149 has_implicit_veb_ctx bool
150}
151
152// LocalBinding represents local binding data used by types.
153struct LocalBinding {
154 name string
155 typ Type
156}
157
158// TypeCache represents type cache data used by types.
159struct TypeCache {
160mut:
161 parse_enabled bool
162 parse_entries map[string]Type
163 c_entries map[string]string
164}
165
166// TypeChecker represents type checker data used by types.
167@[heap]
168pub struct TypeChecker {
169pub mut:
170 a &flat.FlatAst = unsafe { nil }
171 fn_ret_types map[string]Type
172 fn_param_types map[string][]Type
173 fn_ret_type_texts map[string]string // generic struct method key -> original return type text (e.g. `Box[T].clone` -> `Box[T]`)
174 fn_param_type_texts map[string][]string // generic struct method key -> original param type texts (receiver first)
175 fn_type_files map[string]string
176 fn_type_modules map[string]string
177 fn_generic_params map[string][]string
178 fn_variadic map[string]bool
179 c_variadic_fns map[string]bool
180 fn_implicit_veb_ctx map[string]bool
181 structs map[string][]StructField
182 struct_generic_params map[string][]string // generic struct base name -> type-param names (e.g. Vec4 -> [T])
183 struct_field_c_abi_fns map[string]string
184 // concrete `Box[int].method` -> substituted CallInfo for a method *value* on a
185 // generic receiver. The open `Box[T].method` registration is gone by cgen time, so
186 // the checker stashes the resolved signature here for gen_method_value_closure.
187 generic_method_value_info map[string]CallInfo
188 params_structs map[string]bool
189 unions map[string]bool
190 type_aliases map[string]string
191 type_alias_c_abi_fns map[string]string
192 sum_types map[string][]string
193 enum_names map[string]bool
194 enum_fields map[string][]string
195 flag_enums map[string]bool
196 interface_names map[string]bool
197 interface_fields map[string][]StructField
198 interface_embeds map[string][]string
199 interface_abstract_methods map[string][]string // iface -> abstract (declared) method names
200
201 c_globals map[string]Type
202 const_types map[string]Type
203 const_exprs map[string]flat.NodeId
204 const_modules map[string]string
205 const_suffixes map[string]string // dot-suffix -> full const key (O(1) lookup; '' if ambiguous)
206 imports map[string]string // alias -> short module name
207 file_imports map[string]string
208 file_selective_imports map[string][]string
209 file_modules map[string]string
210 file_scope &Scope = unsafe { nil }
211 cur_scope &Scope = unsafe { nil }
212 scope_pool []&Scope
213 scope_pool_index int
214 has_builtins bool
215 cur_module string
216 cur_file string
217 errors []TypeError
218 resolved_call_names []string // node_id -> resolved function name
219 resolved_call_set []bool
220 resolved_fn_value_names []string // node_id -> resolved function value name
221 resolved_fn_value_set []bool
222 // Methods used as *values* (`recv.method` passed as a callback), recorded per enclosing
223 // function during semantic checking — which has full scope/type info, runs before
224 // markused, and (unlike a call) routes a value-context selector through check_selector.
225 // markused seeds these (keeping the wrapper-only method out of the dead-code pruner)
226 // only when their enclosing function is reachable.
227 method_values_by_fn map[int][]string // enclosing fn node id -> method-value `Type.method` keys
228 // Local variables bound to a method value (`cb := c.report`) in the current function.
229 // Such an alias shares the same per-site static receiver slot as the bare selector, so it
230 // escapes (and corrupts other live callbacks) just like `return c.report`; the escape
231 // checks below treat a reference to one of these locals as a method value. Reset per fn.
232 method_value_locals map[string]bool
233 // Scope depth at which each method-value local was marked, so a reassignment to a
234 // non-method value only clears the marker when it dominates later uses (same-or-shallower
235 // scope); a reassignment in a deeper conditional/loop scope keeps the maybe-method marker.
236 method_value_local_depth map[string]int
237 cur_fn_node_id int = -1
238 expr_type_values []Type // node_id -> complex/contextual resolved type
239 expr_type_set []bool
240 checking_nodes []bool
241 diagnose_unknown_calls bool
242 reject_unlowered_map_mutation bool
243 reject_unsupported_generics bool
244 diagnostic_files map[string]bool
245 cur_fn_ret_type Type = Type(void_)
246 smartcasts map[string]Type
247 selfhost bool
248mut:
249 type_cache &TypeCache = unsafe { nil }
250}
251
252// new creates a TypeChecker value for types.
253pub fn TypeChecker.new(a &flat.FlatAst) TypeChecker {
254 fs := new_scope(unsafe { nil })
255 return TypeChecker{
256 a: a
257 fn_ret_types: map[string]Type{}
258 fn_param_types: map[string][]Type{}
259 fn_ret_type_texts: map[string]string{}
260 fn_param_type_texts: map[string][]string{}
261 fn_type_files: map[string]string{}
262 fn_type_modules: map[string]string{}
263 fn_generic_params: map[string][]string{}
264 fn_variadic: map[string]bool{}
265 c_variadic_fns: map[string]bool{}
266 fn_implicit_veb_ctx: map[string]bool{}
267 structs: map[string][]StructField{}
268 struct_generic_params: map[string][]string{}
269 struct_field_c_abi_fns: map[string]string{}
270 generic_method_value_info: map[string]CallInfo{}
271 params_structs: map[string]bool{}
272 unions: map[string]bool{}
273 type_aliases: map[string]string{}
274 type_alias_c_abi_fns: map[string]string{}
275 sum_types: map[string][]string{}
276 enum_names: map[string]bool{}
277 enum_fields: map[string][]string{}
278 flag_enums: map[string]bool{}
279 interface_names: map[string]bool{}
280 interface_fields: map[string][]StructField{}
281 interface_embeds: map[string][]string{}
282 interface_abstract_methods: map[string][]string{}
283 c_globals: map[string]Type{}
284 const_types: map[string]Type{}
285 const_exprs: map[string]flat.NodeId{}
286 const_modules: map[string]string{}
287 const_suffixes: map[string]string{}
288 imports: map[string]string{}
289 file_imports: map[string]string{}
290 file_selective_imports: map[string][]string{}
291 file_modules: map[string]string{}
292 file_scope: fs
293 cur_scope: fs
294 resolved_call_names: []string{len: a.nodes.len}
295 resolved_call_set: []bool{len: a.nodes.len}
296 resolved_fn_value_names: []string{len: a.nodes.len}
297 resolved_fn_value_set: []bool{len: a.nodes.len}
298 method_values_by_fn: map[int][]string{}
299 method_value_locals: map[string]bool{}
300 method_value_local_depth: map[string]int{}
301 expr_type_values: []Type{len: a.nodes.len, init: Type(void_)}
302 expr_type_set: []bool{len: a.nodes.len}
303 checking_nodes: []bool{len: a.nodes.len}
304 diagnostic_files: map[string]bool{}
305 smartcasts: map[string]Type{}
306 type_cache: &TypeCache{
307 parse_entries: map[string]Type{}
308 c_entries: map[string]string{}
309 }
310 }
311}
312
313// fork_for_parallel_transform returns a TypeChecker that shares all of `tc`'s
314// read-only data (semantic maps and node-indexed cache arrays, which the transform
315// pass only reads) but owns a fresh, private `type_cache` and a private AST view.
316// During transform the only hidden mutation a TypeChecker performs through its `&`
317// receiver is memoization into `type_cache` (parse_type / c_type); giving each
318// worker its own cache makes concurrent use race-free without cloning the large
319// semantic maps. `ast` must be the worker's own (cloned) FlatAst so that any
320// expr_type lookup on a freshly-created node id indexes a valid array.
321pub fn (tc &TypeChecker) fork_for_parallel_transform(ast &flat.FlatAst) &TypeChecker {
322 mut forked := *tc
323 forked.a = ast
324 forked.type_cache = &TypeCache{
325 parse_enabled: if tc.type_cache != unsafe { nil } {
326 tc.type_cache.parse_enabled
327 } else {
328 false
329 }
330 parse_entries: map[string]Type{}
331 c_entries: map[string]string{}
332 }
333 return &forked
334}
335
336// reset_node_caches updates reset node caches state for types.
337fn (mut tc TypeChecker) reset_node_caches(n int) {
338 tc.resolved_call_names = []string{len: n}
339 tc.resolved_call_set = []bool{len: n}
340 tc.resolved_fn_value_names = []string{len: n}
341 tc.resolved_fn_value_set = []bool{len: n}
342 tc.expr_type_values = []Type{len: n, init: Type(void_)}
343 tc.expr_type_set = []bool{len: n}
344 tc.checking_nodes = []bool{len: n}
345}
346
347fn (mut tc TypeChecker) extend_node_caches(n int) {
348 if n <= tc.resolved_call_names.len && n <= tc.resolved_fn_value_names.len
349 && n <= tc.expr_type_values.len && n <= tc.checking_nodes.len {
350 return
351 }
352 extend_string_cache(mut tc.resolved_call_names, n)
353 extend_bool_cache(mut tc.resolved_call_set, n)
354 extend_string_cache(mut tc.resolved_fn_value_names, n)
355 extend_bool_cache(mut tc.resolved_fn_value_set, n)
356 extend_type_cache(mut tc.expr_type_values, n)
357 extend_bool_cache(mut tc.expr_type_set, n)
358 extend_bool_cache(mut tc.checking_nodes, n)
359}
360
361fn extend_string_cache(mut values []string, n int) {
362 if n > values.len {
363 values << []string{len: n - values.len}
364 }
365}
366
367fn extend_bool_cache(mut values []bool, n int) {
368 if n > values.len {
369 values << []bool{len: n - values.len}
370 }
371}
372
373fn extend_type_cache(mut values []Type, n int) {
374 if n > values.len {
375 values << []Type{len: n - values.len, init: Type(void_)}
376 }
377}
378
379// push_scope updates push scope state for TypeChecker.
380pub fn (mut tc TypeChecker) push_scope() {
381 tc.cur_scope = tc.reuse_scope(tc.cur_scope)
382}
383
384// pop_scope updates pop scope state for TypeChecker.
385pub fn (mut tc TypeChecker) pop_scope() {
386 if tc.cur_scope == unsafe { nil } {
387 return
388 }
389 parent := tc.cur_scope.parent
390 if parent == unsafe { nil } {
391 return
392 }
393 if tc.scope_pool_index > 0 && tc.cur_scope == tc.scope_pool[tc.scope_pool_index - 1] {
394 tc.scope_pool_index--
395 }
396 tc.cur_scope = parent
397}
398
399// reuse_scope supports reuse scope handling for TypeChecker.
400fn (mut tc TypeChecker) reuse_scope(parent &Scope) &Scope {
401 if tc.scope_pool_index < tc.scope_pool.len {
402 mut scope := tc.scope_pool[tc.scope_pool_index]
403 scope.reset(parent)
404 tc.scope_pool_index++
405 return scope
406 }
407 scope := new_scope(parent)
408 tc.scope_pool << scope
409 tc.scope_pool_index++
410 return scope
411}
412
413// record_error supports record error handling for TypeChecker.
414fn (mut tc TypeChecker) record_error(kind TypeErrorKind, msg string, node flat.NodeId) {
415 if !tc.should_diagnose(node) {
416 return
417 }
418 tc.errors << TypeError{
419 msg: msg
420 kind: kind
421 node: node
422 }
423}
424
425fn (mut tc TypeChecker) record_error_unfiltered(kind TypeErrorKind, msg string, node flat.NodeId) {
426 tc.errors << TypeError{
427 msg: msg
428 kind: kind
429 node: node
430 }
431}
432
433fn (mut tc TypeChecker) record_unsupported_generic(msg string, node flat.NodeId) {
434 if !tc.should_diagnose_unsupported_generic(node) {
435 return
436 }
437 tc.errors << TypeError{
438 msg: msg
439 kind: .unsupported_generic
440 node: node
441 }
442}
443
444// collect supports collect handling for TypeChecker.
445pub fn (mut tc TypeChecker) collect(a &flat.FlatAst) {
446 tc.a = a
447 tc.file_scope = new_scope(unsafe { nil })
448 tc.cur_scope = tc.file_scope
449 tc.scope_pool_index = 0
450 tc.reset_node_caches(a.nodes.len)
451 tc.type_cache = &TypeCache{
452 parse_entries: map[string]Type{}
453 c_entries: map[string]string{}
454 }
455 for node in a.nodes {
456 if node.kind == .struct_decl && node.value == 'string' {
457 tc.has_builtins = true
458 break
459 }
460 }
461 // Pass 1: collect type-level names (aliases, enums, sum types)
462 for node in a.nodes {
463 match node.kind {
464 .file {
465 tc.enter_file(node.value)
466 }
467 .module_decl {
468 tc.enter_module(node.value)
469 }
470 .import_decl {
471 tc.imports[node.typ] = node.value
472 tc.file_imports[file_import_key(tc.cur_file, node.typ)] = node.value
473 tc.register_selective_imports(node)
474 }
475 .enum_decl {
476 qn := tc.qualify_name(node.value)
477 tc.enum_names[qn] = true
478 mut fields := []string{}
479 for i in 0 .. node.children_count {
480 f := a.child_node(&node, i)
481 if f.kind == .enum_field {
482 fields << f.value
483 }
484 }
485 tc.enum_fields[qn] = fields
486 if node.typ == 'flag' {
487 tc.flag_enums[qn] = true
488 }
489 }
490 .struct_decl {
491 qname := tc.qualify_name(node.value)
492 if qname !in tc.structs {
493 tc.structs[qname] = []StructField{}
494 }
495 if node.generic_params.len > 0 {
496 tc.struct_generic_params[qname] = node.generic_params.clone()
497 if qname != node.value {
498 tc.struct_generic_params[node.value] = node.generic_params.clone()
499 }
500 }
501 if node.typ.contains('union') {
502 tc.unions[qname] = true
503 }
504 if node.typ.contains('params') {
505 tc.params_structs[qname] = true
506 }
507 }
508 .type_decl {
509 if node.children_count > 0 {
510 mut variants := []string{}
511 for i in 0 .. node.children_count {
512 v := a.child_node(&node, i)
513 variants << tc.qualify_name(v.value)
514 }
515 tc.sum_types[tc.qualify_name(node.value)] = variants
516 } else if node.typ.len > 0 {
517 qname := tc.qualify_name(node.value)
518 tc.type_aliases[qname] = tc.qualify_type_text(node.typ)
519 if c_abi_fn := tc.c_abi_fn_ptr_type_from_text(node.typ) {
520 tc.type_alias_c_abi_fns[qname] = c_abi_fn
521 }
522 if tc.cur_module in ['', 'main', 'builtin'] && node.value !in tc.type_aliases {
523 tc.type_aliases[node.value] = tc.qualify_type_text(node.typ)
524 if c_abi_fn := tc.c_abi_fn_ptr_type_from_text(node.typ) {
525 tc.type_alias_c_abi_fns[node.value] = c_abi_fn
526 }
527 }
528 }
529 }
530 .interface_decl {
531 tc.interface_names[tc.qualify_name(node.value)] = true
532 }
533 else {}
534 }
535 }
536 // Pass 2: collect struct fields, function signatures (type aliases now available)
537 tc.type_cache.parse_enabled = true
538 tc.cur_module = ''
539 for node in a.nodes {
540 match node.kind {
541 .file {
542 tc.enter_file(node.value)
543 }
544 .module_decl {
545 tc.enter_module(node.value)
546 }
547 .fn_decl {
548 qname := tc.qualify_fn_name(node.value)
549 ret_type := tc.parse_type(node.typ)
550 mut ptypes := []Type{}
551 mut param_texts := []string{}
552 mut is_variadic := false
553 for i in 0 .. node.children_count {
554 child := a.child_node(&node, i)
555 if child.kind == .param {
556 if child.typ.starts_with('...') {
557 is_variadic = true
558 }
559 ptypes << tc.parse_type(child.typ)
560 param_texts << child.typ
561 }
562 }
563 needs_ctx := tc.fn_needs_implicit_veb_ctx(node)
564 ptypes = tc.fn_param_types_with_implicit_veb_ctx(node, ptypes)
565 tc.register_fn_signature(qname, ret_type, ptypes, is_variadic, needs_ctx)
566 if tc.cur_module in ['', 'main', 'builtin'] && qname != node.value
567 && node.value !in tc.fn_param_types {
568 tc.register_fn_signature(node.value, ret_type, ptypes, is_variadic, needs_ctx)
569 }
570 // A generic struct method (`Box[T].clone`) keeps its original signature
571 // TEXT: the parsed types collapse a non-concrete `Box[T]` to the bare base,
572 // so a concrete call must re-substitute the type arguments from the text to
573 // recover applications like the receiver type in the return position
574 // (`Box[T]` -> `Box[int]`). Only such methods carry `[` in their key.
575 if node.generic_params.len > 0 || node.value.contains('[') {
576 for name in [qname, node.value] {
577 tc.fn_ret_type_texts[name] = node.typ
578 tc.fn_param_type_texts[name] = param_texts.clone()
579 tc.fn_type_files[name] = tc.cur_file
580 tc.fn_type_modules[name] = tc.cur_module
581 if node.generic_params.len > 0 {
582 tc.fn_generic_params[name] = node.generic_params.clone()
583 }
584 }
585 }
586 }
587 .struct_decl {
588 mut fields := []StructField{}
589 mut field_c_abi_fns := map[string]string{}
590 for i in 0 .. node.children_count {
591 f := a.child_node(&node, i)
592 if f.kind != .field_decl {
593 continue
594 }
595 field_typ := if f.typ.len > 0 { f.typ } else { f.value }
596 mut typ := tc.parse_type(field_typ)
597 if f.value == field_typ {
598 typ = tc.resolve_known_field_type(field_typ, typ)
599 }
600 if c_abi_fn := tc.c_abi_fn_ptr_type_for_type_text(field_typ) {
601 field_c_abi_fns[f.value] = c_abi_fn
602 }
603 fields << StructField{
604 name: f.value
605 typ: typ
606 }
607 }
608 qname := tc.qualify_name(node.value)
609 tc.structs[qname] = fields
610 for field_name, c_abi_fn in field_c_abi_fns {
611 tc.struct_field_c_abi_fns[struct_field_c_abi_key(qname, field_name)] = c_abi_fn
612 }
613 if node.typ.contains('union') {
614 tc.unions[qname] = true
615 }
616 if node.typ.contains('params') {
617 tc.params_structs[qname] = true
618 }
619 }
620 .c_fn_decl {
621 ret_type := tc.parse_type(node.typ)
622 mut ptypes := []Type{}
623 mut is_variadic := false
624 for i in 0 .. node.children_count {
625 child := a.child_node(&node, i)
626 if child.kind == .param {
627 if child.typ.starts_with('...') {
628 is_variadic = true
629 }
630 ptypes << tc.parse_type(child.typ)
631 }
632 }
633 tc.register_fn_signature(node.value, ret_type, ptypes, is_variadic, false)
634 if is_variadic {
635 tc.register_c_variadic_fn(node.value)
636 }
637 if !node.value.starts_with('C.') {
638 tc.register_fn_signature('C.${node.value}', ret_type, ptypes, is_variadic,
639 false)
640 if is_variadic {
641 tc.register_c_variadic_fn('C.${node.value}')
642 }
643 }
644 }
645 .interface_decl {
646 iface_name := tc.qualify_name(node.value)
647 for i in 0 .. node.children_count {
648 f := a.child_node(&node, i)
649 if f.kind != .interface_field {
650 continue
651 }
652 if f.op == .dot {
653 mname := '${iface_name}.${f.value}'
654 mut absm := tc.interface_abstract_methods[iface_name] or { []string{} }
655 absm << f.value
656 tc.interface_abstract_methods[iface_name] = absm
657 tc.fn_ret_types[mname] = tc.parse_type(f.typ)
658 mut ptypes := []Type{}
659 mut is_variadic := false
660 ptypes << Type(Pointer{
661 base_type: Type(Interface{
662 name: iface_name
663 })
664 })
665 for j in 0 .. f.children_count {
666 child := a.child_node(f, j)
667 if child.kind == .param {
668 if child.typ.starts_with('...') {
669 is_variadic = true
670 }
671 ptypes << tc.parse_type(child.typ)
672 }
673 }
674 tc.fn_param_types[mname] = ptypes
675 tc.fn_variadic[mname] = is_variadic
676 } else if f.typ.len > 0 {
677 mut fields := tc.interface_fields[iface_name] or { []StructField{} }
678 fields << StructField{
679 name: f.value
680 typ: tc.parse_type(f.typ)
681 }
682 tc.interface_fields[iface_name] = fields
683 } else if f.value.len > 0 {
684 mut embeds := tc.interface_embeds[iface_name] or { []string{} }
685 embeds << tc.qualify_name(f.value)
686 tc.interface_embeds[iface_name] = embeds
687 }
688 }
689 }
690 .global_decl {
691 for i in 0 .. node.children_count {
692 f := a.child_node(&node, i)
693 if f.value.len > 0 && f.value.starts_with('C.') {
694 tc.c_globals[f.value] = tc.parse_type(f.typ)
695 } else if f.value.len > 0 {
696 mut ft := tc.parse_type(f.typ)
697 if ft is Void && f.children_count > 0 {
698 ft = tc.resolve_type(a.child(f, 0))
699 }
700 qname := tc.qualify_name(f.value)
701 tc.file_scope.insert(qname, ft)
702 }
703 }
704 }
705 .const_decl {
706 for i in 0 .. node.children_count {
707 f := a.child_node(&node, i)
708 if f.kind == .const_field && f.children_count > 0 {
709 qname := tc.qualify_name(f.value)
710 tc.const_types[qname] = unknown_type('pending const `${qname}`')
711 tc.const_exprs[qname] = a.child(f, 0)
712 tc.const_modules[qname] = tc.cur_module
713 } else if f.kind == .const_field && f.typ.len > 0 {
714 qname := tc.qualify_name(f.value)
715 tc.const_types[qname] = tc.parse_type(f.typ)
716 }
717 }
718 }
719 else {}
720 }
721 }
722 tc.resolve_const_types()
723 tc.build_const_suffixes()
724}
725
726// build_const_suffixes maps every dot-delimited suffix of each const key to that
727// key, so qualified const lookups resolve in O(1) instead of scanning all consts
728// (with per-iteration string building) on every selector/ident in module code.
729fn (mut tc TypeChecker) build_const_suffixes() {
730 for key, _ in tc.const_types {
731 mut i := 0
732 for i < key.len {
733 if key[i] == `.` {
734 suffix := key[i + 1..]
735 if existing := tc.const_suffixes[suffix] {
736 if existing != key {
737 tc.const_suffixes[suffix] = ''
738 }
739 } else {
740 tc.const_suffixes[suffix] = key
741 }
742 }
743 i++
744 }
745 }
746}
747
748// const_key_for_suffix returns the const key matching `.${name}` as a suffix,
749// equivalent to scanning const_types for `key.ends_with('.' + name)` but O(1).
750fn (tc &TypeChecker) const_key_for_suffix(name string) ?string {
751 if key := tc.const_suffixes[name] {
752 if key.len > 0 {
753 return key
754 }
755 }
756 return none
757}
758
759// resolve_const_types resolves resolve const types information for types.
760fn (mut tc TypeChecker) resolve_const_types() {
761 if tc.const_exprs.len == 0 {
762 return
763 }
764 saved_module := tc.cur_module
765 for _ in 0 .. tc.const_exprs.len {
766 mut changed := false
767 for name, expr_id in tc.const_exprs {
768 tc.cur_module = tc.const_modules[name] or { '' }
769 mut new_type := tc.resolve_type(expr_id)
770 new_type = tc.const_type_from_initializer(name, new_type)
771 if new_type is Unknown {
772 continue
773 }
774 expr := tc.a.nodes[int(expr_id)]
775 if new_type is ArrayFixed && expr.kind == .call {
776 new_type = Type(Array{
777 elem_type: new_type.elem_type
778 })
779 }
780 old_type := tc.const_types[name] or { Type(void_) }
781 if old_type.name() != new_type.name() {
782 tc.const_types[name] = new_type
783 changed = true
784 }
785 }
786 if !changed {
787 break
788 }
789 }
790 tc.cur_module = saved_module
791}
792
793// const_type_from_initializer converts const type from initializer data for types.
794fn (tc &TypeChecker) const_type_from_initializer(name string, typ Type) Type {
795 if typ !is Unknown {
796 return typ
797 }
798 expr_id := tc.const_exprs[name] or { return typ }
799 if int(expr_id) < 0 || int(expr_id) >= tc.a.nodes.len {
800 return typ
801 }
802 expr := tc.a.nodes[int(expr_id)]
803 if expr.kind != .call || expr.children_count == 0 {
804 return typ
805 }
806 fn_node := tc.a.child_node(&expr, 0)
807 if fn_node.kind != .ident || fn_node.value.len == 0 {
808 return typ
809 }
810 mod_name := tc.const_modules[name] or { '' }
811 mut candidates := []string{}
812 if mod_name.len > 0 && mod_name != 'main' && mod_name != 'builtin' {
813 candidates << '${mod_name}.${fn_node.value}'
814 }
815 candidates << fn_node.value
816 candidates << c_name(fn_node.value)
817 for candidate in candidates {
818 if ret := tc.fn_ret_types[candidate] {
819 return ret
820 }
821 }
822 if fn_node.value == 'new_keywords_matcher_trie' {
823 type_name := if mod_name.len > 0 && mod_name != 'main' && mod_name != 'builtin' {
824 '${mod_name}.KeywordsMatcherTrie'
825 } else {
826 'KeywordsMatcherTrie'
827 }
828 return Type(Struct{
829 name: type_name
830 })
831 }
832 return typ
833}
834
835// qualify_fn_name supports qualify fn name handling for TypeChecker.
836pub fn (tc &TypeChecker) qualify_fn_name(name string) string {
837 if tc.cur_module.len == 0 || tc.cur_module == 'main' || tc.cur_module == 'builtin' {
838 return name
839 }
840 return '${tc.cur_module}.${name}'
841}
842
843// qualify_name supports qualify name handling for TypeChecker.
844pub fn (tc &TypeChecker) qualify_name(name string) string {
845 if tc.cur_module.len == 0 || tc.cur_module == 'main' || tc.cur_module == 'builtin' {
846 return name
847 }
848 if name.starts_with('[]') {
849 return '[]' + tc.qualify_name(name[2..])
850 }
851 if name.starts_with('[') {
852 idx := name.index_u8(`]`)
853 if idx > 0 {
854 return name[..idx + 1] + tc.qualify_name(name[idx + 1..])
855 }
856 }
857 if name.starts_with('map[') {
858 bracket_end := find_matching_bracket(name, 3)
859 key_str := name[4..bracket_end]
860 val_str := name[bracket_end + 1..]
861 return 'map[${tc.qualify_name(key_str)}]${tc.qualify_name(val_str)}'
862 }
863 if name.starts_with('&') {
864 return '&' + tc.qualify_name(name[1..])
865 }
866 if name.starts_with('?') {
867 return '?' + tc.qualify_name(name[1..])
868 }
869 if name.contains('.') {
870 return tc.resolve_imported_type_text(name)
871 }
872 if is_builtin_type_name(name) {
873 return name
874 }
875 return tc.cur_module + '.' + name
876}
877
878// qualify_type_text supports qualify type text handling for TypeChecker.
879fn (tc &TypeChecker) qualify_type_text(typ string) string {
880 clean := typ.trim_space()
881 if clean.len == 0 {
882 return typ
883 }
884 if clean.starts_with('&') {
885 return '&' + tc.qualify_type_text(clean[1..])
886 }
887 if clean.starts_with('mut ') {
888 inner := tc.qualify_type_text(clean[4..])
889 if inner.starts_with('&') {
890 return inner
891 }
892 return '&' + inner
893 }
894 if clean.starts_with('shared ') {
895 return 'shared ' + tc.qualify_type_text(clean[7..])
896 }
897 if clean.starts_with('atomic ') {
898 return 'atomic ' + tc.qualify_type_text(clean[7..])
899 }
900 if clean.starts_with('?') {
901 return '?' + tc.qualify_type_text(clean[1..])
902 }
903 if clean.starts_with('!') {
904 return '!' + tc.qualify_type_text(clean[1..])
905 }
906 if clean.starts_with('...') {
907 return '...' + tc.qualify_type_text(clean[3..])
908 }
909 if clean.starts_with('[]') {
910 return '[]' + tc.qualify_type_text(clean[2..])
911 }
912 if clean.starts_with('map[') {
913 bracket_end := find_matching_bracket(clean, 3)
914 if bracket_end < clean.len {
915 key := tc.qualify_type_text(clean[4..bracket_end])
916 val := tc.qualify_type_text(clean[bracket_end + 1..])
917 return 'map[${key}]${val}'
918 }
919 }
920 if clean.starts_with('[') {
921 bracket_end := find_matching_bracket(clean, 0)
922 if bracket_end < clean.len {
923 return clean[..bracket_end + 1] + tc.qualify_type_text(clean[bracket_end + 1..])
924 }
925 }
926 if clean.starts_with('(') && clean.ends_with(')') && clean.contains(',') {
927 mut parts := []string{}
928 for part in split_params(clean[1..clean.len - 1]) {
929 parts << tc.qualify_type_text(part)
930 }
931 return '(' + parts.join(', ') + ')'
932 }
933 if clean.starts_with('fn(') || clean.starts_with('fn (') {
934 return tc.qualify_fn_type_text(clean)
935 }
936 bracket := clean.index_u8(`[`)
937 if bracket > 0 {
938 bracket_end := find_matching_bracket(clean, bracket)
939 if bracket_end < clean.len {
940 mut parts := []string{}
941 for part in split_params(clean[bracket + 1..bracket_end]) {
942 parts << tc.qualify_type_text(part)
943 }
944 return tc.qualify_name(clean[..bracket]) + '[' + parts.join(', ') + ']' +
945 clean[bracket_end + 1..]
946 }
947 }
948 return tc.qualify_name(clean)
949}
950
951// qualify_fn_type_text supports qualify fn type text handling for TypeChecker.
952fn (tc &TypeChecker) qualify_fn_type_text(typ string) string {
953 params_start := typ.index_u8(`(`) + 1
954 mut depth := 1
955 mut params_end := params_start
956 for params_end < typ.len {
957 if typ[params_end] == `(` {
958 depth++
959 } else if typ[params_end] == `)` {
960 depth--
961 if depth == 0 {
962 break
963 }
964 }
965 params_end++
966 }
967 params_str := typ[params_start..params_end]
968 mut params := []string{}
969 if params_str.trim_space().len > 0 {
970 for part in split_params(params_str) {
971 params << tc.qualify_type_text(normalize_fn_type_param_text(part))
972 }
973 }
974 ret_str := typ[params_end + 1..].trim_space()
975 if ret_str.len > 0 {
976 return 'fn(${params.join(', ')}) ${tc.qualify_type_text(ret_str)}'
977 }
978 return 'fn(${params.join(', ')})'
979}
980
981// file_import_key supports file import key handling for types.
982fn file_import_key(file string, alias string) string {
983 return '${file}\n${alias}'
984}
985
986// enter_file supports enter file handling for TypeChecker.
987fn (mut tc TypeChecker) enter_file(file string) {
988 tc.cur_file = file
989 tc.cur_module = tc.file_modules[file] or { '' }
990}
991
992// enter_module supports enter module handling for TypeChecker.
993fn (mut tc TypeChecker) enter_module(name string) {
994 tc.cur_module = name
995 if tc.cur_file.len > 0 {
996 tc.file_modules[tc.cur_file] = name
997 }
998}
999
1000fn (mut tc TypeChecker) register_selective_imports(node flat.Node) {
1001 if node.children_count == 0 {
1002 return
1003 }
1004 for i in 0 .. node.children_count {
1005 child_id := tc.a.child(&node, i)
1006 child := tc.a.nodes[int(child_id)]
1007 if child.kind != .ident {
1008 continue
1009 }
1010 key := file_import_key(tc.cur_file, child.value)
1011 if key in tc.file_selective_imports {
1012 tc.file_selective_imports[key] = []string{}
1013 tc.record_error_unfiltered(.unknown_fn, 'ambiguous selective import `${child.value}`',
1014 child_id)
1015 continue
1016 }
1017 mut candidates := []string{}
1018 path_name := '${node.value}.${child.value}'
1019 if path_name !in candidates {
1020 candidates << path_name
1021 }
1022 alias_name := '${node.typ}.${child.value}'
1023 if alias_name != path_name && alias_name !in candidates {
1024 candidates << alias_name
1025 }
1026 tc.file_selective_imports[key] = candidates
1027 }
1028}
1029
1030// resolve_import_alias resolves resolve import alias information for types.
1031fn (tc &TypeChecker) resolve_import_alias(alias string) ?string {
1032 if mod := tc.file_imports[file_import_key(tc.cur_file, alias)] {
1033 return mod
1034 }
1035 return none
1036}
1037
1038fn (tc &TypeChecker) resolve_selective_import_symbol(name string) ?string {
1039 candidates := tc.file_selective_imports[file_import_key(tc.cur_file, name)] or { return none }
1040 for candidate in candidates {
1041 if tc.fn_signature_known(candidate) {
1042 return candidate
1043 }
1044 }
1045 return none
1046}
1047
1048fn (tc &TypeChecker) resolve_selective_import_type_symbol(name string) ?string {
1049 candidates := tc.file_selective_imports[file_import_key(tc.cur_file, name)] or { return none }
1050 for candidate in candidates {
1051 if tc.type_symbol_known(candidate) {
1052 return candidate
1053 }
1054 }
1055 return none
1056}
1057
1058fn (tc &TypeChecker) type_symbol_known(name string) bool {
1059 return name in tc.type_aliases || name in tc.structs || name in tc.interface_names
1060 || name in tc.flag_enums || name in tc.enum_names || name in tc.sum_types
1061}
1062
1063fn (tc &TypeChecker) type_from_known_symbol(name string) ?Type {
1064 if name in tc.type_aliases {
1065 return Type(Alias{
1066 name: name
1067 base_type: tc.parse_type(tc.type_aliases[name])
1068 })
1069 }
1070 if name in tc.structs {
1071 return Type(Struct{
1072 name: name
1073 })
1074 }
1075 if name in tc.interface_names {
1076 return Type(Interface{
1077 name: name
1078 })
1079 }
1080 if name in tc.flag_enums {
1081 return Type(Enum{
1082 name: name
1083 is_flag: true
1084 })
1085 }
1086 if name in tc.enum_names {
1087 return Type(Enum{
1088 name: name
1089 })
1090 }
1091 if name in tc.sum_types {
1092 return Type(SumType{
1093 name: name
1094 })
1095 }
1096 return none
1097}
1098
1099fn (tc &TypeChecker) selective_import_symbol_is_ambiguous(name string) bool {
1100 candidates := tc.file_selective_imports[file_import_key(tc.cur_file, name)] or { return false }
1101 return candidates.len == 0
1102}
1103
1104fn (tc &TypeChecker) resolve_imported_type_text(typ string) string {
1105 if !typ.contains('.') || typ.starts_with('C.') {
1106 return typ
1107 }
1108 dot := typ.index_u8(`.`)
1109 if dot <= 0 {
1110 return typ
1111 }
1112 alias := typ[..dot]
1113 if resolved := tc.resolve_import_alias(alias) {
1114 if resolved != alias {
1115 return resolved + typ[dot..]
1116 }
1117 }
1118 return typ
1119}
1120
1121// has_active_import reports whether has active import applies in types.
1122fn (tc &TypeChecker) has_active_import(alias string) bool {
1123 return file_import_key(tc.cur_file, alias) in tc.file_imports
1124}
1125
1126// register_fn_signature updates register fn signature state for types.
1127fn (mut tc TypeChecker) register_fn_signature(name string, ret_type Type, params []Type, is_variadic bool, implicit_veb_ctx bool) {
1128 tc.register_fn_name_alias(name, ret_type, params, is_variadic, implicit_veb_ctx)
1129 lowered_name := c_name(name)
1130 if lowered_name != name {
1131 tc.register_fn_name_alias(lowered_name, ret_type, params, is_variadic, implicit_veb_ctx)
1132 }
1133 if name.ends_with('.str') {
1134 receiver := name.all_before_last('.')
1135 legacy_name := '${receiver}_str'
1136 if !legacy_name.contains('.') {
1137 tc.register_fn_name_alias(legacy_name, ret_type, params, is_variadic, implicit_veb_ctx)
1138 }
1139 }
1140}
1141
1142// register_fn_name_alias updates register fn name alias state for types.
1143fn (mut tc TypeChecker) register_fn_name_alias(name string, ret_type Type, params []Type, is_variadic bool, implicit_veb_ctx bool) {
1144 tc.fn_ret_types[name] = ret_type
1145 tc.fn_param_types[name] = params.clone()
1146 tc.fn_variadic[name] = is_variadic
1147 tc.fn_implicit_veb_ctx[name] = implicit_veb_ctx
1148}
1149
1150fn (mut tc TypeChecker) register_c_variadic_fn(name string) {
1151 if name.len == 0 {
1152 return
1153 }
1154 tc.c_variadic_fns[name] = true
1155 lowered_name := c_name(name)
1156 if lowered_name != name {
1157 tc.c_variadic_fns[lowered_name] = true
1158 }
1159 if !name.starts_with('C.') {
1160 tc.c_variadic_fns['C.${name}'] = true
1161 }
1162}
1163
1164// annotate_types performs a scope-aware walk over every function body, tracking
1165// local variable types as they are declared, and records complex/contextual
1166// expression types. This mirrors what the v2 transformer relies on: the type
1167// checker runs BEFORE the transformer and publishes per-expression types, so the
1168// transformer can own type-dependent lowering (string ops, `in` membership, ...)
1169// instead of the backend.
1170//
1171// It uses a single flat scope per function (an over-approximation: a local stays
1172// visible after its block ends), which is harmless for type lookup since variable
1173// names are effectively unique within a function.
1174pub fn (mut tc TypeChecker) annotate_types() {
1175 tc.annotate_types_with_used(map[string]bool{})
1176}
1177
1178// annotate_types_with_used annotates only functions that can be emitted when
1179// `used_fns` is non-empty. This mirrors transform/cgen pruning and avoids
1180// resolving types in dead, untransformed function bodies after markused.
1181pub fn (mut tc TypeChecker) annotate_types_with_used(used_fns map[string]bool) {
1182 tc.extend_node_caches(tc.a.nodes.len)
1183 tc.cur_module = ''
1184 for node in tc.a.nodes {
1185 if node.kind == .file {
1186 tc.enter_file(node.value)
1187 } else if node.kind == .module_decl {
1188 tc.enter_module(node.value)
1189 } else if node.kind == .fn_decl {
1190 if !tc.should_annotate_fn(node, used_fns) {
1191 continue
1192 }
1193 tc.cur_scope = tc.file_scope
1194 tc.push_scope()
1195 for pi in 0 .. node.children_count {
1196 p := tc.a.child_node(&node, pi)
1197 if p.kind == .param && p.value.len > 0 {
1198 tc.cur_scope.insert(p.value, tc.parse_type(p.typ))
1199 }
1200 }
1201 tc.insert_implicit_veb_ctx(node)
1202 for i in 0 .. node.children_count {
1203 child := tc.a.child_node(&node, i)
1204 if child.kind != .param {
1205 tc.annotate_node(tc.a.child(&node, i))
1206 }
1207 }
1208 tc.pop_scope()
1209 }
1210 }
1211}
1212
1213fn (tc &TypeChecker) should_annotate_fn(node flat.Node, used_fns map[string]bool) bool {
1214 if used_fns.len == 0 {
1215 return true
1216 }
1217 qname := checker_qualified_fn_name(tc.cur_module, node.value)
1218 if qname in tc.a.export_fn_names || tc.fn_needs_implicit_veb_ctx(node) {
1219 return true
1220 }
1221 if node.value in used_fns {
1222 return true
1223 }
1224 if qname in used_fns {
1225 return true
1226 }
1227 cname := c_name(qname)
1228 if cname != qname && cname in used_fns {
1229 return true
1230 }
1231 return false
1232}
1233
1234fn checker_qualified_fn_name(mod string, name string) string {
1235 if mod.len == 0 || mod == 'main' || mod == 'builtin' {
1236 return name
1237 }
1238 return '${mod}.${name}'
1239}
1240
1241// annotate_node supports annotate node handling for TypeChecker.
1242fn (mut tc TypeChecker) annotate_node(id flat.NodeId) {
1243 if int(id) < 0 {
1244 return
1245 }
1246 node := tc.a.nodes[int(id)]
1247 match node.kind {
1248 .decl_assign {
1249 // children are interleaved pairs [lhs0, rhs0, lhs1, rhs1, ...].
1250 // Multi-return (`a, b := f()`) yields an odd count; we only insert
1251 // locals for clean pairs and skip MultiReturn rhs values.
1252 mut i := 0
1253 for i + 1 < node.children_count {
1254 lhs_id := tc.a.child(&node, i)
1255 rhs_id := tc.a.child(&node, i + 1)
1256 tc.annotate_node(rhs_id)
1257 lhs := tc.a.nodes[int(lhs_id)]
1258 if lhs.kind == .ident && lhs.value.len > 0 {
1259 mut typ := Type(void_)
1260 if node.children_count == 2 && node.typ.len > 0 {
1261 typ = tc.parse_type(node.typ)
1262 tc.annotate_expected_expr(rhs_id, typ)
1263 } else {
1264 typ = tc.resolve_type(rhs_id)
1265 }
1266 if typ !is MultiReturn && typ !is Void {
1267 tc.cur_scope.insert(lhs.value, typ)
1268 tc.remember_expr_type(lhs_id, typ)
1269 }
1270 }
1271 i += 2
1272 }
1273 return
1274 }
1275 .for_in_stmt {
1276 tc.annotate_for_in(id, node)
1277 return
1278 }
1279 .assign, .selector_assign, .index_assign {
1280 tc.annotate_assign_expected_exprs(node)
1281 }
1282 .struct_init {
1283 tc.annotate_struct_init_expected_exprs(node)
1284 }
1285 .call {
1286 tc.annotate_call_expected_exprs(id, node)
1287 }
1288 else {}
1289 }
1290
1291 tc.remember_expr_type(id, tc.resolve_type(id))
1292 for i in 0 .. node.children_count {
1293 tc.annotate_node(tc.a.child(&node, i))
1294 }
1295}
1296
1297fn (mut tc TypeChecker) annotate_expected_expr(id flat.NodeId, expected Type) {
1298 if _ := fn_type_from_type(expected) {
1299 _ = tc.resolve_expr(id, expected)
1300 }
1301}
1302
1303fn (mut tc TypeChecker) annotate_assign_expected_exprs(node flat.Node) {
1304 if node.children_count < 2 {
1305 return
1306 }
1307 mut i := 0
1308 for i + 1 < node.children_count {
1309 lhs_id := tc.a.child(&node, i)
1310 rhs_id := tc.a.child(&node, i + 1)
1311 lhs_type := tc.resolve_lvalue_type(lhs_id)
1312 tc.annotate_expected_expr(rhs_id, lhs_type)
1313 i += 2
1314 }
1315}
1316
1317fn (mut tc TypeChecker) annotate_struct_init_expected_exprs(node flat.Node) {
1318 init_type := tc.parse_type(node.value)
1319 init_struct := struct_type_from_type(init_type) or { return }
1320 init_name := init_struct.name
1321 fields := tc.structs[init_name] or { []StructField{} }
1322 for i in 0 .. node.children_count {
1323 field := tc.a.child_node(&node, i)
1324 if field.kind != .field_init || field.children_count == 0 {
1325 continue
1326 }
1327 value_id := tc.a.child(field, 0)
1328 mut expected := Type(void_)
1329 if field.value.len > 0 {
1330 expected = tc.struct_field_type(init_name, field.value) or { Type(void_) }
1331 } else if i < fields.len {
1332 expected = fields[i].typ
1333 }
1334 tc.annotate_expected_expr(value_id, expected)
1335 }
1336}
1337
1338fn (mut tc TypeChecker) annotate_call_expected_exprs(id flat.NodeId, node flat.Node) {
1339 info := tc.resolve_call_info(id, node) or { return }
1340 if info.name.len > 0 && !is_array_dsl_call_name(info.name) {
1341 tc.remember_resolved_call(id, info.name)
1342 }
1343 if !info.params_known || info.params.len == 0 {
1344 return
1345 }
1346 mut field_init_args := 0
1347 for i in 1 .. node.children_count {
1348 if tc.a.child_node(&node, i).kind == .field_init {
1349 field_init_args++
1350 }
1351 }
1352 collapsed := if field_init_args > 0 { 1 } else { 0 }
1353 recv_extra := if info.has_receiver { 1 } else { 0 }
1354 actual_count := node.children_count - 1 - field_init_args + collapsed + recv_extra
1355 ctx_count := if info.has_implicit_veb_ctx { 1 } else { 0 }
1356 ctx_omitted := ctx_count > 0 && actual_count < info.params.len
1357 for i in 1 .. node.children_count {
1358 raw_arg := tc.a.child_node(&node, i)
1359 arg_id := tc.call_arg_value(tc.a.child(&node, i))
1360 if raw_arg.kind == .field_init {
1361 tc.annotate_params_field_expected_expr(arg_id, raw_arg.value, info)
1362 continue
1363 }
1364 arg_shift := if ctx_omitted { ctx_count } else { 0 }
1365 param_idx := (if info.has_receiver { i } else { i - 1 }) + arg_shift
1366 if info.is_c_variadic && param_idx >= c_variadic_fixed_param_count(info) {
1367 continue
1368 }
1369 if param_idx >= info.params.len {
1370 continue
1371 }
1372 expected := tc.call_arg_expected_type(info, param_idx)
1373 tc.annotate_expected_expr(arg_id, expected)
1374 }
1375}
1376
1377fn (tc &TypeChecker) call_arg_expected_type(info CallInfo, param_idx int) Type {
1378 expected := info.params[param_idx]
1379 if info.is_variadic && param_idx == info.params.len - 1 && expected is Array {
1380 return array_elem_type(expected)
1381 }
1382 return expected
1383}
1384
1385fn (mut tc TypeChecker) annotate_params_field_expected_expr(arg_id flat.NodeId, field_name string, info CallInfo) {
1386 if field_name.len == 0 {
1387 return
1388 }
1389 for param in info.params {
1390 clean := unwrap_pointer(param)
1391 if clean is Struct {
1392 if expected := tc.struct_field_type(clean.name, field_name) {
1393 tc.annotate_expected_expr(arg_id, expected)
1394 return
1395 }
1396 }
1397 }
1398}
1399
1400// annotate_for_in supports annotate for in handling for TypeChecker.
1401fn (mut tc TypeChecker) annotate_for_in(_id flat.NodeId, node flat.Node) {
1402 header := node.value.int()
1403 if header < 3 || node.children_count < 3 {
1404 return
1405 }
1406 key_id := tc.a.child(&node, 0)
1407 val_id := tc.a.child(&node, 1)
1408 container_id := tc.a.child(&node, 2)
1409 tc.annotate_node(container_id)
1410 has_val := int(val_id) >= 0
1411 if header == 4 {
1412 tc.insert_loop_var(key_id, Type(int_))
1413 tc.annotate_node(tc.a.child(&node, 3))
1414 } else {
1415 clean := unwrap_pointer(tc.resolve_type(container_id))
1416 if clean is Array {
1417 if has_val {
1418 tc.insert_loop_var(key_id, Type(int_))
1419 tc.insert_loop_var(val_id, clean.elem_type)
1420 } else {
1421 tc.insert_loop_var(key_id, clean.elem_type)
1422 }
1423 } else if clean is ArrayFixed {
1424 if has_val {
1425 tc.insert_loop_var(key_id, Type(int_))
1426 tc.insert_loop_var(val_id, clean.elem_type)
1427 } else {
1428 tc.insert_loop_var(key_id, clean.elem_type)
1429 }
1430 } else if clean is Map {
1431 if has_val {
1432 tc.insert_loop_var(key_id, clean.key_type)
1433 tc.insert_loop_var(val_id, clean.value_type)
1434 } else {
1435 tc.insert_loop_var(key_id, clean.value_type)
1436 }
1437 } else if clean is String {
1438 if has_val {
1439 tc.insert_loop_var(key_id, Type(int_))
1440 tc.insert_loop_var(val_id, Type(u8_))
1441 } else {
1442 tc.insert_loop_var(key_id, Type(u8_))
1443 }
1444 } else {
1445 container := tc.a.nodes[int(container_id)]
1446 if container.kind == .range {
1447 tc.insert_loop_var(key_id, Type(int_))
1448 }
1449 }
1450 }
1451 for i in header .. node.children_count {
1452 tc.annotate_node(tc.a.child(&node, i))
1453 }
1454}
1455
1456// insert_loop_var updates insert loop var state for types.
1457fn (mut tc TypeChecker) insert_loop_var(id flat.NodeId, typ Type) {
1458 if int(id) < 0 {
1459 return
1460 }
1461 v := tc.a.nodes[int(id)]
1462 if v.kind == .ident && v.value.len > 0 {
1463 tc.cur_scope.insert(v.value, typ)
1464 tc.remember_expr_type(id, typ)
1465 }
1466}
1467
1468// expr_type returns the resolved type recorded for a node during annotate_types.
1469pub fn (tc &TypeChecker) expr_type(id flat.NodeId) ?Type {
1470 if int(id) >= 0 {
1471 node := tc.a.nodes[int(id)]
1472 if node.kind == .call && node.typ.len > 0 {
1473 return tc.parse_type(node.typ)
1474 }
1475 }
1476 if t := tc.resolved_call_type(id) {
1477 return t
1478 }
1479 if t := tc.cached_expr_type(id) {
1480 return t
1481 }
1482 return none
1483}
1484
1485// resolved_call_type supports resolved call type handling for TypeChecker.
1486fn (tc &TypeChecker) resolved_call_type(id flat.NodeId) ?Type {
1487 if int(id) < 0 {
1488 return none
1489 }
1490 node := tc.a.nodes[int(id)]
1491 if node.kind != .call {
1492 return none
1493 }
1494 if t := tc.cached_expr_type(id) {
1495 return t
1496 }
1497 if name := tc.cached_resolved_call(id) {
1498 if t := tc.fn_ret_types[name] {
1499 return t
1500 }
1501 }
1502 return none
1503}
1504
1505// cached_expr_type supports cached expr type handling for TypeChecker.
1506fn (tc &TypeChecker) cached_expr_type(id flat.NodeId) ?Type {
1507 idx := int(id)
1508 if idx >= 0 && idx < tc.expr_type_set.len && tc.expr_type_set[idx] {
1509 return tc.expr_type_values[idx]
1510 }
1511 return none
1512}
1513
1514// cached_resolved_call supports cached resolved call handling for TypeChecker.
1515fn (tc &TypeChecker) cached_resolved_call(id flat.NodeId) ?string {
1516 idx := int(id)
1517 if idx >= 0 && idx < tc.resolved_call_set.len && tc.resolved_call_set[idx] {
1518 return tc.resolved_call_names[idx]
1519 }
1520 return none
1521}
1522
1523// resolved_call_name returns the checker-resolved function name for a call node.
1524pub fn (tc &TypeChecker) resolved_call_name(id flat.NodeId) ?string {
1525 return tc.cached_resolved_call(id)
1526}
1527
1528// resolved_fn_value_name returns the checker-resolved function name for a function value node.
1529pub fn (tc &TypeChecker) resolved_fn_value_name(id flat.NodeId) ?string {
1530 idx := int(id)
1531 if idx >= 0 && idx < tc.resolved_fn_value_set.len && tc.resolved_fn_value_set[idx] {
1532 return tc.resolved_fn_value_names[idx]
1533 }
1534 return none
1535}
1536
1537// resolve_fn_value_name_for_expected resolves and records a function value in an expected FnType context.
1538fn (mut tc TypeChecker) resolve_fn_value_name_for_expected(id flat.NodeId, expected Type) ?string {
1539 if name := tc.resolved_fn_value_name(id) {
1540 return name
1541 }
1542 if int(id) < 0 || int(id) >= tc.a.nodes.len {
1543 return none
1544 }
1545 node := tc.a.nodes[int(id)]
1546 if tc.fn_value_shadowed_by_value(node) {
1547 return none
1548 }
1549 key := tc.fn_value_match_key(node, expected) or { return none }
1550 tc.remember_resolved_fn_value_chain(id, key)
1551 return key
1552}
1553
1554// remember_resolved_call supports remember resolved call handling for TypeChecker.
1555fn (mut tc TypeChecker) remember_resolved_call(id flat.NodeId, name string) {
1556 idx := int(id)
1557 if idx < 0 {
1558 return
1559 }
1560 if idx >= tc.resolved_call_names.len {
1561 tc.extend_node_caches(tc.a.nodes.len)
1562 }
1563 if idx < tc.resolved_call_names.len {
1564 tc.resolved_call_names[idx] = name
1565 tc.resolved_call_set[idx] = true
1566 }
1567}
1568
1569// remember_resolved_fn_value records the exact function declaration used by a function value.
1570fn (mut tc TypeChecker) remember_resolved_fn_value(id flat.NodeId, name string) {
1571 idx := int(id)
1572 if idx < 0 {
1573 return
1574 }
1575 if idx >= tc.resolved_fn_value_names.len {
1576 tc.extend_node_caches(tc.a.nodes.len)
1577 }
1578 if idx < tc.resolved_fn_value_names.len {
1579 tc.resolved_fn_value_names[idx] = name
1580 tc.resolved_fn_value_set[idx] = true
1581 }
1582}
1583
1584fn (mut tc TypeChecker) remember_resolved_fn_value_chain(id flat.NodeId, name string) {
1585 tc.remember_resolved_fn_value(id, name)
1586 if int(id) < 0 || int(id) >= tc.a.nodes.len {
1587 return
1588 }
1589 node := tc.a.nodes[int(id)]
1590 if node.kind in [.cast_expr, .paren, .expr_stmt] && node.children_count > 0 {
1591 tc.remember_resolved_fn_value_chain(tc.a.child(&node, 0), name)
1592 }
1593}
1594
1595// register_synth_type records the type of a generated or transformed node.
1596pub fn (mut tc TypeChecker) register_synth_type(id flat.NodeId, typ Type) {
1597 tc.remember_expr_type(id, typ)
1598}
1599
1600// remember_expr_type supports remember expr type handling for TypeChecker.
1601fn (mut tc TypeChecker) remember_expr_type(id flat.NodeId, typ Type) {
1602 if int(id) < 0 {
1603 return
1604 }
1605 kind := if int(id) < tc.a.nodes.len { tc.a.nodes[int(id)].kind } else { flat.NodeKind.empty }
1606 if should_cache_expr_type(kind, typ) {
1607 idx := int(id)
1608 if idx >= tc.expr_type_values.len {
1609 tc.extend_node_caches(tc.a.nodes.len)
1610 }
1611 if idx < tc.expr_type_values.len {
1612 tc.expr_type_values[idx] = typ
1613 tc.expr_type_set[idx] = true
1614 }
1615 }
1616}
1617
1618// should_cache_expr_type reports whether should cache expr type applies in types.
1619fn should_cache_expr_type(kind flat.NodeKind, typ Type) bool {
1620 if typ is Void || typ is Unknown {
1621 return false
1622 }
1623 if typ is Array || typ is ArrayFixed || typ is Map || typ is Pointer || typ is FnType
1624 || typ is OptionType || typ is ResultType || typ is Struct || typ is Interface
1625 || typ is Enum || typ is SumType || typ is Alias || typ is MultiReturn {
1626 return true
1627 }
1628 kind_id := int(kind)
1629 return kind_id != 1 && kind_id != 2 && kind_id != 3 && kind_id != 4 && kind_id != 5
1630 && kind_id != 28
1631}
1632
1633// check_semantics validates check semantics state for types.
1634pub fn (mut tc TypeChecker) check_semantics() {
1635 tc.check_export_attrs()
1636 tc.cur_module = ''
1637 tc.cur_file = ''
1638 for i, node in tc.a.nodes {
1639 match node.kind {
1640 .file {
1641 tc.enter_file(node.value)
1642 }
1643 .module_decl {
1644 tc.enter_module(node.value)
1645 }
1646 .struct_decl {
1647 tc.check_decl_type_strings(flat.NodeId(i), node)
1648 tc.check_struct_field_defaults(node)
1649 }
1650 .type_decl, .interface_decl {
1651 tc.check_decl_type_strings(flat.NodeId(i), node)
1652 }
1653 .enum_decl {
1654 tc.check_enum_field_values(node)
1655 }
1656 .const_decl {
1657 tc.check_const_field_values(node)
1658 }
1659 .fn_decl {
1660 tc.check_decl_type_strings(flat.NodeId(i), node)
1661 tc.cur_fn_ret_type = tc.parse_type(node.typ)
1662 tc.cur_fn_node_id = i
1663 tc.method_value_locals = map[string]bool{}
1664 tc.method_value_local_depth = map[string]int{}
1665 tc.push_scope()
1666 for pi in 0 .. node.children_count {
1667 p := tc.a.child_node(&node, pi)
1668 if p.kind == .param && p.value.len > 0 {
1669 tc.cur_scope.insert(p.value, tc.parse_type(p.typ))
1670 }
1671 }
1672 tc.insert_implicit_veb_ctx(node)
1673 tc.check_fn_body(node)
1674 tc.cur_fn_node_id = -1
1675 is_disabled_stub := node.value in tc.a.disabled_fns
1676 if !type_allows_implicit_return(tc.cur_fn_ret_type)
1677 && !tc.fn_body_definitely_returns(node) && !is_disabled_stub
1678 && tc.should_diagnose(flat.NodeId(i)) {
1679 tc.record_error(.return_mismatch,
1680 'missing return at end of function `${node.value}`; expected `${tc.cur_fn_ret_type.name()}`',
1681 flat.NodeId(i))
1682 }
1683 tc.pop_scope()
1684 tc.cur_fn_ret_type = Type(void_)
1685 }
1686 .c_fn_decl {
1687 if tc.reject_unsupported_generics {
1688 tc.check_decl_type_strings(flat.NodeId(i), node)
1689 }
1690 }
1691 else {}
1692 }
1693
1694 _ = i
1695 }
1696}
1697
1698fn (mut tc TypeChecker) check_export_attrs() {
1699 mut natural_symbols := map[string]string{}
1700 synthetic_main_reserved := tc.has_synthetic_c_entry_main()
1701 mut cur_module := ''
1702 for node in tc.a.nodes {
1703 match node.kind {
1704 .file {
1705 tc.enter_file(node.value)
1706 cur_module = tc.cur_module
1707 }
1708 .module_decl {
1709 cur_module = node.value
1710 tc.enter_module(node.value)
1711 }
1712 .fn_decl {
1713 qname := export_qualified_fn_name(cur_module, node.value)
1714 natural_symbol := export_natural_c_symbol(cur_module, node.value)
1715 natural_symbols[natural_symbol] = qname
1716 }
1717 else {}
1718 }
1719 }
1720 mut export_symbols := map[string]string{}
1721 cur_module = ''
1722 for i, node in tc.a.nodes {
1723 match node.kind {
1724 .file {
1725 tc.enter_file(node.value)
1726 cur_module = tc.cur_module
1727 }
1728 .module_decl {
1729 cur_module = node.value
1730 tc.enter_module(node.value)
1731 }
1732 .fn_decl {
1733 qname := export_qualified_fn_name(cur_module, node.value)
1734 export_name := tc.a.export_fn_names[qname] or { continue }
1735 if export_name.len == 0 {
1736 tc.record_error_unfiltered(.unsupported_generic,
1737 'empty export name for `${qname}`', flat.NodeId(i))
1738 continue
1739 }
1740 if !is_valid_export_c_name(export_name) {
1741 tc.record_error_unfiltered(.unsupported_generic,
1742 'invalid export name `${export_name}` for `${qname}`', flat.NodeId(i))
1743 }
1744 if synthetic_main_reserved && export_name == 'main' {
1745 tc.record_error_unfiltered(.unsupported_generic,
1746 'export name `main` for `${qname}` collides with synthetic entry point `main`',
1747 flat.NodeId(i))
1748 }
1749 if node.generic_params.len > 0 {
1750 tc.record_error_unfiltered(.unsupported_generic,
1751 'generic function `${qname}` cannot be exported', flat.NodeId(i))
1752 }
1753 for pi in 0 .. node.children_count {
1754 p := tc.a.child_node(&node, pi)
1755 if p.kind == .param && (p.value.len == 0 || p.typ.len == 0) {
1756 tc.record_error_unfiltered(.unsupported_generic,
1757 'exported function `${qname}` must name all parameters', flat.NodeId(i))
1758 }
1759 }
1760 if existing := export_symbols[export_name] {
1761 if existing != qname {
1762 tc.record_error_unfiltered(.unsupported_generic,
1763 'duplicate export name `${export_name}` for `${qname}` and `${existing}`',
1764 flat.NodeId(i))
1765 }
1766 } else {
1767 export_symbols[export_name] = qname
1768 }
1769 if existing := natural_symbols[export_name] {
1770 tc.record_error_unfiltered(.unsupported_generic,
1771 'export name `${export_name}` for `${qname}` collides with `${existing}`',
1772 flat.NodeId(i))
1773 }
1774 }
1775 else {}
1776 }
1777 }
1778}
1779
1780fn (tc &TypeChecker) has_synthetic_c_entry_main() bool {
1781 if tc.has_c_test_harness_main() {
1782 return true
1783 }
1784 if tc.has_main_module_fn_main() {
1785 return false
1786 }
1787 return tc.has_c_top_level_main()
1788}
1789
1790fn (tc &TypeChecker) has_main_module_fn_main() bool {
1791 mut cur_module := ''
1792 for node in tc.a.nodes {
1793 match node.kind {
1794 .file {
1795 cur_module = ''
1796 }
1797 .module_decl {
1798 cur_module = node.value
1799 }
1800 .fn_decl {
1801 if node.value == 'main' && (cur_module.len == 0 || cur_module == 'main') {
1802 return true
1803 }
1804 }
1805 else {}
1806 }
1807 }
1808 return false
1809}
1810
1811fn (tc &TypeChecker) has_c_test_harness_main() bool {
1812 for file_idx, file_node in tc.a.nodes {
1813 if file_idx < tc.a.user_code_start || file_node.kind != .file || file_node.value.len == 0 {
1814 continue
1815 }
1816 if !tc.is_selected_input_file(file_node.value) {
1817 continue
1818 }
1819 module_name := tc.top_level_file_module_name(file_node)
1820 if is_c_backend_test_file(file_node.value)
1821 && (module_name.len == 0 || module_name == 'main') {
1822 return true
1823 }
1824 }
1825 return false
1826}
1827
1828fn (tc &TypeChecker) has_c_top_level_main() bool {
1829 for file_idx, file_node in tc.a.nodes {
1830 if !tc.should_emit_c_top_level_file(file_idx, file_node) {
1831 continue
1832 }
1833 for i in 0 .. file_node.children_count {
1834 child_id := tc.a.child(&file_node, i)
1835 if int(child_id) < tc.a.user_code_start {
1836 continue
1837 }
1838 if tc.is_c_top_level_stmt(child_id) {
1839 return true
1840 }
1841 }
1842 }
1843 return false
1844}
1845
1846fn (tc &TypeChecker) should_emit_c_top_level_file(file_idx int, file_node flat.Node) bool {
1847 if file_idx < tc.a.user_code_start || file_node.kind != .file || file_node.children_count == 0 {
1848 return false
1849 }
1850 module_name := tc.top_level_file_module_name(file_node)
1851 return module_name.len == 0 || module_name == 'main'
1852}
1853
1854fn (tc &TypeChecker) top_level_file_module_name(file_node flat.Node) string {
1855 if module_name := tc.file_modules[file_node.value] {
1856 return module_name
1857 }
1858 for i in 0 .. file_node.children_count {
1859 child := tc.a.child_node(&file_node, i)
1860 if child.kind == .module_decl {
1861 return child.value
1862 }
1863 }
1864 return ''
1865}
1866
1867fn (tc &TypeChecker) is_c_top_level_stmt(id flat.NodeId) bool {
1868 if int(id) < 0 {
1869 return false
1870 }
1871 node := tc.a.nodes[int(id)]
1872 return match node.kind {
1873 .expr_stmt, .assign, .decl_assign, .selector_assign, .index_assign, .for_stmt,
1874 .for_in_stmt, .if_expr, .match_stmt, .assert_stmt, .defer_stmt {
1875 true
1876 }
1877 .block, .comptime_if {
1878 for i in 0 .. node.children_count {
1879 if tc.is_c_top_level_stmt(tc.a.child(&node, i)) {
1880 return true
1881 }
1882 }
1883 false
1884 }
1885 else {
1886 false
1887 }
1888 }
1889}
1890
1891fn (tc &TypeChecker) is_selected_input_file(file string) bool {
1892 return tc.diagnostic_files.len == 0 || tc.diagnostic_files[file]
1893}
1894
1895fn is_c_backend_test_file(path string) bool {
1896 file := path.all_after_last('/').all_after_last('\\')
1897 if file.ends_with('_test.v') || file.ends_with('_test.c.v') {
1898 return true
1899 }
1900 if !file.ends_with('.v') {
1901 return false
1902 }
1903 base := file[..file.len - 2]
1904 if !base.contains('.') {
1905 return false
1906 }
1907 return base.all_after_last('.') == 'c' && base.all_before_last('.').ends_with('_test')
1908}
1909
1910fn export_qualified_fn_name(module_name string, name string) string {
1911 if module_name.len == 0 || module_name == 'main' || module_name == 'builtin' {
1912 return name
1913 }
1914 return '${module_name}.${name}'
1915}
1916
1917fn export_natural_c_symbol(module_name string, name string) string {
1918 if module_name == 'builtin' && name == 'free' {
1919 return 'v_free'
1920 }
1921 if module_name.len > 0 && module_name != 'main' && module_name != 'builtin' {
1922 return c_name('${module_name}.${name}')
1923 }
1924 if name == 'free' {
1925 return 'v_free'
1926 }
1927 if name == 'exit' {
1928 return 'v_exit'
1929 }
1930 if name in export_c_libc_collision_symbols {
1931 return 'v_${name}'
1932 }
1933 return c_name(name)
1934}
1935
1936fn is_valid_export_c_name(name string) bool {
1937 if name.len == 0 {
1938 return false
1939 }
1940 if name in export_c_reserved_words {
1941 return false
1942 }
1943 if name in export_v3_reserved_c_symbols {
1944 return false
1945 }
1946 first := name[0]
1947 if !((first >= `a` && first <= `z`) || (first >= `A` && first <= `Z`) || first == `_`) {
1948 return false
1949 }
1950 for i in 1 .. name.len {
1951 c := name[i]
1952 if (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) || (c >= `0` && c <= `9`) || c == `_` {
1953 continue
1954 }
1955 return false
1956 }
1957 return true
1958}
1959
1960fn (mut tc TypeChecker) insert_implicit_veb_ctx(node flat.Node) {
1961 if !tc.fn_needs_implicit_veb_ctx(node) {
1962 return
1963 }
1964 tc.cur_scope.insert('ctx', tc.implicit_veb_ctx_type())
1965}
1966
1967fn (tc &TypeChecker) fn_param_types_with_implicit_veb_ctx(node flat.Node, params []Type) []Type {
1968 if !tc.fn_needs_implicit_veb_ctx(node) {
1969 return params
1970 }
1971 insert_idx := tc.fn_implicit_veb_ctx_insert_index(node)
1972 mut result := []Type{cap: params.len + 1}
1973 for i, param in params {
1974 if i == insert_idx {
1975 result << tc.implicit_veb_ctx_type()
1976 }
1977 result << param
1978 }
1979 if insert_idx >= params.len {
1980 result << tc.implicit_veb_ctx_type()
1981 }
1982 return result
1983}
1984
1985fn (tc &TypeChecker) implicit_veb_ctx_type() Type {
1986 return tc.parse_type('mut Context')
1987}
1988
1989fn (tc &TypeChecker) fn_needs_implicit_veb_ctx(node flat.Node) bool {
1990 return tc.fn_returns_veb_result(node) && tc.fn_has_receiver_param(node)
1991 && !tc.fn_receiver_type_is_context(node) && !tc.fn_has_param(node, 'ctx')
1992 && tc.type_name_known_in_current_module('Context')
1993}
1994
1995fn (tc &TypeChecker) fn_implicit_veb_ctx_insert_index(node flat.Node) int {
1996 if tc.fn_has_receiver_param(node) {
1997 return 1
1998 }
1999 return 0
2000}
2001
2002fn (tc &TypeChecker) fn_has_receiver_param(node flat.Node) bool {
2003 if !node.value.contains('.') || node.children_count == 0 {
2004 return false
2005 }
2006 first := tc.a.child_node(&node, 0)
2007 if first.kind != .param || first.typ.len == 0 {
2008 return false
2009 }
2010 receiver := node.value.all_before_last('.').all_after_last('.')
2011 param_type := first.typ.trim_left('&').all_after_last('.')
2012 return receiver == param_type
2013}
2014
2015fn (tc &TypeChecker) fn_receiver_type_is_context(node flat.Node) bool {
2016 if !tc.fn_has_receiver_param(node) {
2017 return false
2018 }
2019 first := tc.a.child_node(&node, 0)
2020 return first.typ.trim_left('&').all_after_last('.') == 'Context'
2021}
2022
2023fn (tc &TypeChecker) fn_has_param(node flat.Node, name string) bool {
2024 for i in 0 .. node.children_count {
2025 p := tc.a.child_node(&node, i)
2026 if p.kind == .param && p.value == name {
2027 return true
2028 }
2029 }
2030 return false
2031}
2032
2033fn (tc &TypeChecker) fn_returns_veb_result(node flat.Node) bool {
2034 if node.typ == 'veb.Result' {
2035 return true
2036 }
2037 ret := tc.parse_type(node.typ)
2038 return ret.name() == 'veb.Result'
2039}
2040
2041// check_fn_body validates check fn body state for types.
2042fn (mut tc TypeChecker) check_fn_body(node flat.Node) {
2043 for i in 0 .. node.children_count {
2044 child_id := tc.a.child(&node, i)
2045 child := tc.a.child_node(&node, i)
2046 if child.kind == .param {
2047 continue
2048 }
2049 tc.check_node(child_id)
2050 }
2051}
2052
2053// check_decl_type_strings validates check decl type strings state for types.
2054fn (mut tc TypeChecker) check_decl_type_strings(node_id flat.NodeId, node flat.Node) {
2055 generic_params := tc.infer_decl_generic_params(node)
2056 if node.kind == .struct_decl {
2057 if node.typ.contains('generic') && tc.reject_unsupported_generics {
2058 tc.record_unsupported_generic('unsupported generic struct `${node.value}`', node_id)
2059 }
2060 } else {
2061 if node.generic_params.len > 0 && tc.reject_unsupported_generics {
2062 tc.record_unsupported_generic('unsupported generic declaration `${node.value}`',
2063 node_id)
2064 }
2065 tc.check_type_string_for_unsupported_generics(node.typ, node_id, generic_params)
2066 }
2067 for i in 0 .. node.children_count {
2068 child_id := tc.a.child(&node, i)
2069 if int(child_id) < 0 {
2070 continue
2071 }
2072 child := tc.a.nodes[int(child_id)]
2073 tc.check_type_string_for_unsupported_generics(child.typ, child_id, generic_params)
2074 if node.kind == .type_decl && child.value.len > 0 {
2075 tc.check_type_string_for_unsupported_generics(child.value, child_id, generic_params)
2076 }
2077 for j in 0 .. child.children_count {
2078 grandchild_id := tc.a.child(&child, j)
2079 if int(grandchild_id) < 0 {
2080 continue
2081 }
2082 grandchild := tc.a.nodes[int(grandchild_id)]
2083 tc.check_type_string_for_unsupported_generics(grandchild.typ, grandchild_id,
2084 generic_params)
2085 }
2086 }
2087}
2088
2089fn (tc &TypeChecker) infer_decl_generic_params(node flat.Node) map[string]bool {
2090 mut params := map[string]bool{}
2091 for name in node.generic_params {
2092 params[name] = true
2093 }
2094 tc.collect_generic_receiver_params(node, mut params)
2095 return params
2096}
2097
2098fn (tc &TypeChecker) collect_generic_receiver_params(node flat.Node, mut params map[string]bool) {
2099 if node.kind != .fn_decl && node.kind != .c_fn_decl {
2100 return
2101 }
2102 if !node.value.contains('.') {
2103 return
2104 }
2105 if node.children_count == 0 {
2106 return
2107 }
2108 receiver_id := tc.a.child(&node, 0)
2109 if int(receiver_id) < 0 {
2110 return
2111 }
2112 receiver := tc.a.nodes[int(receiver_id)]
2113 if receiver.kind != .param {
2114 return
2115 }
2116 receiver_type := receiver.typ.trim_left('&')
2117 if receiver_type != node.value.all_before_last('.') {
2118 return
2119 }
2120 mut counts := map[string]int{}
2121 if !tc.collect_generic_param_candidates(receiver.typ, mut counts) {
2122 return
2123 }
2124 for name, _ in counts {
2125 params[name] = true
2126 }
2127}
2128
2129fn (tc &TypeChecker) collect_generic_param_candidates(typ string, mut counts map[string]int) bool {
2130 clean := typ.trim_space()
2131 if clean.len == 0 {
2132 return false
2133 }
2134 if clean.starts_with('&') || clean.starts_with('?') || clean.starts_with('!') {
2135 return tc.collect_generic_param_candidates(clean[1..], mut counts)
2136 }
2137 if clean.starts_with('shared ') {
2138 return tc.collect_generic_param_candidates(clean[7..], mut counts)
2139 }
2140 if clean.starts_with('...') {
2141 return tc.collect_generic_param_candidates(clean[3..], mut counts)
2142 }
2143 if clean.starts_with('[]') {
2144 return tc.collect_generic_param_candidates(clean[2..], mut counts)
2145 }
2146 if clean.starts_with('map[') {
2147 mut found_context := false
2148 bracket_end := find_matching_bracket(clean, 3)
2149 if bracket_end < clean.len {
2150 if tc.collect_generic_param_candidates(clean[4..bracket_end], mut counts) {
2151 found_context = true
2152 }
2153 if tc.collect_generic_param_candidates(clean[bracket_end + 1..], mut counts) {
2154 found_context = true
2155 }
2156 }
2157 return found_context
2158 }
2159 if clean.starts_with('[') {
2160 idx := clean.index_u8(`]`)
2161 if idx > 0 {
2162 return tc.collect_generic_param_candidates(clean[idx + 1..], mut counts)
2163 }
2164 return false
2165 }
2166 if clean.starts_with('(') && clean.ends_with(')') {
2167 mut found_context := false
2168 for part in split_params(clean[1..clean.len - 1]) {
2169 if tc.collect_generic_param_candidates(part, mut counts) {
2170 found_context = true
2171 }
2172 }
2173 return found_context
2174 }
2175 if clean.starts_with('fn(') || clean.starts_with('fn (') {
2176 mut found_context := false
2177 params_start := clean.index_u8(`(`) + 1
2178 mut depth := 1
2179 mut params_end := params_start
2180 for params_end < clean.len {
2181 if clean[params_end] == `(` {
2182 depth++
2183 } else if clean[params_end] == `)` {
2184 depth--
2185 if depth == 0 {
2186 break
2187 }
2188 }
2189 params_end++
2190 }
2191 if params_end < clean.len {
2192 for part in split_params(clean[params_start..params_end]) {
2193 trimmed := part.trim_space()
2194 parts := trimmed.split(' ')
2195 param_type := if parts.len >= 2 { parts[parts.len - 1] } else { trimmed }
2196 if tc.collect_generic_param_candidates(param_type, mut counts) {
2197 found_context = true
2198 }
2199 }
2200 if tc.collect_generic_param_candidates(clean[params_end + 1..], mut counts) {
2201 found_context = true
2202 }
2203 }
2204 return found_context
2205 }
2206 if generic_type_application(clean) {
2207 bracket := clean.index_u8(`[`)
2208 bracket_end := find_matching_bracket(clean, bracket)
2209 if bracket_end < clean.len {
2210 for part in split_params(clean[bracket + 1..bracket_end]) {
2211 tc.collect_generic_param_candidates(part, mut counts)
2212 }
2213 }
2214 return true
2215 }
2216 if is_bare_generic_param(clean) && !tc.type_name_known(clean) {
2217 counts[clean] = (counts[clean] or { 0 }) + 1
2218 }
2219 return false
2220}
2221
2222// check_type_string_for_unsupported_generics
2223// validates helper state for types.
2224fn (mut tc TypeChecker) check_type_string_for_unsupported_generics(typ string, node_id flat.NodeId, generic_params map[string]bool) {
2225 clean := typ.trim_space()
2226 if clean.len == 0 {
2227 return
2228 }
2229 if clean in ['generic', 'params', 'union'] {
2230 return
2231 }
2232 if clean.starts_with('&') || clean.starts_with('?') || clean.starts_with('!') {
2233 tc.check_type_string_for_unsupported_generics(clean[1..], node_id, generic_params)
2234 return
2235 }
2236 if clean.starts_with('shared ') {
2237 tc.check_type_string_for_unsupported_generics(clean[7..], node_id, generic_params)
2238 return
2239 }
2240 if clean == 'thread' || clean == 'chan' {
2241 return
2242 }
2243 if clean.starts_with('thread ') {
2244 // `thread T` is a thread handle; only its element type T needs checking.
2245 tc.check_type_string_for_unsupported_generics(clean[7..], node_id, generic_params)
2246 return
2247 }
2248 if clean.starts_with('chan ') {
2249 tc.check_type_string_for_unsupported_generics(clean[5..], node_id, generic_params)
2250 return
2251 }
2252 if clean.starts_with('...') {
2253 tc.check_type_string_for_unsupported_generics(clean[3..], node_id, generic_params)
2254 return
2255 }
2256 if clean.starts_with('[]') {
2257 tc.check_type_string_for_unsupported_generics(clean[2..], node_id, generic_params)
2258 return
2259 }
2260 if clean.starts_with('map[') {
2261 bracket_end := find_matching_bracket(clean, 3)
2262 if bracket_end < clean.len {
2263 tc.check_type_string_for_unsupported_generics(clean[4..bracket_end], node_id,
2264 generic_params)
2265 tc.check_type_string_for_unsupported_generics(clean[bracket_end + 1..], node_id,
2266 generic_params)
2267 }
2268 return
2269 }
2270 if clean.starts_with('[') {
2271 idx := clean.index_u8(`]`)
2272 if idx > 0 {
2273 tc.check_type_string_for_unsupported_generics(clean[idx + 1..], node_id, generic_params)
2274 }
2275 return
2276 }
2277 if clean.starts_with('(') && clean.ends_with(')') {
2278 for part in split_params(clean[1..clean.len - 1]) {
2279 tc.check_type_string_for_unsupported_generics(part, node_id, generic_params)
2280 }
2281 return
2282 }
2283 if clean.starts_with('fn(') || clean.starts_with('fn (') {
2284 tc.check_fn_type_string_for_unsupported_generics(clean, node_id, generic_params)
2285 return
2286 }
2287 if generic_type_application(clean) {
2288 if tc.reject_unsupported_generics {
2289 tc.record_unsupported_generic('unsupported generic type application `${clean}`',
2290 node_id)
2291 return
2292 }
2293 bracket := clean.index_u8(`[`)
2294 bracket_end := find_matching_bracket(clean, bracket)
2295 base := clean[..bracket].trim_space()
2296 if should_check_named_type(base) && !tc.type_name_known(base) {
2297 tc.record_error(.unknown_type, 'unknown type `${base}`', node_id)
2298 }
2299 if bracket_end < clean.len {
2300 for part in split_params(clean[bracket + 1..bracket_end]) {
2301 tc.check_type_string_for_unsupported_generics(part, node_id, generic_params)
2302 }
2303 }
2304 return
2305 }
2306 if is_bare_generic_param(clean) && !tc.type_name_known(clean) {
2307 if tc.reject_unsupported_generics {
2308 tc.record_unsupported_generic('unsupported generic type parameter `${clean}`', node_id)
2309 return
2310 }
2311 if clean in generic_params {
2312 return
2313 }
2314 }
2315 if should_check_named_type(clean) && !tc.type_name_known(clean) {
2316 tc.record_error(.unknown_type, 'unknown type `${clean}`', node_id)
2317 }
2318}
2319
2320// check_fn_type_string_for_unsupported_generics
2321// validates helper state for types.
2322fn (mut tc TypeChecker) check_fn_type_string_for_unsupported_generics(typ string, node_id flat.NodeId, generic_params map[string]bool) {
2323 params_start := typ.index_u8(`(`) + 1
2324 mut depth := 1
2325 mut params_end := params_start
2326 for params_end < typ.len {
2327 if typ[params_end] == `(` {
2328 depth++
2329 } else if typ[params_end] == `)` {
2330 depth--
2331 if depth == 0 {
2332 break
2333 }
2334 }
2335 params_end++
2336 }
2337 if params_end >= typ.len {
2338 return
2339 }
2340 for part in split_params(typ[params_start..params_end]) {
2341 trimmed := part.trim_space()
2342 parts := trimmed.split(' ')
2343 param_type := if parts.len >= 2 { parts[parts.len - 1] } else { trimmed }
2344 tc.check_type_string_for_unsupported_generics(param_type, node_id, generic_params)
2345 }
2346 ret := typ[params_end + 1..].trim_space()
2347 tc.check_type_string_for_unsupported_generics(ret, node_id, generic_params)
2348}
2349
2350// generic_type_application supports generic type application handling for types.
2351fn generic_type_application(typ string) bool {
2352 _, _, ok := generic_type_application_parts(typ)
2353 return ok
2354}
2355
2356fn (tc &TypeChecker) generic_args_are_concrete(args []string) bool {
2357 for arg in args {
2358 if tc.type_text_has_generic_placeholder(arg) {
2359 return false
2360 }
2361 }
2362 return true
2363}
2364
2365fn (tc &TypeChecker) type_text_has_generic_placeholder(typ string) bool {
2366 clean := typ.trim_space()
2367 if is_bare_generic_param(clean) {
2368 return !tc.is_known_type_text(clean)
2369 }
2370 if clean.starts_with('&') {
2371 return tc.type_text_has_generic_placeholder(clean[1..])
2372 }
2373 if clean.starts_with('mut ') {
2374 return tc.type_text_has_generic_placeholder(clean[4..])
2375 }
2376 if clean.starts_with('?') || clean.starts_with('!') {
2377 return tc.type_text_has_generic_placeholder(clean[1..])
2378 }
2379 if clean.starts_with('...') {
2380 return tc.type_text_has_generic_placeholder(clean[3..])
2381 }
2382 if clean.starts_with('[]') {
2383 return tc.type_text_has_generic_placeholder(clean[2..])
2384 }
2385 if clean.starts_with('map[') {
2386 bracket_end := find_matching_bracket(clean, 3)
2387 if bracket_end < clean.len {
2388 return tc.type_text_has_generic_placeholder(clean[4..bracket_end])
2389 || tc.type_text_has_generic_placeholder(clean[bracket_end + 1..])
2390 }
2391 }
2392 if clean.starts_with('[') {
2393 bracket_end := find_matching_bracket(clean, 0)
2394 if bracket_end < clean.len {
2395 return tc.type_text_has_generic_placeholder(clean[bracket_end + 1..])
2396 }
2397 }
2398 _, args, ok := generic_type_application_parts(clean)
2399 if ok {
2400 for arg in args {
2401 if tc.type_text_has_generic_placeholder(arg) {
2402 return true
2403 }
2404 }
2405 }
2406 return false
2407}
2408
2409fn generic_type_application_parts(typ string) (string, []string, bool) {
2410 if typ.starts_with('[') || !typ.contains('[') {
2411 return '', []string{}, false
2412 }
2413 bracket := typ.index_u8(`[`)
2414 bracket_end := find_matching_bracket(typ, bracket)
2415 if bracket <= 0 || bracket_end <= bracket {
2416 return '', []string{}, false
2417 }
2418 inner := typ[bracket + 1..bracket_end].trim_space()
2419 if is_fixed_array_len_text(inner) {
2420 return '', []string{}, false
2421 }
2422 return typ[..bracket], split_params(inner), true
2423}
2424
2425// is_fixed_array_len_text reports whether a postfix `Base[inner]` bracket holds a fixed-array
2426// length rather than a generic type argument. `ArrayFixed.name()` renders the length as a decimal
2427// (`u8[16]`), a non-decimal literal (`u8[0x10]`), or the source length expression (`u8[segs + 1]`);
2428// a generic argument is always a type, never a number or arithmetic expression. Recognising all
2429// three keeps such a postfix name parsing as a fixed array (e.g. when `[]thread T.wait()` recovers
2430// the spawned return type) instead of a bogus generic application.
2431fn is_fixed_array_len_text(inner string) bool {
2432 s := inner.trim_space()
2433 if s.len == 0 {
2434 return false
2435 }
2436 // A fixed-array length is a single integer expression; a comma means the brackets hold a
2437 // generic argument LIST (`Pair[int, &Node]`), not a length. Without this an `&` (or `-`)
2438 // that merely leads a later pointer type argument would be read as a length operator.
2439 if s.contains(',') {
2440 return false
2441 }
2442 if v_int_literal_value(s) != none {
2443 return true
2444 }
2445 for i in 0 .. s.len {
2446 c := s[i]
2447 if c in [`+`, `*`, `/`, `%`, `|`, `^`, `<`, `>`] {
2448 return true
2449 }
2450 // A leading `-`/`&` is a negative literal / pointer-type argument; elsewhere they are the
2451 // subtraction / bitwise-and operators of a length expression.
2452 if (c == `-` || c == `&`) && i > 0 {
2453 return true
2454 }
2455 }
2456 return false
2457}
2458
2459// is_decimal_int_literal reports whether is decimal int literal applies in types.
2460fn is_decimal_int_literal(s string) bool {
2461 if s.len == 0 {
2462 return false
2463 }
2464 for i in 0 .. s.len {
2465 if s[i] < `0` || s[i] > `9` {
2466 return false
2467 }
2468 }
2469 return true
2470}
2471
2472// v_int_literal_value parses a complete V integer literal — decimal, hex (`0x`), octal
2473// (`0o`), or binary (`0b`), with optional `_` digit separators — to its value. Returns
2474// none when `s` is not a whole integer literal (a const name, an expression, etc.), so
2475// const-length folding accepts `0xF & 6` / `[0b1100 >> 1]int`, not just decimal text.
2476fn v_int_literal_value(s string) ?int {
2477 if s.len == 0 {
2478 return none
2479 }
2480 t := s.replace('_', '')
2481 if t.len == 0 {
2482 return none
2483 }
2484 mut base := 10
2485 mut digits := t
2486 if t.len >= 2 && t[0] == `0` {
2487 c := t[1]
2488 if c == `x` || c == `X` {
2489 base = 16
2490 digits = t[2..]
2491 } else if c == `o` || c == `O` {
2492 base = 8
2493 digits = t[2..]
2494 } else if c == `b` || c == `B` {
2495 base = 2
2496 digits = t[2..]
2497 }
2498 }
2499 if digits.len == 0 {
2500 return none
2501 }
2502 mut value := 0
2503 for ch in digits {
2504 mut d := 0
2505 if ch >= `0` && ch <= `9` {
2506 d = int(ch - `0`)
2507 } else if ch >= `a` && ch <= `f` {
2508 d = int(ch - `a`) + 10
2509 } else if ch >= `A` && ch <= `F` {
2510 d = int(ch - `A`) + 10
2511 } else {
2512 return none
2513 }
2514 if d >= base {
2515 return none
2516 }
2517 value = value * base + d
2518 }
2519 return value
2520}
2521
2522// is_bare_generic_param reports whether is bare generic param applies in types.
2523fn is_bare_generic_param(typ string) bool {
2524 return typ.len == 1 && typ[0] >= `A` && typ[0] <= `Z`
2525}
2526
2527fn generic_param_index(name string) int {
2528 return match name {
2529 'T', 'A', 'K', 'X' { 0 }
2530 'U', 'B', 'V', 'Y' { 1 }
2531 'C', 'W', 'Z' { 2 }
2532 else { 0 }
2533 }
2534}
2535
2536fn generic_placeholder_from_unknown(typ Unknown) ?string {
2537 start := typ.reason.index_u8(`\``)
2538 if start < 0 {
2539 return none
2540 }
2541 end := typ.reason[start + 1..].index_u8(`\``)
2542 if end < 0 {
2543 return none
2544 }
2545 name := typ.reason[start + 1..start + 1 + end]
2546 if !is_bare_generic_param(name) {
2547 return none
2548 }
2549 return name
2550}
2551
2552fn (tc &TypeChecker) resolve_known_field_type(type_name string, fallback Type) Type {
2553 qname := tc.qualify_name(type_name)
2554 if qname in tc.structs {
2555 return Type(Struct{
2556 name: qname
2557 })
2558 }
2559 if type_name in tc.structs {
2560 return Type(Struct{
2561 name: type_name
2562 })
2563 }
2564 if qname in tc.interface_names {
2565 return Type(Interface{
2566 name: qname
2567 })
2568 }
2569 if type_name in tc.interface_names {
2570 return Type(Interface{
2571 name: type_name
2572 })
2573 }
2574 if qname in tc.type_aliases {
2575 return Type(Alias{
2576 name: qname
2577 base_type: tc.parse_type(tc.type_aliases[qname])
2578 })
2579 }
2580 if type_name in tc.type_aliases {
2581 return Type(Alias{
2582 name: type_name
2583 base_type: tc.parse_type(tc.type_aliases[type_name])
2584 })
2585 }
2586 return fallback
2587}
2588
2589// type_name_known returns type name known data for TypeChecker.
2590fn (tc &TypeChecker) type_name_known(typ string) bool {
2591 if is_builtin_type_name(typ) || typ == 'unknown' || typ.starts_with('C.') {
2592 return true
2593 }
2594 qtyp := tc.qualify_name(typ)
2595 if !typ.contains('.') {
2596 if resolved := tc.resolve_selective_import_type_symbol(typ) {
2597 return tc.type_symbol_known(resolved)
2598 }
2599 }
2600 return typ in tc.type_aliases || qtyp in tc.type_aliases || typ in tc.structs
2601 || qtyp in tc.structs || typ in tc.interface_names || qtyp in tc.interface_names
2602 || typ in tc.enum_names || qtyp in tc.enum_names || typ in tc.sum_types
2603 || qtyp in tc.sum_types
2604}
2605
2606fn (tc &TypeChecker) type_name_known_in_current_module(typ string) bool {
2607 qtyp := tc.qualify_name(typ)
2608 return qtyp in tc.type_aliases || qtyp in tc.structs || qtyp in tc.interface_names
2609 || qtyp in tc.enum_names || qtyp in tc.sum_types
2610}
2611
2612// should_check_named_type reports whether should check named type applies in types.
2613fn should_check_named_type(typ string) bool {
2614 if typ.len == 0 {
2615 return false
2616 }
2617 for i in 0 .. typ.len {
2618 c := typ[i]
2619 if !((c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`)
2620 || (c >= `0` && c <= `9`) || c == `_` || c == `.`) {
2621 return false
2622 }
2623 }
2624 return true
2625}
2626
2627// check_struct_field_defaults validates check struct field defaults state for types.
2628fn (mut tc TypeChecker) check_struct_field_defaults(node flat.Node) {
2629 for i in 0 .. node.children_count {
2630 field := tc.a.child_node(&node, i)
2631 if field.kind != .field_decl || field.children_count == 0 {
2632 continue
2633 }
2634 default_id := tc.a.child(field, 0)
2635 expected := tc.parse_type(field.typ)
2636 tc.check_node(default_id)
2637 actual := tc.resolve_expr(default_id, expected)
2638 if !tc.type_compatible(actual, expected) {
2639 tc.type_mismatch(.assignment_mismatch,
2640 'cannot initialize field `${field.value}` with `${actual.name()}`; expected `${expected.name()}`',
2641 default_id)
2642 }
2643 }
2644}
2645
2646// check_enum_field_values validates check enum field values state for types.
2647fn (mut tc TypeChecker) check_enum_field_values(node flat.Node) {
2648 for i in 0 .. node.children_count {
2649 field := tc.a.child_node(&node, i)
2650 if field.kind != .enum_field || field.children_count == 0 {
2651 continue
2652 }
2653 value_id := tc.a.child(field, 0)
2654 tc.check_node(value_id)
2655 value_type := tc.resolve_type(value_id)
2656 if value_type is Unknown {
2657 continue
2658 }
2659 if !value_type.is_integer() {
2660 tc.type_mismatch(.assignment_mismatch,
2661 'enum field `${field.value}` value must be integer, not `${value_type.name()}`',
2662 value_id)
2663 }
2664 }
2665}
2666
2667// check_const_field_values validates check const field values state for types.
2668fn (mut tc TypeChecker) check_const_field_values(node flat.Node) {
2669 for i in 0 .. node.children_count {
2670 field := tc.a.child_node(&node, i)
2671 if field.kind != .const_field || field.children_count == 0 {
2672 continue
2673 }
2674 tc.check_node(tc.a.child(field, 0))
2675 }
2676}
2677
2678// fn_body_definitely_returns supports fn body definitely returns handling for TypeChecker.
2679fn (tc &TypeChecker) fn_body_definitely_returns(node flat.Node) bool {
2680 for i in 0 .. node.children_count {
2681 child_id := tc.a.child(&node, i)
2682 child := tc.a.child_node(&node, i)
2683 if child.kind == .param {
2684 continue
2685 }
2686 if tc.stmt_definitely_returns(child_id) {
2687 return true
2688 }
2689 }
2690 return false
2691}
2692
2693fn type_allows_implicit_return(typ Type) bool {
2694 if typ is Void {
2695 return true
2696 }
2697 if typ is OptionType {
2698 return typ.base_type is Void
2699 }
2700 if typ is ResultType {
2701 return typ.base_type is Void
2702 }
2703 return false
2704}
2705
2706// valid_node_id supports valid node id handling for TypeChecker.
2707fn (tc &TypeChecker) valid_node_id(id flat.NodeId) bool {
2708 return int(id) >= 0 && tc.a != unsafe { nil } && int(id) < tc.a.nodes.len
2709}
2710
2711// stmt_definitely_returns supports stmt definitely returns handling for TypeChecker.
2712fn (tc &TypeChecker) stmt_definitely_returns(id flat.NodeId) bool {
2713 if !tc.valid_node_id(id) {
2714 return false
2715 }
2716 node := tc.a.nodes[int(id)]
2717 match node.kind {
2718 .return_stmt {
2719 return true
2720 }
2721 .block {
2722 for i in 0 .. node.children_count {
2723 if tc.stmt_definitely_returns(tc.a.child(&node, i)) {
2724 return true
2725 }
2726 }
2727 return false
2728 }
2729 .if_expr {
2730 if node.children_count < 3 {
2731 return false
2732 }
2733 return tc.stmt_definitely_returns(tc.a.child(&node, 1))
2734 && tc.stmt_definitely_returns(tc.a.child(&node, 2))
2735 }
2736 .match_stmt {
2737 if node.children_count < 2 {
2738 return false
2739 }
2740 mut has_else := false
2741 for i in 1 .. node.children_count {
2742 branch := tc.a.child_node(&node, i)
2743 if branch.kind != .match_branch {
2744 return false
2745 }
2746 if branch.value == 'else' {
2747 has_else = true
2748 }
2749 if !tc.match_branch_definitely_returns(branch) {
2750 return false
2751 }
2752 }
2753 return has_else || tc.match_without_else_exhaustive_enum_returns(node)
2754 }
2755 else {
2756 return false
2757 }
2758 }
2759}
2760
2761// match_covers_all_enum_variants reports whether a `match` over an enum subject
2762// lists every variant of that enum (so it is exhaustive without an `else`).
2763fn (tc &TypeChecker) match_covers_all_enum_variants(node flat.Node) bool {
2764 if node.children_count < 2 {
2765 return false
2766 }
2767 subject_type := unwrap_pointer(tc.resolve_type(tc.a.child(&node, 0)))
2768 mut enum_name := ''
2769 if subject_type is Enum {
2770 enum_name = subject_type.name
2771 } else {
2772 return false
2773 }
2774 // A `[flag]` enum value can hold combined or zero bits (`.read | .write`, `0`) that
2775 // no single-field branch covers, so listing every field is NOT exhaustive — such a
2776 // match needs an explicit `else`.
2777 if enum_name in tc.flag_enums {
2778 return false
2779 }
2780 all_fields := tc.enum_fields[enum_name] or { return false }
2781 if all_fields.len == 0 {
2782 return false
2783 }
2784 mut covered := map[string]bool{}
2785 for i in 1 .. node.children_count {
2786 branch := tc.a.child_node(&node, i)
2787 if branch.kind != .match_branch {
2788 return false
2789 }
2790 if branch.value == 'else' {
2791 return true
2792 }
2793 n_conds := branch.value.int()
2794 for j in 0 .. n_conds {
2795 cond := tc.a.child_node(branch, j)
2796 if cond.kind == .enum_val {
2797 covered[cond.value.all_after_last('.')] = true
2798 }
2799 }
2800 }
2801 for f in all_fields {
2802 if f !in covered {
2803 return false
2804 }
2805 }
2806 return true
2807}
2808
2809// match_branch_definitely_returns
2810// supports helper handling in types.
2811fn (tc &TypeChecker) match_branch_definitely_returns(branch &flat.Node) bool {
2812 body_start := if branch.value == 'else' { 0 } else { branch.value.int() }
2813 for i in body_start .. branch.children_count {
2814 if tc.stmt_definitely_returns(tc.a.child(branch, i)) {
2815 return true
2816 }
2817 }
2818 return false
2819}
2820
2821fn (tc &TypeChecker) match_without_else_exhaustive_enum_returns(node flat.Node) bool {
2822 subject_type := unwrap_pointer(tc.resolve_type(tc.a.child(&node, 0)))
2823 if subject_type is Enum {
2824 enum_name := tc.resolve_enum_name(subject_type.name) or { subject_type.name }
2825 if subject_type.is_flag || enum_name in tc.flag_enums {
2826 return false
2827 }
2828 fields := tc.enum_fields[enum_name] or { return false }
2829 if fields.len == 0 {
2830 return false
2831 }
2832 mut covered := map[string]bool{}
2833 for i in 1 .. node.children_count {
2834 branch := tc.a.child_node(&node, i)
2835 if branch.kind != .match_branch || branch.value == 'else' {
2836 return false
2837 }
2838 n_conds := branch.value.int()
2839 if n_conds <= 0 || n_conds > branch.children_count {
2840 return false
2841 }
2842 for j in 0 .. n_conds {
2843 cond := tc.a.child_node(branch, j)
2844 field := tc.match_enum_condition_field(cond, enum_name) or { return false }
2845 covered[field] = true
2846 }
2847 }
2848 for field in fields {
2849 if field !in covered {
2850 return false
2851 }
2852 }
2853 return true
2854 }
2855 return false
2856}
2857
2858fn (tc &TypeChecker) match_enum_condition_field(cond &flat.Node, enum_name string) ?string {
2859 match cond.kind {
2860 .enum_val {
2861 field := cond.value.all_after_last('.')
2862 if tc.enum_value_matches(cond.value, enum_name) {
2863 return field
2864 }
2865 }
2866 .selector {
2867 if typ := tc.enum_selector_type(cond) {
2868 if typ is Enum {
2869 cond_enum_name := tc.resolve_enum_name(typ.name) or { typ.name }
2870 if cond_enum_name == enum_name && tc.enum_has_field(enum_name, cond.value) {
2871 return cond.value
2872 }
2873 }
2874 }
2875 }
2876 else {}
2877 }
2878
2879 return none
2880}
2881
2882// node_kind_id supports node kind id handling for types.
2883fn node_kind_id(node flat.Node) int {
2884 mut kind_id := node.kind_id
2885 if kind_id == 0 && int(node.kind) != 0 {
2886 kind_id = int(node.kind)
2887 }
2888 return kind_id
2889}
2890
2891// check_node validates check node state for types.
2892fn (mut tc TypeChecker) check_node(id flat.NodeId) {
2893 idx := int(id)
2894 if idx < 0 {
2895 return
2896 }
2897 if idx >= tc.checking_nodes.len {
2898 tc.extend_node_caches(tc.a.nodes.len)
2899 }
2900 if idx < tc.checking_nodes.len {
2901 if tc.checking_nodes[idx] {
2902 return
2903 }
2904 tc.checking_nodes[idx] = true
2905 defer {
2906 tc.checking_nodes[idx] = false
2907 }
2908 }
2909 node := tc.a.nodes[idx]
2910 kind_id := node_kind_id(node)
2911 if kind_id == 1 || kind_id == 2 || kind_id == 3 || kind_id == 4 || kind_id == 5 || kind_id == 28
2912 || kind_id == 29 {
2913 return
2914 }
2915 if kind_id == 45 {
2916 tc.check_block(node)
2917 return
2918 }
2919 if kind_id == 46 {
2920 tc.check_for_stmt(node)
2921 return
2922 }
2923 if kind_id == 47 {
2924 tc.check_for_in_stmt(node)
2925 return
2926 }
2927 if kind_id == 41 {
2928 tc.check_decl_assign(id, node)
2929 return
2930 }
2931 if kind_id == 40 || kind_id == 42 || kind_id == 43 {
2932 tc.check_assign(id, node)
2933 return
2934 }
2935 if kind_id == 44 {
2936 tc.check_return(id, node)
2937 return
2938 }
2939 if kind_id == 12 {
2940 tc.check_call(id, node)
2941 return
2942 }
2943 if kind_id == 21 {
2944 tc.check_fn_literal(node)
2945 return
2946 }
2947 if kind_id == 32 {
2948 tc.check_lambda_expr(node)
2949 return
2950 }
2951 if kind_id == 15 {
2952 tc.check_if_expr(id, node)
2953 return
2954 }
2955 if kind_id == 22 {
2956 tc.check_or_expr(node)
2957 return
2958 }
2959 if kind_id == 50 {
2960 tc.check_match_stmt(id, node)
2961 return
2962 }
2963 if kind_id == 37 {
2964 tc.check_is_expr(id, node)
2965 return
2966 }
2967 if kind_id == 10 {
2968 tc.check_postfix(id, node)
2969 return
2970 }
2971 if kind_id == 16 || kind_id == 26 {
2972 tc.check_struct_init(id, node)
2973 return
2974 }
2975 if kind_id == 13 {
2976 tc.check_selector(id, node)
2977 return
2978 }
2979 if kind_id == 14 {
2980 tc.check_index(id, node)
2981 return
2982 }
2983 if kind_id == 7 {
2984 tc.check_ident(id, node)
2985 return
2986 }
2987 if node.kind == .array_init {
2988 tc.check_array_init(node)
2989 return
2990 }
2991 if node.kind == .select_stmt {
2992 tc.check_select_stmt(node)
2993 return
2994 }
2995 // A method value stored in a container escapes the single-use guarantee of its per-site
2996 // static receiver, so reject `[obj.method]` / `arr << obj.method` / `{'k': obj.method}`.
2997 if node.kind == .array_literal {
2998 for i in 0 .. node.children_count {
2999 tc.reject_stored_method_value(tc.a.child(&node, i))
3000 }
3001 } else if node.kind == .map_init {
3002 // children alternate key, value, key, value, ...; check the value positions.
3003 for j := 1; j < node.children_count; j += 2 {
3004 tc.reject_stored_method_value(tc.a.child(&node, j))
3005 }
3006 } else if node.kind == .infix && node.op == .left_shift && node.children_count >= 2 {
3007 if unwrap_pointer(tc.resolve_type(tc.a.child(&node, 0))) is Array {
3008 tc.reject_stored_method_value(tc.a.child(&node, 1))
3009 }
3010 }
3011
3012 for i in 0 .. node.children_count {
3013 tc.check_node(tc.a.child(&node, i))
3014 }
3015}
3016
3017// check_select_stmt validates a `select { ... }` statement. A receive branch
3018// `val := <-ch` binds `val` (the channel's element type) in the branch body's
3019// scope; other branches (sends, bare conditions, `else`) are checked as-is.
3020fn (mut tc TypeChecker) check_select_stmt(node flat.Node) {
3021 for i in 0 .. node.children_count {
3022 branch_id := tc.a.child(&node, i)
3023 if !tc.valid_node_id(branch_id) {
3024 continue
3025 }
3026 branch := tc.a.nodes[int(branch_id)]
3027 if branch.kind != .select_branch {
3028 tc.check_node(branch_id)
3029 continue
3030 }
3031 tc.push_scope()
3032 mut body_start := 0
3033 if branch.value == 'recv' && branch.children_count >= 2 {
3034 // children[0] = bound var ident, children[1] = `<-ch` receive expr.
3035 var_id := tc.a.child(&branch, 0)
3036 recv_id := tc.a.child(&branch, 1)
3037 tc.check_node(recv_id)
3038 elem_type := tc.resolve_type(recv_id)
3039 if tc.valid_node_id(var_id) {
3040 var_node := tc.a.nodes[int(var_id)]
3041 if var_node.kind == .ident && var_node.value.len > 0 {
3042 tc.cur_scope.insert(var_node.value, elem_type)
3043 }
3044 }
3045 body_start = 2
3046 }
3047 for j in body_start .. branch.children_count {
3048 tc.check_node(tc.a.child(&branch, j))
3049 }
3050 tc.pop_scope()
3051 }
3052}
3053
3054// check_array_init validates an `[]T{len: ..., init: ...}` initializer. The `init:`
3055// expression may reference the magic `index` variable (the current element index),
3056// so it is checked in a scope where `index` is bound to an int.
3057fn (mut tc TypeChecker) check_array_init(node flat.Node) {
3058 for i in 0 .. node.children_count {
3059 child_id := tc.a.child(&node, i)
3060 child := tc.a.nodes[int(child_id)]
3061 if child.kind == .field_init && child.value == 'init' {
3062 tc.push_scope()
3063 tc.cur_scope.insert('index', Type(int_))
3064 tc.check_node(child_id)
3065 tc.pop_scope()
3066 } else {
3067 tc.check_node(child_id)
3068 }
3069 }
3070}
3071
3072// check_or_expr validates check or expr state for types.
3073fn (mut tc TypeChecker) check_or_expr(node flat.Node) {
3074 if node.children_count == 0 {
3075 return
3076 }
3077 inner_id := tc.a.child(&node, 0)
3078 tc.check_node(inner_id)
3079 if node.children_count < 2 || node.value in ['!', '?'] {
3080 return
3081 }
3082 tc.push_scope()
3083 tc.cur_scope.insert('err', tc.parse_type('IError'))
3084 tc.check_node(tc.a.child(&node, 1))
3085 tc.pop_scope()
3086}
3087
3088// check_fn_literal validates check fn literal state for types.
3089fn (mut tc TypeChecker) check_fn_literal(node flat.Node) {
3090 saved_ret := tc.cur_fn_ret_type
3091 tc.cur_fn_ret_type = tc.parse_type(node.typ)
3092 tc.push_scope()
3093 for i in 0 .. node.children_count {
3094 child := tc.a.child_node(&node, i)
3095 if child.kind == .param && child.value.len > 0 {
3096 tc.cur_scope.insert(child.value, tc.parse_type(child.typ))
3097 }
3098 }
3099 for i in 0 .. node.children_count {
3100 child_id := tc.a.child(&node, i)
3101 child := tc.a.nodes[int(child_id)]
3102 if child.kind == .param || child.kind == .ident {
3103 continue
3104 }
3105 tc.check_node(child_id)
3106 }
3107 tc.pop_scope()
3108 tc.cur_fn_ret_type = saved_ret
3109}
3110
3111// check_lambda_expr validates check lambda expr state for types.
3112fn (mut tc TypeChecker) check_lambda_expr(node flat.Node) {
3113 if node.children_count == 0 {
3114 return
3115 }
3116 tc.push_scope()
3117 for i in 0 .. node.children_count - 1 {
3118 child := tc.a.child_node(&node, i)
3119 if child.kind == .ident && child.value.len > 0 {
3120 tc.cur_scope.insert(child.value, unknown_type('lambda parameter `${child.value}`'))
3121 }
3122 }
3123 tc.check_node(tc.a.child(&node, node.children_count - 1))
3124 tc.pop_scope()
3125}
3126
3127// check_block validates check block state for types.
3128fn (mut tc TypeChecker) check_block(node flat.Node) {
3129 tc.push_scope()
3130 for i in 0 .. node.children_count {
3131 tc.check_node(tc.a.child(&node, i))
3132 }
3133 tc.pop_scope()
3134}
3135
3136// check_for_stmt validates check for stmt state for types.
3137fn (mut tc TypeChecker) check_for_stmt(node flat.Node) {
3138 tc.push_scope()
3139 if node.children_count > 0 {
3140 init_id := tc.a.child(&node, 0)
3141 if int(init_id) >= 0 {
3142 tc.check_node(init_id)
3143 }
3144 }
3145 if node.children_count > 1 {
3146 cond_id := tc.a.child(&node, 1)
3147 if int(cond_id) >= 0 {
3148 tc.check_bool_condition(cond_id)
3149 }
3150 }
3151 if node.children_count > 2 {
3152 post_id := tc.a.child(&node, 2)
3153 if int(post_id) >= 0 {
3154 tc.check_node(post_id)
3155 }
3156 }
3157 for i in 3 .. node.children_count {
3158 tc.check_node(tc.a.child(&node, i))
3159 }
3160 tc.pop_scope()
3161}
3162
3163// check_for_in_stmt validates check for in stmt state for types.
3164fn (mut tc TypeChecker) check_for_in_stmt(node flat.Node) {
3165 header := node.value.int()
3166 if header < 3 || node.children_count < 3 {
3167 return
3168 }
3169 tc.push_scope()
3170 key_id := tc.a.child(&node, 0)
3171 val_id := tc.a.child(&node, 1)
3172 container_id := tc.a.child(&node, 2)
3173 tc.check_node(container_id)
3174 has_val := int(val_id) >= 0
3175 if header == 4 {
3176 tc.insert_loop_var(key_id, Type(int_))
3177 tc.check_node(tc.a.child(&node, 3))
3178 } else {
3179 clean := unwrap_pointer(tc.resolve_type(container_id))
3180 if clean is Array {
3181 if has_val {
3182 tc.insert_loop_var(key_id, Type(int_))
3183 tc.insert_loop_var(val_id, clean.elem_type)
3184 } else {
3185 tc.insert_loop_var(key_id, clean.elem_type)
3186 }
3187 } else if clean is ArrayFixed {
3188 if has_val {
3189 tc.insert_loop_var(key_id, Type(int_))
3190 tc.insert_loop_var(val_id, clean.elem_type)
3191 } else {
3192 tc.insert_loop_var(key_id, clean.elem_type)
3193 }
3194 } else if clean is Map {
3195 if has_val {
3196 tc.insert_loop_var(key_id, clean.key_type)
3197 tc.insert_loop_var(val_id, clean.value_type)
3198 } else {
3199 tc.insert_loop_var(key_id, clean.value_type)
3200 }
3201 } else if clean is String {
3202 if has_val {
3203 tc.insert_loop_var(key_id, Type(int_))
3204 tc.insert_loop_var(val_id, Type(u8_))
3205 } else {
3206 tc.insert_loop_var(key_id, Type(u8_))
3207 }
3208 } else {
3209 container := tc.a.nodes[int(container_id)]
3210 if container.kind == .range {
3211 tc.insert_loop_var(key_id, Type(int_))
3212 } else if tc.should_diagnose(container_id) {
3213 tc.record_error(.cannot_index, 'cannot iterate over `${clean.name()}`',
3214 container_id)
3215 }
3216 }
3217 }
3218 for i in header .. node.children_count {
3219 tc.check_node(tc.a.child(&node, i))
3220 }
3221 tc.pop_scope()
3222}
3223
3224// check_decl_assign validates check decl assign state for types.
3225fn (mut tc TypeChecker) check_decl_assign(id flat.NodeId, node flat.Node) {
3226 if node.children_count == 0 {
3227 return
3228 }
3229 if tc.check_multi_return_decl_assign(id, node) {
3230 return
3231 }
3232 mut i := 0
3233 for i + 1 < node.children_count {
3234 lhs_id := tc.a.child(&node, i)
3235 rhs_id := tc.a.child(&node, i + 1)
3236 tc.check_node(rhs_id)
3237 mut rhs_type := tc.decl_assign_inferred_type(rhs_id)
3238 mut expected := rhs_type
3239 if node.children_count == 2 && node.typ.len > 0 {
3240 expected = tc.parse_type(node.typ)
3241 rhs_type = tc.resolve_expr(rhs_id, expected)
3242 if !tc.type_compatible(rhs_type, expected) {
3243 tc.type_mismatch(.assignment_mismatch,
3244 'cannot assign `${rhs_type.name()}` to `${expected.name()}`', id)
3245 }
3246 }
3247 tc.insert_decl_lhs(lhs_id, expected)
3248 tc.track_method_value_local(lhs_id, rhs_id)
3249 i += 2
3250 }
3251}
3252
3253// cur_scope_depth returns the number of enclosing scopes (the current scope's parent-chain
3254// length), used to tell a dominating top-level reassignment from one nested in a branch/loop.
3255fn (tc &TypeChecker) cur_scope_depth() int {
3256 mut d := 0
3257 mut s := tc.cur_scope
3258 for s != unsafe { nil } {
3259 d++
3260 s = s.parent
3261 }
3262 return d
3263}
3264
3265// track_method_value_local records (or clears) a local variable bound to a method value, so a
3266// later `return cb` / `arr << cb` aliasing the same per-site static receiver is rejected as an
3267// escape just like the bare `return c.report`.
3268fn (mut tc TypeChecker) track_method_value_local(lhs_id flat.NodeId, rhs_id flat.NodeId) {
3269 if int(lhs_id) < 0 {
3270 return
3271 }
3272 lhs := tc.a.nodes[int(lhs_id)]
3273 if lhs.kind != .ident || lhs.value.len == 0 || lhs.value == '_' {
3274 return
3275 }
3276 if tc.expr_is_method_value(rhs_id) {
3277 tc.method_value_locals[lhs.value] = true
3278 tc.method_value_local_depth[lhs.value] = tc.cur_scope_depth()
3279 } else if lhs.value in tc.method_value_locals {
3280 // Reassigned to a non-method-value. Only clear the marker when this reassignment
3281 // dominates later uses — at the same or a shallower scope than where the local was
3282 // marked. A reassignment in a deeper conditional/loop scope does not run on every path
3283 // (`mut cb := c.report; if x { cb = plain }; return cb`), so the local may still hold the
3284 // method value; keep the maybe-method marker and let the later escape be rejected.
3285 marked_depth := tc.method_value_local_depth[lhs.value] or { 0 }
3286 if tc.cur_scope_depth() <= marked_depth {
3287 tc.method_value_locals.delete(lhs.value)
3288 tc.method_value_local_depth.delete(lhs.value)
3289 }
3290 }
3291}
3292
3293fn (mut tc TypeChecker) decl_assign_inferred_type(rhs_id flat.NodeId) Type {
3294 if int(rhs_id) < 0 || int(rhs_id) >= tc.a.nodes.len {
3295 return unknown_type('missing declaration initializer')
3296 }
3297 rhs := tc.a.nodes[int(rhs_id)]
3298 if rhs.kind == .cast_expr && rhs.value.len > 0 {
3299 typ := tc.parse_type(rhs.value)
3300 if typ is Alias {
3301 return typ
3302 }
3303 }
3304 if typ := tc.infer_fn_value_decl_type(rhs_id) {
3305 return typ
3306 }
3307 return tc.resolve_type(rhs_id)
3308}
3309
3310fn (mut tc TypeChecker) infer_fn_value_decl_type(rhs_id flat.NodeId) ?Type {
3311 if int(rhs_id) < 0 || int(rhs_id) >= tc.a.nodes.len {
3312 return none
3313 }
3314 rhs := tc.a.nodes[int(rhs_id)]
3315 if tc.fn_value_shadowed_by_value(rhs) {
3316 return none
3317 }
3318 key := tc.fn_value_key(rhs) or { return none }
3319 typ := tc.fn_type_from_key(key) or { return none }
3320 tc.remember_resolved_fn_value_chain(rhs_id, key)
3321 tc.register_synth_type(rhs_id, typ)
3322 return typ
3323}
3324
3325fn (tc &TypeChecker) fn_value_shadowed_by_value(node flat.Node) bool {
3326 match node.kind {
3327 .ident {
3328 return tc.name_bound_as_value(node.value)
3329 }
3330 .selector {
3331 return tc.selector_base_bound_as_value(node)
3332 }
3333 .cast_expr, .paren, .expr_stmt {
3334 if node.children_count == 0 {
3335 return false
3336 }
3337 return tc.fn_value_shadowed_by_value(tc.a.child_node(&node, 0))
3338 }
3339 else {
3340 return false
3341 }
3342 }
3343}
3344
3345// lvalue_is_local_var reports whether an assignment target is safe to receive a method value:
3346// the blank discard `_` (stores nothing) or a plain function-local variable bound under its bare
3347// name in the current scope. Non-local storage (a struct field `h.cb`, an array/map element
3348// `cbs[i]`, or a module-level global, which lives in file_scope under its qualified name and so
3349// misses a bare lookup) is not. A method value may alias a local (tracked for a later escape) but
3350// must not be stored into anything that outlives the call site.
3351fn (tc &TypeChecker) lvalue_is_local_var(lhs_id flat.NodeId) bool {
3352 if int(lhs_id) < 0 {
3353 return false
3354 }
3355 lhs := tc.a.nodes[int(lhs_id)]
3356 if lhs.kind != .ident || lhs.value.len == 0 {
3357 return false
3358 }
3359 if lhs.value == '_' {
3360 return true
3361 }
3362 return tc.cur_scope.lookup(lhs.value) != none
3363}
3364
3365fn (tc &TypeChecker) selector_base_bound_as_value(node flat.Node) bool {
3366 if node.children_count == 0 {
3367 return false
3368 }
3369 base := tc.a.child_node(&node, 0)
3370 match base.kind {
3371 .ident {
3372 return tc.name_bound_as_value(base.value)
3373 }
3374 .selector {
3375 return tc.selector_base_bound_as_value(base)
3376 }
3377 .cast_expr, .paren, .expr_stmt {
3378 if base.children_count == 0 {
3379 return false
3380 }
3381 return tc.fn_value_shadowed_by_value(tc.a.child_node(base, 0))
3382 }
3383 else {
3384 return false
3385 }
3386 }
3387}
3388
3389fn (tc &TypeChecker) name_bound_as_value(name string) bool {
3390 if name.len == 0 {
3391 return false
3392 }
3393 if typ := tc.cur_scope.lookup(name) {
3394 return typ !is Void
3395 }
3396 if typ := tc.file_scope.lookup(name) {
3397 return typ !is Void
3398 }
3399 return false
3400}
3401
3402// check_multi_return_decl_assign validates check multi return decl assign state for types.
3403fn (mut tc TypeChecker) check_multi_return_decl_assign(id flat.NodeId, node flat.Node) bool {
3404 if node.children_count < 3 {
3405 return false
3406 }
3407 rhs_id := tc.a.child(&node, 1)
3408 rhs_type := tc.resolve_type(rhs_id)
3409 rhs_type_name := rhs_type.name()
3410 if rhs_type is MultiReturn {
3411 tc.check_node(rhs_id)
3412 lhs_ids := tc.multi_assign_lhs_ids(node)
3413 if lhs_ids.len != rhs_type.types.len {
3414 if tc.should_diagnose(id) {
3415 tc.record_error(.assignment_mismatch,
3416 'multi-return assignment mismatch: ${lhs_ids.len} variables but `${rhs_type_name}` has ${rhs_type.types.len} values',
3417 id)
3418 }
3419 return true
3420 }
3421 for i, lhs_id in lhs_ids {
3422 tc.insert_decl_lhs(lhs_id, rhs_type.types[i])
3423 }
3424 return true
3425 }
3426 return false
3427}
3428
3429// multi_assign_lhs_ids supports multi assign lhs ids handling for TypeChecker.
3430fn (tc &TypeChecker) multi_assign_lhs_ids(node flat.Node) []flat.NodeId {
3431 mut lhs_ids := []flat.NodeId{}
3432 if node.children_count > 0 {
3433 lhs_ids << tc.a.child(&node, 0)
3434 }
3435 for i in 2 .. node.children_count {
3436 lhs_ids << tc.a.child(&node, i)
3437 }
3438 return lhs_ids
3439}
3440
3441// insert_decl_lhs updates insert decl lhs state for types.
3442fn (mut tc TypeChecker) insert_decl_lhs(lhs_id flat.NodeId, typ Type) {
3443 if int(lhs_id) < 0 || typ is Void {
3444 return
3445 }
3446 lhs := tc.a.nodes[int(lhs_id)]
3447 if lhs.kind == .ident && lhs.value.len > 0 {
3448 tc.cur_scope.insert(lhs.value, typ)
3449 tc.register_synth_type(lhs_id, typ)
3450 }
3451}
3452
3453// check_assign validates check assign state for types.
3454fn (mut tc TypeChecker) check_assign(id flat.NodeId, node flat.Node) {
3455 if node.children_count < 2 {
3456 return
3457 }
3458 if node.kind == .index_assign && tc.reject_unlowered_map_mutation
3459 && tc.index_assign_lhs_is_map(node) {
3460 if tc.should_diagnose(id) {
3461 tc.record_error(.assignment_mismatch,
3462 'internal compiler error: unlowered map index assignment reached post-transform checker',
3463 id)
3464 }
3465 for i := 1; i < node.children_count; i += 2 {
3466 tc.check_node(tc.a.child(&node, i))
3467 }
3468 return
3469 }
3470 if tc.check_multi_return_assign(id, node) {
3471 return
3472 }
3473 mut i := 0
3474 for i + 1 < node.children_count {
3475 lhs_id := tc.a.child(&node, i)
3476 rhs_id := tc.a.child(&node, i + 1)
3477 lhs_type := tc.resolve_lvalue_type(lhs_id)
3478 tc.check_node(rhs_id)
3479 rhs_type := tc.resolve_expr(rhs_id, lhs_type)
3480 if !tc.type_compatible(rhs_type, lhs_type) {
3481 tc.type_mismatch(.assignment_mismatch,
3482 'cannot assign `${rhs_type.name()}` to `${lhs_type.name()}`', id)
3483 }
3484 if node.kind in [.assign, .selector_assign, .index_assign] {
3485 if tc.expr_is_method_value(rhs_id) && !tc.lvalue_is_local_var(lhs_id) {
3486 // Storing a method value into a struct field (`h.cb = ..`), an array/map element
3487 // (`cbs[i] = ..`), or a global lets it outlive the per-site static `_mvctx_N`
3488 // receiver slot, which the next evaluation of the same site overwrites — so every
3489 // stored callback would use the last receiver. Reject it like the other escapes.
3490 tc.reject_stored_method_value(rhs_id)
3491 } else {
3492 tc.track_method_value_local(lhs_id, rhs_id)
3493 }
3494 }
3495 i += 2
3496 }
3497}
3498
3499// index_assign_lhs_is_map supports index assign lhs is map handling for TypeChecker.
3500fn (tc &TypeChecker) index_assign_lhs_is_map(node flat.Node) bool {
3501 if node.children_count == 0 {
3502 return false
3503 }
3504 lhs_id := tc.a.child(&node, 0)
3505 if int(lhs_id) < 0 {
3506 return false
3507 }
3508 lhs := tc.a.nodes[int(lhs_id)]
3509 if lhs.kind != .index || lhs.children_count < 2 {
3510 return false
3511 }
3512 base_type := unwrap_pointer(tc.resolve_type(tc.a.child(&lhs, 0)))
3513 return base_type is Map
3514}
3515
3516// check_multi_return_assign validates check multi return assign state for types.
3517fn (mut tc TypeChecker) check_multi_return_assign(id flat.NodeId, node flat.Node) bool {
3518 if node.children_count < 3 {
3519 return false
3520 }
3521 rhs_id := tc.a.child(&node, 1)
3522 rhs_type := tc.resolve_type(rhs_id)
3523 rhs_type_name := rhs_type.name()
3524 if rhs_type is MultiReturn {
3525 tc.check_node(rhs_id)
3526 lhs_ids := tc.multi_assign_lhs_ids(node)
3527 if lhs_ids.len != rhs_type.types.len {
3528 if tc.should_diagnose(id) {
3529 tc.record_error(.assignment_mismatch,
3530 'multi-return assignment mismatch: ${lhs_ids.len} variables but `${rhs_type_name}` has ${rhs_type.types.len} values',
3531 id)
3532 }
3533 return true
3534 }
3535 for i, lhs_id in lhs_ids {
3536 lhs_type := tc.resolve_lvalue_type(lhs_id)
3537 if !tc.type_compatible(rhs_type.types[i], lhs_type) {
3538 tc.type_mismatch(.assignment_mismatch,
3539 'cannot assign `${rhs_type.types[i].name()}` to `${lhs_type.name()}`', id)
3540 }
3541 }
3542 return true
3543 }
3544 return false
3545}
3546
3547// check_postfix validates check postfix state for types.
3548fn (mut tc TypeChecker) check_postfix(id flat.NodeId, node flat.Node) {
3549 if node.children_count == 0 {
3550 return
3551 }
3552 child_id := tc.a.child(&node, 0)
3553 tc.check_node(child_id)
3554 child := tc.a.nodes[int(child_id)]
3555 if child.kind == .index && child.children_count >= 2 {
3556 base_type := unwrap_pointer(tc.resolve_type(tc.a.child(&child, 0)))
3557 if base_type is Map && node.op in [.inc, .dec] && tc.reject_unlowered_map_mutation
3558 && tc.should_diagnose(id) {
3559 tc.record_error(.assignment_mismatch,
3560 'internal compiler error: unlowered map index postfix mutation reached post-transform checker',
3561 id)
3562 }
3563 }
3564}
3565
3566// resolve_lvalue_type resolves resolve lvalue type information for types.
3567fn (mut tc TypeChecker) resolve_lvalue_type(lhs_id flat.NodeId) Type {
3568 if int(lhs_id) < 0 {
3569 return Type(void_)
3570 }
3571 lhs := tc.a.nodes[int(lhs_id)]
3572 if lhs.kind == .ident {
3573 if typ := tc.cur_scope.lookup(lhs.value) {
3574 return typ
3575 }
3576 if typ := tc.file_scope.lookup(lhs.value) {
3577 return typ
3578 }
3579 if tc.should_diagnose(lhs_id) && lhs.value != '_' {
3580 tc.record_error(.unknown_ident, 'unknown identifier `${lhs.value}`', lhs_id)
3581 }
3582 return unknown_type('unknown identifier `${lhs.value}`')
3583 }
3584 if lhs.kind == .selector {
3585 tc.check_selector(lhs_id, lhs)
3586 return tc.resolve_type(lhs_id)
3587 }
3588 if lhs.kind == .index {
3589 tc.check_index(lhs_id, lhs)
3590 return tc.resolve_type(lhs_id)
3591 }
3592 return tc.resolve_type(lhs_id)
3593}
3594
3595// check_return validates check return state for types.
3596fn (mut tc TypeChecker) check_return(id flat.NodeId, node flat.Node) {
3597 // A returned method value escapes the function, where its per-site static receiver
3598 // can't keep multiple returned callbacks distinct (a factory `fn bind(c) fn () int {
3599 // return c.report }`); reject it rather than emitting invalid C.
3600 for i in 0 .. node.children_count {
3601 tc.reject_stored_method_value(tc.a.child(&node, i))
3602 }
3603 expected := tc.cur_fn_ret_type
3604 if expected is Void {
3605 if node.children_count > 0 && tc.should_diagnose(id) {
3606 tc.record_error(.return_mismatch, 'void function should not return a value', id)
3607 }
3608 for i in 0 .. node.children_count {
3609 tc.check_node(tc.a.child(&node, i))
3610 }
3611 return
3612 }
3613 if node.children_count == 0 {
3614 if type_allows_implicit_return(expected) {
3615 return
3616 }
3617 if tc.should_diagnose(id) {
3618 tc.record_error(.return_mismatch, 'missing return value of type `${expected.name()}`',
3619 id)
3620 }
3621 return
3622 }
3623 if multi := multi_return_payload_type(expected) {
3624 if node.children_count == 1 {
3625 child_id := tc.a.child(&node, 0)
3626 tc.check_node(child_id)
3627 actual := tc.resolve_expr(child_id, expected)
3628 if tc.type_compatible(actual, expected) {
3629 return
3630 }
3631 }
3632 if node.children_count != multi.types.len {
3633 if tc.should_diagnose(id) {
3634 tc.record_error(.return_mismatch,
3635 'return value count mismatch: expected ${multi.types.len}, got ${node.children_count}',
3636 id)
3637 }
3638 return
3639 }
3640 for i in 0 .. node.children_count {
3641 child_id := tc.a.child(&node, i)
3642 tc.check_node(child_id)
3643 actual := tc.resolve_expr(child_id, multi.types[i])
3644 if !tc.type_compatible(actual, multi.types[i]) {
3645 tc.type_mismatch(.return_mismatch,
3646 'cannot return `${actual.name()}` as `${multi.types[i].name()}`', id)
3647 }
3648 }
3649 return
3650 }
3651 if node.children_count != 1 {
3652 if tc.should_diagnose(id) {
3653 tc.record_error(.return_mismatch,
3654 'return value count mismatch: expected 1, got ${node.children_count}', id)
3655 }
3656 return
3657 }
3658 child_id := tc.a.child(&node, 0)
3659 tc.check_node(child_id)
3660 actual := tc.resolve_expr(child_id, expected)
3661 if !tc.type_compatible(actual, expected) {
3662 tc.type_mismatch(.return_mismatch,
3663 'cannot return `${actual.name()}` as `${expected.name()}`', id)
3664 }
3665}
3666
3667fn multi_return_payload_type(typ Type) ?MultiReturn {
3668 if typ is MultiReturn {
3669 return typ
3670 }
3671 if typ is OptionType {
3672 base := typ.base_type
3673 if base is MultiReturn {
3674 return base
3675 }
3676 }
3677 if typ is ResultType {
3678 base := typ.base_type
3679 if base is MultiReturn {
3680 return base
3681 }
3682 }
3683 return none
3684}
3685
3686// check_call validates check call state for types.
3687fn (mut tc TypeChecker) check_call(id flat.NodeId, node flat.Node) {
3688 if info := tc.resolve_call_info(id, node) {
3689 if info.name.len > 0 && !is_array_dsl_call_name(info.name) {
3690 tc.remember_resolved_call(id, info.name)
3691 }
3692 if info.return_type !is Void && info.return_type !is Unknown {
3693 tc.remember_expr_type(id, info.return_type)
3694 }
3695 tc.check_call_arg_types(id, node, info)
3696 return
3697 }
3698 if tc.call_has_ambiguous_selective_import(node) {
3699 tc.record_error(.unknown_fn, 'ambiguous selective import `${tc.call_display_name(node)}`',
3700 id)
3701 return
3702 }
3703 if tc.should_diagnose(id) && !tc.is_known_call(node) {
3704 tc.record_error(.unknown_fn, 'unknown function `${tc.call_display_name(node)}`', id)
3705 }
3706 for i in 1 .. node.children_count {
3707 tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))
3708 }
3709}
3710
3711// should_diagnose reports whether should diagnose applies in types.
3712fn (tc &TypeChecker) should_diagnose(id flat.NodeId) bool {
3713 if int(id) < 0 || int(id) < tc.a.user_code_start {
3714 return false
3715 }
3716 if int(id) < tc.a.nodes.len && !tc.a.nodes[int(id)].pos.is_valid() && !tc.diagnose_unknown_calls {
3717 return false
3718 }
3719 if tc.diagnostic_files.len == 0 {
3720 return true
3721 }
3722 return tc.cur_file in tc.diagnostic_files
3723}
3724
3725fn (tc &TypeChecker) should_diagnose_unsupported_generic(id flat.NodeId) bool {
3726 if tc.should_diagnose(id) {
3727 return true
3728 }
3729 if int(id) < 0 || int(id) < tc.a.user_code_start {
3730 return false
3731 }
3732 if tc.diagnostic_files.len == 0 {
3733 return false
3734 }
3735 return tc.diagnostic_files['generic:' + tc.cur_file]
3736}
3737
3738// should_diagnose_unknown_call reports whether should diagnose unknown call applies in types.
3739fn (tc &TypeChecker) should_diagnose_unknown_call(id flat.NodeId) bool {
3740 return tc.diagnose_unknown_calls && tc.should_diagnose(id)
3741}
3742
3743// resolve_call_info resolves resolve call info information for types.
3744fn (mut tc TypeChecker) resolve_call_info(_id flat.NodeId, node flat.Node) ?CallInfo {
3745 if node.children_count == 0 {
3746 return none
3747 }
3748 fn_node := tc.a.child_node(&node, 0)
3749 if info := tc.resolve_generic_call_info(fn_node) {
3750 return info
3751 }
3752 if fn_node.kind == .selector {
3753 base_id := tc.a.child(fn_node, 0)
3754 base_node := tc.a.nodes[int(base_id)]
3755 if base_node.kind == .ident && base_node.value == 'C' {
3756 return none
3757 }
3758 if base_node.kind == .ident {
3759 if resolved_mod := tc.resolve_import_alias(base_node.value) {
3760 mod_name := '${resolved_mod}.${fn_node.value}'
3761 if mod_name in tc.fn_ret_types {
3762 return tc.call_info(mod_name, false)
3763 }
3764 }
3765 qbase := tc.qualify_name(base_node.value)
3766 static_name := '${qbase}.${fn_node.value}'
3767 if static_name in tc.fn_ret_types && (qbase in tc.structs
3768 || qbase in tc.enum_names || qbase in tc.sum_types
3769 || qbase in tc.interface_names || qbase in tc.type_aliases) {
3770 // `qbase in tc.type_aliases` covers static methods on a type alias,
3771 // e.g. `fn SimdFloat4.new()` for `type SimdFloat4 = vec.Vec4[f32]`.
3772 return tc.call_info(static_name, false)
3773 }
3774 if fn_node.value == 'zero' && qbase in tc.flag_enums {
3775 return CallInfo{
3776 name: ''
3777 params: []Type{}
3778 return_type: Type(Enum{
3779 name: qbase
3780 is_flag: true
3781 })
3782 params_known: true
3783 }
3784 }
3785 } else if base_node.kind == .selector {
3786 if method_name := tc.module_const_receiver_method_name(base_node, fn_node.value) {
3787 return tc.call_info(method_name, true)
3788 }
3789 inner := tc.a.child_node(base_node, 0)
3790 if inner.kind == .ident {
3791 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
3792 full_name := '${mod_name}.${base_node.value}.${fn_node.value}'
3793 if full_name in tc.fn_ret_types {
3794 return tc.call_info(full_name, false)
3795 }
3796 }
3797 }
3798 if fn_typ := tc.selector_fn_type(fn_node) {
3799 return CallInfo{
3800 name: ''
3801 params: fn_typ.params.clone()
3802 return_type: fn_typ.return_type
3803 params_known: true
3804 }
3805 }
3806 base_type := tc.resolve_type(base_id)
3807 clean := unwrap_pointer(base_type)
3808 if clean is Channel && fn_node.value == 'close' {
3809 return CallInfo{
3810 name: 'chan.close'
3811 params: tarr1(base_type)
3812 return_type: Type(void_)
3813 has_receiver: true
3814 params_known: true
3815 }
3816 }
3817 if clean is Array && fn_node.value == 'clone' {
3818 return CallInfo{
3819 name: 'array_clone'
3820 params: tarr1(base_type)
3821 return_type: base_type
3822 has_receiver: true
3823 params_known: true
3824 }
3825 }
3826 if clean is Map && fn_node.value == 'clone' {
3827 return CallInfo{
3828 name: ''
3829 params: tarr1(base_type)
3830 return_type: base_type
3831 has_receiver: true
3832 params_known: true
3833 }
3834 }
3835 if clean is Array {
3836 match fn_node.value {
3837 'first', 'last', 'pop' {
3838 return CallInfo{
3839 name: ''
3840 params: tarr1(base_type)
3841 return_type: clean.elem_type
3842 has_receiver: true
3843 params_known: true
3844 }
3845 }
3846 'contains' {
3847 elem_type := tc.array_contains_elem_type(base_node, clean)
3848 return CallInfo{
3849 name: ''
3850 params: tarr2(base_type, elem_type)
3851 return_type: Type(bool_)
3852 has_receiver: true
3853 params_known: true
3854 }
3855 }
3856 'join' {
3857 return CallInfo{
3858 name: 'array.join'
3859 params: tarr2(base_type, Type(String{}))
3860 return_type: Type(String{})
3861 has_receiver: true
3862 params_known: true
3863 }
3864 }
3865 'index', 'last_index' {
3866 return CallInfo{
3867 name: ''
3868 params: tarr2(base_type, clean.elem_type)
3869 return_type: Type(int_)
3870 has_receiver: true
3871 params_known: true
3872 }
3873 }
3874 'repeat' {
3875 return CallInfo{
3876 name: 'array.repeat_to_depth'
3877 params: tarr2(base_type, Type(int_))
3878 return_type: base_type
3879 has_receiver: true
3880 params_known: true
3881 }
3882 }
3883 'repeat_to_depth' {
3884 return CallInfo{
3885 name: 'array.repeat_to_depth'
3886 params: tarr3(base_type, Type(int_), Type(int_))
3887 return_type: base_type
3888 has_receiver: true
3889 params_known: true
3890 }
3891 }
3892 'delete' {
3893 return CallInfo{
3894 name: ''
3895 params: tarr2(Type(Pointer{
3896 base_type: base_type
3897 }), Type(int_))
3898 return_type: Type(void_)
3899 has_receiver: true
3900 params_known: true
3901 }
3902 }
3903 'delete_last', 'clear' {
3904 return CallInfo{
3905 name: ''
3906 params: tarr1(Type(Pointer{
3907 base_type: base_type
3908 }))
3909 return_type: Type(void_)
3910 has_receiver: true
3911 params_known: true
3912 }
3913 }
3914 'filter' {
3915 return CallInfo{
3916 name: 'array.filter'
3917 params: tarr2(base_type, Type(bool_))
3918 return_type: base_type
3919 has_receiver: true
3920 params_known: true
3921 }
3922 }
3923 'map' {
3924 elem_type := tc.array_map_return_elem_type(node)
3925 return CallInfo{
3926 name: 'array.map'
3927 params: tarr2(base_type, elem_type)
3928 return_type: Type(Array{
3929 elem_type: elem_type
3930 })
3931 has_receiver: true
3932 params_known: true
3933 }
3934 }
3935 'any', 'all' {
3936 return CallInfo{
3937 name: 'array.${fn_node.value}'
3938 params: tarr2(base_type, Type(bool_))
3939 return_type: Type(bool_)
3940 has_receiver: true
3941 params_known: true
3942 }
3943 }
3944 'count' {
3945 return CallInfo{
3946 name: 'array.count'
3947 params: tarr2(base_type, Type(bool_))
3948 return_type: Type(int_)
3949 has_receiver: true
3950 params_known: true
3951 }
3952 }
3953 'sort' {
3954 mut params := tarr1(Type(Pointer{
3955 base_type: base_type
3956 }))
3957 if call_explicit_arg_count(node) > 0 {
3958 params << Type(bool_)
3959 }
3960 return CallInfo{
3961 name: 'array.sort'
3962 params: params
3963 return_type: Type(void_)
3964 has_receiver: true
3965 params_known: true
3966 }
3967 }
3968 'sorted' {
3969 mut params := tarr1(base_type)
3970 if call_explicit_arg_count(node) > 0 {
3971 params << Type(bool_)
3972 }
3973 return CallInfo{
3974 name: 'array.sorted'
3975 params: params
3976 return_type: base_type
3977 has_receiver: true
3978 params_known: true
3979 }
3980 }
3981 else {}
3982 }
3983 }
3984 type_name := resolve_type_name_for_method(clean)
3985 if type_name.len > 0 {
3986 if fn_node.value == 'str' && clean is Primitive {
3987 return CallInfo{
3988 name: ''
3989 params: tarr1(base_type)
3990 return_type: Type(string_)
3991 has_receiver: true
3992 params_known: true
3993 }
3994 }
3995 mname := '${type_name}.${fn_node.value}'
3996 if mname in tc.fn_ret_types {
3997 return tc.call_info(mname, true)
3998 }
3999 if info := tc.embedded_method_call_info(type_name, fn_node.value) {
4000 return info
4001 }
4002 if info := tc.resolve_generic_struct_method(type_name, fn_node.value) {
4003 return info
4004 }
4005 if fn_node.value == 'use' && clean is Struct
4006 && tc.struct_has_middleware_receiver(type_name) {
4007 return CallInfo{
4008 name: '${type_name}.use'
4009 params: []Type{}
4010 return_type: Type(void_)
4011 has_receiver: true
4012 params_known: false
4013 }
4014 }
4015 }
4016 if clean is SumType {
4017 mname := '${clean.name}.${fn_node.value}'
4018 if mname in tc.fn_ret_types {
4019 return tc.call_info(mname, true)
4020 }
4021 }
4022 if clean is Enum {
4023 if clean.is_flag && fn_node.value in ['has', 'all'] {
4024 return CallInfo{
4025 name: ''
4026 params: tarr2(base_type, base_type)
4027 return_type: Type(bool_)
4028 has_receiver: true
4029 params_known: true
4030 }
4031 }
4032 if fn_node.value == 'str' {
4033 return CallInfo{
4034 name: '${clean.name}.str'
4035 params: tarr1(base_type)
4036 return_type: Type(string_)
4037 has_receiver: true
4038 params_known: true
4039 }
4040 }
4041 mname := '${clean.name}.${fn_node.value}'
4042 if mname in tc.fn_ret_types {
4043 return tc.call_info(mname, true)
4044 }
4045 }
4046 return none
4047 }
4048 if fn_node.kind == .ident {
4049 if fn_node.value == 'error' || fn_node.value == 'error_with_code' {
4050 mut params := []Type{}
4051 if fn_node.value == 'error_with_code' {
4052 params = tarr2(Type(string_), Type(int_))
4053 } else {
4054 params = tarr1(Type(string_))
4055 }
4056 return CallInfo{
4057 name: fn_node.value
4058 params: params
4059 return_type: tc.parse_type('IError')
4060 params_known: true
4061 }
4062 }
4063 if typ := tc.cur_scope.lookup(fn_node.value) {
4064 if fn_typ := fn_type_from_type(typ) {
4065 return CallInfo{
4066 name: ''
4067 params: fn_typ.params
4068 return_type: fn_typ.return_type
4069 params_known: true
4070 }
4071 }
4072 }
4073 qfn := tc.qualify_fn_name(fn_node.value)
4074 if qfn in tc.fn_ret_types {
4075 return tc.call_info(qfn, false)
4076 }
4077 if imported_name := tc.resolve_selective_import_symbol(fn_node.value) {
4078 return tc.call_info(imported_name, false)
4079 }
4080 if fn_node.value in tc.fn_ret_types {
4081 return tc.call_info(fn_node.value, false)
4082 }
4083 if fn_node.value in ['print', 'println', 'eprint', 'eprintln', 'panic'] {
4084 return CallInfo{
4085 name: fn_node.value
4086 params: []Type{}
4087 return_type: Type(void_)
4088 params_known: false
4089 }
4090 }
4091 }
4092 return none
4093}
4094
4095fn (mut tc TypeChecker) resolve_generic_call_info(fn_node flat.Node) ?CallInfo {
4096 if fn_node.kind != .index || fn_node.children_count < 2 || fn_node.value == 'range' {
4097 return none
4098 }
4099 base_id := tc.a.child(&fn_node, 0)
4100 type_arg_id := tc.a.child(&fn_node, 1)
4101 type_arg := tc.generic_call_type_arg_name(type_arg_id)
4102 if type_arg.len == 0 {
4103 return none
4104 }
4105 base_node := tc.a.nodes[int(base_id)]
4106 call_name := tc.generic_call_base_name(base_node) or { return none }
4107 if is_veb_run_at_call_name(call_name) {
4108 return CallInfo{
4109 name: call_name
4110 params: []Type{}
4111 return_type: Type(ResultType{
4112 base_type: Type(void_)
4113 })
4114 params_known: false
4115 }
4116 }
4117 if call_name !in tc.fn_ret_types {
4118 return none
4119 }
4120 if is_decode_call_name(call_name) {
4121 return CallInfo{
4122 name: call_name
4123 params: tc.fn_param_types[call_name] or { []Type{} }
4124 return_type: Type(ResultType{
4125 base_type: tc.parse_type(type_arg)
4126 })
4127 has_receiver: false
4128 is_variadic: tc.fn_variadic[call_name] or { false }
4129 is_c_variadic: tc.c_variadic_fns[call_name] or { false }
4130 params_known: call_name in tc.fn_param_types
4131 }
4132 }
4133 return tc.call_info(call_name, false)
4134}
4135
4136fn is_decode_call_name(name string) bool {
4137 return name in ['json.decode', 'json2.decode']
4138}
4139
4140fn is_veb_run_at_call_name(name string) bool {
4141 return name == 'veb.run_at'
4142}
4143
4144fn (tc &TypeChecker) generic_call_base_name(base_node flat.Node) ?string {
4145 if base_node.kind == .ident {
4146 qfn := tc.qualify_fn_name(base_node.value)
4147 if qfn in tc.fn_ret_types {
4148 return qfn
4149 }
4150 if imported_name := tc.resolve_selective_import_symbol(base_node.value) {
4151 return imported_name
4152 }
4153 if base_node.value in tc.fn_ret_types {
4154 return base_node.value
4155 }
4156 return none
4157 }
4158 if base_node.kind == .selector && base_node.children_count > 0 {
4159 inner := tc.a.child_node(&base_node, 0)
4160 if inner.kind == .ident {
4161 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
4162 full_name := '${mod_name}.${base_node.value}'
4163 if is_veb_run_at_call_name(full_name) {
4164 return full_name
4165 }
4166 if full_name in tc.fn_ret_types {
4167 return full_name
4168 }
4169 }
4170 }
4171 return none
4172}
4173
4174fn (tc &TypeChecker) generic_call_type_arg_name(id flat.NodeId) string {
4175 if int(id) < 0 {
4176 return ''
4177 }
4178 node := tc.a.nodes[int(id)]
4179 match node.kind {
4180 .ident {
4181 return node.value
4182 }
4183 .selector {
4184 if node.children_count == 0 {
4185 return node.value
4186 }
4187 base := tc.generic_call_type_arg_name(tc.a.child(&node, 0))
4188 if base.len == 0 {
4189 return node.value
4190 }
4191 return '${base}.${node.value}'
4192 }
4193 .index {
4194 if node.children_count < 2 || node.value == 'range' {
4195 return ''
4196 }
4197 base := tc.generic_call_type_arg_name(tc.a.child(&node, 0))
4198 arg := tc.generic_call_type_arg_name(tc.a.child(&node, 1))
4199 if base.len == 0 || arg.len == 0 {
4200 return ''
4201 }
4202 return '${base}[${arg}]'
4203 }
4204 .array_init {
4205 if node.value.len > 0 {
4206 return '[]${node.value}'
4207 }
4208 return ''
4209 }
4210 .prefix {
4211 if node.children_count == 0 {
4212 return ''
4213 }
4214 child := tc.generic_call_type_arg_name(tc.a.child(&node, 0))
4215 if child.len == 0 {
4216 return ''
4217 }
4218 if node.op == .amp {
4219 return '&${child}'
4220 }
4221 return child
4222 }
4223 else {
4224 return ''
4225 }
4226 }
4227}
4228
4229// call_info updates call info state for TypeChecker.
4230fn (tc &TypeChecker) call_info(name string, has_receiver bool) CallInfo {
4231 mut params := []Type{}
4232 mut params_known := false
4233 if p := tc.fn_param_types[name] {
4234 params = p.clone()
4235 params_known = true
4236 }
4237 if is_print_style_fn_name(name) && params.len == 1
4238 && print_style_param_accepts_string(params[0]) {
4239 params[0] = unknown_type('print argument')
4240 }
4241 return CallInfo{
4242 name: name
4243 params: params
4244 return_type: tc.fn_ret_types[name] or {
4245 unknown_type('unknown return type for `${name}`')
4246 }
4247 has_receiver: has_receiver
4248 is_variadic: tc.fn_variadic[name] or { false }
4249 is_c_variadic: tc.c_variadic_fns[name] or { false }
4250 params_known: params_known
4251 has_implicit_veb_ctx: tc.fn_implicit_veb_ctx[name] or { false }
4252 }
4253}
4254
4255// is_print_style_fn_name reports whether is print style fn name applies in types.
4256fn is_print_style_fn_name(name string) bool {
4257 return name in ['print', 'println', 'eprint', 'eprintln', 'builtin.print', 'builtin.println',
4258 'builtin.eprint', 'builtin.eprintln']
4259}
4260
4261// print_style_param_accepts_string updates print style param accepts string state for types.
4262fn print_style_param_accepts_string(typ Type) bool {
4263 mut clean := typ
4264 for _ in 0 .. 8 {
4265 if clean is Alias {
4266 clean = clean.base_type
4267 continue
4268 }
4269 break
4270 }
4271 return clean is String
4272}
4273
4274// check_call_arg_types validates check call arg types state for types.
4275fn (mut tc TypeChecker) check_call_arg_types(id flat.NodeId, node flat.Node, info0 CallInfo) {
4276 info := tc.specialized_plain_generic_call_info(node, info0)
4277 if node.children_count == 0 {
4278 return
4279 }
4280 if info.name.starts_with('map.') {
4281 for i in 1 .. node.children_count {
4282 tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))
4283 }
4284 return
4285 }
4286 if !info.params_known {
4287 for i in 1 .. node.children_count {
4288 tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))
4289 }
4290 return
4291 }
4292 // `@[params]` struct args: trailing `key: value` args collapse into one struct argument.
4293 // field_init args only appear for this syntax, so they are a reliable signal.
4294 mut field_init_args := 0
4295 for i in 1 .. node.children_count {
4296 if tc.a.child_node(&node, i).kind == .field_init {
4297 field_init_args++
4298 }
4299 }
4300 collapsed := if field_init_args > 0 { 1 } else { 0 }
4301 recv_extra := if info.has_receiver { 1 } else { 0 }
4302 actual_count := node.children_count - 1 - field_init_args + collapsed + recv_extra
4303 // A hidden veb `Context` parameter may be supplied implicitly from the
4304 // enclosing handler instead of by the caller, so accept argument counts both
4305 // with the ctx (route dispatch) and without it (handler delegation).
4306 ctx_count := if info.has_implicit_veb_ctx { 1 } else { 0 }
4307 ctx_omitted := ctx_count > 0 && actual_count < info.params.len
4308 min_count := tc.min_required_arg_count(info) - ctx_count
4309 if actual_count < min_count || (!info.is_variadic && actual_count > info.params.len) {
4310 if tc.should_diagnose(id) {
4311 tc.record_error(.call_arg_mismatch,
4312 'argument count mismatch for `${tc.call_display_name(node)}`: expected ${info.params.len}, got ${actual_count}',
4313 id)
4314 }
4315 for i in 1 .. node.children_count {
4316 tc.check_node(tc.call_arg_value(tc.a.child(&node, i)))
4317 }
4318 return
4319 }
4320 if info.has_receiver && info.params.len > 0 {
4321 fn_node := tc.a.child_node(&node, 0)
4322 recv_id := tc.a.child(fn_node, 0)
4323 tc.check_node(recv_id)
4324 recv_type := tc.resolve_expr(recv_id, info.params[0])
4325 if !tc.receiver_compatible(recv_type, info.params[0])
4326 && !tc.receiver_embeds(recv_type, info.params[0]) {
4327 tc.type_mismatch(.call_arg_mismatch,
4328 'cannot use receiver `${recv_type.name()}` as `${info.params[0].name()}`', id)
4329 }
4330 }
4331 for i in 1 .. node.children_count {
4332 arg_id := tc.call_arg_value(tc.a.child(&node, i))
4333 // field_init args are fields of the collapsed `@[params]` struct, not positional params
4334 if tc.a.child_node(&node, i).kind == .field_init {
4335 tc.check_node(arg_id)
4336 continue
4337 }
4338 // When the caller omitted the implicit veb `Context` parameter, skip it
4339 // (it is inserted right after the receiver) while mapping the caller's
4340 // positional arguments to the callee's params.
4341 arg_shift := if ctx_omitted { ctx_count } else { 0 }
4342 param_idx := (if info.has_receiver { i } else { i - 1 }) + arg_shift
4343 has_dsl_scope := tc.call_arg_needs_array_dsl_scope(info.name, param_idx)
4344 if has_dsl_scope {
4345 tc.push_array_dsl_scope(node, info.name)
4346 tc.check_node(arg_id)
4347 } else {
4348 tc.check_node(arg_id)
4349 }
4350 if info.is_c_variadic && param_idx >= c_variadic_fixed_param_count(info) {
4351 if has_dsl_scope {
4352 tc.pop_scope()
4353 }
4354 continue
4355 }
4356 if param_idx >= info.params.len {
4357 if info.is_variadic && info.params.len > 0 {
4358 variadic_raw := info.params[info.params.len - 1]
4359 if variadic_raw is Array {
4360 elem_type := array_elem_type(variadic_raw)
4361 actual := tc.resolve_expr(arg_id, elem_type)
4362 if !tc.receiver_compatible(actual, elem_type) {
4363 tc.type_mismatch(.call_arg_mismatch, 'cannot use `${actual.name()}` as argument ${
4364 param_idx + 1} to `${tc.call_display_name(node)}`; expected `${elem_type.name()}`',
4365 id)
4366 }
4367 }
4368 }
4369 if has_dsl_scope {
4370 tc.pop_scope()
4371 }
4372 continue
4373 }
4374 mut expected := info.params[param_idx]
4375 if tc.is_zero_literal(arg_id) && is_fn_pointer_type(expected) {
4376 if has_dsl_scope {
4377 tc.pop_scope()
4378 }
4379 continue
4380 }
4381 expected_raw := expected
4382 if info.is_variadic && param_idx == info.params.len - 1 && expected_raw is Array {
4383 elem_type := array_elem_type(expected_raw)
4384 actual := tc.resolve_expr(arg_id, expected)
4385 actual_name := actual.name()
4386 expected_name := '[]${elem_type.name()}'
4387 actual_raw := actual
4388 if actual is Array {
4389 if !tc.receiver_compatible(actual_raw, expected) {
4390 tc.type_mismatch(.call_arg_mismatch, 'cannot use `${actual_name}` as argument ${
4391 param_idx + 1} to `${tc.call_display_name(node)}`; expected `${expected_name}`',
4392 id)
4393 }
4394 if has_dsl_scope {
4395 tc.pop_scope()
4396 }
4397 continue
4398 }
4399 expected = elem_type
4400 }
4401 mut actual := Type(void_)
4402 if has_dsl_scope {
4403 actual = tc.resolve_expr(arg_id, expected)
4404 tc.pop_scope()
4405 } else {
4406 actual = tc.resolve_expr(arg_id, expected)
4407 }
4408 if !tc.receiver_compatible(actual, expected) {
4409 if tc.is_os_args_contains_call(node) && param_idx == 1 && actual is String {
4410 continue
4411 }
4412 tc.type_mismatch(.call_arg_mismatch, 'cannot use `${actual.name()}` as argument ${
4413 param_idx + 1} to `${tc.call_display_name(node)}`; expected `${expected.name()}`',
4414 id)
4415 }
4416 }
4417}
4418
4419fn (mut tc TypeChecker) specialized_plain_generic_call_info(node flat.Node, info CallInfo) CallInfo {
4420 generic_params := tc.fn_generic_params[info.name] or { return info }
4421 param_texts := tc.fn_param_type_texts[info.name] or { return info }
4422 if generic_params.len == 0 || node.children_count <= 1 {
4423 return info
4424 }
4425 mut inferred := map[string]string{}
4426 for i, param_text in param_texts {
4427 arg_idx := i + 1
4428 if arg_idx >= node.children_count {
4429 break
4430 }
4431 arg_id := tc.call_arg_value(tc.a.child(&node, arg_idx))
4432 actual := tc.resolve_type(arg_id)
4433 tc.infer_generic_type_text_from_type(param_text, actual, generic_params, mut inferred)
4434 }
4435 mut concrete_args := []string{cap: generic_params.len}
4436 for param in generic_params {
4437 arg := inferred[param] or { return info }
4438 concrete_args << arg
4439 }
4440 mut sub_params := []Type{}
4441 for param_text in param_texts {
4442 sub_params << tc.parse_fn_signature_type(info.name, subst_generic_text(param_text,
4443 concrete_args, generic_params))
4444 }
4445 ret_text := tc.fn_ret_type_texts[info.name] or { '' }
4446 sub_ret := if ret_text.len > 0 {
4447 tc.parse_fn_signature_type(info.name, subst_generic_text(ret_text, concrete_args,
4448 generic_params))
4449 } else {
4450 info.return_type
4451 }
4452 return CallInfo{
4453 name: info.name
4454 params: sub_params
4455 return_type: sub_ret
4456 has_receiver: info.has_receiver
4457 is_variadic: info.is_variadic
4458 is_c_variadic: info.is_c_variadic
4459 params_known: true
4460 has_implicit_veb_ctx: info.has_implicit_veb_ctx
4461 }
4462}
4463
4464fn (tc &TypeChecker) parse_fn_signature_type(name string, typ string) Type {
4465 decl_file := tc.fn_type_files[name] or { return tc.parse_type(typ) }
4466 mut scoped := *tc
4467 scoped.cur_file = decl_file
4468 scoped.cur_module = tc.fn_type_modules[name] or {
4469 tc.file_modules[decl_file] or { tc.cur_module }
4470 }
4471 scoped.type_cache = &TypeCache{
4472 parse_enabled: if tc.type_cache != unsafe { nil } {
4473 tc.type_cache.parse_enabled
4474 } else {
4475 false
4476 }
4477 parse_entries: map[string]Type{}
4478 c_entries: map[string]string{}
4479 }
4480 return scoped.parse_type(typ)
4481}
4482
4483fn (mut tc TypeChecker) infer_generic_type_text_from_type(param_text string, actual Type, generic_params []string, mut inferred map[string]string) {
4484 clean := param_text.trim_space()
4485 if clean.len == 0 {
4486 return
4487 }
4488 if clean.starts_with('&') {
4489 if actual is Pointer {
4490 tc.infer_generic_type_text_from_type(clean[1..], actual.base_type, generic_params, mut
4491 inferred)
4492 } else {
4493 tc.infer_generic_type_text_from_type(clean[1..], actual, generic_params, mut inferred)
4494 }
4495 return
4496 }
4497 if clean.starts_with('mut ') {
4498 tc.infer_generic_type_text_from_type(clean[4..], actual, generic_params, mut inferred)
4499 return
4500 }
4501 if clean.starts_with('...') {
4502 if actual is Array {
4503 tc.infer_generic_type_text_from_type(clean[3..], actual.elem_type, generic_params, mut
4504 inferred)
4505 }
4506 return
4507 }
4508 if clean.starts_with('[]') {
4509 if actual is Array {
4510 tc.infer_generic_type_text_from_type(clean[2..], actual.elem_type, generic_params, mut
4511 inferred)
4512 }
4513 return
4514 }
4515 if clean.starts_with('?') {
4516 if actual is OptionType {
4517 tc.infer_generic_type_text_from_type(clean[1..], actual.base_type, generic_params, mut
4518 inferred)
4519 }
4520 return
4521 }
4522 if clean.starts_with('!') {
4523 if actual is ResultType {
4524 tc.infer_generic_type_text_from_type(clean[1..], actual.base_type, generic_params, mut
4525 inferred)
4526 }
4527 return
4528 }
4529 if clean.starts_with('fn(') || clean.starts_with('fn (') {
4530 if actual is FnType {
4531 tc.infer_generic_fn_type_text_from_type(clean, actual, generic_params, mut inferred)
4532 }
4533 return
4534 }
4535 for param in generic_params {
4536 if clean == param && param !in inferred {
4537 inferred[param] = actual.name()
4538 return
4539 }
4540 }
4541}
4542
4543fn (mut tc TypeChecker) infer_generic_fn_type_text_from_type(param_text string, actual FnType, generic_params []string, mut inferred map[string]string) {
4544 params_start := param_text.index_u8(`(`) + 1
4545 mut depth := 1
4546 mut params_end := params_start
4547 for params_end < param_text.len {
4548 if param_text[params_end] == `(` {
4549 depth++
4550 } else if param_text[params_end] == `)` {
4551 depth--
4552 if depth == 0 {
4553 break
4554 }
4555 }
4556 params_end++
4557 }
4558 if params_end >= param_text.len {
4559 return
4560 }
4561 parts := split_params(param_text[params_start..params_end])
4562 for i, part in parts {
4563 if i >= actual.params.len {
4564 break
4565 }
4566 tc.infer_generic_type_text_from_type(normalize_fn_type_param_text(part), fn_param_type(actual,
4567 i), generic_params, mut inferred)
4568 }
4569 ret := param_text[params_end + 1..].trim_space()
4570 if ret.len > 0 {
4571 tc.infer_generic_type_text_from_type(ret, actual.return_type, generic_params, mut inferred)
4572 }
4573}
4574
4575// array_map_return_elem_type supports array map return elem type handling for TypeChecker.
4576fn (mut tc TypeChecker) array_map_return_elem_type(node flat.Node) Type {
4577 if node.children_count < 2 {
4578 return Type(void_)
4579 }
4580 tc.push_array_dsl_scope(node, 'array.map')
4581 arg_id := tc.call_arg_value(tc.a.child(&node, 1))
4582 tc.check_node(arg_id)
4583 elem_type := tc.resolve_type(arg_id)
4584 tc.pop_scope()
4585 if elem_type is Void || elem_type is Unknown {
4586 return Type(void_)
4587 }
4588 return elem_type
4589}
4590
4591fn (tc &TypeChecker) array_contains_elem_type(base_node flat.Node, array_type Array) Type {
4592 if base_node.kind == .selector && base_node.value == 'args' && base_node.children_count > 0 {
4593 parent := tc.a.child_node(&base_node, 0)
4594 if parent.kind == .ident && parent.value == 'os' {
4595 return Type(String{})
4596 }
4597 }
4598 return array_type.elem_type
4599}
4600
4601fn (tc &TypeChecker) is_os_args_contains_call(node flat.Node) bool {
4602 if node.children_count == 0 {
4603 return false
4604 }
4605 fn_node := tc.a.child_node(&node, 0)
4606 if fn_node.kind != .selector || fn_node.value != 'contains' || fn_node.children_count == 0 {
4607 return false
4608 }
4609 base_node := tc.a.child_node(fn_node, 0)
4610 if base_node.kind != .selector || base_node.value != 'args' || base_node.children_count == 0 {
4611 return false
4612 }
4613 parent := tc.a.child_node(base_node, 0)
4614 return parent.kind == .ident && parent.value == 'os'
4615}
4616
4617// min_required_arg_count supports min required arg count handling for TypeChecker.
4618fn (tc &TypeChecker) min_required_arg_count(info CallInfo) int {
4619 if info.is_variadic && info.params.len > 0 {
4620 return info.params.len - 1
4621 }
4622 mut n := info.params.len
4623 for n > 0 {
4624 param := info.params[n - 1]
4625 if tc.is_params_struct_type(param) {
4626 n--
4627 continue
4628 }
4629 break
4630 }
4631 return n
4632}
4633
4634fn c_variadic_fixed_param_count(info CallInfo) int {
4635 if info.is_c_variadic && info.params.len > 0 && info.params[info.params.len - 1] is Array {
4636 return info.params.len - 1
4637 }
4638 return info.params.len
4639}
4640
4641fn (tc &TypeChecker) is_params_struct_type(typ Type) bool {
4642 if typ is Struct {
4643 if typ.name in tc.params_structs {
4644 return true
4645 }
4646 qname := tc.qualify_name(typ.name)
4647 return qname in tc.params_structs
4648 }
4649 if typ is Alias {
4650 return tc.is_params_struct_type(typ.base_type)
4651 }
4652 return false
4653}
4654
4655// call_arg_needs_array_dsl_scope updates call arg needs array dsl scope state for TypeChecker.
4656fn (tc &TypeChecker) call_arg_needs_array_dsl_scope(name string, param_idx int) bool {
4657 return param_idx == 1 && is_array_dsl_call_name(name)
4658}
4659
4660// is_array_dsl_call_name reports whether is array dsl call name applies in types.
4661fn is_array_dsl_call_name(name string) bool {
4662 return name in ['array.filter', 'array.any', 'array.all', 'array.count', 'array.map',
4663 'array.sort', 'array.sorted']
4664}
4665
4666// call_explicit_arg_count updates call explicit arg count state for types.
4667fn call_explicit_arg_count(node flat.Node) int {
4668 if node.children_count <= 1 {
4669 return 0
4670 }
4671 mut n := 0
4672 for i in 1 .. node.children_count {
4673 if int(node.children_start) + i < 0 {
4674 continue
4675 }
4676 n++
4677 }
4678 return n
4679}
4680
4681// push_array_dsl_scope updates push array dsl scope state for TypeChecker.
4682fn (mut tc TypeChecker) push_array_dsl_scope(node flat.Node, name string) {
4683 tc.push_scope()
4684 arr := tc.call_receiver_array_type(node) or { return }
4685 if name in ['array.sort', 'array.sorted'] {
4686 tc.cur_scope.insert('a', arr.elem_type)
4687 tc.cur_scope.insert('b', arr.elem_type)
4688 return
4689 }
4690 tc.cur_scope.insert('it', arr.elem_type)
4691}
4692
4693// call_receiver_array_type updates call receiver array type state for TypeChecker.
4694fn (tc &TypeChecker) call_receiver_array_type(node flat.Node) ?Array {
4695 if node.children_count == 0 {
4696 return none
4697 }
4698 fn_node := tc.a.child_node(&node, 0)
4699 if fn_node.kind != .selector || fn_node.children_count == 0 {
4700 return none
4701 }
4702 base_type := unwrap_pointer(tc.resolve_type(tc.a.child(fn_node, 0)))
4703 if base_type is Array {
4704 return base_type
4705 }
4706 return none
4707}
4708
4709// call_arg_value updates call arg value state for TypeChecker.
4710fn (tc &TypeChecker) call_arg_value(id flat.NodeId) flat.NodeId {
4711 if int(id) < 0 {
4712 return id
4713 }
4714 node := tc.a.nodes[int(id)]
4715 if node.kind == .field_init && node.children_count > 0 {
4716 return tc.a.child(&node, 0)
4717 }
4718 return id
4719}
4720
4721// receiver_compatible supports receiver compatible handling for TypeChecker.
4722fn (tc &TypeChecker) receiver_compatible(actual Type, expected Type) bool {
4723 if tc.type_compatible(actual, expected) {
4724 return true
4725 }
4726 // Check the generic-base relaxation before the pointer fallbacks: it unwraps
4727 // pointers itself, so a bare `&Box{...}` literal still matches an expected
4728 // `&Box[int]` (while `&Box[string]` vs `&Box[int]` stays rejected).
4729 if tc.generic_receiver_base_match(actual, expected) {
4730 return true
4731 }
4732 if expected is Pointer {
4733 return tc.type_compatible(actual, expected.base_type)
4734 }
4735 if actual is Pointer {
4736 return tc.type_compatible(actual.base_type, expected)
4737 }
4738 return false
4739}
4740
4741// generic_receiver_base_match relaxes compatibility between two same-base generic
4742// struct types when at least one side is the open/bare generic form — a bare
4743// `Vec4{...}` literal specializing to the expected `Vec4[f32]`, or a concrete
4744// `Vec4[f32]` value matching the open `Vec4` / `Vec4[T]` method-receiver form.
4745//
4746// It deliberately does NOT relax two *different concrete* instantiations. Because
4747// `receiver_compatible` is also used for ordinary call arguments, field inits,
4748// array literals, and expected-type propagation, conflating them would let
4749// `Box[string]` satisfy an expected `Box[int]` and emit incompatible C structs.
4750fn (tc &TypeChecker) generic_receiver_base_match(actual Type, expected Type) bool {
4751 // Both sides must share pointer shape before unwrapping: a `&Box{...}` value must
4752 // not satisfy an expected `Box[int]` value. The C argument path only
4753 // auto-dereferences when the actual/expected type names match exactly, so the bare
4754 // `Box` vs `Box[int]` mismatch would otherwise be emitted as a pointer where a value
4755 // is required. (A pointer receiver on a value-receiver method is handled by
4756 // receiver_compatible's pointer fallbacks, not here.)
4757 if (actual is Pointer) != (expected is Pointer) {
4758 return false
4759 }
4760 a_full := unwrap_pointer(actual).name()
4761 e_full := unwrap_pointer(expected).name()
4762 a := strip_generic_args_name(a_full)
4763 if a.len == 0 || a != strip_generic_args_name(e_full) {
4764 return false
4765 }
4766 if a !in tc.struct_generic_params {
4767 return false
4768 }
4769 if a_full == e_full {
4770 return false
4771 }
4772 // Reject two *different concrete* instantiations (`Box[string]` vs `Box[int]`),
4773 // which produce incompatible C structs. Relax only when at least one side is
4774 // the open/bare generic form: a bare `Box{...}` literal specializing to the
4775 // expected `Box[int]`, or a concrete `Box[int]` value matching the open
4776 // `Box`/`Box[T]` method-receiver form.
4777 if tc.is_concrete_generic_instance(a_full) && tc.is_concrete_generic_instance(e_full) {
4778 return false
4779 }
4780 return true
4781}
4782
4783// is_concrete_generic_instance reports whether `name` is a fully concrete generic
4784// instantiation (e.g. `Box[int]`), as opposed to the bare base (`Box`) or an open
4785// parameter form (`Box[T]`).
4786fn (tc &TypeChecker) is_concrete_generic_instance(name string) bool {
4787 _, args, ok := generic_type_application_parts(name)
4788 if !ok {
4789 return false
4790 }
4791 return tc.generic_args_are_concrete(args)
4792}
4793
4794// bare_generic_literal_adopts reports whether a struct literal written as the bare
4795// generic base (`Box{...}`, no type args) should adopt the concrete `expected`
4796// instance (`Box[int]`, optionally behind a pointer). The base short-names must match
4797// and the base must be a known generic struct, so a non-generic same-named struct is
4798// left to ordinary checking.
4799fn (tc &TypeChecker) bare_generic_literal_adopts(lit_value string, expected Type) bool {
4800 if lit_value.len == 0 || lit_value.contains('[') {
4801 return false
4802 }
4803 e_base, _, e_ok := generic_type_application_parts(unwrap_pointer(expected).name())
4804 if !e_ok || e_base.all_after_last('.') != lit_value.all_after_last('.') {
4805 return false
4806 }
4807 return e_base in tc.struct_generic_params
4808 || e_base.all_after_last('.') in tc.struct_generic_params
4809}
4810
4811// generic_literal_fields_compatible checks a bare generic struct literal's named
4812// field initializers against the expected concrete instantiation (`Box[int]`),
4813// substituting the struct's type parameters into each field's declared type. It
4814// returns false only on a *definite* mismatch (e.g. `Box{v: 'str'}` for `Box[int]`),
4815// so a clearly-unrelated literal yields a clean checker error instead of adopting the
4816// type and emitting broken C; unresolvable fields stay lenient.
4817fn (mut tc TypeChecker) generic_literal_fields_compatible(node flat.Node, expected Type) bool {
4818 e_base, e_args, e_ok := generic_type_application_parts(unwrap_pointer(expected).name())
4819 if !e_ok {
4820 return true
4821 }
4822 params := tc.struct_generic_params[e_base] or {
4823 tc.struct_generic_params[e_base.all_after_last('.')] or { return true }
4824 }
4825 if params.len != e_args.len {
4826 return true
4827 }
4828 fields := tc.structs[e_base] or { tc.structs[e_base.all_after_last('.')] or { return true } }
4829 for i in 0 .. node.children_count {
4830 fi := tc.a.child_node(&node, i)
4831 if fi.kind != .field_init || fi.children_count == 0 {
4832 continue
4833 }
4834 mut decl_typ := Type(void_)
4835 mut found := false
4836 if fi.value.len > 0 {
4837 // Named initializer (`Box{v: 'x'}`): match by field name.
4838 for f in fields {
4839 if f.name == fi.value {
4840 decl_typ = f.typ
4841 found = true
4842 break
4843 }
4844 }
4845 } else if i < fields.len {
4846 // Positional initializer (`Box{'x'}`): the parser emits a `field_init` with
4847 // an empty name, so match by field order like `check_struct_init` does.
4848 decl_typ = fields[i].typ
4849 found = true
4850 }
4851 if !found {
4852 continue
4853 }
4854 sub := tc.substitute_generic_type(decl_typ, e_args, params)
4855 if sub is Unknown || sub is Void {
4856 continue
4857 }
4858 actual := tc.resolve_expr(tc.a.child(fi, 0), sub)
4859 if !tc.receiver_compatible(actual, sub) {
4860 return false
4861 }
4862 }
4863 return true
4864}
4865
4866// strip_generic_args_name returns the base name of a generic instance type
4867// (`Box[int]` -> `Box`); array/map types (leading `[`) yield the name unchanged.
4868fn strip_generic_args_name(name string) string {
4869 bracket := name.index_u8(`[`)
4870 if bracket <= 0 {
4871 return name
4872 }
4873 return name[..bracket]
4874}
4875
4876// is_zero_literal reports whether is zero literal applies in types.
4877fn (tc &TypeChecker) is_zero_literal(id flat.NodeId) bool {
4878 if int(id) < 0 {
4879 return false
4880 }
4881 node := tc.a.nodes[int(id)]
4882 return node.kind == .int_literal && node.value == '0'
4883}
4884
4885// is_fn_pointer_type reports whether is fn pointer type applies in types.
4886fn is_fn_pointer_type(typ Type) bool {
4887 clean0 := typ
4888 mut clean := clean0
4889 if clean0 is Alias {
4890 clean = clean0.base_type
4891 }
4892 return clean is FnType
4893}
4894
4895// fn_type_from_type converts fn type from type data for types.
4896fn fn_type_from_type(typ Type) ?FnType {
4897 if typ is FnType {
4898 return typ
4899 }
4900 if typ is Alias {
4901 return fn_type_from_type(typ.base_type)
4902 }
4903 return none
4904}
4905
4906fn struct_type_from_type(typ Type) ?Struct {
4907 if typ is Struct {
4908 return typ
4909 }
4910 if typ is Alias {
4911 return struct_type_from_type(typ.base_type)
4912 }
4913 return none
4914}
4915
4916// selector_fn_type supports selector fn type handling for TypeChecker.
4917fn (tc &TypeChecker) selector_fn_type(node flat.Node) ?FnType {
4918 if node.children_count == 0 {
4919 return none
4920 }
4921 if !valid_string_data(node.value) {
4922 return none
4923 }
4924 base_id := tc.a.child(&node, 0)
4925 base_type := tc.selector_fn_base_type(base_id) or { return none }
4926 clean0 := unwrap_pointer(base_type)
4927 mut clean := clean0
4928 if clean0 is Alias {
4929 clean = clean0.base_type
4930 }
4931 if clean is Struct {
4932 if typ := tc.struct_field_type(clean.name, node.value) {
4933 return fn_type_from_type(typ)
4934 }
4935 }
4936 if clean is Interface {
4937 if typ := tc.interface_field_type(clean.name, node.value) {
4938 return fn_type_from_type(typ)
4939 }
4940 }
4941 return none
4942}
4943
4944fn (tc &TypeChecker) method_value_type(receiver_name string, method string) ?Type {
4945 method_name := '${receiver_name}.${method}'
4946 mut ret_type := tc.fn_ret_types[method_name] or { Type(void_) }
4947 mut params := tc.fn_param_types[method_name] or { []Type{} }
4948 if method_name !in tc.fn_ret_types && method_name !in tc.fn_param_types {
4949 // A concrete generic receiver (`Box[int]`) has its methods registered under the
4950 // open key (`Box[T].method`); resolve and substitute so a method *value* on a
4951 // generic struct is typed instead of reported as an unknown field.
4952 ci := tc.resolve_generic_struct_method(receiver_name, method) or { return none }
4953 ret_type = ci.return_type
4954 params = ci.params.clone()
4955 }
4956 mut bound_params := []Type{}
4957 if params.len > 1 {
4958 bound_params = params[1..].clone()
4959 }
4960 return Type(FnType{
4961 params: bound_params
4962 return_type: ret_type
4963 })
4964}
4965
4966// selector_fn_base_type supports selector fn base type handling for TypeChecker.
4967fn (tc &TypeChecker) selector_fn_base_type(base_id flat.NodeId) ?Type {
4968 if typ := tc.cached_expr_type(base_id) {
4969 return typ
4970 }
4971 if int(base_id) < 0 {
4972 return none
4973 }
4974 base_node := tc.a.nodes[int(base_id)]
4975 if base_node.typ.len > 0 && base_node.typ != 'unknown' {
4976 return tc.parse_type(base_node.typ)
4977 }
4978 if base_node.kind == .call {
4979 if typ := tc.resolved_call_type(base_id) {
4980 return typ
4981 }
4982 if typ := tc.direct_call_return_type(base_node) {
4983 return typ
4984 }
4985 return none
4986 }
4987 return tc.resolve_type(base_id)
4988}
4989
4990// direct_call_return_type supports direct call return type handling for TypeChecker.
4991fn (tc &TypeChecker) direct_call_return_type(node flat.Node) ?Type {
4992 if node.children_count == 0 {
4993 return none
4994 }
4995 fn_node := tc.a.child_node(&node, 0)
4996 if fn_node.kind == .ident {
4997 qfn := tc.qualify_fn_name(fn_node.value)
4998 if typ := tc.fn_ret_types[qfn] {
4999 return typ
5000 }
5001 if typ := tc.fn_ret_types[fn_node.value] {
5002 return typ
5003 }
5004 return none
5005 }
5006 if fn_node.kind != .selector || fn_node.children_count == 0 {
5007 return none
5008 }
5009 base_node := tc.a.child_node(fn_node, 0)
5010 if base_node.kind == .ident {
5011 if resolved := tc.resolve_import_alias(base_node.value) {
5012 mod_name := '${resolved}.${fn_node.value}'
5013 if typ := tc.fn_ret_types[mod_name] {
5014 return typ
5015 }
5016 }
5017 qbase := tc.qualify_name(base_node.value)
5018 static_name := '${qbase}.${fn_node.value}'
5019 if static_name in tc.fn_ret_types && (qbase in tc.structs
5020 || qbase in tc.enum_names || qbase in tc.sum_types
5021 || qbase in tc.interface_names) {
5022 return tc.fn_ret_types[static_name] or { none }
5023 }
5024 return none
5025 }
5026 if base_node.kind == .selector {
5027 inner := tc.a.child_node(base_node, 0)
5028 if inner.kind == .ident {
5029 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
5030 full_name := '${mod_name}.${base_node.value}.${fn_node.value}'
5031 if typ := tc.fn_ret_types[full_name] {
5032 return typ
5033 }
5034 }
5035 }
5036 return none
5037}
5038
5039// module_const_receiver_method_name supports module_const_receiver_method_name handling in types.
5040fn (tc &TypeChecker) module_const_receiver_method_name(base_node flat.Node, method string) ?string {
5041 if base_node.kind != .selector || base_node.children_count == 0 || method.len == 0 {
5042 return none
5043 }
5044 inner := tc.a.child_node(&base_node, 0)
5045 if inner.kind != .ident {
5046 return none
5047 }
5048 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
5049 const_name := '${mod_name}.${base_node.value}'
5050 mut const_type := tc.const_types[const_name] or { Type(Unknown{}) }
5051 const_type = tc.const_type_from_initializer(const_name, const_type)
5052 if const_type is Unknown && base_node.value == 'scanner_matcher' {
5053 const_type = Type(Struct{
5054 name: '${mod_name}.KeywordsMatcherTrie'
5055 })
5056 }
5057 clean := unwrap_pointer(const_type)
5058 type_name := resolve_type_name_for_method(clean)
5059 if type_name.len == 0 {
5060 return none
5061 }
5062 method_name := '${type_name}.${method}'
5063 if method_name in tc.fn_ret_types {
5064 return method_name
5065 }
5066 return none
5067}
5068
5069// valid_string_data supports valid string data handling for types.
5070fn valid_string_data(s string) bool {
5071 if s.len == 0 {
5072 return true
5073 }
5074 ptr := unsafe { u64(voidptr(s.str)) }
5075 return ptr >= 4096 && ptr < 281474976710656 && s.len < 1048576
5076}
5077
5078// clone_smartcasts supports clone smartcasts handling for types.
5079fn clone_smartcasts(src map[string]Type) map[string]Type {
5080 mut dst := map[string]Type{}
5081 for key, typ in src {
5082 if valid_string_data(key) {
5083 dst[key] = typ
5084 }
5085 }
5086 return dst
5087}
5088
5089// array_elem_type supports array elem type handling for types.
5090fn array_elem_type(arr Array) Type {
5091 return arr.elem_type
5092}
5093
5094// array_like_elem_type returns the element type of an `Array` or `ArrayFixed`.
5095fn array_like_elem_type(t Type) ?Type {
5096 if t is Array {
5097 return t.elem_type
5098 }
5099 if t is ArrayFixed {
5100 return t.elem_type
5101 }
5102 return none
5103}
5104
5105// if_branch_types_compatible reports whether two if-expression branch types are
5106// compatible. Bare array literals (`[a, b, c]`) resolve to a fixed `T[n]`, but V
5107// treats them as dynamic `[]T`; two *literal* branches with compatible element
5108// types must therefore not be flagged as a mismatch merely because their lengths
5109// differ. The length-agnostic relaxation is limited to literal tails: genuine
5110// fixed-array values keep their length (handled by `type_compatible` above), so
5111// e.g. `[2]int` vs `[3]int` branches still mismatch.
5112fn (tc &TypeChecker) if_branch_types_compatible(a Type, b Type, a_is_array_lit bool, b_is_array_lit bool) bool {
5113 if tc.type_compatible(a, b) || tc.type_compatible(b, a) {
5114 return true
5115 }
5116 if !a_is_array_lit || !b_is_array_lit {
5117 return false
5118 }
5119 a_elem := array_like_elem_type(a) or { return false }
5120 b_elem := array_like_elem_type(b) or { return false }
5121 return tc.type_compatible(a_elem, b_elem) || tc.type_compatible(b_elem, a_elem)
5122}
5123
5124// branch_tail_is_array_literal reports whether a branch's value tail is a bare
5125// array literal (`[a, b, c]`) — directly, or through a const whose initializer is
5126// one. V types such values as dynamic `[]T` regardless of element count, so they
5127// must not constrain if-branch length compatibility. Explicit fixed-array
5128// initializers (`[N]T{...}`, parsed as `.array_init`) are genuine fixed arrays and
5129// keep their length.
5130fn (tc &TypeChecker) branch_tail_is_array_literal(id flat.NodeId) bool {
5131 return tc.expr_is_bare_array_literal(tc.branch_tail_expr_id(id))
5132}
5133
5134// expr_is_bare_array_literal reports whether `id` is a bare `[a, b, c]` literal,
5135// directly or through a single const reference.
5136fn (tc &TypeChecker) expr_is_bare_array_literal(id flat.NodeId) bool {
5137 if !tc.valid_node_id(id) {
5138 return false
5139 }
5140 node := tc.a.nodes[int(id)]
5141 if node.kind == .array_literal {
5142 return true
5143 }
5144 if node.kind == .ident {
5145 for cand in [tc.qualify_name(node.value), node.value] {
5146 if expr_id := tc.const_exprs[cand] {
5147 if tc.valid_node_id(expr_id) {
5148 return tc.a.nodes[int(expr_id)].kind == .array_literal
5149 }
5150 }
5151 }
5152 }
5153 return false
5154}
5155
5156// fixed_array_elem_type supports fixed array elem type handling for types.
5157fn fixed_array_elem_type(arr ArrayFixed) Type {
5158 return arr.elem_type
5159}
5160
5161// map_value_type supports map value type handling for types.
5162fn map_value_type(m Map) Type {
5163 return m.value_type
5164}
5165
5166// pointer_base_type supports pointer base type handling for types.
5167fn pointer_base_type(p Pointer) Type {
5168 return p.base_type
5169}
5170
5171// fn_param_type supports fn param type handling for types.
5172fn fn_param_type(f FnType, idx int) Type {
5173 return f.params[idx]
5174}
5175
5176// is_known_call reports whether is known call applies in types.
5177fn (tc &TypeChecker) is_known_call(node flat.Node) bool {
5178 if node.children_count == 0 {
5179 return true
5180 }
5181 if node.typ.len > 0 {
5182 return true
5183 }
5184 fn_node := tc.a.child_node(&node, 0)
5185 if fn_node.kind == .selector {
5186 base_node := tc.a.child_node(fn_node, 0)
5187 if base_node.kind == .ident {
5188 if base_node.value == 'C' {
5189 return true
5190 }
5191 if resolved_mod := tc.resolve_import_alias(base_node.value) {
5192 mod_name := '${resolved_mod}.${fn_node.value}'
5193 if mod_name in tc.fn_ret_types || mod_name in tc.sum_types || mod_name in tc.structs
5194 || mod_name in tc.enum_names {
5195 return true
5196 }
5197 }
5198 if base_node.value in tc.structs || base_node.value in tc.enum_names {
5199 qname := tc.qualify_name(base_node.value)
5200 if '${qname}.${fn_node.value}' in tc.fn_ret_types {
5201 return true
5202 }
5203 } else {
5204 qname := tc.qualify_name(base_node.value)
5205 if qname in tc.structs || qname in tc.enum_names {
5206 if '${qname}.${fn_node.value}' in tc.fn_ret_types {
5207 return true
5208 }
5209 }
5210 }
5211 } else if base_node.kind == .selector {
5212 inner := tc.a.child_node(base_node, 0)
5213 if inner.kind == .ident {
5214 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
5215 if '${mod_name}.${base_node.value}.${fn_node.value}' in tc.fn_ret_types {
5216 return true
5217 }
5218 }
5219 }
5220 if _ := tc.selector_fn_type(fn_node) {
5221 return true
5222 }
5223 base_type := tc.resolve_type(tc.a.child(fn_node, 0))
5224 clean_type := unwrap_pointer(base_type)
5225 if clean_type is Array || clean_type is ArrayFixed || clean_type is Map {
5226 return true
5227 }
5228 if clean_type is String {
5229 return 'string.${fn_node.value}' in tc.fn_ret_types
5230 }
5231 if clean_type is Alias {
5232 mname := '${clean_type.name}.${fn_node.value}'
5233 if mname in tc.fn_ret_types {
5234 return true
5235 }
5236 base_name := resolve_type_name_for_method(clean_type.base_type)
5237 if base_name.len > 0 {
5238 return '${base_name}.${fn_node.value}' in tc.fn_ret_types
5239 }
5240 }
5241 if clean_type is Struct {
5242 return '${clean_type.name}.${fn_node.value}' in tc.fn_ret_types
5243 }
5244 if clean_type is Interface {
5245 return '${clean_type.name}.${fn_node.value}' in tc.fn_ret_types
5246 }
5247 if clean_type is SumType {
5248 return '${clean_type.name}.${fn_node.value}' in tc.fn_ret_types
5249 }
5250 if clean_type is Enum {
5251 if fn_node.value == 'str' {
5252 return true
5253 }
5254 return '${clean_type.name}.${fn_node.value}' in tc.fn_ret_types
5255 }
5256 if clean_type is Primitive {
5257 mname := '${prim_c_type_from(clean_type.props, clean_type.size)}.${fn_node.value}'
5258 return mname in tc.fn_ret_types
5259 }
5260 return false
5261 }
5262 if fn_node.kind == .ident {
5263 if typ := tc.cur_scope.lookup(fn_node.value) {
5264 return typ is FnType
5265 }
5266 qfn := tc.qualify_fn_name(fn_node.value)
5267 if qfn in tc.fn_ret_types || fn_node.value in tc.fn_ret_types {
5268 return true
5269 }
5270 if _ := tc.resolve_selective_import_symbol(fn_node.value) {
5271 return true
5272 }
5273 }
5274 return false
5275}
5276
5277fn (tc &TypeChecker) call_has_ambiguous_selective_import(node flat.Node) bool {
5278 if node.children_count == 0 {
5279 return false
5280 }
5281 fn_node := tc.a.child_node(&node, 0)
5282 if fn_node.kind == .index && fn_node.children_count > 0 {
5283 base := tc.a.child_node(fn_node, 0)
5284 return base.kind == .ident && tc.selective_import_symbol_is_ambiguous(base.value)
5285 }
5286 return fn_node.kind == .ident && tc.selective_import_symbol_is_ambiguous(fn_node.value)
5287}
5288
5289// call_display_name updates call display name state for TypeChecker.
5290fn (tc &TypeChecker) call_display_name(node flat.Node) string {
5291 if node.children_count == 0 {
5292 return '<missing>'
5293 }
5294 fn_node := tc.a.child_node(&node, 0)
5295 if fn_node.kind == .ident {
5296 return fn_node.value
5297 }
5298 if fn_node.kind == .selector && fn_node.children_count > 0 {
5299 base := tc.a.child_node(fn_node, 0)
5300 if base.value.len > 0 {
5301 return '${base.value}.${fn_node.value}'
5302 }
5303 }
5304 return fn_node.value
5305}
5306
5307// check_if_expr validates check if expr state for types.
5308fn (mut tc TypeChecker) check_if_expr(id flat.NodeId, node flat.Node) {
5309 if node.children_count < 2 {
5310 return
5311 }
5312 cond_id := tc.a.child(&node, 0)
5313 guard_bindings := tc.check_condition(cond_id)
5314 smartcasts := tc.extract_smartcasts(cond_id)
5315 then_id := tc.a.child(&node, 1)
5316 saved_smartcasts := clone_smartcasts(tc.smartcasts)
5317 for sc in smartcasts {
5318 if valid_string_data(sc.name) {
5319 tc.smartcasts[sc.name] = sc.typ
5320 }
5321 }
5322 tc.push_scope()
5323 for binding in guard_bindings {
5324 tc.cur_scope.insert(binding.name, binding.typ)
5325 }
5326 tc.check_node(then_id)
5327 then_type := tc.branch_tail_type(then_id)
5328 tc.pop_scope()
5329 tc.smartcasts = clone_smartcasts(saved_smartcasts)
5330 mut else_type := Type(void_)
5331 if node.children_count > 2 {
5332 else_id := tc.a.child(&node, 2)
5333 tc.check_node(else_id)
5334 else_type = tc.branch_tail_type(else_id)
5335 }
5336 if then_type !is Void && else_type !is Void {
5337 else_id := tc.a.child(&node, 2)
5338 if tc.branch_has_value_tail(then_id) && tc.branch_has_value_tail(else_id)
5339 && !tc.if_branch_types_compatible(then_type, else_type, tc.branch_tail_is_array_literal(then_id), tc.branch_tail_is_array_literal(else_id)) {
5340 if tc.should_diagnose(id) {
5341 tc.record_error(.if_branch_mismatch,
5342 'if-expression branch type mismatch: then `${then_type.name()}` vs else `${else_type.name()}`',
5343 id)
5344 }
5345 }
5346 }
5347}
5348
5349// branch_has_value_tail converts branch has value tail data for types.
5350fn (tc &TypeChecker) branch_has_value_tail(id flat.NodeId) bool {
5351 if !tc.valid_node_id(id) {
5352 return false
5353 }
5354 node := tc.a.nodes[int(id)]
5355 if node.kind == .block {
5356 if node.children_count == 0 {
5357 return false
5358 }
5359 last_id := tc.a.child(&node, node.children_count - 1)
5360 if !tc.valid_node_id(last_id) {
5361 return false
5362 }
5363 last := tc.a.nodes[int(last_id)]
5364 return last.kind == .expr_stmt
5365 }
5366 if node.kind == .match_branch {
5367 body_start := if node.value == 'else' { 0 } else { node.value.int() }
5368 if node.children_count <= body_start {
5369 return false
5370 }
5371 last_id := tc.a.child(&node, node.children_count - 1)
5372 if !tc.valid_node_id(last_id) {
5373 return false
5374 }
5375 last := tc.a.nodes[int(last_id)]
5376 return last.kind == .expr_stmt
5377 }
5378 return node.kind !in [.assign, .decl_assign, .selector_assign, .index_assign, .return_stmt,
5379 .block]
5380}
5381
5382// check_condition validates check condition state for types.
5383fn (mut tc TypeChecker) check_condition(cond_id flat.NodeId) []LocalBinding {
5384 if int(cond_id) < 0 {
5385 return []LocalBinding{}
5386 }
5387 cond := tc.a.nodes[int(cond_id)]
5388 if cond.kind == .decl_assign {
5389 return tc.check_if_guard(cond_id, cond)
5390 }
5391 if cond.kind == .infix && cond.op == .logical_and && cond.children_count >= 2 {
5392 lhs_id := tc.a.child(&cond, 0)
5393 rhs_id := tc.a.child(&cond, 1)
5394 tc.check_bool_condition(lhs_id)
5395 saved_smartcasts := clone_smartcasts(tc.smartcasts)
5396 for sc in tc.extract_smartcasts(lhs_id) {
5397 if valid_string_data(sc.name) {
5398 tc.smartcasts[sc.name] = sc.typ
5399 }
5400 }
5401 rhs_bindings := tc.check_condition(rhs_id)
5402 tc.smartcasts = clone_smartcasts(saved_smartcasts)
5403 return rhs_bindings
5404 }
5405 tc.check_bool_condition(cond_id)
5406 return []LocalBinding{}
5407}
5408
5409// check_bool_condition validates check bool condition state for types.
5410fn (mut tc TypeChecker) check_bool_condition(cond_id flat.NodeId) {
5411 tc.check_node(cond_id)
5412 cond_type := tc.resolve_type(cond_id)
5413 if !tc.type_compatible(cond_type, Type(bool_)) && tc.should_diagnose(cond_id) {
5414 tc.record_error(.condition_mismatch,
5415 'if condition must be `bool`, not `${cond_type.name()}`', cond_id)
5416 }
5417}
5418
5419// check_if_guard validates check if guard state for types.
5420fn (mut tc TypeChecker) check_if_guard(id flat.NodeId, node flat.Node) []LocalBinding {
5421 if node.children_count < 2 {
5422 return []LocalBinding{}
5423 }
5424 lhs_id := tc.a.child(&node, 0)
5425 rhs_id := tc.a.child(&node, 1)
5426 tc.check_node(rhs_id)
5427 rhs_type := tc.resolve_type(rhs_id)
5428 mut payload := Type(void_)
5429 if rhs_type is OptionType {
5430 payload = rhs_type.base_type
5431 } else if rhs_type is ResultType {
5432 payload = rhs_type.base_type
5433 } else {
5434 rhs := tc.a.nodes[int(rhs_id)]
5435 if rhs.kind == .index && rhs.children_count > 0 {
5436 base_type := unwrap_pointer(tc.resolve_type(tc.a.child(&rhs, 0)))
5437 if base_type is Map {
5438 payload = base_type.value_type
5439 }
5440 }
5441 }
5442 if payload is Void {
5443 if tc.should_diagnose(id) {
5444 tc.record_error(.condition_mismatch,
5445 'if guard expression must be optional or result, not `${rhs_type.name()}`', id)
5446 }
5447 }
5448 lhs := tc.a.nodes[int(lhs_id)]
5449 if lhs.kind == .ident && lhs.value.len > 0 && payload !is Void {
5450 mut result := []LocalBinding{}
5451 result << LocalBinding{
5452 name: lhs.value
5453 typ: payload
5454 }
5455 return result
5456 }
5457 return []LocalBinding{}
5458}
5459
5460// check_match_stmt validates check match stmt state for types.
5461fn (mut tc TypeChecker) check_match_stmt(_id flat.NodeId, node flat.Node) {
5462 if node.children_count == 0 {
5463 return
5464 }
5465 subject_id := tc.a.child(&node, 0)
5466 tc.check_node(subject_id)
5467 subject_key := tc.expr_key(subject_id)
5468 subject_type := unwrap_pointer(tc.resolve_type(subject_id))
5469 for i in 1 .. node.children_count {
5470 branch_id := tc.a.child(&node, i)
5471 branch := tc.a.child_node(&node, i)
5472 if branch.kind != .match_branch {
5473 tc.check_node(branch_id)
5474 continue
5475 }
5476 n_conds := if branch.value == 'else' { 0 } else { branch.value.int() }
5477 if subject_type is SumType {
5478 for j in 0 .. n_conds {
5479 cond := tc.a.node(tc.a.child(branch, j))
5480 if pattern := tc.match_type_pattern(cond) {
5481 if !tc.sum_has_variant(subject_type.name, pattern) {
5482 tc.record_error(.condition_mismatch,
5483 '`${pattern}` is not a variant of sum type `${subject_type.name}`', tc.a.child(branch,
5484 j))
5485 }
5486 }
5487 }
5488 }
5489 saved_smartcasts := clone_smartcasts(tc.smartcasts)
5490 if subject_key.len > 0 && valid_string_data(subject_key) && n_conds == 1
5491 && branch.children_count > 0 {
5492 cond_id := tc.a.child(branch, 0)
5493 cond := tc.a.node(cond_id)
5494 if pattern := tc.match_type_pattern(cond) {
5495 tc.smartcasts[subject_key] = tc.parse_type(pattern)
5496 }
5497 }
5498 tc.push_scope()
5499 for j in n_conds .. branch.children_count {
5500 tc.check_node(tc.a.child(branch, j))
5501 }
5502 tc.pop_scope()
5503 tc.smartcasts = clone_smartcasts(saved_smartcasts)
5504 }
5505}
5506
5507// check_is_expr validates check is expr state for types.
5508fn (mut tc TypeChecker) check_is_expr(id flat.NodeId, node flat.Node) {
5509 if node.children_count == 0 {
5510 return
5511 }
5512 expr_id := tc.a.child(&node, 0)
5513 tc.check_node(expr_id)
5514 expr_type := unwrap_pointer(tc.resolve_type(expr_id))
5515 if expr_type is SumType {
5516 if node.value.len > 0 && !tc.sum_has_variant(expr_type.name, node.value)
5517 && tc.should_diagnose(id) {
5518 tc.record_error(.condition_mismatch,
5519 '`${node.value}` is not a variant of sum type `${expr_type.name}`', id)
5520 }
5521 return
5522 }
5523 if expr_type is Interface || expr_type is Unknown {
5524 return
5525 }
5526 if tc.should_diagnose(id) {
5527 tc.record_error(.condition_mismatch,
5528 '`is` can only be used with sum type or interface values, not `${expr_type.name()}`',
5529 id)
5530 }
5531}
5532
5533// branch_tail_type supports branch tail type handling for TypeChecker.
5534fn (tc &TypeChecker) branch_tail_type(id flat.NodeId) Type {
5535 if !tc.valid_node_id(id) {
5536 return Type(void_)
5537 }
5538 node := tc.a.nodes[int(id)]
5539 if node.kind == .if_expr {
5540 return tc.if_expr_tail_type(id)
5541 }
5542 if node.kind == .block {
5543 if node.children_count == 0 {
5544 return Type(void_)
5545 }
5546 last_id := tc.a.child(&node, node.children_count - 1)
5547 if !tc.valid_node_id(last_id) {
5548 return Type(void_)
5549 }
5550 last := tc.a.nodes[int(last_id)]
5551 if last.kind == .expr_stmt && last.children_count > 0 {
5552 return tc.resolve_type(tc.a.child(&last, 0))
5553 }
5554 return tc.resolve_type(last_id)
5555 }
5556 if node.kind == .match_branch {
5557 body_start := if node.value == 'else' { 0 } else { node.value.int() }
5558 if node.children_count <= body_start {
5559 return Type(void_)
5560 }
5561 last_id := tc.a.child(&node, node.children_count - 1)
5562 if !tc.valid_node_id(last_id) {
5563 return Type(void_)
5564 }
5565 last := tc.a.nodes[int(last_id)]
5566 if last.kind == .expr_stmt && last.children_count > 0 {
5567 return tc.resolve_type(tc.a.child(&last, 0))
5568 }
5569 return tc.resolve_type(last_id)
5570 }
5571 return tc.resolve_type(id)
5572}
5573
5574// if_expr_tail_type supports if expr tail type handling for TypeChecker.
5575fn (tc &TypeChecker) if_expr_tail_type(id flat.NodeId) Type {
5576 mut cur_id := id
5577 mut result := Type(void_)
5578 for tc.valid_node_id(cur_id) {
5579 node := tc.a.nodes[int(cur_id)]
5580 if node.kind != .if_expr {
5581 return choose_if_tail_type(result, tc.branch_tail_type(cur_id))
5582 }
5583 if node.children_count > 1 {
5584 then_type := tc.branch_tail_type(tc.a.child(&node, 1))
5585 result = choose_if_tail_type(result, then_type)
5586 }
5587 if node.children_count <= 2 {
5588 return result
5589 }
5590 else_id := tc.a.child(&node, 2)
5591 if !tc.valid_node_id(else_id) {
5592 return result
5593 }
5594 else_node := tc.a.nodes[int(else_id)]
5595 if else_node.kind == .if_expr {
5596 cur_id = else_id
5597 continue
5598 }
5599 else_type := tc.branch_tail_type(else_id)
5600 return choose_if_tail_type(result, else_type)
5601 }
5602 return result
5603}
5604
5605// choose_if_tail_type supports choose if tail type handling for types.
5606fn choose_if_tail_type(current Type, next Type) Type {
5607 if current is Void {
5608 return next
5609 }
5610 if next is Void {
5611 return current
5612 }
5613 if current !is Primitive {
5614 return current
5615 }
5616 if next !is Primitive {
5617 return next
5618 }
5619 return current
5620}
5621
5622// branch_tail_expr_id returns the value-producing tail expression of a branch
5623// body (a `block` or `match_branch`), unwrapping a trailing `expr_stmt`. Returns
5624// `empty_node` when the branch has no expression tail.
5625fn (tc &TypeChecker) branch_tail_expr_id(id flat.NodeId) flat.NodeId {
5626 if !tc.valid_node_id(id) {
5627 return flat.empty_node
5628 }
5629 node := tc.a.nodes[int(id)]
5630 mut last_id := flat.empty_node
5631 if node.kind == .block {
5632 if node.children_count == 0 {
5633 return flat.empty_node
5634 }
5635 last_id = tc.a.child(&node, node.children_count - 1)
5636 } else if node.kind == .match_branch {
5637 body_start := if node.value == 'else' { 0 } else { node.value.int() }
5638 if node.children_count <= body_start {
5639 return flat.empty_node
5640 }
5641 last_id = tc.a.child(&node, node.children_count - 1)
5642 } else {
5643 return id
5644 }
5645 if !tc.valid_node_id(last_id) {
5646 return flat.empty_node
5647 }
5648 last := tc.a.nodes[int(last_id)]
5649 if last.kind == .expr_stmt {
5650 if last.children_count > 0 {
5651 return tc.a.child(&last, 0)
5652 }
5653 return flat.empty_node
5654 }
5655 return last_id
5656}
5657
5658// branches_compatible_with propagates `expected` into every value-producing tail
5659// of a match/if expression (so context-dependent tails such as enum shorthand
5660// `.foo`, `none`, or fn literals type against it instead of defaulting to e.g.
5661// `int`). Returns true when the node is a match/if expression and every branch
5662// tail is compatible with `expected`.
5663fn (mut tc TypeChecker) branches_compatible_with(id flat.NodeId, expected Type) bool {
5664 if !tc.valid_node_id(id) {
5665 return false
5666 }
5667 node := tc.a.nodes[int(id)]
5668 if node.kind == .match_stmt {
5669 mut saw_branch := false
5670 for i in 1 .. node.children_count {
5671 branch_id := tc.a.child(&node, i)
5672 if !tc.valid_node_id(branch_id) {
5673 continue
5674 }
5675 if tc.a.nodes[int(branch_id)].kind != .match_branch {
5676 continue
5677 }
5678 tail := tc.branch_tail_expr_id(branch_id)
5679 if !tc.valid_node_id(tail) {
5680 return false
5681 }
5682 saw_branch = true
5683 actual := tc.resolve_expr(tail, expected)
5684 if !tc.receiver_compatible(actual, expected) {
5685 return false
5686 }
5687 }
5688 return saw_branch
5689 }
5690 if node.kind == .if_expr {
5691 // A value if-expression needs an else branch (child 2). children: cond,
5692 // then-block, else (block or nested if_expr).
5693 if node.children_count <= 2 {
5694 return false
5695 }
5696 then_tail := tc.branch_tail_expr_id(tc.a.child(&node, 1))
5697 if !tc.valid_node_id(then_tail) {
5698 return false
5699 }
5700 then_actual := tc.resolve_expr(then_tail, expected)
5701 if !tc.receiver_compatible(then_actual, expected) {
5702 return false
5703 }
5704 else_id := tc.a.child(&node, 2)
5705 if !tc.valid_node_id(else_id) {
5706 return false
5707 }
5708 if tc.a.nodes[int(else_id)].kind == .if_expr {
5709 return tc.branches_compatible_with(else_id, expected)
5710 }
5711 else_tail := tc.branch_tail_expr_id(else_id)
5712 if !tc.valid_node_id(else_tail) {
5713 return false
5714 }
5715 else_actual := tc.resolve_expr(else_tail, expected)
5716 return tc.receiver_compatible(else_actual, expected)
5717 }
5718 return false
5719}
5720
5721// extract_smartcasts supports extract smartcasts handling for TypeChecker.
5722fn (tc &TypeChecker) extract_smartcasts(cond_id flat.NodeId) []LocalBinding {
5723 if int(cond_id) < 0 {
5724 return []LocalBinding{}
5725 }
5726 cond := tc.a.nodes[int(cond_id)]
5727 if cond.kind == .is_expr && cond.children_count > 0 {
5728 expr_id := tc.a.child(&cond, 0)
5729 key := tc.expr_key(expr_id)
5730 if key.len > 0 && valid_string_data(key) && cond.value.len > 0 {
5731 mut result := []LocalBinding{}
5732 result << LocalBinding{
5733 name: key
5734 typ: tc.parse_type(cond.value)
5735 }
5736 return result
5737 }
5738 }
5739 if cond.kind == .infix && cond.op == .logical_and && cond.children_count >= 2 {
5740 mut result := tc.extract_smartcasts(tc.a.child(&cond, 0))
5741 result << tc.extract_smartcasts(tc.a.child(&cond, 1))
5742 return result
5743 }
5744 return []LocalBinding{}
5745}
5746
5747// check_struct_init validates check struct init state for types.
5748fn (mut tc TypeChecker) check_struct_init(id flat.NodeId, node flat.Node) {
5749 init_type := tc.parse_type(node.value)
5750 if init_type is Struct {
5751 fields := tc.structs[init_type.name] or { []StructField{} }
5752 for i in 0 .. node.children_count {
5753 field_id := tc.a.child(&node, i)
5754 field := tc.a.nodes[int(field_id)]
5755 if field.kind != .field_init || field.children_count == 0 {
5756 tc.check_node(field_id)
5757 continue
5758 }
5759 value_id := tc.a.child(&field, 0)
5760 // A method value stored in a struct field escapes the evaluation site (several
5761 // instances from the same `Foo{cb: obj.method}` site would share one receiver).
5762 tc.reject_stored_method_value(value_id)
5763 mut expected := Type(void_)
5764 if field.value.len > 0 {
5765 mut found := false
5766 if typ := tc.struct_field_type(init_type.name, field.value) {
5767 expected = typ
5768 found = true
5769 }
5770 if !found && tc.should_diagnose(field_id) && fields.len > 0 {
5771 tc.record_error(.unknown_field,
5772 'unknown field `${field.value}` in `${init_type.name}`', field_id)
5773 }
5774 } else if i < fields.len {
5775 expected = fields[i].typ
5776 }
5777 tc.check_node(value_id)
5778 if expected !is Void {
5779 actual := tc.resolve_expr(value_id, expected)
5780 if !tc.type_compatible(actual, expected) {
5781 tc.type_mismatch(.assignment_mismatch,
5782 'cannot initialize field `${field.value}` with `${actual.name()}`; expected `${expected.name()}`',
5783 field_id)
5784 }
5785 }
5786 }
5787 return
5788 }
5789 for i in 0 .. node.children_count {
5790 tc.check_node(tc.a.child(&node, i))
5791 }
5792 _ = id
5793}
5794
5795// expr_is_method_value reports whether `id` is a selector that resolves to a *method
5796// value* — a struct method used as a value (`obj.draw`), not a field access or a method
5797// call. cgen backs such a value with a per-evaluation-site static receiver slot, which is
5798// only safe for an immediately-consumed callback; it cannot hold several live instances
5799// from the same site at once (see gen_method_value_closure).
5800fn (tc &TypeChecker) expr_is_method_value(id flat.NodeId) bool {
5801 if int(id) < 0 {
5802 return false
5803 }
5804 node := tc.a.nodes[int(id)]
5805 // A local bound to a method value (`cb := c.report`) carries the same escape hazard as the
5806 // bare selector, so a reference to it counts as a method value here too.
5807 if node.kind == .ident {
5808 return node.value in tc.method_value_locals
5809 }
5810 if node.kind != .selector || node.children_count == 0 {
5811 return false
5812 }
5813 clean := unwrap_pointer(tc.resolve_type(tc.a.child(&node, 0)))
5814 if clean !is Struct {
5815 return false
5816 }
5817 sname := (clean as Struct).name
5818 if tc.struct_field_type(sname, node.value) != none {
5819 return false
5820 }
5821 if '${sname}.${node.value}' in tc.fn_param_types {
5822 return true
5823 }
5824 if _ := tc.resolve_generic_struct_method(sname, node.value) {
5825 return true
5826 }
5827 return false
5828}
5829
5830// reject_stored_method_value reports a clear error when a method value escapes its
5831// evaluation site — stored in an array/map/struct field or returned. The per-site static
5832// receiver slot cannot keep several live instances distinct, so a factory like
5833// `fn bind(c Counter) fn () int { return c.report }` (or storage in a loop) would make
5834// every escaped callback use the last receiver; without this the value also reaches cgen
5835// and emits C referencing an unsupported helper. Pass method values directly as a
5836// callback argument instead (a real closure capture is not yet supported).
5837fn (mut tc TypeChecker) reject_stored_method_value(id flat.NodeId) {
5838 if tc.expr_is_method_value(id) && tc.should_diagnose(id) {
5839 tc.record_error(.assignment_mismatch,
5840 'a method value (`obj.method`) cannot escape its call site (no closure capture); pass it directly as a callback argument',
5841 id)
5842 }
5843}
5844
5845// check_selector validates check selector state for types.
5846fn (mut tc TypeChecker) check_selector(id flat.NodeId, node flat.Node) {
5847 if node.children_count == 0 {
5848 return
5849 }
5850 base_id := tc.a.child(&node, 0)
5851 base := tc.a.nodes[int(base_id)]
5852 if tc.is_namespace_selector(node, base) {
5853 if typ := tc.enum_selector_type(&node) {
5854 tc.register_synth_type(id, typ)
5855 }
5856 return
5857 }
5858 tc.check_node(base_id)
5859 base_type := tc.resolve_type(base_id)
5860 tc.register_synth_type(base_id, base_type)
5861 // A value-context selector whose name is a method (not a field) of a struct
5862 // receiver is a method value; record the concrete `Type.method` so it survives
5863 // dead-code elimination (cgen emits a wrapper that calls it).
5864 clean_recv := unwrap_pointer(base_type)
5865 if clean_recv is Struct {
5866 if tc.struct_field_type(clean_recv.name, node.value) == none {
5867 mut mkey := '${clean_recv.name}.${node.value}'
5868 if mkey !in tc.fn_param_types {
5869 // Generic receiver methods are registered under the open key
5870 // (`Box[T].method`); mark that one reachable for the wrapper, and stash
5871 // the substituted signature for cgen (the open form is gone by cgen time).
5872 if ci := tc.resolve_generic_struct_method(clean_recv.name, node.value) {
5873 tc.generic_method_value_info['${clean_recv.name}.${node.value}'] = ci
5874 mkey = ci.name
5875 }
5876 }
5877 if mkey in tc.fn_param_types && tc.cur_fn_node_id >= 0 {
5878 // Record per enclosing function so markused marks it only when that
5879 // function is reachable; over-marking an unreachable method value can pull
5880 // in (and fail to compile) an otherwise-unused specialization. A method
5881 // value can only appear inside a function body — escaping to a const/global
5882 // is rejected elsewhere — so a non-fn context needs no recording here.
5883 tc.method_values_by_fn[tc.cur_fn_node_id] << mkey
5884 // Also record the concrete instance key (`Box[int].report`) — distinct from the
5885 // open key (`Box[T].report`) above — so monomorphize can gate a generic method's
5886 // specialization on *this* instance's method value being reachable (it shares the
5887 // open key with every other instance, e.g. `Box[Pair]`).
5888 concrete_mkey := '${clean_recv.name}.${node.value}'
5889 if concrete_mkey != mkey {
5890 tc.method_values_by_fn[tc.cur_fn_node_id] << concrete_mkey
5891 }
5892 }
5893 }
5894 }
5895 if typ := tc.selector_type(id, node) {
5896 tc.register_synth_type(id, typ)
5897 return
5898 } else {
5899 if tc.should_diagnose(id) {
5900 if base_type is Unknown {
5901 return
5902 }
5903 tc.record_error(.unknown_field,
5904 'unknown field `${node.value}` on `${base_type.name()}`', id)
5905 }
5906 }
5907}
5908
5909// is_namespace_selector reports whether is namespace selector applies in types.
5910fn (tc &TypeChecker) is_namespace_selector(node flat.Node, base flat.Node) bool {
5911 if base.kind != .ident {
5912 return false
5913 }
5914 if base.value == 'C' || tc.has_active_import(base.value) {
5915 return true
5916 }
5917 if resolved := tc.resolve_selective_import_type_symbol(base.value) {
5918 if resolved in tc.structs || resolved in tc.enum_names || resolved in tc.flag_enums
5919 || resolved in tc.sum_types || resolved in tc.interface_names {
5920 return true
5921 }
5922 }
5923 qbase := tc.qualify_name(base.value)
5924 if qbase in tc.structs || qbase in tc.enum_names || qbase in tc.sum_types
5925 || qbase in tc.interface_names {
5926 return true
5927 }
5928 qname := '${qbase}.${node.value}'
5929 return qname in tc.const_types || qname in tc.fn_ret_types || qname in tc.enum_names
5930}
5931
5932// selector_type supports selector type handling for TypeChecker.
5933fn (tc &TypeChecker) selector_type(_id flat.NodeId, node flat.Node) ?Type {
5934 if node.children_count == 0 {
5935 return none
5936 }
5937 if typ := tc.enum_selector_type(&node) {
5938 return typ
5939 }
5940 base_id := tc.a.child(&node, 0)
5941 base_type := tc.resolve_type(base_id)
5942 clean0 := unwrap_pointer(base_type)
5943 mut clean := clean0
5944 if clean0 is Alias {
5945 clean = clean0.base_type
5946 }
5947 clean_name := clean.name()
5948 if typ := option_result_selector_type(clean, node.value) {
5949 return typ
5950 }
5951 if node.value == 'len' {
5952 if clean is Array || clean is Map || clean is String || clean is ArrayFixed {
5953 return Type(int_)
5954 }
5955 }
5956 if clean is Channel && node.value == 'closed' {
5957 return Type(bool_)
5958 }
5959 if clean is Struct {
5960 if typ := tc.struct_field_type(clean_name, node.value) {
5961 return typ
5962 }
5963 if typ := tc.method_value_type(clean_name, node.value) {
5964 return typ
5965 }
5966 }
5967 if clean is Interface {
5968 if typ := tc.interface_field_type(clean.name, node.value) {
5969 return typ
5970 }
5971 }
5972 if clean is MultiReturn {
5973 if typ := multi_return_selector_type(clean, node.value) {
5974 return typ
5975 }
5976 }
5977 if clean_name == 'IError' || clean_name.ends_with('.IError') {
5978 if node.value == 'message' {
5979 return Type(String{})
5980 }
5981 if node.value == '_object' {
5982 return tc.parse_type('voidptr')
5983 }
5984 }
5985 if clean is SumType {
5986 if typ := tc.lowered_sum_selector_type(clean, node.value) {
5987 return typ
5988 }
5989 if typ := tc.sum_shared_field_type(clean, node.value) {
5990 return typ
5991 }
5992 }
5993 if clean is Array || clean is Map || clean is String {
5994 sname := if clean is Array {
5995 'array'
5996 } else if clean is Map {
5997 'map'
5998 } else {
5999 'string'
6000 }
6001 if fields := tc.structs[sname] {
6002 for f in fields {
6003 if f.name == node.value {
6004 return f.typ
6005 }
6006 }
6007 }
6008 }
6009 return none
6010}
6011
6012// multi_return_selector_type supports multi return selector type handling for types.
6013fn multi_return_selector_type(typ MultiReturn, field string) ?Type {
6014 if !field.starts_with('arg') || field.len <= 3 {
6015 return none
6016 }
6017 idx_str := field[3..]
6018 idx := idx_str.int()
6019 if idx_str != idx.str() || idx < 0 || idx >= typ.types.len {
6020 return none
6021 }
6022 return typ.types[idx]
6023}
6024
6025// lowered_sum_selector_type supports lowered sum selector type handling for TypeChecker.
6026fn (tc &TypeChecker) lowered_sum_selector_type(sum SumType, field string) ?Type {
6027 if field == 'typ' {
6028 return Type(int_)
6029 }
6030 variants := tc.sum_types[sum.name] or { return none }
6031 for variant in variants {
6032 short := if variant.contains('.') { variant.all_after_last('.') } else { variant }
6033 if field == variant || field == short || field == c_name(variant) {
6034 return tc.parse_type(variant)
6035 }
6036 }
6037 return none
6038}
6039
6040// sum_shared_field_type supports sum shared field type handling for TypeChecker.
6041fn (tc &TypeChecker) sum_shared_field_type(sum SumType, field string) ?Type {
6042 variants := tc.sum_types[sum.name] or { return none }
6043 if variants.len == 0 {
6044 return none
6045 }
6046 mut has_common := false
6047 mut common_typ := Type(void_)
6048 for variant in variants {
6049 variant_field := tc.struct_field_type(variant, field) or { return none }
6050 if !has_common {
6051 common_typ = variant_field
6052 has_common = true
6053 continue
6054 }
6055 if !tc.type_compatible(variant_field, common_typ)
6056 || !tc.type_compatible(common_typ, variant_field) {
6057 return none
6058 }
6059 }
6060 return common_typ
6061}
6062
6063// option_result_selector_type supports option result selector type handling for types.
6064fn option_result_selector_type(typ Type, field string) ?Type {
6065 if typ is OptionType {
6066 if field == 'ok' {
6067 return Type(bool_)
6068 }
6069 if field == 'value' {
6070 return typ.base_type
6071 }
6072 }
6073 if typ is ResultType {
6074 if field == 'ok' {
6075 return Type(bool_)
6076 }
6077 if field == 'value' {
6078 return typ.base_type
6079 }
6080 }
6081 return none
6082}
6083
6084// check_index validates check index state for types.
6085fn (mut tc TypeChecker) check_index(id flat.NodeId, node flat.Node) {
6086 if node.children_count == 0 {
6087 return
6088 }
6089 base_id := tc.a.child(&node, 0)
6090 tc.check_node(base_id)
6091 base_type_raw := tc.resolve_type(base_id)
6092 base_type0 := unwrap_pointer(base_type_raw)
6093 mut base_type := base_type0
6094 if base_type0 is Alias {
6095 base_type = base_type0.base_type
6096 }
6097 if node.value == 'range' {
6098 if !(base_type is Array || base_type is ArrayFixed || base_type is String)
6099 && tc.should_diagnose(id) {
6100 tc.record_error(.cannot_index, 'cannot slice `${base_type.name()}`', id)
6101 }
6102 for i in 1 .. node.children_count {
6103 bound_id := tc.a.child(&node, i)
6104 if int(bound_id) >= 0 {
6105 bound := tc.a.nodes[int(bound_id)]
6106 if bound.kind == .empty {
6107 continue
6108 }
6109 tc.check_node(bound_id)
6110 bound_type := tc.resolve_type(bound_id)
6111 if bound_type !is Unknown && !bound_type.is_integer() {
6112 tc.type_mismatch(.cannot_index,
6113 'slice bound must be integer, not `${bound_type.name()}`', bound_id)
6114 }
6115 }
6116 }
6117 tc.register_synth_type(id, tc.resolve_index_type(node))
6118 return
6119 }
6120 if node.children_count > 2 {
6121 for i in 2 .. node.children_count {
6122 tc.check_node(tc.a.child(&node, i))
6123 }
6124 tc.record_error(.cannot_index,
6125 'index expression accepts one index, got ${node.children_count - 1}', id)
6126 tc.register_synth_type(id, tc.resolve_index_type(node))
6127 return
6128 }
6129 if node.children_count > 1 {
6130 index_id := tc.a.child(&node, 1)
6131 tc.check_node(index_id)
6132 if base_type is Map {
6133 actual_key := tc.resolve_expr(index_id, base_type.key_type)
6134 if !tc.type_compatible(actual_key, base_type.key_type) {
6135 tc.type_mismatch(.cannot_index,
6136 'map key must be `${base_type.key_type.name()}`, not `${actual_key.name()}`',
6137 index_id)
6138 }
6139 tc.register_synth_type(id, base_type.value_type)
6140 return
6141 }
6142 index_type := tc.resolve_type(index_id)
6143 if index_type !is Unknown && !index_type.is_integer() {
6144 tc.type_mismatch(.cannot_index, 'index must be integer, not `${index_type.name()}`',
6145 index_id)
6146 }
6147 }
6148 if !(base_type is Array || base_type is ArrayFixed || base_type is String
6149 || base_type is Map || base_type is Unknown || base_type_raw is Pointer)
6150 && tc.should_diagnose(id) {
6151 tc.record_error(.cannot_index, 'cannot index `${base_type.name()}`', id)
6152 }
6153 tc.register_synth_type(id, tc.resolve_index_type(node))
6154}
6155
6156// check_ident validates check ident state for types.
6157fn (mut tc TypeChecker) check_ident(id flat.NodeId, node flat.Node) {
6158 if node.value.len == 0 || node.value == '_' {
6159 return
6160 }
6161 if is_bare_generic_param(node.value) {
6162 tc.register_synth_type(id, unknown_type('generic placeholder `${node.value}`'))
6163 return
6164 }
6165 if typ := tc.cur_scope.lookup(node.value) {
6166 tc.register_synth_type(id, typ)
6167 return
6168 }
6169 if typ := tc.file_scope.lookup(node.value) {
6170 tc.register_synth_type(id, typ)
6171 return
6172 }
6173 if node.value == 'err' {
6174 tc.register_synth_type(id, tc.parse_type('IError'))
6175 return
6176 }
6177 qname := tc.qualify_name(node.value)
6178 if typ := tc.const_types[qname] {
6179 tc.register_synth_type(id, typ)
6180 return
6181 }
6182 if typ := tc.const_types[node.value] {
6183 tc.register_synth_type(id, typ)
6184 return
6185 }
6186 if typ := tc.fn_value_type(node.value) {
6187 tc.register_synth_type(id, typ)
6188 return
6189 }
6190 if tc.selective_import_symbol_is_ambiguous(node.value) {
6191 tc.register_synth_type(id, unknown_type('ambiguous selective import `${node.value}`'))
6192 return
6193 }
6194 if node.value in tc.fn_ret_types || tc.qualify_fn_name(node.value) in tc.fn_ret_types {
6195 return
6196 }
6197 if tc.has_active_import(node.value) || qname in tc.structs || qname in tc.enum_names
6198 || qname in tc.sum_types || qname in tc.interface_names {
6199 return
6200 }
6201 if tc.should_diagnose(id) {
6202 tc.record_error(.unknown_ident, 'unknown identifier `${node.value}`', id)
6203 }
6204}
6205
6206// resolve_expr resolves resolve expr information for types.
6207fn (mut tc TypeChecker) resolve_expr(id flat.NodeId, expected Type) Type {
6208 if int(id) < 0 {
6209 return unknown_type('missing expression')
6210 }
6211 expected_raw := expected
6212 node := tc.a.nodes[int(id)]
6213 if node.kind == .field_init && node.children_count > 0 {
6214 return tc.resolve_expr(tc.a.child(&node, 0), expected)
6215 }
6216 if node.kind == .none_expr {
6217 if expected is OptionType || expected is ResultType || is_ierror_type(expected) {
6218 tc.register_synth_type(id, expected)
6219 return expected
6220 }
6221 }
6222 if node.kind == .enum_val && expected is Enum {
6223 if tc.enum_value_matches(node.value, expected.name) {
6224 tc.register_synth_type(id, expected_raw)
6225 return expected_raw
6226 }
6227 tc.type_mismatch(.assignment_mismatch,
6228 'unknown enum field `${node.value}` for `${expected.name}`', id)
6229 return Type(int_)
6230 }
6231 // A bare generic struct literal (`Box{...}` / `&Box{...}`) adopts a matching concrete
6232 // expected instance (`Box[int]` / `&Box[int]`), so `fn make() Box[int] { return
6233 // Box{...} }` and bare literals passed/assigned where a concrete instance is expected
6234 // type-check and carry the concrete type into codegen. A *value* literal only adopts a
6235 // *value* expectation: `bare_generic_literal_adopts` unwraps the pointer, so without
6236 // the `expected !is Pointer` guard `return Box{...}` would be accepted for an expected
6237 // `&Box[int]`, and cgen would emit a `Box_int` value where a `Box_int*` is required.
6238 // The pointer case is the `prefix .amp` (`&Box{...}`) path below.
6239 if node.kind == .struct_init && expected !is Pointer
6240 && tc.bare_generic_literal_adopts(node.value, expected)
6241 && tc.generic_literal_fields_compatible(node, expected) {
6242 tc.register_synth_type(id, expected_raw)
6243 return expected_raw
6244 }
6245 if node.kind == .prefix && node.op == .amp && node.children_count == 1 && expected is Pointer {
6246 child := tc.a.nodes[int(tc.a.child(&node, 0))]
6247 if child.kind == .struct_init && tc.bare_generic_literal_adopts(child.value, expected)
6248 && tc.generic_literal_fields_compatible(child, expected) {
6249 tc.register_synth_type(id, expected_raw)
6250 return expected_raw
6251 }
6252 }
6253 if node.kind == .array_literal {
6254 mut elem_expected := Type(void_)
6255 mut expected_is_array := false
6256 if expected is Array {
6257 elem_expected = array_elem_type(expected)
6258 expected_is_array = true
6259 } else if expected is ArrayFixed {
6260 elem_expected = fixed_array_elem_type(expected)
6261 expected_is_array = true
6262 }
6263 if expected_is_array {
6264 // Empty literal `[]` simply adopts the expected array type.
6265 if node.children_count == 0 {
6266 tc.register_synth_type(id, expected_raw)
6267 return expected_raw
6268 }
6269 // Non-empty literal: propagate the expected element type down to each
6270 // element so context-dependent elements (enum shorthand `[.foo]`, `none`,
6271 // fn literals) type against it instead of defaulting to a fixed `int[N]`.
6272 // Adopt the expected array type when every element fits.
6273 mut all_ok := true
6274 for i in 0 .. node.children_count {
6275 child_id := tc.a.child(&node, i)
6276 elem_actual := tc.resolve_expr(child_id, elem_expected)
6277 if !tc.receiver_compatible(elem_actual, elem_expected) {
6278 all_ok = false
6279 break
6280 }
6281 }
6282 // A fixed-array expectation additionally requires the literal to have
6283 // exactly the expected number of elements; otherwise `[1, 2]` would be
6284 // accepted as e.g. `[4]int` and the C backend would copy/read past the
6285 // compound literal. Element-type propagation above still happens either
6286 // way; only the type adoption is gated. Unresolvable const lengths stay
6287 // lenient (we cannot verify them here).
6288 if all_ok && expected is ArrayFixed {
6289 if expected_len := tc.fixed_array_len_value(expected) {
6290 if node.children_count != expected_len {
6291 all_ok = false
6292 }
6293 }
6294 }
6295 if all_ok {
6296 tc.register_synth_type(id, expected_raw)
6297 return expected_raw
6298 }
6299 }
6300 }
6301 if node.kind == .match_stmt || node.kind == .if_expr {
6302 // Match/if used as a value expression: propagate the expected type into
6303 // each branch tail so enum-shorthand / `none` / fn-literal tails type
6304 // against it (e.g. `return match s { 'a' { .foo } ... }` with an enum
6305 // return type), then adopt the expected type when every branch fits.
6306 if expected !is Void && expected !is Unknown && tc.branches_compatible_with(id, expected) {
6307 tc.register_synth_type(id, expected_raw)
6308 return expected_raw
6309 }
6310 }
6311 if _ := fn_type_from_type(expected) {
6312 if _ := tc.resolve_fn_value_name_for_expected(id, expected_raw) {
6313 tc.register_synth_type(id, expected_raw)
6314 return expected_raw
6315 }
6316 }
6317 if node.kind == .fn_literal || node.kind == .lambda_expr {
6318 if _ := fn_type_from_type(expected) {
6319 tc.register_synth_type(id, expected_raw)
6320 return expected_raw
6321 }
6322 }
6323 if tc.expr_is_unsafe_nil(id) {
6324 if _ := fn_type_from_type(expected) {
6325 tc.register_synth_type(id, expected_raw)
6326 return expected_raw
6327 }
6328 }
6329 actual := tc.resolve_type(id)
6330 if tc.type_compatible(actual, expected) {
6331 if actual.name() == expected.name() {
6332 tc.register_synth_type(id, expected)
6333 return expected
6334 }
6335 if expected is OptionType || expected is ResultType {
6336 if actual is OptionType || actual is ResultType {
6337 tc.register_synth_type(id, expected_raw)
6338 return expected_raw
6339 }
6340 return actual
6341 }
6342 if expected is SumType || expected is Enum {
6343 tc.register_synth_type(id, expected_raw)
6344 return expected_raw
6345 }
6346 }
6347 return actual
6348}
6349
6350// fn_value_match_key returns the single exact function declaration key accepted for a function value.
6351fn (tc &TypeChecker) fn_value_match_key(node flat.Node, expected Type) ?string {
6352 expected_fn := fn_type_from_type(expected) or { return none }
6353 key := tc.fn_value_key(node) or { return none }
6354 actual := tc.fn_type_from_key(key) or { return none }
6355 if actual is FnType {
6356 if actual.params.len != expected_fn.params.len {
6357 return none
6358 }
6359 for i in 0 .. actual.params.len {
6360 actual_param := fn_param_type(actual, i)
6361 expected_param := fn_param_type(expected_fn, i)
6362 if !tc.fn_param_compatible(actual_param, expected_param) {
6363 return none
6364 }
6365 }
6366 if tc.fn_return_compatible(actual.return_type, expected_fn.return_type) {
6367 return key
6368 }
6369 }
6370 return none
6371}
6372
6373// fn_value_key resolves a function value expression to one exact function declaration key.
6374fn (tc &TypeChecker) fn_value_key(node flat.Node) ?string {
6375 if node.kind == .ident {
6376 return tc.ident_fn_value_key(node.value)
6377 }
6378 if node.kind == .selector {
6379 return tc.selector_fn_value_key(node)
6380 }
6381 if node.kind in [.cast_expr, .paren, .expr_stmt] && node.children_count > 0 {
6382 return tc.fn_value_key(tc.a.child_node(&node, 0))
6383 }
6384 return none
6385}
6386
6387fn (tc &TypeChecker) ident_fn_value_key(name string) ?string {
6388 qfn := tc.qualify_fn_name(name)
6389 if tc.fn_signature_known(qfn) {
6390 return qfn
6391 }
6392 if imported_name := tc.resolve_selective_import_symbol(name) {
6393 return imported_name
6394 }
6395 if tc.fn_signature_known(name) {
6396 return name
6397 }
6398 return none
6399}
6400
6401fn (tc &TypeChecker) selector_fn_value_key(node flat.Node) ?string {
6402 if node.children_count == 0 || !valid_string_data(node.value) {
6403 return none
6404 }
6405 base := tc.a.child_node(&node, 0)
6406 if base.kind == .ident {
6407 if base.value == 'C' {
6408 key := 'C.${node.value}'
6409 if tc.fn_signature_known(key) {
6410 return key
6411 }
6412 return none
6413 }
6414 if resolved_mod := tc.resolve_import_alias(base.value) {
6415 key := '${resolved_mod}.${node.value}'
6416 if tc.fn_signature_known(key) {
6417 return key
6418 }
6419 return none
6420 }
6421 qbase := tc.qualify_name(base.value)
6422 key := '${qbase}.${node.value}'
6423 if tc.fn_signature_known(key) && (qbase in tc.structs
6424 || qbase in tc.enum_names || qbase in tc.sum_types
6425 || qbase in tc.interface_names) {
6426 return key
6427 }
6428 return none
6429 }
6430 if base.kind == .selector && base.children_count > 0 {
6431 inner := tc.a.child_node(base, 0)
6432 if inner.kind == .ident {
6433 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
6434 key := '${mod_name}.${base.value}.${node.value}'
6435 if tc.fn_signature_known(key) {
6436 return key
6437 }
6438 }
6439 }
6440 return none
6441}
6442
6443fn (tc &TypeChecker) fn_signature_known(key string) bool {
6444 return key in tc.fn_ret_types && key in tc.fn_param_types
6445}
6446
6447fn (tc &TypeChecker) expr_is_unsafe_nil(id flat.NodeId) bool {
6448 if int(id) < 0 || int(id) >= tc.a.nodes.len {
6449 return false
6450 }
6451 node := tc.a.nodes[int(id)]
6452 if node.kind != .block {
6453 return false
6454 }
6455 return tc.expr_tail_is_nil(id)
6456}
6457
6458fn (tc &TypeChecker) expr_tail_is_nil(id flat.NodeId) bool {
6459 if int(id) < 0 || int(id) >= tc.a.nodes.len {
6460 return false
6461 }
6462 node := tc.a.nodes[int(id)]
6463 match node.kind {
6464 .nil_literal {
6465 return true
6466 }
6467 .expr_stmt, .paren {
6468 if node.children_count == 0 {
6469 return false
6470 }
6471 return tc.expr_tail_is_nil(tc.a.child(&node, 0))
6472 }
6473 .block {
6474 if node.children_count == 0 {
6475 return false
6476 }
6477 return tc.expr_tail_is_nil(tc.a.child(&node, node.children_count - 1))
6478 }
6479 else {
6480 return false
6481 }
6482 }
6483}
6484
6485// fn_value_type supports fn value type handling for TypeChecker.
6486fn (tc &TypeChecker) fn_value_type(name string) ?Type {
6487 qfn := tc.qualify_fn_name(name)
6488 if qfn in tc.fn_ret_types {
6489 return tc.fn_type_from_key(qfn)
6490 }
6491 if imported_name := tc.resolve_selective_import_symbol(name) {
6492 return tc.fn_type_from_key(imported_name)
6493 }
6494 if name in tc.fn_ret_types {
6495 return tc.fn_type_from_key(name)
6496 }
6497 return none
6498}
6499
6500// fn_type_from_key converts fn type from key data for types.
6501fn (tc &TypeChecker) fn_type_from_key(key string) ?Type {
6502 params := tc.fn_param_types[key] or { return none }
6503 ret := tc.fn_ret_types[key] or { return none }
6504 return Type(FnType{
6505 params: params.clone()
6506 return_type: ret
6507 })
6508}
6509
6510// struct_field_c_abi_fn_ptr_type returns the C ABI function-pointer type for a struct field.
6511pub fn (tc &TypeChecker) struct_field_c_abi_fn_ptr_type(struct_name string, field_name string) ?string {
6512 key := struct_field_c_abi_key(struct_name, field_name)
6513 if typ := tc.struct_field_c_abi_fns[key] {
6514 return typ
6515 }
6516 return none
6517}
6518
6519// enum_value_matches supports enum value matches handling for TypeChecker.
6520fn (tc &TypeChecker) enum_value_matches(value string, enum_name string) bool {
6521 if value.contains('.') {
6522 prefix := value.all_before_last('.')
6523 field := value.all_after_last('.')
6524 if prefix != enum_name && short_type_name(prefix) != short_type_name(enum_name) {
6525 return false
6526 }
6527 return tc.enum_has_field(enum_name, field)
6528 }
6529 return tc.enum_has_field(enum_name, value)
6530}
6531
6532// enum_has_field converts enum has field data for types.
6533fn (tc &TypeChecker) enum_has_field(enum_name string, field string) bool {
6534 fields := tc.enum_fields[enum_name] or { return false }
6535 return field in fields
6536}
6537
6538// resolve_enum_name resolves resolve enum name information for types.
6539fn (tc &TypeChecker) resolve_enum_name(name string) ?string {
6540 if name in tc.enum_names {
6541 return name
6542 }
6543 qname := tc.qualify_name(name)
6544 if qname in tc.enum_names {
6545 return qname
6546 }
6547 if !name.contains('.') {
6548 if resolved := tc.resolve_selective_import_type_symbol(name) {
6549 if resolved in tc.enum_names || resolved in tc.flag_enums {
6550 return resolved
6551 }
6552 }
6553 }
6554 return none
6555}
6556
6557// enum_selector_type supports enum selector type handling for TypeChecker.
6558fn (tc &TypeChecker) enum_selector_type(node &flat.Node) ?Type {
6559 if node.kind != .selector || node.children_count == 0 {
6560 return none
6561 }
6562 base := tc.a.child_node(node, 0)
6563 mut enum_name := ''
6564 if base.kind == .ident {
6565 enum_name = tc.resolve_enum_name(base.value) or { '' }
6566 } else if base.kind == .selector && base.children_count > 0 {
6567 inner := tc.a.child_node(base, 0)
6568 if inner.kind == .ident {
6569 mod_name := tc.resolve_import_alias(inner.value) or { inner.value }
6570 enum_name = tc.resolve_enum_name('${mod_name}.${base.value}') or { '' }
6571 }
6572 }
6573 if enum_name.len == 0 || !tc.enum_has_field(enum_name, node.value) {
6574 return none
6575 }
6576 return Type(Enum{
6577 name: enum_name
6578 is_flag: enum_name in tc.flag_enums
6579 })
6580}
6581
6582// type_compatible returns type compatible data for TypeChecker.
6583fn (tc &TypeChecker) type_compatible(actual Type, expected Type) bool {
6584 actual_raw := actual
6585 expected_raw := expected
6586 if actual.name() == expected.name() {
6587 return true
6588 }
6589 if actual is Unknown || expected is Unknown {
6590 return true
6591 }
6592 if actual is Alias && expected is Alias {
6593 return tc.type_compatible(actual.base_type, expected.base_type)
6594 }
6595 if actual is Alias {
6596 return tc.type_compatible(actual.base_type, expected)
6597 }
6598 if expected is Alias {
6599 return tc.type_compatible(actual, expected.base_type)
6600 }
6601 if expected is String && is_ierror_type(actual) {
6602 return true
6603 }
6604 if actual is Array && is_runtime_array_type(expected) {
6605 return true
6606 }
6607 if actual is Array && expected is Array && actual.elem_type is Void {
6608 return true
6609 }
6610 if is_runtime_array_type(actual) && expected is Array {
6611 return true
6612 }
6613 if actual is None {
6614 return expected is OptionType || expected is ResultType
6615 }
6616 if expected is OptionType {
6617 if actual is OptionType {
6618 return tc.type_compatible(actual.base_type, expected.base_type)
6619 }
6620 return tc.type_compatible(actual, expected.base_type)
6621 }
6622 if expected is ResultType {
6623 if is_ierror_type(actual) {
6624 return true
6625 }
6626 if actual is ResultType {
6627 return tc.type_compatible(actual.base_type, expected.base_type)
6628 }
6629 return tc.type_compatible(actual, expected.base_type)
6630 }
6631 if expected is SumType {
6632 return tc.type_matches_sum(actual_raw, expected_raw)
6633 }
6634 if expected is Interface {
6635 return tc.type_implements_interface(actual, expected)
6636 }
6637 if actual is Interface {
6638 if expected is Interface {
6639 return tc.interface_implements_interface(actual.name, expected.name)
6640 }
6641 return false
6642 }
6643 if expected is Primitive {
6644 if actual is Primitive {
6645 if expected.props.has(.boolean) || actual.props.has(.boolean) {
6646 return expected.props.has(.boolean) && actual.props.has(.boolean)
6647 }
6648 if expected.props.has(.float) && actual.props.has(.integer) {
6649 return true
6650 }
6651 if expected.props.has(.integer) && actual.props.has(.integer) {
6652 return true
6653 }
6654 if expected.props.has(.float) && actual.props.has(.float) {
6655 return true
6656 }
6657 }
6658 if expected.props.has(.integer) && actual.is_integer() {
6659 return true
6660 }
6661 }
6662 if actual is Primitive && actual.props.has(.integer) && expected.is_integer() {
6663 return true
6664 }
6665 if expected is String {
6666 return actual is String || is_ierror_type(actual)
6667 }
6668 if expected is Char {
6669 return actual is Char || actual.name() == 'u8'
6670 }
6671 if expected is Pointer {
6672 if actual is Nil {
6673 return true
6674 }
6675 if actual is Pointer {
6676 if expected.base_type is Void || actual.base_type is Void {
6677 return true
6678 }
6679 return tc.type_compatible(actual.base_type, expected.base_type)
6680 }
6681 }
6682 if expected is Array {
6683 if actual is Array {
6684 return tc.type_compatible(actual.elem_type, expected.elem_type)
6685 }
6686 if actual is ArrayFixed {
6687 return tc.type_compatible(actual.elem_type, expected.elem_type)
6688 }
6689 }
6690 if expected is ArrayFixed {
6691 if actual is ArrayFixed {
6692 return tc.fixed_array_lengths_compatible(actual, expected)
6693 && tc.type_compatible(actual.elem_type, expected.elem_type)
6694 }
6695 }
6696 if expected is Channel {
6697 if actual is Channel {
6698 return tc.type_compatible(actual.elem_type, expected.elem_type)
6699 }
6700 }
6701 if expected is Map {
6702 if actual is Map {
6703 return tc.type_compatible(actual.key_type, expected.key_type)
6704 && tc.type_compatible(actual.value_type, expected.value_type)
6705 }
6706 }
6707 if expected is FnType {
6708 if actual is FnType {
6709 if actual.params.len != expected.params.len {
6710 return false
6711 }
6712 for i in 0 .. actual.params.len {
6713 actual_param := fn_param_type(actual, i)
6714 expected_param := fn_param_type(expected, i)
6715 if !tc.fn_param_compatible(actual_param, expected_param) {
6716 return false
6717 }
6718 }
6719 return tc.fn_return_compatible(actual.return_type, expected.return_type)
6720 }
6721 }
6722 return false
6723}
6724
6725fn (tc &TypeChecker) fn_param_compatible(actual Type, expected Type) bool {
6726 if actual is Unknown || expected is Unknown {
6727 return false
6728 }
6729 if tc.c_type(actual) == tc.c_type(expected) {
6730 return true
6731 }
6732 return fn_param_can_cast_userdata_param(actual, expected)
6733}
6734
6735fn (tc &TypeChecker) fn_return_compatible(actual Type, expected Type) bool {
6736 if actual.name() == expected.name() {
6737 return true
6738 }
6739 return fn_return_canonical_type_name(actual) == fn_return_canonical_type_name(expected)
6740}
6741
6742fn fn_param_can_cast_userdata_param(actual Type, expected Type) bool {
6743 return (fn_param_is_voidptr_type(expected) && fn_param_is_nonvoid_pointer_type(actual))
6744 || (fn_param_is_nonvoid_pointer_type(expected) && fn_param_is_voidptr_type(actual))
6745}
6746
6747fn fn_param_is_voidptr_type(typ Type) bool {
6748 clean := fn_param_unalias_type(typ)
6749 if clean is Pointer {
6750 base := fn_param_unalias_type(clean.base_type)
6751 return base is Void
6752 }
6753 return false
6754}
6755
6756fn fn_param_is_nonvoid_pointer_type(typ Type) bool {
6757 clean := fn_param_unalias_type(typ)
6758 if clean is Pointer {
6759 base := fn_param_unalias_type(clean.base_type)
6760 return base !is Void
6761 }
6762 return false
6763}
6764
6765fn fn_param_unalias_type(typ Type) Type {
6766 if typ is Alias {
6767 return fn_param_unalias_type(typ.base_type)
6768 }
6769 return typ
6770}
6771
6772fn fn_return_canonical_type_name(typ Type) string {
6773 if typ is Alias {
6774 return fn_return_canonical_type_name(typ.base_type)
6775 }
6776 return typ.name()
6777}
6778
6779// is_ierror_type reports whether is ierror type applies in types.
6780fn is_ierror_type(t Type) bool {
6781 if t is Alias {
6782 return t.name == 'IError' || t.name.ends_with('.IError') || is_ierror_type(t.base_type)
6783 }
6784 if t is Pointer {
6785 return is_ierror_type(t.base_type)
6786 }
6787 if t is Struct {
6788 return t.name == 'IError' || t.name.ends_with('.IError')
6789 }
6790 if t is Interface {
6791 return t.name == 'IError' || t.name.ends_with('.IError')
6792 }
6793 return false
6794}
6795
6796// is_runtime_array_type reports whether is runtime array type applies in types.
6797fn is_runtime_array_type(t Type) bool {
6798 if t is Alias {
6799 return is_runtime_array_type(t.base_type)
6800 }
6801 if t is Struct {
6802 return t.name == 'array'
6803 }
6804 return false
6805}
6806
6807// fixed_array_lengths_compatible supports fixed array lengths compatible handling for TypeChecker.
6808fn (tc &TypeChecker) fixed_array_lengths_compatible(actual ArrayFixed, expected ArrayFixed) bool {
6809 if actual.len > 0 && expected.len > 0 {
6810 return actual.len == expected.len
6811 }
6812 actual_len := tc.fixed_array_len_value(actual) or {
6813 return actual.len_expr == expected.len_expr
6814 }
6815 expected_len := tc.fixed_array_len_value(expected) or {
6816 return actual.len_expr == expected.len_expr
6817 }
6818 return actual_len == expected_len
6819}
6820
6821// fixed_array_len_value returns the evaluated fixed-array length when it can be resolved.
6822pub fn (tc &TypeChecker) fixed_array_len_value(arr ArrayFixed) ?int {
6823 if arr.len > 0 {
6824 return arr.len
6825 }
6826 if arr.len_expr.len == 0 {
6827 return none
6828 }
6829 return tc.const_int_value(arr.len_expr, []string{})
6830}
6831
6832// const_expr_paren_wraps_whole reports whether `s` is a single parenthesised group that
6833// encloses the entire string (`(a + b)`), as opposed to one that only covers part of it
6834// (`(a) + (b)`), so a const length wrapped in redundant parentheses can be unwrapped.
6835fn const_expr_paren_wraps_whole(s string) bool {
6836 if s.len < 2 || s[0] != `(` || s[s.len - 1] != `)` {
6837 return false
6838 }
6839 mut depth := 0
6840 for i := 0; i < s.len; i++ {
6841 if s[i] == `(` {
6842 depth++
6843 } else if s[i] == `)` {
6844 depth--
6845 if depth == 0 {
6846 return i == s.len - 1
6847 }
6848 }
6849 }
6850 return false
6851}
6852
6853// const_int_value supports const int value handling for TypeChecker.
6854pub fn (tc &TypeChecker) const_int_value(name string, seen []string) ?int {
6855 if name in seen {
6856 return none
6857 }
6858 mut candidates := []string{}
6859 candidates << name
6860 qname := tc.qualify_name(name)
6861 if qname != name {
6862 candidates << qname
6863 }
6864 for key in candidates {
6865 if expr_id := tc.const_exprs[key] {
6866 mut next_seen := seen.clone()
6867 next_seen << key
6868 return tc.const_int_expr(expr_id, next_seen)
6869 }
6870 }
6871 if v := v_int_literal_value(name) {
6872 return v
6873 }
6874 // Simple const arithmetic in string form, e.g. a fixed-array size `[SEGS + 1]`,
6875 // `[SEGS+1]`, `[segs / 2]`, `[segs % 4]` or `[2 * (segs + 1)]`. A length wrapped
6876 // wholly in parentheses is the inner expression, so strip the outer pair and
6877 // re-evaluate. Otherwise split on the rightmost operator of the lowest precedence
6878 // level present (`+ -`, then `* / %`) that sits OUTSIDE any parentheses, so a nested
6879 // operator (the `+` inside `2 * (segs + 1)`) is not chosen; precedence and left
6880 // associativity hold and each side is trimmed and resolved recursively. A leading `-`
6881 // (unary) leaves an empty lhs and is skipped.
6882 expr := name.trim_space()
6883 if const_expr_paren_wraps_whole(expr) {
6884 return tc.const_int_value(expr[1..expr.len - 1].trim_space(), seen)
6885 }
6886 // Operators grouped by precedence level, lowest first, MATCHING the v3 parser's binding
6887 // power (token.left_binding_power) so a length text recovered from source folds to the
6888 // same value as the AST const evaluator below: `|` < `^` < `<< >> >>>` < `+ -` <
6889 // `* / % &`. The parser keeps shifts below additive operators (so `arr << a + b` appends
6890 // `a + b`), hence `1 << 2 + 1` groups as `1 << (2 + 1)` — split on `<<` before `+` here
6891 // too, not the reverse. Split on the rightmost top-level operator of the lowest level
6892 // present; longer operators match first (`>>>` before `>>`, two-char before one) and
6893 // `idx + op.len` skips the operator.
6894 for level in [['|'], ['^'], ['<<', '>>', '>>>'], ['+', '-'],
6895 ['*', '/', '%', '&']] {
6896 mut idx := -1
6897 mut op := ''
6898 mut depth := 0
6899 mut i := 0
6900 for i < expr.len {
6901 ch := expr[i..i + 1]
6902 if ch == '(' {
6903 depth++
6904 i++
6905 continue
6906 }
6907 if ch == ')' {
6908 depth--
6909 i++
6910 continue
6911 }
6912 if depth == 0 {
6913 three := if i + 3 <= expr.len { expr[i..i + 3] } else { '' }
6914 if three.len == 3 && three in level {
6915 idx = i
6916 op = three
6917 i += 3
6918 continue
6919 }
6920 two := if i + 2 <= expr.len { expr[i..i + 2] } else { '' }
6921 if two.len == 2 && two in level {
6922 idx = i
6923 op = two
6924 i += 2
6925 continue
6926 }
6927 if ch in level {
6928 idx = i
6929 op = ch
6930 }
6931 }
6932 i++
6933 }
6934 if idx <= 0 {
6935 continue
6936 }
6937 lhs := expr[..idx].trim_space()
6938 rhs := expr[idx + op.len..].trim_space()
6939 if lhs.len == 0 || rhs.len == 0 {
6940 continue
6941 }
6942 l := tc.const_int_value(lhs, seen) or { return none }
6943 r := tc.const_int_value(rhs, seen) or { return none }
6944 if (op == '/' || op == '%') && r == 0 {
6945 return none
6946 }
6947 if (op == '<<' || op == '>>' || op == '>>>') && (r < 0 || r >= 64) {
6948 return none
6949 }
6950 return match op {
6951 '+' { l + r }
6952 '-' { l - r }
6953 '*' { l * r }
6954 '/' { l / r }
6955 '%' { l % r }
6956 '|' { l | r }
6957 '^' { l ^ r }
6958 '&' { l & r }
6959 '<<' { l << r }
6960 '>>' { l >> r }
6961 else { int(u64(l) >> r) }
6962 }
6963 }
6964 return none
6965}
6966
6967// const_int_expr supports const int expr handling for TypeChecker.
6968fn (tc &TypeChecker) const_int_expr(id flat.NodeId, seen []string) ?int {
6969 if int(id) < 0 {
6970 return none
6971 }
6972 node := tc.a.nodes[int(id)]
6973 match node.kind {
6974 .int_literal {
6975 if v := v_int_literal_value(node.value) {
6976 return v
6977 }
6978 }
6979 .ident {
6980 return tc.const_int_value(node.value, seen)
6981 }
6982 .paren {
6983 if node.children_count > 0 {
6984 return tc.const_int_expr(tc.a.child(&node, 0), seen)
6985 }
6986 }
6987 .prefix {
6988 if node.children_count == 0 {
6989 return none
6990 }
6991 value := tc.const_int_expr(tc.a.child(&node, 0), seen) or { return none }
6992 return match node.op {
6993 .minus { -value }
6994 .plus { value }
6995 .bit_not { ~value }
6996 else { none }
6997 }
6998 }
6999 .infix {
7000 if node.children_count < 2 {
7001 return none
7002 }
7003 left := tc.const_int_expr(tc.a.child(&node, 0), seen) or { return none }
7004 right := tc.const_int_expr(tc.a.child(&node, 1), seen) or { return none }
7005 match node.op {
7006 .plus {
7007 return left + right
7008 }
7009 .minus {
7010 return left - right
7011 }
7012 .mul {
7013 return left * right
7014 }
7015 .div {
7016 if right == 0 {
7017 return none
7018 }
7019 return left / right
7020 }
7021 .mod {
7022 if right == 0 {
7023 return none
7024 }
7025 return left % right
7026 }
7027 .amp {
7028 return left & right
7029 }
7030 .pipe {
7031 return left | right
7032 }
7033 .xor {
7034 return left ^ right
7035 }
7036 .left_shift {
7037 if right < 0 || right >= 64 {
7038 return none
7039 }
7040 return left << right
7041 }
7042 .right_shift {
7043 if right < 0 || right >= 64 {
7044 return none
7045 }
7046 return left >> right
7047 }
7048 .right_shift_unsigned {
7049 if right < 0 || right >= 64 {
7050 return none
7051 }
7052 return int(u64(left) >> right)
7053 }
7054 else {
7055 return none
7056 }
7057 }
7058 }
7059 else {}
7060 }
7061
7062 return none
7063}
7064
7065// type_implements_interface returns type implements interface data for TypeChecker.
7066fn (tc &TypeChecker) type_implements_interface(actual Type, expected Interface) bool {
7067 clean := unwrap_pointer(actual)
7068 if clean is Unknown {
7069 return true
7070 }
7071 if clean is Interface {
7072 return tc.interface_implements_interface(clean.name, expected.name)
7073 }
7074 concrete_name := method_type_name(clean)
7075 if concrete_name.len == 0 {
7076 return false
7077 }
7078 return tc.named_type_implements_interface(concrete_name, expected.name)
7079}
7080
7081// interface_implements_interface supports interface implements interface handling for TypeChecker.
7082fn (tc &TypeChecker) interface_implements_interface(actual_name string, expected_name string) bool {
7083 if actual_name == expected_name {
7084 return true
7085 }
7086 for method in tc.interface_method_names(expected_name) {
7087 actual_key := tc.interface_method_signature_key(actual_name, method) or {
7088 '${actual_name}.${method}'
7089 }
7090 expected_key := tc.interface_method_signature_key(expected_name, method) or {
7091 '${expected_name}.${method}'
7092 }
7093 if actual_key !in tc.fn_param_types
7094 || !tc.method_signature_compatible(actual_key, expected_key) {
7095 return false
7096 }
7097 }
7098 for field in tc.interface_field_list(expected_name) {
7099 actual_field := tc.interface_field_type(actual_name, field.name) or { return false }
7100 if !tc.type_compatible(actual_field, field.typ)
7101 || !tc.type_compatible(field.typ, actual_field) {
7102 return false
7103 }
7104 }
7105 return true
7106}
7107
7108// named_type_implements_interface
7109// supports helper handling in types.
7110pub fn (tc &TypeChecker) named_type_implements_interface(concrete_name string, iface_name string) bool {
7111 // Only the abstract (declared) methods must be provided by the concrete type.
7112 // Methods defined directly on the interface (default implementations) are
7113 // inherited and need not be reimplemented.
7114 for method in tc.interface_abstract_method_names(iface_name) {
7115 concrete_key := '${concrete_name}.${method}'
7116 if concrete_key !in tc.fn_param_types {
7117 return false
7118 }
7119 expected_key := tc.interface_method_signature_key(iface_name, method) or {
7120 '${iface_name}.${method}'
7121 }
7122 if !tc.method_signature_compatible(concrete_key, expected_key) {
7123 return false
7124 }
7125 }
7126 for field in tc.interface_field_list(iface_name) {
7127 actual_field := tc.struct_field_type(concrete_name, field.name) or { return false }
7128 if !tc.type_compatible(actual_field, field.typ)
7129 || !tc.type_compatible(field.typ, actual_field) {
7130 return false
7131 }
7132 }
7133 return true
7134}
7135
7136// interface_method_names supports interface method names handling for TypeChecker.
7137fn (tc &TypeChecker) interface_method_names(iface_name string) []string {
7138 mut seen := map[string]bool{}
7139 return tc.interface_method_names_inner(iface_name, mut seen)
7140}
7141
7142// interface_abstract_method_names returns the methods an implementer must provide:
7143// the interface's own declared (abstract) methods plus those of any embedded
7144// interfaces. Default methods defined directly on the interface are excluded.
7145pub fn (tc &TypeChecker) interface_abstract_method_names(iface_name string) []string {
7146 mut seen := map[string]bool{}
7147 return tc.interface_abstract_method_names_inner(iface_name, mut seen)
7148}
7149
7150// interface_abstract_method_names_inner supports interface_abstract_method_names_inner handling.
7151fn (tc &TypeChecker) interface_abstract_method_names_inner(iface_name string, mut seen map[string]bool) []string {
7152 if iface_name in seen {
7153 return []string{}
7154 }
7155 seen[iface_name] = true
7156 mut methods := []string{}
7157 for embed in tc.interface_embeds[iface_name] or { []string{} } {
7158 for method in tc.interface_abstract_method_names_inner(embed, mut seen) {
7159 if method !in methods {
7160 methods << method
7161 }
7162 }
7163 }
7164 for method in tc.interface_abstract_methods[iface_name] or { []string{} } {
7165 if method !in methods {
7166 methods << method
7167 }
7168 }
7169 return methods
7170}
7171
7172// interface_method_names_inner supports interface method names inner handling for TypeChecker.
7173fn (tc &TypeChecker) interface_method_names_inner(iface_name string, mut seen map[string]bool) []string {
7174 if iface_name in seen {
7175 return []string{}
7176 }
7177 seen[iface_name] = true
7178 mut methods := []string{}
7179 for embed in tc.interface_embeds[iface_name] or { []string{} } {
7180 for method in tc.interface_method_names_inner(embed, mut seen) {
7181 if method !in methods {
7182 methods << method
7183 }
7184 }
7185 }
7186 prefix := '${iface_name}.'
7187 for key, _ in tc.fn_ret_types {
7188 if key.starts_with(prefix) {
7189 method := key[prefix.len..]
7190 if method !in methods {
7191 methods << method
7192 }
7193 }
7194 }
7195 return methods
7196}
7197
7198pub fn (tc &TypeChecker) interface_method_signature_key(iface_name string, method string) ?string {
7199 key := '${iface_name}.${method}'
7200 if key in tc.fn_ret_types || key in tc.fn_param_types {
7201 return key
7202 }
7203 for embed in tc.interface_embeds[iface_name] or { []string{} } {
7204 if found := tc.interface_method_signature_key(embed, method) {
7205 return found
7206 }
7207 }
7208 return none
7209}
7210
7211// interface_field_list supports interface field list handling for TypeChecker.
7212fn (tc &TypeChecker) interface_field_list(iface_name string) []StructField {
7213 mut seen := map[string]bool{}
7214 return tc.interface_field_list_inner(iface_name, mut seen)
7215}
7216
7217// interface_field_list_inner supports interface field list inner handling for TypeChecker.
7218fn (tc &TypeChecker) interface_field_list_inner(iface_name string, mut seen map[string]bool) []StructField {
7219 if iface_name in seen {
7220 return []StructField{}
7221 }
7222 seen[iface_name] = true
7223 mut fields := []StructField{}
7224 for embed in tc.interface_embeds[iface_name] or { []string{} } {
7225 fields << tc.interface_field_list_inner(embed, mut seen)
7226 }
7227 for field in tc.interface_fields[iface_name] or { []StructField{} } {
7228 fields << field
7229 }
7230 return fields
7231}
7232
7233// interface_field_type supports interface field type handling for TypeChecker.
7234fn (tc &TypeChecker) interface_field_type(iface_name string, field_name string) ?Type {
7235 for field in tc.interface_field_list(iface_name) {
7236 if field.name == field_name {
7237 return field.typ
7238 }
7239 }
7240 return none
7241}
7242
7243// struct_field_type supports struct field type handling for TypeChecker.
7244fn (tc &TypeChecker) struct_field_type(struct_name string, field_name string) ?Type {
7245 mut seen := map[string]bool{}
7246 return tc.struct_field_type_inner(struct_name, field_name, mut seen)
7247}
7248
7249fn (tc &TypeChecker) struct_field_type_inner(struct_name string, field_name string, mut seen map[string]bool) ?Type {
7250 base_name, generic_args, is_generic := generic_type_application_parts(struct_name)
7251 lookup_name := if is_generic { base_name } else { struct_name }
7252 if lookup_name in seen {
7253 return none
7254 }
7255 seen[lookup_name] = true
7256 for field in tc.structs[lookup_name] or { []StructField{} } {
7257 if field.name == field_name {
7258 if is_generic {
7259 return tc.substitute_generic_type(field.typ, generic_args, tc.struct_generic_params[base_name] or {
7260 []string{}
7261 })
7262 }
7263 return field.typ
7264 }
7265 mut embedded_type := embedded_field_type(field) or { continue }
7266 embedded_type = if is_generic {
7267 tc.substitute_generic_type(embedded_type, generic_args, tc.struct_generic_params[base_name] or {
7268 []string{}
7269 })
7270 } else {
7271 embedded_type
7272 }
7273 embedded_name := method_type_name(unwrap_pointer(embedded_type))
7274 if embedded_name.len == 0 {
7275 continue
7276 }
7277 if typ := tc.struct_field_type_inner(embedded_name, field_name, mut seen) {
7278 return typ
7279 }
7280 }
7281 return none
7282}
7283
7284// substitute_generic_type replaces generic placeholders in `typ` with the concrete
7285// `args`. When `param_names` (the struct/fn's declared type parameters, e.g.
7286// `['L', 'R']`) is provided, a placeholder is matched to its arg by its declared
7287// position — so `Pair[L, R]`'s `R` resolves to `args[1]`, not the letter-based
7288// `generic_param_index` guess (which maps any unrecognised name to 0). The
7289// letter-based fallback is kept only for callers that have no declared names.
7290fn (tc &TypeChecker) substitute_generic_type(typ Type, args []string, param_names []string) Type {
7291 if args.len == 0 {
7292 return typ
7293 }
7294 if typ is Unknown {
7295 if name := generic_placeholder_from_unknown(typ) {
7296 mut idx := if param_names.len > 0 { param_names.index(name) } else { -1 }
7297 if idx < 0 {
7298 idx = generic_param_index(name)
7299 }
7300 if idx >= 0 && idx < args.len {
7301 return tc.parse_type(args[idx].trim_space())
7302 }
7303 }
7304 return typ
7305 }
7306 if typ is Array {
7307 return Type(Array{
7308 elem_type: tc.substitute_generic_type(typ.elem_type, args, param_names)
7309 })
7310 }
7311 if typ is ArrayFixed {
7312 return Type(ArrayFixed{
7313 elem_type: tc.substitute_generic_type(typ.elem_type, args, param_names)
7314 len: typ.len
7315 len_expr: typ.len_expr
7316 })
7317 }
7318 if typ is Map {
7319 return Type(Map{
7320 key_type: tc.substitute_generic_type(typ.key_type, args, param_names)
7321 value_type: tc.substitute_generic_type(typ.value_type, args, param_names)
7322 })
7323 }
7324 if typ is Pointer {
7325 return Type(Pointer{
7326 base_type: tc.substitute_generic_type(typ.base_type, args, param_names)
7327 })
7328 }
7329 if typ is OptionType {
7330 return Type(OptionType{
7331 base_type: tc.substitute_generic_type(typ.base_type, args, param_names)
7332 })
7333 }
7334 if typ is ResultType {
7335 return Type(ResultType{
7336 base_type: tc.substitute_generic_type(typ.base_type, args, param_names)
7337 })
7338 }
7339 if typ is FnType {
7340 mut params := []Type{}
7341 for param in typ.params {
7342 params << tc.substitute_generic_type(param, args, param_names)
7343 }
7344 return Type(FnType{
7345 params: params
7346 return_type: tc.substitute_generic_type(typ.return_type, args, param_names)
7347 })
7348 }
7349 if typ is MultiReturn {
7350 mut parts := []Type{}
7351 for part in typ.types {
7352 parts << tc.substitute_generic_type(part, args, param_names)
7353 }
7354 return Type(MultiReturn{
7355 types: parts
7356 })
7357 }
7358 return typ
7359}
7360
7361fn (tc &TypeChecker) embedded_method_call_info(struct_name string, method string) ?CallInfo {
7362 mut seen := map[string]bool{}
7363 return tc.embedded_method_call_info_inner(struct_name, method, mut seen)
7364}
7365
7366fn (tc &TypeChecker) embedded_method_call_info_inner(struct_name string, method string, mut seen map[string]bool) ?CallInfo {
7367 if seen[struct_name] {
7368 return none
7369 }
7370 seen[struct_name] = true
7371 for field in tc.structs[struct_name] or { []StructField{} } {
7372 embedded_type := embedded_field_type(field) or { continue }
7373 receiver := method_type_name(unwrap_pointer(embedded_type))
7374 if receiver.len == 0 {
7375 continue
7376 }
7377 mut method_names := ['${receiver}.${method}']
7378 base_name, _, is_generic := generic_type_application_parts(receiver)
7379 if is_generic {
7380 method_names << '${base_name}.${method}'
7381 }
7382 for method_name in method_names {
7383 if method_name in tc.fn_ret_types {
7384 info := tc.call_info(method_name, true)
7385 return CallInfo{
7386 name: info.name
7387 params: info.params
7388 return_type: info.return_type
7389 has_receiver: info.has_receiver
7390 is_variadic: info.is_variadic
7391 is_c_variadic: info.is_c_variadic
7392 params_known: if receiver.contains('[') { false } else { info.params_known }
7393 }
7394 }
7395 }
7396 if info := tc.embedded_method_call_info_inner(receiver, method, mut seen) {
7397 return info
7398 }
7399 }
7400 return none
7401}
7402
7403fn (tc &TypeChecker) struct_has_middleware_receiver(struct_name string) bool {
7404 if is_middleware_type_name(struct_name) {
7405 return true
7406 }
7407 for field in tc.structs[struct_name] or { []StructField{} } {
7408 embedded_type := embedded_field_type(field) or { continue }
7409 embedded_name := method_type_name(unwrap_pointer(embedded_type))
7410 if is_middleware_type_name(embedded_name) {
7411 return true
7412 }
7413 }
7414 return false
7415}
7416
7417fn is_middleware_type_name(name string) bool {
7418 base := if name.contains('[') { name.all_before('[') } else { name }
7419 return base == 'veb.Middleware'
7420}
7421
7422fn (tc &TypeChecker) receiver_embeds(actual Type, expected Type) bool {
7423 actual_name := method_type_name(unwrap_pointer(actual))
7424 expected_name := method_type_name(unwrap_pointer(expected))
7425 if actual_name.len == 0 || expected_name.len == 0 {
7426 return false
7427 }
7428 mut seen := map[string]bool{}
7429 return tc.receiver_embeds_inner(actual_name, expected_name, mut seen)
7430}
7431
7432fn (tc &TypeChecker) receiver_embeds_inner(actual_name string, expected_name string, mut seen map[string]bool) bool {
7433 if seen[actual_name] {
7434 return false
7435 }
7436 seen[actual_name] = true
7437 for field in tc.structs[actual_name] or { []StructField{} } {
7438 embedded_type := embedded_field_type(field) or { continue }
7439 embedded_name := method_type_name(unwrap_pointer(embedded_type))
7440 if embedded_name == expected_name {
7441 return true
7442 }
7443 if tc.receiver_embeds_inner(embedded_name, expected_name, mut seen) {
7444 return true
7445 }
7446 }
7447 return false
7448}
7449
7450fn embedded_field_type(field StructField) ?Type {
7451 field_type_name := method_type_name(unwrap_pointer(field.typ))
7452 if field_type_name.len == 0 {
7453 return none
7454 }
7455 mut names := [field_type_name]
7456 base_name, _, is_generic := generic_type_application_parts(field_type_name)
7457 if is_generic {
7458 names << base_name
7459 }
7460 for name in names {
7461 short_name := if name.contains('.') { name.all_after_last('.') } else { name }
7462 if field.name == name || field.name == short_name {
7463 return field.typ
7464 }
7465 }
7466 return none
7467}
7468
7469// method_signature_compatible supports method signature compatible handling for TypeChecker.
7470fn (tc &TypeChecker) method_signature_compatible(actual_key string, expected_key string) bool {
7471 actual_params := tc.fn_param_types[actual_key] or { return false }
7472 expected_params := tc.fn_param_types[expected_key] or { return false }
7473 if actual_params.len != expected_params.len {
7474 return false
7475 }
7476 for i in 1 .. actual_params.len {
7477 if !tc.type_compatible(actual_params[i], expected_params[i])
7478 || !tc.type_compatible(expected_params[i], actual_params[i]) {
7479 return false
7480 }
7481 }
7482 actual_ret := tc.fn_ret_types[actual_key] or { Type(void_) }
7483 expected_ret := tc.fn_ret_types[expected_key] or { Type(void_) }
7484 return tc.type_compatible(actual_ret, expected_ret)
7485}
7486
7487// method_type_name supports method type name handling for types.
7488fn method_type_name(t Type) string {
7489 if t is Alias {
7490 return t.name
7491 }
7492 if t is Struct {
7493 return t.name
7494 }
7495 if t is Interface {
7496 return t.name
7497 }
7498 if t is SumType {
7499 return t.name
7500 }
7501 if t is Enum {
7502 return t.name
7503 }
7504 if t is String {
7505 return 'string'
7506 }
7507 if t is Primitive {
7508 return prim_c_type_from(t.props, t.size)
7509 }
7510 if t is ISize {
7511 return 'isize'
7512 }
7513 if t is USize {
7514 return 'usize'
7515 }
7516 if t is Rune {
7517 return 'rune'
7518 }
7519 return ''
7520}
7521
7522// type_matches_sum returns type matches sum data for TypeChecker.
7523fn (tc &TypeChecker) type_matches_sum(actual Type, expected Type) bool {
7524 if expected is SumType {
7525 actual_name := actual.name()
7526 return tc.sum_has_variant(expected.name, actual_name)
7527 }
7528 return false
7529}
7530
7531// sum_has_variant converts sum has variant data for types.
7532fn (tc &TypeChecker) sum_has_variant(sum_name string, variant_name string) bool {
7533 variants := tc.sum_types[sum_name] or { return false }
7534 variant_short := short_type_name(variant_name)
7535 for variant in variants {
7536 if variant == variant_name || short_type_name(variant) == variant_short {
7537 return true
7538 }
7539 }
7540 qvariant := tc.qualify_name(variant_name)
7541 if qvariant != variant_name {
7542 for variant in variants {
7543 if variant == qvariant || short_type_name(variant) == variant_short {
7544 return true
7545 }
7546 }
7547 }
7548 return false
7549}
7550
7551// match_type_pattern supports match type pattern handling for TypeChecker.
7552fn (tc &TypeChecker) match_type_pattern(node &flat.Node) ?string {
7553 if node.kind == .ident {
7554 return node.value
7555 }
7556 if node.kind == .selector && node.children_count > 0 {
7557 base := tc.a.child_node(node, 0)
7558 if base.kind == .ident {
7559 return '${base.value}.${node.value}'
7560 }
7561 }
7562 return none
7563}
7564
7565// short_type_name supports short type name handling for types.
7566fn short_type_name(name string) string {
7567 if name.contains('.') {
7568 return name.all_after_last('.')
7569 }
7570 return name
7571}
7572
7573// type_mismatch returns type mismatch data for TypeChecker.
7574fn (mut tc TypeChecker) type_mismatch(kind TypeErrorKind, msg string, node flat.NodeId) {
7575 if tc.should_diagnose(node) {
7576 tc.record_error(kind, msg, node)
7577 }
7578}
7579
7580// expr_key supports expr key handling for TypeChecker.
7581fn (tc &TypeChecker) expr_key(id flat.NodeId) string {
7582 if int(id) < 0 {
7583 return ''
7584 }
7585 node := tc.a.nodes[int(id)]
7586 if node.kind == .ident {
7587 if valid_string_data(node.value) {
7588 return node.value
7589 }
7590 return ''
7591 }
7592 if node.kind == .selector && node.children_count > 0 {
7593 base := tc.expr_key(tc.a.child(&node, 0))
7594 if base.len > 0 && node.value.len > 0 && valid_string_data(node.value) {
7595 return '${base}.${node.value}'
7596 }
7597 }
7598 return ''
7599}
7600
7601// smartcast_type supports smartcast type handling for TypeChecker.
7602fn (tc &TypeChecker) smartcast_type(id flat.NodeId) ?Type {
7603 key := tc.expr_key(id)
7604 if key.len == 0 {
7605 return none
7606 }
7607 if !valid_string_data(key) {
7608 return none
7609 }
7610 if typ := tc.smartcasts[key] {
7611 return typ
7612 }
7613 return none
7614}
7615
7616// parse_type converts a V type string (from parser) to a structured Type.
7617pub fn (tc &TypeChecker) parse_type(typ string) Type {
7618 if tc.type_cache != unsafe { nil } && tc.type_cache.parse_enabled {
7619 key := tc.cur_file + '\n' + tc.cur_module + '\n' + typ
7620 mut cache := unsafe { tc.type_cache }
7621 if cached := cache.parse_entries[key] {
7622 return cached
7623 }
7624 result := tc.parse_type_uncached(typ)
7625 cache.parse_entries[key] = result
7626 return result
7627 }
7628 return tc.parse_type_uncached(typ)
7629}
7630
7631// parse_type_uncached reads parse type uncached input for types.
7632fn (tc &TypeChecker) parse_type_uncached(typ string) Type {
7633 if typ.len == 0 {
7634 return Type(void_)
7635 }
7636 if is_generic_placeholder_type(typ) && !tc.is_known_type_text(typ) {
7637 return unknown_type('generic placeholder `${typ}`')
7638 }
7639 if typ.starts_with('&') {
7640 return Type(Pointer{
7641 base_type: tc.parse_type(typ[1..])
7642 })
7643 }
7644 if typ.starts_with('mut ') {
7645 return Type(Pointer{
7646 base_type: tc.parse_type(typ[4..])
7647 })
7648 }
7649 if typ.starts_with('shared ') {
7650 return tc.parse_type(typ[7..])
7651 }
7652 if typ.starts_with('atomic ') {
7653 return tc.parse_type(typ[7..])
7654 }
7655 if typ.starts_with('chan ') {
7656 return Type(Channel{
7657 elem_type: tc.parse_type(typ[5..])
7658 })
7659 }
7660 if typ == 'chan' {
7661 return Type(Channel{
7662 elem_type: Type(void_)
7663 })
7664 }
7665 if typ.starts_with('thread ') || typ == 'thread' {
7666 // A thread handle. The element type (the spawned fn's return type) is kept
7667 // in the struct name (`thread T`) so `array_of_threads.wait()` can recover
7668 // `[]T`. The handle itself lowers to `void*` in C (see c_type).
7669 return Type(Struct{
7670 name: typ
7671 })
7672 }
7673 if typ.starts_with('?') {
7674 return Type(OptionType{
7675 base_type: tc.parse_type(typ[1..])
7676 })
7677 }
7678 if typ.starts_with('!') {
7679 return Type(ResultType{
7680 base_type: tc.parse_type(typ[1..])
7681 })
7682 }
7683 if typ.starts_with('...') {
7684 return Type(Array{
7685 elem_type: tc.parse_type(typ[3..])
7686 })
7687 }
7688 if typ.starts_with('[]') {
7689 return Type(Array{
7690 elem_type: tc.parse_type(typ[2..])
7691 })
7692 }
7693 if typ.starts_with('map[') {
7694 bracket_end := find_matching_bracket(typ, 3)
7695 if bracket_end >= typ.len {
7696 return Type(Unknown{
7697 reason: 'malformed map type'
7698 })
7699 }
7700 key_str := typ[4..bracket_end]
7701 val_str := typ[bracket_end + 1..]
7702 return Type(Map{
7703 key_type: tc.parse_type(key_str)
7704 value_type: tc.parse_type(val_str)
7705 })
7706 }
7707 if typ.starts_with('[') {
7708 idx := typ.index_u8(`]`)
7709 if idx > 0 {
7710 len_text := typ[1..idx].trim_space()
7711 return Type(ArrayFixed{
7712 elem_type: tc.parse_type(typ[idx + 1..])
7713 len: if is_decimal_int_literal(len_text) { len_text.int() } else { 0 }
7714 len_expr: if is_decimal_int_literal(len_text) { '' } else { len_text }
7715 })
7716 }
7717 }
7718 if typ.starts_with('(') && typ.contains(',') {
7719 inner := typ[1..typ.len - 1]
7720 parts := split_params(inner)
7721 mut types := []Type{}
7722 for p in parts {
7723 types << tc.parse_type(p.trim_space())
7724 }
7725 return Type(MultiReturn{
7726 types: types
7727 })
7728 }
7729 if typ.starts_with('fn(') || typ.starts_with('fn (') {
7730 return tc.parse_fn_type(typ)
7731 }
7732 qtyp := tc.qualify_name(typ)
7733 if typ == 'array' && tc.has_builtins && typ in tc.structs {
7734 return Type(Struct{
7735 name: typ
7736 })
7737 }
7738 if typ == 'map' && tc.has_builtins {
7739 return Type(Struct{
7740 name: typ
7741 })
7742 }
7743 if typ == 'array' && tc.has_builtins {
7744 return Type(Struct{
7745 name: typ
7746 })
7747 }
7748 if is_builtin_type_name(typ) {
7749 return builtin_type_value(typ)
7750 }
7751 if typ == 'unknown' {
7752 return Type(Unknown{
7753 reason: 'unknown'
7754 })
7755 }
7756 if typ.starts_with('C.') {
7757 return Type(Struct{
7758 name: typ
7759 })
7760 }
7761 if qtyp in tc.type_aliases {
7762 return Type(Alias{
7763 name: qtyp
7764 base_type: tc.parse_type(tc.type_aliases[qtyp])
7765 })
7766 }
7767 if qtyp in tc.structs {
7768 return Type(Struct{
7769 name: qtyp
7770 })
7771 }
7772 if typ in tc.type_aliases {
7773 return Type(Alias{
7774 name: typ
7775 base_type: tc.parse_type(tc.type_aliases[typ])
7776 })
7777 }
7778 if typ in tc.interface_names {
7779 return Type(Interface{
7780 name: typ
7781 })
7782 }
7783 if qtyp in tc.interface_names {
7784 return Type(Interface{
7785 name: qtyp
7786 })
7787 }
7788 if typ in tc.structs {
7789 return Type(Struct{
7790 name: typ
7791 })
7792 }
7793 if qtyp in tc.structs {
7794 return Type(Struct{
7795 name: qtyp
7796 })
7797 }
7798 if typ in tc.flag_enums {
7799 return Type(Enum{
7800 name: typ
7801 is_flag: true
7802 })
7803 }
7804 if qtyp in tc.flag_enums {
7805 return Type(Enum{
7806 name: qtyp
7807 is_flag: true
7808 })
7809 }
7810 if typ in tc.enum_names {
7811 return Type(Enum{
7812 name: typ
7813 })
7814 }
7815 if qtyp in tc.enum_names {
7816 return Type(Enum{
7817 name: qtyp
7818 })
7819 }
7820 if typ in tc.sum_types {
7821 return Type(SumType{
7822 name: typ
7823 })
7824 }
7825 if qtyp in tc.sum_types {
7826 return Type(SumType{
7827 name: qtyp
7828 })
7829 }
7830 if !typ.contains('.') {
7831 if resolved := tc.resolve_selective_import_type_symbol(typ) {
7832 if resolved_type := tc.type_from_known_symbol(resolved) {
7833 return resolved_type
7834 }
7835 }
7836 }
7837 if !typ.contains('.') {
7838 if resolved := tc.unique_qualified_type_name(typ) {
7839 if resolved in tc.type_aliases {
7840 return Type(Alias{
7841 name: resolved
7842 base_type: tc.parse_type(tc.type_aliases[resolved])
7843 })
7844 }
7845 if resolved in tc.structs {
7846 return Type(Struct{
7847 name: resolved
7848 })
7849 }
7850 if resolved in tc.interface_names {
7851 return Type(Interface{
7852 name: resolved
7853 })
7854 }
7855 if resolved in tc.flag_enums {
7856 return Type(Enum{
7857 name: resolved
7858 is_flag: true
7859 })
7860 }
7861 if resolved in tc.enum_names {
7862 return Type(Enum{
7863 name: resolved
7864 })
7865 }
7866 if resolved in tc.sum_types {
7867 return Type(SumType{
7868 name: resolved
7869 })
7870 }
7871 }
7872 }
7873 if generic_type_application(typ) {
7874 bracket := typ.index_u8(`[`)
7875 base := typ[..bracket]
7876 generic_suffix := typ[bracket..]
7877 _, generic_args, _ := generic_type_application_parts(typ)
7878 is_concrete_generic := tc.generic_args_are_concrete(generic_args)
7879 qbase := tc.qualify_name(base)
7880 if qbase in tc.type_aliases {
7881 return Type(Alias{
7882 name: qbase
7883 base_type: tc.parse_type(tc.type_aliases[qbase])
7884 })
7885 }
7886 if qbase in tc.structs {
7887 return Type(Struct{
7888 name: if is_concrete_generic { qbase + generic_suffix } else { qbase }
7889 })
7890 }
7891 if qbase in tc.interface_names {
7892 return Type(Interface{
7893 name: qbase
7894 })
7895 }
7896 if qbase in tc.sum_types {
7897 return Type(SumType{
7898 name: qbase
7899 })
7900 }
7901 if !base.contains('.') {
7902 if resolved := tc.resolve_selective_import_type_symbol(base) {
7903 if resolved in tc.type_aliases {
7904 return Type(Alias{
7905 name: resolved
7906 base_type: tc.parse_type(tc.type_aliases[resolved])
7907 })
7908 }
7909 if resolved in tc.structs {
7910 return Type(Struct{
7911 name: if is_concrete_generic { resolved + generic_suffix } else { resolved }
7912 })
7913 }
7914 if resolved in tc.interface_names {
7915 return Type(Interface{
7916 name: resolved
7917 })
7918 }
7919 if resolved in tc.sum_types {
7920 return Type(SumType{
7921 name: resolved
7922 })
7923 }
7924 }
7925 }
7926 if base in tc.type_aliases {
7927 return Type(Alias{
7928 name: base
7929 base_type: tc.parse_type(tc.type_aliases[base])
7930 })
7931 }
7932 if base in tc.structs {
7933 return Type(Struct{
7934 name: if is_concrete_generic { typ } else { base }
7935 })
7936 }
7937 if base in tc.interface_names {
7938 return Type(Interface{
7939 name: base
7940 })
7941 }
7942 if base in tc.sum_types {
7943 return Type(SumType{
7944 name: base
7945 })
7946 }
7947 if is_concrete_generic && !is_builtin_type_name(base) {
7948 // A concrete generic instance (`Vec4[f32]`) is a monomorphized struct, even
7949 // when the generic base decl has been erased after monomorphization. It is
7950 // never a fixed array, so don't fall through to the `[N]T` handler below.
7951 // Qualify an imported base (`Vec4` -> `vec.Vec4`) so its c_type matches the
7952 // materialized struct (`vec__Vec4_f32`) everywhere it appears. A builtin base
7953 // (`int[seg_count]`) cannot be a generic application, so let it fall through to
7954 // the fixed-array handler — its bracket is a const/expression length.
7955 mut full := typ
7956 if !base.contains('.') {
7957 if resolved := tc.unique_qualified_type_name(base) {
7958 full = resolved + generic_suffix
7959 }
7960 }
7961 return Type(Struct{
7962 name: full
7963 })
7964 }
7965 }
7966 if is_generic_placeholder_type(typ) {
7967 return unknown_type('generic type parameter `${typ}`')
7968 }
7969 if typ.contains('[') && !typ.starts_with('[') {
7970 // Postfix fixed-array name (`ArrayFixed.name()`): the element comes first and
7971 // each dimension is appended, so the OUTERMOST dimension is the trailing `[N]`
7972 // (`int[3][2]` is `[2][3]int`). Split on the last bracket pair so a nested fixed
7973 // array recovers the outer length and recurses into the inner element, instead of
7974 // taking the first `[N]` and dropping the rest. For a single dimension the last
7975 // and first brackets coincide, so this matches the previous behaviour.
7976 bracket := typ.last_index_u8(`[`)
7977 bracket_end := typ.last_index_u8(`]`)
7978 if bracket >= 0 && bracket_end > bracket {
7979 len_text := typ[bracket + 1..bracket_end].trim_space()
7980 return Type(ArrayFixed{
7981 elem_type: tc.parse_type(typ[..bracket])
7982 len: if is_decimal_int_literal(len_text) { len_text.int() } else { 0 }
7983 len_expr: if is_decimal_int_literal(len_text) { '' } else { len_text }
7984 })
7985 }
7986 }
7987 if qtyp != typ {
7988 return Type(Struct{
7989 name: qtyp
7990 })
7991 }
7992 return Type(Struct{
7993 name: typ
7994 })
7995}
7996
7997fn (tc &TypeChecker) is_known_type_text(typ string) bool {
7998 qtyp := tc.qualify_name(typ)
7999 if !typ.contains('.') {
8000 if resolved := tc.resolve_selective_import_type_symbol(typ) {
8001 return tc.type_symbol_known(resolved)
8002 }
8003 }
8004 return typ in tc.structs || qtyp in tc.structs || typ in tc.interface_names
8005 || qtyp in tc.interface_names || typ in tc.enum_names || qtyp in tc.enum_names
8006 || typ in tc.sum_types || qtyp in tc.sum_types || typ in tc.type_aliases
8007 || qtyp in tc.type_aliases
8008}
8009
8010// unique_qualified_type_name supports unique qualified type name handling for TypeChecker.
8011fn (tc &TypeChecker) unique_qualified_type_name(short_name string) ?string {
8012 if short_name.len == 0 {
8013 return none
8014 }
8015 mut found := ''
8016 for name, _ in tc.type_aliases {
8017 if name.all_after_last('.') == short_name {
8018 if found.len > 0 && found != name {
8019 return none
8020 }
8021 found = name
8022 }
8023 }
8024 for name, _ in tc.structs {
8025 if name.all_after_last('.') == short_name {
8026 if found.len > 0 && found != name {
8027 return none
8028 }
8029 found = name
8030 }
8031 }
8032 for name, _ in tc.interface_names {
8033 if name.all_after_last('.') == short_name {
8034 if found.len > 0 && found != name {
8035 return none
8036 }
8037 found = name
8038 }
8039 }
8040 for name, _ in tc.enum_names {
8041 if name.all_after_last('.') == short_name {
8042 if found.len > 0 && found != name {
8043 return none
8044 }
8045 found = name
8046 }
8047 }
8048 for name, _ in tc.sum_types {
8049 if name.all_after_last('.') == short_name {
8050 if found.len > 0 && found != name {
8051 return none
8052 }
8053 found = name
8054 }
8055 }
8056 if found.len == 0 {
8057 return none
8058 }
8059 return found
8060}
8061
8062// is_generic_placeholder_type reports whether is generic placeholder type applies in types.
8063fn is_generic_placeholder_type(typ string) bool {
8064 if typ.contains('.') {
8065 last := typ.all_after_last('.')
8066 return is_generic_placeholder_type(last)
8067 }
8068 return is_bare_generic_param(typ)
8069}
8070
8071// parse_fn_type reads parse fn type input for types.
8072fn (tc &TypeChecker) parse_fn_type(typ string) Type {
8073 params_start := typ.index_u8(`(`) + 1
8074 mut depth := 1
8075 mut params_end := params_start
8076 for params_end < typ.len {
8077 if typ[params_end] == `(` {
8078 depth++
8079 } else if typ[params_end] == `)` {
8080 depth--
8081 if depth == 0 {
8082 break
8083 }
8084 }
8085 params_end++
8086 }
8087 params_str := typ[params_start..params_end]
8088 ret_str := typ[params_end + 1..].trim_left(' ')
8089 mut params := []Type{}
8090 if params_str.len > 0 {
8091 param_parts := split_params(params_str)
8092 for p in param_parts {
8093 trimmed := p.trim_space()
8094 param_type := normalize_fn_type_param_text(trimmed)
8095 params << tc.parse_type(param_type)
8096 }
8097 }
8098 mut ret_type := Type(Void{})
8099 if ret_str.len > 0 {
8100 ret_type = tc.parse_type(ret_str)
8101 }
8102 return Type(FnType{
8103 params: params
8104 return_type: ret_type
8105 })
8106}
8107
8108fn (tc &TypeChecker) c_abi_fn_ptr_type_from_text(typ string) ?string {
8109 clean := typ.trim_space()
8110 if !clean.starts_with('fn(') && !clean.starts_with('fn (') {
8111 return none
8112 }
8113 params_start := clean.index_u8(`(`) + 1
8114 mut depth := 1
8115 mut params_end := params_start
8116 for params_end < clean.len {
8117 if clean[params_end] == `(` {
8118 depth++
8119 } else if clean[params_end] == `)` {
8120 depth--
8121 if depth == 0 {
8122 break
8123 }
8124 }
8125 params_end++
8126 }
8127 if params_end >= clean.len {
8128 return none
8129 }
8130 params_str := clean[params_start..params_end]
8131 ret_str := clean[params_end + 1..].trim_left(' ')
8132 mut params := []string{}
8133 mut has_c_abi_param := false
8134 if params_str.trim_space().len > 0 {
8135 for part in split_params(params_str) {
8136 ct, is_c_abi := tc.c_abi_fn_param_type(part)
8137 params << ct
8138 if is_c_abi {
8139 has_c_abi_param = true
8140 }
8141 }
8142 }
8143 if !has_c_abi_param {
8144 return none
8145 }
8146 ret_type := if ret_str.len > 0 { tc.parse_type(ret_str) } else { Type(Void{}) }
8147 ret_ct := tc.fn_ptr_return_c_type(ret_type)
8148 params_ct := if params.len == 0 { 'void' } else { params.join(', ') }
8149 return 'fn_ptr:${ret_ct}|${params_ct}'
8150}
8151
8152fn (tc &TypeChecker) c_abi_fn_ptr_type_for_type_text(typ string) ?string {
8153 mut seen := map[string]bool{}
8154 return tc.c_abi_fn_ptr_type_for_type_text_inner(typ.trim_space(), mut seen)
8155}
8156
8157fn (tc &TypeChecker) c_abi_fn_ptr_type_for_type_text_inner(typ string, mut seen map[string]bool) ?string {
8158 if typ.len == 0 || seen[typ] {
8159 return none
8160 }
8161 seen[typ] = true
8162 if c_abi_fn := tc.c_abi_fn_ptr_type_from_text(typ) {
8163 return c_abi_fn
8164 }
8165 for name in [tc.qualify_name(typ), typ] {
8166 if name.len == 0 {
8167 continue
8168 }
8169 if c_abi_fn := tc.type_alias_c_abi_fns[name] {
8170 return c_abi_fn
8171 }
8172 if target := tc.type_aliases[name] {
8173 if c_abi_fn := tc.c_abi_fn_ptr_type_for_type_text_inner(target, mut seen) {
8174 return c_abi_fn
8175 }
8176 }
8177 }
8178 return none
8179}
8180
8181fn (tc &TypeChecker) c_abi_fn_param_type(param string) (string, bool) {
8182 clean := param.trim_space()
8183 param_type := normalize_fn_type_param_text(clean)
8184 if c_abi_fn_param_name(clean).starts_with('const_') && param_type.starts_with('&') {
8185 base_type := tc.parse_type(param_type[1..])
8186 return 'const ${tc.c_type(base_type)}*', true
8187 }
8188 if param_type.starts_with('&') {
8189 if ct := tc.c_abi_alias_pointer_param_c_type(param_type[1..]) {
8190 if clean.starts_with('mut ') {
8191 return '${ct}*', true
8192 }
8193 return 'const ${ct}*', true
8194 }
8195 }
8196 return tc.c_type(tc.parse_type(param_type)), false
8197}
8198
8199fn (tc &TypeChecker) c_abi_alias_pointer_param_c_type(typ string) ?string {
8200 t := tc.parse_type(typ)
8201 if t is Alias {
8202 base := c_abi_alias_c_base_type(t.base_type) or { return none }
8203 return tc.c_type(base)
8204 }
8205 return none
8206}
8207
8208fn c_abi_alias_c_base_type(t Type) ?Type {
8209 if t is Alias {
8210 return c_abi_alias_c_base_type(t.base_type)
8211 }
8212 if t is Struct && t.name.starts_with('C.') {
8213 return t
8214 }
8215 return none
8216}
8217
8218fn c_abi_fn_param_name(param string) string {
8219 mut text := param.trim_space()
8220 if text.starts_with('mut ') {
8221 text = text[4..].trim_space()
8222 }
8223 space := top_level_space_index(text)
8224 if space <= 0 {
8225 return ''
8226 }
8227 head := text[..space].trim_space()
8228 tail := text[space + 1..].trim_space()
8229 if fn_type_param_head_is_name(head, tail) {
8230 return head
8231 }
8232 return ''
8233}
8234
8235fn struct_field_c_abi_key(struct_name string, field_name string) string {
8236 return '${struct_name}\n${field_name}'
8237}
8238
8239// resolve_type resolves resolve type information for types.
8240pub fn (tc &TypeChecker) resolve_type(id flat.NodeId) Type {
8241 if int(id) < 0 {
8242 return unknown_type('missing node')
8243 }
8244 node := tc.a.nodes[int(id)]
8245 kind_id := node_kind_id(node)
8246 if kind_id == 1 {
8247 return Type(int_)
8248 }
8249 if kind_id == 2 {
8250 return Type(f64_)
8251 }
8252 if kind_id == 3 {
8253 return Type(bool_)
8254 }
8255 if kind_id == 4 {
8256 return Type(u8_)
8257 }
8258 if kind_id == 5 || kind_id == 6 {
8259 return Type(string_)
8260 }
8261 if kind_id == 28 {
8262 return Type(voidptr_)
8263 }
8264 if kind_id == 29 {
8265 return Type(OptionType{
8266 base_type: Type(void_)
8267 })
8268 }
8269 if kind_id == 21 {
8270 return tc.fn_literal_type(node)
8271 }
8272 if kind_id == 32 {
8273 return tc.lambda_expr_type(node)
8274 }
8275 if kind_id == 12 && node.typ.len > 0 {
8276 return tc.parse_type(node.typ)
8277 }
8278 if t := tc.resolved_call_type(id) {
8279 return t
8280 }
8281 if kind_id == 22 {
8282 inner := tc.resolve_type(tc.a.child(&node, 0))
8283 if inner is OptionType {
8284 return inner.base_type
8285 }
8286 if inner is ResultType {
8287 return inner.base_type
8288 }
8289 return inner
8290 }
8291 if smart_type := tc.smartcast_type(id) {
8292 return smart_type
8293 }
8294 if kind_id == 14 {
8295 return tc.resolve_index_type(node)
8296 }
8297 if kind_id != 7 && kind_id != 8 && kind_id != 13 && !(tc.smartcasts.len > 0
8298 && (kind_id == 7 || kind_id == 13)) {
8299 if typ := tc.cached_expr_type(id) {
8300 return typ
8301 }
8302 }
8303 if node.typ.len > 0 && node.typ != 'unknown' {
8304 return tc.parse_type(node.typ)
8305 }
8306 match node.kind {
8307 .int_literal {
8308 return Type(int_)
8309 }
8310 .float_literal {
8311 return Type(f64_)
8312 }
8313 .bool_literal {
8314 return Type(bool_)
8315 }
8316 .char_literal {
8317 return Type(u8_)
8318 }
8319 .string_literal, .string_interp {
8320 return Type(string_)
8321 }
8322 .nil_literal {
8323 return Type(voidptr_)
8324 }
8325 .none_expr {
8326 return Type(OptionType{
8327 base_type: Type(void_)
8328 })
8329 }
8330 .enum_val {
8331 return Type(int_)
8332 }
8333 .ident {
8334 if is_bare_generic_param(node.value) {
8335 return unknown_type('generic placeholder `${node.value}`')
8336 }
8337 if smart_type := tc.smartcast_type(id) {
8338 return smart_type
8339 }
8340 if typ := tc.cur_scope.lookup(node.value) {
8341 return typ
8342 }
8343 if typ := tc.file_scope.lookup(node.value) {
8344 return typ
8345 }
8346 qname := tc.qualify_name(node.value)
8347 if qname in tc.const_types {
8348 return tc.const_types[qname] or { unknown_type('unknown const `${qname}`') }
8349 }
8350 if node.value in tc.const_types {
8351 return tc.const_types[node.value] or {
8352 unknown_type('unknown const `${node.value}`')
8353 }
8354 }
8355 if typ := tc.fn_value_type(node.value) {
8356 return typ
8357 }
8358 if tc.selective_import_symbol_is_ambiguous(node.value) {
8359 return unknown_type('ambiguous selective import `${node.value}`')
8360 }
8361 if node.value == 'err' {
8362 return tc.parse_type('IError')
8363 }
8364 return unknown_type('unknown identifier `${node.value}`')
8365 }
8366 .call {
8367 fn_node := tc.a.child_node(&node, 0)
8368 if fn_node.kind == .ident {
8369 if typ := tc.cur_scope.lookup(fn_node.value) {
8370 if typ is FnType {
8371 return typ.return_type
8372 }
8373 }
8374 }
8375 if fn_node.kind == .selector {
8376 base_node := tc.a.child_node(fn_node, 0)