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(kind) => Some(types.float(kind)),
75 _ => None,
76 }
77}
78
79#[must_use]
81pub fn is_floating(types: &Types, id: TypeId) -> bool {
82 matches!(bare(types, id), TypeKind::Float(_) | TypeKind::Complex(_))
83}
84
85#[must_use]
87pub fn is_arithmetic(types: &Types, id: TypeId) -> bool {
88 is_integer(types, id) || is_floating(types, id)
89}
90
91#[must_use]
96pub fn is_real(types: &Types, id: TypeId) -> bool {
97 is_integer(types, id) || is_real_floating(types, id)
98}
99
100#[must_use]
102pub fn is_pointer(types: &Types, id: TypeId) -> bool {
103 matches!(bare(types, id), TypeKind::Pointer(_))
104}
105
106#[must_use]
108pub fn pointee(types: &Types, id: TypeId) -> Option<TypeId> {
109 match bare(types, id) {
110 TypeKind::Pointer(inner) => Some(inner),
111 _ => None,
112 }
113}
114
115#[must_use]
117pub fn is_array(types: &Types, id: TypeId) -> bool {
118 matches!(bare(types, id), TypeKind::Array { .. })
119}
120
121#[must_use]
123pub fn element(types: &Types, id: TypeId) -> Option<TypeId> {
124 match bare(types, id) {
125 TypeKind::Array { elem, .. } | TypeKind::Vector { elem, .. } => Some(elem),
126 _ => None,
127 }
128}
129
130#[must_use]
132pub fn is_function(types: &Types, id: TypeId) -> bool {
133 matches!(bare(types, id), TypeKind::Function(_))
134}
135
136#[must_use]
138pub fn is_record(types: &Types, id: TypeId) -> bool {
139 matches!(bare(types, id), TypeKind::Record(_))
140}
141
142#[must_use]
144pub fn is_vector(types: &Types, id: TypeId) -> bool {
145 matches!(bare(types, id), TypeKind::Vector { .. })
146}
147
148#[must_use]
150pub fn lanes(types: &Types, id: TypeId) -> Option<u32> {
151 match bare(types, id) {
152 TypeKind::Vector { len, .. } => Some(len),
153 _ => None,
154 }
155}
156
157#[must_use]
161pub fn is_atomic(types: &Types, id: TypeId) -> bool {
162 matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
163}
164
165#[must_use]
171pub fn is_scalar(types: &Types, id: TypeId) -> bool {
172 is_arithmetic(types, id) || is_pointer(types, id)
173}
174
175#[must_use]
180pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
181 match bare(types, id) {
182 TypeKind::Array { .. } => true,
183 TypeKind::Record(record) => {
184 matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
185 }
186 _ => false,
187 }
188}
189
190#[must_use]
195pub fn is_object(types: &Types, id: TypeId) -> bool {
196 !is_function(types, id)
197}
198
199#[must_use]
206pub fn is_complete(types: &Types, id: TypeId) -> bool {
207 match bare(types, id) {
208 TypeKind::Void => false,
209 TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
210 TypeKind::Array { elem, .. } => is_complete(types, elem),
211 TypeKind::Record(record) => types.record_info(record).layout.is_some(),
212 TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
213 _ => true,
214 }
215}
216
217#[must_use]
223pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
224 if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
225 return false;
226 }
227 match bare(types, id) {
228 TypeKind::Record(record) => {
229 types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
230 }
231 _ => true,
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use rucc_base::Interner;
238 use rucc_target::{TargetInfo, Triple};
239
240 use super::*;
241 use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
242 use crate::record::{FieldDecl, RecordOptions, layout_record};
243
244 #[test]
245 fn an_enumeration_is_an_integer_type() {
246 let mut types = Types::new();
247 let id = types.declare_enum(None);
248 let int = types.int(IntKind::Int);
249 types.complete_enum(id, int, false);
250 let enumeration = types.enumeration(id);
251
252 assert!(is_integer(&types, enumeration));
254 assert!(is_arithmetic(&types, enumeration));
255 assert!(is_scalar(&types, enumeration));
256 }
257
258 #[test]
259 fn atomic_is_in_whatever_category_it_wraps() {
260 let mut types = Types::new();
261 let int = types.int(IntKind::Int);
262 let atomic = types.atomic(int);
263
264 assert!(is_integer(&types, atomic));
265 assert!(is_scalar(&types, atomic));
266 assert!(is_atomic(&types, atomic));
267 assert!(!is_atomic(&types, int));
268 }
269
270 #[test]
271 fn a_typedef_answers_as_what_it_names() {
272 let mut types = Types::new();
273 let mut names = Interner::new();
274 let int = types.int(IntKind::Int);
275 let name = names.intern("size_t");
276 let alias = types.typedef(name, int);
277
278 assert!(is_integer(&types, alias));
279 assert!(types.is_sugar(alias));
280 }
281
282 #[test]
283 fn a_complex_type_is_arithmetic_and_is_not_real() {
284 let mut types = Types::new();
285 let complex = types.complex(FloatKind::Double);
286
287 assert!(is_arithmetic(&types, complex));
288 assert!(is_floating(&types, complex));
289 assert!(!is_real(&types, complex));
291 }
292
293 #[test]
294 fn the_corresponding_real_type_is_the_type_of_both_halves() {
295 let mut types = Types::new();
296 let complex = types.complex(FloatKind::Float);
297 let qualified = types.qualified(complex, Qualifiers::CONST);
298
299 assert_eq!(real_part(&types, complex), Some(types.float(FloatKind::Float)));
300 assert_eq!(real_part(&types, qualified), Some(types.float(FloatKind::Float)));
303 assert_eq!(real_part(&types, types.float(FloatKind::Float)), None);
305 assert_eq!(real_part(&types, types.int(IntKind::Int)), None);
306 }
307
308 #[test]
309 fn void_is_an_object_type_and_is_never_complete() {
310 let types = Types::new();
311 let void = types.void();
312
313 assert!(is_object(&types, void));
314 assert!(!is_complete(&types, void));
315 assert!(!is_scalar(&types, void));
316 }
317
318 #[test]
319 fn a_union_is_not_an_aggregate() {
320 let mut types = Types::new();
321 let union = types.declare_record(RecordKind::Union, None);
322 let union = types.record(union);
323 let int = types.int(IntKind::Int);
324 let array = types.array(int, ArrayLen::Fixed(2));
325
326 assert!(!is_aggregate(&types, union));
328 assert!(is_aggregate(&types, array));
329 }
330
331 #[test]
332 fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
333 let mut types = Types::new();
334 let record = types.declare_record(RecordKind::Struct, None);
335 let id = types.record(record);
336
337 assert!(is_object(&types, id));
338 assert!(!is_complete(&types, id));
339 assert!(!is_modifiable(&types, id));
340 }
341
342 #[test]
343 fn a_const_member_makes_the_whole_structure_unmodifiable() {
344 let mut types = Types::new();
345 let int = types.int(IntKind::Int);
346 let constant = types.qualified(int, Qualifiers::CONST);
347 let record = types.declare_record(RecordKind::Struct, None);
348 let target =
349 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
350 let laid_out = layout_record(
351 &types,
352 RecordKind::Struct,
353 &[FieldDecl::new(None, constant)],
354 &RecordOptions::default(),
355 &target,
356 )
357 .expect("a layout");
358 types.complete_record(record, laid_out);
359 let id = types.record(record);
360
361 assert!(!is_modifiable(&types, id));
363 }
364}