1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub struct StructId(pub u32);
11
12impl StructId {
13 #[inline]
18 pub fn from_pool_index(pool_index: u32) -> Self {
19 StructId(pool_index)
20 }
21
22 #[inline]
26 pub fn pool_index(self) -> u32 {
27 self.0
28 }
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
36pub struct EnumId(pub u32);
37
38impl EnumId {
39 #[inline]
44 pub fn from_pool_index(pool_index: u32) -> Self {
45 EnumId(pool_index)
46 }
47
48 #[inline]
52 pub fn pool_index(self) -> u32 {
53 self.0
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
60pub struct ArrayTypeId(pub u32);
61
62impl ArrayTypeId {
63 #[inline]
68 pub fn from_pool_index(pool_index: u32) -> Self {
69 ArrayTypeId(pool_index)
70 }
71
72 #[inline]
76 pub fn pool_index(self) -> u32 {
77 self.0
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
84pub struct PtrConstTypeId(pub u32);
85
86impl PtrConstTypeId {
87 #[inline]
89 pub fn from_pool_index(pool_index: u32) -> Self {
90 PtrConstTypeId(pool_index)
91 }
92
93 #[inline]
95 pub fn pool_index(self) -> u32 {
96 self.0
97 }
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
103pub struct PtrMutTypeId(pub u32);
104
105impl PtrMutTypeId {
106 #[inline]
108 pub fn from_pool_index(pool_index: u32) -> Self {
109 PtrMutTypeId(pool_index)
110 }
111
112 #[inline]
114 pub fn pool_index(self) -> u32 {
115 self.0
116 }
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
124pub struct ModuleId(pub u32);
125
126impl ModuleId {
127 #[inline]
129 pub fn new(index: u32) -> Self {
130 ModuleId(index)
131 }
132
133 #[inline]
135 pub fn index(self) -> u32 {
136 self.0
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150pub enum TypeKind {
151 I8,
153 I16,
155 I32,
157 I64,
159 U8,
161 U16,
163 U32,
165 U64,
167 Isize,
169 Usize,
171 F16,
173 F32,
175 F64,
177 Bool,
179 Unit,
181 Struct(StructId),
183 Enum(EnumId),
185 Array(ArrayTypeId),
187 PtrConst(PtrConstTypeId),
189 PtrMut(PtrMutTypeId),
191 Module(ModuleId),
193 Error,
195 Never,
197 ComptimeType,
199 ComptimeStr,
201 ComptimeInt,
203}
204
205#[derive(Clone, Copy, PartialEq, Eq, Hash)]
240pub struct Type(u32);
241
242impl Default for Type {
243 fn default() -> Self {
244 Type::UNIT
245 }
246}
247
248impl std::fmt::Debug for Type {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 match self.kind() {
252 TypeKind::I8 => write!(f, "Type::I8"),
253 TypeKind::I16 => write!(f, "Type::I16"),
254 TypeKind::I32 => write!(f, "Type::I32"),
255 TypeKind::I64 => write!(f, "Type::I64"),
256 TypeKind::U8 => write!(f, "Type::U8"),
257 TypeKind::U16 => write!(f, "Type::U16"),
258 TypeKind::U32 => write!(f, "Type::U32"),
259 TypeKind::U64 => write!(f, "Type::U64"),
260 TypeKind::Isize => write!(f, "Type::ISIZE"),
261 TypeKind::Usize => write!(f, "Type::USIZE"),
262 TypeKind::F16 => write!(f, "Type::F16"),
263 TypeKind::F32 => write!(f, "Type::F32"),
264 TypeKind::F64 => write!(f, "Type::F64"),
265 TypeKind::Bool => write!(f, "Type::BOOL"),
266 TypeKind::Unit => write!(f, "Type::UNIT"),
267 TypeKind::Error => write!(f, "Type::ERROR"),
268 TypeKind::Never => write!(f, "Type::NEVER"),
269 TypeKind::ComptimeType => write!(f, "Type::COMPTIME_TYPE"),
270 TypeKind::ComptimeStr => write!(f, "Type::COMPTIME_STR"),
271 TypeKind::ComptimeInt => write!(f, "Type::COMPTIME_INT"),
272 TypeKind::Struct(id) => write!(f, "Type::new_struct(StructId({}))", id.0),
273 TypeKind::Enum(id) => write!(f, "Type::new_enum(EnumId({}))", id.0),
274 TypeKind::Array(id) => write!(f, "Type::new_array(ArrayTypeId({}))", id.0),
275 TypeKind::PtrConst(id) => write!(f, "Type::new_ptr_const(PtrConstTypeId({}))", id.0),
276 TypeKind::PtrMut(id) => write!(f, "Type::new_ptr_mut(PtrMutTypeId({}))", id.0),
277 TypeKind::Module(id) => write!(f, "Type::new_module(ModuleId({}))", id.0),
278 }
279 }
280}
281
282const TAG_STRUCT: u32 = 100;
286const TAG_ENUM: u32 = 101;
287const TAG_ARRAY: u32 = 102;
288const TAG_MODULE: u32 = 103;
289const TAG_PTR_CONST: u32 = 104;
290const TAG_PTR_MUT: u32 = 105;
291
292impl Type {
294 pub const I8: Type = Type(0);
296 pub const I16: Type = Type(1);
298 pub const I32: Type = Type(2);
300 pub const I64: Type = Type(3);
302 pub const U8: Type = Type(4);
304 pub const U16: Type = Type(5);
306 pub const U32: Type = Type(6);
308 pub const U64: Type = Type(7);
310 pub const ISIZE: Type = Type(8);
312 pub const USIZE: Type = Type(9);
314 pub const F16: Type = Type(10);
316 pub const F32: Type = Type(11);
318 pub const F64: Type = Type(12);
320 pub const BOOL: Type = Type(13);
322 pub const UNIT: Type = Type(14);
324 pub const ERROR: Type = Type(15);
326 pub const NEVER: Type = Type(16);
328 pub const COMPTIME_TYPE: Type = Type(17);
330 pub const COMPTIME_STR: Type = Type(18);
332 pub const COMPTIME_INT: Type = Type(19);
334}
335
336impl Type {
338 #[inline]
340 pub const fn new_struct(id: StructId) -> Type {
341 Type(TAG_STRUCT | (id.0 << 8))
342 }
343
344 #[inline]
346 pub const fn new_enum(id: EnumId) -> Type {
347 Type(TAG_ENUM | (id.0 << 8))
348 }
349
350 #[inline]
352 pub const fn new_array(id: ArrayTypeId) -> Type {
353 Type(TAG_ARRAY | (id.0 << 8))
354 }
355
356 #[inline]
358 pub const fn new_ptr_const(id: PtrConstTypeId) -> Type {
359 Type(TAG_PTR_CONST | (id.0 << 8))
360 }
361
362 #[inline]
364 pub const fn new_ptr_mut(id: PtrMutTypeId) -> Type {
365 Type(TAG_PTR_MUT | (id.0 << 8))
366 }
367
368 #[inline]
370 pub const fn new_module(id: ModuleId) -> Type {
371 Type(TAG_MODULE | (id.0 << 8))
372 }
373}
374
375#[derive(Debug, Clone)]
377pub struct StructDef {
378 pub name: String,
380 pub fields: Vec<StructField>,
382 pub is_copy: bool,
384 pub is_handle: bool,
386 pub is_linear: bool,
388 pub destructor: Option<String>,
390 pub is_builtin: bool,
395 pub is_pub: bool,
397 pub file_id: gruel_span::FileId,
399}
400
401#[derive(Debug, Clone)]
403pub struct StructField {
404 pub name: String,
406 pub ty: Type,
408}
409
410impl StructDef {
411 pub fn find_field(&self, name: &str) -> Option<(usize, &StructField)> {
413 self.fields.iter().enumerate().find(|(_, f)| f.name == name)
414 }
415
416 pub fn field_count(&self) -> usize {
418 self.fields.len()
419 }
420}
421
422#[derive(Debug, Clone)]
424pub struct EnumVariantDef {
425 pub name: String,
427 pub fields: Vec<Type>,
430 pub field_names: Vec<String>,
433}
434
435impl EnumVariantDef {
436 pub fn unit(name: impl Into<String>) -> Self {
438 Self {
439 name: name.into(),
440 fields: Vec::new(),
441 field_names: Vec::new(),
442 }
443 }
444
445 pub fn has_data(&self) -> bool {
447 !self.fields.is_empty()
448 }
449
450 pub fn is_struct_variant(&self) -> bool {
452 !self.field_names.is_empty()
453 }
454
455 pub fn find_field(&self, name: &str) -> Option<usize> {
457 self.field_names.iter().position(|n| n == name)
458 }
459}
460
461#[derive(Debug, Clone)]
463pub struct EnumDef {
464 pub name: String,
466 pub variants: Vec<EnumVariantDef>,
468 pub is_pub: bool,
470 pub file_id: gruel_span::FileId,
472}
473
474impl EnumDef {
475 pub fn variant_count(&self) -> usize {
477 self.variants.len()
478 }
479
480 pub fn find_variant(&self, name: &str) -> Option<usize> {
482 self.variants.iter().position(|v| v.name == name)
483 }
484
485 pub fn has_data_variants(&self) -> bool {
487 self.variants.iter().any(|v| v.has_data())
488 }
489
490 pub fn is_unit_only(&self) -> bool {
492 !self.has_data_variants()
493 }
494
495 pub fn discriminant_type(&self) -> Type {
498 let count = self.variants.len();
499 if count == 0 {
500 Type::NEVER } else if count <= 256 {
502 Type::U8
503 } else if count <= 65536 {
504 Type::U16
505 } else if count <= 4_294_967_296 {
506 Type::U32
507 } else {
508 Type::U64
509 }
510 }
511}
512
513#[derive(Debug, Clone)]
519pub struct ModuleDef {
520 pub import_path: String,
522 pub file_path: String,
524 pub functions: std::collections::HashMap<String, String>,
527 pub structs: Vec<String>,
529 pub enums: Vec<String>,
531}
532
533impl ModuleDef {
534 pub fn new(import_path: String, file_path: String) -> Self {
536 Self {
537 import_path,
538 file_path,
539 functions: std::collections::HashMap::new(),
540 structs: Vec::new(),
541 enums: Vec::new(),
542 }
543 }
544
545 pub fn find_function(&self, name: &str) -> Option<&str> {
548 self.functions.get(name).map(|s| s.as_str())
549 }
550}
551
552impl Type {
553 #[inline]
574 pub fn kind(&self) -> TypeKind {
575 self.try_kind().unwrap_or_else(|| {
576 panic!(
577 "invalid Type encoding: raw value {:#010x} (tag={}, id={}). \
578 This indicates data corruption or a bug in Type construction. \
579 Valid tags are 0-18 (primitives) or 100-105 (composites).",
580 self.0,
581 self.0 & 0xFF,
582 self.0 >> 8
583 )
584 })
585 }
586
587 #[inline]
605 pub fn try_kind(&self) -> Option<TypeKind> {
606 let tag = self.0 & 0xFF;
607 match tag {
608 0 => Some(TypeKind::I8),
609 1 => Some(TypeKind::I16),
610 2 => Some(TypeKind::I32),
611 3 => Some(TypeKind::I64),
612 4 => Some(TypeKind::U8),
613 5 => Some(TypeKind::U16),
614 6 => Some(TypeKind::U32),
615 7 => Some(TypeKind::U64),
616 8 => Some(TypeKind::Isize),
617 9 => Some(TypeKind::Usize),
618 10 => Some(TypeKind::F16),
619 11 => Some(TypeKind::F32),
620 12 => Some(TypeKind::F64),
621 13 => Some(TypeKind::Bool),
622 14 => Some(TypeKind::Unit),
623 15 => Some(TypeKind::Error),
624 16 => Some(TypeKind::Never),
625 17 => Some(TypeKind::ComptimeType),
626 18 => Some(TypeKind::ComptimeStr),
627 19 => Some(TypeKind::ComptimeInt),
628 TAG_STRUCT => Some(TypeKind::Struct(StructId(self.0 >> 8))),
629 TAG_ENUM => Some(TypeKind::Enum(EnumId(self.0 >> 8))),
630 TAG_ARRAY => Some(TypeKind::Array(ArrayTypeId(self.0 >> 8))),
631 TAG_PTR_CONST => Some(TypeKind::PtrConst(PtrConstTypeId(self.0 >> 8))),
632 TAG_PTR_MUT => Some(TypeKind::PtrMut(PtrMutTypeId(self.0 >> 8))),
633 TAG_MODULE => Some(TypeKind::Module(ModuleId(self.0 >> 8))),
634 _ => None,
635 }
636 }
637
638 pub fn name(&self) -> &'static str {
642 match self.kind() {
643 TypeKind::I8 => "i8",
644 TypeKind::I16 => "i16",
645 TypeKind::I32 => "i32",
646 TypeKind::I64 => "i64",
647 TypeKind::U8 => "u8",
648 TypeKind::U16 => "u16",
649 TypeKind::U32 => "u32",
650 TypeKind::U64 => "u64",
651 TypeKind::Isize => "isize",
652 TypeKind::Usize => "usize",
653 TypeKind::F16 => "f16",
654 TypeKind::F32 => "f32",
655 TypeKind::F64 => "f64",
656 TypeKind::Bool => "bool",
657 TypeKind::Unit => "()",
658 TypeKind::Struct(_) => "<struct>",
659 TypeKind::Enum(_) => "<enum>",
660 TypeKind::Array(_) => "<array>",
661 TypeKind::PtrConst(_) => "<ptr const>",
662 TypeKind::PtrMut(_) => "<ptr mut>",
663 TypeKind::Module(_) => "<module>",
664 TypeKind::Error => "<error>",
665 TypeKind::Never => "!",
666 TypeKind::ComptimeType => "type",
667 TypeKind::ComptimeStr => "comptime_str",
668 TypeKind::ComptimeInt => "comptime_int",
669 }
670 }
671
672 pub fn safe_name_with_pool(&self, pool: Option<&crate::intern_pool::TypeInternPool>) -> String {
685 match self.try_kind() {
686 Some(TypeKind::Struct(struct_id)) => {
687 if let Some(pool) = pool {
688 let def = pool.struct_def(struct_id);
689 return def.name.clone();
690 }
691 format!("<struct#{}>", struct_id.0)
692 }
693 Some(TypeKind::Enum(enum_id)) => {
694 if let Some(pool) = pool {
695 let def = pool.enum_def(enum_id);
696 return def.name.clone();
697 }
698 format!("<enum#{}>", enum_id.0)
699 }
700 Some(_kind) => self.name().to_string(),
701 None => format!("<invalid type encoding: {:#x}>", self.0),
702 }
703 }
704
705 #[inline]
708 pub fn is_integer(&self) -> bool {
709 self.0 <= 9
710 }
711
712 #[inline]
714 pub fn is_error(&self) -> bool {
715 *self == Type::ERROR
716 }
717
718 #[inline]
720 pub fn is_never(&self) -> bool {
721 *self == Type::NEVER
722 }
723
724 #[inline]
726 pub fn is_comptime_type(&self) -> bool {
727 *self == Type::COMPTIME_TYPE
728 }
729
730 #[inline]
732 pub fn is_comptime_str(&self) -> bool {
733 *self == Type::COMPTIME_STR
734 }
735
736 #[inline]
738 pub fn is_comptime_int(&self) -> bool {
739 *self == Type::COMPTIME_INT
740 }
741
742 #[inline]
744 pub fn is_struct(&self) -> bool {
745 (self.0 & 0xFF) == TAG_STRUCT
746 }
747
748 #[inline]
750 pub fn as_struct(&self) -> Option<StructId> {
751 if self.is_struct() {
752 Some(StructId(self.0 >> 8))
753 } else {
754 None
755 }
756 }
757
758 #[inline]
760 pub fn is_array(&self) -> bool {
761 (self.0 & 0xFF) == TAG_ARRAY
762 }
763
764 #[inline]
766 pub fn as_array(&self) -> Option<ArrayTypeId> {
767 if self.is_array() {
768 Some(ArrayTypeId(self.0 >> 8))
769 } else {
770 None
771 }
772 }
773
774 #[inline]
776 pub fn is_enum(&self) -> bool {
777 (self.0 & 0xFF) == TAG_ENUM
778 }
779
780 #[inline]
782 pub fn as_enum(&self) -> Option<EnumId> {
783 if self.is_enum() {
784 Some(EnumId(self.0 >> 8))
785 } else {
786 None
787 }
788 }
789
790 #[inline]
792 pub fn is_module(&self) -> bool {
793 (self.0 & 0xFF) == TAG_MODULE
794 }
795
796 #[inline]
798 pub fn as_module(&self) -> Option<ModuleId> {
799 if self.is_module() {
800 Some(ModuleId(self.0 >> 8))
801 } else {
802 None
803 }
804 }
805
806 #[inline]
808 pub fn is_ptr_const(&self) -> bool {
809 (self.0 & 0xFF) == TAG_PTR_CONST
810 }
811
812 #[inline]
814 pub fn as_ptr_const(&self) -> Option<PtrConstTypeId> {
815 if self.is_ptr_const() {
816 Some(PtrConstTypeId(self.0 >> 8))
817 } else {
818 None
819 }
820 }
821
822 #[inline]
824 pub fn is_ptr_mut(&self) -> bool {
825 (self.0 & 0xFF) == TAG_PTR_MUT
826 }
827
828 #[inline]
830 pub fn as_ptr_mut(&self) -> Option<PtrMutTypeId> {
831 if self.is_ptr_mut() {
832 Some(PtrMutTypeId(self.0 >> 8))
833 } else {
834 None
835 }
836 }
837
838 #[inline]
840 pub fn is_ptr(&self) -> bool {
841 let tag = self.0 & 0xFF;
842 tag == TAG_PTR_CONST || tag == TAG_PTR_MUT
843 }
844
845 #[inline]
848 pub fn is_signed(&self) -> bool {
849 self.0 <= 3 || self.0 == 8
850 }
851
852 #[inline]
855 pub fn is_float(&self) -> bool {
856 self.0 >= 10 && self.0 <= 12
857 }
858
859 #[inline]
861 pub fn is_numeric(&self) -> bool {
862 self.0 <= 12
863 }
864
865 pub fn is_copy(&self) -> bool {
882 let tag = self.0 & 0xFF;
883 match tag {
884 0..=14 => true,
886 15..=19 => true,
888 TAG_ENUM => true,
890 TAG_MODULE => true,
892 TAG_STRUCT => false,
894 TAG_ARRAY => false,
896 _ => false,
897 }
898 }
899
900 pub fn is_copy_in_pool(&self, type_pool: &crate::intern_pool::TypeInternPool) -> bool {
905 if let Some(struct_id) = self.as_struct() {
906 type_pool.struct_def(struct_id).is_copy
907 } else {
908 self.is_copy()
909 }
910 }
911
912 #[inline]
915 pub fn is_64_bit(&self) -> bool {
916 self.0 == 3 || self.0 == 7
917 }
918
919 #[inline]
922 pub fn is_pointer_sized(&self) -> bool {
923 self.0 == 8 || self.0 == 9
924 }
925
926 pub fn can_coerce_to(&self, target: &Type) -> bool {
933 self.is_never() || self.is_error() || self == target
934 }
935
936 #[inline]
939 #[must_use]
940 pub fn is_unsigned(&self) -> bool {
941 (self.0 >= 4 && self.0 <= 7) || self.0 == 9
942 }
943
944 #[must_use]
952 pub fn literal_fits(&self, value: u64) -> bool {
953 match self.0 {
954 0 => value <= i8::MAX as u64, 1 => value <= i16::MAX as u64, 2 => value <= i32::MAX as u64, 3 => value <= i64::MAX as u64, 4 => value <= u8::MAX as u64, 5 => value <= u16::MAX as u64, 6 => value <= u32::MAX as u64, 7 => true, 8 => value <= i64::MAX as u64, 9 => true, _ => false,
965 }
966 }
967
968 #[must_use]
973 pub fn negated_literal_fits(&self, value: u64) -> bool {
974 match self.0 {
975 0 => value <= (i8::MIN as i64).unsigned_abs(), 1 => value <= (i16::MIN as i64).unsigned_abs(), 2 => value <= (i32::MIN as i64).unsigned_abs(), 3 => value <= (i64::MIN).unsigned_abs(), 8 => value <= (i64::MIN).unsigned_abs(), _ => false,
981 }
982 }
983
984 #[inline]
988 pub fn as_u32(&self) -> u32 {
989 self.0
990 }
991
992 #[inline]
1002 pub fn from_u32(v: u32) -> Self {
1003 Type(v)
1004 }
1005
1006 #[inline]
1022 pub fn try_from_u32(v: u32) -> Option<Self> {
1023 if Self::is_valid_encoding(v) {
1024 Some(Type(v))
1025 } else {
1026 None
1027 }
1028 }
1029
1030 #[inline]
1034 pub fn is_valid_encoding(v: u32) -> bool {
1035 let tag = v & 0xFF;
1036 match tag {
1037 0..=19 => true,
1039 TAG_STRUCT | TAG_ENUM | TAG_ARRAY | TAG_PTR_CONST | TAG_PTR_MUT | TAG_MODULE => true,
1041 _ => false,
1043 }
1044 }
1045
1046 #[inline]
1050 pub fn is_valid(&self) -> bool {
1051 Self::is_valid_encoding(self.0)
1052 }
1053}
1054
1055impl std::fmt::Display for Type {
1056 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1057 write!(f, "{}", self.name())
1058 }
1059}
1060
1061#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1063pub enum PtrMutability {
1064 Const,
1066 Mut,
1068}
1069
1070pub fn parse_pointer_type_syntax(type_name: &str) -> Option<(String, PtrMutability)> {
1074 let type_name = type_name.trim();
1075 if let Some(rest) = type_name.strip_prefix("ptr const ") {
1076 Some((rest.trim().to_string(), PtrMutability::Const))
1077 } else {
1078 type_name
1079 .strip_prefix("ptr mut ")
1080 .map(|rest| (rest.trim().to_string(), PtrMutability::Mut))
1081 }
1082}
1083
1084pub fn parse_array_type_syntax(type_name: &str) -> Option<(String, u64)> {
1089 let type_name = type_name.trim();
1090 if !type_name.starts_with('[') || !type_name.ends_with(']') {
1091 return None;
1092 }
1093
1094 let inner = &type_name[1..type_name.len() - 1];
1096
1097 let mut bracket_depth = 0;
1100 let mut semi_pos = None;
1101 for (i, ch) in inner.char_indices() {
1102 match ch {
1103 '[' => bracket_depth += 1,
1104 ']' => bracket_depth -= 1,
1105 ';' if bracket_depth == 0 => semi_pos = Some(i),
1106 _ => {}
1107 }
1108 }
1109
1110 let semi_pos = semi_pos?;
1111 let element_type = inner[..semi_pos].trim().to_string();
1112 let length_str = inner[semi_pos + 1..].trim();
1113 let length: u64 = length_str.parse().ok()?;
1114
1115 Some((element_type, length))
1116}
1117
1118#[cfg(test)]
1119mod tests {
1120 use super::*;
1121
1122 #[test]
1125 fn test_struct_id_equality() {
1126 let id1 = StructId(0);
1127 let id2 = StructId(0);
1128 let id3 = StructId(1);
1129 assert_eq!(id1, id2);
1130 assert_ne!(id1, id3);
1131 }
1132
1133 #[test]
1134 fn test_enum_id_equality() {
1135 let id1 = EnumId(0);
1136 let id2 = EnumId(0);
1137 let id3 = EnumId(1);
1138 assert_eq!(id1, id2);
1139 assert_ne!(id1, id3);
1140 }
1141
1142 #[test]
1143 fn test_array_type_id_equality() {
1144 let id1 = ArrayTypeId(0);
1145 let id2 = ArrayTypeId(0);
1146 let id3 = ArrayTypeId(1);
1147 assert_eq!(id1, id2);
1148 assert_ne!(id1, id3);
1149 }
1150
1151 #[test]
1154 fn test_type_name_integers() {
1155 assert_eq!(Type::I8.name(), "i8");
1156 assert_eq!(Type::I16.name(), "i16");
1157 assert_eq!(Type::I32.name(), "i32");
1158 assert_eq!(Type::I64.name(), "i64");
1159 assert_eq!(Type::U8.name(), "u8");
1160 assert_eq!(Type::U16.name(), "u16");
1161 assert_eq!(Type::U32.name(), "u32");
1162 assert_eq!(Type::U64.name(), "u64");
1163 }
1164
1165 #[test]
1166 fn test_type_name_other() {
1167 assert_eq!(Type::BOOL.name(), "bool");
1168 assert_eq!(Type::UNIT.name(), "()");
1169 assert_eq!(Type::ERROR.name(), "<error>");
1170 assert_eq!(Type::NEVER.name(), "!");
1171 }
1172
1173 #[test]
1174 fn test_type_name_composite() {
1175 assert_eq!(Type::new_struct(StructId(0)).name(), "<struct>");
1176 assert_eq!(Type::new_enum(EnumId(0)).name(), "<enum>");
1177 assert_eq!(Type::new_array(ArrayTypeId(0)).name(), "<array>");
1178 }
1179
1180 #[test]
1183 fn test_is_integer_signed() {
1184 assert!(Type::I8.is_integer());
1185 assert!(Type::I16.is_integer());
1186 assert!(Type::I32.is_integer());
1187 assert!(Type::I64.is_integer());
1188 }
1189
1190 #[test]
1191 fn test_is_integer_unsigned() {
1192 assert!(Type::U8.is_integer());
1193 assert!(Type::U16.is_integer());
1194 assert!(Type::U32.is_integer());
1195 assert!(Type::U64.is_integer());
1196 }
1197
1198 #[test]
1199 fn test_is_integer_non_integers() {
1200 assert!(!Type::BOOL.is_integer());
1201 assert!(!Type::UNIT.is_integer());
1202 assert!(!Type::new_struct(StructId(0)).is_integer());
1203 assert!(!Type::new_enum(EnumId(0)).is_integer());
1204 assert!(!Type::new_array(ArrayTypeId(0)).is_integer());
1205 assert!(!Type::ERROR.is_integer());
1206 assert!(!Type::NEVER.is_integer());
1207 }
1208
1209 #[test]
1212 fn test_is_signed() {
1213 assert!(Type::I8.is_signed());
1214 assert!(Type::I16.is_signed());
1215 assert!(Type::I32.is_signed());
1216 assert!(Type::I64.is_signed());
1217
1218 assert!(!Type::U8.is_signed());
1219 assert!(!Type::U16.is_signed());
1220 assert!(!Type::U32.is_signed());
1221 assert!(!Type::U64.is_signed());
1222 assert!(!Type::BOOL.is_signed());
1223 }
1224
1225 #[test]
1228 fn test_is_unsigned() {
1229 assert!(Type::U8.is_unsigned());
1230 assert!(Type::U16.is_unsigned());
1231 assert!(Type::U32.is_unsigned());
1232 assert!(Type::U64.is_unsigned());
1233
1234 assert!(!Type::I8.is_unsigned());
1235 assert!(!Type::I16.is_unsigned());
1236 assert!(!Type::I32.is_unsigned());
1237 assert!(!Type::I64.is_unsigned());
1238 assert!(!Type::BOOL.is_unsigned());
1239 }
1240
1241 #[test]
1244 fn test_is_64_bit() {
1245 assert!(Type::I64.is_64_bit());
1246 assert!(Type::U64.is_64_bit());
1247
1248 assert!(!Type::I8.is_64_bit());
1249 assert!(!Type::I16.is_64_bit());
1250 assert!(!Type::I32.is_64_bit());
1251 assert!(!Type::U8.is_64_bit());
1252 assert!(!Type::U16.is_64_bit());
1253 assert!(!Type::U32.is_64_bit());
1254 assert!(!Type::BOOL.is_64_bit());
1255 }
1256
1257 #[test]
1260 fn test_is_error() {
1261 assert!(Type::ERROR.is_error());
1262 assert!(!Type::I32.is_error());
1263 assert!(!Type::NEVER.is_error());
1264 }
1265
1266 #[test]
1269 fn test_is_never() {
1270 assert!(Type::NEVER.is_never());
1271 assert!(!Type::I32.is_never());
1272 assert!(!Type::ERROR.is_never());
1273 }
1274
1275 #[test]
1278 fn test_is_struct() {
1279 assert!(Type::new_struct(StructId(0)).is_struct());
1280 assert!(Type::new_struct(StructId(42)).is_struct());
1281 assert!(!Type::I32.is_struct());
1282 assert!(!Type::new_enum(EnumId(0)).is_struct());
1283 }
1284
1285 #[test]
1286 fn test_as_struct() {
1287 assert_eq!(Type::new_struct(StructId(5)).as_struct(), Some(StructId(5)));
1288 assert_eq!(Type::I32.as_struct(), None);
1289 assert_eq!(Type::new_enum(EnumId(0)).as_struct(), None);
1290 }
1291
1292 #[test]
1295 fn test_is_enum() {
1296 assert!(Type::new_enum(EnumId(0)).is_enum());
1297 assert!(Type::new_enum(EnumId(42)).is_enum());
1298 assert!(!Type::I32.is_enum());
1299 assert!(!Type::new_struct(StructId(0)).is_enum());
1300 }
1301
1302 #[test]
1303 fn test_as_enum() {
1304 assert_eq!(Type::new_enum(EnumId(5)).as_enum(), Some(EnumId(5)));
1305 assert_eq!(Type::I32.as_enum(), None);
1306 assert_eq!(Type::new_struct(StructId(0)).as_enum(), None);
1307 }
1308
1309 #[test]
1312 fn test_is_array() {
1313 assert!(Type::new_array(ArrayTypeId(0)).is_array());
1314 assert!(Type::new_array(ArrayTypeId(42)).is_array());
1315 assert!(!Type::I32.is_array());
1316 assert!(!Type::new_struct(StructId(0)).is_array());
1317 }
1318
1319 #[test]
1320 fn test_as_array() {
1321 assert_eq!(
1322 Type::new_array(ArrayTypeId(5)).as_array(),
1323 Some(ArrayTypeId(5))
1324 );
1325 assert_eq!(Type::I32.as_array(), None);
1326 assert_eq!(Type::new_struct(StructId(0)).as_array(), None);
1327 }
1328
1329 #[test]
1332 fn test_is_copy_primitives() {
1333 assert!(Type::I8.is_copy());
1335 assert!(Type::I16.is_copy());
1336 assert!(Type::I32.is_copy());
1337 assert!(Type::I64.is_copy());
1338 assert!(Type::U8.is_copy());
1339 assert!(Type::U16.is_copy());
1340 assert!(Type::U32.is_copy());
1341 assert!(Type::U64.is_copy());
1342
1343 assert!(Type::BOOL.is_copy());
1345 assert!(Type::UNIT.is_copy());
1346 }
1347
1348 #[test]
1349 fn test_is_copy_special() {
1350 assert!(Type::new_enum(EnumId(0)).is_copy());
1352
1353 assert!(Type::NEVER.is_copy());
1355 assert!(Type::ERROR.is_copy());
1356 }
1357
1358 #[test]
1359 fn test_is_copy_move_types() {
1360 assert!(!Type::new_struct(StructId(0)).is_copy());
1362 assert!(!Type::new_array(ArrayTypeId(0)).is_copy());
1363 }
1364
1365 #[test]
1368 fn test_can_coerce_to_same_type() {
1369 assert!(Type::I32.can_coerce_to(&Type::I32));
1370 assert!(Type::BOOL.can_coerce_to(&Type::BOOL));
1371 assert!(Type::new_struct(StructId(0)).can_coerce_to(&Type::new_struct(StructId(0))));
1372 }
1373
1374 #[test]
1375 fn test_can_coerce_to_never_coerces_to_anything() {
1376 assert!(Type::NEVER.can_coerce_to(&Type::I32));
1377 assert!(Type::NEVER.can_coerce_to(&Type::BOOL));
1378 assert!(Type::NEVER.can_coerce_to(&Type::new_struct(StructId(0))));
1379 }
1380
1381 #[test]
1382 fn test_can_coerce_to_error_coerces_to_anything() {
1383 assert!(Type::ERROR.can_coerce_to(&Type::I32));
1384 assert!(Type::ERROR.can_coerce_to(&Type::BOOL));
1385 assert!(Type::ERROR.can_coerce_to(&Type::new_struct(StructId(0))));
1386 }
1387
1388 #[test]
1389 fn test_can_coerce_to_different_types_fail() {
1390 assert!(!Type::I32.can_coerce_to(&Type::BOOL));
1391 assert!(!Type::BOOL.can_coerce_to(&Type::I32));
1392 assert!(!Type::I32.can_coerce_to(&Type::I64));
1393 assert!(!Type::new_struct(StructId(0)).can_coerce_to(&Type::I32));
1394 }
1395
1396 #[test]
1399 fn test_literal_fits_i8() {
1400 assert!(Type::I8.literal_fits(0));
1401 assert!(Type::I8.literal_fits(127)); assert!(!Type::I8.literal_fits(128));
1403 }
1404
1405 #[test]
1406 fn test_literal_fits_i16() {
1407 assert!(Type::I16.literal_fits(0));
1408 assert!(Type::I16.literal_fits(32767)); assert!(!Type::I16.literal_fits(32768));
1410 }
1411
1412 #[test]
1413 fn test_literal_fits_i32() {
1414 assert!(Type::I32.literal_fits(0));
1415 assert!(Type::I32.literal_fits(2147483647)); assert!(!Type::I32.literal_fits(2147483648));
1417 }
1418
1419 #[test]
1420 fn test_literal_fits_i64() {
1421 assert!(Type::I64.literal_fits(0));
1422 assert!(Type::I64.literal_fits(9223372036854775807)); assert!(!Type::I64.literal_fits(9223372036854775808));
1424 }
1425
1426 #[test]
1427 fn test_literal_fits_u8() {
1428 assert!(Type::U8.literal_fits(0));
1429 assert!(Type::U8.literal_fits(255)); assert!(!Type::U8.literal_fits(256));
1431 }
1432
1433 #[test]
1434 fn test_literal_fits_u16() {
1435 assert!(Type::U16.literal_fits(0));
1436 assert!(Type::U16.literal_fits(65535)); assert!(!Type::U16.literal_fits(65536));
1438 }
1439
1440 #[test]
1441 fn test_literal_fits_u32() {
1442 assert!(Type::U32.literal_fits(0));
1443 assert!(Type::U32.literal_fits(4294967295)); assert!(!Type::U32.literal_fits(4294967296));
1445 }
1446
1447 #[test]
1448 fn test_literal_fits_u64() {
1449 assert!(Type::U64.literal_fits(0));
1450 assert!(Type::U64.literal_fits(u64::MAX)); }
1452
1453 #[test]
1454 fn test_literal_fits_non_integer() {
1455 assert!(!Type::BOOL.literal_fits(0));
1456 assert!(!Type::new_struct(StructId(0)).literal_fits(0));
1457 assert!(!Type::UNIT.literal_fits(0));
1458 }
1459
1460 #[test]
1463 fn test_negated_literal_fits_i8() {
1464 assert!(Type::I8.negated_literal_fits(128)); assert!(!Type::I8.negated_literal_fits(129));
1466 }
1467
1468 #[test]
1469 fn test_negated_literal_fits_i16() {
1470 assert!(Type::I16.negated_literal_fits(32768)); assert!(!Type::I16.negated_literal_fits(32769));
1472 }
1473
1474 #[test]
1475 fn test_negated_literal_fits_i32() {
1476 assert!(Type::I32.negated_literal_fits(2147483648)); assert!(!Type::I32.negated_literal_fits(2147483649));
1478 }
1479
1480 #[test]
1481 fn test_negated_literal_fits_i64() {
1482 assert!(Type::I64.negated_literal_fits(9223372036854775808)); assert!(!Type::I64.negated_literal_fits(9223372036854775809));
1484 }
1485
1486 #[test]
1487 fn test_negated_literal_fits_unsigned() {
1488 assert!(!Type::U8.negated_literal_fits(1));
1490 assert!(!Type::U16.negated_literal_fits(1));
1491 assert!(!Type::U32.negated_literal_fits(1));
1492 assert!(!Type::U64.negated_literal_fits(1));
1493 }
1494
1495 #[test]
1496 fn test_negated_literal_fits_non_integer() {
1497 assert!(!Type::BOOL.negated_literal_fits(1));
1498 assert!(!Type::new_struct(StructId(0)).negated_literal_fits(1));
1499 }
1500
1501 #[test]
1504 fn test_type_display() {
1505 assert_eq!(format!("{}", Type::I32), "i32");
1506 assert_eq!(format!("{}", Type::BOOL), "bool");
1507 assert_eq!(format!("{}", Type::NEVER), "!");
1508 }
1509
1510 #[test]
1513 fn test_type_default() {
1514 assert_eq!(Type::default(), Type::UNIT);
1515 }
1516
1517 #[test]
1520 fn test_struct_def_find_field() {
1521 let def = StructDef {
1522 name: "Point".to_string(),
1523 fields: vec![
1524 StructField {
1525 name: "x".to_string(),
1526 ty: Type::I32,
1527 },
1528 StructField {
1529 name: "y".to_string(),
1530 ty: Type::I32,
1531 },
1532 ],
1533 is_copy: false,
1534 is_handle: false,
1535 is_linear: false,
1536 destructor: None,
1537 is_builtin: false,
1538 is_pub: false,
1539 file_id: gruel_span::FileId::DEFAULT,
1540 };
1541
1542 let (idx, field) = def.find_field("x").unwrap();
1543 assert_eq!(idx, 0);
1544 assert_eq!(field.name, "x");
1545 assert_eq!(field.ty, Type::I32);
1546
1547 let (idx, field) = def.find_field("y").unwrap();
1548 assert_eq!(idx, 1);
1549 assert_eq!(field.name, "y");
1550
1551 assert!(def.find_field("z").is_none());
1552 }
1553
1554 #[test]
1555 fn test_struct_def_field_count() {
1556 let empty = StructDef {
1557 name: "Empty".to_string(),
1558 fields: vec![],
1559 is_copy: false,
1560 is_handle: false,
1561 is_linear: false,
1562 destructor: None,
1563 is_builtin: false,
1564 is_pub: false,
1565 file_id: gruel_span::FileId::DEFAULT,
1566 };
1567 assert_eq!(empty.field_count(), 0);
1568
1569 let with_fields = StructDef {
1570 name: "Data".to_string(),
1571 fields: vec![
1572 StructField {
1573 name: "a".to_string(),
1574 ty: Type::I32,
1575 },
1576 StructField {
1577 name: "b".to_string(),
1578 ty: Type::BOOL,
1579 },
1580 StructField {
1581 name: "c".to_string(),
1582 ty: Type::I64,
1583 },
1584 ],
1585 is_copy: false,
1586 is_handle: false,
1587 is_linear: false,
1588 destructor: None,
1589 is_builtin: false,
1590 is_pub: false,
1591 file_id: gruel_span::FileId::DEFAULT,
1592 };
1593 assert_eq!(with_fields.field_count(), 3);
1594 }
1595
1596 #[test]
1599 fn test_enum_def_variant_count() {
1600 let empty = EnumDef {
1601 name: "Empty".to_string(),
1602 variants: vec![],
1603 is_pub: false,
1604 file_id: gruel_span::FileId::DEFAULT,
1605 };
1606 assert_eq!(empty.variant_count(), 0);
1607
1608 let color = EnumDef {
1609 name: "Color".to_string(),
1610 variants: vec![
1611 EnumVariantDef::unit("Red"),
1612 EnumVariantDef::unit("Green"),
1613 EnumVariantDef::unit("Blue"),
1614 ],
1615 is_pub: false,
1616 file_id: gruel_span::FileId::DEFAULT,
1617 };
1618 assert_eq!(color.variant_count(), 3);
1619 }
1620
1621 #[test]
1622 fn test_enum_def_find_variant() {
1623 let color = EnumDef {
1624 name: "Color".to_string(),
1625 variants: vec![
1626 EnumVariantDef::unit("Red"),
1627 EnumVariantDef::unit("Green"),
1628 EnumVariantDef::unit("Blue"),
1629 ],
1630 is_pub: false,
1631 file_id: gruel_span::FileId::DEFAULT,
1632 };
1633
1634 assert_eq!(color.find_variant("Red"), Some(0));
1635 assert_eq!(color.find_variant("Green"), Some(1));
1636 assert_eq!(color.find_variant("Blue"), Some(2));
1637 assert_eq!(color.find_variant("Yellow"), None);
1638 }
1639
1640 #[test]
1641 fn test_enum_def_discriminant_type_empty() {
1642 let empty = EnumDef {
1643 name: "Empty".to_string(),
1644 variants: vec![],
1645 is_pub: false,
1646 file_id: gruel_span::FileId::DEFAULT,
1647 };
1648 assert_eq!(empty.discriminant_type(), Type::NEVER);
1649 }
1650
1651 #[test]
1652 fn test_enum_def_discriminant_type_small() {
1653 let small = EnumDef {
1655 name: "Small".to_string(),
1656 variants: vec![EnumVariantDef::unit("A")],
1657 is_pub: false,
1658 file_id: gruel_span::FileId::DEFAULT,
1659 };
1660 assert_eq!(small.discriminant_type(), Type::U8);
1661
1662 let max_u8 = EnumDef {
1663 name: "MaxU8".to_string(),
1664 variants: (0..256)
1665 .map(|i| EnumVariantDef::unit(format!("V{}", i)))
1666 .collect(),
1667 is_pub: false,
1668 file_id: gruel_span::FileId::DEFAULT,
1669 };
1670 assert_eq!(max_u8.discriminant_type(), Type::U8);
1671 }
1672
1673 #[test]
1674 fn test_enum_def_discriminant_type_medium() {
1675 let medium = EnumDef {
1677 name: "Medium".to_string(),
1678 variants: (0..257)
1679 .map(|i| EnumVariantDef::unit(format!("V{}", i)))
1680 .collect(),
1681 is_pub: false,
1682 file_id: gruel_span::FileId::DEFAULT,
1683 };
1684 assert_eq!(medium.discriminant_type(), Type::U16);
1685 }
1686
1687 #[test]
1690 fn test_comptime_type_name() {
1691 assert_eq!(Type::COMPTIME_TYPE.name(), "type");
1692 }
1693
1694 #[test]
1695 fn test_comptime_type_is_copy() {
1696 assert!(Type::COMPTIME_TYPE.is_copy());
1697 }
1698
1699 #[test]
1700 fn test_comptime_type_is_comptime_type() {
1701 assert!(Type::COMPTIME_TYPE.is_comptime_type());
1702 assert!(!Type::I32.is_comptime_type());
1703 assert!(!Type::BOOL.is_comptime_type());
1704 }
1705
1706 #[test]
1707 fn test_comptime_type_not_integer() {
1708 assert!(!Type::COMPTIME_TYPE.is_integer());
1709 }
1710
1711 #[test]
1712 fn test_comptime_type_not_signed() {
1713 assert!(!Type::COMPTIME_TYPE.is_signed());
1714 }
1715
1716 #[test]
1717 fn test_comptime_type_not_64_bit() {
1718 assert!(!Type::COMPTIME_TYPE.is_64_bit());
1719 }
1720
1721 #[test]
1722 fn test_comptime_type_can_coerce_to_itself() {
1723 assert!(Type::COMPTIME_TYPE.can_coerce_to(&Type::COMPTIME_TYPE));
1724 }
1725
1726 #[test]
1727 fn test_comptime_type_cannot_coerce_to_runtime_types() {
1728 assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::I32));
1729 assert!(!Type::COMPTIME_TYPE.can_coerce_to(&Type::BOOL));
1730 }
1731
1732 #[test]
1735 fn test_is_valid_encoding_primitives() {
1736 for i in 0..=19u32 {
1738 assert!(
1739 Type::is_valid_encoding(i),
1740 "primitive tag {} should be valid",
1741 i
1742 );
1743 }
1744 }
1745
1746 #[test]
1747 fn test_is_valid_encoding_composites() {
1748 assert!(Type::is_valid_encoding(100)); assert!(Type::is_valid_encoding(101)); assert!(Type::is_valid_encoding(102)); assert!(Type::is_valid_encoding(103)); assert!(Type::is_valid_encoding(104)); assert!(Type::is_valid_encoding(105)); assert!(Type::is_valid_encoding(100 | (42 << 8))); assert!(Type::is_valid_encoding(101 | (100 << 8))); }
1760
1761 #[test]
1762 fn test_is_valid_encoding_invalid() {
1763 for tag in 20..100u32 {
1765 assert!(
1766 !Type::is_valid_encoding(tag),
1767 "tag {} should be invalid",
1768 tag
1769 );
1770 }
1771
1772 for tag in 106..=255u32 {
1774 assert!(
1775 !Type::is_valid_encoding(tag),
1776 "tag {} should be invalid",
1777 tag
1778 );
1779 }
1780 }
1781
1782 #[test]
1783 fn test_try_from_u32_valid() {
1784 assert!(Type::try_from_u32(0).is_some()); assert!(Type::try_from_u32(2).is_some()); assert!(Type::try_from_u32(10).is_some()); assert!(Type::try_from_u32(100).is_some()); assert!(Type::try_from_u32(100 | (42 << 8)).is_some()); }
1793
1794 #[test]
1795 fn test_try_from_u32_invalid() {
1796 assert!(Type::try_from_u32(50).is_none());
1798 assert!(Type::try_from_u32(99).is_none());
1799 assert!(Type::try_from_u32(106).is_none());
1800 assert!(Type::try_from_u32(255).is_none());
1801 }
1802
1803 #[test]
1804 fn test_try_kind_valid() {
1805 assert_eq!(Type::I32.try_kind(), Some(TypeKind::I32));
1806 assert_eq!(Type::BOOL.try_kind(), Some(TypeKind::Bool));
1807 assert_eq!(
1808 Type::new_struct(StructId(42)).try_kind(),
1809 Some(TypeKind::Struct(StructId(42)))
1810 );
1811 }
1812
1813 #[test]
1814 fn test_try_kind_invalid() {
1815 let invalid = Type::from_u32(50); assert!(invalid.try_kind().is_none());
1818
1819 let invalid2 = Type::from_u32(200); assert!(invalid2.try_kind().is_none());
1821 }
1822
1823 #[test]
1824 fn test_is_valid_method() {
1825 assert!(Type::I32.is_valid());
1826 assert!(Type::new_struct(StructId(0)).is_valid());
1827
1828 let invalid = Type::from_u32(50);
1830 assert!(!invalid.is_valid());
1831 }
1832
1833 #[test]
1834 #[should_panic(expected = "invalid Type encoding")]
1835 fn test_kind_panics_on_invalid() {
1836 let invalid = Type::from_u32(50);
1837 let _ = invalid.kind(); }
1839
1840 #[test]
1841 fn test_roundtrip_encoding() {
1842 let types = [
1844 Type::I8,
1845 Type::I16,
1846 Type::I32,
1847 Type::I64,
1848 Type::U8,
1849 Type::U16,
1850 Type::U32,
1851 Type::U64,
1852 Type::BOOL,
1853 Type::UNIT,
1854 Type::ERROR,
1855 Type::NEVER,
1856 Type::COMPTIME_TYPE,
1857 Type::COMPTIME_STR,
1858 Type::new_struct(StructId(0)),
1859 Type::new_struct(StructId(1000)),
1860 Type::new_enum(EnumId(5)),
1861 Type::new_array(ArrayTypeId(10)),
1862 Type::new_ptr_const(PtrConstTypeId(20)),
1863 Type::new_ptr_mut(PtrMutTypeId(30)),
1864 Type::new_module(ModuleId(40)),
1865 ];
1866
1867 for ty in types {
1868 let encoded = ty.as_u32();
1869 let decoded = Type::from_u32(encoded);
1870 assert_eq!(ty, decoded, "roundtrip failed for {:?}", ty);
1871 assert!(
1872 decoded.is_valid(),
1873 "{:?} should be valid after roundtrip",
1874 ty
1875 );
1876 }
1877 }
1878}