swamp_vm_layout/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5//! Layouts analyzed into Vm Types (`BasicType`)
6
7use seq_map::SeqMap;
8use std::cmp::max;
9use std::rc::Rc;
10use swamp_symbol::TopLevelSymbolId;
11use swamp_types::prelude::{AnonymousStructType, EnumType, EnumVariantType, NamedStructType};
12use swamp_types::{TypeId, TypeKind, TypeRef};
13use swamp_vm_types::types::{
14    BasicType, BasicTypeId, BasicTypeKind, BasicTypeRef, OffsetMemoryItem, StructType, TaggedUnion,
15    TaggedUnionVariant, TupleType,
16};
17use swamp_vm_types::{
18    adjust_size_to_alignment, align_to, CountU16, MemoryAlignment, MemoryOffset,
19    MemorySize, ANY_HEADER_ALIGNMENT, ANY_HEADER_SIZE, GRID_HEADER_ALIGNMENT, GRID_HEADER_SIZE, MAP_HEADER_ALIGNMENT,
20    PTR_ALIGNMENT, PTR_SIZE, STRING_PTR_ALIGNMENT, STRING_PTR_SIZE,
21    VEC_HEADER_ALIGNMENT, VEC_HEADER_SIZE,
22};
23
24#[derive(Clone)]
25pub struct LayoutCache {
26    pub id_to_layout: SeqMap<TypeId, BasicTypeRef>,
27    pub kind_to_layout: SeqMap<TypeKind, BasicTypeRef>,
28}
29
30impl LayoutCache {}
31
32impl Default for LayoutCache {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl LayoutCache {
39    #[must_use]
40    pub fn new() -> Self {
41        Self {
42            id_to_layout: SeqMap::default(),
43            kind_to_layout: SeqMap::default(),
44        }
45    }
46
47    #[must_use]
48    pub fn layout(&mut self, analyzed_type: &TypeRef) -> BasicTypeRef {
49        // First check if we already have a layout for this type ID
50        if let Some(x) = self.id_to_layout.get(&analyzed_type.id) {
51            return x.clone();
52        }
53
54        // Check if we already have a layout for this kind of type
55        if let Some(existing_layout) = self.kind_to_layout.get(&analyzed_type.kind) {
56            // For deduplication, we reuse the existing layout directly
57            // This ensures pointer equality for structurally identical types
58            let _ = self
59                .id_to_layout
60                .insert(analyzed_type.id, existing_layout.clone());
61            return existing_layout.clone();
62        }
63
64        let basic_type = self.layout_type(analyzed_type);
65
66        let _ = self
67            .id_to_layout
68            .insert(analyzed_type.id, basic_type.clone());
69
70        // Also store in kind_to_layout for future deduplication
71        let _ = self
72            .kind_to_layout
73            .insert((*analyzed_type.kind).clone(), basic_type.clone());
74
75        basic_type
76    }
77
78    #[must_use]
79    fn layout_tagged_union(variants: &[TaggedUnionVariant], name: &str) -> TaggedUnion {
80        let num_variants = variants.len();
81        let (tag_size, tag_alignment) = if num_variants <= 0xFF {
82            (MemorySize(1), MemoryAlignment::U8)
83        } else if num_variants <= 0xFFFF {
84            (MemorySize(2), MemoryAlignment::U16)
85        } else {
86            (MemorySize(4), MemoryAlignment::U32)
87        };
88
89        let max_payload_size = variants
90            .iter()
91            .map(|v| v.ty.total_size)
92            .max()
93            .unwrap_or(MemorySize(0));
94        let max_payload_alignment = variants
95            .iter()
96            .map(|v| v.ty.max_alignment)
97            .max()
98            .unwrap_or(MemoryAlignment::U8);
99
100        let payload_offset = align_to(MemoryOffset(tag_size.0), max_payload_alignment);
101        let max_alignment = max(tag_alignment, max_payload_alignment);
102
103        let complete_size_before_alignment = MemorySize(payload_offset.0 + max_payload_size.0);
104        let total_size = adjust_size_to_alignment(complete_size_before_alignment, max_alignment);
105
106        TaggedUnion {
107            name: name.to_string(),
108            tag_offset: MemoryOffset(0),
109            tag_size,
110            tag_alignment,
111            payload_offset,
112            payload_max_size: max_payload_size,
113            max_payload_alignment,
114            total_size,
115            max_alignment,
116            variants: variants.to_vec(),
117        }
118    }
119
120    #[allow(clippy::too_many_lines)]
121    #[must_use]
122    pub fn layout_enum_into_tagged_union(
123        &mut self,
124        name: &str,
125        v: &[EnumVariantType],
126    ) -> TaggedUnion {
127        let variant_layouts: Vec<_> = v
128            .iter()
129            .map(|variant| {
130                let gen_payload_type = self.layout_type(&variant.payload_type);
131
132                TaggedUnionVariant {
133                    name: variant.common.assigned_name.clone(),
134                    ty: gen_payload_type,
135                }
136            })
137            .collect();
138
139        Self::layout_tagged_union(&variant_layouts, name)
140    }
141
142    #[must_use]
143    pub fn layout_enum(
144        &mut self,
145        name: &str,
146        variants: &[EnumVariantType],
147        type_id: TypeId,
148        symbol_id: TopLevelSymbolId,
149    ) -> BasicTypeRef {
150        // Check if we already have a layout for this kind
151        let enum_kind = TypeKind::Enum(EnumType::new(
152            source_map_node::Node::default(),
153            name,
154            symbol_id,
155            vec![String::new()],
156        ));
157        if let Some(existing_layout) = self.kind_to_layout.get(&enum_kind) {
158            return existing_layout.clone();
159        }
160
161        let tagged_union = self.layout_enum_into_tagged_union(name, variants);
162
163        let basic_type = Rc::new(BasicType {
164            id: BasicTypeId(type_id.inner()),
165            total_size: tagged_union.total_size,
166            max_alignment: tagged_union.max_alignment,
167            kind: BasicTypeKind::TaggedUnion(tagged_union),
168        });
169
170        // Store in kind_to_layout
171        let _ = self.kind_to_layout.insert(enum_kind, basic_type.clone());
172
173        basic_type
174    }
175
176    #[must_use]
177    fn layout_vec_like(
178        &mut self,
179        element_type: &TypeRef,
180        capacity: usize,
181    ) -> (BasicTypeRef, MemorySize, MemoryAlignment) {
182        let element_type_basic = self.layout_type(element_type);
183        let (mem_size, mem_alignment) =
184            self.layout_vec_like_from_basic(&element_type_basic, capacity);
185
186        (element_type_basic, mem_size, mem_alignment)
187    }
188
189    #[must_use]
190    fn layout_vec_like_from_basic(
191        &mut self,
192        element_type_basic: &BasicTypeRef,
193        capacity: usize,
194    ) -> (MemorySize, MemoryAlignment) {
195        let total_size =
196            element_type_basic.total_size.0 as usize * capacity + VEC_HEADER_SIZE.0 as usize;
197        let max_alignment = max(element_type_basic.max_alignment, MemoryAlignment::U32);
198
199        (MemorySize(total_size as u32), max_alignment)
200    }
201
202    /// Computes the memory layout for a type in the target architecture.
203    ///
204    /// In compiler terminology:
205    ///
206    /// - "layout" determines size, alignment, and field offsets of types
207    /// - "materialization" refers to how types are represented in memory
208    /// - "lowering" is the process of mapping source types to machine types
209    ///
210    /// This function performs type lowering by mapping high-level types to their
211    /// concrete memory representations, handling:
212    ///
213    /// - Primitive, scalar types (integers, floats, booleans)
214    /// - Aggregate types (structs, tuples, enums)
215    /// - Reference types (pointers, slices)
216    /// - Collection types (vectors, maps)
217    ///
218    /// The layout process considers:
219    ///
220    /// - Size: Total bytes needed for the type
221    /// - Alignment: Memory boundary requirements
222    /// - Padding: Gaps needed for proper field alignment
223    /// - ABI: Target architecture requirements
224    ///
225    /// # Panics
226    ///
227    #[allow(clippy::too_many_lines)]
228    #[must_use]
229    fn layout_type(&mut self, ty: &TypeRef) -> BasicTypeRef {
230        // First check if we already have a layout for this type ID
231        if let Some(x) = self.id_to_layout.get(&ty.id) {
232            return x.clone();
233        }
234
235        // Check if we already have a layout for this kind of type
236        if let Some(existing_layout) = self.kind_to_layout.get(&ty.kind) {
237            // For deduplication, we need to create a new BasicType with the same structure
238            // but with the original TypeId, so that pointer equality works for the same
239            // structural types, but we still have separate entries for each TypeId
240            let new_basic_type = Rc::new(BasicType {
241                id: BasicTypeId(ty.id.inner()),
242                total_size: existing_layout.total_size,
243                max_alignment: existing_layout.max_alignment,
244                kind: existing_layout.kind.clone(),
245            });
246
247            // Store the mapping from this type ID to the new layout
248            let _ = self.id_to_layout.insert(ty.id, new_basic_type.clone());
249
250            // For nested types, we need to properly track all component types
251            // This is essential for the deduplication to work correctly
252            match &*ty.kind {
253                TypeKind::AnonymousStruct(struct_type) => {
254                    // Process each field type
255                    for field in struct_type.field_name_sorted_fields.values() {
256                        let field_type = &field.field_type;
257                        let field_layout = self.layout(field_type);
258                        let _ = self.id_to_layout.insert(field_type.id, field_layout);
259                    }
260                }
261
262                TypeKind::NamedStruct(named_struct) => {
263                    // Process each field type in the anonymous struct
264                    if let TypeKind::AnonymousStruct(anon_struct) =
265                        &*named_struct.anon_struct_type.kind
266                    {
267                        for field in anon_struct.field_name_sorted_fields.values() {
268                            let field_type = &field.field_type;
269                            let field_layout = self.layout(field_type);
270                            let _ = self.id_to_layout.insert(field_type.id, field_layout);
271                        }
272                    }
273                }
274
275                TypeKind::Tuple(tuple_types) => {
276                    // Process each tuple element type
277                    for elem_type in tuple_types {
278                        let elem_layout = self.layout(elem_type);
279                        let _ = self.id_to_layout.insert(elem_type.id, elem_layout);
280                    }
281                }
282                _ => {}
283            }
284
285            return new_basic_type;
286        }
287
288        let basic_type = match &*ty.kind {
289            TypeKind::Never => {
290                create_basic_type(ty.id, BasicTypeKind::U8, MemorySize(0), MemoryAlignment::U8)
291            }
292            TypeKind::Byte => {
293                create_basic_type(ty.id, BasicTypeKind::U8, MemorySize(1), MemoryAlignment::U8)
294            }
295            TypeKind::Codepoint => create_basic_type(
296                ty.id,
297                BasicTypeKind::U32,
298                MemorySize(4),
299                MemoryAlignment::U32,
300            ),
301            TypeKind::Int => create_basic_type(
302                ty.id,
303                BasicTypeKind::S32,
304                MemorySize(4),
305                MemoryAlignment::U32,
306            ),
307
308            TypeKind::Float => create_basic_type(
309                ty.id,
310                BasicTypeKind::Fixed32,
311                MemorySize(4),
312                MemoryAlignment::U32,
313            ),
314
315            TypeKind::Bool => {
316                create_basic_type(ty.id, BasicTypeKind::B8, MemorySize(1), MemoryAlignment::U8)
317            }
318
319            TypeKind::String(byte, char) => create_basic_type(
320                ty.id,
321                BasicTypeKind::StringView {
322                    byte: self.layout(byte),
323                    char: self.layout(char),
324                },
325                STRING_PTR_SIZE,
326                STRING_PTR_ALIGNMENT,
327            ),
328
329            TypeKind::AnonymousStruct(struct_type) => {
330                self.layout_struct(struct_type, "anonymous", ty.id)
331            }
332
333            TypeKind::NamedStruct(named_struct) => self.layout_named_struct(named_struct, ty.id),
334
335            TypeKind::Tuple(tuple_types) => self.layout_tuple(tuple_types, ty.id),
336
337            TypeKind::Optional(inner_type) => self.layout_optional_type(inner_type, ty.id),
338
339            TypeKind::Enum(enum_type) => {
340                let variants = enum_type.variants.values().cloned().collect::<Vec<_>>();
341                self.layout_enum(&enum_type.assigned_name, &variants, ty.id, enum_type.symbol_id)
342            }
343
344            TypeKind::FixedCapacityAndLengthArray(element_type, capacity) => {
345                let (element_layout, total_size, max_alignment) =
346                    self.layout_vec_like(element_type, *capacity);
347
348                let array_type = Rc::new(BasicType {
349                    id: BasicTypeId(ty.id.inner()),
350                    kind: BasicTypeKind::FixedCapacityArray(element_layout.clone(), *capacity),
351                    total_size,
352                    max_alignment,
353                });
354
355                // Also store the element type in id_to_layout
356                let _ = self.id_to_layout.insert(element_type.id, element_layout);
357
358                array_type
359            }
360
361            TypeKind::StringStorage(byte_type, char_type, capacity) => {
362                let (element_layout, total_size, max_alignment) =
363                    self.layout_vec_like(byte_type, *capacity);
364
365                let array_type = Rc::new(BasicType {
366                    id: BasicTypeId(ty.id.inner()),
367                    kind: BasicTypeKind::StringStorage {
368                        element_type: element_layout.clone(),
369                        char: self.layout(char_type),
370                        capacity: *capacity,
371                    },
372                    total_size,
373                    max_alignment,
374                });
375
376                // Also store the element type in id_to_layout
377                let _ = self.id_to_layout.insert(byte_type.id, element_layout);
378
379                array_type
380            }
381
382            TypeKind::Any => create_basic_type(
383                ty.id,
384                BasicTypeKind::Any,
385                ANY_HEADER_SIZE,
386                ANY_HEADER_ALIGNMENT,
387            ),
388
389            TypeKind::DynamicLengthVecView(element_type) => {
390                let (element_layout, _, _) = self.layout_vec_like(element_type, 0);
391
392                let vec_type = Rc::new(BasicType {
393                    id: BasicTypeId(ty.id.inner()),
394                    kind: BasicTypeKind::DynamicLengthVecView(element_layout.clone()),
395                    total_size: PTR_SIZE,
396                    max_alignment: PTR_ALIGNMENT,
397                });
398
399                let _ = self.id_to_layout.insert(element_type.id, element_layout);
400
401                vec_type
402            }
403
404            TypeKind::VecStorage(element_type, capacity) => {
405                let (element_layout, total_size, element_alignment) =
406                    self.layout_vec_like(element_type, *capacity);
407
408                let storage_type = Rc::new(BasicType {
409                    id: BasicTypeId(ty.id.inner()),
410                    kind: BasicTypeKind::VecStorage(element_layout.clone(), *capacity),
411                    total_size,
412                    max_alignment: max(VEC_HEADER_ALIGNMENT, element_alignment),
413                });
414
415                let _ = self.id_to_layout.insert(element_type.id, element_layout);
416
417                storage_type
418            }
419
420            TypeKind::DynamicLengthMapView(key_type, value_type) => {
421                // Layout key type
422                let key_layout = self.layout(key_type);
423                let _ = self.id_to_layout.insert(key_type.id, key_layout.clone());
424
425                // Layout value type
426                let value_layout = self.layout(value_type);
427                let _ = self
428                    .id_to_layout
429                    .insert(value_type.id, value_layout.clone());
430
431                let key_item = OffsetMemoryItem {
432                    offset: MemoryOffset(0),
433                    size: key_layout.total_size,
434                    name: "key".to_string(),
435                    ty: key_layout.clone(),
436                };
437
438                let value_offset = align_to(
439                    MemoryOffset(key_layout.total_size.0),
440                    value_layout.max_alignment,
441                );
442
443                let value_item = OffsetMemoryItem {
444                    offset: value_offset,
445                    size: value_layout.total_size,
446                    name: "value".to_string(),
447                    ty: value_layout.clone(),
448                };
449
450                Rc::new(BasicType {
451                    id: BasicTypeId(ty.id.inner()),
452                    kind: BasicTypeKind::DynamicLengthMapView(
453                        Box::new(key_item),
454                        Box::new(value_item),
455                    ),
456                    total_size: PTR_SIZE,
457                    max_alignment: PTR_ALIGNMENT,
458                })
459            }
460
461            TypeKind::MapStorage(key_type, value_type, logical_limit) => {
462                // Layout key type
463                let key_layout = self.layout(key_type);
464                let _ = self.id_to_layout.insert(key_type.id, key_layout.clone());
465
466                // Layout value type
467                let value_layout = self.layout(value_type);
468                let _ = self
469                    .id_to_layout
470                    .insert(value_type.id, value_layout.clone());
471
472                let logical_limit = *logical_limit;
473
474                let (_bucket_layout, map_init) = hashmap_mem::layout(
475                    key_layout.total_size.0,
476                    key_layout.max_alignment.into(),
477                    value_layout.total_size.0,
478                    value_layout.max_alignment.into(),
479                    logical_limit as u16,
480                );
481                let total_size = MemorySize(map_init.total_size);
482
483                Rc::new(BasicType {
484                    id: BasicTypeId(ty.id.inner()),
485                    kind: BasicTypeKind::MapStorage {
486                        key_type: key_layout,
487                        logical_limit,
488                        capacity: CountU16(map_init.capacity),
489                        value_type: value_layout,
490                    },
491                    total_size,
492                    max_alignment: MAP_HEADER_ALIGNMENT,
493                })
494            }
495
496            TypeKind::Range(_range_struct) => create_basic_type(
497                ty.id,
498                BasicTypeKind::InternalRangeHeader,
499                MemorySize(12),
500                MemoryAlignment::U32,
501            ),
502
503            TypeKind::GridView(element_type) => {
504                let element_layout = self.layout(element_type);
505                let _ = self
506                    .id_to_layout
507                    .insert(element_type.id, element_layout.clone());
508
509                Rc::new(BasicType {
510                    id: BasicTypeId(ty.id.inner()),
511                    kind: BasicTypeKind::GridView(element_layout),
512                    total_size: PTR_SIZE,
513                    max_alignment: PTR_ALIGNMENT,
514                })
515            }
516
517            TypeKind::GridStorage(element_type, width, height) => {
518                let element_layout = self.layout(element_type);
519                let _ = self
520                    .id_to_layout
521                    .insert(element_type.id, element_layout.clone());
522
523                let element_size = element_layout.total_size;
524                let element_alignment = element_layout.max_alignment;
525
526                Rc::new(BasicType {
527                    id: BasicTypeId(ty.id.inner()),
528                    kind: BasicTypeKind::GridStorage(element_layout, *width, *height),
529                    total_size: MemorySize(
530                        GRID_HEADER_SIZE.0 + element_size.0 * (*width as u32) * (*height as u32),
531                    ),
532                    max_alignment: max(GRID_HEADER_ALIGNMENT, element_alignment),
533                })
534            }
535
536            TypeKind::SliceView(element_type) => {
537                let element_layout = self.layout(element_type);
538                let _ = self
539                    .id_to_layout
540                    .insert(element_type.id, element_layout.clone());
541
542                Rc::new(BasicType {
543                    id: BasicTypeId(ty.id.inner()),
544                    kind: BasicTypeKind::SliceView(element_layout),
545                    total_size: PTR_SIZE,
546                    max_alignment: PTR_ALIGNMENT,
547                })
548            }
549
550            TypeKind::SparseStorage(element_type, capacity) => {
551                let element_layout = self.layout(element_type);
552                let size = sparse_mem::layout_size(*capacity as u16, element_layout.total_size.0);
553
554                Rc::new(BasicType {
555                    id: BasicTypeId(ty.id.inner()),
556                    kind: BasicTypeKind::SparseStorage(element_layout, *capacity),
557                    total_size: MemorySize(size as u32),
558                    max_alignment: MemoryAlignment::U64,
559                })
560            }
561
562            TypeKind::SparseView(element_type) => {
563                let element_layout = self.layout(element_type);
564                let _ = self
565                    .id_to_layout
566                    .insert(element_type.id, element_layout.clone());
567
568                Rc::new(BasicType {
569                    id: BasicTypeId(ty.id.inner()),
570                    kind: BasicTypeKind::SparseView(element_layout),
571                    total_size: PTR_SIZE,
572                    max_alignment: PTR_ALIGNMENT,
573                })
574            }
575
576            TypeKind::StackStorage(element_type, capacity) => {
577                let (element_layout, total_size, _max_alignment) =
578                    self.layout_vec_like(element_type, *capacity);
579
580                let storage_type = Rc::new(BasicType {
581                    id: BasicTypeId(ty.id.inner()),
582                    kind: BasicTypeKind::StackStorage(element_layout.clone(), *capacity),
583                    total_size,
584                    max_alignment: VEC_HEADER_ALIGNMENT,
585                });
586
587                let _ = self.id_to_layout.insert(element_type.id, element_layout);
588
589                storage_type
590            }
591
592            TypeKind::StackView(element_type) => {
593                let element_layout = self.layout(element_type);
594                let _ = self
595                    .id_to_layout
596                    .insert(element_type.id, element_layout.clone());
597
598                Rc::new(BasicType {
599                    id: BasicTypeId(ty.id.inner()),
600                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
601                    total_size: PTR_SIZE,
602                    max_alignment: PTR_ALIGNMENT,
603                })
604            }
605
606            TypeKind::QueueStorage(element_type, capacity) => {
607                let (element_layout, total_size, _max_alignment) =
608                    self.layout_vec_like(element_type, *capacity);
609
610                let storage_type = Rc::new(BasicType {
611                    id: BasicTypeId(ty.id.inner()),
612                    kind: BasicTypeKind::QueueStorage(element_layout.clone(), *capacity),
613                    total_size,
614                    max_alignment: VEC_HEADER_ALIGNMENT,
615                });
616
617                let _ = self.id_to_layout.insert(element_type.id, element_layout);
618
619                storage_type
620            }
621
622            TypeKind::QueueView(element_type) => {
623                let element_layout = self.layout(element_type);
624                let _ = self
625                    .id_to_layout
626                    .insert(element_type.id, element_layout.clone());
627
628                Rc::new(BasicType {
629                    id: BasicTypeId(ty.id.inner()),
630                    kind: BasicTypeKind::DynamicLengthVecView(element_layout),
631                    total_size: PTR_SIZE,
632                    max_alignment: PTR_ALIGNMENT,
633                })
634            }
635
636            TypeKind::Function(_) => {
637                panic!("function types can not be laid out")
638            }
639
640            TypeKind::Unit => create_basic_type(
641                ty.id,
642                BasicTypeKind::Empty,
643                MemorySize(0),
644                MemoryAlignment::U8,
645            ),
646        };
647
648        // Store in both caches
649        let _ = self.id_to_layout.insert(ty.id, basic_type.clone());
650        let _ = self
651            .kind_to_layout
652            .insert((*ty.kind).clone(), basic_type.clone());
653
654        basic_type
655    }
656
657    fn layout_named_struct(
658        &mut self,
659        named_struct_type: &NamedStructType,
660        type_id: TypeId,
661    ) -> BasicTypeRef {
662        // Check if we already have a layout for this kind
663        let struct_kind = TypeKind::NamedStruct(named_struct_type.clone());
664        if let Some(existing_layout) = self.kind_to_layout.get(&struct_kind) {
665            return existing_layout.clone();
666        }
667
668        // Extract AnonymousStructType from the TypeRef
669        let anon_struct = match &*named_struct_type.anon_struct_type.kind {
670            TypeKind::AnonymousStruct(anon_struct) => anon_struct,
671            _ => panic!("Expected AnonymousStruct in NamedStructType"),
672        };
673
674        let inner_struct = self.layout_struct_type(anon_struct, &named_struct_type.assigned_name);
675
676        // Use the provided TypeId
677        let basic_type = Rc::new(BasicType {
678            id: BasicTypeId(type_id.inner()), // Use the provided type ID
679            total_size: inner_struct.total_size,
680            max_alignment: inner_struct.max_alignment,
681            kind: BasicTypeKind::Struct(inner_struct),
682        });
683
684        // Store in kind_to_layout for future deduplication
685        let _ = self.kind_to_layout.insert(struct_kind, basic_type.clone());
686
687        basic_type
688    }
689
690    #[must_use]
691    pub fn layout_struct_type(
692        &mut self,
693        struct_type: &AnonymousStructType,
694        name: &str,
695    ) -> StructType {
696        let mut offset = MemoryOffset(0);
697        let mut max_alignment = MemoryAlignment::U8;
698        let mut items = Vec::with_capacity(struct_type.field_name_sorted_fields.len());
699
700        for (field_name, field_type) in &struct_type.field_name_sorted_fields {
701            // Use layout instead of layout_type to ensure proper caching
702            let field_layout = self.layout(&field_type.field_type);
703            check_type_size(&field_layout, &format!("field {name}::{field_name}"));
704
705            // Make sure the field type is in the id_to_layout map
706            let _ = self
707                .id_to_layout
708                .insert(field_type.field_type.id, field_layout.clone());
709
710            offset = align_to(offset, field_layout.max_alignment);
711
712            items.push(OffsetMemoryItem {
713                offset,
714                size: field_layout.total_size,
715                name: field_name.clone(),
716                ty: field_layout.clone(),
717            });
718
719            offset = offset + field_layout.total_size;
720
721            if field_layout.max_alignment > max_alignment {
722                max_alignment = field_layout.max_alignment;
723            }
724        }
725
726        let total_size = adjust_size_to_alignment(offset.as_size(), max_alignment);
727
728        StructType {
729            name: name.to_string(),
730            fields: items,
731            total_size,
732            max_alignment,
733        }
734    }
735
736    pub fn layout_struct(
737        &mut self,
738        struct_type: &AnonymousStructType,
739        name: &str,
740        type_id: TypeId,
741    ) -> BasicTypeRef {
742        // Always process each field's type to ensure it's in the cache
743        for (_field_name, struct_field) in &struct_type.field_name_sorted_fields {
744            let field_type = &struct_field.field_type;
745            let _field_layout = self.layout(field_type);
746
747            // The layout method already handles storing in both caches
748        }
749
750        // Check if we already have a layout for this kind
751        let struct_kind = TypeKind::AnonymousStruct(struct_type.clone());
752        if let Some(existing_layout) = self.kind_to_layout.get(&struct_kind) {
753            // Store the mapping from this type ID to the existing layout
754            let _ = self.id_to_layout.insert(type_id, existing_layout.clone());
755            return existing_layout.clone();
756        }
757
758        let struct_layout = self.layout_struct_type(struct_type, name);
759
760        // Use the provided type ID
761        let struct_id = type_id;
762
763        let basic_type = Rc::new(BasicType {
764            id: BasicTypeId(struct_id.inner()),
765            total_size: struct_layout.total_size,
766            max_alignment: struct_layout.max_alignment,
767            kind: BasicTypeKind::Struct(struct_layout),
768        });
769
770        // Store in both caches
771        let _ = self.id_to_layout.insert(struct_id, basic_type.clone());
772        let _ = self.kind_to_layout.insert(struct_kind, basic_type.clone());
773
774        basic_type
775    }
776
777    #[must_use]
778    pub fn layout_optional_type(&mut self, inner_type: &TypeRef, type_id: TypeId) -> BasicTypeRef {
779        // Check if we already have a layout for this kind
780        let optional_kind = TypeKind::Optional(inner_type.clone());
781        if let Some(existing_layout) = self.kind_to_layout.get(&optional_kind) {
782            assert!(matches!(&existing_layout.kind, BasicTypeKind::Optional(_)));
783            return existing_layout.clone();
784        }
785
786        // Layout the inner type first
787        let inner_layout = self.layout(inner_type);
788
789        // Store the inner type in both caches
790        let _ = self
791            .id_to_layout
792            .insert(inner_type.id, inner_layout.clone());
793        let _ = self
794            .kind_to_layout
795            .insert((*inner_type.kind).clone(), inner_layout);
796
797        // Create the optional type
798        let optional_union = self.layout_optional_type_items(inner_type);
799
800        // Use the provided type ID
801        let optional_id = type_id;
802
803        let basic_type = Rc::new(BasicType {
804            id: BasicTypeId(optional_id.inner()),
805            total_size: optional_union.total_size,
806            max_alignment: optional_union.max_alignment,
807            kind: BasicTypeKind::Optional(optional_union),
808        });
809
810        // Store in both caches
811        let _ = self.id_to_layout.insert(optional_id, basic_type.clone());
812        let _ = self
813            .kind_to_layout
814            .insert(optional_kind, basic_type.clone());
815
816        basic_type
817    }
818
819    #[must_use]
820    pub fn layout_optional_type_items(&mut self, inner_type: &TypeRef) -> TaggedUnion {
821        let gen_type = self.layout_type(inner_type);
822
823        let payload_tagged_variant = TaggedUnionVariant {
824            name: "Some".to_string(),
825            ty: gen_type,
826        };
827
828        let none_tagged_variant = TaggedUnionVariant {
829            name: "None".to_string(),
830            ty: Rc::new(BasicType {
831                id: BasicTypeId(0),
832                kind: BasicTypeKind::Empty,
833                total_size: MemorySize(0),
834                max_alignment: MemoryAlignment::U8,
835            }),
836        };
837
838        Self::layout_tagged_union(&[none_tagged_variant, payload_tagged_variant], "Maybe")
839    }
840
841    #[must_use]
842    pub fn layout_tuple_items(&mut self, types: &[TypeRef]) -> TupleType {
843        // First pass: Determine maximum alignment requirement
844        let mut max_alignment = MemoryAlignment::U8;
845        for ty in types {
846            // Use layout instead of layout_type to ensure proper caching
847            let elem_layout = self.layout(ty);
848
849            // Make sure the element type is in the id_to_layout map
850            let _ = self.id_to_layout.insert(ty.id, elem_layout.clone());
851
852            if elem_layout.max_alignment > max_alignment {
853                max_alignment = elem_layout.max_alignment;
854            }
855        }
856
857        // Second pass: Layout fields using the maximum alignment
858        let mut offset = MemoryOffset(0);
859        let mut items = Vec::with_capacity(types.len());
860
861        for (i, ty) in types.iter().enumerate() {
862            // Reuse the layout from the cache
863            let elem_layout = self.layout(ty);
864
865            offset = align_to(offset, elem_layout.max_alignment);
866
867            items.push(OffsetMemoryItem {
868                offset,
869                size: elem_layout.total_size,
870                name: i.to_string(),
871                ty: elem_layout.clone(),
872            });
873
874            offset = offset + elem_layout.total_size;
875        }
876
877        // Ensure total size is aligned to max_alignment
878        let total_size = adjust_size_to_alignment(offset.as_size(), max_alignment);
879
880        TupleType {
881            fields: items,
882            total_size,
883            max_alignment,
884        }
885    }
886
887    #[must_use]
888    pub fn layout_tuple(&mut self, types: &[TypeRef], tuple_id: TypeId) -> BasicTypeRef {
889        // Always process each inner type to ensure it's in the cache
890        for ty in types {
891            let inner_layout = self.layout(ty);
892            let _ = self.id_to_layout.insert(ty.id, inner_layout.clone());
893            let _ = self
894                .kind_to_layout
895                .insert((*ty.kind).clone(), inner_layout.clone());
896        }
897
898        // Check if we already have a layout for this kind
899        let tuple_kind = TypeKind::Tuple(types.to_vec());
900        if let Some(existing_layout) = self.kind_to_layout.get(&tuple_kind) {
901            // Store the mapping from this type ID to the existing layout
902            let _ = self.id_to_layout.insert(tuple_id, existing_layout.clone());
903            return existing_layout.clone();
904        }
905
906        let tuple_layout = self.layout_tuple_items(types);
907
908        let basic_type = Rc::new(BasicType {
909            id: BasicTypeId(tuple_id.inner()),
910            total_size: tuple_layout.total_size,
911            max_alignment: tuple_layout.max_alignment,
912            kind: BasicTypeKind::Tuple(tuple_layout),
913        });
914
915        // Store in both caches
916        let _ = self.id_to_layout.insert(tuple_id, basic_type.clone());
917        let _ = self.kind_to_layout.insert(tuple_kind, basic_type.clone());
918
919        basic_type
920    }
921}
922
923fn create_basic_type(
924    type_id: TypeId,
925    kind: BasicTypeKind,
926    size: MemorySize,
927    alignment: MemoryAlignment,
928) -> BasicTypeRef {
929    let basic_type_id = BasicTypeId(type_id.inner());
930
931    Rc::new(BasicType {
932        id: basic_type_id,
933        kind,
934        total_size: size,
935        max_alignment: alignment,
936    })
937}
938
939pub const fn check_type_size(ty: &BasicType, _comment: &str) {
940    if ty.total_size.0 > 1024 * 1024 {
941        //eprintln!("suspicious allocation: {} for {ty}", ty.total_size);
942    }
943}