Skip to content

Commit 8e36066

Browse files
committed
transpile: Rework is_va_list for better accuracy
1 parent a2c2149 commit 8e36066

File tree

2 files changed

+109
-66
lines changed

2 files changed

+109
-66
lines changed

c2rust-transpile/src/c_ast/mod.rs

Lines changed: 104 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -347,83 +347,126 @@ impl TypedAstContext {
347347
.is_some()
348348
}
349349

350-
/// Follow a chain of typedefs and return true iff the last typedef is named
351-
/// `__builtin_va_list` thus naming the type clang uses to represent `va_list`s.
352-
pub fn is_builtin_va_list(&self, typ: CTypeId) -> bool {
353-
match self.index(typ).kind {
354-
CTypeKind::Typedef(decl) => match &self.index(decl).kind {
355-
CDeclKind::Typedef {
356-
name: name_,
357-
typ: ty,
358-
..
359-
} => {
360-
if name_ == "__builtin_va_list" {
361-
true
362-
} else {
363-
self.is_builtin_va_list(ty.ctype)
364-
}
365-
}
366-
_ => panic!("Typedef decl did not point to a typedef"),
367-
},
368-
_ => false,
369-
}
370-
}
350+
/// Returns whether `typ` is a chain of typedefs that ends in `__builtin_va_list`,
351+
/// thus naming the type clang uses to represent `va_list`s.
352+
/// This works on all architectures, but does not work in situations where typedefs are
353+
/// resolved/bypassed, such as with array-to-pointer decay.
354+
pub fn is_builtin_va_list(&self, mut typ: CTypeId) -> bool {
355+
loop {
356+
// Skip over Elaborated types
357+
let mut kind = &self.index(typ).kind;
358+
359+
while let &CTypeKind::Elaborated(typ) = kind {
360+
kind = &self.index(typ).kind;
361+
}
371362

372-
/// Predicate for types that are used to implement C's `va_list`.
373-
/// FIXME: can we get rid of this method and use `is_builtin_va_list` instead?
374-
pub fn is_va_list_struct(&self, typ: CTypeId) -> bool {
375-
// detect `va_list`s based on typedef (should work across implementations)
376-
// if self.is_builtin_va_list(typ) {
377-
// return true;
378-
// }
363+
// TODO: Rust 1.65: use let-else
364+
let decl = match kind {
365+
&CTypeKind::Typedef(decl) => decl,
366+
_ => return false,
367+
};
368+
let (name, qtyp) = match &self.index(decl).kind {
369+
&CDeclKind::Typedef { ref name, typ, .. } => (name, typ),
370+
_ => panic!("Typedef decl did not point to a typedef"),
371+
};
379372

380-
// detect `va_list`s based on type (assumes struct-based implementation)
381-
let resolved_ctype = self.resolve_type(typ);
382-
use CTypeKind::*;
383-
match resolved_ctype.kind {
384-
Struct(record_id) => {
385-
if let CDeclKind::Struct {
386-
name: Some(ref name_),
387-
..
388-
} = &self[record_id].kind
389-
{
390-
name_ == "__va_list_tag" || name_ == "__va_list"
391-
} else {
392-
false
393-
}
373+
if name == "__builtin_va_list" {
374+
return true;
394375
}
395-
// va_list is a 1 element array; return true iff element type is struct __va_list_tag
396-
ConstantArray(typ, 1) => self.is_va_list(typ),
397-
_ => false,
376+
377+
typ = qtyp.ctype;
398378
}
399379
}
400380

401-
/// Predicate for pointers to types that are used to implement C's `va_list`.
402-
pub fn is_va_list(&self, typ: CTypeId) -> bool {
381+
/// Returns whether `typ` is the architecture-specific type used for `va_list`.
382+
/// Returns `false` for architectures where `va_list` is a generic pointer type.
383+
pub fn is_va_list_struct(&self, typ: CTypeId) -> bool {
403384
use BuiltinVaListKind::*;
385+
404386
match self.va_list_kind {
405-
CharPtrBuiltinVaList | VoidPtrBuiltinVaList | X86_64ABIBuiltinVaList => {
406-
match self.resolve_type(typ).kind {
407-
CTypeKind::Pointer(CQualTypeId { ctype, .. })
408-
| CTypeKind::ConstantArray(ctype, _) => self.is_va_list_struct(ctype),
409-
_ => false,
410-
}
387+
// No special identification is possible with generic types.
388+
CharPtrBuiltinVaList | VoidPtrBuiltinVaList => false,
389+
390+
// ARM32:
391+
// typedef struct __va_list {
392+
// void *__ap;
393+
// } __builtin_va_list;
394+
395+
// ARM64:
396+
// typedef struct __va_list {
397+
// void *__stack;
398+
// void *__gr_top;
399+
// void *__vr_top;
400+
// int __gr_offs;
401+
// int __vr_offs;
402+
// } __builtin_va_list;
403+
AArch64ABIBuiltinVaList | AAPCSABIBuiltinVaList => {
404+
// TODO: Rust 1.65: use let-else
405+
let decl = match self.resolve_type(typ).kind {
406+
CTypeKind::Struct(decl) => decl,
407+
_ => return false,
408+
};
409+
let name = match &self[decl].kind {
410+
CDeclKind::Struct {
411+
name: Some(name), ..
412+
} => name,
413+
_ => return false,
414+
};
415+
416+
name == "__va_list"
411417
}
412418

413-
AArch64ABIBuiltinVaList => self.is_va_list_struct(typ),
419+
// X86-64:
420+
// typedef struct __va_list_tag {
421+
// unsigned int gp_offset;
422+
// unsigned int fp_offset;
423+
// void *overflow_arg_area;
424+
// void *reg_save_area;
425+
// } __builtin_va_list[1];
426+
427+
// Power:
428+
// typedef struct __va_list_tag {
429+
// unsigned char gpr;
430+
// unsigned char fpr;
431+
// unsigned short reserved;
432+
// char *overflow_arg_area;
433+
// char *reg_save_area;
434+
// } __builtin_va_list[1];
435+
PowerABIBuiltinVaList | X86_64ABIBuiltinVaList => {
436+
// TODO: Rust 1.65: use let-else
437+
let inner = match self.resolve_type(typ).kind {
438+
CTypeKind::ConstantArray(inner, 1) => inner,
439+
// Account for array-to-pointer decay in function parameters.
440+
CTypeKind::Pointer(CQualTypeId { ctype: inner, .. }) => inner,
441+
_ => return false,
442+
};
443+
let decl = match self.resolve_type(inner).kind {
444+
CTypeKind::Struct(decl) => decl,
445+
_ => return false,
446+
};
447+
let name = match &self[decl].kind {
448+
CDeclKind::Struct {
449+
name: Some(name), ..
450+
} => name,
451+
_ => return false,
452+
};
414453

415-
AAPCSABIBuiltinVaList => {
416-
// The mechanism applies: va_list is a `struct __va_list { ... }` as per
417-
// https://documentation-service.arm.com/static/5f201281bb903e39c84d7eae
418-
// ("Procedure Call Standard for the Arm Architecture Release 2020Q2, Document
419-
// number IHI 0042J") Section 8.1.4 "Additional Types"
420-
self.is_va_list_struct(typ)
454+
name == "__va_list_tag"
421455
}
422456

423-
kind => unimplemented!("va_list type {:?} not yet implemented", kind),
457+
// TODO:
458+
PNaClABIBuiltinVaList => false,
459+
460+
// TODO:
461+
SystemZBuiltinVaList => false,
424462
}
425463
}
426464

465+
/// Returns whether `typ` is a C `va_list`.
466+
pub fn is_va_list(&self, typ: CTypeId) -> bool {
467+
self.is_builtin_va_list(typ) || self.is_va_list_struct(typ)
468+
}
469+
427470
/// Predicate for function pointers
428471
pub fn is_function_pointer(&self, typ: CTypeId) -> bool {
429472
let resolved_ctype = self.resolve_type(typ);

c2rust-transpile/src/translator/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4601,7 +4601,7 @@ impl<'c> Translation<'c> {
46014601
// and to be a pointer as a function argument we would get
46024602
// spurious casts when trying to treat it like a VaList which
46034603
// has reference semantics.
4604-
if self.ast_context.is_va_list(target_cty.ctype) {
4604+
if self.ast_context.is_va_list(source_cty.ctype) {
46054605
return Ok(val);
46064606
}
46074607

@@ -4843,17 +4843,17 @@ impl<'c> Translation<'c> {
48434843
ty_id: CTypeId,
48444844
is_static: bool,
48454845
) -> TranslationResult<WithStmts<Box<Expr>>> {
4846-
let resolved_ty_id = self.ast_context.resolve_type_id(ty_id);
4847-
let resolved_ty = &self.ast_context.index(resolved_ty_id).kind;
4848-
4849-
if self.ast_context.is_va_list(resolved_ty_id) {
4846+
if self.ast_context.is_va_list(ty_id) {
48504847
// generate MaybeUninit::uninit().assume_init()
48514848
let path = vec!["core", "mem", "MaybeUninit", "uninit"];
48524849
let call = mk().call_expr(mk().abs_path_expr(path), vec![]);
48534850
let call = mk().method_call_expr(call, "assume_init", vec![]);
48544851
return Ok(WithStmts::new_val(call));
48554852
}
48564853

4854+
let resolved_ty_id = self.ast_context.resolve_type_id(ty_id);
4855+
let resolved_ty = &self.ast_context.index(resolved_ty_id).kind;
4856+
48574857
if resolved_ty.is_bool() {
48584858
Ok(WithStmts::new_val(mk().lit_expr(mk().bool_lit(false))))
48594859
} else if resolved_ty.is_integral_type() {

0 commit comments

Comments
 (0)