1use crate::kind::{ArrayLen, Qualifiers, TypeKind};
26use crate::types::{TypeId, Types};
27
28pub(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#[must_use]
38pub fn is_void(types: &Types, id: TypeId) -> bool {
39 matches!(bare(types, id), TypeKind::Void)
40}
41
42#[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#[must_use]
56pub fn is_real_floating(types: &Types, id: TypeId) -> bool {
57 matches!(bare(types, id), TypeKind::Float(_))
58}
59
60#[must_use]
62pub fn is_complex(types: &Types, id: TypeId) -> bool {
63 matches!(bare(types, id), TypeKind::Complex(_))
64}
65
66#[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#[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#[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#[must_use]
108pub fn is_real(types: &Types, id: TypeId) -> bool {
109 is_integer(types, id) || is_real_floating(types, id)
110}
111
112#[must_use]
114pub fn is_pointer(types: &Types, id: TypeId) -> bool {
115 matches!(bare(types, id), TypeKind::Pointer(_))
116}
117
118#[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#[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 TypeKind::Typedef { underlying, .. } | TypeKind::Atomic(underlying) => id = underlying,
150 _ => return None,
151 }
152 }
153}
154
155#[must_use]
157pub fn is_array(types: &Types, id: TypeId) -> bool {
158 matches!(bare(types, id), TypeKind::Array { .. })
159}
160
161#[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#[must_use]
172pub fn is_function(types: &Types, id: TypeId) -> bool {
173 matches!(bare(types, id), TypeKind::Function(_))
174}
175
176#[must_use]
178pub fn is_record(types: &Types, id: TypeId) -> bool {
179 matches!(bare(types, id), TypeKind::Record(_))
180}
181
182#[must_use]
184pub fn is_vector(types: &Types, id: TypeId) -> bool {
185 matches!(bare(types, id), TypeKind::Vector { .. })
186}
187
188#[must_use]
190pub fn lanes(types: &Types, id: TypeId) -> Option<u32> {
191 match bare(types, id) {
192 TypeKind::Vector { len, .. } => Some(len),
193 _ => None,
194 }
195}
196
197#[must_use]
201pub fn is_atomic(types: &Types, id: TypeId) -> bool {
202 matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
203}
204
205#[must_use]
211pub fn is_scalar(types: &Types, id: TypeId) -> bool {
212 is_arithmetic(types, id) || is_pointer(types, id)
213}
214
215#[must_use]
220pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
221 match bare(types, id) {
222 TypeKind::Array { .. } => true,
223 TypeKind::Record(record) => {
224 matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
225 }
226 _ => false,
227 }
228}
229
230#[must_use]
235pub fn is_object(types: &Types, id: TypeId) -> bool {
236 !is_function(types, id)
237}
238
239#[must_use]
246pub fn is_complete(types: &Types, id: TypeId) -> bool {
247 match bare(types, id) {
248 TypeKind::Void => false,
249 TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
250 TypeKind::Array { elem, .. } => is_complete(types, elem),
251 TypeKind::Record(record) => types.record_info(record).layout.is_some(),
252 TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
253 _ => true,
254 }
255}
256
257#[must_use]
263pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
264 if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
265 return false;
266 }
267 match bare(types, id) {
268 TypeKind::Record(record) => {
269 types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
270 }
271 _ => true,
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use rucc_base::Interner;
278 use rucc_target::{TargetInfo, Triple};
279
280 use super::*;
281 use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
282 use crate::record::{FieldDecl, RecordOptions, layout_record};
283
284 #[test]
285 fn an_enumeration_is_an_integer_type() {
286 let mut types = Types::new();
287 let id = types.declare_enum(None);
288 let int = types.int(IntKind::Int);
289 types.complete_enum(id, int, false);
290 let enumeration = types.enumeration(id);
291
292 assert!(is_integer(&types, enumeration));
294 assert!(is_arithmetic(&types, enumeration));
295 assert!(is_scalar(&types, enumeration));
296 }
297
298 #[test]
299 fn atomic_is_in_whatever_category_it_wraps() {
300 let mut types = Types::new();
301 let int = types.int(IntKind::Int);
302 let atomic = types.atomic(int);
303
304 assert!(is_integer(&types, atomic));
305 assert!(is_scalar(&types, atomic));
306 assert!(is_atomic(&types, atomic));
307 assert!(!is_atomic(&types, int));
308 }
309
310 #[test]
311 fn a_typedef_answers_as_what_it_names() {
312 let mut types = Types::new();
313 let mut names = Interner::new();
314 let int = types.int(IntKind::Int);
315 let name = names.intern("size_t");
316 let alias = types.typedef(name, int);
317
318 assert!(is_integer(&types, alias));
319 assert!(types.is_sugar(alias));
320 }
321
322 #[test]
323 fn a_complex_type_is_arithmetic_and_is_not_real() {
324 let mut types = Types::new();
325 let complex = types.complex_float(FloatKind::Double);
326
327 assert!(is_arithmetic(&types, complex));
328 assert!(is_floating(&types, complex));
329 assert!(!is_real(&types, complex));
331 }
332
333 #[test]
334 fn the_corresponding_real_type_is_the_type_of_both_halves() {
335 let mut types = Types::new();
336 let complex = types.complex_float(FloatKind::Float);
337 let qualified = types.qualified(complex, Qualifiers::CONST);
338
339 assert_eq!(real_part(&types, complex), Some(types.float(FloatKind::Float)));
340 assert_eq!(real_part(&types, qualified), Some(types.float(FloatKind::Float)));
343 assert_eq!(real_part(&types, types.float(FloatKind::Float)), None);
345 assert_eq!(real_part(&types, types.int(IntKind::Int)), None);
346 }
347
348 #[test]
349 fn void_is_an_object_type_and_is_never_complete() {
350 let types = Types::new();
351 let void = types.void();
352
353 assert!(is_object(&types, void));
354 assert!(!is_complete(&types, void));
355 assert!(!is_scalar(&types, void));
356 }
357
358 #[test]
359 fn a_union_is_not_an_aggregate() {
360 let mut types = Types::new();
361 let union = types.declare_record(RecordKind::Union, None);
362 let union = types.record(union);
363 let int = types.int(IntKind::Int);
364 let array = types.array(int, ArrayLen::Fixed(2));
365
366 assert!(!is_aggregate(&types, union));
368 assert!(is_aggregate(&types, array));
369 }
370
371 #[test]
372 fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
373 let mut types = Types::new();
374 let record = types.declare_record(RecordKind::Struct, None);
375 let id = types.record(record);
376
377 assert!(is_object(&types, id));
378 assert!(!is_complete(&types, id));
379 assert!(!is_modifiable(&types, id));
380 }
381
382 #[test]
383 fn a_const_member_makes_the_whole_structure_unmodifiable() {
384 let mut types = Types::new();
385 let int = types.int(IntKind::Int);
386 let constant = types.qualified(int, Qualifiers::CONST);
387 let record = types.declare_record(RecordKind::Struct, None);
388 let target =
389 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
390 let laid_out = layout_record(
391 &types,
392 RecordKind::Struct,
393 &[FieldDecl::new(None, constant)],
394 &RecordOptions::default(),
395 &target,
396 )
397 .expect("a layout");
398 types.complete_record(record, laid_out);
399 let id = types.record(record);
400
401 assert!(!is_modifiable(&types, id));
403 }
404}