Skip to main content

rucc_types/
classify.rs

1//! What category a type is in, which is what almost every constraint in C is written over.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.1.
4//!
5//! The standard states its rules in terms of categories rather than types: an operand of `%`
6//! must have integer type, an operand of `!` must have scalar type, a member of a `struct` must
7//! have complete object type. Those categories are asked about constantly and they are exactly
8//! where a compiler drifts, because each of them has one or two members nobody remembers.
9//!
10//! The three that get forgotten:
11//!
12//! An enumeration is an integer type. `enum e x; x % 2` is legal C and a compiler that asks
13//! whether the kind is `Int` says it is not.
14//!
15//! `_Atomic(T)` is in whatever category `T` is in. It is a type here rather than a qualifier,
16//! which is the right way round for spelling it and the wrong way round for this question, so
17//! everything below looks through it. `_Atomic(int)` is an integer type.
18//!
19//! `void` is an object type and is never a complete one. Those are two different questions and
20//! collapsing them is how `sizeof (void)` ends up either accepted or rejected for the wrong
21//! reason, since it is a constraint violation that gcc accepts as an extension worth one byte.
22//!
23//! Every question here reads [`Types::canonical`], so a typedef name answers as what it names.
24
25use crate::kind::{ArrayLen, Qualifiers, TypeKind};
26use crate::types::{TypeId, Types};
27
28/// What a type is, once the sugar and `_Atomic` are off it.
29pub(crate) fn bare(types: &Types, id: TypeId) -> TypeKind {
30    match types.kind(types.canonical(id)) {
31        TypeKind::Atomic(inner) => types.kind(types.canonical(inner)),
32        other => other,
33    }
34}
35
36/// `void`.
37#[must_use]
38pub fn is_void(types: &Types, id: TypeId) -> bool {
39    matches!(bare(types, id), TypeKind::Void)
40}
41
42/// An integer type, 6.2.5p17.
43///
44/// `bool`, the standard and extended integer types, `_BitInt`, and every enumeration. The last
45/// is the one that gets forgotten, and forgetting it rejects `enum e x; x % 2`.
46#[must_use]
47pub fn is_integer(types: &Types, id: TypeId) -> bool {
48    matches!(
49        bare(types, id),
50        TypeKind::Bool | TypeKind::Int(_) | TypeKind::BitInt { .. } | TypeKind::Enum(_)
51    )
52}
53
54/// A real floating type: `float`, `double`, `long double` and the extended ones.
55#[must_use]
56pub fn is_real_floating(types: &Types, id: TypeId) -> bool {
57    matches!(bare(types, id), TypeKind::Float(_))
58}
59
60/// A complex type, `_Complex T`.
61#[must_use]
62pub fn is_complex(types: &Types, id: TypeId) -> bool {
63    matches!(bare(types, id), TypeKind::Complex(_))
64}
65
66/// The corresponding real type of a complex one, 6.2.5p14, and [`None`] for every other type.
67///
68/// This is the type both halves of the object have, so it is what a walk over one asks for. It
69/// is deliberately not [`element`]: an array and a vector are a count of elements and a complex
70/// type is two named halves, and a caller that wanted one of those does not want the other.
71#[must_use]
72pub fn real_part(types: &Types, id: TypeId) -> Option<TypeId> {
73    match bare(types, id) {
74        TypeKind::Complex(part) => Some(part),
75        _ => None,
76    }
77}
78
79/// A floating type, which is the real ones and the complex ones together.
80///
81/// `_Complex int` is not one. That type is gcc's rather than C's and its halves are integers,
82/// so every rule written over the floating types has nothing to say about it, and the question
83/// asked here is what those rules ask.
84#[must_use]
85pub fn is_floating(types: &Types, id: TypeId) -> bool {
86    match bare(types, id) {
87        TypeKind::Float(_) => true,
88        TypeKind::Complex(part) => is_real_floating(types, part),
89        _ => false,
90    }
91}
92
93/// An arithmetic type, 6.2.5p18: the integer types and the floating types.
94///
95/// `_Complex int` is here too, although C's list does not have it, because gcc's extension is
96/// an arithmetic type in every way the rest of the compiler asks about: it is added, compared,
97/// converted and initialized like the ones C wrote down.
98#[must_use]
99pub fn is_arithmetic(types: &Types, id: TypeId) -> bool {
100    is_integer(types, id) || is_floating(types, id) || is_complex(types, id)
101}
102
103/// A real type, 6.2.5p17: the integer types and the real floating types.
104///
105/// Not the same question as [`is_arithmetic`]. `<` takes real operands, so comparing two
106/// `_Complex double` values is a constraint violation while adding them is not.
107#[must_use]
108pub fn is_real(types: &Types, id: TypeId) -> bool {
109    is_integer(types, id) || is_real_floating(types, id)
110}
111
112/// A pointer type.
113#[must_use]
114pub fn is_pointer(types: &Types, id: TypeId) -> bool {
115    matches!(bare(types, id), TypeKind::Pointer(_))
116}
117
118/// What a pointer points to, or [`None`] where it is not a pointer.
119#[must_use]
120pub fn pointee(types: &Types, id: TypeId) -> Option<TypeId> {
121    match bare(types, id) {
122        TypeKind::Pointer(inner) => Some(inner),
123        _ => None,
124    }
125}
126
127/// The same, with whatever the pointee was written as still on it.
128///
129/// [`pointee`] answers through [`Types::canonical`], which resolves every typedef in the whole
130/// type and not only the one on the pointer, so a `u1 *` where `u1` is a typedef comes back as
131/// what `u1` stands for. That is the right answer for every question about what kind of thing is
132/// being pointed at and the wrong one for the two questions a typedef can change the answer to:
133/// how aligned an object of it is, which `__attribute__((aligned))` on a typedef sets rather than
134/// raises, and how a diagnostic spells the type.
135///
136/// So this resolves the sugar on the pointer and stops there. `*p` has the type the pointee was
137/// declared with, which is what makes `*(const unalign32 *)p` a one byte aligned read of four
138/// bytes: zlib, zstd and every other library that reads an unaligned word writes exactly that,
139/// and without this the read is a four byte aligned one and the safety monitor refuses it.
140#[must_use]
141pub fn pointee_as_written(types: &Types, id: TypeId) -> Option<TypeId> {
142    let mut id = id;
143    loop {
144        match types.kind(id) {
145            TypeKind::Pointer(inner) => return Some(inner),
146            // The two shapes that can sit over a pointer without being one. An `_Atomic` pointer
147            // is a pointer to whatever it was written over, and a typedef stands for what it was
148            // declared as, which may be another typedef.
149            TypeKind::Typedef { underlying, .. } | TypeKind::Atomic(underlying) => id = underlying,
150            _ => return None,
151        }
152    }
153}
154
155/// An array type.
156#[must_use]
157pub fn is_array(types: &Types, id: TypeId) -> bool {
158    matches!(bare(types, id), TypeKind::Array { .. })
159}
160
161/// The element type of an array or a vector, or [`None`] where it is neither.
162#[must_use]
163pub fn element(types: &Types, id: TypeId) -> Option<TypeId> {
164    match bare(types, id) {
165        TypeKind::Array { elem, .. } | TypeKind::Vector { elem, .. } => Some(elem),
166        _ => None,
167    }
168}
169
170/// The element type of an array with whatever it was written as still on it, or [`None`] where
171/// it is not an array.
172///
173/// The same reasoning as [`pointee_as_written`]. The canonical array holds the canonical element,
174/// which has lost any alignment a typedef in it asked for, and mingw-w64's `jmp_buf` is an array of
175/// a sixteen byte struct that only a typedef aligns to sixteen. msvcrt's `setjmp` stores into it
176/// with `movdqa`, so the element as written is the one an array's alignment comes from.
177#[must_use]
178pub fn element_as_written(types: &Types, id: TypeId) -> Option<TypeId> {
179    let mut id = id;
180    loop {
181        match types.kind(id) {
182            TypeKind::Array { elem, .. } => return Some(elem),
183            TypeKind::Typedef { underlying, .. } => id = underlying,
184            _ => return None,
185        }
186    }
187}
188
189/// A function type.
190#[must_use]
191pub fn is_function(types: &Types, id: TypeId) -> bool {
192    matches!(bare(types, id), TypeKind::Function(_))
193}
194
195/// A `struct` or a `union`.
196#[must_use]
197pub fn is_record(types: &Types, id: TypeId) -> bool {
198    matches!(bare(types, id), TypeKind::Record(_))
199}
200
201/// A GNU vector type.
202#[must_use]
203pub fn is_vector(types: &Types, id: TypeId) -> bool {
204    matches!(bare(types, id), TypeKind::Vector { .. })
205}
206
207/// How many lanes a vector has, and [`None`] where the type is not one.
208#[must_use]
209pub fn lanes(types: &Types, id: TypeId) -> Option<u32> {
210    match bare(types, id) {
211        TypeKind::Vector { len, .. } => Some(len),
212        _ => None,
213    }
214}
215
216/// `_Atomic(T)`, whatever `T` is.
217///
218/// The one question that does not look through the wrapper, since it is asking about it.
219#[must_use]
220pub fn is_atomic(types: &Types, id: TypeId) -> bool {
221    matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
222}
223
224/// A scalar type, 6.2.5p21: the arithmetic types and the pointer types.
225///
226/// This is the category a condition, a `!`, and both operands of `&&` have to be in. A vector
227/// is deliberately not one, because GNU vectors are compared and negated elementwise and
228/// letting them through here would silently accept the scalar rules for them.
229#[must_use]
230pub fn is_scalar(types: &Types, id: TypeId) -> bool {
231    is_arithmetic(types, id) || is_pointer(types, id)
232}
233
234/// An aggregate type, 6.2.5p21: an array or a `struct`.
235///
236/// A `union` is not one. That is not a quirk of wording: it is why a `union` is initialized
237/// from its first member and an aggregate is initialized member by member.
238#[must_use]
239pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
240    match bare(types, id) {
241        TypeKind::Array { .. } => true,
242        TypeKind::Record(record) => {
243            matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
244        }
245        _ => false,
246    }
247}
248
249/// An object type, 6.2.5p1: anything that is not a function type.
250///
251/// `void` is one, and so is an incomplete `struct`. Whether the object can be made is
252/// [`is_complete`], and the two questions are asked in different places.
253#[must_use]
254pub fn is_object(types: &Types, id: TypeId) -> bool {
255    !is_function(types, id)
256}
257
258/// A complete type: one whose size is known, so an object of it can exist.
259///
260/// `void` is never complete. An array is complete when its length is known, which includes a
261/// variable length array, since the length is known when the declaration is reached even though
262/// it is not known here. A `struct`, a `union` or an `enum` is complete once its definition has
263/// been seen, which is a property of the declaration and not of the type expression.
264#[must_use]
265pub fn is_complete(types: &Types, id: TypeId) -> bool {
266    match bare(types, id) {
267        TypeKind::Void => false,
268        TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
269        TypeKind::Array { elem, .. } => is_complete(types, elem),
270        TypeKind::Record(record) => types.record_info(record).layout.is_some(),
271        TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
272        _ => true,
273    }
274}
275
276/// Whether a value of this type may be modified, 6.3.2.1p1.
277///
278/// An array is not modifiable, a `const` object is not, an incomplete type is not, and a
279/// `struct` with a `const` member anywhere inside it is not, which is the part that takes a
280/// walk rather than a look and the part a compiler forgets.
281#[must_use]
282pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
283    if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
284        return false;
285    }
286    match bare(types, id) {
287        TypeKind::Record(record) => {
288            types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
289        }
290        _ => true,
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use rucc_base::Interner;
297    use rucc_target::{TargetInfo, Triple};
298
299    use super::*;
300    use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
301    use crate::record::{FieldDecl, RecordOptions, layout_record};
302
303    #[test]
304    fn an_enumeration_is_an_integer_type() {
305        let mut types = Types::new();
306        let id = types.declare_enum(None);
307        let int = types.int(IntKind::Int);
308        types.complete_enum(id, int, false);
309        let enumeration = types.enumeration(id);
310
311        // The rule that gets forgotten, and forgetting it rejects `enum e x; x % 2`.
312        assert!(is_integer(&types, enumeration));
313        assert!(is_arithmetic(&types, enumeration));
314        assert!(is_scalar(&types, enumeration));
315    }
316
317    #[test]
318    fn atomic_is_in_whatever_category_it_wraps() {
319        let mut types = Types::new();
320        let int = types.int(IntKind::Int);
321        let atomic = types.atomic(int);
322
323        assert!(is_integer(&types, atomic));
324        assert!(is_scalar(&types, atomic));
325        assert!(is_atomic(&types, atomic));
326        assert!(!is_atomic(&types, int));
327    }
328
329    #[test]
330    fn a_typedef_answers_as_what_it_names() {
331        let mut types = Types::new();
332        let mut names = Interner::new();
333        let int = types.int(IntKind::Int);
334        let name = names.intern("size_t");
335        let alias = types.typedef(name, int);
336
337        assert!(is_integer(&types, alias));
338        assert!(types.is_sugar(alias));
339    }
340
341    #[test]
342    fn a_complex_type_is_arithmetic_and_is_not_real() {
343        let mut types = Types::new();
344        let complex = types.complex_float(FloatKind::Double);
345
346        assert!(is_arithmetic(&types, complex));
347        assert!(is_floating(&types, complex));
348        // Which is why `<` on two of them is a constraint violation and `+` is not.
349        assert!(!is_real(&types, complex));
350    }
351
352    #[test]
353    fn the_corresponding_real_type_is_the_type_of_both_halves() {
354        let mut types = Types::new();
355        let complex = types.complex_float(FloatKind::Float);
356        let qualified = types.qualified(complex, Qualifiers::CONST);
357
358        assert_eq!(real_part(&types, complex), Some(types.float(FloatKind::Float)));
359        // Through the qualifiers, since an access to a half of a `const _Complex float` is still
360        // an access to a `float`.
361        assert_eq!(real_part(&types, qualified), Some(types.float(FloatKind::Float)));
362        // And not an answer for the types that have elements rather than halves.
363        assert_eq!(real_part(&types, types.float(FloatKind::Float)), None);
364        assert_eq!(real_part(&types, types.int(IntKind::Int)), None);
365    }
366
367    #[test]
368    fn void_is_an_object_type_and_is_never_complete() {
369        let types = Types::new();
370        let void = types.void();
371
372        assert!(is_object(&types, void));
373        assert!(!is_complete(&types, void));
374        assert!(!is_scalar(&types, void));
375    }
376
377    #[test]
378    fn a_union_is_not_an_aggregate() {
379        let mut types = Types::new();
380        let union = types.declare_record(RecordKind::Union, None);
381        let union = types.record(union);
382        let int = types.int(IntKind::Int);
383        let array = types.array(int, ArrayLen::Fixed(2));
384
385        // Not a quirk of wording: it is why a union is initialized from its first member.
386        assert!(!is_aggregate(&types, union));
387        assert!(is_aggregate(&types, array));
388    }
389
390    #[test]
391    fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
392        let mut types = Types::new();
393        let record = types.declare_record(RecordKind::Struct, None);
394        let id = types.record(record);
395
396        assert!(is_object(&types, id));
397        assert!(!is_complete(&types, id));
398        assert!(!is_modifiable(&types, id));
399    }
400
401    #[test]
402    fn a_const_member_makes_the_whole_structure_unmodifiable() {
403        let mut types = Types::new();
404        let int = types.int(IntKind::Int);
405        let constant = types.qualified(int, Qualifiers::CONST);
406        let record = types.declare_record(RecordKind::Struct, None);
407        let target =
408            TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
409        let laid_out = layout_record(
410            &types,
411            RecordKind::Struct,
412            &[FieldDecl::new(None, constant)],
413            &RecordOptions::default(),
414            &target,
415        )
416        .expect("a layout");
417        types.complete_record(record, laid_out);
418        let id = types.record(record);
419
420        // The part that takes a walk rather than a look, and the part a compiler forgets.
421        assert!(!is_modifiable(&types, id));
422    }
423}