repr_c_impl/
layout.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2use std::fmt::Debug;
3
4/// A C type.
5#[derive(Clone, Debug, Eq, PartialEq)]
6pub struct Type<I: Layout> {
7    /// The layout of the type.
8    pub layout: I::TypeLayout,
9    /// The annotations on this type.
10    pub annotations: Vec<Annotation>,
11    /// The variant of the type.
12    pub variant: TypeVariant<I>,
13}
14
15impl<I: Layout> Type<I> {
16    /// Returns the identical type with the [`Layout`] converted.
17    pub fn into<J: Layout>(self) -> Type<J>
18    where
19        I::TypeLayout: Into<J::TypeLayout>,
20        I::FieldLayout: Into<J::FieldLayout>,
21        I::OpaqueLayout: Into<J::OpaqueLayout>,
22    {
23        Type {
24            layout: self.layout.into(),
25            annotations: self.annotations,
26            variant: self.variant.into(),
27        }
28    }
29}
30
31/// An annotation of a type or field.
32///
33/// Builtin types, arrays, and opaque types cannot be annotated.
34#[derive(Copy, Clone, Debug, Eq, PartialEq)]
35pub enum Annotation {
36    /// The `PragmaPack` annotation.
37    ///
38    /// This cannot be used on fields. At most one of these can be used on a type.
39    ///
40    /// If the argument is `n`, it corresponds to `#pragma pack(n/8)` in C.
41    /// If `n` is not a multiple of 8, this annotation will be ignored.
42    PragmaPack(u64),
43    /// The `AttrPacked` annotation.
44    ///
45    /// This corresponds to `__attribute__((packed))` in C. On MSVC targets, the behavior
46    /// is the behavior of Clang.
47    AttrPacked,
48    /// The `Aligned` annotation.
49    ///
50    /// If the argument is `Some(n)`, `n` must be a power of two and at least 8. It is the
51    /// corresponding C argument but in bits instead of bytes.
52    ///
53    /// If the argument is `Some(n)`, it corresponds to `__declspec(align(n/8))` on MSVC
54    /// targets and `__attribute__((aligned(n/8)))` otherwise.
55    ///
56    /// If the argument is `None`, it corresponds to `__attribute__((aligned))`. On MSVC
57    /// targets, the behavior is the behavior of Clang.
58    Align(Option<u64>),
59}
60
61/// A collection of types encoding the layout of a type.
62pub trait Layout {
63    /// The type used to encode the layout of the type itself.
64    type TypeLayout: Copy + Default + Debug + Eq + PartialEq;
65    /// The type used to encode the layout of a field in a record.
66    type FieldLayout: Copy + Default + Debug + Eq + PartialEq;
67    /// The type used to encode the layout of an opaque type.
68    type OpaqueLayout: Copy + Default + Debug + Eq + PartialEq;
69}
70
71/// The computed layout of a type.
72#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
73pub struct TypeLayout {
74    /// The size of the type in bits.
75    ///
76    /// This is the value returned by `sizeof` and C and `std::mem::size_of` in Rust
77    /// (but in bits instead of bytes). This is a multiple of `pointer_alignment_bits`.
78    pub size_bits: u64,
79    /// The alignment of the type, in bits, when used as a field in a record.
80    ///
81    /// This is usually the value returned by `_Alignof` in C, but there are some edge
82    /// cases in GCC where `_Alignof` returns a smaller value.
83    pub field_alignment_bits: u64,
84    /// The alignment, in bits, of valid pointers to this type.
85    ///
86    /// This is the value returned by `std::mem::align_of` in Rust
87    /// (but in bits instead of bytes). `size_bits` is a multiple of this value.
88    pub pointer_alignment_bits: u64,
89    /// The required alignment of the type in bits.
90    ///
91    /// This value is only used by MSVC targets. It is 8 on all other
92    /// targets. On MSVC targets, this value restricts the effects of `#pragma pack` except
93    /// in some cases involving bit-fields.
94    pub required_alignment_bits: u64,
95}
96
97/// The layout of a field.
98#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
99pub struct FieldLayout {
100    /// The offset of the struct, in bits, from the start of the struct.
101    pub offset_bits: u64,
102    /// The size, in bits, of the field.
103    ///
104    /// For bit-fields, this is the width of the field.
105    pub size_bits: u64,
106}
107
108impl Layout for TypeLayout {
109    type TypeLayout = TypeLayout;
110    type FieldLayout = FieldLayout;
111    type OpaqueLayout = TypeLayout;
112}
113
114impl Layout for () {
115    type TypeLayout = ();
116    type FieldLayout = ();
117    type OpaqueLayout = TypeLayout;
118}
119
120impl Into<()> for TypeLayout {
121    fn into(self) {}
122}
123
124impl Into<()> for FieldLayout {
125    fn into(self) {}
126}
127
128/// An enum of all available types.
129#[derive(Clone, Debug, Eq, PartialEq)]
130pub enum TypeVariant<I: Layout> {
131    /// A builtin type.
132    Builtin(BuiltinType),
133    /// A record. Struct or union.
134    Record(Record<I>),
135    /// A typedef.
136    ///
137    /// The box contains the target type.
138    Typedef(Box<Type<I>>),
139    /// An array.
140    Array(Array<I>),
141    /// An enum.
142    ///
143    /// The vector contains the values of the variants.
144    Enum(Vec<i128>),
145    /// An opaque type.
146    ///
147    /// This does not correspond to anything in C. It is useful if the layout of a nested
148    /// type is already known and should not be recomputed. On all supported targets,
149    /// substituting a type for the opaque type with the same layout leads to the same
150    /// layout for the containing type.
151    Opaque(I::OpaqueLayout),
152}
153
154impl<I: Layout> TypeVariant<I> {
155    /// Returns the identical type variant with the [`Layout`] converted.
156    pub fn into<J: Layout>(self) -> TypeVariant<J>
157    where
158        I::TypeLayout: Into<J::TypeLayout>,
159        I::FieldLayout: Into<J::FieldLayout>,
160        I::OpaqueLayout: Into<J::OpaqueLayout>,
161    {
162        match self {
163            TypeVariant::Builtin(bi) => TypeVariant::Builtin(bi),
164            TypeVariant::Record(rt) => TypeVariant::Record(Record {
165                kind: rt.kind,
166                fields: rt.fields.into_iter().map(|v| v.into()).collect(),
167            }),
168            TypeVariant::Typedef(td) => TypeVariant::Typedef(Box::new((*td).into())),
169            TypeVariant::Array(at) => TypeVariant::Array(Array {
170                element_type: Box::new((*at.element_type).into()),
171                num_elements: at.num_elements,
172            }),
173            TypeVariant::Opaque(l) => TypeVariant::Opaque(l.into()),
174            TypeVariant::Enum(v) => TypeVariant::Enum(v),
175        }
176    }
177}
178
179/// A record.
180///
181/// This corresponds to a struct or a union in C.
182///
183/// # Example
184///
185/// ```c
186/// #pragma pack(8)
187/// struct {
188///     int i __attribute__((aligned(16)));
189///     char :1;
190/// };
191/// ```
192///
193/// ```
194/// # use repr_c_impl::layout::{Record, RecordKind, RecordField, Annotation, Type, TypeVariant, BuiltinType};
195/// Type::<()> {
196///     layout: (),
197///     annotations: vec!(Annotation::PragmaPack(64)),
198///     variant: TypeVariant::Record(Record::<()> {
199///         kind: RecordKind::Struct,
200///         fields: vec![
201///             RecordField {
202///                 layout: None,
203///                 annotations: vec!(Annotation::Align(Some(128))),
204///                 named: true,
205///                 bit_width: None,
206///                 ty: Type {
207///                     layout: (),
208///                     annotations: vec!(),
209///                     variant: TypeVariant::Builtin(BuiltinType::Int),
210///                 },
211///             },
212///             RecordField {
213///                 layout: None,
214///                 annotations: vec!(),
215///                 named: false,
216///                 bit_width: Some(1),
217///                 ty: Type {
218///                     layout: (),
219///                     annotations: vec!(),
220///                     variant: TypeVariant::Builtin(BuiltinType::Char),
221///                 },
222///             },
223///         ],
224///     }),
225/// };
226/// ```
227#[derive(Clone, Debug, Eq, PartialEq)]
228pub struct Record<I: Layout> {
229    /// The type of the record. Struct or union.
230    pub kind: RecordKind,
231    /// The fields of the record.
232    pub fields: Vec<RecordField<I>>,
233}
234
235/// An array.
236///
237/// This corresponds to the type of `T var[N]` in C.
238///
239/// # Example
240///
241/// ```c
242/// int i[1];
243/// ```
244///
245/// ```
246/// # use repr_c_impl::layout::{Type, TypeVariant, Array, BuiltinType};
247/// Array::<()> {
248///     element_type: Box::new(Type {
249///         layout: (),
250///         annotations: vec!(),
251///         variant: TypeVariant::Builtin(BuiltinType::Int),
252///     }),
253///     num_elements: Some(1),
254/// };
255/// ```
256#[derive(Clone, Debug, Eq, PartialEq)]
257pub struct Array<I: Layout> {
258    /// The type of elements of the array.
259    pub element_type: Box<Type<I>>,
260    /// The number of elements in the array.
261    ///
262    /// If this is `None`, it corresponds to a flexible array in C.
263    pub num_elements: Option<u64>,
264}
265
266/// A field of a record.
267#[derive(Clone, Debug, Eq, PartialEq)]
268pub struct RecordField<I: Layout> {
269    /// The layout of the field.
270    ///
271    /// This is `None` for unnamed fields.
272    pub layout: Option<I::FieldLayout>,
273    /// The annotations on this field.
274    pub annotations: Vec<Annotation>,
275    /// Whether the field is named.
276    ///
277    /// An unnamed field in C is always a bit-field and is declared like `T : N` where
278    /// `T` is the type of the field and `N` is the width of the bit-field.
279    pub named: bool,
280    /// If this is a bit-field, the width of the field.
281    ///
282    /// The field is recognized as a bit-field if and only if this is `Some`.
283    pub bit_width: Option<u64>,
284    /// The type of the field.
285    pub ty: Type<I>,
286}
287
288impl<I: Layout> RecordField<I> {
289    /// Returns the identical record field with the [`Layout`] converted.
290    pub fn into<J: Layout>(self) -> RecordField<J>
291    where
292        I::TypeLayout: Into<J::TypeLayout>,
293        I::FieldLayout: Into<J::FieldLayout>,
294        I::OpaqueLayout: Into<J::OpaqueLayout>,
295    {
296        RecordField {
297            layout: self.layout.map(|v| v.into()),
298            annotations: self.annotations,
299            named: self.named,
300            bit_width: self.bit_width,
301            ty: self.ty.into(),
302        }
303    }
304}
305
306/// The type of a record. Either a struct or a union.
307#[derive(Copy, Clone, Debug, Eq, PartialEq)]
308pub enum RecordKind {
309    /// A struct.
310    Struct,
311    /// A union.
312    Union,
313}
314
315/// A builtin type.
316///
317/// This includes both builtin Rust and C types. The Rust types will be treated like the
318/// corresponding C types if possible. If the type does not exist on the target, the
319/// results might not be meaningful.
320#[derive(Copy, Clone, Debug, Eq, PartialEq)]
321pub enum BuiltinType {
322    /// `()`
323    Unit,
324    /// `bool`
325    Bool,
326    /// `u8`
327    U8,
328    /// `u16`
329    U16,
330    /// `u32`
331    U32,
332    /// `u64`
333    U64,
334    /// `u128`
335    U128,
336    /// `i8`
337    I8,
338    /// `i16`
339    I16,
340    /// `i32`
341    I32,
342    /// `i64`
343    I64,
344    /// `i128`
345    I128,
346    /// `c_char`
347    Char,
348    /// `c_schar`
349    SignedChar,
350    /// `c_uchar`
351    UnsignedChar,
352    /// `c_short`
353    Short,
354    /// `c_ushort`
355    UnsignedShort,
356    /// `c_int`
357    Int,
358    /// `c_uint`
359    UnsignedInt,
360    /// `c_long`
361    Long,
362    /// `c_ulong`
363    UnsignedLong,
364    /// `c_longlong`
365    LongLong,
366    /// `c_ulonglong`
367    UnsignedLongLong,
368    /// `f32`
369    F32,
370    /// `f64`
371    F64,
372    /// `c_float`
373    Float,
374    /// `c_double`
375    Double,
376    /// `*const T`,`*mut T`, `&T`, `&mut T` for sized `T`; `fn()`, `Option<fn()>`, etc.
377    Pointer,
378}