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