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