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}