sway_ir/
irtype.rs

1//! Each of the valid `Value` types.
2//!
3//! These generally mimic the Sway types with a couple of exceptions:
4//! - [`Type::Unit`] is still a discrete type rather than an empty tuple.  This may change in the
5//!   future.
6//! - [`Type::Union`] is a sum type which resembles a C union.  Each member of the union uses the
7//!   same storage and the size of the union is the size of the largest member.
8//!
9//! [`Aggregate`] is an abstract collection of [`Type`]s used for structs, unions and arrays,
10//! though see below for future improvements around splitting arrays into a different construct.
11
12use 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        // Trying to avoiding cloning t unless we're creating a new type.
48        #[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    /// Get Type if it already exists.
59    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    /// Get the content for this [Type].
77    pub fn get_content<'a>(&self, context: &'a Context) -> &'a TypeContent {
78        &context.types[self.0]
79    }
80
81    /// Get never type
82    pub fn get_never(context: &Context) -> Type {
83        Self::get_type(context, &TypeContent::Never).expect("create_basic_types not called")
84    }
85
86    /// Get unit type
87    pub fn get_unit(context: &Context) -> Type {
88        Self::get_type(context, &TypeContent::Unit).expect("create_basic_types not called")
89    }
90
91    /// Get bool type
92    pub fn get_bool(context: &Context) -> Type {
93        Self::get_type(context, &TypeContent::Bool).expect("create_basic_types not called")
94    }
95
96    /// New unsigned integer type
97    pub fn new_uint(context: &mut Context, width: u16) -> Type {
98        Self::get_or_create_unique_type(context, TypeContent::Uint(width))
99    }
100
101    /// New u8 type
102    pub fn get_uint8(context: &Context) -> Type {
103        Self::get_type(context, &TypeContent::Uint(8)).expect("create_basic_types not called")
104    }
105
106    /// New u16 type
107    pub fn get_uint16(context: &Context) -> Type {
108        Self::get_type(context, &TypeContent::Uint(16)).expect("create_basic_types not called")
109    }
110
111    /// New u32 type
112    pub fn get_uint32(context: &Context) -> Type {
113        Self::get_type(context, &TypeContent::Uint(32)).expect("create_basic_types not called")
114    }
115
116    /// New u64 type
117    pub fn get_uint64(context: &Context) -> Type {
118        Self::get_type(context, &TypeContent::Uint(64)).expect("create_basic_types not called")
119    }
120
121    /// New u64 type
122    pub fn get_uint256(context: &Context) -> Type {
123        Self::get_type(context, &TypeContent::Uint(256)).expect("create_basic_types not called")
124    }
125
126    /// Get unsigned integer type
127    pub fn get_uint(context: &Context, width: u16) -> Option<Type> {
128        Self::get_type(context, &TypeContent::Uint(width))
129    }
130
131    /// Get B256 type
132    pub fn get_b256(context: &Context) -> Type {
133        Self::get_type(context, &TypeContent::B256).expect("create_basic_types not called")
134    }
135
136    /// Get string type
137    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    /// Get array type
142    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    /// Get union type
147    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    /// Get struct type
152    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    /// New pointer type
157    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    /// Get slice type
162    pub fn get_slice(context: &Context) -> Type {
163        Self::get_type(context, &TypeContent::Slice).expect("create_basic_types not called")
164    }
165
166    /// Get typed slice type
167    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    /// Return a string representation of type, used for printing.
172    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    /// Compare a type to this one for equivalence.
205    /// `PartialEq` does not take into account the special case for Unions below.
206    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            // Unions are special.  We say unions are equivalent to any of their variant types.
227            (_, TypeContent::Union(_)) => other.eq(context, self),
228            (TypeContent::Union(l), _) => l.iter().any(|field_ty| other.eq(context, field_ty)),
229            // Never type can coerce into any other type.
230            (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    /// Is Never type
238    pub fn is_never(&self, context: &Context) -> bool {
239        matches!(*self.get_content(context), TypeContent::Never)
240    }
241
242    /// Is bool type
243    pub fn is_bool(&self, context: &Context) -> bool {
244        matches!(*self.get_content(context), TypeContent::Bool)
245    }
246
247    /// Is unit type
248    pub fn is_unit(&self, context: &Context) -> bool {
249        matches!(*self.get_content(context), TypeContent::Unit)
250    }
251
252    /// Is unsigned integer type
253    pub fn is_uint(&self, context: &Context) -> bool {
254        matches!(*self.get_content(context), TypeContent::Uint(_))
255    }
256
257    /// Is u8 type
258    pub fn is_uint8(&self, context: &Context) -> bool {
259        matches!(*self.get_content(context), TypeContent::Uint(8))
260    }
261
262    /// Is u32 type
263    pub fn is_uint32(&self, context: &Context) -> bool {
264        matches!(*self.get_content(context), TypeContent::Uint(32))
265    }
266
267    /// Is u64 type
268    pub fn is_uint64(&self, context: &Context) -> bool {
269        matches!(*self.get_content(context), TypeContent::Uint(64))
270    }
271
272    /// Is unsigned integer type of specific width
273    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    /// Is B256 type
278    pub fn is_b256(&self, context: &Context) -> bool {
279        matches!(*self.get_content(context), TypeContent::B256)
280    }
281
282    /// Is string type
283    pub fn is_string_slice(&self, context: &Context) -> bool {
284        matches!(*self.get_content(context), TypeContent::StringSlice)
285    }
286
287    /// Is string type
288    pub fn is_string_array(&self, context: &Context) -> bool {
289        matches!(*self.get_content(context), TypeContent::StringArray(_))
290    }
291
292    /// Is array type
293    pub fn is_array(&self, context: &Context) -> bool {
294        matches!(*self.get_content(context), TypeContent::Array(..))
295    }
296
297    /// Is union type
298    pub fn is_union(&self, context: &Context) -> bool {
299        matches!(*self.get_content(context), TypeContent::Union(_))
300    }
301
302    /// Is struct type
303    pub fn is_struct(&self, context: &Context) -> bool {
304        matches!(*self.get_content(context), TypeContent::Struct(_))
305    }
306
307    /// Is enum type
308    pub fn is_enum(&self, context: &Context) -> bool {
309        // We have to do some painful special handling here for enums, which are tagged unions.
310        // This really should be handled by the IR more explicitly and is something that will
311        // hopefully be addressed by https://github.com/FuelLabs/sway/issues/2819#issuecomment-1256930392
312
313        // Enums are at the moment represented as structs with two fields, first one being
314        // the tag and second the union of variants. Enums are the only place we currently use unions
315        // which makes the below heuristics valid.
316        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    /// Is aggregate type: struct, union, enum or array.
326    pub fn is_aggregate(&self, context: &Context) -> bool {
327        // Notice that enums are structs of tags and unions.
328        self.is_struct(context) || self.is_union(context) || self.is_array(context)
329    }
330
331    /// Returns true if `self` is a slice type.
332    pub fn is_slice(&self, context: &Context) -> bool {
333        matches!(*self.get_content(context), TypeContent::Slice)
334    }
335
336    // TODO: (REFERENCES) Check all the usages of `is_ptr`.
337    /// Returns true if `self` is a pointer type.
338    pub fn is_ptr(&self, context: &Context) -> bool {
339        matches!(*self.get_content(context), TypeContent::Pointer(_))
340    }
341
342    /// Get pointed to type iff `self`` is a pointer.
343    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    /// Get width of an integer type.
352    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    /// What's the type of the struct/array value indexed by indices.
361    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    /// What's the type of the struct/array value indexed by indices.
376    pub fn get_value_indexed_type(&self, context: &Context, indices: &[Value]) -> Option<Type> {
377        // Fetch the field type from the vector of Values.  If the value is a constant int then
378        // unwrap it and try to fetch the field type (which will fail for arrays) otherwise (i.e.,
379        // not a constant int or not a struct) fetch the array element type, which will fail for
380        // non-arrays.
381        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    /// What's the offset, in bytes, of the indexed element?
397    /// Returns `None` on invalid indices.
398    /// Panics if `self` is not an aggregate (struct, union, or array).
399    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                    // Sum up all sizes of all previous fields.
405                    // Every struct field is aligned to word boundary.
406                    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                    // Union variants have their raw size in bytes and are
414                    // left padded within the union.
415                    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                    // size_of_element * idx will be the offset of idx.
430                    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    /// What's the offset, in bytes, of the value indexed element?
445    /// It may not always be possible to determine statically.
446    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            // Trying to index a non-aggregate.
472            None
473        }
474    }
475
476    /// Get the type of the array element, if applicable.
477    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    /// Get the type of the array element, if applicable.
486    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    /// Get the length of the array , if applicable.
495    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    /// Get the length of a string.
504    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    /// Get the type of each field of a struct Type. Empty vector otherwise.
513    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    /// Get the offset, in bytes, and the [Type] of the struct field at the index `field_idx`, if `self` is a struct,
521    /// otherwise `None`.
522    /// Panics if the `field_idx` is out of bounds.
523    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                // Struct fields are aligned to word boundary.
539                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    /// Get the offset, in bytes, and the [Type] of the union field at the index `field_idx`, if `self` is a union,
547    /// otherwise `None`.
548    /// Panics if the `field_idx` is out of bounds.
549    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        // Union variants have their raw size in bytes and are
562        // left padded within the union.
563        let field_size_in_bytes = field_type.size(context).in_bytes();
564
565        // The union fields are at offset (union_size - field_size) due to left padding.
566        Some((union_size_in_bytes - field_size_in_bytes, field_type))
567    }
568
569    /// Returns the memory size of the [Type].
570    /// The returned `TypeSize::in_bytes` will provide the raw memory size of the `self`,
571    /// when it's not embedded in an aggregate.
572    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            // All integers larger than a byte are words since FuelVM only has memory operations on those two units.
578            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                // Sum up all the field sizes, aligned to words.
594                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                // Find the max size for field sizes.
603                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// This is a mouthful...
616#[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
623/// A helper to check if an Option<Type> value is of a particular Type.
624pub 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/// Provides information about a size of a type, raw and aligned to word boundaries.
635#[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    /// Returns the actual (unaligned) size of the type in bytes.
646    pub fn in_bytes(&self) -> u64 {
647        self.size_in_bytes
648    }
649
650    /// Returns the size of the type in bytes, aligned to word boundary.
651    pub fn in_bytes_aligned(&self) -> u64 {
652        (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
653    }
654
655    /// Returns the size of the type in words (aligned to word boundary).
656    pub fn in_words(&self) -> u64 {
657        self.size_in_bytes.div_ceil(8)
658    }
659}
660
661/// Provides information about padding expected when laying values in memory.
662/// Padding depends on the type of the value, but also on the embedding of
663/// the value in aggregates. E.g., in an array of `u8`, each `u8` is "padded"
664/// to its size of one byte while as a struct field, it will be right padded
665/// to 8 bytes.
666#[derive(Clone, Debug, serde::Serialize)]
667pub enum Padding {
668    Left { target_size: usize },
669    Right { target_size: usize },
670}
671
672impl Padding {
673    /// Returns the default [Padding] for `u8`.
674    pub fn default_for_u8(_value: u8) -> Self {
675        // Dummy _value is used only to ensure correct usage at the call site.
676        Self::Right { target_size: 1 }
677    }
678
679    /// Returns the default [Padding] for `u64`.
680    pub fn default_for_u64(_value: u64) -> Self {
681        // Dummy _value is used only to ensure correct usage at the call site.
682        Self::Right { target_size: 8 }
683    }
684
685    /// Returns the default [Padding] for a byte array.
686    pub fn default_for_byte_array(value: &[u8]) -> Self {
687        Self::Right {
688            target_size: value.len(),
689        }
690    }
691
692    /// Returns the default [Padding] for an aggregate.
693    /// `aggregate_size` is the overall size of the aggregate in bytes.
694    pub fn default_for_aggregate(aggregate_size: usize) -> Self {
695        Self::Right {
696            target_size: aggregate_size,
697        }
698    }
699
700    /// The target size in bytes.
701    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    /// Unit tests in this module document and assert decisions on memory layout.
713    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        /// Bool, when not embedded in aggregates, has a size of 1 byte.
722        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        /// Unit, when not embedded in aggregates, has a size of 1 byte.
732        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        /// `u8`, when not embedded in aggregates, has a size of 1 byte.
742        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        /// `u16`, `u32`, and `u64,`, when not embedded in aggregates, have a size of 8 bytes/1 word.
752        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        /// `u256`, when not embedded in aggregates, has a size of 32 bytes.
771        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        /// Pointer to any type, when not embedded in aggregates, has a size of 8 bytes/1 word.
782        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        /// Slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
795        /// The first word is the pointer to the actual content, and the second the
796        /// length of the slice.
797        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        /// `B256`, when not embedded in aggregates, has a size of 32 bytes.
808        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        /// String slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
819        /// The first word is the pointer to the actual content, and the second the
820        /// length of the slice.
821        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        /// String array, when not embedded in aggregates, has a size in bytes of its length, aligned to the word boundary.
833        /// Note that this differs from other arrays, which are packed but not, in addition, aligned to the word boundary.
834        /// The reason we have the alignment/padding in case of string arrays, is because of the current ABI encoding.
835        /// The output receipt returned by a contract call can be a string array, and the way the output is encoded
836        /// (at least for small strings) is by literally putting the ASCII bytes in the return value register.
837        /// For string arrays smaller than 8 bytes this poses a problem, because we have to fill the register with something
838        /// or start reading memory that isn't ours. And the workaround was to simply pad all string arrays with zeroes so
839        /// they're all at least 8 bytes long.
840        /// Thus, changing this behavior would be a breaking change in ABI compatibility.
841        /// Note that we do want to change this behavior in the future, as a part of either refactoring the ABI encoding
842        /// or proper support for slices.
843        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)); // Just in case.
848
849                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        /// Array, when not embedded in aggregates, has a size in bytes of its length multiplied by the size of its element's type.
863        /// Arrays are packed. The offset of the n-th element is `n * __size_of<ElementType>()`.
864        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)); // Just in case.
869
870                let s_array = array_ty.size(&context);
871
872                assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
873
874                // The size of the array is the length multiplied by the element size.
875                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                    // The offset of the element is its index multiplied by the element size.
883                    assert_eq!(elem_offset, elem_index * elem_size);
884                }
885            }
886        }
887
888        #[test]
889        /// Struct has a size in bytes of the sum of all of its fields.
890        /// The size of each individual field is a multiple of the word size. Thus,
891        /// if needed, fields are right-padded to the multiple of the word size.
892        /// Each individual field is aligned to the word boundary.
893        /// Struct fields are ordered in the order of their appearance in the struct definition.
894        /// The offset of each field is the sum of the sizes of the preceding fields.
895        /// Since the size of the each individual field is a multiple of the word size,
896        /// the size of the struct is also always a multiple of the word size.
897        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)); // Just in case.
902
903                let s_struct = struct_ty.size(&context);
904
905                // The size of the struct is the sum of the field sizes,
906                // where each field is, if needed, right-padded to the multiple of the
907                // word size.
908                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                // Structs' sizes are always multiples of the word size.
916                assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
917
918                for field_index in 0..fields.len() {
919                    // The offset of a field is the sum of the sizes of the previous fields.
920                    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        /// Union has a size in bytes of the largest of all of its variants,
942        /// where the largest variant is, if needed, left-padded to the multiple of the word size.
943        /// Variants overlap in memory and are left-padded (aligned to the right) to the size of the
944        /// largest variant (already _right_ aligned/left-padded to the word boundary).
945        /// Thus, a variant, in a general case, needs not to be aligned to the word boundary.
946        /// The offset of a variant, relative to the union address is:
947        ///
948        ///  `__size_of<UnionType>() - __size_of<VariantType>()`.
949        ///
950        /// Since the size of the largest variant is a multiple of the word size,
951        /// the size of the union is also always a multiple of the word size.
952        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)); // Just in case.
957
958                let s_union = union_ty.size(&context);
959
960                // The size of the union is the size of the largest variant,
961                // where the largest variant is, if needed, left-padded to the multiple
962                // of the word size.
963                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                // Unions' sizes are always multiples of the word size.
972                assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
973
974                for (variant_index, variant) in variants.iter().enumerate() {
975                    // Variants are left-padded.
976                    // The offset of a variant is the union size minus the raw variant size.
977                    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        // A bit of trickery just to avoid bloating test setups by having `SourceEngine`
994        // instantiation in every test.
995        // Not that we can't do the same with the `Context` because it must be isolated and
996        // unique in every test.
997        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        /// Creates sample types that are not aggregates and do not point to
1004        /// other types. Where applicable, several typical representatives of
1005        /// a type are created, e.g., string arrays of different sizes.
1006        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        /// Creates sample string array types of different lengths and
1030        /// returns the string array types and their respective lengths.
1031        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        /// Creates sample array types of different lengths and
1042        /// different element types and returns the created array types
1043        /// and their respective lengths and the size of the element type.
1044        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                    // As commented in other places, we trust the result of the
1050                    // `size` method for non-aggregate types.
1051                    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                    // We cannot use the `size` methods on arrays here because we use this
1060                    // samples to actually test it. We calculate the expected size manually
1061                    // according to the definition of the layout for the arrays.
1062                    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        /// Creates sample struct types and returns the created struct types
1078        /// and their respective field types and their raw size in bytes
1079        /// (when not embedded).
1080        fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1081            let mut types = vec![];
1082
1083            // Empty struct.
1084            types.push((Type::new_struct(context, vec![]), vec![]));
1085
1086            // Structs with only one 1-byte long field.
1087            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1088            // Structs with only one 1-word long field.
1089            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1090
1091            // Complex struct with fields of all non aggregate types, arrays, and structs.
1092            let mut fields = vec![];
1093            for ty in sample_non_aggregate_types(context) {
1094                // We can trust the result of the `size` method here,
1095                // because it is tested in tests for individual non-aggregate types.
1096                fields.push((ty, ty.size(context).in_bytes()));
1097            }
1098            for (array_ty, len, elem_size) in sample_arrays(context) {
1099                // We can't trust the result of the `size` method here,
1100                // because tests for arrays test embedded structs and vice versa.
1101                // So we will manually calculate the expected raw size in bytes,
1102                // as per the definition of the memory layout for the arrays.
1103                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        /// Creates sample union types and returns the created union types
1138        /// and their respective variant types and their raw size in bytes
1139        /// (when not embedded).
1140        fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1141            let mut types = vec![];
1142
1143            // Empty union.
1144            types.push((Type::new_union(context, vec![]), vec![]));
1145
1146            // Unions with only one 1-byte long variant.
1147            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1148            // Unions with only one 1-word long variant.
1149            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1150
1151            // Complex union with variants of all non aggregate types, arrays, and structs.
1152            // For the reasons for using the `size` method for non-aggregates vs
1153            // calculating sizes for non aggregates, see the comment in the
1154            // `sample_structs` function.
1155            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        /// Creates sample arrays to embed in other aggregates.
1195        /// Returns the created array types, its length, and the size
1196        /// of the element type.
1197        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        /// Creates sample structs to embed in other aggregates.
1214        /// Returns the struct type and size in bytes for each created struct.
1215        fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1216            let mut types = vec![];
1217
1218            // Create structs with just one field for each non_aggregate type.
1219            for field_ty in sample_non_aggregate_types(context) {
1220                // We can trust the result of the `size` method here,
1221                // because it is tested in tests for individual non-aggregate types.
1222                // We align it to the word boundary to satisfy the layout of structs.
1223                types.push((
1224                    Type::new_struct(context, vec![field_ty]),
1225                    field_ty.size(context).in_bytes_aligned(),
1226                ));
1227            }
1228
1229            // Create structs for pairwise combinations of field types.
1230            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                    // Again, we trust the `size` method called on non-aggregate types
1234                    // and calculate the struct size on our own.
1235                    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            // Create a struct with a field for each aggregate type.
1245            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        /// Returns all types that we can have, including several typical samples for
1256        /// aggregates like, e.g., arrays of different elements and different sizes.
1257        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}