1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
// SPDX-License-Identifier: MIT OR Apache-2.0
use std::fmt::Debug;

/// A C type.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Type<I: Layout> {
    /// The layout of the type.
    pub layout: I::TypeLayout,
    /// The annotations on this type.
    pub annotations: Vec<Annotation>,
    /// The variant of the type.
    pub variant: TypeVariant<I>,
}

impl<I: Layout> Type<I> {
    /// Returns the identical type with the [`Layout`] converted.
    pub fn into<J: Layout>(self) -> Type<J>
    where
        I::TypeLayout: Into<J::TypeLayout>,
        I::FieldLayout: Into<J::FieldLayout>,
        I::OpaqueLayout: Into<J::OpaqueLayout>,
    {
        Type {
            layout: self.layout.into(),
            annotations: self.annotations,
            variant: self.variant.into(),
        }
    }
}

/// An annotation of a type or field.
///
/// Builtin types, arrays, and opaque types cannot be annotated.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Annotation {
    /// The `PragmaPack` annotation.
    ///
    /// This cannot be used on fields. At most one of these can be used on a type.
    ///
    /// If the argument is `n`, it corresponds to `#pragma pack(n/8)` in C.
    /// If `n` is not a multiple of 8, this annotation will be ignored.
    PragmaPack(u64),
    /// The `AttrPacked` annotation.
    ///
    /// This corresponds to `__attribute__((packed))` in C. On MSVC targets, the behavior
    /// is the behavior of Clang.
    AttrPacked,
    /// The `Aligned` annotation.
    ///
    /// If the argument is `Some(n)`, `n` must be a power of two and at least 8. It is the
    /// corresponding C argument but in bits instead of bytes.
    ///
    /// If the argument is `Some(n)`, it corresponds to `__declspec(align(n/8))` on MSVC
    /// targets and `__attribute__((aligned(n/8)))` otherwise.
    ///
    /// If the argument is `None`, it corresponds to `__attribute__((aligned))`. On MSVC
    /// targets, the behavior is the behavior of Clang.
    Align(Option<u64>),
}

/// A collection of types encoding the layout of a type.
pub trait Layout {
    /// The type used to encode the layout of the type itself.
    type TypeLayout: Copy + Default + Debug + Eq + PartialEq;
    /// The type used to encode the layout of a field in a record.
    type FieldLayout: Copy + Default + Debug + Eq + PartialEq;
    /// The type used to encode the layout of an opaque type.
    type OpaqueLayout: Copy + Default + Debug + Eq + PartialEq;
}

/// The computed layout of a type.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub struct TypeLayout {
    /// The size of the type in bits.
    ///
    /// This is the value returned by `sizeof` and C and `std::mem::size_of` in Rust
    /// (but in bits instead of bytes). This is a multiple of `pointer_alignment_bits`.
    pub size_bits: u64,
    /// The alignment of the type, in bits, when used as a field in a record.
    ///
    /// This is usually the value returned by `_Alignof` in C, but there are some edge
    /// cases in GCC where `_Alignof` returns a smaller value.
    pub field_alignment_bits: u64,
    /// The alignment, in bits, of valid pointers to this type.
    ///
    /// This is the value returned by `std::mem::align_of` in Rust
    /// (but in bits instead of bytes). `size_bits` is a multiple of this value.
    pub pointer_alignment_bits: u64,
    /// The required alignment of the type in bits.
    ///
    /// This value is only used by MSVC targets. It is 8 on all other
    /// targets. On MSVC targets, this value restricts the effects of `#pragma pack` except
    /// in some cases involving bit-fields.
    pub required_alignment_bits: u64,
}

/// The layout of a field.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
pub struct FieldLayout {
    /// The offset of the struct, in bits, from the start of the struct.
    pub offset_bits: u64,
    /// The size, in bits, of the field.
    ///
    /// For bit-fields, this is the width of the field.
    pub size_bits: u64,
}

impl Layout for TypeLayout {
    type TypeLayout = TypeLayout;
    type FieldLayout = FieldLayout;
    type OpaqueLayout = TypeLayout;
}

impl Layout for () {
    type TypeLayout = ();
    type FieldLayout = ();
    type OpaqueLayout = TypeLayout;
}

impl Into<()> for TypeLayout {
    fn into(self) {}
}

impl Into<()> for FieldLayout {
    fn into(self) {}
}

/// An enum of all available types.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum TypeVariant<I: Layout> {
    /// A builtin type.
    Builtin(BuiltinType),
    /// A record. Struct or union.
    Record(Record<I>),
    /// A typedef.
    ///
    /// The box contains the target type.
    Typedef(Box<Type<I>>),
    /// An array.
    Array(Array<I>),
    /// An enum.
    ///
    /// The vector contains the values of the variants.
    Enum(Vec<i128>),
    /// An opaque type.
    ///
    /// This does not correspond to anything in C. It is useful if the layout of a nested
    /// type is already known and should not be recomputed. On all supported targets,
    /// substituting a type for the opaque type with the same layout leads to the same
    /// layout for the containing type.
    Opaque(I::OpaqueLayout),
}

impl<I: Layout> TypeVariant<I> {
    /// Returns the identical type variant with the [`Layout`] converted.
    pub fn into<J: Layout>(self) -> TypeVariant<J>
    where
        I::TypeLayout: Into<J::TypeLayout>,
        I::FieldLayout: Into<J::FieldLayout>,
        I::OpaqueLayout: Into<J::OpaqueLayout>,
    {
        match self {
            TypeVariant::Builtin(bi) => TypeVariant::Builtin(bi),
            TypeVariant::Record(rt) => TypeVariant::Record(Record {
                kind: rt.kind,
                fields: rt.fields.into_iter().map(|v| v.into()).collect(),
            }),
            TypeVariant::Typedef(td) => TypeVariant::Typedef(Box::new((*td).into())),
            TypeVariant::Array(at) => TypeVariant::Array(Array {
                element_type: Box::new((*at.element_type).into()),
                num_elements: at.num_elements,
            }),
            TypeVariant::Opaque(l) => TypeVariant::Opaque(l.into()),
            TypeVariant::Enum(v) => TypeVariant::Enum(v),
        }
    }
}

/// A record.
///
/// This corresponds to a struct or a union in C.
///
/// # Example
///
/// ```c
/// #pragma pack(8)
/// struct {
///     int i __attribute__((aligned(16)));
///     char :1;
/// };
/// ```
///
/// ```
/// # use repc_impl::layout::{Record, RecordKind, RecordField, Annotation, Type, TypeVariant, BuiltinType};
/// Type::<()> {
///     layout: (),
///     annotations: vec!(Annotation::PragmaPack(64)),
///     variant: TypeVariant::Record(Record::<()> {
///         kind: RecordKind::Struct,
///         fields: vec![
///             RecordField {
///                 layout: None,
///                 annotations: vec!(Annotation::Align(Some(128))),
///                 named: true,
///                 bit_width: None,
///                 ty: Type {
///                     layout: (),
///                     annotations: vec!(),
///                     variant: TypeVariant::Builtin(BuiltinType::Int),
///                 },
///             },
///             RecordField {
///                 layout: None,
///                 annotations: vec!(),
///                 named: false,
///                 bit_width: Some(1),
///                 ty: Type {
///                     layout: (),
///                     annotations: vec!(),
///                     variant: TypeVariant::Builtin(BuiltinType::Char),
///                 },
///             },
///         ],
///     }),
/// };
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Record<I: Layout> {
    /// The type of the record. Struct or union.
    pub kind: RecordKind,
    /// The fields of the record.
    pub fields: Vec<RecordField<I>>,
}

/// An array.
///
/// This corresponds to the type of `T var[N]` in C.
///
/// # Example
///
/// ```c
/// int i[1];
/// ```
///
/// ```
/// # use repc_impl::layout::{Type, TypeVariant, Array, BuiltinType};
/// Array::<()> {
///     element_type: Box::new(Type {
///         layout: (),
///         annotations: vec!(),
///         variant: TypeVariant::Builtin(BuiltinType::Int),
///     }),
///     num_elements: Some(1),
/// };
/// ```
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Array<I: Layout> {
    /// The type of elements of the array.
    pub element_type: Box<Type<I>>,
    /// The number of elements in the array.
    ///
    /// If this is `None`, it corresponds to a flexible array in C.
    pub num_elements: Option<u64>,
}

/// A field of a record.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RecordField<I: Layout> {
    /// The layout of the field.
    ///
    /// This is `None` for unnamed fields.
    pub layout: Option<I::FieldLayout>,
    /// The annotations on this field.
    pub annotations: Vec<Annotation>,
    /// Whether the field is named.
    ///
    /// An unnamed field in C is always a bit-field and is declared like `T : N` where
    /// `T` is the type of the field and `N` is the width of the bit-field.
    pub named: bool,
    /// If this is a bit-field, the width of the field.
    ///
    /// The field is recognized as a bit-field if and only if this is `Some`.
    pub bit_width: Option<u64>,
    /// The type of the field.
    pub ty: Type<I>,
}

impl<I: Layout> RecordField<I> {
    /// Returns the identical record field with the [`Layout`] converted.
    pub fn into<J: Layout>(self) -> RecordField<J>
    where
        I::TypeLayout: Into<J::TypeLayout>,
        I::FieldLayout: Into<J::FieldLayout>,
        I::OpaqueLayout: Into<J::OpaqueLayout>,
    {
        RecordField {
            layout: self.layout.map(|v| v.into()),
            annotations: self.annotations,
            named: self.named,
            bit_width: self.bit_width,
            ty: self.ty.into(),
        }
    }
}

/// The type of a record. Either a struct or a union.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum RecordKind {
    /// A struct.
    Struct,
    /// A union.
    Union,
}

/// A builtin type.
///
/// This includes both builtin Rust and C types. The Rust types will be treated like the
/// corresponding C types if possible. If the type does not exist on the target, the
/// results might not be meaningful.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum BuiltinType {
    /// `()`
    Unit,
    /// `bool`
    Bool,
    /// `u8`
    U8,
    /// `u16`
    U16,
    /// `u32`
    U32,
    /// `u64`
    U64,
    /// `u128`
    U128,
    /// `i8`
    I8,
    /// `i16`
    I16,
    /// `i32`
    I32,
    /// `i64`
    I64,
    /// `i128`
    I128,
    /// `c_char`
    Char,
    /// `c_schar`
    SignedChar,
    /// `c_uchar`
    UnsignedChar,
    /// `c_short`
    Short,
    /// `c_ushort`
    UnsignedShort,
    /// `c_int`
    Int,
    /// `c_uint`
    UnsignedInt,
    /// `c_long`
    Long,
    /// `c_ulong`
    UnsignedLong,
    /// `c_longlong`
    LongLong,
    /// `c_ulonglong`
    UnsignedLongLong,
    /// `f32`
    F32,
    /// `f64`
    F64,
    /// `c_float`
    Float,
    /// `c_double`
    Double,
    /// `*const T`,`*mut T`, `&T`, `&mut T` for sized `T`; `fn()`, `Option<fn()>`, etc.
    Pointer,
}