v / vlib / v2 / types / types.v
997 lines · 893 sloc · 18.28 KB · ddb021b9866c3b4523b746fa2f4c16a594f8bd89
Raw
1// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4module types
5
6import v2.ast
7
8// TODO: fix nested sum type in tinyv (like TS)
9pub type Type = Alias
10 | Array
11 | ArrayFixed
12 | Channel
13 | Char
14 | Enum
15 | FnType
16 | ISize
17 | Interface
18 | Map
19 | NamedType
20 | Nil
21 | None
22 | OptionType
23 | Pointer
24 | Primitive
25 | ResultType
26 | Rune
27 | String
28 | Struct
29 | SumType
30 | Thread
31 | Tuple
32 | USize
33 | Void
34
35@[flag]
36pub enum Properties {
37 boolean
38 float
39 integer
40 unsigned
41 untyped
42}
43
44// TODO: decide if kind will be used or just properties
45// enum PrimitiveKind {
46// bool_
47// i8_
48// i16_
49// // i32_
50// int_
51// i64_
52// // u8_
53// byte_
54// u16_
55// u32_
56// u64_
57// untyped_int
58// untyped_float
59// }
60
61pub struct Primitive {
62pub:
63 // kind PrimitiveKind
64 props Properties
65 size u8
66}
67
68pub struct Alias {
69pub:
70 name string
71pub mut:
72 base_type Type
73}
74
75pub struct Array {
76pub:
77 elem_type Type
78}
79
80pub struct ArrayFixed {
81pub:
82 len int
83 elem_type Type
84}
85
86pub struct Channel {
87pub:
88 elem_type ?Type
89}
90
91pub struct Enum {
92pub:
93 // TODO: store attributes enum or bool?
94 is_flag bool
95 name string
96 fields []Field
97 // fields map[string]Type
98}
99
100pub struct OptionType {
101pub:
102 base_type Type
103}
104
105struct Parameter {
106 name string
107 typ Type
108 is_mut bool
109}
110
111pub struct ResultType {
112pub:
113 base_type Type
114}
115
116@[flag]
117enum FnTypeAttribute {
118 empty
119 noreturn
120}
121
122fn FnTypeAttribute.from_ast_attributes(ast_attrs []ast.Attribute) FnTypeAttribute {
123 mut attrs := FnTypeAttribute.empty
124 for attr in ast_attrs {
125 match attr.name {
126 'noreturn' { attrs.set(.noreturn) }
127 else {}
128 }
129 }
130 return attrs
131}
132
133pub struct FnType {
134 // generic_params []NamedType // T ,Y
135 generic_params []string // T ,Y
136 // TODO: save in checker.env? or gere?
137 // I think I prefer checker env
138 // generic_types []Types // int,int
139 params []Parameter
140 return_type ?Type
141 is_variadic bool
142 attributes FnTypeAttribute
143mut:
144 is_mut_receiver bool
145 generic_types []map[string]Type
146 // scope was originally used for deferred type checking
147 // but its better if we dont need it here, although it may
148 // be meeded for soething later im not thinking of??
149 // scope &Scope
150}
151
152// get_return_type returns the function's return type, or none if void
153pub fn (f &FnType) get_return_type() ?Type {
154 return f.return_type
155}
156
157// get_param_types returns function parameter types in declaration order.
158pub fn (f &FnType) get_param_types() []Type {
159 mut param_types := []Type{cap: f.params.len}
160 for param in f.params {
161 param_types << param.typ
162 }
163 return param_types
164}
165
166// get_param_names returns function parameter names in declaration order.
167pub fn (f &FnType) get_param_names() []string {
168 mut names := []string{cap: f.params.len}
169 for param in f.params {
170 names << param.name
171 }
172 return names
173}
174
175// get_generic_params returns the declared generic parameter names.
176pub fn (f &FnType) get_generic_params() []string {
177 return f.generic_params.clone()
178}
179
180// is_variadic_fn reports whether this function type was declared variadic.
181pub fn (f &FnType) is_variadic_fn() bool {
182 return f.is_variadic
183}
184
185// get_generic_types returns the concrete generic instantiations inferred for this function.
186pub fn (f &FnType) get_generic_types() []map[string]Type {
187 mut out := []map[string]Type{cap: f.generic_types.len}
188 for generic_types in f.generic_types {
189 out << generic_types.clone()
190 }
191 return out
192}
193
194pub struct Interface {
195pub:
196 name string
197pub mut:
198 fields []Field
199 // fields map[string]Type
200 // TODO:
201}
202
203pub struct Map {
204pub:
205 key_type Type
206 value_type Type
207}
208
209pub struct Pointer {
210pub:
211 lifetime string
212pub mut:
213 base_type Type
214}
215
216// struct String {
217// }
218
219// Generic `T`, `Y` etc
220type NamedType = string
221
222// struct NamedType {
223// name string
224// }
225
226pub struct Field {
227pub:
228 name string
229 typ Type
230 default_expr ast.Expr = ast.empty_expr
231 attributes []ast.Attribute
232 is_public bool
233 is_mut bool
234 is_module_mut bool
235 is_interface_method bool
236 owner_module string
237}
238
239// struct Method {
240// name string
241// typ FnType
242// }
243
244pub struct Struct {
245pub:
246 name string
247 generic_params []string
248 implements []string
249pub mut:
250 embedded []Struct
251 // embedded []Type
252 fields []Field
253 // fields map[string]Type
254 // methods []Method
255 is_soa bool // @[soa] - Structure of Arrays layout for better cache performance
256}
257
258// TODO:
259fn (t Struct) str() string {
260 return 'Struct.str (${t.name()})'
261}
262
263// TODO: module not included in check
264fn (a Struct) == (b Struct) bool {
265 if a.name == b.name {
266 return true
267 }
268 return false
269}
270
271pub struct SumType {
272pub:
273 name string
274pub mut:
275 generic_params []string
276 variants []Type
277}
278
279fn (a SumType) == (b SumType) bool {
280 if a.name == b.name {
281 return true
282 }
283 return false
284}
285
286struct Thread {
287 elem_type ?Type
288 // return_type Type
289}
290
291struct Tuple {
292 types []Type
293}
294
295type Char = u8
296type ISize = u8
297type USize = u8
298type Rune = u8
299type String = u8
300
301// type IntLiteral = u8
302// type FloatLiteral = u8
303type Void = u8
304type Nil = u8
305type None = u8
306
307pub fn (t Type) base_type() Type {
308 match t {
309 // TODO: add base_type method
310 Alias {
311 if type_data_ptr_is_nil(t) {
312 return Type(void_)
313 }
314 return t.base_type
315 // should we fully resolve all aliases, or just one level here?
316 // return t.base_type.base_type()
317 }
318 OptionType, ResultType, Pointer {
319 if type_data_ptr_is_nil(t) {
320 return Type(void_)
321 }
322 return t.base_type
323 }
324 // TODO: why as I doing this instead of else? to make it easy to find unhandled cases?
325 Primitive, Array, ArrayFixed, Channel, Char, Enum, FnType, Interface, ISize, Map, Rune,
326 String, Struct, SumType, Thread, Tuple, USize, Void, Nil, None, NamedType {
327 return t
328 }
329 // else {
330 // return t
331 // }
332 }
333}
334
335pub fn (t Type) channel_elem_type() ?Type {
336 mut cur := t
337 for {
338 match cur {
339 Alias {
340 cur = Type(cur).base_type()
341 }
342 Pointer {
343 cur = Type(cur).base_type()
344 }
345 Channel {
346 channel_type := cur as Channel
347 if elem_type := channel_type.elem_type {
348 return elem_type
349 }
350 return none
351 }
352 else {
353 return none
354 }
355 }
356 }
357 return none
358}
359
360fn type_data_ptr_is_nil(t Type) bool {
361 return !type_has_valid_payload(t)
362}
363
364// Safely unwrap all Alias layers, guarding against null data pointers
365// from ARM64 codegen corruption.
366pub fn resolve_alias(t Type) Type {
367 mut cur := t
368 for cur is Alias {
369 if type_data_ptr_is_nil(cur) {
370 return Type(void_)
371 }
372 cur = (cur as Alias).base_type
373 }
374 return cur
375}
376
377// return the key type used with for in loops
378pub fn (t Type) key_type() Type {
379 match t {
380 Alias {
381 if type_data_ptr_is_nil(t) {
382 return int_
383 }
384 return t.base_type.key_type()
385 }
386 Map {
387 return t.key_type
388 }
389 Pointer {
390 if type_data_ptr_is_nil(t) {
391 return int_
392 }
393 return t.base_type.key_type()
394 }
395 // TODO: struct here is 'struct string', need to fix this.
396 // we could use an alias? remove once fixed.
397 // Array, ArrayFixed, String, Struct { return int_ }
398 // else { panic('TODO: should never be called on ${t.type_name()}') }
399 // TODO: see checker ForStmt -> ForInStmt when value is pointer
400 else {
401 return int_
402 }
403 }
404}
405
406// return the value type used with for in loops
407pub fn (t Type) value_type() Type {
408 return value_type_with_depth(t, 0)
409}
410
411fn value_type_with_depth(t Type, depth int) Type {
412 if depth > 64 {
413 return t.base_type()
414 }
415 if type_data_ptr_is_nil(t) {
416 return Type(void_)
417 }
418 match t {
419 Alias {
420 return value_type_with_depth(t.base_type, depth + 1)
421 }
422 Array, ArrayFixed {
423 return t.elem_type
424 }
425 Channel {
426 if elem_type := t.elem_type {
427 return elem_type
428 }
429 return Type(empty_channel())
430 } // TODO: ?
431 Map {
432 return t.value_type
433 }
434 Pointer {
435 if t.base_type is String {
436 // `&string` is used as pointer-to-first-string in several builtin APIs.
437 // Indexing it should yield `string`, not `u8`.
438 return string_
439 }
440 if t.base_type is Struct
441 && (t.base_type.name == 'string' || t.base_type.name.ends_with('__string')) {
442 return string_
443 }
444 return value_type_with_depth(t.base_type, depth + 1)
445 }
446 Struct {
447 return Type(t)
448 }
449 String {
450 return u8_
451 }
452 Thread {
453 if elem_type := t.elem_type {
454 return elem_type
455 }
456 return Type(empty_thread())
457 } // TODO: ?
458 OptionType, ResultType {
459 return value_type_with_depth(t.base_type, depth + 1)
460 }
461 else {
462 return t.base_type()
463 }
464 }
465}
466
467// converts untyped constant / literal to its default type
468// if is already typed then it returns itself
469fn (t Type) typed_default() Type {
470 // this handles int & float
471 if t is Primitive && t.is_number_literal() {
472 mut concrete_props := t.props
473 concrete_props.clear(Properties.untyped)
474 // TODO: platform dependant size - see universe
475 size := u8(if t.props.has(Properties.float) { 64 } else { 0 })
476 return Type(Primitive{
477 props: concrete_props
478 size: size
479 })
480 }
481 return t
482}
483
484// unwraps option or result
485fn (t Type) unwrap() Type {
486 match t {
487 OptionType, ResultType {
488 return t.base_type
489 }
490 else {
491 return t
492 }
493 }
494}
495
496fn (t Type) ref() Pointer {
497 return Pointer{
498 base_type: t
499 }
500}
501
502fn (t Type) deref() Type {
503 if t is Pointer {
504 return t.base_type
505 }
506 panic('Type.deref(): ${t.name()} is not a pointer')
507}
508
509// TODO:
510fn (t Type) is_compatible_with(t2 Type) bool {
511 if t == t2 {
512 return true
513 }
514 // Unwrap aliases for comparison
515 mut t1_unwrapped := t
516 mut t2_unwrapped := t2
517 if t is Alias {
518 t1_unwrapped = t.base_type
519 }
520 if t2 is Alias {
521 t2_unwrapped = t2.base_type
522 }
523 return t1_unwrapped == t2_unwrapped
524}
525
526fn same_type_name(a Type, b Type) bool {
527 if !type_has_valid_payload(a) || !type_has_valid_payload(b) {
528 return false
529 }
530 match a {
531 Alias {
532 return b is Alias && a.name == b.name
533 }
534 Array {
535 return b is Array && same_type_name(a.elem_type, b.elem_type)
536 }
537 ArrayFixed {
538 return b is ArrayFixed && a.len == b.len && same_type_name(a.elem_type, b.elem_type)
539 }
540 Channel {
541 if b is Channel {
542 a_elem := a.elem_type or {
543 if _ := b.elem_type {
544 return false
545 }
546 return true
547 }
548
549 b_elem := b.elem_type or { return false }
550 return same_type_name(a_elem, b_elem)
551 }
552 }
553 Char {
554 return b is Char
555 }
556 Enum {
557 return b is Enum && a.name == b.name
558 }
559 FnType {
560 if b is FnType {
561 if a.params.len != b.params.len || a.is_variadic != b.is_variadic
562 || a.is_mut_receiver != b.is_mut_receiver {
563 return false
564 }
565 for i, param in a.params {
566 if param.name != b.params[i].name || param.is_mut != b.params[i].is_mut
567 || !same_type_name(param.typ, b.params[i].typ) {
568 return false
569 }
570 }
571 a_ret := a.return_type or {
572 if _ := b.return_type {
573 return false
574 }
575 return true
576 }
577
578 b_ret := b.return_type or { return false }
579 return same_type_name(a_ret, b_ret)
580 }
581 }
582 Interface {
583 return b is Interface && a.name == b.name
584 }
585 ISize {
586 return b is ISize
587 }
588 Map {
589 return b is Map && same_type_name(a.key_type, b.key_type)
590 && same_type_name(a.value_type, b.value_type)
591 }
592 NamedType {
593 return b is NamedType && string(a) == string(b as NamedType)
594 }
595 Nil {
596 return b is Nil
597 }
598 None {
599 return b is None
600 }
601 OptionType {
602 return b is OptionType && same_type_name(a.base_type, b.base_type)
603 }
604 Pointer {
605 return b is Pointer && a.lifetime == b.lifetime
606 && same_type_name(a.base_type, b.base_type)
607 }
608 Primitive {
609 return b is Primitive && a == b
610 }
611 ResultType {
612 return b is ResultType && same_type_name(a.base_type, b.base_type)
613 }
614 Rune {
615 return b is Rune
616 }
617 String {
618 return b is String
619 }
620 Struct {
621 return b is Struct && a.name == b.name
622 }
623 SumType {
624 return b is SumType && a.name == b.name
625 }
626 Thread {
627 if b is Thread {
628 a_elem := a.elem_type or {
629 if _ := b.elem_type {
630 return false
631 }
632 return true
633 }
634
635 b_elem := b.elem_type or { return false }
636 return same_type_name(a_elem, b_elem)
637 }
638 }
639 Tuple {
640 if b is Tuple {
641 if a.types.len != b.types.len {
642 return false
643 }
644 for i, typ in a.types {
645 if !same_type_name(typ, b.types[i]) {
646 return false
647 }
648 }
649 return true
650 }
651 }
652 USize {
653 return b is USize
654 }
655 Void {
656 return b is Void
657 }
658 }
659
660 return false
661}
662
663fn (t Type) is_float() bool {
664 if t is Primitive {
665 return t.is_float()
666 }
667 return false
668}
669
670fn (t Type) is_integer() bool {
671 if t is Char || t is Rune || t is ISize || t is USize {
672 return true
673 }
674 if t is Primitive {
675 return t.is_integer()
676 }
677 return false
678}
679
680fn (t Type) is_number() bool {
681 if t is Char || t is Rune || t is ISize || t is USize {
682 return true
683 }
684 if t is Primitive {
685 return t.is_number()
686 }
687 return false
688}
689
690// int_literal || float_literal
691fn (t Type) is_number_literal() bool {
692 if t is Primitive {
693 return t.is_number_literal()
694 }
695 return false
696}
697
698fn (t Type) is_float_literal() bool {
699 if t is Primitive {
700 return t.is_float_literal()
701 }
702 return false
703}
704
705fn (t Type) is_int_literal() bool {
706 if t is Primitive {
707 return t.is_int_literal()
708 }
709 return false
710}
711
712fn (t Primitive) is_float() bool {
713 return t.props.has(.float)
714}
715
716fn (t Primitive) is_integer() bool {
717 return t.props.has(.integer)
718}
719
720fn (t Primitive) is_number() bool {
721 // TODO: should we make sure is not .untyped
722 return !t.props.has(.untyped) && t.props.has(.integer | .float)
723}
724
725fn (t Primitive) is_number_literal() bool {
726 return t.props.has(.untyped) && t.props.has(.integer | .float)
727}
728
729fn (t Primitive) is_float_literal() bool {
730 return t.props.has(.untyped) && t.props.has(.float)
731}
732
733fn (t Primitive) is_int_literal() bool {
734 return t.props.has(.untyped) && t.props.has(.integer)
735}
736
737fn type_tag_has_inline_payload(tag u64) bool {
738 return tag == 4 || tag == 7 || tag == 11 || tag == 12 || tag == 15 || tag == 17 || tag == 18
739 || tag == 23 || tag == 24
740}
741
742pub fn type_has_valid_payload(t Type) bool {
743 data := unsafe { *(&u64(&u8(&t) + 8)) }
744 if data == 0 {
745 tag := unsafe { *(&u64(&t)) }
746 return type_tag_has_inline_payload(tag)
747 }
748 return true
749}
750
751pub fn type_name(t Type) string {
752 // Guard against corrupted sumtype values from ARM64 codegen.
753 // SSA sumtype layout: {i64 _tag, i64 _data}. For large variants, _data
754 // is a heap pointer. Invalid payloads would crash match dispatch.
755 if !type_has_valid_payload(t) {
756 return ''
757 }
758 match t {
759 Primitive, Alias, Array, ArrayFixed, Channel, Char, Enum, FnType, Interface, ISize, Map,
760 OptionType, Pointer, ResultType, Rune, String, Struct, SumType, Thread, Tuple, USize, Void,
761 Nil, None, NamedType {
762 return t.name()
763 }
764 }
765}
766
767pub fn alias_base_type_name(t Type) ?string {
768 return match t {
769 Alias {
770 type_name(t.base_type)
771 }
772 else {
773 none
774 }
775 }
776}
777
778pub fn (t Type) name() string {
779 return type_name(t)
780}
781
782// TODO: clean up :0
783fn (t Primitive) name() string {
784 if t.props.has(.boolean) {
785 return 'bool'
786 } else if t.props.has(.untyped) {
787 if t.props.has(.integer) {
788 return 'int_literal'
789 } else if t.props.has(.float) {
790 return 'float_literal'
791 }
792 } else if t.props.has(.integer) {
793 if t.props.has(.unsigned) {
794 match t.size {
795 8 { return 'u8' }
796 16 { return 'u16' }
797 32 { return 'u32' }
798 64 { return 'u64' }
799 else { return 'u${t.size}' }
800 }
801 } else {
802 // TODO:
803 if t.size == 0 {
804 return 'int'
805 }
806 match t.size {
807 8 { return 'i8' }
808 16 { return 'i16' }
809 32 { return 'i32' }
810 64 { return 'i64' }
811 else { return 'i${t.size}' }
812 }
813 }
814 } else if t.props.has(.float) {
815 match t.size {
816 32 { return 'f32' }
817 64 { return 'f64' }
818 else { return 'f${t.size}' }
819 }
820 }
821 // Fallback for zero-initialized Primitive (shouldn't happen after const ordering fix).
822 return 'int'
823 // TODO: match seems broke when multuple flags are set
824 // actually it was not broken, it matches the bits for flags
825 // matches single only, if multiple are set it will not match.
826 //
827 // match t.props {
828 // .boolean {
829 // return 'bool'
830 // }
831 // .integer {
832 // if t.props.has(.unsigned) {
833 // return 'u${t.size}'
834 // } else {
835 // if t.size == 32 {
836 // return 'int'
837 // }
838 // return 'i${t.size}'
839 // }
840 // }
841 // .float {
842 // return 'f${t.size}'
843 // }
844 // else {
845 // println(t)
846 // panic(t.props.str())
847 // return 'malformed primitive' // lol
848 // }
849 // }
850}
851
852fn (t Alias) name() string {
853 return t.name
854}
855
856fn (t Array) name() string {
857 return '[]${t.elem_type.name()}'
858}
859
860fn (t ArrayFixed) name() string {
861 return '[${t.len}]${t.elem_type.name()}'
862}
863
864fn (t Channel) name() string {
865 if elem_type := t.elem_type {
866 return 'chan ${elem_type.name()}'
867 }
868 return 'chan'
869}
870
871fn (t Char) name() string {
872 return 'char'
873}
874
875fn (t Enum) name() string {
876 return t.name
877}
878
879fn (t FnType) name() string {
880 mut name := 'fn ('
881 for i, param in t.params {
882 if param.name != '' {
883 name += '${param.name} '
884 }
885 name += param.typ.name()
886 if i < t.params.len - 1 {
887 name += ', '
888 }
889 }
890 mut return_type_name := ''
891 if rt := t.return_type {
892 return_type_name = rt.name()
893 }
894 name += ') ${return_type_name}'
895 return name
896}
897
898fn (t Interface) name() string {
899 return t.name
900}
901
902fn (t Map) name() string {
903 return 'map[${t.key_type.name()}]${t.value_type.name()}'
904}
905
906fn (t NamedType) name() string {
907 return t
908}
909
910fn (t OptionType) name() string {
911 return '?' + t.base_type.name()
912}
913
914fn (t Pointer) name() string {
915 if t.lifetime != '' {
916 return '&^${t.lifetime} ' + t.base_type.name()
917 }
918 return '&' + t.base_type.name()
919}
920
921fn (t ResultType) name() string {
922 return '!' + t.base_type.name()
923}
924
925fn (t Rune) name() string {
926 return 'rune'
927}
928
929fn (t String) name() string {
930 return 'string'
931}
932
933fn (t Struct) name() string {
934 return t.name
935}
936
937fn (t SumType) name() string {
938 return t.name
939}
940
941// get_sum_type_name returns the name of a SumType (public accessor)
942pub fn (t SumType) get_name() string {
943 return t.name
944}
945
946pub fn sum_type_name(t SumType) string {
947 return t.name
948}
949
950// get_variants returns the variant types of a SumType
951pub fn (t SumType) get_variants() []Type {
952 return t.variants
953}
954
955fn (t Thread) name() string {
956 return 'thread'
957}
958
959fn (t Tuple) name() string {
960 mut names := []string{cap: t.types.len}
961 for typ in t.types {
962 names << typ.name()
963 }
964 return 'tuple (${names.join(', ')})'
965}
966
967pub fn (t &Tuple) get_types() []Type {
968 return t.types
969}
970
971fn (t ISize) name() string {
972 return 'isize'
973}
974
975fn (t USize) name() string {
976 return 'usize'
977}
978
979// fn (t IntLiteral) name() string {
980// return 'int_literal'
981// }
982
983// fn (t FloatLiteral) name() string {
984// return 'float_literal'
985// }
986
987fn (t Void) name() string {
988 return 'void'
989}
990
991fn (t Nil) name() string {
992 return 'nil'
993}
994
995fn (t None) name() string {
996 return 'none'
997}
998