v2 / vlib / v / ast / types.v
2367 lines · 2200 sloc · 57.01 KB · fc8ef0e70cdb75713db4cc149df78cc1fb5e95fe
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// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type'
1646pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string {
1647 cache_key := (u64(import_aliases.len) << 32) | u64(typ)
1648 mut mt := unsafe { &Table(t) }
1649 rlock mt.cached_type_to_str {
1650 if cached_res := mt.cached_type_to_str[cache_key] {
1651 return cached_res
1652 }
1653 }
1654 idx := typ.idx()
1655 if idx == 0 || idx >= t.type_symbols.len {
1656 return 'unknown'
1657 }
1658 sym := t.sym(typ)
1659 mut res := sym.name
1660 defer {
1661 lock mt.cached_type_to_str {
1662 // Note, that this relies on `res = value return res` if you want to return early!
1663 mt.cached_type_to_str[cache_key] = res
1664 }
1665 }
1666 // Note, that the duplication of code in some of the match branches here
1667 // is VERY deliberate. DO NOT be tempted to use `else {}` instead, because
1668 // that strongly reduces the usefulness of the exhaustive checking that
1669 // match does.
1670 // Using else{} here led to subtle bugs in vfmt discovered *months*
1671 // after the original code was written.
1672 // It is important that each case here is handled *explicitly* and
1673 // *clearly*, and that when a new kind is added, it should also be handled
1674 // explicitly.
1675 match sym.kind {
1676 .int_literal, .float_literal {}
1677 .i8, .i16, .i32, .int, .i64, .isize, .u8, .u16, .u32, .u64, .usize, .f32, .f64, .char,
1678 .rune, .string, .bool, .none, .voidptr, .byteptr, .charptr {
1679 // primitive types
1680 res = sym.kind.str()
1681 }
1682 .array {
1683 if typ == array_type {
1684 res = 'array'
1685 return res
1686 }
1687 if typ.has_flag(.variadic) {
1688 res = t.type_to_str_using_aliases(t.value_type(typ), import_aliases)
1689 } else {
1690 if sym.info is Array {
1691 elem_str := t.type_to_str_using_aliases(sym.info.elem_type, import_aliases)
1692 res = '[]${elem_str}'
1693 } else {
1694 res = 'array'
1695 }
1696 }
1697 }
1698 .array_fixed {
1699 info := sym.info as ArrayFixed
1700 elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases)
1701 if info.size_expr is EmptyExpr {
1702 res = '[${info.size}]${elem_str}'
1703 } else {
1704 size_str := t.fixed_array_size_expr_to_str(info.size_expr, import_aliases)
1705 res = '[${size_str}]${elem_str}'
1706 }
1707 }
1708 .chan {
1709 // TODO: currently the `chan` struct in builtin is not considered a struct but a chan
1710 if sym.mod != 'builtin' && sym.name != 'chan' {
1711 info := sym.info as Chan
1712 mut elem_type := info.elem_type
1713 mut mut_str := ''
1714 if info.is_mut {
1715 mut_str = 'mut '
1716 elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1)
1717 }
1718 elem_str := t.type_to_str_using_aliases(elem_type, import_aliases)
1719 res = 'chan ${mut_str}${elem_str}'
1720 }
1721 }
1722 .function {
1723 info := sym.info as FnType
1724 if !t.is_fmt {
1725 res = t.fn_signature(info.func, type_only: true)
1726 } else {
1727 if res.starts_with('fn (') {
1728 // fn foo ()
1729 has_names := info.func.params.any(it.name != '')
1730 res = t.fn_signature_using_aliases(info.func, import_aliases,
1731 type_only: !has_names
1732 )
1733 } else {
1734 // FnFoo
1735 res = t.shorten_user_defined_typenames(res, import_aliases)
1736 }
1737 }
1738 }
1739 .map {
1740 if int(typ) == map_type_idx {
1741 res = 'map'
1742 return res
1743 }
1744 info := sym.info as Map
1745 key_str := t.type_to_str_using_aliases(info.key_type, import_aliases)
1746 val_str := t.type_to_str_using_aliases(info.value_type, import_aliases)
1747 res = 'map[${key_str}]${val_str}'
1748 }
1749 .multi_return {
1750 res = '('
1751 info := sym.info as MultiReturn
1752 for i, typ2 in info.types {
1753 if i > 0 {
1754 res += ', '
1755 }
1756 res += t.type_to_str_using_aliases(typ2, import_aliases)
1757 }
1758 res += ')'
1759 }
1760 .struct, .interface, .sum_type {
1761 if typ.has_flag(.generic) {
1762 match sym.info {
1763 Struct, Interface, SumType {
1764 base_name := if sym.ngname == '' {
1765 strip_extra_struct_types(res)
1766 } else {
1767 sym.ngname
1768 }
1769 res = t.shorten_user_defined_typenames(base_name, import_aliases)
1770 generic_types := if sym.generic_types.len > 0 {
1771 sym.generic_types
1772 } else {
1773 sym.info.generic_types
1774 }
1775 res += '['
1776 for i, gtyp in generic_types {
1777 res += t.type_to_str_using_aliases(gtyp, import_aliases)
1778 if i != generic_types.len - 1 {
1779 res += ', '
1780 }
1781 }
1782 res += ']'
1783 }
1784 else {}
1785 }
1786 } else if sym.info is SumType && (sym.info as SumType).is_anon {
1787 variant_names := sym.info.variants.map(t.shorten_user_defined_typenames(t.sym(it).name,
1788 import_aliases))
1789 res = '${variant_names.join('|')}'
1790 } else {
1791 res = strip_extra_struct_types(res)
1792 res = t.shorten_user_defined_typenames(res, import_aliases)
1793 }
1794 }
1795 .generic_inst {
1796 info := sym.info as GenericInst
1797 res = t.shorten_user_defined_typenames(sym.name.all_before('['), import_aliases)
1798 res += '['
1799 for i, ctyp in info.concrete_types {
1800 res += t.type_to_str_using_aliases(ctyp, import_aliases)
1801 if i != info.concrete_types.len - 1 {
1802 res += ', '
1803 }
1804 }
1805 res += ']'
1806 }
1807 .void {
1808 if typ.has_flag(.option) {
1809 res = '?'
1810 return res
1811 }
1812 if typ.has_flag(.result) {
1813 res = '!'
1814 return res
1815 }
1816 res = 'void'
1817 return res
1818 }
1819 .thread {
1820 rtype := sym.thread_info().return_type
1821 if rtype != 1 {
1822 res = 'thread ' + t.type_to_str_using_aliases(rtype, import_aliases)
1823 }
1824 }
1825 .alias, .any, .placeholder, .enum {
1826 res = t.shorten_user_defined_typenames(res, import_aliases)
1827 /*
1828 if res.ends_with('.byte') {
1829 res = 'u8'
1830 } else {
1831 res = t.shorten_user_defined_typenames(res, import_aliases)
1832 }
1833 */
1834 }
1835 .aggregate {}
1836 }
1837
1838 mut nr_muls := typ.nr_muls()
1839 if typ.has_flag(.shared_f) {
1840 nr_muls--
1841 res = 'shared ' + res
1842 }
1843 if typ.has_flag(.atomic_f) {
1844 nr_muls--
1845 res = 'atomic ' + res
1846 }
1847 if nr_muls > 0 && !typ.has_flag(.variadic) {
1848 res = strings.repeat(`&`, nr_muls) + res
1849 }
1850 if typ.has_flag(.option) && res[0] != `?` {
1851 res = '?${res}'
1852 }
1853 if typ.has_flag(.result) {
1854 res = '!${res}'
1855 }
1856 return res
1857}
1858
1859// fixed_array_size_expr_to_str renders a fixed-array size expression preserving
1860// fully-qualified enum names. The default `Expr.str()` drops the enum name on
1861// `EnumVal` (returning `.value`), which produces invalid output when the size
1862// is something like `int(TestEnum._max)` because the enum type cannot be
1863// inferred from context inside the array brackets.
1864fn (t &Table) fixed_array_size_expr_to_str(expr Expr, import_aliases map[string]string) string {
1865 match expr {
1866 CastExpr {
1867 type_name := t.shorten_user_defined_typenames(t.type_to_str_using_aliases(expr.typ,
1868 import_aliases), import_aliases)
1869 return '${type_name}(${t.fixed_array_size_expr_to_str(expr.expr, import_aliases)})'
1870 }
1871 EnumVal {
1872 if expr.enum_name != '' {
1873 name := t.shorten_user_defined_typenames(expr.enum_name, import_aliases)
1874 return '${name}.${expr.val}'
1875 }
1876 return '.${expr.val}'
1877 }
1878 Ident {
1879 return t.shorten_user_defined_typenames(expr.name, import_aliases)
1880 }
1881 else {
1882 return expr.str()
1883 }
1884 }
1885}
1886
1887fn (t &Table) shorten_user_defined_typenames(original_name string, import_aliases map[string]string) string {
1888 if alias := import_aliases[original_name] {
1889 return alias
1890 }
1891 mut mod, mut typ := original_name.rsplit_once('.') or { return original_name }
1892 if !mod.contains('[') {
1893 if !t.is_fmt {
1894 mod = mod.all_after_last('.')
1895 }
1896 if alias := import_aliases[mod] {
1897 mod = alias
1898 } else if t.cmod_prefix != '' {
1899 if alias := import_aliases[t.cmod_prefix + mod] {
1900 mod = alias
1901 } else {
1902 // cur_mod.Type => Type
1903 return original_name.all_after(t.cmod_prefix)
1904 }
1905 }
1906 }
1907 // E.g.: []mod.Type => []Type; []mod.submod.Type => []submod.Type;
1908 // imported_mod.Type[mod.Result[[]mod.Token]] => imported_mod.Type[Result[[]Token]]
1909 if original_name.contains('[]') {
1910 if lhs, typ_after_lsbr := original_name.split_once('[') {
1911 if mod_, typ_before_lsbr := lhs.rsplit_once('.') {
1912 mod = mod_
1913 typ = '${typ_before_lsbr}[${typ_after_lsbr}'
1914 }
1915 }
1916 }
1917 return '${mod}.${typ}'
1918}
1919
1920@[minify]
1921pub struct FnSignatureOpts {
1922pub:
1923 skip_receiver bool
1924 type_only bool
1925}
1926
1927pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string {
1928 return t.fn_signature_using_aliases(func, map[string]string{}, opts)
1929}
1930
1931pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string]string, opts FnSignatureOpts) string {
1932 mut sb := strings.new_builder(20)
1933 if !opts.skip_receiver {
1934 sb.write_string('fn ')
1935 // TODO: write receiver
1936 }
1937 if !opts.type_only {
1938 sb.write_string(func.name)
1939 }
1940 sb.write_string('(')
1941 start := int(func.is_method && opts.skip_receiver)
1942 for i in start .. func.params.len {
1943 if i != start {
1944 sb.write_string(', ')
1945 }
1946 param := func.params[i]
1947 mut typ := param.typ
1948 if param.is_mut {
1949 if param.typ.is_ptr() {
1950 typ = typ.deref()
1951 }
1952 sb.write_string('mut ')
1953 }
1954 if !opts.type_only {
1955 sb.write_string(param.name)
1956 sb.write_string(' ')
1957 }
1958 styp := t.type_to_str_using_aliases(typ, import_aliases)
1959 if i == func.params.len - 1 && func.is_variadic && !func.is_c_variadic {
1960 sb.write_string('...')
1961 sb.write_string(styp)
1962 } else {
1963 sb.write_string(styp)
1964 }
1965 }
1966 if func.is_c_variadic {
1967 if func.params.len > 0 {
1968 sb.write_string(', ')
1969 }
1970 sb.write_string('...')
1971 }
1972 sb.write_string(')')
1973 if func.return_type != void_type {
1974 sb.write_string(' ')
1975 sb.write_string(t.type_to_str_using_aliases(func.return_type, import_aliases))
1976 }
1977 return sb.str()
1978}
1979
1980// symbol_name_except_generic return the name of the complete qualified name of the type,
1981// but without the generic parts. The potential `[]`, `&[][]` etc prefixes are kept. For example:
1982// `main.Abc[int]` -> `main.Abc`
1983// `main.Abc<T>[int]` -> `main.Abc<T>`
1984// `[]main.Abc<T>[int]` -> `[]main.Abc<T>`
1985// `&[][]main.Abc<T>[int]` -> `&[][]main.Abc<T>`
1986pub fn (t &TypeSymbol) symbol_name_except_generic() string {
1987 // &[]main.Abc[int], [][]main.Abc[int]...
1988 mut prefix := ''
1989 mut name := t.name
1990 for i, ch in t.name {
1991 if ch in [`&`, `[`, `]`] {
1992 continue
1993 }
1994 if i > 0 {
1995 prefix = t.name[..i]
1996 name = t.name[i..]
1997 }
1998 break
1999 }
2000 // main.Abc[int]
2001 // remove generic part from name
2002 // main.Abc[int] => main.Abc
2003 if name.contains('[') {
2004 name = name.all_before('[')
2005 }
2006 return prefix + name
2007}
2008
2009// embed_name return the pure name of the complete qualified name of the type,
2010// without the generic parts, concrete parts and mod parts. For example:
2011// `main.Abc[int]` -> `Abc`
2012// `main.Abc<T>[int]` -> `Abc`
2013pub fn (t &TypeSymbol) embed_name() string {
2014 if t.name.contains('<') {
2015 // Abc<T>[int] => Abc
2016 // main.Abc<T>[main.Enum] => Abc
2017 return t.name.split('<')[0].split('.').last()
2018 } else if t.name.contains('[') {
2019 // Abc[int] => Abc
2020 // main.Abc[main.Enum] => Abc
2021 return t.name.split('[')[0].split('.').last()
2022 } else {
2023 // main.Abc => Abc
2024 return t.name.split('.').last()
2025 }
2026}
2027
2028pub fn (t &TypeSymbol) has_method(name string) bool {
2029 for mut method in unsafe { t.methods } {
2030 if method.name.len == name.len && method.name == name {
2031 return true
2032 }
2033 }
2034 return false
2035}
2036
2037pub fn (t &TypeSymbol) has_method_with_generic_parent(name string) bool {
2038 m := t.find_method_with_generic_parent(name) or { return false }
2039 return t.kind != .interface || !m.no_body
2040}
2041
2042pub fn (t &TypeSymbol) find_method(name string) ?Fn {
2043 for mut method in unsafe { t.methods } {
2044 if method.name.len == name.len && method.name == name {
2045 return method
2046 }
2047 }
2048 return none
2049}
2050
2051fn specialize_method_with_concrete_types(method Fn, generic_names []string, concrete_types []Type) Fn {
2052 mut table := global_table
2053 mut resolved := method
2054 return_sym := table.sym(resolved.return_type)
2055 if return_sym.kind in [.struct, .interface, .sum_type] {
2056 resolved.return_type = table.unwrap_generic_type(resolved.return_type, generic_names,
2057 concrete_types)
2058 } else if rt := table.convert_generic_type(resolved.return_type, generic_names, concrete_types) {
2059 resolved.return_type = rt
2060 }
2061 resolved.params = resolved.params.clone()
2062 for mut param in resolved.params {
2063 if pt := table.convert_generic_type(param.typ, generic_names, concrete_types) {
2064 param.typ = pt
2065 }
2066 }
2067 return resolved
2068}
2069
2070pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn {
2071 mut table := global_table
2072 mut generic_names := []string{}
2073 mut concrete_types := []Type{}
2074 mut generic_inst_parent_idx := 0
2075 match t.info {
2076 Struct, Interface, SumType {
2077 generic_names = t.info.generic_types.map(table.sym(it).name)
2078 if t.info.concrete_types.len == generic_names.len {
2079 concrete_types = t.info.concrete_types.clone()
2080 } else if t.generic_types.len == generic_names.len
2081 && t.generic_types != t.info.generic_types {
2082 concrete_types = t.generic_types.clone()
2083 }
2084 }
2085 GenericInst {
2086 generic_inst_parent_idx = t.info.parent_idx
2087 parent_sym := table.sym(new_type(generic_inst_parent_idx))
2088 match parent_sym.info {
2089 Struct, Interface, SumType {
2090 generic_names = parent_sym.info.generic_types.map(table.sym(it).name)
2091 concrete_types = t.info.concrete_types.clone()
2092 }
2093 FnType {
2094 generic_names = parent_sym.info.func.generic_names.clone()
2095 concrete_types = t.info.concrete_types.clone()
2096 }
2097 else {}
2098 }
2099 }
2100 else {}
2101 }
2102
2103 if m := t.find_method(name) {
2104 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
2105 return specialize_method_with_concrete_types(m, generic_names, concrete_types)
2106 }
2107 return m
2108 }
2109 if generic_inst_parent_idx != 0 {
2110 mut psym := table.sym(new_type(generic_inst_parent_idx))
2111 for {
2112 if m := psym.find_method(name) {
2113 if generic_names.len == concrete_types.len && concrete_types.len > 0 {
2114 return specialize_method_with_concrete_types(m, generic_names, concrete_types)
2115 }
2116 return m
2117 }
2118 if psym.parent_idx == 0 {
2119 break
2120 }
2121 psym = table.type_symbols[psym.parent_idx]
2122 }
2123 }
2124 match t.info {
2125 Struct, Interface, SumType {
2126 if t.info.parent_type.has_flag(.generic) {
2127 mut psym2 := table.sym(t.info.parent_type)
2128 for {
2129 if x := psym2.find_method(name) {
2130 match psym2.info {
2131 Struct, Interface, SumType {
2132 return specialize_method_with_concrete_types(x, generic_names,
2133 concrete_types)
2134 }
2135 else {}
2136 }
2137 }
2138 if psym2.parent_idx == 0 {
2139 break
2140 }
2141 psym2 = table.type_symbols[psym2.parent_idx]
2142 }
2143 }
2144 }
2145 else {}
2146 }
2147
2148 return none
2149}
2150
2151// is_js_compatible returns true if type can be converted to JS type and from JS type back to V type
2152pub fn (t &TypeSymbol) is_js_compatible() bool {
2153 mut table := global_table
2154 if t.kind == .void {
2155 return true
2156 }
2157 if t.kind == .function {
2158 return true
2159 }
2160 if t.language == .js || t.name.starts_with('JS.') {
2161 return true
2162 }
2163 match t.info {
2164 SumType {
2165 for variant in t.info.variants {
2166 sym := table.final_sym(variant)
2167 if !sym.is_js_compatible() {
2168 return false
2169 }
2170 }
2171 return true
2172 }
2173 else {
2174 return true
2175 }
2176 }
2177}
2178
2179pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
2180 mut has_str_method := false
2181 mut expects_ptr := false
2182 mut nr_args := 0
2183 if sym_str_method := t.find_method_with_generic_parent('str') {
2184 has_str_method = t.kind != .interface || !sym_str_method.no_body
2185 nr_args = sym_str_method.params.len
2186 if nr_args > 0 {
2187 expects_ptr = sym_str_method.params[0].typ.is_ptr()
2188 }
2189 } else {
2190 // C Struct which does not implement str() are passed as pointer to handle incomplete type
2191 expects_ptr = t.is_c_struct()
2192 }
2193 return has_str_method, expects_ptr, nr_args
2194}
2195
2196pub fn (t &TypeSymbol) find_field(name string) ?StructField {
2197 match t.info {
2198 Struct { return t.info.find_field(name) }
2199 Interface { return t.info.find_field(name) }
2200 SumType { return t.info.find_sum_type_field(name) }
2201 Aggregate { return t.info.find_field(name) }
2202 else { return none }
2203 }
2204}
2205
2206pub fn (t &TypeSymbol) has_field(name string) bool {
2207 t.find_field(name) or { return false }
2208
2209 return true
2210}
2211
2212fn (a &Aggregate) find_field(name string) ?StructField {
2213 for mut field in unsafe { a.fields } {
2214 if field.name.len == name.len && field.name == name {
2215 return field
2216 }
2217 }
2218 return none
2219}
2220
2221pub fn (i &Interface) find_field(name string) ?StructField {
2222 for mut field in unsafe { i.fields } {
2223 if field.name.len == name.len && field.name == name {
2224 return field
2225 }
2226 }
2227 return none
2228}
2229
2230pub fn (i &Interface) find_method(name string) ?Fn {
2231 for mut method in unsafe { i.methods } {
2232 if method.name.len == name.len && method.name == name {
2233 return method
2234 }
2235 }
2236 return none
2237}
2238
2239pub fn (i &Interface) has_method(name string) bool {
2240 for mut method in unsafe { i.methods } {
2241 if method.name.len == name.len && method.name == name {
2242 return true
2243 }
2244 }
2245 return false
2246}
2247
2248pub fn (s Struct) find_field(name string) ?StructField {
2249 for mut field in unsafe { s.fields } {
2250 if name.len == field.name.len && field.name == name {
2251 return field
2252 }
2253 }
2254 return none
2255}
2256
2257pub fn (s Struct) get_field(name string) StructField {
2258 if field := s.find_field(name) {
2259 return field
2260 }
2261 panic('unknown field `${name}`')
2262}
2263
2264pub fn (s &SumType) find_sum_type_field(name string) ?StructField {
2265 for mut field in unsafe { s.fields } {
2266 if field.name == name {
2267 return field
2268 }
2269 }
2270 return none
2271}
2272
2273// For the 'field does not exist or have the same type in all sumtype variants' error.
2274// To print all sumtype variants the developer has to fix.
2275pub fn (t &Table) find_missing_variants(s &SumType, field_name string) string {
2276 mut res := []string{cap: 5}
2277 for variant in s.variants {
2278 ts := t.sym(variant)
2279 if ts.kind != .struct {
2280 continue
2281 }
2282 mut found := false
2283 struct_info := ts.info as Struct
2284 for field in struct_info.fields {
2285 if field.name == field_name {
2286 found = true
2287 break
2288 }
2289 }
2290 if !found {
2291 res << ts.name
2292 }
2293 }
2294 // println('!!!!! field_name=${field_name}')
2295 // print_backtrace()
2296 // println(res)
2297 str := res.join(', ')
2298 return str.replace("'", '`')
2299}
2300
2301pub fn (i Interface) defines_method(name string) bool {
2302 if i.methods.any(it.name == name) {
2303 return true
2304 }
2305 if i.parent_type.has_flag(.generic) {
2306 parent_sym := global_table.sym(i.parent_type)
2307 parent_info := parent_sym.info as Interface
2308 if parent_info.methods.any(it.name == name) {
2309 return true
2310 }
2311 }
2312 return false
2313}
2314
2315pub fn (i Interface) get_methods() []string {
2316 if i.methods.len > 0 {
2317 return i.methods.map(it.name)
2318 }
2319 if i.parent_type.has_flag(.generic) {
2320 parent_sym := global_table.sym(i.parent_type)
2321 parent_info := parent_sym.info as Interface
2322 return parent_info.methods.map(it.name)
2323 }
2324 return []
2325}
2326
2327pub fn (t &TypeSymbol) get_methods() []Fn {
2328 mut methods := t.methods.filter(it.attrs.len == 0) // methods without attrs first
2329 mut methods_with_attrs := t.methods.filter(it.attrs.len > 0) // methods with attrs second
2330 mut existing_method_names := map[string]bool{}
2331 for method in t.methods {
2332 existing_method_names[method.name] = true
2333 }
2334 mut inherited_methods := []Fn{}
2335 match t.info {
2336 Struct, Interface, SumType {
2337 if t.info.parent_type.has_flag(.generic) {
2338 parent_sym := global_table.sym(t.info.parent_type)
2339 match parent_sym.info {
2340 Struct, Interface, SumType, Alias {
2341 inherited_methods = parent_sym.get_methods()
2342 }
2343 else {}
2344 }
2345 }
2346 }
2347 Alias {
2348 parent_sym := global_table.sym(t.info.parent_type)
2349 inherited_methods = parent_sym.get_methods()
2350 }
2351 else {}
2352 }
2353
2354 for method in inherited_methods {
2355 if method.name in existing_method_names {
2356 continue
2357 }
2358 existing_method_names[method.name] = true
2359 if method.attrs.len == 0 {
2360 methods << method
2361 } else {
2362 methods_with_attrs << method
2363 }
2364 }
2365 methods << methods_with_attrs
2366 return methods
2367}
2368