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,
42    TypedPointer(Type),
43    TypedSlice(Type),
44}
45
46impl Type {
47    fn get_or_create_unique_type(context: &mut Context, t: TypeContent) -> Type {
48        // Trying to avoiding cloning t unless we're creating a new type.
49        #[allow(clippy::map_entry)]
50        if !context.type_map.contains_key(&t) {
51            let new_type = Type(context.types.insert(t.clone()));
52            context.type_map.insert(t, new_type);
53            new_type
54        } else {
55            context.type_map.get(&t).copied().unwrap()
56        }
57    }
58
59    /// Get Type if it already exists.
60    pub fn get_type(context: &Context, t: &TypeContent) -> Option<Type> {
61        context.type_map.get(t).copied()
62    }
63
64    pub fn create_basic_types(context: &mut Context) {
65        Self::get_or_create_unique_type(context, TypeContent::Never);
66        Self::get_or_create_unique_type(context, TypeContent::Unit);
67        Self::get_or_create_unique_type(context, TypeContent::Bool);
68        Self::get_or_create_unique_type(context, TypeContent::Uint(8));
69        Self::get_or_create_unique_type(context, TypeContent::Uint(16));
70        Self::get_or_create_unique_type(context, TypeContent::Uint(32));
71        Self::get_or_create_unique_type(context, TypeContent::Uint(64));
72        Self::get_or_create_unique_type(context, TypeContent::Uint(256));
73        Self::get_or_create_unique_type(context, TypeContent::B256);
74        Self::get_or_create_unique_type(context, TypeContent::Slice);
75        Self::get_or_create_unique_type(context, TypeContent::Pointer);
76    }
77
78    /// Get the content for this [Type].
79    pub fn get_content<'a>(&self, context: &'a Context) -> &'a TypeContent {
80        &context.types[self.0]
81    }
82
83    /// Get never type
84    pub fn get_never(context: &Context) -> Type {
85        Self::get_type(context, &TypeContent::Never).expect("create_basic_types not called")
86    }
87
88    /// Get unit type
89    pub fn get_unit(context: &Context) -> Type {
90        Self::get_type(context, &TypeContent::Unit).expect("create_basic_types not called")
91    }
92
93    /// Get bool type
94    pub fn get_bool(context: &Context) -> Type {
95        Self::get_type(context, &TypeContent::Bool).expect("create_basic_types not called")
96    }
97
98    /// New unsigned integer type
99    pub fn new_uint(context: &mut Context, width: u16) -> Type {
100        Self::get_or_create_unique_type(context, TypeContent::Uint(width))
101    }
102
103    /// New u8 type
104    pub fn get_uint8(context: &Context) -> Type {
105        Self::get_type(context, &TypeContent::Uint(8)).expect("create_basic_types not called")
106    }
107
108    /// New u16 type
109    pub fn get_uint16(context: &Context) -> Type {
110        Self::get_type(context, &TypeContent::Uint(16)).expect("create_basic_types not called")
111    }
112
113    /// New u32 type
114    pub fn get_uint32(context: &Context) -> Type {
115        Self::get_type(context, &TypeContent::Uint(32)).expect("create_basic_types not called")
116    }
117
118    /// New u64 type
119    pub fn get_uint64(context: &Context) -> Type {
120        Self::get_type(context, &TypeContent::Uint(64)).expect("create_basic_types not called")
121    }
122
123    /// New u64 type
124    pub fn get_uint256(context: &Context) -> Type {
125        Self::get_type(context, &TypeContent::Uint(256)).expect("create_basic_types not called")
126    }
127
128    /// Get unsigned integer type
129    pub fn get_uint(context: &Context, width: u16) -> Option<Type> {
130        Self::get_type(context, &TypeContent::Uint(width))
131    }
132
133    /// Get B256 type
134    pub fn get_b256(context: &Context) -> Type {
135        Self::get_type(context, &TypeContent::B256).expect("create_basic_types not called")
136    }
137
138    /// Get untyped pointer type
139    pub fn get_ptr(context: &Context) -> Type {
140        Self::get_type(context, &TypeContent::Pointer).expect("create_basic_types not called")
141    }
142
143    pub fn new_untyped_slice(context: &mut Context) -> Type {
144        Self::get_or_create_unique_type(context, TypeContent::Slice)
145    }
146
147    /// Get string type
148    pub fn new_string_array(context: &mut Context, len: u64) -> Type {
149        Self::get_or_create_unique_type(context, TypeContent::StringArray(len))
150    }
151
152    /// Get array type
153    pub fn new_array(context: &mut Context, elm_ty: Type, len: u64) -> Type {
154        Self::get_or_create_unique_type(context, TypeContent::Array(elm_ty, len))
155    }
156
157    /// Get union type
158    pub fn new_union(context: &mut Context, fields: Vec<Type>) -> Type {
159        Self::get_or_create_unique_type(context, TypeContent::Union(fields))
160    }
161
162    /// Get struct type
163    pub fn new_struct(context: &mut Context, fields: Vec<Type>) -> Type {
164        Self::get_or_create_unique_type(context, TypeContent::Struct(fields))
165    }
166
167    /// New pointer type
168    pub fn new_typed_pointer(context: &mut Context, to_ty: Type) -> Type {
169        Self::get_or_create_unique_type(context, TypeContent::TypedPointer(to_ty))
170    }
171
172    /// Get slice type
173    pub fn get_slice(context: &Context) -> Type {
174        Self::get_type(context, &TypeContent::Slice).expect("create_basic_types not called")
175    }
176
177    /// Get typed slice type
178    pub fn get_typed_slice(context: &mut Context, item_ty: Type) -> Type {
179        Self::get_or_create_unique_type(context, TypeContent::TypedSlice(item_ty))
180    }
181
182    /// Return a string representation of type, used for printing.
183    pub fn as_string(&self, context: &Context) -> String {
184        let sep_types_str = |agg_content: &Vec<Type>, sep: &str| {
185            agg_content
186                .iter()
187                .map(|ty| ty.as_string(context))
188                .collect::<Vec<_>>()
189                .join(sep)
190        };
191
192        match self.get_content(context) {
193            TypeContent::Never => "never".into(),
194            TypeContent::Unit => "()".into(),
195            TypeContent::Bool => "bool".into(),
196            TypeContent::Uint(nbits) => format!("u{nbits}"),
197            TypeContent::B256 => "b256".into(),
198            TypeContent::StringSlice => "str".into(),
199            TypeContent::StringArray(n) => format!("string<{n}>"),
200            TypeContent::Array(ty, cnt) => {
201                format!("[{}; {}]", ty.as_string(context), cnt)
202            }
203            TypeContent::Union(agg) => {
204                format!("( {} )", sep_types_str(agg, " | "))
205            }
206            TypeContent::Struct(agg) => {
207                format!("{{ {} }}", sep_types_str(agg, ", "))
208            }
209            TypeContent::Slice => "slice".into(),
210            TypeContent::Pointer => "ptr".into(),
211            TypeContent::TypedSlice(ty) => format!("__slice[{}]", ty.as_string(context)),
212            TypeContent::TypedPointer(ty) => format!("__ptr {}", ty.as_string(context)),
213        }
214    }
215
216    /// Compare a type to this one for equivalence.
217    /// `PartialEq` does not take into account the special case for Unions below.
218    pub fn eq(&self, context: &Context, other: &Type) -> bool {
219        match (self.get_content(context), other.get_content(context)) {
220            (TypeContent::Unit, TypeContent::Unit) => true,
221            (TypeContent::Bool, TypeContent::Bool) => true,
222            (TypeContent::Uint(l), TypeContent::Uint(r)) => l == r,
223            (TypeContent::B256, TypeContent::B256) => true,
224
225            (TypeContent::StringSlice, TypeContent::StringSlice) => true,
226            (TypeContent::StringArray(l), TypeContent::StringArray(r)) => l == r,
227
228            (TypeContent::Array(l, llen), TypeContent::Array(r, rlen)) => {
229                llen == rlen && l.eq(context, r)
230            }
231
232            (TypeContent::TypedSlice(l), TypeContent::TypedSlice(r)) => l.eq(context, r),
233
234            (TypeContent::Struct(l), TypeContent::Struct(r))
235            | (TypeContent::Union(l), TypeContent::Union(r)) => {
236                l.len() == r.len() && l.iter().zip(r.iter()).all(|(l, r)| l.eq(context, r))
237            }
238            // Unions are special.  We say unions are equivalent to any of their variant types.
239            (_, TypeContent::Union(_)) => other.eq(context, self),
240            (TypeContent::Union(l), _) => l.iter().any(|field_ty| other.eq(context, field_ty)),
241            // Never type can coerce into any other type.
242            (TypeContent::Never, _) => true,
243            (TypeContent::Slice, TypeContent::Slice) => true,
244            (TypeContent::Pointer, TypeContent::Pointer) => true,
245            (TypeContent::TypedPointer(l), TypeContent::TypedPointer(r)) => l.eq(context, r),
246            _ => false,
247        }
248    }
249
250    /// Is Never type
251    pub fn is_never(&self, context: &Context) -> bool {
252        matches!(*self.get_content(context), TypeContent::Never)
253    }
254
255    /// Is bool type
256    pub fn is_bool(&self, context: &Context) -> bool {
257        matches!(*self.get_content(context), TypeContent::Bool)
258    }
259
260    /// Is unit type
261    pub fn is_unit(&self, context: &Context) -> bool {
262        matches!(*self.get_content(context), TypeContent::Unit)
263    }
264
265    /// Is unsigned integer type
266    pub fn is_uint(&self, context: &Context) -> bool {
267        matches!(*self.get_content(context), TypeContent::Uint(_))
268    }
269
270    /// Is u8 type
271    pub fn is_uint8(&self, context: &Context) -> bool {
272        matches!(*self.get_content(context), TypeContent::Uint(8))
273    }
274
275    /// Is u32 type
276    pub fn is_uint32(&self, context: &Context) -> bool {
277        matches!(*self.get_content(context), TypeContent::Uint(32))
278    }
279
280    /// Is u64 type
281    pub fn is_uint64(&self, context: &Context) -> bool {
282        matches!(*self.get_content(context), TypeContent::Uint(64))
283    }
284
285    /// Is unsigned integer type of specific width
286    pub fn is_uint_of(&self, context: &Context, width: u16) -> bool {
287        matches!(*self.get_content(context), TypeContent::Uint(width_) if width == width_)
288    }
289
290    /// Is B256 type
291    pub fn is_b256(&self, context: &Context) -> bool {
292        matches!(*self.get_content(context), TypeContent::B256)
293    }
294
295    /// Is string type
296    pub fn is_string_slice(&self, context: &Context) -> bool {
297        matches!(*self.get_content(context), TypeContent::StringSlice)
298    }
299
300    /// Is string type
301    pub fn is_string_array(&self, context: &Context) -> bool {
302        matches!(*self.get_content(context), TypeContent::StringArray(_))
303    }
304
305    /// Is array type
306    pub fn is_array(&self, context: &Context) -> bool {
307        matches!(*self.get_content(context), TypeContent::Array(..))
308    }
309
310    /// Is union type
311    pub fn is_union(&self, context: &Context) -> bool {
312        matches!(*self.get_content(context), TypeContent::Union(_))
313    }
314
315    /// Is struct type
316    pub fn is_struct(&self, context: &Context) -> bool {
317        matches!(*self.get_content(context), TypeContent::Struct(_))
318    }
319
320    /// Is enum type
321    pub fn is_enum(&self, context: &Context) -> bool {
322        // We have to do some painful special handling here for enums, which are tagged unions.
323        // This really should be handled by the IR more explicitly and is something that will
324        // hopefully be addressed by https://github.com/FuelLabs/sway/issues/2819#issuecomment-1256930392
325
326        // Enums are at the moment represented as structs with two fields, first one being
327        // the tag and second the union of variants. Enums are the only place we currently use unions
328        // which makes the below heuristics valid.
329        if !self.is_struct(context) {
330            return false;
331        }
332
333        let field_tys = self.get_field_types(context);
334
335        field_tys.len() == 2 && field_tys[0].is_uint(context) && field_tys[1].is_union(context)
336    }
337
338    /// Is aggregate type: struct, union, enum or array.
339    pub fn is_aggregate(&self, context: &Context) -> bool {
340        // Notice that enums are structs of tags and unions.
341        self.is_struct(context) || self.is_union(context) || self.is_array(context)
342    }
343
344    /// Returns true if `self` is a slice type.
345    pub fn is_slice(&self, context: &Context) -> bool {
346        matches!(*self.get_content(context), TypeContent::Slice)
347    }
348
349    // TODO: (REFERENCES) Check all the usages of `is_ptr`.
350    /// Returns true if `self` is a pointer type.
351    pub fn is_ptr(&self, context: &Context) -> bool {
352        matches!(
353            *self.get_content(context),
354            TypeContent::TypedPointer(_) | TypeContent::Pointer
355        )
356    }
357
358    /// Get pointed to type iff `self`` is a pointer.
359    pub fn get_pointee_type(&self, context: &Context) -> Option<Type> {
360        if let TypeContent::TypedPointer(to_ty) = self.get_content(context) {
361            Some(*to_ty)
362        } else {
363            None
364        }
365    }
366
367    /// Get width of an integer type.
368    pub fn get_uint_width(&self, context: &Context) -> Option<u16> {
369        if let TypeContent::Uint(width) = self.get_content(context) {
370            Some(*width)
371        } else {
372            None
373        }
374    }
375
376    /// What's the type of the struct/array value indexed by indices.
377    pub fn get_indexed_type(&self, context: &Context, indices: &[u64]) -> Option<Type> {
378        if indices.is_empty() {
379            return None;
380        }
381
382        indices.iter().try_fold(*self, |ty, idx| {
383            ty.get_field_type(context, *idx)
384                .or_else(|| match ty.get_content(context) {
385                    TypeContent::Array(ty, len) if idx < len => Some(*ty),
386                    _ => None,
387                })
388        })
389    }
390
391    /// What's the type of the struct/array value indexed by indices.
392    pub fn get_value_indexed_type(&self, context: &Context, indices: &[Value]) -> Option<Type> {
393        // Fetch the field type from the vector of Values.  If the value is a constant int then
394        // unwrap it and try to fetch the field type (which will fail for arrays) otherwise (i.e.,
395        // not a constant int or not a struct) fetch the array element type, which will fail for
396        // non-arrays.
397        indices.iter().try_fold(*self, |ty, idx_val| {
398            idx_val
399                .get_constant(context)
400                .and_then(|const_ref| {
401                    if let ConstantValue::Uint(n) = const_ref.get_content(context).value {
402                        Some(n)
403                    } else {
404                        None
405                    }
406                })
407                .and_then(|idx| ty.get_field_type(context, idx))
408                .or_else(|| ty.get_array_elem_type(context))
409        })
410    }
411
412    /// What's the offset, in bytes, of the indexed element?
413    /// Returns `None` on invalid indices.
414    /// Panics if `self` is not an aggregate (struct, union, or array).
415    pub fn get_indexed_offset(&self, context: &Context, indices: &[u64]) -> Option<u64> {
416        indices
417            .iter()
418            .try_fold((*self, 0), |(ty, accum_offset), idx| {
419                if ty.is_struct(context) {
420                    // Sum up all sizes of all previous fields.
421                    // Every struct field is aligned to word boundary.
422                    let prev_idxs_offset = (0..(*idx)).try_fold(0, |accum, pre_idx| {
423                        ty.get_field_type(context, pre_idx)
424                            .map(|field_ty| accum + field_ty.size(context).in_bytes_aligned())
425                    })?;
426                    ty.get_field_type(context, *idx)
427                        .map(|field_ty| (field_ty, accum_offset + prev_idxs_offset))
428                } else if ty.is_union(context) {
429                    // Union variants have their raw size in bytes and are
430                    // left padded within the union.
431                    let union_size_in_bytes = ty.size(context).in_bytes();
432                    ty.get_field_type(context, *idx).map(|field_ty| {
433                        (
434                            field_ty,
435                            accum_offset
436                                + (union_size_in_bytes - field_ty.size(context).in_bytes()),
437                        )
438                    })
439                } else {
440                    assert!(
441                        ty.is_array(context),
442                        "Expected aggregate type. Got {}.",
443                        ty.as_string(context)
444                    );
445                    // size_of_element * idx will be the offset of idx.
446                    ty.get_array_elem_type(context).map(|elm_ty| {
447                        let prev_idxs_offset = ty
448                            .get_array_elem_type(context)
449                            .unwrap()
450                            .size(context)
451                            .in_bytes()
452                            * idx;
453                        (elm_ty, accum_offset + prev_idxs_offset)
454                    })
455                }
456            })
457            .map(|pair| pair.1)
458    }
459
460    /// What's the offset, in bytes, of the value indexed element?
461    /// It may not always be possible to determine statically.
462    pub fn get_value_indexed_offset(&self, context: &Context, indices: &[Value]) -> Option<u64> {
463        let const_indices: Vec<_> = indices
464            .iter()
465            .map_while(|idx| {
466                if let Some(ConstantContent {
467                    value: ConstantValue::Uint(idx),
468                    ty: _,
469                }) = idx.get_constant(context).map(|c| c.get_content(context))
470                {
471                    Some(*idx)
472                } else {
473                    None
474                }
475            })
476            .collect();
477        (const_indices.len() == indices.len())
478            .then(|| self.get_indexed_offset(context, &const_indices))
479            .flatten()
480    }
481
482    pub fn get_field_type(&self, context: &Context, idx: u64) -> Option<Type> {
483        if let TypeContent::Struct(fields) | TypeContent::Union(fields) = self.get_content(context)
484        {
485            fields.get(idx as usize).cloned()
486        } else {
487            // Trying to index a non-aggregate.
488            None
489        }
490    }
491
492    /// Get the type of the array element, if applicable.
493    pub fn get_array_elem_type(&self, context: &Context) -> Option<Type> {
494        if let TypeContent::Array(ty, _) = *self.get_content(context) {
495            Some(ty)
496        } else {
497            None
498        }
499    }
500
501    /// Get the type of the array element, if applicable.
502    pub fn get_typed_slice_elem_type(&self, context: &Context) -> Option<Type> {
503        if let TypeContent::TypedSlice(ty) = *self.get_content(context) {
504            Some(ty)
505        } else {
506            None
507        }
508    }
509
510    /// Get the length of the array , if applicable.
511    pub fn get_array_len(&self, context: &Context) -> Option<u64> {
512        if let TypeContent::Array(_, n) = *self.get_content(context) {
513            Some(n)
514        } else {
515            None
516        }
517    }
518
519    /// Get the length of a string.
520    pub fn get_string_len(&self, context: &Context) -> Option<u64> {
521        if let TypeContent::StringArray(n) = *self.get_content(context) {
522            Some(n)
523        } else {
524            None
525        }
526    }
527
528    /// Get the type of each field of a struct Type. Empty vector otherwise.
529    pub fn get_field_types(&self, context: &Context) -> Vec<Type> {
530        match self.get_content(context) {
531            TypeContent::Struct(fields) | TypeContent::Union(fields) => fields.clone(),
532            _ => vec![],
533        }
534    }
535
536    /// Get the offset, in bytes, and the [Type] of the struct field at the index `field_idx`, if `self` is a struct,
537    /// otherwise `None`.
538    /// Panics if the `field_idx` is out of bounds.
539    pub fn get_struct_field_offset_and_type(
540        &self,
541        context: &Context,
542        field_idx: u64,
543    ) -> Option<(u64, Type)> {
544        if !self.is_struct(context) {
545            return None;
546        }
547
548        let field_idx = field_idx as usize;
549        let field_types = self.get_field_types(context);
550        let field_offs_in_bytes = field_types
551            .iter()
552            .take(field_idx)
553            .map(|field_ty| {
554                // Struct fields are aligned to word boundary.
555                field_ty.size(context).in_bytes_aligned()
556            })
557            .sum::<u64>();
558
559        Some((field_offs_in_bytes, field_types[field_idx]))
560    }
561
562    /// Get the offset, in bytes, and the [Type] of the union field at the index `field_idx`, if `self` is a union,
563    /// otherwise `None`.
564    /// Panics if the `field_idx` is out of bounds.
565    pub fn get_union_field_offset_and_type(
566        &self,
567        context: &Context,
568        field_idx: u64,
569    ) -> Option<(u64, Type)> {
570        if !self.is_union(context) {
571            return None;
572        }
573
574        let field_idx = field_idx as usize;
575        let field_type = self.get_field_types(context)[field_idx];
576        let union_size_in_bytes = self.size(context).in_bytes();
577        // Union variants have their raw size in bytes and are
578        // left padded within the union.
579        let field_size_in_bytes = field_type.size(context).in_bytes();
580
581        // The union fields are at offset (union_size - field_size) due to left padding.
582        Some((union_size_in_bytes - field_size_in_bytes, field_type))
583    }
584
585    /// Returns the memory size of the [Type].
586    /// The returned `TypeSize::in_bytes` will provide the raw memory size of the `self`,
587    /// when it's not embedded in an aggregate.
588    pub fn size(&self, context: &Context) -> TypeSize {
589        match self.get_content(context) {
590            TypeContent::Uint(8) | TypeContent::Bool | TypeContent::Unit | TypeContent::Never => {
591                TypeSize::new(1)
592            }
593            // All integers larger than a byte are words since FuelVM only has memory operations on those two units.
594            TypeContent::Uint(16)
595            | TypeContent::Uint(32)
596            | TypeContent::Uint(64)
597            | TypeContent::TypedPointer(_)
598            | TypeContent::Pointer => TypeSize::new(8),
599            TypeContent::Uint(256) => TypeSize::new(32),
600            TypeContent::Uint(_) => unreachable!(),
601            TypeContent::Slice => TypeSize::new(16),
602            TypeContent::TypedSlice(..) => TypeSize::new(16),
603            TypeContent::B256 => TypeSize::new(32),
604            TypeContent::StringSlice => TypeSize::new(16),
605            TypeContent::StringArray(n) => {
606                TypeSize::new(super::size_bytes_round_up_to_word_alignment!(*n))
607            }
608            TypeContent::Array(el_ty, cnt) => TypeSize::new(cnt * el_ty.size(context).in_bytes()),
609            TypeContent::Struct(field_tys) => {
610                // Sum up all the field sizes, aligned to words.
611                TypeSize::new(
612                    field_tys
613                        .iter()
614                        .map(|field_ty| field_ty.size(context).in_bytes_aligned())
615                        .sum(),
616                )
617            }
618            TypeContent::Union(field_tys) => {
619                // Find the max size for field sizes.
620                TypeSize::new(
621                    field_tys
622                        .iter()
623                        .map(|field_ty| field_ty.size(context).in_bytes_aligned())
624                        .max()
625                        .unwrap_or(0),
626                )
627            }
628        }
629    }
630}
631
632// This is a mouthful...
633#[macro_export]
634macro_rules! size_bytes_round_up_to_word_alignment {
635    ($bytes_expr: expr) => {
636        ($bytes_expr + 7) - (($bytes_expr + 7) % 8)
637    };
638}
639
640/// A helper to check if an Option<Type> value is of a particular Type.
641pub trait TypeOption {
642    fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool;
643}
644
645impl TypeOption for Option<Type> {
646    fn is(&self, pred: fn(&Type, &Context) -> bool, context: &Context) -> bool {
647        self.filter(|ty| pred(ty, context)).is_some()
648    }
649}
650
651/// Provides information about a size of a type, raw and aligned to word boundaries.
652#[derive(Clone, Debug)]
653pub struct TypeSize {
654    size_in_bytes: u64,
655}
656
657impl TypeSize {
658    pub(crate) fn new(size_in_bytes: u64) -> Self {
659        Self { size_in_bytes }
660    }
661
662    /// Returns the actual (unaligned) size of the type in bytes.
663    pub fn in_bytes(&self) -> u64 {
664        self.size_in_bytes
665    }
666
667    /// Returns the size of the type in bytes, aligned to word boundary.
668    pub fn in_bytes_aligned(&self) -> u64 {
669        (self.size_in_bytes + 7) - ((self.size_in_bytes + 7) % 8)
670    }
671
672    /// Returns the size of the type in words (aligned to word boundary).
673    pub fn in_words(&self) -> u64 {
674        self.size_in_bytes.div_ceil(8)
675    }
676}
677
678/// Provides information about padding expected when laying values in memory.
679/// Padding depends on the type of the value, but also on the embedding of
680/// the value in aggregates. E.g., in an array of `u8`, each `u8` is "padded"
681/// to its size of one byte while as a struct field, it will be right padded
682/// to 8 bytes.
683#[derive(Clone, Debug, serde::Serialize)]
684pub enum Padding {
685    Left { target_size: usize },
686    Right { target_size: usize },
687}
688
689impl Padding {
690    /// Returns the default [Padding] for `u8`.
691    pub fn default_for_u8(_value: u8) -> Self {
692        // Dummy _value is used only to ensure correct usage at the call site.
693        Self::Right { target_size: 1 }
694    }
695
696    /// Returns the default [Padding] for `u64`.
697    pub fn default_for_u64(_value: u64) -> Self {
698        // Dummy _value is used only to ensure correct usage at the call site.
699        Self::Right { target_size: 8 }
700    }
701
702    /// Returns the default [Padding] for a byte array.
703    pub fn default_for_byte_array(value: &[u8]) -> Self {
704        Self::Right {
705            target_size: value.len(),
706        }
707    }
708
709    /// Returns the default [Padding] for an aggregate.
710    /// `aggregate_size` is the overall size of the aggregate in bytes.
711    pub fn default_for_aggregate(aggregate_size: usize) -> Self {
712        Self::Right {
713            target_size: aggregate_size,
714        }
715    }
716
717    /// The target size in bytes.
718    pub fn target_size(&self) -> usize {
719        use Padding::*;
720        match self {
721            Left { target_size } | Right { target_size } => *target_size,
722        }
723    }
724}
725
726#[cfg(test)]
727mod tests {
728    pub use super::*;
729    /// Unit tests in this module document and assert decisions on memory layout.
730    mod memory_layout {
731        use super::*;
732        use crate::{Backtrace, Context};
733        use once_cell::sync::Lazy;
734        use sway_features::ExperimentalFeatures;
735        use sway_types::SourceEngine;
736
737        #[test]
738        /// Bool, when not embedded in aggregates, has a size of 1 byte.
739        fn boolean() {
740            let context = create_context();
741
742            let s_bool = Type::get_bool(&context).size(&context);
743
744            assert_eq!(s_bool.in_bytes(), 1);
745        }
746
747        #[test]
748        /// Unit, when not embedded in aggregates, has a size of 1 byte.
749        fn unit() {
750            let context = create_context();
751
752            let s_unit = Type::get_unit(&context).size(&context);
753
754            assert_eq!(s_unit.in_bytes(), 1);
755        }
756
757        #[test]
758        /// `u8`, when not embedded in aggregates, has a size of 1 byte.
759        fn unsigned_u8() {
760            let context = create_context();
761
762            let s_u8 = Type::get_uint8(&context).size(&context);
763
764            assert_eq!(s_u8.in_bytes(), 1);
765        }
766
767        #[test]
768        /// `u16`, `u32`, and `u64,`, when not embedded in aggregates, have a size of 8 bytes/1 word.
769        fn unsigned_u16_u32_u64() {
770            let context = create_context();
771
772            let s_u16 = Type::get_uint16(&context).size(&context);
773            let s_u32 = Type::get_uint32(&context).size(&context);
774            let s_u64 = Type::get_uint64(&context).size(&context);
775
776            assert_eq!(s_u16.in_bytes(), 8);
777            assert_eq!(s_u16.in_bytes(), s_u16.in_bytes_aligned());
778
779            assert_eq!(s_u32.in_bytes(), 8);
780            assert_eq!(s_u32.in_bytes(), s_u32.in_bytes_aligned());
781
782            assert_eq!(s_u64.in_bytes(), 8);
783            assert_eq!(s_u64.in_bytes(), s_u64.in_bytes_aligned());
784        }
785
786        #[test]
787        /// `u256`, when not embedded in aggregates, has a size of 32 bytes.
788        fn unsigned_u256() {
789            let context = create_context();
790
791            let s_u256 = Type::get_uint256(&context).size(&context);
792
793            assert_eq!(s_u256.in_bytes(), 32);
794            assert_eq!(s_u256.in_bytes(), s_u256.in_bytes_aligned());
795        }
796
797        #[test]
798        /// Pointer to any type, when not embedded in aggregates, has a size of 8 bytes/1 word.
799        fn pointer() {
800            let mut context = create_context();
801
802            for ty in all_sample_types(&mut context) {
803                let s_ptr = Type::new_typed_pointer(&mut context, ty).size(&context);
804
805                assert_eq!(s_ptr.in_bytes(), 8);
806                assert_eq!(s_ptr.in_bytes(), s_ptr.in_bytes_aligned());
807            }
808
809            assert_eq!(Type::get_ptr(&context).size(&context).in_bytes(), 8);
810            assert_eq!(
811                Type::get_ptr(&context).size(&context).in_bytes(),
812                Type::get_ptr(&context).size(&context).in_bytes_aligned()
813            );
814        }
815
816        #[test]
817        /// Slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
818        /// The first word is the pointer to the actual content, and the second the
819        /// length of the slice.
820        fn slice() {
821            let context = create_context();
822
823            let s_slice = Type::get_slice(&context).size(&context);
824
825            assert_eq!(s_slice.in_bytes(), 16);
826            assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
827        }
828
829        #[test]
830        /// `B256`, when not embedded in aggregates, has a size of 32 bytes.
831        fn b256() {
832            let context = create_context();
833
834            let s_b256 = Type::get_b256(&context).size(&context);
835
836            assert_eq!(s_b256.in_bytes(), 32);
837            assert_eq!(s_b256.in_bytes(), s_b256.in_bytes_aligned());
838        }
839
840        #[test]
841        /// String slice, when not embedded in aggregates, has a size of 16 bytes/2 words.
842        /// The first word is the pointer to the actual content, and the second the
843        /// length of the slice.
844        fn string_slice() {
845            let mut context = create_context();
846
847            let s_slice = Type::get_or_create_unique_type(&mut context, TypeContent::StringSlice)
848                .size(&context);
849
850            assert_eq!(s_slice.in_bytes(), 16);
851            assert_eq!(s_slice.in_bytes(), s_slice.in_bytes_aligned());
852        }
853
854        #[test]
855        /// String array, when not embedded in aggregates, has a size in bytes of its length, aligned to the word boundary.
856        /// Note that this differs from other arrays, which are packed but not, in addition, aligned to the word boundary.
857        /// The reason we have the alignment/padding in case of string arrays, is because of the current ABI encoding.
858        /// The output receipt returned by a contract call can be a string array, and the way the output is encoded
859        /// (at least for small strings) is by literally putting the ASCII bytes in the return value register.
860        /// For string arrays smaller than 8 bytes this poses a problem, because we have to fill the register with something
861        /// or start reading memory that isn't ours. And the workaround was to simply pad all string arrays with zeroes so
862        /// they're all at least 8 bytes long.
863        /// Thus, changing this behavior would be a breaking change in ABI compatibility.
864        /// Note that we do want to change this behavior in the future, as a part of either refactoring the ABI encoding
865        /// or proper support for slices.
866        fn string_array() {
867            let mut context = create_context();
868
869            for (str_array_ty, len) in sample_string_arrays(&mut context) {
870                assert!(str_array_ty.is_string_array(&context)); // Just in case.
871
872                let s_str_array = str_array_ty.size(&context);
873
874                assert_eq!(str_array_ty.get_string_len(&context).unwrap(), len);
875
876                assert_eq!(
877                    s_str_array.in_bytes(),
878                    size_bytes_round_up_to_word_alignment!(len)
879                );
880                assert_eq!(s_str_array.in_bytes(), s_str_array.in_bytes_aligned());
881            }
882        }
883
884        #[test]
885        /// Array, when not embedded in aggregates, has a size in bytes of its length multiplied by the size of its element's type.
886        /// Arrays are packed. The offset of the n-th element is `n * __size_of<ElementType>()`.
887        fn array() {
888            let mut context = create_context();
889
890            for (array_ty, len, elem_size) in sample_arrays(&mut context) {
891                assert!(array_ty.is_array(&context)); // Just in case.
892
893                let s_array = array_ty.size(&context);
894
895                assert_eq!(array_ty.get_array_len(&context).unwrap(), len);
896
897                // The size of the array is the length multiplied by the element size.
898                assert_eq!(s_array.in_bytes(), len * elem_size);
899
900                for elem_index in 0..len {
901                    let elem_offset = array_ty
902                        .get_indexed_offset(&context, &[elem_index])
903                        .unwrap();
904
905                    // The offset of the element is its index multiplied by the element size.
906                    assert_eq!(elem_offset, elem_index * elem_size);
907                }
908            }
909        }
910
911        #[test]
912        /// Struct has a size in bytes of the sum of all of its fields.
913        /// The size of each individual field is a multiple of the word size. Thus,
914        /// if needed, fields are right-padded to the multiple of the word size.
915        /// Each individual field is aligned to the word boundary.
916        /// Struct fields are ordered in the order of their appearance in the struct definition.
917        /// The offset of each field is the sum of the sizes of the preceding fields.
918        /// Since the size of the each individual field is a multiple of the word size,
919        /// the size of the struct is also always a multiple of the word size.
920        fn r#struct() {
921            let mut context = create_context();
922
923            for (struct_ty, fields) in sample_structs(&mut context) {
924                assert!(struct_ty.is_struct(&context)); // Just in case.
925
926                let s_struct = struct_ty.size(&context);
927
928                // The size of the struct is the sum of the field sizes,
929                // where each field is, if needed, right-padded to the multiple of the
930                // word size.
931                assert_eq!(
932                    s_struct.in_bytes(),
933                    fields
934                        .iter()
935                        .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
936                        .sum::<u64>()
937                );
938                // Structs' sizes are always multiples of the word size.
939                assert_eq!(s_struct.in_bytes(), s_struct.in_bytes_aligned());
940
941                for field_index in 0..fields.len() {
942                    // The offset of a field is the sum of the sizes of the previous fields.
943                    let expected_offset = fields
944                        .iter()
945                        .take(field_index)
946                        .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
947                        .sum::<u64>();
948
949                    let field_offset = struct_ty
950                        .get_indexed_offset(&context, &[field_index as u64])
951                        .unwrap();
952                    assert_eq!(field_offset, expected_offset);
953
954                    let (field_offset, field_type) = struct_ty
955                        .get_struct_field_offset_and_type(&context, field_index as u64)
956                        .unwrap();
957                    assert_eq!(field_offset, expected_offset);
958                    assert_eq!(field_type, fields[field_index].0);
959                }
960            }
961        }
962
963        #[test]
964        /// Union has a size in bytes of the largest of all of its variants,
965        /// where the largest variant is, if needed, left-padded to the multiple of the word size.
966        /// Variants overlap in memory and are left-padded (aligned to the right) to the size of the
967        /// largest variant (already _right_ aligned/left-padded to the word boundary).
968        /// Thus, a variant, in a general case, needs not to be aligned to the word boundary.
969        /// The offset of a variant, relative to the union address is:
970        ///
971        ///  `__size_of<UnionType>() - __size_of<VariantType>()`.
972        ///
973        /// Since the size of the largest variant is a multiple of the word size,
974        /// the size of the union is also always a multiple of the word size.
975        fn union() {
976            let mut context = create_context();
977
978            for (union_ty, variants) in sample_unions(&mut context) {
979                assert!(union_ty.is_union(&context)); // Just in case.
980
981                let s_union = union_ty.size(&context);
982
983                // The size of the union is the size of the largest variant,
984                // where the largest variant is, if needed, left-padded to the multiple
985                // of the word size.
986                assert_eq!(
987                    s_union.in_bytes(),
988                    variants
989                        .iter()
990                        .map(|(_, raw_size)| size_bytes_round_up_to_word_alignment!(raw_size))
991                        .max()
992                        .unwrap_or_default()
993                );
994                // Unions' sizes are always multiples of the word size.
995                assert_eq!(s_union.in_bytes(), s_union.in_bytes_aligned());
996
997                for (variant_index, variant) in variants.iter().enumerate() {
998                    // Variants are left-padded.
999                    // The offset of a variant is the union size minus the raw variant size.
1000                    let expected_offset = s_union.in_bytes() - variant.1;
1001
1002                    let variant_offset = union_ty
1003                        .get_indexed_offset(&context, &[variant_index as u64])
1004                        .unwrap();
1005                    assert_eq!(variant_offset, expected_offset);
1006
1007                    let (variant_offset, field_type) = union_ty
1008                        .get_union_field_offset_and_type(&context, variant_index as u64)
1009                        .unwrap();
1010                    assert_eq!(variant_offset, expected_offset);
1011                    assert_eq!(field_type, variant.0);
1012                }
1013            }
1014        }
1015
1016        // A bit of trickery just to avoid bloating test setups by having `SourceEngine`
1017        // instantiation in every test.
1018        // Not that we can't do the same with the `Context` because it must be isolated and
1019        // unique in every test.
1020        static SOURCE_ENGINE: Lazy<SourceEngine> = Lazy::new(SourceEngine::default);
1021
1022        fn create_context() -> Context<'static> {
1023            Context::new(
1024                &SOURCE_ENGINE,
1025                ExperimentalFeatures::default(),
1026                Backtrace::default(),
1027            )
1028        }
1029
1030        /// Creates sample types that are not aggregates and do not point to
1031        /// other types. Where applicable, several typical representatives of
1032        /// a type are created, e.g., string arrays of different sizes.
1033        fn sample_non_aggregate_types(context: &mut Context) -> Vec<Type> {
1034            let mut types = vec![
1035                Type::get_bool(context),
1036                Type::get_unit(context),
1037                Type::get_uint(context, 8).unwrap(),
1038                Type::get_uint(context, 16).unwrap(),
1039                Type::get_uint(context, 32).unwrap(),
1040                Type::get_uint(context, 64).unwrap(),
1041                Type::get_uint(context, 256).unwrap(),
1042                Type::get_b256(context),
1043                Type::get_slice(context),
1044                Type::get_or_create_unique_type(context, TypeContent::StringSlice),
1045            ];
1046
1047            types.extend(
1048                sample_string_arrays(context)
1049                    .into_iter()
1050                    .map(|(string_array, _)| string_array),
1051            );
1052
1053            types
1054        }
1055
1056        /// Creates sample string array types of different lengths and
1057        /// returns the string array types and their respective lengths.
1058        fn sample_string_arrays(context: &mut Context) -> Vec<(Type, u64)> {
1059            let mut types = vec![];
1060
1061            for len in [0, 1, 7, 8, 15] {
1062                types.push((Type::new_string_array(context, len), len));
1063            }
1064
1065            types
1066        }
1067
1068        /// Creates sample array types of different lengths and
1069        /// different element types and returns the created array types
1070        /// and their respective lengths and the size of the element type.
1071        fn sample_arrays(context: &mut Context) -> Vec<(Type, u64, u64)> {
1072            let mut types = vec![];
1073
1074            for len in [0, 1, 7, 8, 15] {
1075                for ty in sample_non_aggregate_types(context) {
1076                    // As commented in other places, we trust the result of the
1077                    // `size` method for non-aggregate types.
1078                    types.push((
1079                        Type::new_array(context, ty, len),
1080                        len,
1081                        ty.size(context).in_bytes(),
1082                    ));
1083                }
1084
1085                for (array_ty, array_len, elem_size) in sample_arrays_to_embed(context) {
1086                    // We cannot use the `size` methods on arrays here because we use this
1087                    // samples to actually test it. We calculate the expected size manually
1088                    // according to the definition of the layout for the arrays.
1089                    types.push((
1090                        Type::new_array(context, array_ty, len),
1091                        len,
1092                        array_len * elem_size,
1093                    ));
1094                }
1095
1096                for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1097                    types.push((Type::new_array(context, struct_ty, len), len, struct_size));
1098                }
1099            }
1100
1101            types
1102        }
1103
1104        /// Creates sample struct types and returns the created struct types
1105        /// and their respective field types and their raw size in bytes
1106        /// (when not embedded).
1107        fn sample_structs(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1108            let mut types = vec![];
1109
1110            // Empty struct.
1111            types.push((Type::new_struct(context, vec![]), vec![]));
1112
1113            // Structs with only one 1-byte long field.
1114            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1115            // Structs with only one 1-word long field.
1116            add_structs_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1117
1118            // Complex struct with fields of all non aggregate types, arrays, and structs.
1119            let mut fields = vec![];
1120            for ty in sample_non_aggregate_types(context) {
1121                // We can trust the result of the `size` method here,
1122                // because it is tested in tests for individual non-aggregate types.
1123                fields.push((ty, ty.size(context).in_bytes()));
1124            }
1125            for (array_ty, len, elem_size) in sample_arrays(context) {
1126                // We can't trust the result of the `size` method here,
1127                // because tests for arrays test embedded structs and vice versa.
1128                // So we will manually calculate the expected raw size in bytes,
1129                // as per the definition of the memory layout for the arrays.
1130                fields.push((array_ty, len * elem_size));
1131            }
1132            for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1133                fields.push((struct_ty, struct_size));
1134            }
1135
1136            types.push((
1137                Type::new_struct(
1138                    context,
1139                    fields.iter().map(|(field_ty, _)| *field_ty).collect(),
1140                ),
1141                fields,
1142            ));
1143
1144            return types;
1145
1146            fn add_structs_with_non_aggregate_types_of_length_in_bytes(
1147                types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1148                context: &mut Context,
1149                field_type_size_in_bytes: u64,
1150            ) {
1151                for ty in sample_non_aggregate_types(context) {
1152                    if ty.size(context).in_bytes() != field_type_size_in_bytes {
1153                        continue;
1154                    }
1155
1156                    types.push((
1157                        Type::new_struct(context, vec![ty]),
1158                        vec![(ty, field_type_size_in_bytes)],
1159                    ));
1160                }
1161            }
1162        }
1163
1164        /// Creates sample union types and returns the created union types
1165        /// and their respective variant types and their raw size in bytes
1166        /// (when not embedded).
1167        fn sample_unions(context: &mut Context) -> Vec<(Type, Vec<(Type, u64)>)> {
1168            let mut types = vec![];
1169
1170            // Empty union.
1171            types.push((Type::new_union(context, vec![]), vec![]));
1172
1173            // Unions with only one 1-byte long variant.
1174            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 1);
1175            // Unions with only one 1-word long variant.
1176            add_unions_with_non_aggregate_types_of_length_in_bytes(&mut types, context, 8);
1177
1178            // Complex union with variants of all non aggregate types, arrays, and structs.
1179            // For the reasons for using the `size` method for non-aggregates vs
1180            // calculating sizes for non aggregates, see the comment in the
1181            // `sample_structs` function.
1182            let mut variants = vec![];
1183            for ty in sample_non_aggregate_types(context) {
1184                variants.push((ty, ty.size(context).in_bytes()));
1185            }
1186            for (array_ty, len, elem_size) in sample_arrays(context) {
1187                variants.push((array_ty, len * elem_size));
1188            }
1189            for (struct_ty, struct_size) in sample_structs_to_embed(context) {
1190                variants.push((struct_ty, struct_size));
1191            }
1192
1193            types.push((
1194                Type::new_union(
1195                    context,
1196                    variants.iter().map(|(field_ty, _)| *field_ty).collect(),
1197                ),
1198                variants,
1199            ));
1200
1201            return types;
1202
1203            fn add_unions_with_non_aggregate_types_of_length_in_bytes(
1204                types: &mut Vec<(Type, Vec<(Type, u64)>)>,
1205                context: &mut Context,
1206                variant_type_size_in_bytes: u64,
1207            ) {
1208                for ty in sample_non_aggregate_types(context) {
1209                    if ty.size(context).in_bytes() != variant_type_size_in_bytes {
1210                        continue;
1211                    }
1212
1213                    types.push((
1214                        Type::new_union(context, vec![ty]),
1215                        vec![(ty, variant_type_size_in_bytes)],
1216                    ));
1217                }
1218            }
1219        }
1220
1221        /// Creates sample arrays to embed in other aggregates.
1222        /// Returns the created array types, its length, and the size
1223        /// of the element type.
1224        fn sample_arrays_to_embed(context: &mut Context) -> Vec<(Type, u64, u64)> {
1225            let mut types = vec![];
1226
1227            for len in [0, 1, 7, 8, 15] {
1228                for elem_ty in sample_non_aggregate_types(context) {
1229                    types.push((
1230                        Type::new_array(context, elem_ty, len),
1231                        len,
1232                        elem_ty.size(context).in_bytes(),
1233                    ));
1234                }
1235            }
1236
1237            types
1238        }
1239
1240        /// Creates sample structs to embed in other aggregates.
1241        /// Returns the struct type and size in bytes for each created struct.
1242        fn sample_structs_to_embed(context: &mut Context) -> Vec<(Type, u64)> {
1243            let mut types = vec![];
1244
1245            // Create structs with just one field for each non_aggregate type.
1246            for field_ty in sample_non_aggregate_types(context) {
1247                // We can trust the result of the `size` method here,
1248                // because it is tested in tests for individual non-aggregate types.
1249                // We align it to the word boundary to satisfy the layout of structs.
1250                types.push((
1251                    Type::new_struct(context, vec![field_ty]),
1252                    field_ty.size(context).in_bytes_aligned(),
1253                ));
1254            }
1255
1256            // Create structs for pairwise combinations of field types.
1257            let field_types = sample_non_aggregate_types(context);
1258            for (index, first_field_ty) in field_types.iter().enumerate() {
1259                for second_field_type in field_types.iter().skip(index) {
1260                    // Again, we trust the `size` method called on non-aggregate types
1261                    // and calculate the struct size on our own.
1262                    let struct_size = first_field_ty.size(context).in_bytes_aligned()
1263                        + second_field_type.size(context).in_bytes_aligned();
1264                    types.push((
1265                        Type::new_struct(context, vec![*first_field_ty, *second_field_type]),
1266                        struct_size,
1267                    ));
1268                }
1269            }
1270
1271            // Create a struct with a field for each aggregate type.
1272            let field_types = sample_non_aggregate_types(context);
1273            let struct_size = field_types
1274                .iter()
1275                .map(|ty| ty.size(context).in_bytes_aligned())
1276                .sum();
1277            types.push((Type::new_struct(context, field_types), struct_size));
1278
1279            types
1280        }
1281
1282        /// Returns all types that we can have, including several typical samples for
1283        /// aggregates like, e.g., arrays of different elements and different sizes.
1284        fn all_sample_types(context: &mut Context) -> Vec<Type> {
1285            let mut types = vec![];
1286
1287            types.extend(sample_non_aggregate_types(context));
1288            types.extend(
1289                sample_arrays(context)
1290                    .into_iter()
1291                    .map(|(array_ty, _, _)| array_ty),
1292            );
1293            types.extend(
1294                sample_structs(context)
1295                    .into_iter()
1296                    .map(|(array_ty, __)| array_ty),
1297            );
1298
1299            types
1300        }
1301    }
1302}