1use crate::{context::Context, pretty::DebugWithContext, ConstantContent, ConstantValue, Value};
13
14#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
15pub struct Type(pub slotmap::DefaultKey);
16
17impl DebugWithContext for Type {
18 fn fmt_with_context(
19 &self,
20 formatter: &mut std::fmt::Formatter,
21 context: &Context,
22 ) -> std::fmt::Result {
23 self.get_content(context)
24 .fmt_with_context(formatter, context)
25 }
26}
27
28#[derive(Debug, Clone, DebugWithContext, Hash, PartialEq, Eq)]
29pub enum TypeContent {
30 Never,
31 Unit,
32 Bool,
33 Uint(u16),
34 B256,
35 StringSlice,
36 StringArray(u64),
37 Array(Type, u64),
38 Union(Vec<Type>),
39 Struct(Vec<Type>),
40 Slice,
41 Pointer(Type),
42 TypedSlice(Type),
43}
44
45impl Type {
46 fn get_or_create_unique_type(context: &mut Context, t: TypeContent) -> Type {
47 #[allow(clippy::map_entry)]
49 if !context.type_map.contains_key(&t) {
50 let new_type = Type(context.types.insert(t.clone()));
51 context.type_map.insert(t, new_type);
52 new_type
53 } else {
54 context.type_map.get(&t).copied().unwrap()
55 }
56 }
57
58 pub fn get_type(context: &Context, t: &TypeContent) -> Option<Type> {
60 context.type_map.get(t).copied()
61 }
62
63 pub fn create_basic_types(context: &mut Context) {
64 Self::get_or_create_unique_type(context, TypeContent::Never);
65 Self::get_or_create_unique_type(context, TypeContent::Unit);
66 Self::get_or_create_unique_type(context, TypeContent::Bool);
67 Self::get_or_create_unique_type(context, TypeContent::Uint(8));
68 Self::get_or_create_unique_type(context, TypeContent::Uint(16));
69 Self::get_or_create_unique_type(context, TypeContent::Uint(32));
70 Self::get_or_create_unique_type(context, TypeContent::Uint(64));
71 Self::get_or_create_unique_type(context, TypeContent::Uint(256));
72 Self::get_or_create_unique_type(context, TypeContent::B256);
73 Self::get_or_create_unique_type(context, TypeContent::Slice);
74 }
75
76 pub fn get_content<'a>(&self, context: &'a Context) -> &'a TypeContent {
78 &context.types[self.0]
79 }
80
81 pub fn get_never(context: &Context) -> Type {
83 Self::get_type(context, &TypeContent::Never).expect("create_basic_types not called")
84 }
85
86 pub fn get_unit(context: &Context) -> Type {
88 Self::get_type(context, &TypeContent::Unit).expect("create_basic_types not called")
89 }
90
91 pub fn get_bool(context: &Context) -> Type {
93 Self::get_type(context, &TypeContent::Bool).expect("create_basic_types not called")
94 }
95
96 pub fn new_uint(context: &mut Context, width: u16) -> Type {
98 Self::get_or_create_unique_type(context, TypeContent::Uint(width))
99 }
100
101 pub fn get_uint8(context: &Context) -> Type {
103 Self::get_type(context, &TypeContent::Uint(8)).expect("create_basic_types not called")
104 }
105
106 pub fn get_uint16(context: &Context) -> Type {
108 Self::get_type(context, &TypeContent::Uint(16)).expect("create_basic_types not called")
109 }
110
111 pub fn get_uint32(context: &Context) -> Type {
113 Self::get_type(context, &TypeContent::Uint(32)).expect("create_basic_types not called")
114 }
115
116 pub fn get_uint64(context: &Context) -> Type {
118 Self::get_type(context, &TypeContent::Uint(64)).expect("create_basic_types not called")
119 }
120
121 pub fn get_uint256(context: &Context) -> Type {
123 Self::get_type(context, &TypeContent::Uint(256)).expect("create_basic_types not called")
124 }
125
126 pub fn get_uint(context: &Context, width: u16) -> Option<Type> {
128 Self::get_type(context, &TypeContent::Uint(width))
129 }
130
131 pub fn get_b256(context: &Context) -> Type {
133 Self::get_type(context, &TypeContent::B256).expect("create_basic_types not called")
134 }
135
136 pub fn new_string_array(context: &mut Context, len: u64) -> Type {
138 Self::get_or_create_unique_type(context, TypeContent::StringArray(len))
139 }
140
141 pub fn new_array(context: &mut Context, elm_ty: Type, len: u64) -> Type {
143 Self::get_or_create_unique_type(context, TypeContent::Array(elm_ty, len))
144 }
145
146 pub fn new_union(context: &mut Context, fields: Vec<Type>) -> Type {
148 Self::get_or_create_unique_type(context, TypeContent::Union(fields))
149 }
150
151 pub fn new_struct(context: &mut Context, fields: Vec<Type>) -> Type {
153 Self::get_or_create_unique_type(context, TypeContent::Struct(fields))
154 }
155
156 pub fn new_ptr(context: &mut Context, to_ty: Type) -> Type {
158 Self::get_or_create_unique_type(context, TypeContent::Pointer(to_ty))
159 }
160
161 pub fn get_slice(context: &Context) -> Type {
163 Self::get_type(context, &TypeContent::Slice).expect("create_basic_types not called")
164 }
165
166 pub fn get_typed_slice(context: &mut Context, item_ty: Type) -> Type {
168 Self::get_or_create_unique_type(context, TypeContent::TypedSlice(item_ty))
169 }
170
171 pub fn as_string(&self, context: &Context) -> String {
173 let sep_types_str = |agg_content: &Vec<Type>, sep: &str| {
174 agg_content
175 .iter()
176 .map(|ty| ty.as_string(context))
177 .collect::<Vec<_>>()
178 .join(sep)
179 };
180
181 match self.get_content(context) {
182 TypeContent::Never => "never".into(),
183 TypeContent::Unit => "()".into(),
184 TypeContent::Bool => "bool".into(),
185 TypeContent::Uint(nbits) => format!("u{nbits}"),
186 TypeContent::B256 => "b256".into(),
187 TypeContent::StringSlice => "str".into(),
188 TypeContent::StringArray(n) => format!("string<{n}>"),
189 TypeContent::Array(ty, cnt) => {
190 format!("[{}; {}]", ty.as_string(context), cnt)
191 }
192 TypeContent::Union(agg) => {
193 format!("( {} )", sep_types_str(agg, " | "))
194 }
195 TypeContent::Struct(agg) => {
196 format!("{{ {} }}", sep_types_str(agg, ", "))
197 }
198 TypeContent::Slice => "slice".into(),
199 TypeContent::TypedSlice(ty) => format!("__slice[{}]", ty.as_string(context)),
200 TypeContent::Pointer(ty) => format!("ptr {}", ty.as_string(context)),
201 }
202 }
203
204 pub fn eq(&self, context: &Context, other: &Type) -> bool {
207 match (self.get_content(context), other.get_content(context)) {
208 (TypeContent::Unit, TypeContent::Unit) => true,
209 (TypeContent::Bool, TypeContent::Bool) => true,
210 (TypeContent::Uint(l), TypeContent::Uint(r)) => l == r,
211 (TypeContent::B256, TypeContent::B256) => true,
212
213 (TypeContent::StringSlice, TypeContent::StringSlice) => true,
214 (TypeContent::StringArray(l), TypeContent::StringArray(r)) => l == r,
215
216 (TypeContent::Array(l, llen), TypeContent::Array(r, rlen)) => {
217 llen == rlen && l.eq(context, r)
218 }
219
220 (TypeContent::TypedSlice(l), TypeContent::TypedSlice(r)) => l.eq(context, r),
221
222 (TypeContent::Struct(l), TypeContent::Struct(r))
223 | (TypeContent::Union(l), TypeContent::Union(r)) => {
224 l.len() == r.len() && l.iter().zip(r.iter()).all(|(l, r)| l.eq(context, r))
225 }
226 (_, TypeContent::Union(_)) => other.eq(context, self),
228 (TypeContent::Union(l), _) => l.iter().any(|field_ty| other.eq(context, field_ty)),
229 (TypeContent::Never, _) => true,
231 (TypeContent::Slice, TypeContent::Slice) => true,
232 (TypeContent::Pointer(l), TypeContent::Pointer(r)) => l.eq(context, r),
233 _ => false,
234 }
235 }
236
237 pub fn is_never(&self, context: &Context) -> bool {
239 matches!(*self.get_content(context), TypeContent::Never)
240 }
241
242 pub fn is_bool(&self, context: &Context) -> bool {
244 matches!(*self.get_content(context), TypeContent::Bool)
245 }
246
247 pub fn is_unit(&self, context: &Context) -> bool {
249 matches!(*self.get_content(context), TypeContent::Unit)
250 }
251
252 pub fn is_uint(&self, context: &Context) -> bool {
254 matches!(*self.get_content(context), TypeContent::Uint(_))
255 }
256
257 pub fn is_uint8(&self, context: &Context) -> bool {
259 matches!(*self.get_content(context), TypeContent::Uint(8))
260 }
261
262 pub fn is_uint32(&self, context: &Context) -> bool {
264 matches!(*self.get_content(context), TypeContent::Uint(32))
265 }
266
267 pub fn is_uint64(&self, context: &Context) -> bool {
269 matches!(*self.get_content(context), TypeContent::Uint(64))
270 }
271
272 pub fn is_uint_of(&self, context: &Context, width: u16) -> bool {
274 matches!(*self.get_content(context), TypeContent::Uint(width_) if width == width_)
275 }
276
277 pub fn is_b256(&self, context: &Context) -> bool {
279 matches!(*self.get_content(context), TypeContent::B256)
280 }
281
282 pub fn is_string_slice(&self, context: &Context) -> bool {
284 matches!(*self.get_content(context), TypeContent::StringSlice)
285 }
286
287 pub fn is_string_array(&self, context: &Context) -> bool {
289 matches!(*self.get_content(context), TypeContent::StringArray(_))
290 }
291
292 pub fn is_array(&self, context: &Context) -> bool {
294 matches!(*self.get_content(context), TypeContent::Array(..))
295 }
296
297 pub fn is_union(&self, context: &Context) -> bool {
299 matches!(*self.get_content(context), TypeContent::Union(_))
300 }
301
302 pub fn is_struct(&self, context: &Context) -> bool {
304 matches!(*self.get_content(context), TypeContent::Struct(_))
305 }
306
307 pub fn is_enum(&self, context: &Context) -> bool {
309 if !self.is_struct(context) {
317 return false;
318 }
319
320 let field_tys = self.get_field_types(context);
321
322 field_tys.len() == 2 && field_tys[0].is_uint(context) && field_tys[1].is_union(context)
323 }
324
325 pub fn is_aggregate(&self, context: &Context) -> bool {
327 self.is_struct(context) || self.is_union(context) || self.is_array(context)
329 }
330
331 pub fn is_slice(&self, context: &Context) -> bool {
333 matches!(*self.get_content(context), TypeContent::Slice)
334 }
335
336 pub fn is_ptr(&self, context: &Context) -> bool {
339 matches!(*self.get_content(context), TypeContent::Pointer(_))
340 }
341
342 pub fn get_pointee_type(&self, context: &Context) -> Option<Type> {
344 if let TypeContent::Pointer(to_ty) = self.get_content(context) {
345 Some(*to_ty)
346 } else {
347 None
348 }
349 }
350
351 pub fn get_uint_width(&self, context: &Context) -> Option<u16> {
353 if let TypeContent::Uint(width) = self.get_content(context) {
354 Some(*width)
355 } else {
356 None
357 }
358 }
359
360 pub fn get_indexed_type(&self, context: &Context, indices: &[u64]) -> Option<Type> {
362 if indices.is_empty() {
363 return None;
364 }
365
366 indices.iter().try_fold(*self, |ty, idx| {
367 ty.get_field_type(context, *idx)
368 .or_else(|| match ty.get_content(context) {
369 TypeContent::Array(ty, len) if idx < len => Some(*ty),
370 _ => None,
371 })
372 })
373 }
374
375 pub fn get_indexed_offset(&self, context: &Context, indices: &[u64]) -> Option<u64> {
379 indices
380 .iter()
381 .try_fold((*self, 0), |(ty, accum_offset), idx| {
382 if ty.is_struct(context) {
383 let prev_idxs_offset = (0..(*idx)).try_fold(0, |accum, pre_idx| {
386 ty.get_field_type(context, pre_idx)
387 .map(|field_ty| accum + field_ty.size(context).in_bytes_aligned())
388 })?;
389 ty.get_field_type(context, *idx)
390 .map(|field_ty| (field_ty, accum_offset + prev_idxs_offset))
391 } else if ty.is_union(context) {
392 let union_size_in_bytes = ty.size(context).in_bytes();
395 ty.get_field_type(context, *idx).map(|field_ty| {
396 (
397 field_ty,
398 accum_offset
399 + (union_size_in_bytes - field_ty.size(context).in_bytes()),
400 )
401 })
402 } else {
403 assert!(
404 ty.is_array(context),
405 "Expected aggregate type. Got {}.",
406 ty.as_string(context)
407 );
408 ty.get_array_elem_type(context).map(|elm_ty| {
410 let prev_idxs_offset = ty
411 .get_array_elem_type(context)
412 .unwrap()
413 .size(context)
414 .in_bytes()
415 * idx;
416 (elm_ty, accum_offset + prev_idxs_offset)
417 })
418 }
419 })
420 .map(|pair| pair.1)
421 }
422
423 pub fn get_value_indexed_offset(&self, context: &Context, indices: &[Value]) -> Option<u64> {
426 let const_indices: Vec<_> = indices
427 .iter()
428 .map_while(|idx| {
429 if let Some(ConstantContent {
430 value: ConstantValue::Uint(idx),
431 ty: _,
432 }) = idx.get_constant(context).map(|c| c.get_content(context))
433 {
434 Some(*idx)
435 } else {
436 None
437 }
438 })
439 .collect();
440 (const_indices.len() == indices.len())
441 .then(|| self.get_indexed_offset(context, &const_indices))
442 .flatten()
443 }
444
445 pub fn get_field_type(&self, context: &Context, idx: u64) -> Option<Type> {
446 if let TypeContent::Struct(fields) | TypeContent::Union(fields) = self.get_content(context)
447 {
448 fields.get(idx as usize).cloned()
449 } else {
450 None
452 }
453 }
454
455 pub fn get_array_elem_type(&self, context: &Context) -> Option<Type> {
457 if let TypeContent::Array(ty, _) = *self.get_content(context) {
458 Some(ty)
459 } else {
460 None
461 }
462 }
463
464 pub fn get_typed_slice_elem_type(&self, context: &Context) -> Option<Type> {
466 if let TypeContent::TypedSlice(ty) = *self.get_content(context) {
467 Some(ty)
468 } else {
469 None
470 }
471 }
472
473 pub fn get_array_len(&self, context: &Context) -> Option<u64> {
475 if let TypeContent::Array(_, n) = *self.get_content(context) {
476 Some(n)
477 } else {
478 None
479 }
480 }
481
482 pub fn get_string_len(&self, context: &Context) -> Option<u64> {
484 if let TypeContent::StringArray(n) = *self.get_content(context) {
485 Some(n)
486 } else {
487 None
488 }
489 }
490
491 pub fn get_field_types(&self, context: &Context) -> Vec<Type> {
493 match self.get_content(context) {
494 TypeContent::Struct(fields) | TypeContent::Union(fields) => fields.clone(),
495 _ => vec![],
496 }
497 }
498
499 pub fn get_struct_field_offset_and_type(
503 &self,
504 context: &Context,
505 field_idx: u64,
506 ) -> Option<(u64, Type)> {
507 if !self.is_struct(context) {
508 return None;
509 }
510
511 let field_idx = field_idx as usize;
512 let field_types = self.get_field_types(context);
513 let field_offs_in_bytes = field_types
514 .iter()
515 .take(field_idx)
516 .map(|field_ty| {
517 field_ty.size(context).in_bytes_aligned()
519 })
520 .sum::<u64>();
521
522 Some((field_offs_in_bytes, field_types[field_idx]))
523 }
524
525 pub fn get_union_field_offset_and_type(
529 &self,
530 context: &Context,
531 field_idx: u64,
532 ) -> Option<(u64, Type)> {
533 if !self.is_union(context) {
534 return None;
535 }
536
537 let field_idx = field_idx as usize;
538 let field_type = self.get_field_types(context)[field_idx];
539 let union_size_in_bytes = self.size(context).in_bytes();
540 let field_size_in_bytes = field_type.size(context).in_bytes();
543
544 Some((union_size_in_bytes - field_size_in_bytes, field_type))
546 }
547
548 pub fn size(&self, context: &Context) -> TypeSize {
552 match self.get_content(context) {
553 TypeContent::Uint(8) | TypeContent::Bool | TypeContent::Unit | TypeContent::Never => {
554 TypeSize::new(1)
555 }
556 TypeContent::Uint(16)
558 | TypeContent::Uint(32)
559 | TypeContent::Uint(64)
560 | TypeContent::Pointer(_) => TypeSize::new(8),
561 TypeContent::Uint(256) => TypeSize::new(32),
562 TypeContent::Uint(_) => unreachable!(),
563 TypeContent::Slice => TypeSize::new(16),
564 TypeContent::TypedSlice(..) => TypeSize::new(16),
565 TypeContent::B256 => TypeSize::new(32),
566 TypeContent::StringSlice => TypeSize::new(16),
567 TypeContent::StringArray(n) => {
568 TypeSize::new(super::size_bytes_round_up_to_word_alignment!(*n))
569 }
570 TypeContent::Array(el_ty, cnt) => TypeSize::new(cnt * el_ty.size(context).in_bytes()),
571 TypeContent::Struct(field_tys) => {
572 TypeSize::new(
574 field_tys
575 .iter()
576 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
577 .sum(),
578 )
579 }
580 TypeContent::Union(field_tys) => {
581 TypeSize::new(
583 field_tys
584 .iter()
585 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
586 .max()
587 .unwrap_or(0),
588 )
589 }
590 }
591 }
592}
593
594#[macro_export]
596macro_rules! size_bytes_round_up_to_word_alignment {
597 ($bytes_expr: expr) => {
598 ($bytes_expr + 7) - (($bytes_expr + 7) % 8)
599 };
600}
601
602pub trait TypeOption {
604 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool;
605}
606
607impl TypeOption for Option<Type> {
608 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool {
609 self.filter(|ty| pred(ty, context)).is_some()
610 }
611}
612
613#[derive(Clone, Debug)]
615pub struct TypeSize {
616 size_in_bytes: u64,
617}
618
619impl TypeSize {
620 pub(crate) fn new(size_in_bytes: u64) -> Self {
621 Self { size_in_bytes }
622 }
623
624 pub fn in_bytes(&self) -> u64 {
626 self.size_in_bytes
627 }
628
629 pub fn in_bytes_aligned(&self) -> u64 {
631 (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
632 }
633
634 pub fn in_words(&self) -> u64 {
636 (self.size_in_bytes + 7) / 8
637 }
638}
639
640#[derive(Clone, Debug, serde::Serialize)]
646pub enum Padding {
647 Left { target_size: usize },
648 Right { target_size: usize },
649}
650
651impl Padding {
652 pub fn default_for_u8(_value: u8) -> Self {
654 Self::Right { target_size: 1 }
656 }
657
658 pub fn default_for_u64(_value: u64) -> Self {
660 Self::Right { target_size: 8 }
662 }
663
664 pub fn default_for_byte_array(value: &[u8]) -> Self {
666 Self::Right {
667 target_size: value.len(),
668 }
669 }
670
671 pub fn default_for_aggregate(aggregate_size: usize) -> Self {
674 Self::Right {
675 target_size: aggregate_size,
676 }
677 }
678
679 pub fn target_size(&self) -> usize {
681 use Padding::*;
682 match self {
683 Left { target_size } | Right { target_size } => *target_size,
684 }
685 }
686}
687
688#[cfg(test)]
689mod tests {
690 pub use super::*;
691 mod memory_layout {
693 use super::*;
694 use crate::Context;
695 use once_cell::sync::Lazy;
696 use sway_features::ExperimentalFeatures;
697 use sway_types::SourceEngine;
698
699 #[test]
700 fn boolean() {
702 let context = create_context();
703
704 let s_bool = Type::get_bool(&context).size(&context);
705
706 assert_eq!(s_bool.in_bytes(), 1);
707 }
708
709 #[test]
710 fn unit() {
712 let context = create_context();
713
714 let s_unit = Type::get_unit(&context).size(&context);
715
716 assert_eq!(s_unit.in_bytes(), 1);
717 }
718
719 #[test]
720 fn unsigned_u8() {
722 let context = create_context();
723
724 let s_u8 = Type::get_uint8(&context).size(&context);
725
726 assert_eq!(s_u8.in_bytes(), 1);
727 }
728
729 #[test]
730 fn unsigned_u16_u32_u64() {
732 let context = create_context();
733
734 let s_u16 = Type::get_uint16(&context).size(&context);
735 let s_u32 = Type::get_uint32(&context).size(&context);
736 let s_u64 = Type::get_uint64(&context).size(&context);
737
738 assert_eq!(s_u16.in_bytes(), 8);
739 assert_eq!(s_u16.in_bytes(), s_u16.in_bytes_aligned());
740
741 assert_eq!(s_u32.in_bytes(), 8);
742 assert_eq!(s_u32.in_bytes(), s_u32.in_bytes_aligned());
743
744 assert_eq!(s_u64.in_bytes(), 8);
745 assert_eq!(s_u64.in_bytes(), s_u64.in_bytes_aligned());
746 }
747
748 #[test]
749 fn unsigned_u256() {
751 let context = create_context();
752
753 let s_u256 = Type::get_uint256(&context).size(&context);
754
755 assert_eq!(s_u256.in_bytes(), 32);
756 assert_eq!(s_u256.in_bytes(), s_u256.in_bytes_aligned());
757 }
758
759 #[test]
760 fn pointer() {
762 let mut context = create_context();
763
764 for ty in all_sample_types(&mut context) {
765 let s_ptr = Type::new_ptr(&mut context, ty).size(&context);
766
767 assert_eq!(s_ptr.in_bytes(), 8);
768 assert_eq!(s_ptr.in_bytes(), s_ptr.in_bytes_aligned());
769 }
770 }
771
772 #[test]
773 fn slice() {
777 let context = create_context();
778
779 let s_slice = Type::get_slice(&context).size(&context);
780
781 assert_eq!(s_slice.in_bytes(), 16);
782 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
783 }
784
785 #[test]
786 fn b256() {
788 let context = create_context();
789
790 let s_b256 = Type::get_b256(&context).size(&context);
791
792 assert_eq!(s_b256.in_bytes(), 32);
793 assert_eq!(s_b256.in_bytes(), s_b256.in_bytes_aligned());
794 }
795
796 #[test]
797 fn string_slice() {
801 let mut context = create_context();
802
803 let s_slice = Type::get_or_create_unique_type(&mut context, TypeContent::StringSlice)
804 .size(&context);
805
806 assert_eq!(s_slice.in_bytes(), 16);
807 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
808 }
809
810 #[test]
811 fn string_array() {
823 let mut context = create_context();
824
825 for (str_array_ty, len) in sample_string_arrays(&mut context) {
826 assert!(str_array_ty.is_string_array(&context)); let s_str_array = str_array_ty.size(&context);
829
830 assert_eq!(str_array_ty.get_string_len(&context).unwrap(), len);
831
832 assert_eq!(
833 s_str_array.in_bytes(),
834 size_bytes_round_up_to_word_alignment!(len)
835 );
836 assert_eq!(s_str_array.in_bytes(), s_str_array.in_bytes_aligned());
837 }
838 }
839
840 #[test]
841 fn array() {
844 let mut context = create_context();
845
846 for (array_ty, len, elem_size) in sample_arrays(&mut context) {
847 assert!(array_ty.is_array(&context)); let s_array = array_ty.size(&context);
850
851 assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
852
853 assert_eq!(s_array.in_bytes(), len * elem_size);
855
856 for elem_index in 0..len {
857 let elem_offset = array_ty
858 .get_indexed_offset(&context, &[elem_index])
859 .unwrap();
860
861 assert_eq!(elem_offset, elem_index * elem_size);
863 }
864 }
865 }
866
867 #[test]
868 fn r#struct() {
877 let mut context = create_context();
878
879 for (struct_ty, fields) in sample_structs(&mut context) {
880 assert!(struct_ty.is_struct(&context)); let s_struct = struct_ty.size(&context);
883
884 assert_eq!(
888 s_struct.in_bytes(),
889 fields
890 .iter()
891 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
892 .sum::<u64>()
893 );
894 assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
896
897 for field_index in 0..fields.len() {
898 let expected_offset = fields
900 .iter()
901 .take(field_index)
902 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
903 .sum::<u64>();
904
905 let field_offset = struct_ty
906 .get_indexed_offset(&context, &[field_index as u64])
907 .unwrap();
908 assert_eq!(field_offset, expected_offset);
909
910 let (field_offset, field_type) = struct_ty
911 .get_struct_field_offset_and_type(&context, field_index as u64)
912 .unwrap();
913 assert_eq!(field_offset, expected_offset);
914 assert_eq!(field_type, fields[field_index].0);
915 }
916 }
917 }
918
919 #[test]
920 fn union() {
932 let mut context = create_context();
933
934 for (union_ty, variants) in sample_unions(&mut context) {
935 assert!(union_ty.is_union(&context)); let s_union = union_ty.size(&context);
938
939 assert_eq!(
943 s_union.in_bytes(),
944 variants
945 .iter()
946 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
947 .max()
948 .unwrap_or_default()
949 );
950 assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
952
953 for (variant_index, variant) in variants.iter().enumerate() {
954 let expected_offset = s_union.in_bytes() - variant.1;
957
958 let variant_offset = union_ty
959 .get_indexed_offset(&context, &[variant_index as u64])
960 .unwrap();
961 assert_eq!(variant_offset, expected_offset);
962
963 let (variant_offset, field_type) = union_ty
964 .get_union_field_offset_and_type(&context, variant_index as u64)
965 .unwrap();
966 assert_eq!(variant_offset, expected_offset);
967 assert_eq!(field_type, variant.0);
968 }
969 }
970 }
971
972 static SOURCE_ENGINE: Lazy<SourceEngine> = Lazy::new(SourceEngine::default);
977
978 fn create_context() -> Context<'static> {
979 Context::new(&SOURCE_ENGINE, ExperimentalFeatures::default())
980 }
981
982 fn sample_non_aggregate_types(context: &mut Context) -> Vec<Type> {
986 let mut types = vec![
987 Type::get_bool(context),
988 Type::get_unit(context),
989 Type::get_uint(context, 8).unwrap(),
990 Type::get_uint(context, 16).unwrap(),
991 Type::get_uint(context, 32).unwrap(),
992 Type::get_uint(context, 64).unwrap(),
993 Type::get_uint(context, 256).unwrap(),
994 Type::get_b256(context),
995 Type::get_slice(context),
996 Type::get_or_create_unique_type(context, TypeContent::StringSlice),
997 ];
998
999 types.extend(
1000 sample_string_arrays(context)
1001 .into_iter()
1002 .map(|(string_array, _)| string_array),
1003 );
1004
1005 types
1006 }
1007
1008 fn sample_string_arrays(context: &mut Context) -> Vec<(Type, u64)> {
1011 let mut types = vec![];
1012
1013 for len in [0, 1, 7, 8, 15] {
1014 types.push((Type::new_string_array(context, len), len));
1015 }
1016
1017 types
1018 }
1019
1020 fn sample_arrays(context: &mut Context) -> Vec<(Type, u64, u64)> {
1024 let mut types = vec![];
1025
1026 for len in [0, 1, 7, 8, 15] {
1027 for ty in sample_non_aggregate_types(context) {
1028 types.push((
1031 Type::new_array(context, ty, len),
1032 len,
1033 ty.size(context).in_bytes(),
1034 ));
1035 }
1036
1037 for (array_ty, array_len, elem_size) in sample_arrays_to_embed(context) {
1038 types.push((
1042 Type::new_array(context, array_ty, len),
1043 len,
1044 array_len * elem_size,
1045 ));
1046 }
1047
1048 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1049 types.push((Type::new_array(context, struct_ty, len), len, struct_size));
1050 }
1051 }
1052
1053 types
1054 }
1055
1056 fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1060 let mut types = vec![];
1061
1062 types.push((Type::new_struct(context, vec![]), vec![]));
1064
1065 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1067 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1069
1070 let mut fields = vec![];
1072 for ty in sample_non_aggregate_types(context) {
1073 fields.push((ty, ty.size(context).in_bytes()));
1076 }
1077 for (array_ty, len, elem_size) in sample_arrays(context) {
1078 fields.push((array_ty, len * elem_size));
1083 }
1084 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1085 fields.push((struct_ty, struct_size));
1086 }
1087
1088 types.push((
1089 Type::new_struct(
1090 context,
1091 fields.iter().map(|(field_ty, _)| *field_ty).collect(),
1092 ),
1093 fields,
1094 ));
1095
1096 return types;
1097
1098 fn add_structs_with_non_aggregate_types_of_length_in_bytes(
1099 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1100 context: &mut Context,
1101 field_type_size_in_bytes: u64,
1102 ) {
1103 for ty in sample_non_aggregate_types(context) {
1104 if ty.size(context).in_bytes() != field_type_size_in_bytes {
1105 continue;
1106 }
1107
1108 types.push((
1109 Type::new_struct(context, vec![ty]),
1110 vec![(ty, field_type_size_in_bytes)],
1111 ));
1112 }
1113 }
1114 }
1115
1116 fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1120 let mut types = vec![];
1121
1122 types.push((Type::new_union(context, vec![]), vec![]));
1124
1125 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1127 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1129
1130 let mut variants = vec![];
1135 for ty in sample_non_aggregate_types(context) {
1136 variants.push((ty, ty.size(context).in_bytes()));
1137 }
1138 for (array_ty, len, elem_size) in sample_arrays(context) {
1139 variants.push((array_ty, len * elem_size));
1140 }
1141 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1142 variants.push((struct_ty, struct_size));
1143 }
1144
1145 types.push((
1146 Type::new_union(
1147 context,
1148 variants.iter().map(|(field_ty, _)| *field_ty).collect(),
1149 ),
1150 variants,
1151 ));
1152
1153 return types;
1154
1155 fn add_unions_with_non_aggregate_types_of_length_in_bytes(
1156 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1157 context: &mut Context,
1158 variant_type_size_in_bytes: u64,
1159 ) {
1160 for ty in sample_non_aggregate_types(context) {
1161 if ty.size(context).in_bytes() != variant_type_size_in_bytes {
1162 continue;
1163 }
1164
1165 types.push((
1166 Type::new_union(context, vec![ty]),
1167 vec![(ty, variant_type_size_in_bytes)],
1168 ));
1169 }
1170 }
1171 }
1172
1173 fn sample_arrays_to_embed(context: &mut Context) -> Vec<(Type, u64, u64)> {
1177 let mut types = vec![];
1178
1179 for len in [0, 1, 7, 8, 15] {
1180 for elem_ty in sample_non_aggregate_types(context) {
1181 types.push((
1182 Type::new_array(context, elem_ty, len),
1183 len,
1184 elem_ty.size(context).in_bytes(),
1185 ));
1186 }
1187 }
1188
1189 types
1190 }
1191
1192 fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1195 let mut types = vec![];
1196
1197 for field_ty in sample_non_aggregate_types(context) {
1199 types.push((
1203 Type::new_struct(context, vec![field_ty]),
1204 field_ty.size(context).in_bytes_aligned(),
1205 ));
1206 }
1207
1208 let field_types = sample_non_aggregate_types(context);
1210 for (index, first_field_ty) in field_types.iter().enumerate() {
1211 for second_field_type in field_types.iter().skip(index) {
1212 let struct_size = first_field_ty.size(context).in_bytes_aligned()
1215 + second_field_type.size(context).in_bytes_aligned();
1216 types.push((
1217 Type::new_struct(context, vec![*first_field_ty, *second_field_type]),
1218 struct_size,
1219 ));
1220 }
1221 }
1222
1223 let field_types = sample_non_aggregate_types(context);
1225 let struct_size = field_types
1226 .iter()
1227 .map(|ty| ty.size(context).in_bytes_aligned())
1228 .sum();
1229 types.push((Type::new_struct(context, field_types), struct_size));
1230
1231 types
1232 }
1233
1234 fn all_sample_types(context: &mut Context) -> Vec<Type> {
1237 let mut types = vec![];
1238
1239 types.extend(sample_non_aggregate_types(context));
1240 types.extend(
1241 sample_arrays(context)
1242 .into_iter()
1243 .map(|(array_ty, _, _)| array_ty),
1244 );
1245 types.extend(
1246 sample_structs(context)
1247 .into_iter()
1248 .map(|(array_ty, __)| array_ty),
1249 );
1250
1251 types
1252 }
1253 }
1254}