vxx2 / vlib / v / ast / types.v
2751 lines · 2561 sloc · 68.28 KB · a41a13ac62cef64c319e42b075ad7305ed567cbc
Raw
1// Copyright (c) 2019-2024 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4//
5// Type layout information (32 bits)
6// flag (8 bits) | nr_muls (8 bits) | idx (16 bits)
7// pack: (int(flag)<<24) | (nr_muls<<16) | u16(idx)
8// unpack:
9// flag: (int(type)>>24) & 0xff
10// nr_muls: (int(type)>>16) & 0xff
11// idx: u16(type) & 0xffff
12module ast
13
14import strings
15import v.pref
16import v.token
17
18pub type Type = u32
19
20@[inline]
21pub fn idx_to_type(idx int) Type {
22 return Type(u32(idx))
23}
24
25pub struct UnknownTypeInfo {}
26
27pub type TypeInfo = UnknownTypeInfo
28 | Aggregate
29 | Alias
30 | Array
31 | ArrayFixed
32 | Chan
33 | Enum
34 | FnType
35 | GenericInst
36 | Interface
37 | Map
38 | MultiReturn
39 | Struct
40 | SumType
41 | Thread
42
43pub enum Language {
44 v
45 c
46 js
47 wasm
48 amd64 // aka x86_64
49 i386
50 arm64 // 64-bit arm
51 arm32 // 32-bit arm
52 rv64 // 64-bit risc-v
53 rv32 // 32-bit risc-v
54 s390x
55 ppc64le
56 loongarch64
57 sparc64
58 ppc64
59 wasm32
60}
61
62// pref_arch_to_table_language returns target language based on pref_arch
63pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language {
64 return match pref_arch {
65 .amd64 {
66 .amd64
67 }
68 .arm64 {
69 .arm64
70 }
71 .arm32 {
72 .arm32
73 }
74 .rv64 {
75 .rv64
76 }
77 .rv32 {
78 .rv32
79 }
80 .i386 {
81 .i386
82 }
83 .s390x {
84 .s390x
85 }
86 .ppc64le {
87 .ppc64le
88 }
89 .loongarch64 {
90 .loongarch64
91 }
92 .sparc64 {
93 .sparc64
94 }
95 .ppc64 {
96 .ppc64
97 }
98 .ppc {
99 .v
100 }
101 .js_node, .js_browser, .js_freestanding {
102 .js
103 }
104 .wasm32 {
105 .wasm32
106 }
107 ._auto, ._max {
108 .v
109 }
110 }
111}
112
113// Represents a type that only needs an identifier, e.g. int, array_int.
114// A pointer type `&T` would have a TypeSymbol `T`.
115// Note: For a Type, use:
116// * Table.type_to_str(typ) not TypeSymbol.name.
117// * Table.type_kind(typ) not TypeSymbol.kind.
118// Each TypeSymbol is entered into `Table.type_symbols`.
119// See also: Table.sym.
120@[minify]
121pub struct TypeSymbol {
122pub mut:
123 parent_idx int
124 info TypeInfo
125 kind Kind
126 name string // the internal & source name of the type, i.e. `[5]int`.
127 cname string // the name with no dots for use in the generated C code
128 rname string // the raw name
129 ngname string // the name without generic parameters
130 methods []Fn
131 generic_types []Type
132 mod string
133 is_pub bool
134 is_builtin bool
135 language Language
136 idx int
137 size int = -1
138 align int = -1
139}
140
141pub fn (sym TypeSymbol) aggregate_variant_type(idx int) Type {
142 info := sym.info
143 return match info {
144 Aggregate {
145 if idx >= 0 && idx < info.types.len {
146 info.types[idx]
147 } else {
148 Type(0)
149 }
150 }
151 else {
152 Type(0)
153 }
154 }
155}
156
157// max of 8
158pub enum TypeFlag as u32 {
159 option = 1 << 24
160 result = 1 << 25
161 variadic = 1 << 26
162 generic = 1 << 27
163 shared_f = 1 << 28
164 atomic_f = 1 << 29
165 option_mut_param_t = 1 << 30
166}
167
168/*
169To save precious TypeFlag bits the 4 possible ShareTypes are coded in the two
170bits `shared` and `atomic_or_rw` (see sharetype_from_flags() below).
171*/
172pub enum ShareType {
173 mut_t
174 shared_t
175 atomic_t
176}
177
178// str converts t to it's string form of ShareType i.e. mut, shared, atomic
179pub fn (t ShareType) str() string {
180 match t {
181 .mut_t { return 'mut' }
182 .shared_t { return 'shared' }
183 .atomic_t { return 'atomic' }
184 }
185}
186
187pub struct MultiReturn {
188pub mut:
189 types []Type
190}
191
192pub struct FnType {
193pub mut:
194 is_anon bool
195 has_decl bool
196 func Fn
197}
198
199@[minify]
200pub struct Struct {
201pub:
202 attrs []Attr
203 scoped_name string
204pub mut:
205 embeds []Type
206 fields []StructField
207 is_typedef bool // C. [typedef]
208 is_union bool
209 is_heap bool
210 is_minify bool
211 is_anon bool
212 is_generic bool
213 is_shared bool
214 is_markused bool
215 has_option bool // contains any option field
216 generic_types []Type
217 concrete_types []Type
218 parent_type Type
219 name_pos token.Pos
220}
221
222// instantiation of a generic struct
223pub struct GenericInst {
224pub mut:
225 parent_idx int // idx of the base generic struct
226 concrete_types []Type // concrete types, e.g. [int, string]
227}
228
229@[minify]
230pub struct Interface {
231pub mut:
232 types []Type // all types that implement this interface immutably
233 mut_types []Type // all types that require a mutable interface binding
234 fields []StructField
235 methods []Fn
236 embeds []Type
237 // `I1 is I2` conversions
238 conversions shared map[int][]Type
239 // generic interface support
240 is_generic bool
241 is_markused bool
242 generic_types []Type
243 concrete_types []Type
244 parent_type Type
245 name_pos token.Pos
246}
247
248pub fn (info &Interface) has_implementor(typ Type, include_mut_types bool) bool {
249 if info.types.any(it.idx() == typ.idx()) {
250 return true
251 }
252 return include_mut_types && info.mut_types.any(it.idx() == typ.idx())
253}
254
255pub fn (info &Interface) implementor_types(include_mut_types bool) []Type {
256 mut implementors := info.types.clone()
257 if !include_mut_types {
258 return implementors
259 }
260 for typ in info.mut_types {
261 if !implementors.any(it.idx() == typ.idx()) {
262 implementors << typ
263 }
264 }
265 return implementors
266}
267
268pub struct Enum {
269pub:
270 vals []string
271 is_flag bool
272 is_multi_allowed bool
273 is_typedef bool
274 uses_exprs bool
275 typ Type
276 attrs map[string][]Attr
277 name_pos token.Pos
278}
279
280@[minify]
281pub struct Alias {
282pub mut:
283 parent_type Type
284pub:
285 language Language
286 is_import bool
287 name_pos token.Pos
288}
289
290pub struct Aggregate {
291mut:
292 fields []StructField // used for faster lookup inside the module
293pub:
294 sum_type Type
295 types []Type
296}
297
298pub struct Array {
299pub:
300 nr_dims int
301pub mut:
302 elem_type Type
303}
304
305@[minify]
306pub struct ArrayFixed {
307pub:
308 size int
309 size_expr Expr // used by fmt for e.g. ´[my_const]u8´
310pub mut:
311 elem_type Type
312 is_fn_ret bool
313}
314
315pub struct Chan {
316pub mut:
317 elem_type Type
318 is_mut bool
319}
320
321pub struct Thread {
322pub mut:
323 return_type Type
324}
325
326pub struct Map {
327pub mut:
328 key_type Type
329 value_type Type
330 name_pos token.Pos
331}
332
333@[minify]
334pub struct SumType {
335pub mut:
336 fields []StructField
337 found_fields bool
338 is_anon bool
339 // generic sumtype support
340 is_generic bool
341 variants []Type
342 generic_types []Type
343 concrete_types []Type
344 parent_type Type
345 name_pos token.Pos
346}
347
348pub fn (ti TypeInfo) get_name_pos() ?token.Pos {
349 return match ti {
350 Struct, Alias, SumType, Enum, Interface {
351 ti.name_pos
352 }
353 else {
354 none
355 }
356 }
357}
358
359// <atomic.h> defines special typenames
360pub fn (t Type) atomic_typename() string {
361 idx := t.idx()
362 match idx {
363 u32_type_idx { return 'atomic_uint' }
364 int_type_idx { return '_Atomic int' }
365 i32_type_idx { return '_Atomic int' }
366 u64_type_idx { return 'atomic_ullong' }
367 i64_type_idx { return 'atomic_llong' }
368 else { return 'unknown_atomic' }
369 }
370}
371
372pub fn sharetype_from_flags(is_shared bool, is_atomic bool) ShareType {
373 return unsafe { ShareType(int(u32(is_atomic) << 1) | int(is_shared)) }
374}
375
376pub fn (t Type) share() ShareType {
377 return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_f))
378}
379
380// return TypeSymbol idx for `t`
381@[inline]
382pub fn (t Type) idx() int {
383 return u16(t) & 0xffff
384}
385
386// is_void return true if `t` is of type `void`
387@[inline]
388pub fn (t Type) is_void() bool {
389 return t == void_type
390}
391
392// is_full return true if `t` is not of type `void`
393@[inline]
394pub fn (t Type) is_full() bool {
395 return t != 0 && t != void_type
396}
397
398// return nr_muls for `t`
399@[inline]
400pub fn (t Type) nr_muls() int {
401 return (t >> 16) & 0xff
402}
403
404// return true if `t` is a pointer (nr_muls>0)
405@[inline]
406pub fn (t Type) is_ptr() bool {
407 // any normal pointer, i.e. &Type, &&Type etc;
408 // Note: voidptr, charptr and byteptr are NOT included!
409 return (t >> 16) & 0xff != 0
410}
411
412// is_pointer returns true if `typ` is any of the builtin pointer types (voidptr, byteptr, charptr)
413@[inline]
414pub fn (typ Type) is_pointer() bool {
415 // builtin pointer types (voidptr, byteptr, charptr)
416 return typ.idx() in pointer_type_idxs
417}
418
419// is_voidptr returns true if `typ` is a voidptr
420@[inline]
421pub fn (typ Type) is_voidptr() bool {
422 return typ.idx() == voidptr_type_idx
423}
424
425// is_any_kind_of_pointer returns true if t is any type of pointer
426@[inline]
427pub fn (t Type) is_any_kind_of_pointer() bool {
428 return (t >> 16) & 0xff != 0 || (u16(t) & 0xffff) in pointer_type_idxs
429}
430
431// set nr_muls on `t` and return it
432@[inline]
433pub fn (t Type) set_nr_muls(nr_muls int) Type {
434 if nr_muls < 0 || nr_muls > 255 {
435 panic('set_nr_muls: nr_muls must be between 0 & 255')
436 }
437 return t & 0xff00ffff | u32(nr_muls) << 16
438}
439
440// increments nr_muls on `t` and return it
441@[inline]
442pub fn (t Type) ref() Type {
443 nr_muls := (t >> 16) & 0xff
444 if nr_muls == 255 {
445 panic('ref: nr_muls is already at max of 255')
446 }
447 return t & 0xff00ffff | (nr_muls + 1) << 16
448}
449
450// decrement nr_muls on `t` and return it
451@[inline]
452pub fn (t Type) deref() Type {
453 nr_muls := (t >> 16) & 0xff
454 if nr_muls == 0 {
455 panic('deref: type `${t}` is not a pointer')
456 }
457 return t & 0xff00ffff | (nr_muls - 1) << 16
458}
459
460// flags returns type's flags
461@[inline]
462pub fn (t Type) flags() int {
463 return t >> 16
464}
465
466// has_flag returns whether the given named `flag` is set
467@[inline]
468pub fn (t Type) has_flag(flag TypeFlag) bool {
469 return (t & u32(flag)) != 0
470}
471
472// set_flag returns a new type, that is like the input `t`, but with the named `flag` set
473@[inline]
474pub fn (t Type) set_flag(flag TypeFlag) Type {
475 return t | u32(flag)
476}
477
478// clear_flag returns a new type, that is like `t`, but with the named `flag` cleared
479@[inline]
480pub fn (t Type) clear_flag(flag TypeFlag) Type {
481 return t & ~(u32(flag))
482}
483
484// clear_flags returns a new type, based on `t`, but with cleared named `flags`
485@[inline]
486pub fn (t Type) clear_flags(flags ...TypeFlag) Type {
487 if flags.len == 0 {
488 return t & 0xffffff
489 } else {
490 mut typ := u32(t)
491 for flag in flags {
492 typ = typ & ~(u32(flag))
493 }
494 return typ
495 }
496}
497
498// clear_ref clear refs of type
499@[inline]
500pub fn (t Type) clear_ref() Type {
501 return t & ~0x00FF_0000
502}
503
504// clear option and result flags
505@[inline]
506pub fn (t Type) clear_option_and_result() Type {
507 return t & ~0x0300_0000
508}
509
510@[inline]
511pub fn (t Type) has_option_or_result() bool {
512 return t & 0x0300_0000 != 0
513}
514
515@[inline]
516pub fn (ts &TypeSymbol) scoped_name() string {
517 return if ts.info is Struct && ts.info.scoped_name != '' { ts.info.scoped_name } else { ts.name }
518}
519
520@[inline]
521pub fn (ts &TypeSymbol) scoped_cname() string {
522 return if ts.info is Struct && ts.info.scoped_name != '' {
523 if ts.language == .v && ts.info.scoped_name.contains('[') {
524 ts.info.scoped_name.replace('.', '__').replace_each([
525 '[',
526 '_T_',
527 ']',
528 '',
529 ', ',
530 '_T_',
531 ',',
532 '_T_',
533 ' ',
534 '',
535 '&',
536 '__ptr__',
537 '(',
538 '_',
539 ')',
540 '_',
541 ])
542 } else {
543 ts.info.scoped_name.replace('.', '__')
544 }
545 } else if ts.language == .v && ts.kind in [.placeholder, .generic_inst] && ts.name.contains('[') {
546 ts.name.replace('.', '__').replace_each([
547 '[',
548 '_T_',
549 ']',
550 '',
551 ', ',
552 '_T_',
553 ',',
554 '_T_',
555 ' ',
556 '',
557 '&',
558 '__ptr__',
559 '(',
560 '_',
561 ')',
562 '_',
563 ])
564 } else {
565 ts.cname
566 }
567}
568
569// debug returns a verbose representation of the information in ts, useful for tracing/debugging
570pub fn (ts &TypeSymbol) debug() []string {
571 mut res := []string{}
572 ts.dbg_common(mut res)
573 res << 'info: ${ts.info}'
574 res << 'methods (${ts.methods.len}): ' + ts.methods.map(it.str()).join(', ')
575 return res
576}
577
578// same as .debug(), but without the verbose .info and .methods fields
579pub fn (ts &TypeSymbol) dbg() []string {
580 mut res := []string{}
581 ts.dbg_common(mut res)
582 return res
583}
584
585fn (ts &TypeSymbol) dbg_common(mut res []string) {
586 res << 'idx: 0x${ts.idx.hex()}'
587 res << 'parent_idx: 0x${ts.parent_idx.hex()}'
588 res << 'mod: ${ts.mod}'
589 res << 'name: ${ts.name}'
590 res << 'cname: ${ts.cname}'
591 res << 'kind: ${ts.kind}'
592 res << 'is_pub: ${ts.is_pub}'
593 res << 'language: ${ts.language}'
594}
595
596pub fn (ts &TypeSymbol) nr_dims() int {
597 match ts.info {
598 Alias {
599 parent_sym := global_table.sym(ts.info.parent_type)
600 if parent_sym.info is Array {
601 return parent_sym.info.nr_dims
602 }
603 return 0
604 }
605 Array {
606 elem_sym := global_table.sym(ts.info.elem_type)
607 if elem_sym.info is Alias {
608 return ts.info.nr_dims + elem_sym.nr_dims()
609 }
610 return ts.info.nr_dims
611 }
612 else {
613 return 0
614 }
615 }
616}
617
618// str returns a string representation of the type.
619pub fn (t Type) str() string {
620 return 'ast.Type(0x${t.hex()} = ${u32(t)})'
621}
622
623pub fn (t &Table) type_str(typ Type) string {
624 idx := typ.idx()
625 if idx == 0 || idx >= t.type_symbols.len {
626 return 'unknown'
627 }
628 return t.sym(typ).name
629}
630
631// debug returns a verbose representation of the information in the type `t`, useful for tracing/debugging
632pub fn (t Type) debug() []string {
633 mut res := []string{}
634 res << 'idx: 0x${t.idx().hex():-8}'
635 res << 'type: 0x${t.hex():-8}'
636 res << 'nr_muls: ${t.nr_muls()}'
637 if t.has_flag(.option) {
638 res << 'option'
639 }
640 if t.has_flag(.result) {
641 res << 'result'
642 }
643 if t.has_flag(.variadic) {
644 res << 'variadic'
645 }
646 if t.has_flag(.generic) {
647 res << 'generic'
648 }
649 if t.has_flag(.shared_f) {
650 res << 'shared_f'
651 }
652 if t.has_flag(.atomic_f) {
653 res << 'atomic_f'
654 }
655 return res
656}
657
658// copy flags & nr_muls from `t_from` to `t` and return `t`
659@[inline]
660pub fn (t Type) derive(t_from Type) Type {
661 return (0xffff0000 & t_from) | u16(t)
662}
663
664// copy flags from `t_from` to `t` and return `t`
665@[inline]
666pub fn (t Type) derive_add_muls(t_from Type) Type {
667 return Type((0xff000000 & t_from) | u16(t)).set_nr_muls(t.nr_muls() + t_from.nr_muls())
668}
669
670// return new type from its `idx`
671@[inline]
672pub fn (t Type) idx_type() Type {
673 return idx_to_type(t.idx())
674}
675
676// return new type with TypeSymbol idx set to `idx`
677@[inline]
678pub fn new_type(idx int) Type {
679 if idx < 1 || idx > 65535 {
680 panic('new_type: idx must be between 1 & 65535')
681 }
682 return idx
683}
684
685// return new type with TypeSymbol idx set to `idx` & nr_muls set to `nr_muls`
686@[inline]
687pub fn new_type_ptr(idx int, nr_muls int) Type {
688 if idx < 1 || idx > 65535 {
689 panic('new_type_ptr: idx must be between 1 & 65535')
690 }
691 if nr_muls < 0 || nr_muls > 255 {
692 panic('new_type_ptr: nr_muls must be between 0 & 255')
693 }
694 return (u32(nr_muls) << 16) | u16(idx)
695}
696
697// is_float returns `true` if `typ` is float
698@[inline]
699pub fn (typ Type) is_float() bool {
700 return !typ.is_ptr() && typ.idx() in float_type_idxs
701}
702
703// is_int returns `true` if `typ` is int
704@[inline]
705pub fn (typ Type) is_int() bool {
706 return !typ.is_ptr() && typ.idx() in integer_type_idxs
707}
708
709// is_int_valptr returns `true` if `typ` is a pointer to a int
710@[inline]
711pub fn (typ Type) is_int_valptr() bool {
712 return typ.is_ptr() && typ.idx() in integer_type_idxs
713}
714
715// is_float_valptr return `true` if `typ` is a pointer to float
716@[inline]
717pub fn (typ Type) is_float_valptr() bool {
718 return typ.is_ptr() && typ.idx() in float_type_idxs
719}
720
721// is_pure_int return `true` if `typ` is a pure int
722@[inline]
723pub fn (typ Type) is_pure_int() bool {
724 return int(typ) in integer_type_idxs
725}
726
727// is_pure_float return `true` if `typ` is a pure float
728@[inline]
729pub fn (typ Type) is_pure_float() bool {
730 return int(typ) in float_type_idxs
731}
732
733// is_signed return `true` if `typ` is signed
734@[inline]
735pub fn (typ Type) is_signed() bool {
736 return typ.idx() in signed_integer_type_idxs
737}
738
739// is_unsigned return `true` if `typ` is unsigned
740@[inline]
741pub fn (typ Type) is_unsigned() bool {
742 return typ.idx() in unsigned_integer_type_idxs
743}
744
745pub fn (typ Type) flip_signedness() Type {
746 return match typ {
747 i8_type {
748 u8_type
749 }
750 i16_type {
751 u16_type
752 }
753 i32_type {
754 u32_type
755 }
756 i64_type {
757 u64_type
758 }
759 int_type {
760 $if new_int ? && x64 {
761 u64_type
762 } $else {
763 u32_type
764 }
765 }
766 isize_type {
767 usize_type
768 }
769 u8_type {
770 i8_type
771 }
772 u16_type {
773 i16_type
774 }
775 u32_type {
776 i32_type
777 }
778 u64_type {
779 i64_type
780 }
781 usize_type {
782 isize_type
783 }
784 else {
785 void_type
786 }
787 }
788}
789
790// is_int_literal returns `true` if `typ` is a int literal
791@[inline]
792pub fn (typ Type) is_int_literal() bool {
793 return int(typ) == int_literal_type_idx
794}
795
796// is_number returns `true` if `typ` is a number
797@[inline]
798pub fn (typ Type) is_number() bool {
799 return typ.clear_flags() in number_type_idxs
800}
801
802// is_string returns `true` if `typ` is a string type
803@[inline]
804pub fn (typ Type) is_string() bool {
805 return typ.idx() == string_type_idx
806}
807
808// is_bool returns `true` if `typ` is of bool type
809@[inline]
810pub fn (typ Type) is_bool() bool {
811 return typ.idx() == bool_type_idx
812}
813
814pub const invalid_type_idx = -1
815pub const no_type_idx = 0
816pub const void_type_idx = 1
817pub const voidptr_type_idx = 2
818pub const byteptr_type_idx = 3
819pub const charptr_type_idx = 4
820pub const i8_type_idx = 5
821pub const i16_type_idx = 6
822pub const i32_type_idx = 7
823pub const int_type_idx = 8
824pub const i64_type_idx = 9
825pub const isize_type_idx = 10
826pub const u8_type_idx = 11
827pub const u16_type_idx = 12
828pub const u32_type_idx = 13
829pub const u64_type_idx = 14
830pub const usize_type_idx = 15
831pub const f32_type_idx = 16
832pub const f64_type_idx = 17
833pub const char_type_idx = 18
834pub const bool_type_idx = 19
835pub const none_type_idx = 20
836pub const string_type_idx = 21
837pub const rune_type_idx = 22
838pub const array_type_idx = 23
839pub const map_type_idx = 24
840pub const chan_type_idx = 25
841pub const any_type_idx = 26
842pub const float_literal_type_idx = 27
843pub const int_literal_type_idx = 28
844pub const thread_type_idx = 29
845pub const error_type_idx = 30
846pub const nil_type_idx = 31
847
848// Note: builtin_type_names must be in the same order as the idx consts above
849pub const builtin_type_names = ['void', 'voidptr', 'byteptr', 'charptr', 'i8', 'i16', 'i32', 'int',
850 'i64', 'isize', 'u8', 'u16', 'u32', 'u64', 'usize', 'f32', 'f64', 'char', 'bool', 'none',
851 'string', 'rune', 'array', 'map', 'chan', 'any', 'float_literal', 'int_literal', 'thread',
852 'Error', 'nil']
853
854pub const builtin_type_names_matcher = token.new_keywords_matcher_from_array_trie(builtin_type_names)
855
856pub const integer_type_idxs = [i8_type_idx, i16_type_idx, i32_type_idx, int_type_idx, i64_type_idx,
857 u8_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, isize_type_idx, usize_type_idx,
858 int_literal_type_idx, rune_type_idx]
859pub const signed_integer_type_idxs = [char_type_idx, i8_type_idx, i16_type_idx, i32_type_idx,
860 int_type_idx, i64_type_idx, isize_type_idx]
861pub const unsigned_integer_type_idxs = [u8_type_idx, u16_type_idx, u32_type_idx, u64_type_idx,
862 usize_type_idx]
863// C will promote any type smaller than int to int in an expression
864pub const int_promoted_type_idxs = [char_type_idx, i8_type_idx, i16_type_idx, u8_type_idx,
865 u16_type_idx]
866pub const float_type_idxs = [f32_type_idx, f64_type_idx, float_literal_type_idx]
867pub const number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i32_type_idx, i64_type_idx,
868 u8_type_idx, char_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, isize_type_idx,
869 usize_type_idx, f32_type_idx, f64_type_idx, int_literal_type_idx, float_literal_type_idx,
870 rune_type_idx]
871pub const pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx, nil_type_idx]
872
873pub const invalid_type = idx_to_type(invalid_type_idx) // -1 is a purposefully invalid type, not by default, but to signify checker errors
874pub const no_type = idx_to_type(no_type_idx) // 0 is an invalid type, but it is useful for initialising default ast.Type values, in fields or before loops
875pub const void_type = new_type(void_type_idx)
876pub const ovoid_type = new_type(void_type_idx).set_flag(.option) // the return type of `fn ()?`
877pub const rvoid_type = new_type(void_type_idx).set_flag(.result) // the return type of `fn () !`
878pub const voidptr_type = new_type(voidptr_type_idx)
879pub const byteptr_type = new_type(byteptr_type_idx)
880pub const charptr_type = new_type(charptr_type_idx)
881pub const i8_type = new_type(i8_type_idx)
882pub const i16_type = new_type(i16_type_idx)
883pub const i32_type = new_type(i32_type_idx)
884pub const int_type = new_type(int_type_idx)
885pub const i64_type = new_type(i64_type_idx)
886pub const isize_type = new_type(isize_type_idx)
887pub const u8_type = new_type(u8_type_idx)
888pub const u16_type = new_type(u16_type_idx)
889pub const u32_type = new_type(u32_type_idx)
890pub const u64_type = new_type(u64_type_idx)
891pub const usize_type = new_type(usize_type_idx)
892pub const f32_type = new_type(f32_type_idx)
893pub const f64_type = new_type(f64_type_idx)
894pub const char_type = new_type(char_type_idx)
895pub const bool_type = new_type(bool_type_idx)
896pub const none_type = new_type(none_type_idx)
897pub const string_type = new_type(string_type_idx)
898pub const rune_type = new_type(rune_type_idx)
899pub const array_type = new_type(array_type_idx)
900pub const map_type = new_type(map_type_idx)
901pub const chan_type = new_type(chan_type_idx)
902pub const any_type = new_type(any_type_idx)
903pub const float_literal_type = new_type(float_literal_type_idx)
904pub const int_literal_type = new_type(int_literal_type_idx)
905pub const thread_type = new_type(thread_type_idx)
906pub const error_type = new_type(error_type_idx)
907pub const charptr_types = new_charptr_types()
908pub const byteptr_types = new_byteptr_types()
909pub const voidptr_types = new_voidptr_types()
910pub const cptr_types = merge_types(voidptr_types, byteptr_types, charptr_types)
911pub const nil_type = new_type(nil_type_idx)
912
913pub const builtin_array_generic_methods = ['all', 'any', 'count', 'filter', 'map', 'sort', 'sorted']
914pub const builtin_array_generic_methods_matcher = token.new_keywords_matcher_from_array_trie(builtin_array_generic_methods)
915
916pub const builtin_array_generic_methods_no_sort = ['all', 'any', 'count', 'filter', 'map']
917pub const builtin_array_generic_methods_no_sort_matcher = token.new_keywords_matcher_from_array_trie(builtin_array_generic_methods_no_sort)
918
919fn new_charptr_types() []Type {
920 return [charptr_type, new_type(char_type_idx).set_nr_muls(1)]
921}
922
923fn new_byteptr_types() []Type {
924 return [byteptr_type, new_type(u8_type_idx).set_nr_muls(1)]
925}
926
927fn new_voidptr_types() []Type {
928 return [voidptr_type, new_type(voidptr_type_idx).set_nr_muls(1)]
929}
930
931pub fn merge_types(params ...[]Type) []Type {
932 mut res := []Type{cap: params.len}
933 for types in params {
934 for t in types {
935 if t !in res {
936 res << t
937 }
938 }
939 }
940 return res
941}
942
943pub fn mktyp(typ Type) Type {
944 return match typ {
945 float_literal_type { f64_type }
946 int_literal_type { int_type }
947 else { typ }
948 }
949}
950
951// type_kind returns the kind of the given type symbol.
952pub fn (t &Table) type_kind(typ Type) Kind {
953 if typ.nr_muls() > 0 || typ.has_option_or_result() {
954 return Kind.placeholder
955 }
956 return t.sym(typ).kind
957}
958
959pub fn (t &Table) type_is_for_pointer_arithmetic(typ Type) bool {
960 if t.sym(typ).kind == .struct {
961 return false
962 } else {
963 return typ.is_any_kind_of_pointer() || typ.is_int_valptr()
964 }
965}
966
967pub enum Kind {
968 placeholder
969 void
970 voidptr
971 byteptr
972 charptr
973 i8
974 i16
975 i32
976 int
977 i64
978 isize
979 u8
980 u16
981 u32
982 u64
983 usize
984 f32
985 f64
986 char
987 rune
988 bool
989 none
990 string
991 array
992 array_fixed
993 map
994 chan
995 any
996 struct
997 generic_inst
998 multi_return
999 sum_type
1000 alias
1001 enum
1002 function
1003 interface
1004 float_literal
1005 int_literal
1006 aggregate
1007 thread
1008}
1009
1010// str returns the internal & source name of the type
1011pub fn (t &TypeSymbol) str() string {
1012 return t.name.clone()
1013}
1014
1015// TODO why is this needed? str() returns incorrect amount of &
1016pub fn (t &TypeSymbol) str_with_correct_nr_muls(n int) string {
1017 prefix := strings.repeat(`&`, n)
1018 return prefix + t.name
1019}
1020
1021@[noreturn]
1022fn (t &TypeSymbol) no_info_panic(fname string) {
1023 panic('${fname}: no info for type: ${t.name}')
1024}
1025
1026@[inline]
1027pub fn (t &TypeSymbol) enum_info() Enum {
1028 if t.info is Enum {
1029 return t.info
1030 }
1031 if t.info is Alias {
1032 fsym := global_table.final_sym(t.info.parent_type)
1033 if fsym.info is Enum {
1034 return fsym.info
1035 }
1036 }
1037 t.no_info_panic(@METHOD)
1038}
1039
1040@[inline]
1041pub fn (t &TypeSymbol) mr_info() MultiReturn {
1042 if t.info is MultiReturn {
1043 return t.info
1044 }
1045 if t.info is Alias {
1046 fsym := global_table.final_sym(t.info.parent_type)
1047 if fsym.info is MultiReturn {
1048 return fsym.info
1049 }
1050 }
1051 t.no_info_panic(@METHOD)
1052}
1053
1054@[inline]
1055pub fn (t &TypeSymbol) array_info() Array {
1056 if t.info is Array {
1057 return t.info
1058 }
1059 if t.info is Alias {
1060 fsym := global_table.final_sym(t.info.parent_type)
1061 if fsym.info is Array {
1062 return fsym.info
1063 }
1064 }
1065 t.no_info_panic(@METHOD)
1066}
1067
1068@[inline]
1069pub fn (t &TypeSymbol) array_fixed_info() ArrayFixed {
1070 if t.info is ArrayFixed {
1071 return t.info
1072 }
1073 if t.info is Alias {
1074 fsym := global_table.final_sym(t.info.parent_type)
1075 if fsym.info is ArrayFixed {
1076 return fsym.info
1077 }
1078 }
1079 t.no_info_panic(@METHOD)
1080}
1081
1082@[inline]
1083pub fn (t &TypeSymbol) chan_info() Chan {
1084 if t.info is Chan {
1085 return t.info
1086 }
1087 if t.info is Alias {
1088 fsym := global_table.final_sym(t.info.parent_type)
1089 if fsym.info is Chan {
1090 return fsym.info
1091 }
1092 }
1093 t.no_info_panic(@METHOD)
1094}
1095
1096@[inline]
1097pub fn (t &TypeSymbol) thread_info() Thread {
1098 if t.info is Thread {
1099 return t.info
1100 }
1101 if t.info is Alias {
1102 fsym := global_table.final_sym(t.info.parent_type)
1103 if fsym.info is Thread {
1104 return fsym.info
1105 }
1106 }
1107 t.no_info_panic(@METHOD)
1108}
1109
1110@[inline]
1111pub fn (t &TypeSymbol) map_info() Map {
1112 if t.info is Map {
1113 return t.info
1114 }
1115 if t.info is Alias {
1116 fsym := global_table.final_sym(t.info.parent_type)
1117 if fsym.info is Map {
1118 return fsym.info
1119 }
1120 }
1121 t.no_info_panic(@METHOD)
1122}
1123
1124@[inline]
1125pub fn (t &TypeSymbol) struct_info() Struct {
1126 if t.info is Struct {
1127 return t.info
1128 }
1129 if t.info is Alias {
1130 fsym := global_table.final_sym(t.info.parent_type)
1131 if fsym.info is Struct {
1132 return fsym.info
1133 }
1134 }
1135 t.no_info_panic(@METHOD)
1136}
1137
1138@[inline]
1139pub fn (t &TypeSymbol) sumtype_info() SumType {
1140 if t.info is SumType {
1141 return t.info
1142 }
1143 if t.info is SumType {
1144 fsym := global_table.final_sym(t.info.parent_type)
1145 if fsym.info is SumType {
1146 return fsym.info
1147 }
1148 }
1149 t.no_info_panic(@METHOD)
1150}
1151
1152pub fn (t &TypeSymbol) is_heap() bool {
1153 if t.info is Struct {
1154 return t.info.is_heap
1155 } else {
1156 return false
1157 }
1158}
1159
1160pub fn (t &ArrayFixed) is_compatible(t2 ArrayFixed) bool {
1161 return t.size == t2.size && t.elem_type == t2.elem_type
1162}
1163
1164pub fn (t &TypeSymbol) is_empty_struct_array() bool {
1165 if t.info is ArrayFixed {
1166 elem_sym := global_table.final_sym(t.info.elem_type)
1167 if elem_sym.info is Struct {
1168 return elem_sym.info.is_empty_struct()
1169 }
1170 }
1171 return false
1172}
1173
1174@[inline]
1175pub fn (t &Struct) is_empty_struct() bool {
1176 return t.fields.len == 0 && t.embeds.len == 0
1177}
1178
1179@[inline]
1180pub fn (t &Struct) is_unresolved_generic() bool {
1181 return t.generic_types.len > 0 && t.concrete_types.len == 0
1182}
1183
1184pub fn (t &TypeSymbol) is_primitive_fixed_array() bool {
1185 if t.info is ArrayFixed {
1186 return global_table.final_sym(t.info.elem_type).is_primitive()
1187 } else if t.info is Alias {
1188 return global_table.final_sym(t.info.parent_type).is_primitive_fixed_array()
1189 } else {
1190 return false
1191 }
1192}
1193
1194pub fn (t &TypeSymbol) is_array_fixed() bool {
1195 if t.info is ArrayFixed {
1196 return true
1197 } else if t.info is Alias {
1198 return global_table.final_sym(t.info.parent_type).is_array_fixed()
1199 } else {
1200 return false
1201 }
1202}
1203
1204pub fn (t &TypeSymbol) is_c_struct() bool {
1205 if t.info is Struct {
1206 // `C___VAnonStruct` need special handle, it need to create a new struct
1207 return t.language == .c && !t.info.is_anon
1208 } else if t.info is Alias {
1209 return global_table.final_sym(t.info.parent_type).is_c_struct()
1210 }
1211 return false
1212}
1213
1214pub fn (t &TypeSymbol) is_array_fixed_ret() bool {
1215 if t.info is ArrayFixed {
1216 return t.info.is_fn_ret
1217 } else if t.info is Alias {
1218 return global_table.final_sym(t.info.parent_type).is_array_fixed_ret()
1219 } else {
1220 return false
1221 }
1222}
1223
1224pub fn (mut t Table) register_builtin_type_symbols() {
1225 // reserve index 0 so nothing can go there
1226 // save index check, 0 will mean not found
1227 // THE ORDER MUST BE THE SAME AS xxx_type_idx CONSTS EARLIER IN THIS FILE
1228 t.register_sym(kind: .placeholder, name: 'reserved_0')
1229 t.register_sym(kind: .void, name: 'void', cname: 'void', mod: 'builtin', is_pub: true) // 1
1230 t.register_sym(kind: .voidptr, name: 'voidptr', cname: 'voidptr', mod: 'builtin', is_pub: true) // 2
1231 t.register_sym(kind: .byteptr, name: 'byteptr', cname: 'byteptr', mod: 'builtin', is_pub: true) // 3
1232 t.register_sym(kind: .charptr, name: 'charptr', cname: 'charptr', mod: 'builtin', is_pub: true) // 4
1233 t.register_sym(kind: .i8, name: 'i8', cname: 'i8', mod: 'builtin', is_pub: true) // 5
1234 t.register_sym(kind: .i16, name: 'i16', cname: 'i16', mod: 'builtin', is_pub: true) // 6
1235 t.register_sym(kind: .i32, name: 'i32', cname: 'i32', mod: 'builtin', is_pub: true) // 7
1236 t.register_sym(kind: .int, name: 'int', cname: int_type_name, mod: 'builtin', is_pub: true) // 8
1237 t.register_sym(kind: .i64, name: 'i64', cname: 'i64', mod: 'builtin', is_pub: true) // 9
1238 t.register_sym(kind: .isize, name: 'isize', cname: 'isize', mod: 'builtin', is_pub: true) // 10
1239 t.register_sym(kind: .u8, name: 'u8', cname: 'u8', mod: 'builtin', is_pub: true) // 11
1240 t.register_sym(kind: .u16, name: 'u16', cname: 'u16', mod: 'builtin', is_pub: true) // 12
1241 t.register_sym(kind: .u32, name: 'u32', cname: 'u32', mod: 'builtin', is_pub: true) // 13
1242 t.register_sym(kind: .u64, name: 'u64', cname: 'u64', mod: 'builtin', is_pub: true) // 14
1243 t.register_sym(kind: .usize, name: 'usize', cname: 'usize', mod: 'builtin', is_pub: true) // 15
1244 t.register_sym(kind: .f32, name: 'f32', cname: 'f32', mod: 'builtin', is_pub: true) // 16
1245 t.register_sym(kind: .f64, name: 'f64', cname: 'f64', mod: 'builtin', is_pub: true) // 17
1246 t.register_sym(kind: .char, name: 'char', cname: 'char', mod: 'builtin', is_pub: true) // 18
1247 t.register_sym(kind: .bool, name: 'bool', cname: 'bool', mod: 'builtin', is_pub: true) // 19
1248 t.register_sym(kind: .none, name: 'none', cname: 'none', mod: 'builtin', is_pub: true) // 20
1249 t.register_sym(
1250 kind: .string
1251 name: 'string'
1252 cname: 'string'
1253 mod: 'builtin'
1254 is_builtin: true
1255 is_pub: true
1256 ) // 21
1257 t.register_sym(kind: .rune, name: 'rune', cname: 'rune', mod: 'builtin', is_pub: true) // 22
1258 t.register_sym(
1259 kind: .array
1260 name: 'array'
1261 cname: 'array'
1262 mod: 'builtin'
1263 is_builtin: true
1264 is_pub: true
1265 ) // 23
1266 t.register_sym(
1267 kind: .map
1268 name: 'map'
1269 cname: 'map'
1270 mod: 'builtin'
1271 is_builtin: true
1272 is_pub: true
1273 ) // 24
1274 t.register_sym(kind: .chan, name: 'chan', cname: 'chan', mod: 'builtin', is_pub: true) // 25
1275 t.register_sym(kind: .any, name: 'any', cname: 'any', mod: 'builtin', is_pub: true) // 26
1276 t.register_sym(
1277 kind: .float_literal
1278 name: 'float literal'
1279 cname: 'float_literal'
1280 mod: 'builtin'
1281 is_pub: true
1282 ) // 27
1283 t.register_sym(
1284 kind: .int_literal
1285 name: 'int literal'
1286 cname: 'int_literal'
1287 mod: 'builtin'
1288 is_pub: true
1289 ) // 28
1290 t.register_sym(
1291 kind: .thread
1292 name: 'thread'
1293 cname: '__v_thread'
1294 mod: 'builtin'
1295 info: Thread{
1296 return_type: void_type
1297 }
1298 is_pub: true
1299 ) // 29
1300 t.register_sym(
1301 kind: .interface
1302 name: 'IError'
1303 cname: 'IError'
1304 mod: 'builtin'
1305 is_builtin: true
1306 is_pub: true
1307 ) // 30
1308 t.register_sym(kind: .voidptr, name: 'nil', cname: 'voidptr', mod: 'builtin', is_pub: true) // 31
1309}
1310
1311@[inline]
1312pub fn (t &TypeSymbol) is_pointer() bool {
1313 return t.kind in [.byteptr, .charptr, .voidptr]
1314}
1315
1316@[inline]
1317pub fn (t &TypeSymbol) is_int() bool {
1318 res := t.kind in [.i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize,
1319 .int_literal, .rune]
1320 if !res && t.kind == .alias {
1321 return (t.info as Alias).parent_type.is_int()
1322 }
1323 return res
1324}
1325
1326@[inline]
1327pub fn (t &TypeSymbol) is_float() bool {
1328 return t.kind in [.f32, .f64, .float_literal]
1329}
1330
1331@[inline]
1332pub fn (t &TypeSymbol) is_string() bool {
1333 return t.kind == .string
1334}
1335
1336@[inline]
1337pub fn (t &TypeSymbol) is_number() bool {
1338 return t.is_int() || t.is_float()
1339}
1340
1341@[inline]
1342pub fn (t &TypeSymbol) is_bool() bool {
1343 return t.kind == .bool
1344}
1345
1346@[inline]
1347pub fn (t &TypeSymbol) is_primitive() bool {
1348 return t.is_number() || t.is_pointer() || t.is_string() || t.is_bool()
1349}
1350
1351@[inline]
1352pub fn (t &TypeSymbol) is_builtin() bool {
1353 return t.mod == 'builtin'
1354}
1355
1356// type_size returns the size and alignment (in bytes) of `typ`, similarly to C's `sizeof()` and `alignof()`.
1357pub fn (t &Table) type_size(typ Type) (int, int) {
1358 if typ.has_option_or_result() {
1359 return t.type_size(error_type_idx)
1360 }
1361 if typ.nr_muls() > 0 {
1362 return t.pointer_size, t.pointer_size
1363 }
1364 mut sym := t.sym(typ)
1365 if sym.size != -1 {
1366 return sym.size, sym.align
1367 }
1368 mut size := 0
1369 mut align := 0
1370 match sym.kind {
1371 .placeholder, .void, .none, .generic_inst {}
1372 .voidptr, .byteptr, .charptr, .function, .usize, .isize, .any, .thread, .chan {
1373 size = t.pointer_size
1374 align = t.pointer_size
1375 }
1376 .i8, .u8, .char, .bool {
1377 size = 1
1378 align = 1
1379 }
1380 .i16, .u16 {
1381 size = 2
1382 align = 2
1383 }
1384 .i32, .u32, .rune, .f32, .enum {
1385 size = 4
1386 align = 4
1387 }
1388 .int {
1389 $if new_int ? && x64 {
1390 size = 8
1391 align = 8
1392 } $else {
1393 size = 4
1394 align = 4
1395 }
1396 }
1397 .i64, .u64, .int_literal, .f64, .float_literal {
1398 size = 8
1399 align = 8
1400 }
1401 .alias {
1402 size, align = t.type_size((sym.info as Alias).parent_type)
1403 }
1404 .struct {
1405 info := sym.info as Struct
1406 is_packed := info.attrs.contains('packed')
1407 if info.is_union {
1408 mut max_alignment := if is_packed { 1 } else { 0 }
1409 mut max_size := 0
1410 for field in info.fields {
1411 field_size, alignment := t.type_size(field.typ)
1412 if field_size > max_size {
1413 max_size = field_size
1414 }
1415 if !is_packed && alignment > max_alignment {
1416 max_alignment = alignment
1417 }
1418 }
1419 if is_packed {
1420 size = max_size
1421 align = 1
1422 } else {
1423 size = round_up(max_size, max_alignment)
1424 align = max_alignment
1425 }
1426 } else {
1427 mut max_alignment := if is_packed { 1 } else { 0 }
1428 mut total_size := 0
1429 for field in info.fields {
1430 field_size, alignment := t.type_size(field.typ)
1431 if is_packed {
1432 total_size += field_size
1433 } else {
1434 if alignment > max_alignment {
1435 max_alignment = alignment
1436 }
1437 total_size = round_up(total_size, alignment) + field_size
1438 }
1439 }
1440 if is_packed {
1441 size = total_size
1442 align = 1
1443 } else {
1444 size = round_up(total_size, max_alignment)
1445 align = max_alignment
1446 }
1447 }
1448 }
1449 .string, .multi_return {
1450 mut max_alignment := 0
1451 mut total_size := 0
1452 types := if sym.kind == .string {
1453 (sym.info as Struct).fields.map(it.typ)
1454 } else {
1455 (sym.info as MultiReturn).types
1456 }
1457 for ftyp in types {
1458 field_size, alignment := t.type_size(ftyp)
1459 if alignment > max_alignment {
1460 max_alignment = alignment
1461 }
1462 total_size = round_up(total_size, alignment) + field_size
1463 }
1464 size = round_up(total_size, max_alignment)
1465 align = max_alignment
1466 }
1467 .sum_type, .interface, .aggregate {
1468 match mut sym.info {
1469 SumType, Aggregate {
1470 size = (sym.info.fields.len + 2) * t.pointer_size
1471 align = t.pointer_size
1472 }
1473 Interface {
1474 interface_header_size := round_up(t.pointer_size + 4, t.pointer_size) +
1475 t.pointer_size
1476 size = interface_header_size + sym.info.fields.len * t.pointer_size
1477 align = t.pointer_size
1478 for etyp in sym.info.embeds {
1479 esize, _ := t.type_size(etyp)
1480 size += esize - interface_header_size
1481 }
1482 }
1483 else {
1484 // unreachable
1485 }
1486 }
1487 }
1488 .array_fixed {
1489 info := sym.info as ArrayFixed
1490 elem_size, elem_align := t.type_size(info.elem_type)
1491 size = info.size * elem_size
1492 align = elem_align
1493 }
1494 // TODO: hardcoded:
1495 .map {
1496 size = if t.pointer_size == 8 {
1497 $if new_int ? && x64 { 144 } $else { 120 }
1498 } else {
1499 80
1500 }
1501 align = t.pointer_size
1502 }
1503 .array {
1504 size = if t.pointer_size == 8 {
1505 $if new_int ? && x64 { 48 } $else { 32 }
1506 } else {
1507 24
1508 }
1509 align = t.pointer_size
1510 }
1511 }
1512
1513 sym.size = size
1514 sym.align = align
1515 return size, align
1516}
1517
1518// round_up rounds the number `n` up to the next multiple `multiple`.
1519// Note: `multiple` must be a power of 2.
1520@[inline]
1521fn round_up(n int, multiple int) int {
1522 return (n + multiple - 1) & -multiple
1523}
1524
1525// for debugging/errors only, perf is not an issue
1526pub fn (k Kind) str() string {
1527 return match k {
1528 .placeholder { 'placeholder' }
1529 .void { 'void' }
1530 .voidptr { 'voidptr' }
1531 .charptr { 'charptr' }
1532 .byteptr { 'byteptr' }
1533 .struct { 'struct' }
1534 .int { 'int' }
1535 .i8 { 'i8' }
1536 .i16 { 'i16' }
1537 .i32 { 'i32' }
1538 .i64 { 'i64' }
1539 .isize { 'isize' }
1540 .u8 { 'u8' }
1541 .u16 { 'u16' }
1542 .u32 { 'u32' }
1543 .u64 { 'u64' }
1544 .usize { 'usize' }
1545 .int_literal { 'int_literal' }
1546 .f32 { 'f32' }
1547 .f64 { 'f64' }
1548 .float_literal { 'float_literal' }
1549 .string { 'string' }
1550 .char { 'char' }
1551 .bool { 'bool' }
1552 .none { 'none' }
1553 .array { 'array' }
1554 .array_fixed { 'array_fixed' }
1555 .map { 'map' }
1556 .chan { 'chan' }
1557 .multi_return { 'multi_return' }
1558 .sum_type { 'sum_type' }
1559 .alias { 'alias' }
1560 .enum { 'enum' }
1561 .any { 'any' }
1562 .function { 'function' }
1563 .interface { 'interface' }
1564 .generic_inst { 'generic_inst' }
1565 .rune { 'rune' }
1566 .aggregate { 'aggregate' }
1567 .thread { 'thread' }
1568 }
1569}
1570
1571pub fn (kinds []Kind) str() string {
1572 mut kinds_str := ''
1573 for i, k in kinds {
1574 kinds_str += k.str()
1575 if i < kinds.len - 1 {
1576 kinds_str += '_'
1577 }
1578 }
1579 return kinds_str
1580}
1581
1582// human readable type name, also used by vfmt
1583pub fn (t &Table) type_to_str(typ Type) string {
1584 return t.type_to_str_using_aliases(typ, map[string]string{})
1585}
1586
1587// type name in code (for builtin)
1588pub fn (t &Table) type_to_code(typ Type) string {
1589 match typ {
1590 int_literal_type, float_literal_type { return t.sym(typ).kind.str() }
1591 else { return t.type_to_str_using_aliases(typ, map[string]string{}) }
1592 }
1593}
1594
1595// clean type name from generics form. From Type[int] -> Type
1596pub fn (t &Table) clean_generics_type_str(typ Type) string {
1597 result := t.type_to_str(typ)
1598 return result.all_before('[')
1599}
1600
1601// strip_extra_struct_types removes the `<generic names>` from the
1602// complete qualified name and keep the `[concrete names]`, For example:
1603// `main.Foo<T, <T, U>>[int, [int, string]]` -> `main.Foo[int, [int, string]]`
1604// `main.Foo<T>[int] -> main.Foo[int]`
1605fn strip_extra_struct_types(name string) string {
1606 mut start := 0
1607 mut is_start := false
1608 mut nested_count := 0
1609 mut strips := []string{}
1610
1611 for i, ch in name {
1612 if ch == `<` {
1613 if is_start {
1614 nested_count++
1615 } else {
1616 is_start = true
1617 start = i
1618 }
1619 } else if ch == `>` {
1620 if nested_count > 0 {
1621 nested_count--
1622 } else {
1623 strips << name.substr(start, i + 1)
1624 strips << ''
1625 is_start = false
1626 }
1627 }
1628 }
1629 if strips.len > 0 {
1630 return name.replace_each(strips)
1631 } else {
1632 return name
1633 }
1634}
1635
1636// delete_cached_type_to_str remove a `type_to_str` from the cache
1637pub fn (t &Table) delete_cached_type_to_str(typ Type, import_aliases_len int) {
1638 cache_key := (u64(import_aliases_len) << 32) | u64(typ)
1639 mut mt := unsafe { &Table(t) }
1640 lock mt.cached_type_to_str {
1641 mt.cached_type_to_str.delete(cache_key)
1642 }
1643}
1644
1645// invalidate_type_to_str_cache clears every cached `type_to_str` result.
1646// vfmt calls this whenever it enters a new module (see Fmt.set_current_module_name),
1647// because strings that were memoized before the current module was known (e.g. while
1648// parsing, when `cmod_prefix` was still empty) keep stale `mod.` prefixes on types
1649// that actually belong to the current module. Those stale entries are otherwise reused
1650// when the signature of a `fn` typed struct field is rebuilt, producing output like
1651// `fn (s main.Struct) bool` instead of `fn (s Struct) bool` (issue #27475).
1652pub fn (t &Table) invalidate_type_to_str_cache() {
1653 mut mt := unsafe { &Table(t) }
1654 lock mt.cached_type_to_str {
1655 mt.cached_type_to_str.clear()
1656 }
1657}
1658
1659// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
1660pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
1661 cache_key := (u64(import_aliases.len) << 32) | u64(typ)
1662 mut mt := unsafe { &Table(t) }
1663 rlock mt.cached_type_to_str {
1664 if cached_res := mt.cached_type_to_str[cache_key] {
1665 return cached_res
1666 }
1667 }
1668 idx := typ.idx()
1669 if idx == 0 || idx >= t.type_symbols.len {
1670 return 'unknown'
1671 }
1672 sym := t.sym(typ)
1673 mut res := sym.name
1674 defer {
1675 lock mt.cached_type_to_str {
1676 // Note, that this relies on `res = value return res` if you want to return early!
1677 mt.cached_type_to_str[cache_key] = res
1678 }
1679 }
1680 // Note, that the duplication of code in some of the match branches here
1681 // is VERY deliberate. DO NOT be tempted to use `else {}` instead, because
1682 // that strongly reduces the usefulness of the exhaustive checking that
1683 // match does.
1684 // Using else{} here led to subtle bugs in vfmt discovered *months*
1685 // after the original code was written.
1686 // It is important that each case here is handled *explicitly* and
1687 // *clearly*, and that when a new kind is added, it should also be handled
1688 // explicitly.
1689 match sym.kind {
1690 .int_literal, .float_literal {}
1691 .i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .char,
1692 .rune, .string, .bool, .none, .voidptr, .byteptr, .charptr {
1693 // primitive types
1694 res = sym.kind.str()
1695 }
1696 .array {
1697 if typ == array_type {
1698 res = 'array'
1699 return res
1700 }
1701 if typ.has_flag(.variadic) {
1702 res = t.type_to_str_using_aliases(t.value_type(typ), import_aliases)
1703 } else {
1704 if sym.info is Array {
1705 elem_str := t.type_to_str_using_aliases(sym.info.elem_type, import_aliases)
1706 res = '[]${elem_str}'
1707 } else {
1708 res = 'array'
1709 }
1710 }
1711 }
1712 .array_fixed {
1713 info := sym.info as ArrayFixed
1714 elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
1715 if info.size_expr is EmptyExpr {
1716 res = '[${info.size}]${elem_str}'
1717 } else {
1718 size_str := t.fixed_array_size_expr_to_str(info.size_expr, import_aliases)
1719 res = '[${size_str}]${elem_str}'
1720 }
1721 }
1722 .chan {
1723 // TODO: currently the `chan` struct in builtin is not considered a struct but a chan
1724 if sym.mod != 'builtin' && sym.name != 'chan' {
1725 info := sym.info as Chan
1726 mut elem_type := info.elem_type
1727 mut mut_str := ''
1728 if info.is_mut {
1729 mut_str = 'mut '
1730 elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1)
1731 }
1732 elem_str := t.type_to_str_using_aliases(elem_type, import_aliases)
1733 res = 'chan ${mut_str}${elem_str}'
1734 }
1735 }
1736 .function {
1737 info := sym.info as FnType
1738 if !t.is_fmt {
1739 res = t.fn_signature(info.func, type_only: true)
1740 } else {
1741 if res.starts_with('fn (') {
1742 // fn foo ()
1743 has_names := info.func.params.any(it.name != '')
1744 res = t.fn_signature_using_aliases(info.func, import_aliases,
1745 type_only: !has_names
1746 )
1747 } else {
1748 // FnFoo
1749 res = t.shorten_user_defined_typenames(res, import_aliases)
1750 }
1751 }
1752 }
1753 .map {
1754 if int(typ) == map_type_idx {
1755 res = 'map'
1756 return res
1757 }
1758 info := sym.info as Map
1759 key_str := t.type_to_str_using_aliases(info.key_type, import_aliases)
1760 val_str := t.type_to_str_using_aliases(info.value_type, import_aliases)
1761 res = 'map[${key_str}]${val_str}'
1762 }
1763 .multi_return {
1764 res = '('
1765 info := sym.info as MultiReturn
1766 for i, typ2 in info.types {
1767 if i > 0 {
1768 res += ', '
1769 }
1770 res += t.type_to_str_using_aliases(typ2, import_aliases)
1771 }
1772 res += ')'
1773 }
1774 .struct, .interface, .sum_type {
1775 if typ.has_flag(.generic) {
1776 match sym.info {
1777 Struct, Interface, SumType {
1778 base_name := if sym.ngname == '' {
1779 strip_extra_struct_types(res)
1780 } else {
1781 sym.ngname
1782 }
1783 res = t.shorten_user_defined_typenames(base_name, import_aliases)
1784 generic_types := if sym.generic_types.len > 0 {
1785 sym.generic_types
1786 } else {
1787 sym.info.generic_types
1788 }
1789 res += '['
1790 for i, gtyp in generic_types {
1791 res += t.type_to_str_using_aliases(gtyp, import_aliases)
1792 if i != generic_types.len - 1 {
1793 res += ', '
1794 }
1795 }
1796 res += ']'
1797 }
1798 else {}
1799 }
1800 } else if sym.info is SumType && (sym.info as SumType).is_anon {
1801 variant_names := sym.info.variants.map(t.shorten_user_defined_typenames(t.sym(it).name,
1802 import_aliases))
1803 res = '${variant_names.join('|')}'
1804 } else {
1805 res = strip_extra_struct_types(res)
1806 res = t.shorten_user_defined_typenames(res, import_aliases)
1807 }
1808 }
1809 .generic_inst {
1810 info := sym.info as GenericInst
1811 res = t.shorten_user_defined_typenames(sym.name.all_before('['), import_aliases)
1812 res += '['
1813 for i, ctyp in info.concrete_types {
1814 res += t.type_to_str_using_aliases(ctyp, import_aliases)
1815 if i != info.concrete_types.len - 1 {
1816 res += ', '
1817 }
1818 }
1819 res += ']'
1820 }
1821 .void {
1822 if typ.has_flag(.option) {
1823 res = '?'
1824 return res
1825 }
1826 if typ.has_flag(.result) {
1827 res = '!'
1828 return res
1829 }
1830 res = 'void'
1831 return res
1832 }
1833 .thread {
1834 rtype := sym.thread_info().return_type
1835 if rtype != 1 {
1836 res = 'thread ' + t.type_to_str_using_aliases(rtype, import_aliases)
1837 }
1838 }
1839 .alias, .any, .placeholder, .enum {
1840 res = t.shorten_user_defined_typenames(res, import_aliases)
1841 /*
1842 if res.ends_with('.byte') {
1843 res = 'u8'
1844 } else {
1845 res = t.shorten_user_defined_typenames(res, import_aliases)
1846 }
1847 */
1848 }
1849 .aggregate {}
1850 }
1851
1852 mut nr_muls := typ.nr_muls()
1853 if typ.has_flag(.shared_f) {
1854 nr_muls--
1855 res = 'shared ' + res
1856 }
1857 if typ.has_flag(.atomic_f) {
1858 nr_muls--
1859 res = 'atomic ' + res
1860 }
1861 if nr_muls > 0 && !typ.has_flag(.variadic) {
1862 res = strings.repeat(`&`, nr_muls) + res
1863 }
1864 if typ.has_flag(.option) && res[0] != `?` {
1865 res = '?${res}'
1866 }
1867 if typ.has_flag(.result) {
1868 res = '!${res}'
1869 }
1870 return res
1871}
1872
1873// fixed_array_size_expr_to_str renders a fixed-array size expression preserving
1874// fully-qualified enum names. The default `Expr.str()` drops the enum name on
1875// `EnumVal` (returning `.value`), which produces invalid output when the size
1876// is something like `int(TestEnum._max)` because the enum type cannot be
1877// inferred from context inside the array brackets.
1878fn (t &Table) fixed_array_size_expr_to_str(expr Expr, import_aliases map[string]string) string {
1879 match expr {
1880 CastExpr {
1881 type_name := t.shorten_user_defined_typenames(t.type_to_str_using_aliases(expr.typ,
1882 import_aliases), import_aliases)
1883 return '${type_name}(${t.fixed_array_size_expr_to_str(expr.expr, import_aliases)})'
1884 }
1885 EnumVal {
1886 if expr.enum_name != '' {
1887 name := t.shorten_user_defined_typenames(expr.enum_name, import_aliases)
1888 return '${name}.${expr.val}'
1889 }
1890 return '.${expr.val}'
1891 }
1892 Ident {
1893 return t.shorten_user_defined_typenames(expr.name, import_aliases)
1894 }
1895 else {
1896 return expr.str()
1897 }
1898 }
1899}
1900
1901fn (t &Table) shorten_user_defined_typenames(original_name string, import_aliases map[string]string) string {
1902 if alias := import_aliases[original_name] {
1903 return alias
1904 }
1905 mut mod, mut typ := original_name.rsplit_once('.') or { return original_name }
1906 if !mod.contains('[') {
1907 if !t.is_fmt {
1908 mod = mod.all_after_last('.')
1909 }
1910 if alias := import_aliases[mod] {
1911 mod = alias
1912 } else if t.cmod_prefix != '' {
1913 if alias := import_aliases[t.cmod_prefix + mod] {
1914 mod = alias
1915 } else {
1916 // cur_mod.Type => Type
1917 return original_name.all_after(t.cmod_prefix)
1918 }
1919 }
1920 }
1921 // E.g.: []mod.Type => []Type; []mod.submod.Type => []submod.Type;
1922 // imported_mod.Type[mod.Result[[]mod.Token]] => imported_mod.Type[Result[[]Token]]
1923 if original_name.contains('[]') {
1924 if lhs, typ_after_lsbr := original_name.split_once('[') {
1925 if mod_, typ_before_lsbr := lhs.rsplit_once('.') {
1926 mod = mod_
1927 typ = '${typ_before_lsbr}[${typ_after_lsbr}'
1928 }
1929 }
1930 }
1931 return '${mod}.${typ}'
1932}
1933
1934@[minify]
1935pub struct FnSignatureOpts {
1936pub:
1937 skip_receiver bool
1938 type_only bool
1939}
1940
1941pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string {
1942 return t.fn_signature_using_aliases(func, map[string]string{}, opts)
1943}
1944
1945pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string]string, opts FnSignatureOpts) string {
1946 mut sb := strings.new_builder(20)
1947 if !opts.skip_receiver {
1948 sb.write_string('fn ')
1949 // TODO: write receiver
1950 }
1951 if !opts.type_only {
1952 sb.write_string(func.name)
1953 }
1954 sb.write_string('(')
1955 start := int(func.is_method && opts.skip_receiver)
1956 for i in start .. func.params.len {
1957 if i != start {
1958 sb.write_string(', ')
1959 }
1960 param := func.params[i]
1961 mut typ := param.typ
1962 if param.is_mut {
1963 if param.typ.is_ptr() {
1964 typ = typ.deref()
1965 }
1966 sb.write_string('mut ')
1967 }
1968 if !opts.type_only {
1969 sb.write_string(param.name)
1970 sb.write_string(' ')
1971 }
1972 styp := t.type_to_str_using_aliases(typ, import_aliases)
1973 if i == func.params.len - 1 && func.is_variadic && !func.is_c_variadic {
1974 sb.write_string('...')
1975 sb.write_string(styp)
1976 } else {
1977 sb.write_string(styp)
1978 }
1979 }
1980 if func.is_c_variadic {
1981 if func.params.len > 0 {
1982 sb.write_string(', ')
1983 }
1984 sb.write_string('...')
1985 }
1986 sb.write_string(')')
1987 if func.return_type != void_type {
1988 sb.write_string(' ')
1989 sb.write_string(t.type_to_str_using_aliases(func.return_type, import_aliases))
1990 }
1991 return sb.str()
1992}
1993
1994// symbol_name_except_generic return the name of the complete qualified name of the type,
1995// but without the generic parts. The potential `[]`, `&[][]` etc prefixes are kept. For example:
1996// `main.Abc[int]` -> `main.Abc`
1997// `main.Abc<T>[int]` -> `main.Abc<T>`
1998// `[]main.Abc<T>[int]` -> `[]main.Abc<T>`
1999// `&[][]main.Abc<T>[int]` -> `&[][]main.Abc<T>`
2000pub fn (t &TypeSymbol) symbol_name_except_generic() string {
2001 // &[]main.Abc[int], [][]main.Abc[int]...
2002 mut prefix := ''
2003 mut name := t.name
2004 for i, ch in t.name {
2005 if ch in [`&`, `[`, `]`] {
2006 continue
2007 }
2008 if i > 0 {
2009 prefix = t.name[..i]
2010 name = t.name[i..]
2011 }
2012 break
2013 }
2014 // main.Abc[int]
2015 // remove generic part from name
2016 // main.Abc[int] => main.Abc
2017 if name.contains('[') {
2018 name = name.all_before('[')
2019 }
2020 return prefix + name
2021}
2022
2023// embed_name return the pure name of the complete qualified name of the type,
2024// without the generic parts, concrete parts and mod parts. For example:
2025// `main.Abc[int]` -> `Abc`
2026// `main.Abc<T>[int]` -> `Abc`
2027pub fn (t &TypeSymbol) embed_name() string {
2028 if t.name.contains('<') {
2029 // Abc<T>[int] => Abc
2030 // main.Abc<T>[main.Enum] => Abc
2031 return t.name.split('<')[0].split('.').last()
2032 } else if t.name.contains('[') {
2033 // Abc[int] => Abc
2034 // main.Abc[main.Enum] => Abc
2035 return t.name.split('[')[0].split('.').last()
2036 } else {
2037 // main.Abc => Abc
2038 return t.name.split('.').last()
2039 }
2040}
2041
2042pub fn (t &TypeSymbol) has_method(name string) bool {
2043 for mut method in unsafe { t.methods } {
2044 if method.name.len == name.len && method.name == name {
2045 return true
2046 }
2047 }
2048 return false
2049}
2050
2051pub fn (t &TypeSymbol) has_method_with_generic_parent(name string) bool {
2052 m := t.find_method_with_generic_parent(name) or { return false }
2053 return t.kind != .interface || !m.no_body
2054}
2055
2056pub fn (t &TypeSymbol) find_method(name string) ?Fn {
2057 for mut method in unsafe { t.methods } {
2058 if method.name.len == name.len && method.name == name {
2059 return method
2060 }
2061 }
2062 return none
2063}
2064
2065struct ReceiverGenericTypes {
2066 parent_idx int
2067 types []Type
2068}
2069
2070const max_receiver_generic_pattern_depth = 64
2071
2072pub struct StructuredReceiverMethod {
2073pub:
2074 method Fn
2075 concrete_types []Type
2076}
2077
2078fn (t &Table) receiver_generic_types(typ Type) ?ReceiverGenericTypes {
2079 idx := typ.clear_flag(.generic).idx()
2080 if idx == 0 || idx >= t.type_symbols.len {
2081 return none
2082 }
2083 sym := t.sym(typ)
2084 match sym.info {
2085 Struct {
2086 if sym.info.concrete_types.len > 0 {
2087 parent_idx := if sym.info.parent_type != 0 {
2088 sym.info.parent_type.clear_flag(.generic).idx()
2089 } else if sym.parent_idx != 0 {
2090 sym.parent_idx
2091 } else {
2092 sym.idx
2093 }
2094 return ReceiverGenericTypes{
2095 parent_idx: parent_idx
2096 types: sym.info.concrete_types.clone()
2097 }
2098 }
2099 if sym.generic_types.len > 0 {
2100 parent_idx := if sym.parent_idx != 0 { sym.parent_idx } else { sym.idx }
2101 return ReceiverGenericTypes{
2102 parent_idx: parent_idx
2103 types: sym.generic_types.clone()
2104 }
2105 }
2106 if sym.info.generic_types.len > 0 {
2107 return ReceiverGenericTypes{
2108 parent_idx: sym.idx
2109 types: sym.info.generic_types.clone()
2110 }
2111 }
2112 }
2113 Interface {
2114 if sym.info.concrete_types.len > 0 {
2115 parent_idx := if sym.info.parent_type != 0 {
2116 sym.info.parent_type.clear_flag(.generic).idx()
2117 } else if sym.parent_idx != 0 {
2118 sym.parent_idx
2119 } else {
2120 sym.idx
2121 }
2122 return ReceiverGenericTypes{
2123 parent_idx: parent_idx
2124 types: sym.info.concrete_types.clone()
2125 }
2126 }
2127 if sym.generic_types.len > 0 {
2128 parent_idx := if sym.parent_idx != 0 { sym.parent_idx } else { sym.idx }
2129 return ReceiverGenericTypes{
2130 parent_idx: parent_idx
2131 types: sym.generic_types.clone()
2132 }
2133 }
2134 if sym.info.generic_types.len > 0 {
2135 return ReceiverGenericTypes{
2136 parent_idx: sym.idx
2137 types: sym.info.generic_types.clone()
2138 }
2139 }
2140 }
2141 SumType {
2142 if sym.info.concrete_types.len > 0 {
2143 parent_idx := if sym.info.parent_type != 0 {
2144 sym.info.parent_type.clear_flag(.generic).idx()
2145 } else if sym.parent_idx != 0 {
2146 sym.parent_idx
2147 } else {
2148 sym.idx
2149 }
2150 return ReceiverGenericTypes{
2151 parent_idx: parent_idx
2152 types: sym.info.concrete_types.clone()
2153 }
2154 }
2155 if sym.generic_types.len > 0 {
2156 parent_idx := if sym.parent_idx != 0 { sym.parent_idx } else { sym.idx }
2157 return ReceiverGenericTypes{
2158 parent_idx: parent_idx
2159 types: sym.generic_types.clone()
2160 }
2161 }
2162 if sym.info.generic_types.len > 0 {
2163 return ReceiverGenericTypes{
2164 parent_idx: sym.idx
2165 types: sym.info.generic_types.clone()
2166 }
2167 }
2168 }
2169 GenericInst {
2170 return ReceiverGenericTypes{
2171 parent_idx: sym.info.parent_idx
2172 types: sym.info.concrete_types.clone()
2173 }
2174 }
2175 else {}
2176 }
2177
2178 return none
2179}
2180
2181fn (t &Table) actual_receiver_generic_types(typ Type) ?ReceiverGenericTypes {
2182 if receiver := t.receiver_generic_types(typ) {
2183 return receiver
2184 }
2185 sym := t.sym(typ)
2186 if sym.info is Alias {
2187 return t.receiver_generic_types(sym.info.parent_type.derive(typ))
2188 }
2189 return none
2190}
2191
2192pub fn (t &Table) find_alias_parent_exact_method(typ Type, name string) ?Fn {
2193 sym := t.sym(typ)
2194 if sym.info is Alias {
2195 parent_sym := t.sym(sym.info.parent_type.derive(typ))
2196 return parent_sym.find_method(name)
2197 }
2198 return none
2199}
2200
2201fn (t &Table) collect_receiver_generic_pattern_type_names(typ Type, mut names []string, depth int) bool {
2202 if typ == 0 || depth > max_receiver_generic_pattern_depth {
2203 return false
2204 }
2205 sym := t.sym(typ)
2206 if typ.has_flag(.generic) && sym.kind == .any {
2207 if sym.name !in names {
2208 names << sym.name
2209 }
2210 return true
2211 }
2212 next_depth := depth + 1
2213 match sym.info {
2214 Array {
2215 return t.collect_receiver_generic_pattern_type_names(sym.info.elem_type, mut names,
2216 next_depth)
2217 }
2218 Map {
2219 has_key_name := t.collect_receiver_generic_pattern_type_names(sym.info.key_type, mut
2220 names, next_depth)
2221 has_value_name := t.collect_receiver_generic_pattern_type_names(sym.info.value_type, mut
2222 names, next_depth)
2223 return has_key_name || has_value_name
2224 }
2225 else {}
2226 }
2227
2228 return false
2229}
2230
2231pub fn (t &Table) structured_receiver_generic_pattern_names(typ Type) []string {
2232 receiver := t.receiver_generic_types(typ) or { return []string{} }
2233 mut names := []string{}
2234 mut has_structured_pattern := false
2235 for receiver_type in receiver.types {
2236 sym := t.sym(receiver_type)
2237 match sym.info {
2238 Array, Map {
2239 if t.collect_receiver_generic_pattern_type_names(receiver_type, mut names, 0) {
2240 has_structured_pattern = true
2241 }
2242 }
2243 else {
2244 t.collect_receiver_generic_pattern_type_names(receiver_type, mut names, 0)
2245 }
2246 }
2247 }
2248 if !has_structured_pattern {
2249 return []string{}
2250 }
2251 return names
2252}
2253
2254fn (t &Table) receiver_type_is_structured_generic_pattern(typ Type) bool {
2255 return t.structured_receiver_generic_pattern_names(typ).len > 0
2256}
2257
2258fn (t &Table) bind_receiver_generic_type(pattern Type, actual Type, generic_names []string, mut bindings []Type) bool {
2259 return t.bind_receiver_generic_type_with_depth(pattern, actual, generic_names, mut bindings, 0)
2260}
2261
2262fn (t &Table) bind_receiver_generic_type_with_depth(pattern Type, actual Type, generic_names []string, mut bindings []Type, depth int) bool {
2263 if depth > max_receiver_generic_pattern_depth {
2264 return false
2265 }
2266 pattern_sym := t.sym(pattern)
2267 if pattern.has_flag(.generic) && pattern_sym.name in generic_names {
2268 if actual.clear_flags() in voidptr_types {
2269 return false
2270 }
2271 idx := generic_names.index(pattern_sym.name)
2272 if idx < 0 || idx >= bindings.len {
2273 return false
2274 }
2275 if bindings[idx] == 0 {
2276 bindings[idx] = actual
2277 return true
2278 }
2279 return bindings[idx] == actual
2280 }
2281 actual_sym := t.sym(actual)
2282 match pattern_sym.info {
2283 Array {
2284 match actual_sym.info {
2285 Array {
2286 return t.bind_receiver_generic_type_with_depth(pattern_sym.info.elem_type,
2287 actual_sym.info.elem_type, generic_names, mut bindings, depth + 1)
2288 }
2289 else {
2290 return false
2291 }
2292 }
2293 }
2294 Map {
2295 match actual_sym.info {
2296 Map {
2297 if !t.bind_receiver_generic_type_with_depth(pattern_sym.info.key_type,
2298 actual_sym.info.key_type, generic_names, mut bindings, depth + 1) {
2299 return false
2300 }
2301 return t.bind_receiver_generic_type_with_depth(pattern_sym.info.value_type,
2302 actual_sym.info.value_type, generic_names, mut bindings, depth + 1)
2303 }
2304 else {
2305 return false
2306 }
2307 }
2308 }
2309 else {}
2310 }
2311
2312 return pattern == actual
2313}
2314
2315fn (t &Table) infer_generic_receiver_pattern_types(pattern_receiver Type, actual_receiver Type, generic_names []string) ?[]Type {
2316 pattern := t.receiver_generic_types(pattern_receiver)?
2317 actual := t.actual_receiver_generic_types(actual_receiver)?
2318 if pattern.parent_idx != actual.parent_idx || pattern.types.len != actual.types.len {
2319 return none
2320 }
2321 mut bindings := []Type{len: generic_names.len}
2322 for i, pattern_type in pattern.types {
2323 if !t.bind_receiver_generic_type(pattern_type, actual.types[i], generic_names, mut bindings) {
2324 return none
2325 }
2326 }
2327 for binding in bindings {
2328 if binding == 0 {
2329 return none
2330 }
2331 }
2332 return bindings
2333}
2334
2335fn (t &Table) receiver_generic_type_has_voidptr_binding(pattern Type, actual Type, generic_names []string) bool {
2336 return t.receiver_generic_type_has_voidptr_binding_with_depth(pattern, actual, generic_names, 0)
2337}
2338
2339fn (t &Table) receiver_generic_type_has_voidptr_binding_with_depth(pattern Type, actual Type, generic_names []string, depth int) bool {
2340 if depth > max_receiver_generic_pattern_depth {
2341 return false
2342 }
2343 pattern_sym := t.sym(pattern)
2344 if pattern.has_flag(.generic) && pattern_sym.name in generic_names {
2345 return actual.clear_flags() in voidptr_types
2346 }
2347 actual_sym := t.sym(actual)
2348 match pattern_sym.info {
2349 Array {
2350 if actual_sym.info is Array {
2351 return t.receiver_generic_type_has_voidptr_binding_with_depth(pattern_sym.info.elem_type,
2352 actual_sym.info.elem_type, generic_names, depth + 1)
2353 }
2354 }
2355 Map {
2356 match actual_sym.info {
2357 Map {
2358 if t.receiver_generic_type_has_voidptr_binding_with_depth(pattern_sym.info.key_type,
2359 actual_sym.info.key_type, generic_names, depth + 1)
2360 {
2361 return true
2362 }
2363 return t.receiver_generic_type_has_voidptr_binding_with_depth(pattern_sym.info.value_type,
2364 actual_sym.info.value_type, generic_names, depth + 1)
2365 }
2366 else {
2367 return false
2368 }
2369 }
2370 }
2371 else {}
2372 }
2373
2374 return false
2375}
2376
2377fn (t &Table) receiver_pattern_has_voidptr_binding(pattern_receiver Type, actual_receiver Type, generic_names []string) bool {
2378 pattern := t.receiver_generic_types(pattern_receiver) or { return false }
2379 actual := t.actual_receiver_generic_types(actual_receiver) or { return false }
2380 if pattern.parent_idx != actual.parent_idx || pattern.types.len != actual.types.len {
2381 return false
2382 }
2383 for i, pattern_type in pattern.types {
2384 if t.receiver_generic_type_has_voidptr_binding(pattern_type, actual.types[i], generic_names) {
2385 return true
2386 }
2387 }
2388 return false
2389}
2390
2391pub fn (table &Table) structured_receiver_method_rejects_voidptr(actual_type Type, name string) bool {
2392 actual := table.actual_receiver_generic_types(actual_type) or { return false }
2393 key := receiver_pattern_method_key(actual.parent_idx, name)
2394 methods := table.structured_receiver_methods[key] or { return false }
2395 for method in methods {
2396 receiver_generic_names :=
2397 table.structured_receiver_generic_pattern_names(method.receiver_type)
2398 if receiver_generic_names.len > 0
2399 && table.receiver_pattern_has_voidptr_binding(method.receiver_type, actual_type, receiver_generic_names) {
2400 return true
2401 }
2402 }
2403 return false
2404}
2405
2406pub fn (table &Table) find_structured_receiver_method_with_types(actual_type Type, name string) ?StructuredReceiverMethod {
2407 actual := table.actual_receiver_generic_types(actual_type) or { return none }
2408 key := receiver_pattern_method_key(actual.parent_idx, name)
2409 methods := table.structured_receiver_methods[key] or { return none }
2410 for method in methods {
2411 receiver_generic_names :=
2412 table.structured_receiver_generic_pattern_names(method.receiver_type)
2413 if receiver_generic_names.len == 0 || receiver_generic_names.len > method.generic_names.len
2414 || method.generic_names[..receiver_generic_names.len] != receiver_generic_names {
2415 continue
2416 }
2417 concrete_types := table.infer_generic_receiver_pattern_types(method.receiver_type,
2418 actual_type, receiver_generic_names) or { continue }
2419 return StructuredReceiverMethod{
2420 method: method
2421 concrete_types: concrete_types
2422 }
2423 }
2424 return none
2425}
2426
2427fn (table &Table) find_structured_receiver_method(actual_type Type, name string) ?Fn {
2428 structured_method := table.find_structured_receiver_method_with_types(actual_type, name)?
2429 return structured_method.method
2430}
2431
2432fn specialize_method_with_concrete_types(method Fn, generic_names []string, concrete_types []Type) Fn {
2433 mut table := global_table
2434 mut resolved := method
2435 return_sym := table.sym(resolved.return_type)
2436 if return_sym.kind in [.struct, .interface, .sum_type] {
2437 resolved.return_type = table.unwrap_generic_type(resolved.return_type, generic_names,
2438 concrete_types)
2439 } else if rt := table.convert_generic_type(resolved.return_type, generic_names, concrete_types) {
2440 resolved.return_type = rt
2441 }
2442 resolved.params = resolved.params.clone()
2443 for mut param in resolved.params {
2444 if pt := table.convert_generic_type(param.typ, generic_names, concrete_types) {
2445 param.typ = pt
2446 }
2447 }
2448 return resolved
2449}
2450
2451pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
2452 mut table := global_table
2453 mut generic_names := []string{}
2454 mut concrete_types := []Type{}
2455 mut generic_inst_parent_idx := 0
2456 match t.info {
2457 Struct, Interface, SumType {
2458 generic_names = t.info.generic_types.map(table.sym(it).name)
2459 if t.info.concrete_types.len == generic_names.len {
2460 concrete_types = t.info.concrete_types.clone()
2461 } else if t.generic_types.len == generic_names.len
2462 && t.generic_types != t.info.generic_types {
2463 concrete_types = t.generic_types.clone()
2464 }
2465 }
2466 GenericInst {
2467 generic_inst_parent_idx = t.info.parent_idx
2468 parent_sym := table.sym(new_type(generic_inst_parent_idx))
2469 match parent_sym.info {
2470 Struct, Interface, SumType {
2471 generic_names = parent_sym.info.generic_types.map(table.sym(it).name)
2472 concrete_types = t.info.concrete_types.clone()
2473 }
2474 FnType {
2475 generic_names = parent_sym.info.func.generic_names.clone()
2476 concrete_types = t.info.concrete_types.clone()
2477 }
2478 else {}
2479 }
2480 }
2481 else {}
2482 }
2483
2484 if m := t.find_method(name) {
2485 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
2486 return specialize_method_with_concrete_types(m, generic_names, concrete_types)
2487 }
2488 return m
2489 }
2490 if m := table.find_structured_receiver_method(new_type(t.idx), name) {
2491 return m
2492 }
2493 if generic_inst_parent_idx != 0 {
2494 mut psym := table.sym(new_type(generic_inst_parent_idx))
2495 for {
2496 if m := psym.find_method(name) {
2497 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
2498 return specialize_method_with_concrete_types(m, generic_names, concrete_types)
2499 }
2500 return m
2501 }
2502 if psym.parent_idx == 0 {
2503 break
2504 }
2505 psym = table.type_symbols[psym.parent_idx]
2506 }
2507 }
2508 match t.info {
2509 Struct, Interface, SumType {
2510 if t.info.parent_type.has_flag(.generic) {
2511 mut psym2 := table.sym(t.info.parent_type)
2512 for {
2513 if x := psym2.find_method(name) {
2514 match psym2.info {
2515 Struct, Interface, SumType {
2516 return specialize_method_with_concrete_types(x, generic_names,
2517 concrete_types)
2518 }
2519 else {}
2520 }
2521 }
2522 if psym2.parent_idx == 0 {
2523 break
2524 }
2525 psym2 = table.type_symbols[psym2.parent_idx]
2526 }
2527 }
2528 }
2529 else {}
2530 }
2531
2532 return none
2533}
2534
2535// is_js_compatible returns true if type can be converted to JS type and from JS type back to V type
2536pub fn (t &TypeSymbol) is_js_compatible() bool {
2537 mut table := global_table
2538 if t.kind == .void {
2539 return true
2540 }
2541 if t.kind == .function {
2542 return true
2543 }
2544 if t.language == .js || t.name.starts_with('JS.') {
2545 return true
2546 }
2547 match t.info {
2548 SumType {
2549 for variant in t.info.variants {
2550 sym := table.final_sym(variant)
2551 if !sym.is_js_compatible() {
2552 return false
2553 }
2554 }
2555 return true
2556 }
2557 else {
2558 return true
2559 }
2560 }
2561}
2562
2563pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
2564 mut has_str_method := false
2565 mut expects_ptr := false
2566 mut nr_args := 0
2567 if sym_str_method := t.find_method_with_generic_parent('str') {
2568 has_str_method = t.kind != .interface || !sym_str_method.no_body
2569 nr_args = sym_str_method.params.len
2570 if nr_args > 0 {
2571 expects_ptr = sym_str_method.params[0].typ.is_ptr()
2572 }
2573 } else {
2574 // C Struct which does not implement str() are passed as pointer to handle incomplete type
2575 expects_ptr = t.is_c_struct()
2576 }
2577 return has_str_method, expects_ptr, nr_args
2578}
2579
2580pub fn (t &TypeSymbol) find_field(name string) ?StructField {
2581 match t.info {
2582 Struct { return t.info.find_field(name) }
2583 Interface { return t.info.find_field(name) }
2584 SumType { return t.info.find_sum_type_field(name) }
2585 Aggregate { return t.info.find_field(name) }
2586 else { return none }
2587 }
2588}
2589
2590pub fn (t &TypeSymbol) has_field(name string) bool {
2591 t.find_field(name) or { return false }
2592
2593 return true
2594}
2595
2596fn (a &Aggregate) find_field(name string) ?StructField {
2597 for mut field in unsafe { a.fields } {
2598 if field.name.len == name.len && field.name == name {
2599 return field
2600 }
2601 }
2602 return none
2603}
2604
2605pub fn (i &Interface) find_field(name string) ?StructField {
2606 for mut field in unsafe { i.fields } {
2607 if field.name.len == name.len && field.name == name {
2608 return field
2609 }
2610 }
2611 return none
2612}
2613
2614pub fn (i &Interface) find_method(name string) ?Fn {
2615 for mut method in unsafe { i.methods } {
2616 if method.name.len == name.len && method.name == name {
2617 return method
2618 }
2619 }
2620 return none
2621}
2622
2623pub fn (i &Interface) has_method(name string) bool {
2624 for mut method in unsafe { i.methods } {
2625 if method.name.len == name.len && method.name == name {
2626 return true
2627 }
2628 }
2629 return false
2630}
2631
2632pub fn (s Struct) find_field(name string) ?StructField {
2633 for mut field in unsafe { s.fields } {
2634 if name.len == field.name.len && field.name == name {
2635 return field
2636 }
2637 }
2638 return none
2639}
2640
2641pub fn (s Struct) get_field(name string) StructField {
2642 if field := s.find_field(name) {
2643 return field
2644 }
2645 panic('unknown field `${name}`')
2646}
2647
2648pub fn (s &SumType) find_sum_type_field(name string) ?StructField {
2649 for mut field in unsafe { s.fields } {
2650 if field.name == name {
2651 return field
2652 }
2653 }
2654 return none
2655}
2656
2657// For the 'field does not exist or have the same type in all sumtype variants' error.
2658// To print all sumtype variants the developer has to fix.
2659pub fn (t &Table) find_missing_variants(s &SumType, field_name string) string {
2660 mut res := []string{cap: 5}
2661 for variant in s.variants {
2662 ts := t.sym(variant)
2663 if ts.kind != .struct {
2664 continue
2665 }
2666 mut found := false
2667 struct_info := ts.info as Struct
2668 for field in struct_info.fields {
2669 if field.name == field_name {
2670 found = true
2671 break
2672 }
2673 }
2674 if !found {
2675 res << ts.name
2676 }
2677 }
2678 // println('!!!!! field_name=${field_name}')
2679 // print_backtrace()
2680 // println(res)
2681 str := res.join(', ')
2682 return str.replace("'", '`')
2683}
2684
2685pub fn (i Interface) defines_method(name string) bool {
2686 if i.methods.any(it.name == name) {
2687 return true
2688 }
2689 if i.parent_type.has_flag(.generic) {
2690 parent_sym := global_table.sym(i.parent_type)
2691 parent_info := parent_sym.info as Interface
2692 if parent_info.methods.any(it.name == name) {
2693 return true
2694 }
2695 }
2696 return false
2697}
2698
2699pub fn (i Interface) get_methods() []string {
2700 if i.methods.len > 0 {
2701 return i.methods.map(it.name)
2702 }
2703 if i.parent_type.has_flag(.generic) {
2704 parent_sym := global_table.sym(i.parent_type)
2705 parent_info := parent_sym.info as Interface
2706 return parent_info.methods.map(it.name)
2707 }
2708 return []
2709}
2710
2711pub fn (t &TypeSymbol) get_methods() []Fn {
2712 mut methods := t.methods.filter(it.attrs.len == 0) // methods without attrs first
2713 mut methods_with_attrs := t.methods.filter(it.attrs.len > 0) // methods with attrs second
2714 mut existing_method_names := map[string]bool{}
2715 for method in t.methods {
2716 existing_method_names[method.name] = true
2717 }
2718 mut inherited_methods := []Fn{}
2719 match t.info {
2720 Struct, Interface, SumType {
2721 if t.info.parent_type.has_flag(.generic) {
2722 parent_sym := global_table.sym(t.info.parent_type)
2723 match parent_sym.info {
2724 Struct, Interface, SumType, Alias {
2725 inherited_methods = parent_sym.get_methods()
2726 }
2727 else {}
2728 }
2729 }
2730 }
2731 Alias {
2732 parent_sym := global_table.sym(t.info.parent_type)
2733 inherited_methods = parent_sym.get_methods()
2734 }
2735 else {}
2736 }
2737
2738 for method in inherited_methods {
2739 if method.name in existing_method_names {
2740 continue
2741 }
2742 existing_method_names[method.name] = true
2743 if method.attrs.len == 0 {
2744 methods << method
2745 } else {
2746 methods_with_attrs << method
2747 }
2748 }
2749 methods << methods_with_attrs
2750 return methods
2751}
2752