fe_mir/db/queries/
types.rs

1use std::{fmt, rc::Rc, str::FromStr};
2
3use fe_analyzer::namespace::{items::EnumVariantId, types as analyzer_types};
4
5use num_bigint::BigInt;
6use num_traits::ToPrimitive;
7
8use crate::{
9    db::MirDb,
10    ir::{
11        types::{ArrayDef, TupleDef, TypeKind},
12        Type, TypeId, Value,
13    },
14    lower::types::lower_type,
15};
16
17pub fn mir_lowered_type(db: &dyn MirDb, analyzer_type: analyzer_types::TypeId) -> TypeId {
18    lower_type(db, analyzer_type)
19}
20
21impl TypeId {
22    pub fn data(self, db: &dyn MirDb) -> Rc<Type> {
23        db.lookup_mir_intern_type(self)
24    }
25
26    pub fn analyzer_ty(self, db: &dyn MirDb) -> Option<analyzer_types::TypeId> {
27        self.data(db).analyzer_ty
28    }
29
30    pub fn projection_ty(self, db: &dyn MirDb, access: &Value) -> TypeId {
31        let ty = self.deref(db);
32        let pty = match &ty.data(db).kind {
33            TypeKind::Array(ArrayDef { elem_ty, .. }) => *elem_ty,
34            TypeKind::Tuple(def) => {
35                let index = expect_projection_index(access);
36                def.items[index]
37            }
38            TypeKind::Struct(def) | TypeKind::Contract(def) => {
39                let index = expect_projection_index(access);
40                def.fields[index].1
41            }
42            TypeKind::Enum(_) => {
43                let index = expect_projection_index(access);
44                debug_assert_eq!(index, 0);
45                ty.projection_ty_imm(db, 0)
46            }
47            _ => panic!("{:?} can't project onto the `access`", self.as_string(db)),
48        };
49        match &self.data(db).kind {
50            TypeKind::SPtr(_) | TypeKind::Contract(_) => pty.make_sptr(db),
51            TypeKind::MPtr(_) => pty.make_mptr(db),
52            _ => pty,
53        }
54    }
55
56    pub fn deref(self, db: &dyn MirDb) -> TypeId {
57        match self.data(db).kind {
58            TypeKind::SPtr(inner) => inner,
59            TypeKind::MPtr(inner) => inner.deref(db),
60            _ => self,
61        }
62    }
63
64    pub fn make_sptr(self, db: &dyn MirDb) -> TypeId {
65        db.mir_intern_type(Type::new(TypeKind::SPtr(self), None).into())
66    }
67
68    pub fn make_mptr(self, db: &dyn MirDb) -> TypeId {
69        db.mir_intern_type(Type::new(TypeKind::MPtr(self), None).into())
70    }
71
72    pub fn projection_ty_imm(self, db: &dyn MirDb, index: usize) -> TypeId {
73        match &self.data(db).kind {
74            TypeKind::Array(ArrayDef { elem_ty, .. }) => *elem_ty,
75            TypeKind::Tuple(def) => def.items[index],
76            TypeKind::Struct(def) | TypeKind::Contract(def) => def.fields[index].1,
77            TypeKind::Enum(_) => {
78                debug_assert_eq!(index, 0);
79                self.enum_disc_type(db)
80            }
81            _ => panic!("{:?} can't project onto the `index`", self.as_string(db)),
82        }
83    }
84
85    pub fn aggregate_field_num(self, db: &dyn MirDb) -> usize {
86        match &self.data(db).kind {
87            TypeKind::Array(ArrayDef { len, .. }) => *len,
88            TypeKind::Tuple(def) => def.items.len(),
89            TypeKind::Struct(def) | TypeKind::Contract(def) => def.fields.len(),
90            TypeKind::Enum(_) => 2,
91            _ => unreachable!(),
92        }
93    }
94
95    pub fn enum_disc_type(self, db: &dyn MirDb) -> TypeId {
96        let kind = match &self.deref(db).data(db).kind {
97            TypeKind::Enum(def) => def.tag_type(),
98            _ => unreachable!(),
99        };
100        let analyzer_type = match kind {
101            TypeKind::U8 => Some(analyzer_types::Integer::U8),
102            TypeKind::U16 => Some(analyzer_types::Integer::U16),
103            TypeKind::U32 => Some(analyzer_types::Integer::U32),
104            TypeKind::U64 => Some(analyzer_types::Integer::U64),
105            TypeKind::U128 => Some(analyzer_types::Integer::U128),
106            TypeKind::U256 => Some(analyzer_types::Integer::U256),
107            _ => None,
108        }
109        .map(|int| analyzer_types::TypeId::int(db.upcast(), int));
110
111        db.mir_intern_type(Type::new(kind, analyzer_type).into())
112    }
113
114    pub fn enum_data_offset(self, db: &dyn MirDb, slot_size: usize) -> usize {
115        match &self.data(db).kind {
116            TypeKind::Enum(def) => {
117                let disc_size = self.enum_disc_type(db).size_of(db, slot_size);
118                let mut align = 1;
119                for variant in def.variants.iter() {
120                    let variant_align = variant.ty.align_of(db, slot_size);
121                    align = num_integer::lcm(align, variant_align);
122                }
123                round_up(disc_size, align)
124            }
125            _ => unreachable!(),
126        }
127    }
128
129    pub fn enum_variant_type(self, db: &dyn MirDb, variant_id: EnumVariantId) -> TypeId {
130        let name = variant_id.name(db.upcast());
131        match &self.deref(db).data(db).kind {
132            TypeKind::Enum(def) => def
133                .variants
134                .iter()
135                .find(|variant| variant.name == name)
136                .map(|variant| variant.ty)
137                .unwrap(),
138            _ => unreachable!(),
139        }
140    }
141
142    pub fn index_from_fname(self, db: &dyn MirDb, fname: &str) -> BigInt {
143        let ty = self.deref(db);
144        match &ty.data(db).kind {
145            TypeKind::Tuple(_) => {
146                // TODO: Fix this when the syntax for tuple access changes.
147                let index_str = &fname[4..];
148                BigInt::from_str(index_str).unwrap()
149            }
150
151            TypeKind::Struct(def) | TypeKind::Contract(def) => def
152                .fields
153                .iter()
154                .enumerate()
155                .find_map(|(i, field)| (field.0 == fname).then(|| i.into()))
156                .unwrap(),
157
158            other => unreachable!("{:?} does not have fields", other),
159        }
160    }
161
162    pub fn is_primitive(self, db: &dyn MirDb) -> bool {
163        matches!(
164            &self.data(db).kind,
165            TypeKind::I8
166                | TypeKind::I16
167                | TypeKind::I32
168                | TypeKind::I64
169                | TypeKind::I128
170                | TypeKind::I256
171                | TypeKind::U8
172                | TypeKind::U16
173                | TypeKind::U32
174                | TypeKind::U64
175                | TypeKind::U128
176                | TypeKind::U256
177                | TypeKind::Bool
178                | TypeKind::Address
179                | TypeKind::Unit
180        )
181    }
182
183    pub fn is_integral(self, db: &dyn MirDb) -> bool {
184        matches!(
185            &self.data(db).kind,
186            TypeKind::I8
187                | TypeKind::I16
188                | TypeKind::I32
189                | TypeKind::I64
190                | TypeKind::I128
191                | TypeKind::I256
192                | TypeKind::U8
193                | TypeKind::U16
194                | TypeKind::U32
195                | TypeKind::U64
196                | TypeKind::U128
197                | TypeKind::U256
198        )
199    }
200
201    pub fn is_address(self, db: &dyn MirDb) -> bool {
202        matches!(&self.data(db).kind, TypeKind::Address)
203    }
204
205    pub fn is_unit(self, db: &dyn MirDb) -> bool {
206        matches!(&self.data(db).as_ref().kind, TypeKind::Unit)
207    }
208
209    pub fn is_enum(self, db: &dyn MirDb) -> bool {
210        matches!(&self.data(db).as_ref().kind, TypeKind::Enum(_))
211    }
212
213    pub fn is_signed(self, db: &dyn MirDb) -> bool {
214        matches!(
215            &self.data(db).kind,
216            TypeKind::I8
217                | TypeKind::I16
218                | TypeKind::I32
219                | TypeKind::I64
220                | TypeKind::I128
221                | TypeKind::I256
222        )
223    }
224
225    /// Returns size of the type in bytes.
226    pub fn size_of(self, db: &dyn MirDb, slot_size: usize) -> usize {
227        match &self.data(db).kind {
228            TypeKind::Bool | TypeKind::I8 | TypeKind::U8 => 1,
229            TypeKind::I16 | TypeKind::U16 => 2,
230            TypeKind::I32 | TypeKind::U32 => 4,
231            TypeKind::I64 | TypeKind::U64 => 8,
232            TypeKind::I128 | TypeKind::U128 => 16,
233            TypeKind::String(len) => 32 + len,
234            TypeKind::MPtr(..)
235            | TypeKind::SPtr(..)
236            | TypeKind::I256
237            | TypeKind::U256
238            | TypeKind::Map(_) => 32,
239            TypeKind::Address => 20,
240            TypeKind::Unit => 0,
241
242            TypeKind::Array(def) => array_elem_size_imp(db, def, slot_size) * def.len,
243
244            TypeKind::Tuple(def) => {
245                if def.items.is_empty() {
246                    return 0;
247                }
248                let last_idx = def.items.len() - 1;
249                self.aggregate_elem_offset(db, last_idx, slot_size)
250                    + def.items[last_idx].size_of(db, slot_size)
251            }
252
253            TypeKind::Struct(def) | TypeKind::Contract(def) => {
254                if def.fields.is_empty() {
255                    return 0;
256                }
257                let last_idx = def.fields.len() - 1;
258                self.aggregate_elem_offset(db, last_idx, slot_size)
259                    + def.fields[last_idx].1.size_of(db, slot_size)
260            }
261
262            TypeKind::Enum(def) => {
263                let data_offset = self.enum_data_offset(db, slot_size);
264                let maximum_data_size = def
265                    .variants
266                    .iter()
267                    .map(|variant| variant.ty.size_of(db, slot_size))
268                    .max()
269                    .unwrap_or(0);
270                data_offset + maximum_data_size
271            }
272        }
273    }
274
275    pub fn is_zero_sized(self, db: &dyn MirDb) -> bool {
276        // It's ok to use 1 as a slot size because slot size doesn't affect whether a
277        // type is zero sized or not.
278        self.size_of(db, 1) == 0
279    }
280
281    pub fn align_of(self, db: &dyn MirDb, slot_size: usize) -> usize {
282        if self.is_primitive(db) {
283            1
284        } else {
285            // TODO: Too naive, we could implement more efficient layout for aggregate
286            // types.
287            slot_size
288        }
289    }
290
291    /// Returns an offset of the element of aggregate type.
292    pub fn aggregate_elem_offset<T>(self, db: &dyn MirDb, elem_idx: T, slot_size: usize) -> usize
293    where
294        T: num_traits::ToPrimitive,
295    {
296        debug_assert!(self.is_aggregate(db));
297        debug_assert!(elem_idx.to_usize().unwrap() < self.aggregate_field_num(db));
298        let elem_idx = elem_idx.to_usize().unwrap();
299
300        if elem_idx == 0 {
301            return 0;
302        }
303
304        match &self.data(db).kind {
305            TypeKind::Array(def) => array_elem_size_imp(db, def, slot_size) * elem_idx,
306            TypeKind::Enum(_) => self.enum_data_offset(db, slot_size),
307            _ => {
308                let mut offset = self.aggregate_elem_offset(db, elem_idx - 1, slot_size)
309                    + self
310                        .projection_ty_imm(db, elem_idx - 1)
311                        .size_of(db, slot_size);
312
313                let elem_ty = self.projection_ty_imm(db, elem_idx);
314                if (offset % slot_size + elem_ty.size_of(db, slot_size)) > slot_size {
315                    offset = round_up(offset, slot_size);
316                }
317
318                round_up(offset, elem_ty.align_of(db, slot_size))
319            }
320        }
321    }
322
323    pub fn is_aggregate(self, db: &dyn MirDb) -> bool {
324        matches!(
325            &self.data(db).kind,
326            TypeKind::Array(_)
327                | TypeKind::Tuple(_)
328                | TypeKind::Struct(_)
329                | TypeKind::Enum(_)
330                | TypeKind::Contract(_)
331        )
332    }
333
334    pub fn is_struct(self, db: &dyn MirDb) -> bool {
335        matches!(&self.data(db).as_ref().kind, TypeKind::Struct(_))
336    }
337
338    pub fn is_array(self, db: &dyn MirDb) -> bool {
339        matches!(&self.data(db).kind, TypeKind::Array(_))
340    }
341
342    pub fn is_string(self, db: &dyn MirDb) -> bool {
343        matches! {
344            &self.data(db).kind,
345            TypeKind::String(_)
346        }
347    }
348
349    pub fn is_ptr(self, db: &dyn MirDb) -> bool {
350        self.is_mptr(db) || self.is_sptr(db)
351    }
352
353    pub fn is_mptr(self, db: &dyn MirDb) -> bool {
354        matches!(self.data(db).kind, TypeKind::MPtr(_))
355    }
356
357    pub fn is_sptr(self, db: &dyn MirDb) -> bool {
358        matches!(self.data(db).kind, TypeKind::SPtr(_))
359    }
360
361    pub fn is_map(self, db: &dyn MirDb) -> bool {
362        matches!(self.data(db).kind, TypeKind::Map(_))
363    }
364
365    pub fn is_contract(self, db: &dyn MirDb) -> bool {
366        matches!(self.data(db).kind, TypeKind::Contract(_))
367    }
368
369    pub fn array_elem_size(self, db: &dyn MirDb, slot_size: usize) -> usize {
370        let data = self.data(db);
371        if let TypeKind::Array(def) = &data.kind {
372            array_elem_size_imp(db, def, slot_size)
373        } else {
374            panic!("expected `Array` type; but got {:?}", data.as_ref())
375        }
376    }
377
378    pub fn print<W: fmt::Write>(&self, db: &dyn MirDb, w: &mut W) -> fmt::Result {
379        match &self.data(db).kind {
380            TypeKind::I8 => write!(w, "i8"),
381            TypeKind::I16 => write!(w, "i16"),
382            TypeKind::I32 => write!(w, "i32"),
383            TypeKind::I64 => write!(w, "i64"),
384            TypeKind::I128 => write!(w, "i128"),
385            TypeKind::I256 => write!(w, "i256"),
386            TypeKind::U8 => write!(w, "u8"),
387            TypeKind::U16 => write!(w, "u16"),
388            TypeKind::U32 => write!(w, "u32"),
389            TypeKind::U64 => write!(w, "u64"),
390            TypeKind::U128 => write!(w, "u128"),
391            TypeKind::U256 => write!(w, "u256"),
392            TypeKind::Bool => write!(w, "bool"),
393            TypeKind::Address => write!(w, "address"),
394            TypeKind::Unit => write!(w, "()"),
395            TypeKind::String(size) => write!(w, "Str<{size}>"),
396            TypeKind::Array(ArrayDef { elem_ty, len }) => {
397                write!(w, "[")?;
398                elem_ty.print(db, w)?;
399                write!(w, "; {len}]")
400            }
401            TypeKind::Tuple(TupleDef { items }) => {
402                write!(w, "(")?;
403                if items.is_empty() {
404                    return write!(w, ")");
405                }
406
407                let len = items.len();
408                for item in &items[0..len - 1] {
409                    item.print(db, w)?;
410                    write!(w, ", ")?;
411                }
412                items.last().unwrap().print(db, w)?;
413                write!(w, ")")
414            }
415            TypeKind::Struct(def) => {
416                write!(w, "{}", def.name)
417            }
418            TypeKind::Enum(def) => {
419                write!(w, "{}", def.name)
420            }
421            TypeKind::Contract(def) => {
422                write!(w, "{}", def.name)
423            }
424            TypeKind::Map(def) => {
425                write!(w, "Map<")?;
426                def.key_ty.print(db, w)?;
427                write!(w, ",")?;
428                def.value_ty.print(db, w)?;
429                write!(w, ">")
430            }
431            TypeKind::MPtr(inner) => {
432                write!(w, "*@m ")?;
433                inner.print(db, w)
434            }
435            TypeKind::SPtr(inner) => {
436                write!(w, "*@s ")?;
437                inner.print(db, w)
438            }
439        }
440    }
441
442    pub fn as_string(&self, db: &dyn MirDb) -> String {
443        let mut s = String::new();
444        self.print(db, &mut s).unwrap();
445        s
446    }
447}
448
449fn array_elem_size_imp(db: &dyn MirDb, arr: &ArrayDef, slot_size: usize) -> usize {
450    let elem_ty = arr.elem_ty;
451    let elem = elem_ty.size_of(db, slot_size);
452    let align = if elem_ty.is_address(db) {
453        slot_size
454    } else {
455        elem_ty.align_of(db, slot_size)
456    };
457    round_up(elem, align)
458}
459
460fn expect_projection_index(value: &Value) -> usize {
461    match value {
462        Value::Immediate { imm, .. } => imm.to_usize().unwrap(),
463        _ => panic!("given `value` is not an immediate"),
464    }
465}
466
467fn round_up(value: usize, slot_size: usize) -> usize {
468    ((value + slot_size - 1) / slot_size) * slot_size
469}
470
471#[cfg(test)]
472mod tests {
473    use fe_analyzer::namespace::items::ModuleId;
474    use fe_common::Span;
475
476    use super::*;
477    use crate::{
478        db::{MirDb, NewDb},
479        ir::types::StructDef,
480    };
481
482    #[test]
483    fn test_primitive_type_info() {
484        let db = NewDb::default();
485        let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into());
486        let bool = db.mir_intern_type(Type::new(TypeKind::Bool, None).into());
487
488        debug_assert_eq!(i8.size_of(&db, 1), 1);
489        debug_assert_eq!(i8.size_of(&db, 32), 1);
490        debug_assert_eq!(i8.align_of(&db, 1), 1);
491        debug_assert_eq!(i8.align_of(&db, 32), 1);
492        debug_assert_eq!(bool.size_of(&db, 1), 1);
493        debug_assert_eq!(bool.size_of(&db, 32), 1);
494        debug_assert_eq!(i8.align_of(&db, 32), 1);
495        debug_assert_eq!(i8.align_of(&db, 32), 1);
496
497        let u32 = db.mir_intern_type(Type::new(TypeKind::U32, None).into());
498        debug_assert_eq!(u32.size_of(&db, 1), 4);
499        debug_assert_eq!(u32.size_of(&db, 32), 4);
500        debug_assert_eq!(u32.align_of(&db, 32), 1);
501
502        let address = db.mir_intern_type(Type::new(TypeKind::Address, None).into());
503        debug_assert_eq!(address.size_of(&db, 1), 20);
504        debug_assert_eq!(address.size_of(&db, 32), 20);
505        debug_assert_eq!(address.align_of(&db, 32), 1);
506    }
507
508    #[test]
509    fn test_primitive_elem_array_type_info() {
510        let db = NewDb::default();
511        let i32 = db.mir_intern_type(Type::new(TypeKind::I32, None).into());
512
513        let array_len = 10;
514        let array_def = ArrayDef {
515            elem_ty: i32,
516            len: array_len,
517        };
518        let array = db.mir_intern_type(Type::new(TypeKind::Array(array_def), None).into());
519
520        let elem_size = array.array_elem_size(&db, 1);
521        debug_assert_eq!(elem_size, 4);
522        debug_assert_eq!(array.array_elem_size(&db, 32), elem_size);
523
524        debug_assert_eq!(array.size_of(&db, 1), elem_size * array_len);
525        debug_assert_eq!(array.size_of(&db, 32), elem_size * array_len);
526        debug_assert_eq!(array.align_of(&db, 1), 1);
527        debug_assert_eq!(array.align_of(&db, 32), 32);
528
529        debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 32), elem_size * 3);
530        debug_assert_eq!(array.aggregate_elem_offset(&db, 9, 1), elem_size * 9);
531    }
532
533    #[test]
534    fn test_aggregate_elem_array_type_info() {
535        let db = NewDb::default();
536        let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into());
537        let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into());
538        let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into());
539
540        let fields = vec![
541            ("".into(), i64),
542            ("".into(), i64),
543            ("".into(), i8),
544            ("".into(), i128),
545            ("".into(), i8),
546        ];
547
548        let struct_def = StructDef {
549            name: "".into(),
550            fields,
551            span: Span::dummy(),
552            module_id: ModuleId::from_raw_internal(0),
553        };
554        let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into());
555
556        let array_len = 10;
557        let array_def = ArrayDef {
558            elem_ty: aggregate,
559            len: array_len,
560        };
561        let array = db.mir_intern_type(Type::new(TypeKind::Array(array_def), None).into());
562
563        debug_assert_eq!(array.array_elem_size(&db, 1), 34);
564        debug_assert_eq!(array.array_elem_size(&db, 32), 64);
565
566        debug_assert_eq!(array.size_of(&db, 1), 34 * array_len);
567        debug_assert_eq!(array.size_of(&db, 32), 64 * array_len);
568
569        debug_assert_eq!(array.align_of(&db, 1), 1);
570        debug_assert_eq!(array.align_of(&db, 32), 32);
571
572        debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 1), 102);
573        debug_assert_eq!(array.aggregate_elem_offset(&db, 3, 32), 192);
574    }
575
576    #[test]
577    fn test_primitive_elem_aggregate_type_info() {
578        let db = NewDb::default();
579        let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into());
580        let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into());
581        let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into());
582
583        let fields = vec![
584            ("".into(), i64),
585            ("".into(), i64),
586            ("".into(), i8),
587            ("".into(), i128),
588            ("".into(), i8),
589        ];
590
591        let struct_def = StructDef {
592            name: "".into(),
593            fields,
594            span: Span::dummy(),
595            module_id: ModuleId::from_raw_internal(0),
596        };
597        let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into());
598
599        debug_assert_eq!(aggregate.size_of(&db, 1), 34);
600        debug_assert_eq!(aggregate.size_of(&db, 32), 49);
601
602        debug_assert_eq!(aggregate.align_of(&db, 1), 1);
603        debug_assert_eq!(aggregate.align_of(&db, 32), 32);
604
605        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 1), 0);
606        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 32), 0);
607        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 3, 1), 17);
608        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 3, 32), 32);
609        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 4, 1), 33);
610        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 4, 32), 48);
611    }
612
613    #[test]
614    fn test_aggregate_elem_aggregate_type_info() {
615        let db = NewDb::default();
616        let i8 = db.mir_intern_type(Type::new(TypeKind::I8, None).into());
617        let i64 = db.mir_intern_type(Type::new(TypeKind::I64, None).into());
618        let i128 = db.mir_intern_type(Type::new(TypeKind::I128, None).into());
619
620        let fields_inner = vec![
621            ("".into(), i64),
622            ("".into(), i64),
623            ("".into(), i8),
624            ("".into(), i128),
625            ("".into(), i8),
626        ];
627
628        let struct_def_inner = StructDef {
629            name: "".into(),
630            fields: fields_inner,
631            span: Span::dummy(),
632            module_id: ModuleId::from_raw_internal(0),
633        };
634        let aggregate_inner =
635            db.mir_intern_type(Type::new(TypeKind::Struct(struct_def_inner), None).into());
636
637        let fields = vec![("".into(), i8), ("".into(), aggregate_inner)];
638        let struct_def = StructDef {
639            name: "".into(),
640            fields,
641            span: Span::dummy(),
642            module_id: ModuleId::from_raw_internal(0),
643        };
644        let aggregate = db.mir_intern_type(Type::new(TypeKind::Struct(struct_def), None).into());
645
646        debug_assert_eq!(aggregate.size_of(&db, 1), 35);
647        debug_assert_eq!(aggregate.size_of(&db, 32), 81);
648
649        debug_assert_eq!(aggregate.align_of(&db, 1), 1);
650        debug_assert_eq!(aggregate.align_of(&db, 32), 32);
651
652        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 1), 0);
653        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 0, 32), 0);
654        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 1, 1), 1);
655        debug_assert_eq!(aggregate.aggregate_elem_offset(&db, 1, 32), 32);
656    }
657}