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]
139pub fn is_atomic(types: &Types, id: TypeId) -> bool {
140 matches!(types.kind(types.canonical(id)), TypeKind::Atomic(_))
141}
142
143#[must_use]
149pub fn is_scalar(types: &Types, id: TypeId) -> bool {
150 is_arithmetic(types, id) || is_pointer(types, id)
151}
152
153#[must_use]
158pub fn is_aggregate(types: &Types, id: TypeId) -> bool {
159 match bare(types, id) {
160 TypeKind::Array { .. } => true,
161 TypeKind::Record(record) => {
162 matches!(types.record_info(record).kind, crate::kind::RecordKind::Struct)
163 }
164 _ => false,
165 }
166}
167
168#[must_use]
173pub fn is_object(types: &Types, id: TypeId) -> bool {
174 !is_function(types, id)
175}
176
177#[must_use]
184pub fn is_complete(types: &Types, id: TypeId) -> bool {
185 match bare(types, id) {
186 TypeKind::Void => false,
187 TypeKind::Array { len: ArrayLen::Unknown, .. } => false,
188 TypeKind::Array { elem, .. } => is_complete(types, elem),
189 TypeKind::Record(record) => types.record_info(record).layout.is_some(),
190 TypeKind::Enum(id) => types.enum_info(id).underlying.is_some(),
191 _ => true,
192 }
193}
194
195#[must_use]
201pub fn is_modifiable(types: &Types, id: TypeId) -> bool {
202 if types.quals(id).has(Qualifiers::CONST) || is_array(types, id) || !is_complete(types, id) {
203 return false;
204 }
205 match bare(types, id) {
206 TypeKind::Record(record) => {
207 types.record_info(record).fields.iter().all(|field| is_modifiable(types, field.ty))
208 }
209 _ => true,
210 }
211}
212
213#[cfg(test)]
214mod tests {
215 use rucc_base::Interner;
216 use rucc_target::{TargetInfo, Triple};
217
218 use super::*;
219 use crate::kind::{ArrayLen, FloatKind, IntKind, RecordKind};
220 use crate::record::{FieldDecl, RecordOptions, layout_record};
221
222 #[test]
223 fn an_enumeration_is_an_integer_type() {
224 let mut types = Types::new();
225 let id = types.declare_enum(None);
226 let int = types.int(IntKind::Int);
227 types.complete_enum(id, int, false);
228 let enumeration = types.enumeration(id);
229
230 assert!(is_integer(&types, enumeration));
232 assert!(is_arithmetic(&types, enumeration));
233 assert!(is_scalar(&types, enumeration));
234 }
235
236 #[test]
237 fn atomic_is_in_whatever_category_it_wraps() {
238 let mut types = Types::new();
239 let int = types.int(IntKind::Int);
240 let atomic = types.atomic(int);
241
242 assert!(is_integer(&types, atomic));
243 assert!(is_scalar(&types, atomic));
244 assert!(is_atomic(&types, atomic));
245 assert!(!is_atomic(&types, int));
246 }
247
248 #[test]
249 fn a_typedef_answers_as_what_it_names() {
250 let mut types = Types::new();
251 let mut names = Interner::new();
252 let int = types.int(IntKind::Int);
253 let name = names.intern("size_t");
254 let alias = types.typedef(name, int);
255
256 assert!(is_integer(&types, alias));
257 assert!(types.is_sugar(alias));
258 }
259
260 #[test]
261 fn a_complex_type_is_arithmetic_and_is_not_real() {
262 let mut types = Types::new();
263 let complex = types.complex(FloatKind::Double);
264
265 assert!(is_arithmetic(&types, complex));
266 assert!(is_floating(&types, complex));
267 assert!(!is_real(&types, complex));
269 }
270
271 #[test]
272 fn void_is_an_object_type_and_is_never_complete() {
273 let types = Types::new();
274 let void = types.void();
275
276 assert!(is_object(&types, void));
277 assert!(!is_complete(&types, void));
278 assert!(!is_scalar(&types, void));
279 }
280
281 #[test]
282 fn a_union_is_not_an_aggregate() {
283 let mut types = Types::new();
284 let union = types.declare_record(RecordKind::Union, None);
285 let union = types.record(union);
286 let int = types.int(IntKind::Int);
287 let array = types.array(int, ArrayLen::Fixed(2));
288
289 assert!(!is_aggregate(&types, union));
291 assert!(is_aggregate(&types, array));
292 }
293
294 #[test]
295 fn an_incomplete_record_is_an_object_type_that_cannot_be_made() {
296 let mut types = Types::new();
297 let record = types.declare_record(RecordKind::Struct, None);
298 let id = types.record(record);
299
300 assert!(is_object(&types, id));
301 assert!(!is_complete(&types, id));
302 assert!(!is_modifiable(&types, id));
303 }
304
305 #[test]
306 fn a_const_member_makes_the_whole_structure_unmodifiable() {
307 let mut types = Types::new();
308 let int = types.int(IntKind::Int);
309 let constant = types.qualified(int, Qualifiers::CONST);
310 let record = types.declare_record(RecordKind::Struct, None);
311 let target =
312 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
313 let laid_out = layout_record(
314 &types,
315 RecordKind::Struct,
316 &[FieldDecl::new(None, constant)],
317 &RecordOptions::default(),
318 &target,
319 )
320 .expect("a layout");
321 types.complete_record(record, laid_out);
322 let id = types.record(record);
323
324 assert!(!is_modifiable(&types, id));
326 }
327}