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 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 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 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 slot_size
288 }
289 }
290
291 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}