1use crate::TypeInfo;
7
8#[derive(Clone, Copy, Debug, PartialEq, Eq)]
9pub struct CIntegerComparisonType {
10 pub size: u64,
11 pub is_unsigned: bool,
12}
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq)]
15pub struct CIntegerComparisonPlan {
16 pub size: u64,
17 pub is_unsigned: bool,
18}
19
20#[derive(Clone, Debug, PartialEq)]
21pub struct MemberLayout {
22 pub offset: u64,
23 pub member_type: TypeInfo,
24}
25
26#[derive(Clone, Debug, PartialEq)]
27pub struct IndexableElementLayout {
28 pub element_type: TypeInfo,
29 pub stride: u64,
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum TypeLayoutError {
34 #[error("Unknown member '{field}' in {kind} '{type_name}' (known members: {members})")]
35 UnknownMember {
36 kind: &'static str,
37 type_name: String,
38 field: String,
39 members: String,
40 },
41
42 #[error("member access requires struct or union type, got '{type_name}'")]
43 InvalidMemberBase { type_name: String },
44}
45
46impl CIntegerComparisonType {
47 pub fn promoted(self) -> Self {
48 if self.size < 4 {
49 Self {
50 size: 4,
51 is_unsigned: false,
52 }
53 } else {
54 self
55 }
56 }
57
58 pub fn signed_i64() -> Self {
59 Self {
60 size: 8,
61 is_unsigned: false,
62 }
63 }
64}
65
66pub fn strip_type_aliases(mut ty: &TypeInfo) -> &TypeInfo {
67 while let TypeInfo::TypedefType {
68 underlying_type, ..
69 }
70 | TypeInfo::QualifiedType {
71 underlying_type, ..
72 } = ty
73 {
74 ty = underlying_type.as_ref();
75 }
76 ty
77}
78
79pub fn is_c_aggregate_type(ty: &TypeInfo) -> bool {
80 matches!(
81 strip_type_aliases(ty),
82 TypeInfo::StructType { .. } | TypeInfo::UnionType { .. } | TypeInfo::ArrayType { .. }
83 )
84}
85
86pub fn is_c_pointer_or_array_type(ty: &TypeInfo) -> bool {
87 matches!(
88 strip_type_aliases(ty),
89 TypeInfo::PointerType { .. } | TypeInfo::ArrayType { .. }
90 )
91}
92
93pub fn member_layout(ty: &TypeInfo, field: &str) -> Result<MemberLayout, TypeLayoutError> {
94 match strip_type_aliases(ty) {
95 TypeInfo::StructType { name, members, .. } => members
96 .iter()
97 .find(|member| member.name == field)
98 .map(|member| MemberLayout {
99 offset: member.offset,
100 member_type: member.member_type.clone(),
101 })
102 .ok_or_else(|| unknown_member_error("struct", name, field, members)),
103 TypeInfo::UnionType { name, members, .. } => members
104 .iter()
105 .find(|member| member.name == field)
106 .map(|member| MemberLayout {
107 offset: member.offset,
108 member_type: member.member_type.clone(),
109 })
110 .ok_or_else(|| unknown_member_error("union", name, field, members)),
111 other => Err(TypeLayoutError::InvalidMemberBase {
112 type_name: other.type_name(),
113 }),
114 }
115}
116
117pub fn indexable_element_layout(ty: &TypeInfo) -> Option<IndexableElementLayout> {
118 match strip_type_aliases(ty) {
119 TypeInfo::ArrayType { element_type, .. } => Some(IndexableElementLayout {
120 element_type: element_type.as_ref().clone(),
121 stride: element_type.size().max(1),
122 }),
123 TypeInfo::PointerType { target_type, .. } => Some(IndexableElementLayout {
124 element_type: target_type.as_ref().clone(),
125 stride: target_type.size().max(1),
126 }),
127 _ => None,
128 }
129}
130
131pub fn c_integer_comparison_type(ty: &TypeInfo) -> Option<CIntegerComparisonType> {
132 match ty {
133 TypeInfo::BaseType { encoding, size, .. } => {
134 let is_unsigned = *encoding == crate::constants::DW_ATE_unsigned.0 as u16
135 || *encoding == crate::constants::DW_ATE_unsigned_char.0 as u16;
136 let is_signed = *encoding == crate::constants::DW_ATE_signed.0 as u16
137 || *encoding == crate::constants::DW_ATE_signed_char.0 as u16
138 || *encoding == crate::constants::DW_ATE_boolean.0 as u16;
139 if is_unsigned || is_signed {
140 Some(CIntegerComparisonType {
141 size: *size,
142 is_unsigned,
143 })
144 } else {
145 None
146 }
147 }
148 TypeInfo::EnumType {
149 base_type, size, ..
150 } => c_integer_comparison_type(base_type).map(|mut ty| {
151 if ty.size == 0 {
152 ty.size = *size;
153 }
154 ty
155 }),
156 TypeInfo::BitfieldType {
157 underlying_type,
158 bit_size,
159 ..
160 } => c_integer_comparison_type(underlying_type).map(|mut ty| {
161 ty.size = (*bit_size as u64).max(1).div_ceil(8);
162 ty
163 }),
164 TypeInfo::TypedefType {
165 underlying_type, ..
166 }
167 | TypeInfo::QualifiedType {
168 underlying_type, ..
169 } => c_integer_comparison_type(underlying_type),
170 _ => None,
171 }
172}
173
174pub fn is_c_signed_integer_type(ty: &TypeInfo) -> bool {
175 match ty {
176 TypeInfo::BaseType { encoding, .. } => {
177 *encoding == crate::constants::DW_ATE_signed.0 as u16
178 || *encoding == crate::constants::DW_ATE_signed_char.0 as u16
179 }
180 TypeInfo::EnumType { base_type, .. } => is_c_signed_integer_type(base_type),
181 TypeInfo::BitfieldType {
182 underlying_type, ..
183 } => is_c_signed_integer_type(underlying_type),
184 TypeInfo::TypedefType {
185 underlying_type, ..
186 }
187 | TypeInfo::QualifiedType {
188 underlying_type, ..
189 } => is_c_signed_integer_type(underlying_type),
190 _ => false,
191 }
192}
193
194pub fn usual_c_arithmetic_comparison_plan(
195 left: CIntegerComparisonType,
196 right: CIntegerComparisonType,
197) -> CIntegerComparisonPlan {
198 let left = left.promoted();
199 let right = right.promoted();
200 if left.is_unsigned == right.is_unsigned {
201 return CIntegerComparisonPlan {
202 size: left.size.max(right.size),
203 is_unsigned: left.is_unsigned,
204 };
205 }
206
207 let unsigned = if left.is_unsigned { left } else { right };
208 let signed = if left.is_unsigned { right } else { left };
209 if unsigned.size >= signed.size {
210 CIntegerComparisonPlan {
211 size: unsigned.size,
212 is_unsigned: true,
213 }
214 } else {
215 CIntegerComparisonPlan {
216 size: signed.size,
217 is_unsigned: false,
218 }
219 }
220}
221
222fn unknown_member_error(
223 kind: &'static str,
224 type_name: &str,
225 field: &str,
226 members: &[crate::StructMember],
227) -> TypeLayoutError {
228 let mut member_names = members
229 .iter()
230 .map(|member| member.name.clone())
231 .collect::<Vec<_>>();
232 member_names.sort();
233 member_names.dedup();
234 let list = if member_names.is_empty() {
235 "<none>".to_string()
236 } else {
237 member_names.join(", ")
238 };
239
240 TypeLayoutError::UnknownMember {
241 kind,
242 type_name: type_name.to_string(),
243 field: field.to_string(),
244 members: list,
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251 use crate::StructMember;
252
253 fn int_type(name: &str, size: u64, encoding: u16) -> TypeInfo {
254 TypeInfo::BaseType {
255 name: name.to_string(),
256 size,
257 encoding,
258 }
259 }
260
261 fn signed_int() -> TypeInfo {
262 int_type("int", 4, crate::constants::DW_ATE_signed.0 as u16)
263 }
264
265 #[test]
266 fn aggregate_classification_strips_aliases() {
267 let struct_type = TypeInfo::StructType {
268 name: "Request".to_string(),
269 size: 4,
270 members: vec![StructMember {
271 name: "fd".to_string(),
272 member_type: signed_int(),
273 offset: 0,
274 bit_offset: None,
275 bit_size: None,
276 }],
277 };
278 let typedef = TypeInfo::TypedefType {
279 name: "request_t".to_string(),
280 underlying_type: Box::new(TypeInfo::QualifiedType {
281 qualifier: crate::TypeQualifier::Const,
282 underlying_type: Box::new(struct_type),
283 }),
284 };
285
286 assert!(is_c_aggregate_type(&typedef));
287 }
288
289 #[test]
290 fn pointer_or_array_classification_strips_aliases() {
291 let array_type = TypeInfo::ArrayType {
292 element_type: Box::new(signed_int()),
293 element_count: Some(4),
294 total_size: Some(16),
295 };
296 let typedef = TypeInfo::TypedefType {
297 name: "int_array_t".to_string(),
298 underlying_type: Box::new(array_type),
299 };
300
301 assert!(is_c_pointer_or_array_type(&typedef));
302 }
303
304 #[test]
305 fn member_and_index_layout_strip_aliases() {
306 let signed_int = signed_int();
307 let struct_type = TypeInfo::StructType {
308 name: "Request".to_string(),
309 size: 16,
310 members: vec![StructMember {
311 name: "fd".to_string(),
312 member_type: signed_int.clone(),
313 offset: 8,
314 bit_offset: None,
315 bit_size: None,
316 }],
317 };
318 let qualified_struct = TypeInfo::QualifiedType {
319 qualifier: crate::TypeQualifier::Const,
320 underlying_type: Box::new(struct_type),
321 };
322
323 let layout = member_layout(&qualified_struct, "fd").expect("member layout");
324 assert_eq!(layout.offset, 8);
325 assert_eq!(layout.member_type, signed_int.clone());
326
327 let pointer_type = TypeInfo::PointerType {
328 target_type: Box::new(signed_int),
329 size: 8,
330 };
331 let element = indexable_element_layout(&pointer_type).expect("pointer element layout");
332 assert_eq!(element.stride, 4);
333 }
334
335 #[test]
336 fn integer_comparison_type_handles_enums_and_bitfields() {
337 let enum_type = TypeInfo::EnumType {
338 name: "Mode".to_string(),
339 size: 4,
340 variants: vec![],
341 base_type: Box::new(int_type(
342 "unsigned int",
343 0,
344 crate::constants::DW_ATE_unsigned.0 as u16,
345 )),
346 };
347 assert_eq!(
348 c_integer_comparison_type(&enum_type),
349 Some(CIntegerComparisonType {
350 size: 4,
351 is_unsigned: true,
352 })
353 );
354
355 let bitfield_type = TypeInfo::BitfieldType {
356 underlying_type: Box::new(signed_int()),
357 bit_offset: 0,
358 bit_size: 9,
359 };
360 assert_eq!(
361 c_integer_comparison_type(&bitfield_type),
362 Some(CIntegerComparisonType {
363 size: 2,
364 is_unsigned: false,
365 })
366 );
367 }
368
369 #[test]
370 fn signed_integer_classification_excludes_boolean() {
371 let bool_type = int_type("bool", 1, crate::constants::DW_ATE_boolean.0 as u16);
372 assert_eq!(
373 c_integer_comparison_type(&bool_type),
374 Some(CIntegerComparisonType {
375 size: 1,
376 is_unsigned: false,
377 })
378 );
379 assert!(!is_c_signed_integer_type(&bool_type));
380 assert!(is_c_signed_integer_type(&signed_int()));
381 }
382
383 #[test]
384 fn usual_comparison_plan_applies_integer_promotions() {
385 let u8_type = CIntegerComparisonType {
386 size: 1,
387 is_unsigned: true,
388 };
389 let i8_type = CIntegerComparisonType {
390 size: 1,
391 is_unsigned: false,
392 };
393
394 assert_eq!(
395 usual_c_arithmetic_comparison_plan(u8_type, i8_type),
396 CIntegerComparisonPlan {
397 size: 4,
398 is_unsigned: false,
399 }
400 );
401 }
402
403 #[test]
404 fn usual_comparison_plan_respects_unsigned_rank() {
405 let u64_type = CIntegerComparisonType {
406 size: 8,
407 is_unsigned: true,
408 };
409 let i32_type = CIntegerComparisonType {
410 size: 4,
411 is_unsigned: false,
412 };
413
414 assert_eq!(
415 usual_c_arithmetic_comparison_plan(u64_type, i32_type),
416 CIntegerComparisonPlan {
417 size: 8,
418 is_unsigned: true,
419 }
420 );
421 }
422}