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]
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#[must_use]
191pub fn is_function(types: &Types, id: TypeId) -> bool {
192 matches!(bare(types, id), TypeKind::Function(_))
193}
194
195#[must_use]
197pub fn is_record(types: &Types, id: TypeId) -> bool {
198 matches!(bare(types, id), TypeKind::Record(_))
199}
200
201#[must_use]
203pub fn is_vector(types: &Types, id: TypeId) -> bool {
204 matches!(bare(types, id), TypeKind::Vector { .. })
205}
206
207#[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#[must_use]
220pub fn is_atomic(types: &Types, id: TypeId) -> bool {
221 matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
222}
223
224#[must_use]
230pub fn is_scalar(types: &Types, id: TypeId) -> bool {
231 is_arithmetic(types, id) || is_pointer(types, id)
232}
233
234#[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#[must_use]
254pub fn is_object(types: &Types, id: TypeId) -> bool {
255 !is_function(types, id)
256}
257
258#[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#[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 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 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 assert_eq!(real_part(&types, qualified), Some(types.float(FloatKind::Float)));
362 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 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 assert!(!is_modifiable(&types, id));
422 }
423}