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-IG: 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 offset, in bytes, of the indexed element?
376    /// Returns `None` on invalid indices.
377    /// Panics if `self` is not an aggregate (struct, union, or array).
378    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                    // Sum up all sizes of all previous fields.
384                    // Every struct field is aligned to word boundary.
385                    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                    // Union variants have their raw size in bytes and are
393                    // left padded within the union.
394                    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                    // size_of_element * idx will be the offset of idx.
409                    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    /// What's the offset, in bytes, of the value indexed element?
424    /// It may not always be possible to determine statically.
425    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            // Trying to index a non-aggregate.
451            None
452        }
453    }
454
455    /// Get the type of the array element, if applicable.
456    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    /// Get the type of the array element, if applicable.
465    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    /// Get the length of the array , if applicable.
474    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    /// Get the length of a string.
483    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    /// Get the type of each field of a struct Type. Empty vector otherwise.
492    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    /// Get the offset, in bytes, and the [Type] of the struct field at the index `field_idx`, if `self` is a struct,
500    /// otherwise `None`.
501    /// Panics if the `field_idx` is out of bounds.
502    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                // Struct fields are aligned to word boundary.
518                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    /// Get the offset, in bytes, and the [Type] of the union field at the index `field_idx`, if `self` is a union,
526    /// otherwise `None`.
527    /// Panics if the `field_idx` is out of bounds.
528    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        // Union variants have their raw size in bytes and are
541        // left padded within the union.
542        let field_size_in_bytes = field_type.size(context).in_bytes();
543
544        // The union fields are at offset (union_size - field_size) due to left padding.
545        Some((union_size_in_bytes - field_size_in_bytes, field_type))
546    }
547
548    /// Returns the memory size of the [Type].
549    /// The returned `TypeSize::in_bytes` will provide the raw memory size of the `self`,
550    /// when it's not embedded in an aggregate.
551    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            // All integers larger than a byte are words since FuelVM only has memory operations on those two units.
557            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                // Sum up all the field sizes, aligned to words.
573                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                // Find the max size for field sizes.
582                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// This is a mouthful...
595#[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
602/// A helper to check if an Option<Type> value is of a particular Type.
603pub 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/// Provides information about a size of a type, raw and aligned to word boundaries.
614#[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    /// Returns the actual (unaligned) size of the type in bytes.
625    pub fn in_bytes(&self) -> u64 {
626        self.size_in_bytes
627    }
628
629    /// Returns the size of the type in bytes, aligned to word boundary.
630    pub fn in_bytes_aligned(&self) -> u64 {
631        (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
632    }
633
634    /// Returns the size of the type in words (aligned to word boundary).
635    pub fn in_words(&self) -> u64 {
636        (self.size_in_bytes + 7) / 8
637    }
638}
639
640/// Provides information about padding expected when laying values in memory.
641/// Padding depends on the type of the value, but also on the embedding of
642/// the value in aggregates. E.g., in an array of `u8`, each `u8` is "padded"
643/// to its size of one byte while as a struct field, it will be right padded
644/// to 8 bytes.
645#[derive(Clone, Debug, serde::Serialize)]
646pub enum Padding {
647    Left { target_size: usize },
648    Right { target_size: usize },
649}
650
651impl Padding {
652    /// Returns the default [Padding] for `u8`.
653    pub fn default_for_u8(_value: u8) -> Self {
654        // Dummy _value is used only to ensure correct usage at the call site.
655        Self::Right { target_size: 1 }
656    }
657
658    /// Returns the default [Padding] for `u64`.
659    pub fn default_for_u64(_value: u64) -> Self {
660        // Dummy _value is used only to ensure correct usage at the call site.
661        Self::Right { target_size: 8 }
662    }
663
664    /// Returns the default [Padding] for a byte array.
665    pub fn default_for_byte_array(value: &[u8]) -> Self {
666        Self::Right {
667            target_size: value.len(),
668        }
669    }
670
671    /// Returns the default [Padding] for an aggregate.
672    /// `aggregate_size` is the overall size of the aggregate in bytes.
673    pub fn default_for_aggregate(aggregate_size: usize) -> Self {
674        Self::Right {
675            target_size: aggregate_size,
676        }
677    }
678
679    /// The target size in bytes.
680    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    /// Unit tests in this module document and assert decisions on memory layout.
692    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        /// Bool, when not embedded in aggregates, has a size of 1 byte.
701        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        /// Unit, when not embedded in aggregates, has a size of 1 byte.
711        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        /// `u8`, when not embedded in aggregates, has a size of 1 byte.
721        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        /// `u16`, `u32`, and `u64,`, when not embedded in aggregates, have a size of 8 bytes/1 word.
731        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        /// `u256`, when not embedded in aggregates, has a size of 32 bytes.
750        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        /// Pointer to any type, when not embedded in aggregates, has a size of 8 bytes/1 word.
761        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        /// Slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
774        /// The first word is the pointer to the actual content, and the second the
775        /// length of the slice.
776        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        /// `B256`, when not embedded in aggregates, has a size of 32 bytes.
787        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        /// String slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
798        /// The first word is the pointer to the actual content, and the second the
799        /// length of the slice.
800        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        /// String array, when not embedded in aggregates, has a size in bytes of its length, aligned to the word boundary.
812        /// Note that this differs from other arrays, which are packed but not, in addition, aligned to the word boundary.
813        /// The reason we have the alignment/padding in case of string arrays, is because of the current ABI encoding.
814        /// The output receipt returned by a contract call can be a string array, and the way the output is encoded
815        /// (at least for small strings) is by literally putting the ASCII bytes in the return value register.
816        /// For string arrays smaller than 8 bytes this poses a problem, because we have to fill the register with something
817        /// or start reading memory that isn't ours. And the workaround was to simply pad all string arrays with zeroes so
818        /// they're all at least 8 bytes long.
819        /// Thus, changing this behavior would be a breaking change in ABI compatibility.
820        /// Note that we do want to change this behavior in the future, as a part of either refactoring the ABI encoding
821        /// or proper support for slices.
822        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)); // Just in case.
827
828                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        /// Array, when not embedded in aggregates, has a size in bytes of its length multiplied by the size of its element's type.
842        /// Arrays are packed. The offset of the n-th element is `n * __size_of<ElementType>()`.
843        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)); // Just in case.
848
849                let s_array = array_ty.size(&context);
850
851                assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
852
853                // The size of the array is the length multiplied by the element size.
854                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                    // The offset of the element is its index multiplied by the element size.
862                    assert_eq!(elem_offset, elem_index * elem_size);
863                }
864            }
865        }
866
867        #[test]
868        /// Struct has a size in bytes of the sum of all of its fields.
869        /// The size of each individual field is a multiple of the word size. Thus,
870        /// if needed, fields are right-padded to the multiple of the word size.
871        /// Each individual field is aligned to the word boundary.
872        /// Struct fields are ordered in the order of their appearance in the struct definition.
873        /// The offset of each field is the sum of the sizes of the preceding fields.
874        /// Since the size of the each individual field is a multiple of the word size,
875        /// the size of the struct is also always a multiple of the word size.
876        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)); // Just in case.
881
882                let s_struct = struct_ty.size(&context);
883
884                // The size of the struct is the sum of the field sizes,
885                // where each field is, if needed, right-padded to the multiple of the
886                // word size.
887                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                // Structs' sizes are always multiples of the word size.
895                assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
896
897                for field_index in 0..fields.len() {
898                    // The offset of a field is the sum of the sizes of the previous fields.
899                    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        /// Union has a size in bytes of the largest of all of its variants,
921        /// where the largest variant is, if needed, left-padded to the multiple of the word size.
922        /// Variants overlap in memory and are left-padded (aligned to the right) to the size of the
923        /// largest variant (already _right_ aligned/left-padded to the word boundary).
924        /// Thus, a variant, in a general case, needs not to be aligned to the word boundary.
925        /// The offset of a variant, relative to the union address is:
926        ///
927        ///  `__size_of<UnionType>() - __size_of<VariantType>()`.
928        ///
929        /// Since the size of the largest variant is a multiple of the word size,
930        /// the size of the union is also always a multiple of the word size.
931        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)); // Just in case.
936
937                let s_union = union_ty.size(&context);
938
939                // The size of the union is the size of the largest variant,
940                // where the largest variant is, if needed, left-padded to the multiple
941                // of the word size.
942                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                // Unions' sizes are always multiples of the word size.
951                assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
952
953                for (variant_index, variant) in variants.iter().enumerate() {
954                    // Variants are left-padded.
955                    // The offset of a variant is the union size minus the raw variant size.
956                    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        // A bit of trickery just to avoid bloating test setups by having `SourceEngine`
973        // instantiation in every test.
974        // Not that we can't do the same with the `Context` because it must be isolated and
975        // unique in every test.
976        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        /// Creates sample types that are not aggregates and do not point to
983        /// other types. Where applicable, several typical representatives of
984        /// a type are created, e.g., string arrays of different sizes.
985        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        /// Creates sample string array types of different lengths and
1009        /// returns the string array types and their respective lengths.
1010        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        /// Creates sample array types of different lengths and
1021        /// different element types and returns the created array types
1022        /// and their respective lengths and the size of the element type.
1023        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                    // As commented in other places, we trust the result of the
1029                    // `size` method for non-aggregate types.
1030                    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                    // We cannot use the `size` methods on arrays here because we use this
1039                    // samples to actually test it. We calculate the expected size manually
1040                    // according to the definition of the layout for the arrays.
1041                    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        /// Creates sample struct types and returns the created struct types
1057        /// and their respective field types and their raw size in bytes
1058        /// (when not embedded).
1059        fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1060            let mut types = vec![];
1061
1062            // Empty struct.
1063            types.push((Type::new_struct(context, vec![]), vec![]));
1064
1065            // Structs with only one 1-byte long field.
1066            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1067            // Structs with only one 1-word long field.
1068            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1069
1070            // Complex struct with fields of all non aggregate types, arrays, and structs.
1071            let mut fields = vec![];
1072            for ty in sample_non_aggregate_types(context) {
1073                // We can trust the result of the `size` method here,
1074                // because it is tested in tests for individual non-aggregate types.
1075                fields.push((ty, ty.size(context).in_bytes()));
1076            }
1077            for (array_ty, len, elem_size) in sample_arrays(context) {
1078                // We can't trust the result of the `size` method here,
1079                // because tests for arrays test embedded structs and vice versa.
1080                // So we will manually calculate the expected raw size in bytes,
1081                // as per the definition of the memory layout for the arrays.
1082                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        /// Creates sample union types and returns the created union types
1117        /// and their respective variant types and their raw size in bytes
1118        /// (when not embedded).
1119        fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1120            let mut types = vec![];
1121
1122            // Empty union.
1123            types.push((Type::new_union(context, vec![]), vec![]));
1124
1125            // Unions with only one 1-byte long variant.
1126            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1127            // Unions with only one 1-word long variant.
1128            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1129
1130            // Complex union with variants of all non aggregate types, arrays, and structs.
1131            // For the reasons for using the `size` method for non-aggregates vs
1132            // calculating sizes for non aggregates, see the comment in the
1133            // `sample_structs` function.
1134            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        /// Creates sample arrays to embed in other aggregates.
1174        /// Returns the created array types, its length, and the size
1175        /// of the element type.
1176        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        /// Creates sample structs to embed in other aggregates.
1193        /// Returns the struct type and size in bytes for each created struct.
1194        fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1195            let mut types = vec![];
1196
1197            // Create structs with just one field for each non_aggregate type.
1198            for field_ty in sample_non_aggregate_types(context) {
1199                // We can trust the result of the `size` method here,
1200                // because it is tested in tests for individual non-aggregate types.
1201                // We align it to the word boundary to satisfy the layout of structs.
1202                types.push((
1203                    Type::new_struct(context, vec![field_ty]),
1204                    field_ty.size(context).in_bytes_aligned(),
1205                ));
1206            }
1207
1208            // Create structs for pairwise combinations of field types.
1209            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                    // Again, we trust the `size` method called on non-aggregate types
1213                    // and calculate the struct size on our own.
1214                    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            // Create a struct with a field for each aggregate type.
1224            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        /// Returns all types that we can have, including several typical samples for
1235        /// aggregates like, e.g., arrays of different elements and different sizes.
1236        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}