rucc-types 0.2.17

The C type system, interned, and layout computation.
Documentation
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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
//! The type table: interning, canonicalisation, and the nominal declarations.
//!
//! Design: `spec/07-types-and-semantics.md` section 7.1.
//!
//! There is one [`Types`] per translation unit and every [`TypeId`] belongs to it. Interning
//! is what makes type identity an integer comparison, which is the single most frequent
//! question the compiler asks, and it is also what makes the canonical form free to look up:
//! each entry stores the id of its own canonical type, so stripping a stack of typedefs is one
//! array read rather than a walk.

use std::collections::HashMap;

use rucc_base::{Idx, Symbol};

use crate::kind::{
    ArrayLen, EnumId, FloatKind, FunctionId, FunctionType, IntKind, Qualifiers, RecordId,
    RecordKind, Type, TypeKind,
};
use crate::layout::Layout;
use crate::record::{Field, RecordLayout};

/// The identity of a type.
///
/// Four bytes, `Copy`, and equal exactly when the two types are the same type. Ids from two
/// different [`Types`] tables are not comparable, which is not a restriction in practice
/// because there is one table per translation unit.
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TypeId(Idx<Entry>);

impl std::fmt::Debug for TypeId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "TypeId#{}", self.0.raw())
    }
}

/// One row of the table.
///
/// The canonical id is stored rather than computed because almost every read of a type wants
/// it, and computing it means walking a chain whose length is however many typedefs the header
/// author felt like writing.
#[derive(Debug, Clone, Copy)]
struct Entry {
    ty: Type,
    canonical: TypeId,
}

/// What is known about one `struct` or `union` declaration.
#[derive(Debug, Clone)]
pub struct RecordInfo {
    /// Whether it is a `struct` or a `union`.
    pub kind: RecordKind,
    /// The tag, absent for an anonymous one.
    pub tag: Option<Symbol>,
    /// The layout, absent until the members have been seen and laid out.
    ///
    /// This is also what says whether the type is complete. A record is incomplete from the
    /// point its tag is first mentioned until its closing brace, and code in between may
    /// declare pointers to it and nothing else.
    pub layout: Option<Layout>,
    /// The members, placed, and empty until the record is complete.
    ///
    /// One entry per member the program wrote, in that order, so a caller that kept the
    /// declarations can index the two together.
    pub fields: Vec<Field>,
}

/// What is known about one `enum` declaration.
#[derive(Debug, Clone)]
pub struct EnumInfo {
    /// The tag, absent for an anonymous one.
    pub tag: Option<Symbol>,
    /// The type the enumerators are represented in, absent until it is decided.
    ///
    /// C23 lets the program write it, and before that it is chosen once every enumerator has
    /// been seen. Either way it is a fact about the declaration rather than about the type
    /// system, so it is recorded here and not derived twice.
    pub underlying: Option<TypeId>,
    /// Whether the underlying type was written by the program rather than chosen.
    ///
    /// It changes the answer to what an enumerator's own type is, and it decides whether an
    /// enumerator that does not fit is an error or a reason to widen.
    pub fixed: bool,
}

/// Every type in one translation unit.
#[derive(Debug)]
pub struct Types {
    entries: Vec<Entry>,
    map: HashMap<Type, TypeId>,
    functions: Vec<FunctionType>,
    function_map: HashMap<FunctionType, FunctionId>,
    records: Vec<RecordInfo>,
    enums: Vec<EnumInfo>,
    void: TypeId,
    boolean: TypeId,
    ints: [TypeId; 13],
    floats: [TypeId; 9],
}

impl Default for Types {
    fn default() -> Types {
        Types::new()
    }
}

impl Types {
    /// A table holding the basic types and nothing else.
    ///
    /// The basic types are interned here rather than on first use so that asking for `int` is
    /// an array read. They are the ones asked for by far the most often, because every
    /// integer promotion produces one.
    #[must_use]
    pub fn new() -> Types {
        let mut types = Types {
            entries: Vec::new(),
            map: HashMap::new(),
            functions: Vec::new(),
            function_map: HashMap::new(),
            records: Vec::new(),
            enums: Vec::new(),
            // Fixed up immediately below. There is no id to put here before the table exists,
            // and an `Option` on each of them would be paid for on every read for the sake of
            // four lines of construction.
            void: TypeId(Idx::new(0)),
            boolean: TypeId(Idx::new(0)),
            ints: [TypeId(Idx::new(0)); 13],
            floats: [TypeId(Idx::new(0)); 9],
        };
        types.void = types.intern(Type::new(TypeKind::Void));
        types.boolean = types.intern(Type::new(TypeKind::Bool));
        for kind in IntKind::ALL {
            types.ints[kind.index()] = types.intern(Type::new(TypeKind::Int(kind)));
        }
        for kind in FloatKind::ALL {
            types.floats[kind.index()] = types.intern(Type::new(TypeKind::Float(kind)));
        }
        types
    }

    /// How many distinct types there are.
    #[must_use]
    pub fn len(&self) -> usize {
        self.entries.len()
    }

    /// Whether the table is empty, which it never is once [`Types::new`] has run.
    #[must_use]
    pub fn is_empty(&self) -> bool {
        self.entries.is_empty()
    }

    /// The type `id` stands for, with its qualifiers.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn get(&self, id: TypeId) -> Type {
        self.entries[id.0.index()].ty
    }

    /// What `id` is, ignoring its qualifiers.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn kind(&self, id: TypeId) -> TypeKind {
        self.get(id).kind
    }

    /// What `id` is qualified with.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn quals(&self, id: TypeId) -> Qualifiers {
        self.get(id).quals
    }

    /// The canonical form of `id`, with every typedef resolved at every depth.
    ///
    /// This is what every semantic rule reads. `id` itself is what every diagnostic prints.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn canonical(&self, id: TypeId) -> TypeId {
        self.entries[id.0.index()].canonical
    }

    /// Whether `id` is written with a typedef name somewhere inside it.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn is_sugar(&self, id: TypeId) -> bool {
        self.canonical(id) != id
    }

    /// `void`.
    #[must_use]
    pub fn void(&self) -> TypeId {
        self.void
    }

    /// `bool`, which is `_Bool` in the older spellings.
    ///
    /// Named this way because `bool` is a Rust keyword and `r#bool` at every call site would
    /// be a worse trade than one unusual name here.
    #[must_use]
    pub fn boolean(&self) -> TypeId {
        self.boolean
    }

    /// One of the standard integer types.
    #[must_use]
    pub fn int(&self, kind: IntKind) -> TypeId {
        self.ints[kind.index()]
    }

    /// One of the real floating types.
    #[must_use]
    pub fn float(&self, kind: FloatKind) -> TypeId {
        self.floats[kind.index()]
    }

    /// `_Complex T`.
    pub fn complex(&mut self, kind: FloatKind) -> TypeId {
        self.intern(Type::new(TypeKind::Complex(kind)))
    }

    /// `_BitInt(width)`, signed or not.
    ///
    /// The width is not checked against the target's maximum here. That check belongs where
    /// there is a span to point at, and building the type anyway means the rest of the
    /// declaration still gets checked instead of collapsing into a cascade.
    pub fn bit_int(&mut self, signed: bool, width: u32) -> TypeId {
        self.intern(Type::new(TypeKind::BitInt { signed, width }))
    }

    /// A pointer to `pointee`.
    pub fn pointer(&mut self, pointee: TypeId) -> TypeId {
        self.intern(Type::new(TypeKind::Pointer(pointee)))
    }

    /// `_Atomic(inner)`.
    pub fn atomic(&mut self, inner: TypeId) -> TypeId {
        self.intern(Type::new(TypeKind::Atomic(inner)))
    }

    /// An array of `elem`.
    pub fn array(&mut self, elem: TypeId, len: ArrayLen) -> TypeId {
        self.intern(Type::new(TypeKind::Array { elem, len }))
    }

    /// A GNU vector of `len` elements of `elem`.
    pub fn vector(&mut self, elem: TypeId, len: u32) -> TypeId {
        self.intern(Type::new(TypeKind::Vector { elem, len }))
    }

    /// A function type, deduplicated by content.
    ///
    /// # Panics
    ///
    /// Panics past four billion distinct function types in one translation unit. The
    /// alternative to panicking is handing back an id that means a different type, so the
    /// limit is stated rather than worked around.
    pub fn function(&mut self, signature: FunctionType) -> TypeId {
        let id = match self.function_map.get(&signature) {
            Some(&id) => id,
            None => {
                let id = FunctionId(u32::try_from(self.functions.len()).expect("too many types"));
                self.functions.push(signature.clone());
                self.function_map.insert(signature, id);
                id
            }
        };
        self.intern(Type::new(TypeKind::Function(id)))
    }

    /// The signature behind a function type.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn signature(&self, id: FunctionId) -> &FunctionType {
        &self.functions[id.0 as usize]
    }

    /// Declares a `struct` or `union` that has been named but not yet laid out.
    ///
    /// Each call makes a new type even for the same tag, because a record type in C is its
    /// declaration. Redeclaring a tag in an inner scope makes a different type, and the two
    /// being distinct is what the scope rules mean.
    ///
    /// # Panics
    ///
    /// Panics past four billion record declarations in one translation unit.
    pub fn declare_record(&mut self, kind: RecordKind, tag: Option<Symbol>) -> RecordId {
        let id = RecordId(u32::try_from(self.records.len()).expect("too many types"));
        self.records.push(RecordInfo { kind, tag, layout: None, fields: Vec::new() });
        id
    }

    /// The type of a declared record.
    pub fn record(&mut self, id: RecordId) -> TypeId {
        self.intern(Type::new(TypeKind::Record(id)))
    }

    /// What is known about a declared record.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn record_info(&self, id: RecordId) -> &RecordInfo {
        &self.records[id.0 as usize]
    }

    /// Completes a record by recording what [`layout_record`](crate::layout_record) produced.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    pub fn complete_record(&mut self, id: RecordId, laid_out: RecordLayout) {
        let info = &mut self.records[id.0 as usize];
        info.layout = Some(laid_out.layout);
        info.fields = laid_out.fields;
    }

    /// The member of a record with the given name.
    ///
    /// Direct members only. Reaching into an anonymous member is a name lookup with a path to
    /// build rather than a search, so it belongs to whoever is resolving the expression.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn field(&self, id: RecordId, name: Symbol) -> Option<&Field> {
        self.records[id.0 as usize].fields.iter().find(|field| field.name == Some(name))
    }

    /// Declares an `enum` whose underlying type is not decided yet.
    ///
    /// # Panics
    ///
    /// Panics past four billion enumeration declarations in one translation unit.
    pub fn declare_enum(&mut self, tag: Option<Symbol>) -> EnumId {
        let id = EnumId(u32::try_from(self.enums.len()).expect("too many types"));
        self.enums.push(EnumInfo { tag, underlying: None, fixed: false });
        id
    }

    /// The type of a declared enumeration.
    pub fn enumeration(&mut self, id: EnumId) -> TypeId {
        self.intern(Type::new(TypeKind::Enum(id)))
    }

    /// What is known about a declared enumeration.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    #[must_use]
    pub fn enum_info(&self, id: EnumId) -> &EnumInfo {
        &self.enums[id.0 as usize]
    }

    /// Records what an enumeration is represented in, and whether the program said so.
    ///
    /// # Panics
    ///
    /// Panics if `id` came from a different table.
    pub fn complete_enum(&mut self, id: EnumId, underlying: TypeId, fixed: bool) {
        let info = &mut self.enums[id.0 as usize];
        info.underlying = Some(underlying);
        info.fixed = fixed;
    }

    /// A typedef name standing for `underlying`.
    pub fn typedef(&mut self, name: Symbol, underlying: TypeId) -> TypeId {
        self.intern(Type::new(TypeKind::Typedef { name, underlying }))
    }

    /// `id` with `quals` added to whatever it already carries.
    ///
    /// Qualifying an array qualifies its element type and leaves the array itself unqualified,
    /// which is 6.7.3p10 and is not a shortcut. An array type has no qualifiers of its own,
    /// and if it did then `const` on an array parameter would mean nothing at all.
    pub fn qualified(&mut self, id: TypeId, quals: Qualifiers) -> TypeId {
        if quals.is_none() {
            return id;
        }
        let ty = self.get(id);
        if let TypeKind::Array { elem, len } = ty.kind {
            let elem = self.qualified(elem, quals);
            return self.intern(Type { kind: TypeKind::Array { elem, len }, quals: ty.quals });
        }
        self.intern(Type { kind: ty.kind, quals: ty.quals.with(quals) })
    }

    /// `id` with every qualifier removed from its outermost node.
    ///
    /// Only the outermost, because that is what the standard means by the unqualified version
    /// of a type. The pointee of a `const char *` stays `const`.
    pub fn unqualified(&mut self, id: TypeId) -> TypeId {
        let ty = self.get(id);
        if ty.quals.is_none() {
            return id;
        }
        self.intern(Type::new(ty.kind))
    }

    /// The id for `ty`, making one if this is the first time it has been asked for.
    fn intern(&mut self, ty: Type) -> TypeId {
        if let Some(&id) = self.map.get(&ty) {
            return id;
        }
        // Canonicalising can intern other types, which means `self.entries` may have grown by
        // the time this returns and the id below has to be taken afterwards. It cannot have
        // interned `ty` itself, because a canonical type differs from the sugar it came from,
        // but the second lookup is one hash of a cold path against a duplicate entry that
        // would quietly break the promise that equal ids mean equal types.
        let canonical = self.canonicalise(&ty);
        if let Some(&id) = self.map.get(&ty) {
            return id;
        }
        let id = TypeId(Idx::from_usize(self.entries.len()));
        self.entries.push(Entry { ty, canonical: canonical.unwrap_or(id) });
        self.map.insert(ty, id);
        id
    }

    /// The canonical form of `ty`, or `None` when `ty` is already canonical.
    ///
    /// A typedef is not the only place sugar hides. `T *` is sugar when `T` is, and so is an
    /// array of one, and so is a function that returns one, so this rebuilds the type around
    /// whatever its parts canonicalise to rather than only looking at the outermost node.
    fn canonicalise(&mut self, ty: &Type) -> Option<TypeId> {
        match ty.kind {
            TypeKind::Typedef { underlying, .. } => {
                let base = self.canonical(underlying);
                Some(self.qualified(base, ty.quals))
            }
            TypeKind::Pointer(inner) => self.rebuild(ty, inner, TypeKind::Pointer),
            TypeKind::Atomic(inner) => self.rebuild(ty, inner, TypeKind::Atomic),
            TypeKind::Array { elem, len } => {
                self.rebuild(ty, elem, |elem| TypeKind::Array { elem, len })
            }
            TypeKind::Vector { elem, len } => {
                self.rebuild(ty, elem, |elem| TypeKind::Vector { elem, len })
            }
            TypeKind::Function(id) => self.canonicalise_function(ty, id),
            TypeKind::Void
            | TypeKind::Bool
            | TypeKind::Int(_)
            | TypeKind::Float(_)
            | TypeKind::Complex(_)
            | TypeKind::BitInt { .. }
            | TypeKind::Record(_)
            | TypeKind::Enum(_) => None,
        }
    }

    /// The canonical form of a type built out of one other type.
    fn rebuild(
        &mut self,
        ty: &Type,
        inner: TypeId,
        make: impl FnOnce(TypeId) -> TypeKind,
    ) -> Option<TypeId> {
        let canonical = self.canonical(inner);
        if canonical == inner {
            return None;
        }
        Some(self.intern(Type { kind: make(canonical), quals: ty.quals }))
    }

    /// The canonical form of a function type, which is sugar when any part of its signature is.
    fn canonicalise_function(&mut self, ty: &Type, id: FunctionId) -> Option<TypeId> {
        let signature = self.signature(id).clone();
        let ret = self.canonical(signature.ret);
        let params: Vec<TypeId> =
            signature.params.iter().map(|&param| self.canonical(param)).collect();
        if ret == signature.ret && params == signature.params {
            return None;
        }
        let canonical = FunctionType { ret, params, ..signature };
        let id = self.function(canonical);
        Some(self.qualified(id, ty.quals))
    }
}