Skip to main content

charon_lib/ast/items/
layout.rs

1//! The layout of types.
2use crate::ast::*;
3use crate::ids::IndexVec;
4use crate::utils::serialize_map_to_array::SeqHashMapToArray;
5use derive_generic_visitor::*;
6use serde::{Deserialize, Serialize};
7use serde_state::{DeserializeState, SerializeState};
8
9pub type ByteCount = u64;
10
11/// Type layout information.
12///
13/// Does not include information about niches.
14/// If the type does not have a fully known layout (e.g. it is ?Sized)
15/// some of the layout parts are not available.
16#[derive(
17    Debug, Clone, PartialEq, Eq, SerializeState, DeserializeState, Drive, DriveMut, DriveTwo,
18)]
19pub struct Layout {
20    /// The size of the type in bytes.
21    #[drive(skip)]
22    pub size: Option<ByteCount>,
23    /// The alignment, in bytes.
24    #[drive(skip)]
25    pub align: Option<ByteCount>,
26    /// Decision tree that determines the active variant by reading memory. Only `Some` for enums.
27    #[drive(skip)]
28    #[serde_state(stateless)]
29    pub discriminator: Option<Discriminator>,
30    /// Whether the type is uninhabited, i.e. has any valid value at all.
31    /// Note that uninhabited types can have arbitrary layouts: `(u32, !)` has space for the `u32`
32    /// and `enum E2 { A, B(!), C(i32, !) }` may have space for a discriminant.
33    #[drive(skip)]
34    pub uninhabited: bool,
35    /// Map from `VariantId` to the corresponding field layouts. Some variants don't have a
36    /// meaningful layout due to being uninhabited (though an uninhabited variant may have a
37    /// layout). Structs and unions are modeled as having exactly one variant.
38    #[serde_state(stateless)]
39    pub variant_layouts: IndexVec<VariantId, Option<VariantLayout>>,
40    /// The representation options of this type declaration as annotated by the user.
41    #[drive(skip)]
42    #[serde_state(stateless)]
43    pub repr: ReprOptions,
44}
45
46/// Simplified layout of a single variant.
47///
48/// Maps fields to their offset within the layout.
49#[derive(
50    Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, Drive, DriveMut, DriveTwo,
51)]
52pub struct VariantLayout {
53    /// The offset of each field.
54    #[drive(skip)]
55    pub field_offsets: IndexVec<FieldId, ByteCount>,
56    /// Whether the variant is uninhabited, i.e. has any valid possible value.
57    /// Note that uninhabited types can have arbitrary layouts.
58    #[drive(skip)]
59    pub uninhabited: bool,
60    /// How to write the tag when constructing this variant. Each entry means: write `value` at
61    /// byte `offset`. Mirrors MiniRust's `Variant::tagger`.
62    #[drive(skip)]
63    pub tagger: Vec<(ByteCount, ScalarValue)>,
64}
65
66/// Decision tree used to determine the active variant by reading memory. Mirrors MiniRust's
67/// `Discriminator`.
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub enum Discriminator {
70    /// The variant is known.
71    Known(VariantId),
72    /// No valid variant (e.g., invalid tag value).
73    Invalid,
74    /// Branch on an integer value read from memory at `offset`.
75    Branch {
76        /// Byte offset to read from.
77        offset: ByteCount,
78        /// Integer type to read.
79        int_ty: IntegerTy,
80        /// If the integer is in one of these ranges, continue with the given `Discriminator`. The
81        /// ranges are sorted.
82        children: Vec<(std::ops::RangeInclusive<ScalarValue>, Discriminator)>,
83        /// Fallback if no range in `children` matches.
84        fallback: Box<Discriminator>,
85    },
86}
87
88/// The representation options as annotated by the user.
89///
90/// NOTE: This does not include less common/unstable representations such as `#[repr(simd)]`
91/// or the compiler internal `#[repr(linear)]`. Similarly, enum discriminant representations
92/// are encoded in [`Variant::discriminant`] and [`Discriminator`] instead.
93/// This only stores whether the discriminant type was derived from an explicit annotation.
94#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
95pub struct ReprOptions {
96    pub repr_algo: ReprAlgorithm,
97    pub align_modif: Option<AlignmentModifier>,
98    pub transparent: bool,
99    pub explicit_discr_type: bool,
100}
101
102/// Describes which layout algorithm is used for representing the corresponding type.
103/// Depends on the `#[repr(...)]` used.
104#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
105pub enum ReprAlgorithm {
106    /// The default layout algorithm. Used without an explicit `Ĺ—epr` or for `repr(Rust)`.
107    #[default]
108    Rust,
109    /// The C layout algorithm as enforced by `repr(C)`.
110    C,
111}
112
113/// Describes modifiers to the alignment and packing of the corresponding type.
114/// Represents `repr(align(n))` and `repr(packed(n))`.
115#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
116pub enum AlignmentModifier {
117    Align(ByteCount),
118    Pack(ByteCount),
119}
120
121#[derive(Clone, Drive, DriveMut, DriveTwo, SerializeState, DeserializeState)]
122#[serde_state(stateless)]
123pub struct TargetInfo {
124    /// The pointer size of the target in bytes.
125    pub target_pointer_size: ByteCount,
126    /// Whether the target platform uses little endian byte order.
127    pub is_little_endian: bool,
128    /// The minimum size of a [`repr(C)`] enum.
129    pub c_enum_min_size: ByteCount,
130    /// Alignments for primitive types.
131    #[serde(with = "SeqHashMapToArray::<LiteralTy, ByteCount>")]
132    pub primitive_alignments: SeqHashMap<LiteralTy, ByteCount>,
133}
134
135impl Layout {
136    pub fn is_variant_uninhabited(&self, variant_id: VariantId) -> bool {
137        self.variant_layouts[variant_id]
138            .as_ref()
139            .is_none_or(|v| v.uninhabited)
140    }
141
142    pub fn is_c_repr(&self) -> bool {
143        self.repr.repr_algo == ReprAlgorithm::C
144    }
145}
146
147#[derive(Debug, PartialEq, Eq)]
148pub enum DiscriminantReadError {
149    /// We read an uninitialized byte.
150    UninitByte,
151    /// We reached an invalid discriminant state.
152    InvalidDiscriminant,
153}
154
155impl Discriminator {
156    /// Make a trivial discriminator that always returns the given variant id.
157    pub fn trivial(variant_id: VariantId) -> Self {
158        Self::Known(variant_id)
159    }
160
161    /// Read a discriminant from memory. The `read` function simulates reading an integer of the
162    /// given type at the given byte offset from memory and can return `UninitByte` if the byte
163    /// could not be read.
164    pub fn read_discriminant(
165        &self,
166        read: impl Fn(ByteCount, IntegerTy) -> Result<ScalarValue, DiscriminantReadError> + Copy,
167    ) -> Result<VariantId, DiscriminantReadError> {
168        match self {
169            Discriminator::Known(id) => Ok(*id),
170            Discriminator::Invalid => Err(DiscriminantReadError::InvalidDiscriminant),
171            Discriminator::Branch {
172                offset,
173                int_ty,
174                fallback,
175                children,
176            } => {
177                let val = read(*offset, *int_ty)?;
178                for (range, child) in children {
179                    if range.contains(&val) {
180                        return child.read_discriminant(read);
181                    }
182                }
183                fallback.read_discriminant(read)
184            }
185        }
186    }
187}
188
189impl ReprOptions {
190    /// Whether this representation options guarantee a fixed
191    /// field ordering for the type.
192    ///
193    /// Since we don't support `repr(simd)` or `repr(linear)` yet, this is
194    /// the case if it's either `repr(C)` or an explicit discriminant type for
195    /// an enum with fields (if it doesn't have fields, this obviously doesn't matter anyway).
196    ///
197    /// Cf. <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.c.struct>
198    /// and <https://doc.rust-lang.org/reference/type-layout.html#r-layout.repr.primitive.adt>.
199    pub fn guarantees_fixed_field_order(&self) -> bool {
200        self.repr_algo == ReprAlgorithm::C || self.explicit_discr_type
201    }
202}
203
204impl IntTy {
205    /// Important: this returns the target byte count for the types.
206    /// Must not be used for host types from rustc.
207    pub fn target_size(&self, ptr_size: ByteCount) -> usize {
208        match self {
209            IntTy::Isize => ptr_size as usize,
210            IntTy::I8 => size_of::<i8>(),
211            IntTy::I16 => size_of::<i16>(),
212            IntTy::I32 => size_of::<i32>(),
213            IntTy::I64 => size_of::<i64>(),
214            IntTy::I128 => size_of::<i128>(),
215        }
216    }
217}
218impl UIntTy {
219    /// Important: this returns the target byte count for the types.
220    /// Must not be used for host types from rustc.
221    pub fn target_size(&self, ptr_size: ByteCount) -> usize {
222        match self {
223            UIntTy::Usize => ptr_size as usize,
224            UIntTy::U8 => size_of::<u8>(),
225            UIntTy::U16 => size_of::<u16>(),
226            UIntTy::U32 => size_of::<u32>(),
227            UIntTy::U64 => size_of::<u64>(),
228            UIntTy::U128 => size_of::<u128>(),
229        }
230    }
231}
232impl FloatTy {
233    /// Important: this returns the target byte count for the types.
234    /// Must not be used for host types from rustc.
235    pub fn target_size(&self) -> usize {
236        match self {
237            FloatTy::F16 => size_of::<u16>(),
238            FloatTy::F32 => size_of::<u32>(),
239            FloatTy::F64 => size_of::<u64>(),
240            FloatTy::F128 => size_of::<u128>(),
241        }
242    }
243}