Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor variadics #638

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3866a89
Refactor `is_va_decl` to use `.map()` and `.unwrap_or_default()`.
kkysen Aug 23, 2022
593da11
Refactored `match_vacopy` with `?`.
kkysen Aug 23, 2022
67deb33
Replaced an `as &str` with a `.as_str()`.
kkysen Aug 23, 2022
0560612
Refactored `if ... { return None; }`s in `match` arms into `match` `i…
kkysen Aug 23, 2022
c2e1136
Replaced `args.len() == n` and then `args[0]`, etc. with `match`ing o…
kkysen Aug 23, 2022
f9eec5f
Reformatted the `match_vapart` `match` after refactoring it.
kkysen Aug 23, 2022
51b3d91
Refactored out repeated `match`ing on `__builtin_va_*` into a `.strip…
kkysen Aug 23, 2022
15575c3
Improved doc comments on inner variadic matching functions.
kkysen Aug 23, 2022
35701ce
Made the variadic-matching inner functions closures instead so we don…
kkysen Aug 23, 2022
578dc62
Renamed the local `ast_context` to `ast` for brevity since it's used …
kkysen Aug 23, 2022
66cebb5
Removed non-local control flow (`return None`) and variable declarati…
kkysen Aug 23, 2022
8fae26f
Fixed confusing "`*` pointer" language in docs, which could be a `**`…
kkysen Aug 24, 2022
eaccb5c
Favor `.unwrap_or(false)` over `.unwrap_or_default()` as it's less cl…
kkysen Aug 24, 2022
b4428a3
Revert "Refactored out repeated `match`ing on `__builtin_va_*` into a…
kkysen Aug 24, 2022
0e48fdb
Refactored out `let ast = &self.ast_context` for brevity in `fn match…
kkysen Aug 24, 2022
8ac1538
Re-formatted `match_or!`s onto 1 line each. 2 lines with an indented…
kkysen Aug 24, 2022
2439ed4
Revert "Refactor `is_va_decl` to use `.map()` and `.unwrap_or_default…
kkysen Aug 25, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 59 additions & 90 deletions c2rust-transpile/src/translator/variadic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ pub enum VaPart {

macro_rules! match_or {
([$e:expr] $p:pat => $r:tt) => {
let $r = match $e {
$p => $r,
_ => return None,
};
match $e {
$p => Some($r),
_ => None,
}
};
([$e:expr] $p:pat if $g:expr => $r:tt) => {
let $r = match $e {
$p if $g => $r,
_ => return None,
};
match $e {
$p if $g => Some($r),
_ => None,
}
};
}

impl<'c> Translation<'c> {
/// Returns true iff `va_start`, `va_end`, or `va_copy` may be called on `decl_id`.
/// Returns `true` iff `va_start`, `va_end`, or `va_copy` may be called on `decl_id`.
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 {
Expand All @@ -34,105 +34,74 @@ impl<'c> Translation<'c> {
}

pub fn match_vastart(&self, expr: CExprId) -> Option<CDeclId> {
// struct-based va_list (e.g. x86_64)
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 }
let ast = &self.ast_context;

// `struct`-based `va_list` (e.g. x86_64).
let match_vastart_struct = || {
let e = match_or! { [ast[expr].kind] CExprKind::ImplicitCast(_, e, _, _, _) => e }?;
let va_id = match_or! { [ast[e].kind] CExprKind::DeclRef(_, va_id, _) => va_id }?;
Some(va_id)
}
};

// struct-based va_list (e.g. x86_64) where va_list is accessed as a struct member
// supporting this pattern is necessary to transpile apache httpd
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 }
// `struct`-based `va_list` (e.g. x86_64) where `va_list` is accessed as a `struct` member.
//
// Supporting this pattern is necessary to transpile apache httpd.
let match_vastart_struct_member = || {
let me = match_or! { [ast[expr].kind] CExprKind::ImplicitCast(_, me, _, _, _) => me }?;
let e = match_or! { [ast[me].kind] CExprKind::Member(_, e, _, _, _) => e }?;
let va_id = match_or! { [ast[e].kind] CExprKind::DeclRef(_, va_id, _) => va_id }?;
Some(va_id)
}
};

// struct-based va_list (e.g. x86_64) where va_list is accessed as a member of a struct pointer
// supporting this pattern is necessary to transpile [graphviz](https://gitlab.com/graphviz/graphviz/-/blob/5.0.0/lib/sfio/sftable.c#L321)
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 }
// `struct`-based `va_list` (e.g. x86_64) where `va_list` is accessed as a member of a `struct *`.
//
// Supporting this pattern is necessary to transpile
// [graphviz](https://gitlab.com/graphviz/graphviz/-/blob/5.0.0/lib/sfio/sftable.c#L321).
let match_vastart_struct_pointer_member = || {
let me = match_or! { [ast[expr].kind] CExprKind::ImplicitCast(_, me, _, _, _) => me }?;
let ie = match_or! { [ast[me].kind] CExprKind::Member(_, ie, _, _, _) => ie }?;
let e = match_or! { [ast[ie].kind] CExprKind::ImplicitCast(_, e, _, _, _) => e }?;
let va_id = match_or! { [ast[e].kind] CExprKind::DeclRef(_, va_id, _) => va_id }?;
Some(va_id)
}
};

// char pointer-based va_list (e.g. x86)
fn match_vastart_pointer(ast_context: &TypedAstContext, expr: CExprId) -> Option<CDeclId> {
match_or! { [ast_context[expr].kind]
CExprKind::DeclRef(_, va_id, _) => va_id }
// `char *`-based `va_list` (e.g. x86).
let match_vastart_pointer = || {
let va_id = match_or! { [ast[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))
match_vastart_struct()
.or_else(match_vastart_pointer)
.or_else(match_vastart_struct_member)
.or_else(match_vastart_struct_pointer_member)
}

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
let dst_id = self.match_vastart(dst_expr)?;
let src_id = self.match_vastart(src_expr)?;
Some((dst_id, src_id))
}

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)
}

let ast = &self.ast_context;

let (func, args) =
match_or! { [ast[expr].kind] CExprKind::Call(_, func, ref args) => (func, args) }?;
let fexp = match_or! { [ast[func].kind] CExprKind::ImplicitCast(_, fexp, CastKind::BuiltinFnToFnPtr, _, _) => fexp }?;
let decl_id = match_or! { [ast[fexp].kind] CExprKind::DeclRef(_, decl_id, _) => decl_id }?;
let name = match_or! { [ast[decl_id].kind] CDeclKind::Function { ref name, .. } => name }?;

match (name.as_str(), args.as_slice()) {
("__builtin_va_start", &[expr, _]) => self.match_vastart(expr).map(VaPart::Start),
("__builtin_va_copy", &[dst_expr, src_expr]) => self
.match_vacopy(dst_expr, src_expr)
.map(|(did, sid)| VaPart::Copy(did, sid)),
("__builtin_va_end", &[expr]) => self.match_vaend(expr).map(VaPart::End),
_ => None,
}
}
Expand Down