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_value_indexed_type(&self, context: &Context, indices: &[Value]) -> Option<Type> {
377 indices.iter().try_fold(*self, |ty, idx_val| {
382 idx_val
383 .get_constant(context)
384 .and_then(|const_ref| {
385 if let ConstantValue::Uint(n) = const_ref.get_content(context).value {
386 Some(n)
387 } else {
388 None
389 }
390 })
391 .and_then(|idx| ty.get_field_type(context, idx))
392 .or_else(|| ty.get_array_elem_type(context))
393 })
394 }
395
396 pub fn get_indexed_offset(&self, context: &Context, indices: &[u64]) -> Option<u64> {
400 indices
401 .iter()
402 .try_fold((*self, 0), |(ty, accum_offset), idx| {
403 if ty.is_struct(context) {
404 let prev_idxs_offset = (0..(*idx)).try_fold(0, |accum, pre_idx| {
407 ty.get_field_type(context, pre_idx)
408 .map(|field_ty| accum + field_ty.size(context).in_bytes_aligned())
409 })?;
410 ty.get_field_type(context, *idx)
411 .map(|field_ty| (field_ty, accum_offset + prev_idxs_offset))
412 } else if ty.is_union(context) {
413 let union_size_in_bytes = ty.size(context).in_bytes();
416 ty.get_field_type(context, *idx).map(|field_ty| {
417 (
418 field_ty,
419 accum_offset
420 + (union_size_in_bytes - field_ty.size(context).in_bytes()),
421 )
422 })
423 } else {
424 assert!(
425 ty.is_array(context),
426 "Expected aggregate type. Got {}.",
427 ty.as_string(context)
428 );
429 ty.get_array_elem_type(context).map(|elm_ty| {
431 let prev_idxs_offset = ty
432 .get_array_elem_type(context)
433 .unwrap()
434 .size(context)
435 .in_bytes()
436 * idx;
437 (elm_ty, accum_offset + prev_idxs_offset)
438 })
439 }
440 })
441 .map(|pair| pair.1)
442 }
443
444 pub fn get_value_indexed_offset(&self, context: &Context, indices: &[Value]) -> Option<u64> {
447 let const_indices: Vec<_> = indices
448 .iter()
449 .map_while(|idx| {
450 if let Some(ConstantContent {
451 value: ConstantValue::Uint(idx),
452 ty: _,
453 }) = idx.get_constant(context).map(|c| c.get_content(context))
454 {
455 Some(*idx)
456 } else {
457 None
458 }
459 })
460 .collect();
461 (const_indices.len() == indices.len())
462 .then(|| self.get_indexed_offset(context, &const_indices))
463 .flatten()
464 }
465
466 pub fn get_field_type(&self, context: &Context, idx: u64) -> Option<Type> {
467 if let TypeContent::Struct(fields) | TypeContent::Union(fields) = self.get_content(context)
468 {
469 fields.get(idx as usize).cloned()
470 } else {
471 None
473 }
474 }
475
476 pub fn get_array_elem_type(&self, context: &Context) -> Option<Type> {
478 if let TypeContent::Array(ty, _) = *self.get_content(context) {
479 Some(ty)
480 } else {
481 None
482 }
483 }
484
485 pub fn get_typed_slice_elem_type(&self, context: &Context) -> Option<Type> {
487 if let TypeContent::TypedSlice(ty) = *self.get_content(context) {
488 Some(ty)
489 } else {
490 None
491 }
492 }
493
494 pub fn get_array_len(&self, context: &Context) -> Option<u64> {
496 if let TypeContent::Array(_, n) = *self.get_content(context) {
497 Some(n)
498 } else {
499 None
500 }
501 }
502
503 pub fn get_string_len(&self, context: &Context) -> Option<u64> {
505 if let TypeContent::StringArray(n) = *self.get_content(context) {
506 Some(n)
507 } else {
508 None
509 }
510 }
511
512 pub fn get_field_types(&self, context: &Context) -> Vec<Type> {
514 match self.get_content(context) {
515 TypeContent::Struct(fields) | TypeContent::Union(fields) => fields.clone(),
516 _ => vec![],
517 }
518 }
519
520 pub fn get_struct_field_offset_and_type(
524 &self,
525 context: &Context,
526 field_idx: u64,
527 ) -> Option<(u64, Type)> {
528 if !self.is_struct(context) {
529 return None;
530 }
531
532 let field_idx = field_idx as usize;
533 let field_types = self.get_field_types(context);
534 let field_offs_in_bytes = field_types
535 .iter()
536 .take(field_idx)
537 .map(|field_ty| {
538 field_ty.size(context).in_bytes_aligned()
540 })
541 .sum::<u64>();
542
543 Some((field_offs_in_bytes, field_types[field_idx]))
544 }
545
546 pub fn get_union_field_offset_and_type(
550 &self,
551 context: &Context,
552 field_idx: u64,
553 ) -> Option<(u64, Type)> {
554 if !self.is_union(context) {
555 return None;
556 }
557
558 let field_idx = field_idx as usize;
559 let field_type = self.get_field_types(context)[field_idx];
560 let union_size_in_bytes = self.size(context).in_bytes();
561 let field_size_in_bytes = field_type.size(context).in_bytes();
564
565 Some((union_size_in_bytes - field_size_in_bytes, field_type))
567 }
568
569 pub fn size(&self, context: &Context) -> TypeSize {
573 match self.get_content(context) {
574 TypeContent::Uint(8) | TypeContent::Bool | TypeContent::Unit | TypeContent::Never => {
575 TypeSize::new(1)
576 }
577 TypeContent::Uint(16)
579 | TypeContent::Uint(32)
580 | TypeContent::Uint(64)
581 | TypeContent::Pointer(_) => TypeSize::new(8),
582 TypeContent::Uint(256) => TypeSize::new(32),
583 TypeContent::Uint(_) => unreachable!(),
584 TypeContent::Slice => TypeSize::new(16),
585 TypeContent::TypedSlice(..) => TypeSize::new(16),
586 TypeContent::B256 => TypeSize::new(32),
587 TypeContent::StringSlice => TypeSize::new(16),
588 TypeContent::StringArray(n) => {
589 TypeSize::new(super::size_bytes_round_up_to_word_alignment!(*n))
590 }
591 TypeContent::Array(el_ty, cnt) => TypeSize::new(cnt * el_ty.size(context).in_bytes()),
592 TypeContent::Struct(field_tys) => {
593 TypeSize::new(
595 field_tys
596 .iter()
597 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
598 .sum(),
599 )
600 }
601 TypeContent::Union(field_tys) => {
602 TypeSize::new(
604 field_tys
605 .iter()
606 .map(|field_ty| field_ty.size(context).in_bytes_aligned())
607 .max()
608 .unwrap_or(0),
609 )
610 }
611 }
612 }
613}
614
615#[macro_export]
617macro_rules! size_bytes_round_up_to_word_alignment {
618 ($bytes_expr: expr) => {
619 ($bytes_expr + 7) - (($bytes_expr + 7) % 8)
620 };
621}
622
623pub trait TypeOption {
625 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool;
626}
627
628impl TypeOption for Option<Type> {
629 fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool {
630 self.filter(|ty| pred(ty, context)).is_some()
631 }
632}
633
634#[derive(Clone, Debug)]
636pub struct TypeSize {
637 size_in_bytes: u64,
638}
639
640impl TypeSize {
641 pub(crate) fn new(size_in_bytes: u64) -> Self {
642 Self { size_in_bytes }
643 }
644
645 pub fn in_bytes(&self) -> u64 {
647 self.size_in_bytes
648 }
649
650 pub fn in_bytes_aligned(&self) -> u64 {
652 (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
653 }
654
655 pub fn in_words(&self) -> u64 {
657 self.size_in_bytes.div_ceil(8)
658 }
659}
660
661#[derive(Clone, Debug, serde::Serialize)]
667pub enum Padding {
668 Left { target_size: usize },
669 Right { target_size: usize },
670}
671
672impl Padding {
673 pub fn default_for_u8(_value: u8) -> Self {
675 Self::Right { target_size: 1 }
677 }
678
679 pub fn default_for_u64(_value: u64) -> Self {
681 Self::Right { target_size: 8 }
683 }
684
685 pub fn default_for_byte_array(value: &[u8]) -> Self {
687 Self::Right {
688 target_size: value.len(),
689 }
690 }
691
692 pub fn default_for_aggregate(aggregate_size: usize) -> Self {
695 Self::Right {
696 target_size: aggregate_size,
697 }
698 }
699
700 pub fn target_size(&self) -> usize {
702 use Padding::*;
703 match self {
704 Left { target_size } | Right { target_size } => *target_size,
705 }
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 pub use super::*;
712 mod memory_layout {
714 use super::*;
715 use crate::Context;
716 use once_cell::sync::Lazy;
717 use sway_features::ExperimentalFeatures;
718 use sway_types::SourceEngine;
719
720 #[test]
721 fn boolean() {
723 let context = create_context();
724
725 let s_bool = Type::get_bool(&context).size(&context);
726
727 assert_eq!(s_bool.in_bytes(), 1);
728 }
729
730 #[test]
731 fn unit() {
733 let context = create_context();
734
735 let s_unit = Type::get_unit(&context).size(&context);
736
737 assert_eq!(s_unit.in_bytes(), 1);
738 }
739
740 #[test]
741 fn unsigned_u8() {
743 let context = create_context();
744
745 let s_u8 = Type::get_uint8(&context).size(&context);
746
747 assert_eq!(s_u8.in_bytes(), 1);
748 }
749
750 #[test]
751 fn unsigned_u16_u32_u64() {
753 let context = create_context();
754
755 let s_u16 = Type::get_uint16(&context).size(&context);
756 let s_u32 = Type::get_uint32(&context).size(&context);
757 let s_u64 = Type::get_uint64(&context).size(&context);
758
759 assert_eq!(s_u16.in_bytes(), 8);
760 assert_eq!(s_u16.in_bytes(), s_u16.in_bytes_aligned());
761
762 assert_eq!(s_u32.in_bytes(), 8);
763 assert_eq!(s_u32.in_bytes(), s_u32.in_bytes_aligned());
764
765 assert_eq!(s_u64.in_bytes(), 8);
766 assert_eq!(s_u64.in_bytes(), s_u64.in_bytes_aligned());
767 }
768
769 #[test]
770 fn unsigned_u256() {
772 let context = create_context();
773
774 let s_u256 = Type::get_uint256(&context).size(&context);
775
776 assert_eq!(s_u256.in_bytes(), 32);
777 assert_eq!(s_u256.in_bytes(), s_u256.in_bytes_aligned());
778 }
779
780 #[test]
781 fn pointer() {
783 let mut context = create_context();
784
785 for ty in all_sample_types(&mut context) {
786 let s_ptr = Type::new_ptr(&mut context, ty).size(&context);
787
788 assert_eq!(s_ptr.in_bytes(), 8);
789 assert_eq!(s_ptr.in_bytes(), s_ptr.in_bytes_aligned());
790 }
791 }
792
793 #[test]
794 fn slice() {
798 let context = create_context();
799
800 let s_slice = Type::get_slice(&context).size(&context);
801
802 assert_eq!(s_slice.in_bytes(), 16);
803 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
804 }
805
806 #[test]
807 fn b256() {
809 let context = create_context();
810
811 let s_b256 = Type::get_b256(&context).size(&context);
812
813 assert_eq!(s_b256.in_bytes(), 32);
814 assert_eq!(s_b256.in_bytes(), s_b256.in_bytes_aligned());
815 }
816
817 #[test]
818 fn string_slice() {
822 let mut context = create_context();
823
824 let s_slice = Type::get_or_create_unique_type(&mut context, TypeContent::StringSlice)
825 .size(&context);
826
827 assert_eq!(s_slice.in_bytes(), 16);
828 assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
829 }
830
831 #[test]
832 fn string_array() {
844 let mut context = create_context();
845
846 for (str_array_ty, len) in sample_string_arrays(&mut context) {
847 assert!(str_array_ty.is_string_array(&context)); let s_str_array = str_array_ty.size(&context);
850
851 assert_eq!(str_array_ty.get_string_len(&context).unwrap(), len);
852
853 assert_eq!(
854 s_str_array.in_bytes(),
855 size_bytes_round_up_to_word_alignment!(len)
856 );
857 assert_eq!(s_str_array.in_bytes(), s_str_array.in_bytes_aligned());
858 }
859 }
860
861 #[test]
862 fn array() {
865 let mut context = create_context();
866
867 for (array_ty, len, elem_size) in sample_arrays(&mut context) {
868 assert!(array_ty.is_array(&context)); let s_array = array_ty.size(&context);
871
872 assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
873
874 assert_eq!(s_array.in_bytes(), len * elem_size);
876
877 for elem_index in 0..len {
878 let elem_offset = array_ty
879 .get_indexed_offset(&context, &[elem_index])
880 .unwrap();
881
882 assert_eq!(elem_offset, elem_index * elem_size);
884 }
885 }
886 }
887
888 #[test]
889 fn r#struct() {
898 let mut context = create_context();
899
900 for (struct_ty, fields) in sample_structs(&mut context) {
901 assert!(struct_ty.is_struct(&context)); let s_struct = struct_ty.size(&context);
904
905 assert_eq!(
909 s_struct.in_bytes(),
910 fields
911 .iter()
912 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
913 .sum::<u64>()
914 );
915 assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
917
918 for field_index in 0..fields.len() {
919 let expected_offset = fields
921 .iter()
922 .take(field_index)
923 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
924 .sum::<u64>();
925
926 let field_offset = struct_ty
927 .get_indexed_offset(&context, &[field_index as u64])
928 .unwrap();
929 assert_eq!(field_offset, expected_offset);
930
931 let (field_offset, field_type) = struct_ty
932 .get_struct_field_offset_and_type(&context, field_index as u64)
933 .unwrap();
934 assert_eq!(field_offset, expected_offset);
935 assert_eq!(field_type, fields[field_index].0);
936 }
937 }
938 }
939
940 #[test]
941 fn union() {
953 let mut context = create_context();
954
955 for (union_ty, variants) in sample_unions(&mut context) {
956 assert!(union_ty.is_union(&context)); let s_union = union_ty.size(&context);
959
960 assert_eq!(
964 s_union.in_bytes(),
965 variants
966 .iter()
967 .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
968 .max()
969 .unwrap_or_default()
970 );
971 assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
973
974 for (variant_index, variant) in variants.iter().enumerate() {
975 let expected_offset = s_union.in_bytes() - variant.1;
978
979 let variant_offset = union_ty
980 .get_indexed_offset(&context, &[variant_index as u64])
981 .unwrap();
982 assert_eq!(variant_offset, expected_offset);
983
984 let (variant_offset, field_type) = union_ty
985 .get_union_field_offset_and_type(&context, variant_index as u64)
986 .unwrap();
987 assert_eq!(variant_offset, expected_offset);
988 assert_eq!(field_type, variant.0);
989 }
990 }
991 }
992
993 static SOURCE_ENGINE: Lazy<SourceEngine> = Lazy::new(SourceEngine::default);
998
999 fn create_context() -> Context<'static> {
1000 Context::new(&SOURCE_ENGINE, ExperimentalFeatures::default())
1001 }
1002
1003 fn sample_non_aggregate_types(context: &mut Context) -> Vec<Type> {
1007 let mut types = vec![
1008 Type::get_bool(context),
1009 Type::get_unit(context),
1010 Type::get_uint(context, 8).unwrap(),
1011 Type::get_uint(context, 16).unwrap(),
1012 Type::get_uint(context, 32).unwrap(),
1013 Type::get_uint(context, 64).unwrap(),
1014 Type::get_uint(context, 256).unwrap(),
1015 Type::get_b256(context),
1016 Type::get_slice(context),
1017 Type::get_or_create_unique_type(context, TypeContent::StringSlice),
1018 ];
1019
1020 types.extend(
1021 sample_string_arrays(context)
1022 .into_iter()
1023 .map(|(string_array, _)| string_array),
1024 );
1025
1026 types
1027 }
1028
1029 fn sample_string_arrays(context: &mut Context) -> Vec<(Type, u64)> {
1032 let mut types = vec![];
1033
1034 for len in [0, 1, 7, 8, 15] {
1035 types.push((Type::new_string_array(context, len), len));
1036 }
1037
1038 types
1039 }
1040
1041 fn sample_arrays(context: &mut Context) -> Vec<(Type, u64, u64)> {
1045 let mut types = vec![];
1046
1047 for len in [0, 1, 7, 8, 15] {
1048 for ty in sample_non_aggregate_types(context) {
1049 types.push((
1052 Type::new_array(context, ty, len),
1053 len,
1054 ty.size(context).in_bytes(),
1055 ));
1056 }
1057
1058 for (array_ty, array_len, elem_size) in sample_arrays_to_embed(context) {
1059 types.push((
1063 Type::new_array(context, array_ty, len),
1064 len,
1065 array_len * elem_size,
1066 ));
1067 }
1068
1069 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1070 types.push((Type::new_array(context, struct_ty, len), len, struct_size));
1071 }
1072 }
1073
1074 types
1075 }
1076
1077 fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1081 let mut types = vec![];
1082
1083 types.push((Type::new_struct(context, vec![]), vec![]));
1085
1086 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1088 add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1090
1091 let mut fields = vec![];
1093 for ty in sample_non_aggregate_types(context) {
1094 fields.push((ty, ty.size(context).in_bytes()));
1097 }
1098 for (array_ty, len, elem_size) in sample_arrays(context) {
1099 fields.push((array_ty, len * elem_size));
1104 }
1105 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1106 fields.push((struct_ty, struct_size));
1107 }
1108
1109 types.push((
1110 Type::new_struct(
1111 context,
1112 fields.iter().map(|(field_ty, _)| *field_ty).collect(),
1113 ),
1114 fields,
1115 ));
1116
1117 return types;
1118
1119 fn add_structs_with_non_aggregate_types_of_length_in_bytes(
1120 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1121 context: &mut Context,
1122 field_type_size_in_bytes: u64,
1123 ) {
1124 for ty in sample_non_aggregate_types(context) {
1125 if ty.size(context).in_bytes() != field_type_size_in_bytes {
1126 continue;
1127 }
1128
1129 types.push((
1130 Type::new_struct(context, vec![ty]),
1131 vec![(ty, field_type_size_in_bytes)],
1132 ));
1133 }
1134 }
1135 }
1136
1137 fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1141 let mut types = vec![];
1142
1143 types.push((Type::new_union(context, vec![]), vec![]));
1145
1146 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1148 add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1150
1151 let mut variants = vec![];
1156 for ty in sample_non_aggregate_types(context) {
1157 variants.push((ty, ty.size(context).in_bytes()));
1158 }
1159 for (array_ty, len, elem_size) in sample_arrays(context) {
1160 variants.push((array_ty, len * elem_size));
1161 }
1162 for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1163 variants.push((struct_ty, struct_size));
1164 }
1165
1166 types.push((
1167 Type::new_union(
1168 context,
1169 variants.iter().map(|(field_ty, _)| *field_ty).collect(),
1170 ),
1171 variants,
1172 ));
1173
1174 return types;
1175
1176 fn add_unions_with_non_aggregate_types_of_length_in_bytes(
1177 types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1178 context: &mut Context,
1179 variant_type_size_in_bytes: u64,
1180 ) {
1181 for ty in sample_non_aggregate_types(context) {
1182 if ty.size(context).in_bytes() != variant_type_size_in_bytes {
1183 continue;
1184 }
1185
1186 types.push((
1187 Type::new_union(context, vec![ty]),
1188 vec![(ty, variant_type_size_in_bytes)],
1189 ));
1190 }
1191 }
1192 }
1193
1194 fn sample_arrays_to_embed(context: &mut Context) -> Vec<(Type, u64, u64)> {
1198 let mut types = vec![];
1199
1200 for len in [0, 1, 7, 8, 15] {
1201 for elem_ty in sample_non_aggregate_types(context) {
1202 types.push((
1203 Type::new_array(context, elem_ty, len),
1204 len,
1205 elem_ty.size(context).in_bytes(),
1206 ));
1207 }
1208 }
1209
1210 types
1211 }
1212
1213 fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1216 let mut types = vec![];
1217
1218 for field_ty in sample_non_aggregate_types(context) {
1220 types.push((
1224 Type::new_struct(context, vec![field_ty]),
1225 field_ty.size(context).in_bytes_aligned(),
1226 ));
1227 }
1228
1229 let field_types = sample_non_aggregate_types(context);
1231 for (index, first_field_ty) in field_types.iter().enumerate() {
1232 for second_field_type in field_types.iter().skip(index) {
1233 let struct_size = first_field_ty.size(context).in_bytes_aligned()
1236 + second_field_type.size(context).in_bytes_aligned();
1237 types.push((
1238 Type::new_struct(context, vec![*first_field_ty, *second_field_type]),
1239 struct_size,
1240 ));
1241 }
1242 }
1243
1244 let field_types = sample_non_aggregate_types(context);
1246 let struct_size = field_types
1247 .iter()
1248 .map(|ty| ty.size(context).in_bytes_aligned())
1249 .sum();
1250 types.push((Type::new_struct(context, field_types), struct_size));
1251
1252 types
1253 }
1254
1255 fn all_sample_types(context: &mut Context) -> Vec<Type> {
1258 let mut types = vec![];
1259
1260 types.extend(sample_non_aggregate_types(context));
1261 types.extend(
1262 sample_arrays(context)
1263 .into_iter()
1264 .map(|(array_ty, _, _)| array_ty),
1265 );
1266 types.extend(
1267 sample_structs(context)
1268 .into_iter()
1269 .map(|(array_ty, __)| array_ty),
1270 );
1271
1272 types
1273 }
1274 }
1275}