use super::*;
#[derive(Copy, Clone, Debug)]
pub enum VaPart {
Start(CDeclId),
End(CDeclId),
Copy(CDeclId, CDeclId),
}
macro_rules! match_or {
([$e:expr] $p:pat => $r:tt) => {
let $r = match $e {
$p => $r,
_ => return None,
};
};
([$e:expr] $p:pat if $g:expr => $r:tt) => {
let $r = match $e {
$p if $g => $r,
_ => return None,
};
};
}
impl<'c> Translation<'c> {
pub fn is_va_decl(&self, decl_id: CDeclId) -> bool {
let fn_ctx = self.function_context.borrow();
if let Some(ref decls) = fn_ctx.va_list_decl_ids {
decls.contains(&decl_id)
} else {
false
}
}
pub fn match_vastart(&self, expr: CExprId) -> Option<CDeclId> {
fn match_vastart_struct(ast_context: &TypedAstContext, expr: CExprId) -> Option<CDeclId> {
match_or! { [ast_context[expr].kind]
CExprKind::ImplicitCast(_, e, _, _, _) => e }
match_or! { [ast_context[e].kind]
CExprKind::DeclRef(_, va_id, _) => va_id }
Some(va_id)
}
fn match_vastart_struct_member(
ast_context: &TypedAstContext,
expr: CExprId,
) -> Option<CDeclId> {
match_or! { [ast_context[expr].kind]
CExprKind::ImplicitCast(_, me, _, _, _) => me }
match_or! { [ast_context[me].kind]
CExprKind::Member(_, e, _, _, _) => e }
match_or! { [ast_context[e].kind]
CExprKind::DeclRef(_, va_id, _) => va_id }
Some(va_id)
}
fn match_vastart_struct_pointer_member(
ast_context: &TypedAstContext,
expr: CExprId,
) -> Option<CDeclId> {
match_or! { [ast_context[expr].kind]
CExprKind::ImplicitCast(_, me, _, _, _) => me }
match_or! { [ast_context[me].kind]
CExprKind::Member(_, ie, _, _, _) => ie }
match_or! { [ast_context[ie].kind]
CExprKind::ImplicitCast(_, e, _, _, _) => e }
match_or! { [ast_context[e].kind]
CExprKind::DeclRef(_, va_id, _) => va_id }
Some(va_id)
}
fn match_vastart_pointer(ast_context: &TypedAstContext, expr: CExprId) -> Option<CDeclId> {
match_or! { [ast_context[expr].kind]
CExprKind::DeclRef(_, va_id, _) => va_id }
Some(va_id)
}
match_vastart_struct(&self.ast_context, expr)
.or_else(|| match_vastart_pointer(&self.ast_context, expr))
.or_else(|| match_vastart_struct_member(&self.ast_context, expr))
.or_else(|| match_vastart_struct_pointer_member(&self.ast_context, expr))
}
pub fn match_vaend(&self, expr: CExprId) -> Option<CDeclId> {
self.match_vastart(expr)
}
pub fn match_vacopy(&self, dst_expr: CExprId, src_expr: CExprId) -> Option<(CDeclId, CDeclId)> {
let dst_id = self.match_vastart(dst_expr);
let src_id = self.match_vastart(src_expr);
if let (Some(did), Some(sid)) = (dst_id, src_id) {
return Some((did, sid));
}
None
}
pub fn match_vapart(&self, expr: CExprId) -> Option<VaPart> {
match_or! { [self.ast_context[expr].kind]
CExprKind::Call(_, func, ref args) => (func, args) }
match_or! { [self.ast_context[func].kind]
CExprKind::ImplicitCast(_, fexp, CastKind::BuiltinFnToFnPtr, _, _) => fexp }
match_or! { [self.ast_context[fexp].kind]
CExprKind::DeclRef(_, decl_id, _) => decl_id }
match_or! { [self.ast_context[decl_id].kind]
CDeclKind::Function { ref name, .. } => name }
match name as &str {
"__builtin_va_start" => {
if args.len() != 2 {
return None;
}
self.match_vastart(args[0]).map(VaPart::Start)
}
"__builtin_va_copy" => {
if args.len() != 2 {
return None;
}
self.match_vacopy(args[0], args[1])
.map(|(did, sid)| VaPart::Copy(did, sid))
}
"__builtin_va_end" => {
if args.len() != 1 {
return None;
}
self.match_vaend(args[0]).map(VaPart::End)
}
_ => None,
}
}
pub fn convert_vaarg(
&self,
ctx: ExprContext,
ty: CQualTypeId,
val_id: CExprId,
) -> TranslationResult<WithStmts<Box<Expr>>> {
if self.tcfg.translate_valist {
let val = self.convert_expr(ctx.used(), val_id, None)?;
let fn_ptr_ty: Option<Box<Type>> = {
let resolved_ctype = self.ast_context.resolve_type(ty.ctype);
if let CTypeKind::Pointer(p) = resolved_ctype.kind {
let resolved_ctype = self.ast_context.resolve_type(p.ctype);
if let CTypeKind::Function(ret, ref params, is_variadic, is_noreturn, _) =
resolved_ctype.kind
{
let opt_ret = if is_noreturn { None } else { Some(ret) };
let fn_ty = self.type_converter.borrow_mut().convert_function(
&self.ast_context,
opt_ret,
params,
is_variadic,
)?;
let m = if p.qualifiers.is_const {
Mutability::Immutable
} else {
Mutability::Mutable
};
Some(mk().set_mutbl(m).ptr_ty(fn_ty))
} else {
None
}
} else {
None
}
};
let have_fn_ptr = fn_ptr_ty.is_some();
let mut arg_ty = fn_ptr_ty.unwrap_or_else(|| self.convert_type(ty.ctype).unwrap());
let mut real_arg_ty = None;
if self
.ast_context
.get_pointee_qual_type(ty.ctype)
.map_or(false, |ty| {
self.ast_context.is_forward_declared_type(ty.ctype)
})
{
real_arg_ty = Some(arg_ty.clone());
arg_ty = mk()
.mutbl()
.ptr_ty(mk().abs_path_ty(vec!["core", "ffi", "c_void"]));
}
val.and_then(|val| {
let path = mk().path_segment_with_args(
mk().ident("arg"),
mk().angle_bracketed_args(vec![arg_ty]),
);
let mut val = mk().method_call_expr(val, path, vec![]);
if let Some(ty) = real_arg_ty {
val = mk().cast_expr(val, ty);
}
if ctx.is_unused() {
Ok(WithStmts::new(
vec![mk().semi_stmt(val)],
self.panic_or_err("convert_vaarg unused"),
))
} else {
let val = if have_fn_ptr {
transmute_expr(mk().infer_ty(), mk().infer_ty(), val)
} else {
val
};
Ok(WithStmts::new_val(val))
}
})
} else {
Err(format_err!("Variable argument list translation is not enabled.").into())
}
}
pub fn register_va_decls(&self, body: CStmtId) -> String {
self.use_feature("c_variadic");
let va_list_arg_name = self.renamer.borrow_mut().pick_name("args");
let mut va_list_decl_ids: IndexSet<CDeclId> = IndexSet::new();
for s in DFExpr::new(&self.ast_context, body.into()) {
if let SomeId::Expr(e) = s {
if let Some(part) = self.match_vapart(e) {
let id = match part {
VaPart::Start(va_id) | VaPart::End(va_id) => va_id,
VaPart::Copy(dst_va_id, _src_va_id) => dst_va_id,
};
va_list_decl_ids.insert(id);
}
}
}
let mut fn_ctx = self.function_context.borrow_mut();
fn_ctx.va_list_arg_name = Some(va_list_arg_name.clone());
fn_ctx.va_list_decl_ids = Some(va_list_decl_ids);
va_list_arg_name
}
}