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]
68pub fn is_floating(types: &Types, id: TypeId) -> bool {
69 matches!(bare(types, id), TypeKind::Float(_) | TypeKind::Complex(_))
70}
71
72#[must_use]
74pub fn is_arithmetic(types: &Types, id: TypeId) -> bool {
75 is_integer(types, id) || is_floating(types, id)
76}
77
78#[must_use]
83pub fn is_real(types: &Types, id: TypeId) -> bool {
84 is_integer(types, id) || is_real_floating(types, id)
85}
86
87#[must_use]
89pub fn is_pointer(types: &Types, id: TypeId) -> bool {
90 matches!(bare(types, id), TypeKind::Pointer(_))
91}
92
93#[must_use]
95pub fn pointee(types: &Types, id: TypeId) -> Option<TypeId> {
96 match bare(types, id) {
97 TypeKind::Pointer(inner) => Some(inner),
98 _ => None,
99 }
100}
101
102#[must_use]
104pub fn is_array(types: &Types, id: TypeId) -> bool {
105 matches!(bare(types, id), TypeKind::Array { .. })
106}
107
108#[must_use]
110pub fn element(types: &Types, id: TypeId) -> Option<TypeId> {
111 match bare(types, id) {
112 TypeKind::Array { elem, .. } | TypeKind::Vector { elem, .. } => Some(elem),
113 _ => None,
114 }
115}
116
117#[must_use]
119pub fn is_function(types: &Types, id: TypeId) -> bool {
120 matches!(bare(types, id), TypeKind::Function(_))
121}
122
123#[must_use]
125pub fn is_record(types: &Types, id: TypeId) -> bool {
126 matches!(bare(types, id), TypeKind::Record(_))
127}
128
129#[must_use]
131pub fn is_vector(types: &Types, id: TypeId) -> bool {
132 matches!(bare(types, id), TypeKind::Vector { .. })
133}
134
135#[must_use]
137pub fn lanes(types: &Types, id: TypeId) -> Option<u32> {
138 match bare(types, id) {
139 TypeKind::Vector { len, .. } => Some(len),
140 _ => None,
141 }
142}
143
144#[must_use]
148pub fn is_atomic(types: &Types, id: TypeId) -> bool {
149 matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
150}
151
152#[must_use]
158pub fn is_scalar(types: &Types, id: TypeId) -> bool {
159 is_arithmetic(types, id) || is_pointer(types, id)
160}
161
162#[must_use]
167pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
168 match bare(types, id) {
169 TypeKind::Array { .. } => true,
170 TypeKind::Record(record) => {
171 matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
172 }
173 _ => false,
174 }
175}
176
177#[must_use]
182pub fn is_object(types: &Types, id: TypeId) -> bool {
183 !is_function(types, id)
184}
185
186#[must_use]
193pub fn is_complete(types: &Types, id: TypeId) -> bool {
194 match bare(types, id) {
195 TypeKind::Void => false,
196 TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
197 TypeKind::Array { elem, .. } => is_complete(types, elem),
198 TypeKind::Record(record) => types.record_info(record).layout.is_some(),
199 TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
200 _ => true,
201 }
202}
203
204#[must_use]
210pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
211 if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
212 return false;
213 }
214 match bare(types, id) {
215 TypeKind::Record(record) => {
216 types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
217 }
218 _ => true,
219 }
220}
221
222#[cfg(test)]
223mod tests {
224 use rucc_base::Interner;
225 use rucc_target::{TargetInfo, Triple};
226
227 use super::*;
228 use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
229 use crate::record::{FieldDecl, RecordOptions, layout_record};
230
231 #[test]
232 fn an_enumeration_is_an_integer_type() {
233 let mut types = Types::new();
234 let id = types.declare_enum(None);
235 let int = types.int(IntKind::Int);
236 types.complete_enum(id, int, false);
237 let enumeration = types.enumeration(id);
238
239 assert!(is_integer(&types, enumeration));
241 assert!(is_arithmetic(&types, enumeration));
242 assert!(is_scalar(&types, enumeration));
243 }
244
245 #[test]
246 fn atomic_is_in_whatever_category_it_wraps() {
247 let mut types = Types::new();
248 let int = types.int(IntKind::Int);
249 let atomic = types.atomic(int);
250
251 assert!(is_integer(&types, atomic));
252 assert!(is_scalar(&types, atomic));
253 assert!(is_atomic(&types, atomic));
254 assert!(!is_atomic(&types, int));
255 }
256
257 #[test]
258 fn a_typedef_answers_as_what_it_names() {
259 let mut types = Types::new();
260 let mut names = Interner::new();
261 let int = types.int(IntKind::Int);
262 let name = names.intern("size_t");
263 let alias = types.typedef(name, int);
264
265 assert!(is_integer(&types, alias));
266 assert!(types.is_sugar(alias));
267 }
268
269 #[test]
270 fn a_complex_type_is_arithmetic_and_is_not_real() {
271 let mut types = Types::new();
272 let complex = types.complex(FloatKind::Double);
273
274 assert!(is_arithmetic(&types, complex));
275 assert!(is_floating(&types, complex));
276 assert!(!is_real(&types, complex));
278 }
279
280 #[test]
281 fn void_is_an_object_type_and_is_never_complete() {
282 let types = Types::new();
283 let void = types.void();
284
285 assert!(is_object(&types, void));
286 assert!(!is_complete(&types, void));
287 assert!(!is_scalar(&types, void));
288 }
289
290 #[test]
291 fn a_union_is_not_an_aggregate() {
292 let mut types = Types::new();
293 let union = types.declare_record(RecordKind::Union, None);
294 let union = types.record(union);
295 let int = types.int(IntKind::Int);
296 let array = types.array(int, ArrayLen::Fixed(2));
297
298 assert!(!is_aggregate(&types, union));
300 assert!(is_aggregate(&types, array));
301 }
302
303 #[test]
304 fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
305 let mut types = Types::new();
306 let record = types.declare_record(RecordKind::Struct, None);
307 let id = types.record(record);
308
309 assert!(is_object(&types, id));
310 assert!(!is_complete(&types, id));
311 assert!(!is_modifiable(&types, id));
312 }
313
314 #[test]
315 fn a_const_member_makes_the_whole_structure_unmodifiable() {
316 let mut types = Types::new();
317 let int = types.int(IntKind::Int);
318 let constant = types.qualified(int, Qualifiers::CONST);
319 let record = types.declare_record(RecordKind::Struct, None);
320 let target =
321 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
322 let laid_out = layout_record(
323 &types,
324 RecordKind::Struct,
325 &[FieldDecl::new(None, constant)],
326 &RecordOptions::default(),
327 &target,
328 )
329 .expect("a layout");
330 types.complete_record(record, laid_out);
331 let id = types.record(record);
332
333 assert!(!is_modifiable(&types, id));
335 }
336}