use std::collections::{HashMap, HashSet};
use std::io::{self, Write};
use crate::ast::{AssertKind, AssignOp, BinOp, BlockItem, CompoundStmt, Declaration, DeclSpecs, DerivedDecl, Expr, ExprKind, ForInit, FunctionDef, Initializer, ParamDecl, Stmt, TypeSpec};
use crate::intern::InternedStr;
use crate::enum_dict::EnumDict;
use crate::infer_api::InferResult;
use crate::intern::StringInterner;
use crate::macro_infer::{MacroInferContext, MacroInferInfo, MacroParam, ParseResult};
use crate::rust_decl::RustDeclDict;
use crate::syn_codegen::normalize_parens;
use crate::unified_type::UnifiedType;
use crate::sexp::SexpPrinter;
#[derive(Debug, Default, Clone)]
pub struct BindingsInfo {
pub static_arrays: HashSet<String>,
pub static_types: HashMap<String, String>,
pub bitfield_methods: HashMap<String, HashSet<String>>,
}
impl BindingsInfo {
pub fn from_rust_decl_dict(dict: &RustDeclDict) -> Self {
Self {
static_arrays: dict.static_arrays.clone(),
static_types: dict.static_types.clone(),
bitfield_methods: dict.bitfield_methods.clone(),
}
}
pub fn static_array_element_type(&self, name: &str) -> Option<String> {
let ty = self.static_types.get(name)?;
let s = ty.trim();
let s = s.strip_prefix('[')?;
let s = s.strip_suffix(']')?;
let semi = s.rfind(';')?;
Some(s[..semi].trim().to_string())
}
}
const LIBC_FUNCTIONS: &[&str] = &[
"strcmp", "strlen", "strncmp", "strcpy", "strncpy",
"memset", "memchr", "memcpy", "memmove",
];
fn libc_fn_param_type(func_name: &str, arg_index: usize) -> Option<UnifiedType> {
match (func_name, arg_index) {
("memset", 0) => Some(UnifiedType::from_rust_str("*mut c_void")),
("memset", 1) => Some(UnifiedType::from_rust_str("c_int")),
("memset", 2) => Some(UnifiedType::from_rust_str("usize")),
("memcpy", 0) | ("memmove", 0) => Some(UnifiedType::from_rust_str("*mut c_void")),
("memcpy", 1) | ("memmove", 1) => Some(UnifiedType::from_rust_str("*const c_void")),
("memcpy", 2) | ("memmove", 2) => Some(UnifiedType::from_rust_str("usize")),
("memchr", 0) => Some(UnifiedType::from_rust_str("*const c_void")),
("memchr", 1) => Some(UnifiedType::from_rust_str("c_int")),
("memchr", 2) => Some(UnifiedType::from_rust_str("usize")),
("memcmp", 0) | ("memcmp", 1) => Some(UnifiedType::from_rust_str("*const c_void")),
("memcmp", 2) => Some(UnifiedType::from_rust_str("usize")),
("strcmp", 0) | ("strcmp", 1) | ("strncmp", 0) | ("strncmp", 1) =>
Some(UnifiedType::from_rust_str("*const c_char")),
("strncmp", 2) => Some(UnifiedType::from_rust_str("usize")),
("strlen", 0) => Some(UnifiedType::from_rust_str("*const c_char")),
("strcpy", 0) | ("strncpy", 0) => Some(UnifiedType::from_rust_str("*mut c_char")),
("strcpy", 1) | ("strncpy", 1) => Some(UnifiedType::from_rust_str("*const c_char")),
("strncpy", 2) => Some(UnifiedType::from_rust_str("usize")),
_ => None,
}
}
pub struct KnownSymbols {
names: HashSet<String>,
}
impl KnownSymbols {
pub fn new(result: &InferResult, interner: &StringInterner) -> Self {
let mut names = HashSet::new();
if let Some(ref dict) = result.rust_decl_dict {
for name in dict.fns.keys() {
names.insert(name.clone());
}
for name in dict.consts.keys() {
names.insert(name.clone());
}
for name in dict.types.keys() {
names.insert(name.clone());
}
for name in dict.structs.keys() {
names.insert(name.clone());
}
for name in &dict.enums {
names.insert(name.clone());
}
for name in &dict.statics {
names.insert(name.clone());
}
for name in &dict.static_arrays {
names.insert(name.clone());
}
}
for (name_id, info) in &result.infer_ctx.macros {
let name_str = interner.get(*name_id);
if info.has_body && info.is_function {
names.insert(name_str.to_string());
}
}
for (name_id, _) in result.inline_fn_dict.iter() {
let name_str = interner.get(*name_id);
names.insert(name_str.to_string());
}
let builtins = [
"__builtin_expect",
"__builtin_offsetof",
"offsetof",
"__builtin_types_compatible_p",
"__builtin_constant_p",
"__builtin_choose_expr",
"__builtin_unreachable",
"__builtin_trap",
"__builtin_assume",
"__builtin_bswap16",
"__builtin_bswap32",
"__builtin_bswap64",
"__builtin_popcount",
"__builtin_clz",
"__builtin_ctz",
"pthread_mutex_lock",
"pthread_mutex_unlock",
"pthread_rwlock_rdlock",
"pthread_rwlock_wrlock",
"pthread_rwlock_unlock",
"pthread_getspecific",
"pthread_cond_wait",
"pthread_cond_signal",
"getenv",
"ASSERT_IS_LITERAL",
"ASSERT_IS_PTR",
"ASSERT_NOT_PTR",
];
for name in builtins {
names.insert(name.to_string());
}
for name in LIBC_FUNCTIONS {
names.insert(name.to_string());
}
for (name_id, _) in result.global_const_dict.iter() {
names.insert(interner.get(*name_id).to_string());
}
let rust_primitives = [
"true", "false", "std", "crate", "self", "super",
"null_mut", "null",
"PerlInterpreter", "my_perl",
"size_t", "ssize_t", "SSize_t",
"c_void", "c_char", "c_uchar", "c_int", "c_uint",
"c_long", "c_ulong", "c_short", "c_ushort",
];
for name in rust_primitives {
names.insert(name.to_string());
}
Self { names }
}
fn contains(&self, name: &str) -> bool {
self.names.contains(name)
}
pub fn insert(&mut self, name: String) {
self.names.insert(name);
}
}
const RUST_KEYWORDS: &[&str] = &[
"as", "async", "await", "break", "const", "continue", "crate", "dyn",
"else", "enum", "extern", "fn", "for", "if", "impl", "in",
"let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return",
"self", "Self", "static", "struct", "super", "trait", "type",
"unsafe", "use", "where", "while",
"abstract", "become", "box", "do", "final", "gen", "macro", "override",
"priv", "try", "typeof", "unsized", "virtual", "yield",
];
fn escape_rust_keyword(name: &str) -> String {
match name {
"__FILE__" => "file!()".to_string(),
"__LINE__" => "line!()".to_string(),
_ if RUST_KEYWORDS.contains(&name) => format!("r#{}", name),
_ => name.to_string(),
}
}
fn replace_word(s: &str, word: &str, replacement: &str) -> String {
if word.is_empty() {
return s.to_string();
}
let mut result = String::with_capacity(s.len());
let mut start = 0;
let bytes = s.as_bytes();
let word_bytes = word.as_bytes();
while let Some(pos) = s[start..].find(word) {
let abs_pos = start + pos;
let before_ok = abs_pos == 0 || !is_ident_char(bytes[abs_pos - 1]);
let after_pos = abs_pos + word.len();
let after_ok = after_pos >= bytes.len() || !is_ident_char(bytes[after_pos]);
if before_ok && after_ok {
result.push_str(&s[start..abs_pos]);
result.push_str(replacement);
start = after_pos;
} else {
result.push_str(&s[start..abs_pos + word_bytes.len()]);
start = abs_pos + word_bytes.len();
}
}
result.push_str(&s[start..]);
result
}
fn is_ident_char(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'_'
}
fn bin_op_to_rust(op: BinOp) -> &'static str {
match op {
BinOp::Add => "+",
BinOp::Sub => "-",
BinOp::Mul => "*",
BinOp::Div => "/",
BinOp::Mod => "%",
BinOp::BitAnd => "&",
BinOp::BitOr => "|",
BinOp::BitXor => "^",
BinOp::Shl => "<<",
BinOp::Shr => ">>",
BinOp::Lt => "<",
BinOp::Gt => ">",
BinOp::Le => "<=",
BinOp::Ge => ">=",
BinOp::Eq => "==",
BinOp::Ne => "!=",
BinOp::LogAnd => "&&",
BinOp::LogOr => "||",
}
}
fn assign_op_to_rust(op: AssignOp) -> &'static str {
match op {
AssignOp::Assign => "=",
AssignOp::MulAssign => "*=",
AssignOp::DivAssign => "/=",
AssignOp::ModAssign => "%=",
AssignOp::AddAssign => "+=",
AssignOp::SubAssign => "-=",
AssignOp::ShlAssign => "<<=",
AssignOp::ShrAssign => ">>=",
AssignOp::AndAssign => "&=",
AssignOp::XorAssign => "^=",
AssignOp::OrAssign => "|=",
}
}
fn escape_char(c: u8) -> String {
match c {
b'\'' => "\\'".to_string(),
b'\\' => "\\\\".to_string(),
b'\n' => "\\n".to_string(),
b'\r' => "\\r".to_string(),
b'\t' => "\\t".to_string(),
c if c.is_ascii_graphic() || c == b' ' => (c as char).to_string(),
c => format!("\\x{:02x}", c),
}
}
fn escape_string(s: &[u8]) -> String {
s.iter().map(|&c| escape_char(c)).collect()
}
fn is_void_only_param_list(params: &[ParamDecl]) -> bool {
if params.len() != 1 {
return false;
}
let p = ¶ms[0];
let declarator_is_trivial = match &p.declarator {
None => true,
Some(d) => d.name.is_none() && d.derived.is_empty(),
};
let specs_is_void = p.specs.type_specs.len() == 1
&& matches!(p.specs.type_specs[0], TypeSpec::Void);
declarator_is_trivial && specs_is_void
}
fn is_zero_constant(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::IntLit(0) => true,
ExprKind::UIntLit(0) => true,
_ => false,
}
}
pub fn is_boolean_expr(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::Binary { op, .. } => matches!(op,
BinOp::Lt | BinOp::Gt | BinOp::Le | BinOp::Ge |
BinOp::Eq | BinOp::Ne | BinOp::LogAnd | BinOp::LogOr
),
ExprKind::Cast { type_name, .. } => {
type_name.specs.type_specs.iter().any(|ts| {
matches!(ts, TypeSpec::Bool)
})
}
ExprKind::LogNot(_) => true,
_ => false,
}
}
fn is_boolean_expr_recursive(expr: &Expr, interner: &StringInterner) -> bool {
if is_boolean_expr(expr) {
return true;
}
match &expr.kind {
ExprKind::Call { func, args } => {
if let ExprKind::Ident(name) = &func.kind {
if interner.get(*name) == "__builtin_expect" && !args.is_empty() {
return is_boolean_expr_recursive(&args[0], interner);
}
}
}
ExprKind::Cast { type_name, expr: inner } => {
if type_name.specs.type_specs.iter().any(|ts| matches!(ts, TypeSpec::Bool)) {
return true;
}
return is_boolean_expr_recursive(inner, interner);
}
_ => {}
}
false
}
pub fn is_boolean_expr_with_context(
expr: &Expr,
bool_return_macros: &HashSet<InternedStr>,
bool_return_externals: &HashSet<InternedStr>,
) -> bool {
if is_boolean_expr(expr) {
return true;
}
match &expr.kind {
ExprKind::Call { func, .. } => {
if let ExprKind::Ident(name) = &func.kind {
return bool_return_macros.contains(name)
|| bool_return_externals.contains(name);
}
}
ExprKind::MacroCall { name, .. } => {
return bool_return_macros.contains(name)
|| bool_return_externals.contains(name);
}
_ => {}
}
false
}
fn is_type_repr_pointer(ty: &crate::type_repr::TypeRepr) -> bool {
use crate::type_repr::TypeRepr;
match ty {
TypeRepr::CType { derived, .. } => {
derived.iter().any(|d| matches!(d, crate::type_repr::CDerivedType::Pointer { .. }))
}
TypeRepr::RustType { repr, .. } => {
matches!(repr, crate::type_repr::RustTypeRepr::Pointer { .. })
}
TypeRepr::Inferred(inferred) => {
inferred.resolved_type()
.map(|r| is_type_repr_pointer(r))
.unwrap_or(false)
}
}
}
fn best_constraint_for_macro_param(
info: &MacroInferInfo,
param: &MacroParam,
) -> Option<crate::type_repr::TypeRepr> {
let mut best: Option<(&crate::type_repr::TypeRepr, u8)> = None;
let mut all_expr_ids: Vec<crate::ast::ExprId> = info
.type_env
.param_to_exprs
.get(¶m.name)
.map(|ids| ids.iter().cloned().collect())
.unwrap_or_default();
all_expr_ids.push(param.expr_id());
for expr_id in &all_expr_ids {
if let Some(constraints) = info.type_env.expr_constraints.get(expr_id) {
for c in constraints {
if c.ty.is_void() { continue; }
let tier = c.ty.confidence_tier();
if best.is_none() || tier < best.unwrap().1 {
best = Some((&c.ty, tier));
}
}
}
}
best.map(|(t, _)| t.clone())
}
fn substitute_idents(expr: &mut Expr, subs: &HashMap<InternedStr, &Expr>) {
if let ExprKind::Ident(name) = &expr.kind {
if let Some(replacement) = subs.get(name) {
*expr = (*replacement).clone();
return;
}
}
match &mut expr.kind {
ExprKind::Ident(_)
| ExprKind::IntLit(_)
| ExprKind::UIntLit(_)
| ExprKind::FloatLit(_)
| ExprKind::CharLit(_)
| ExprKind::StringLit(_)
| ExprKind::SizeofType(_)
| ExprKind::Alignof(_) => {}
ExprKind::Index { expr: e, index } => {
substitute_idents(e, subs);
substitute_idents(index, subs);
}
ExprKind::Call { func, args } => {
substitute_idents(func, subs);
for arg in args {
substitute_idents(arg, subs);
}
}
ExprKind::Member { expr: e, .. }
| ExprKind::PtrMember { expr: e, .. }
| ExprKind::PostInc(e)
| ExprKind::PostDec(e)
| ExprKind::PreInc(e)
| ExprKind::PreDec(e)
| ExprKind::AddrOf(e)
| ExprKind::Deref(e)
| ExprKind::UnaryPlus(e)
| ExprKind::UnaryMinus(e)
| ExprKind::BitNot(e)
| ExprKind::LogNot(e)
| ExprKind::Sizeof(e)
| ExprKind::Cast { expr: e, .. } => substitute_idents(e, subs),
ExprKind::Binary { lhs, rhs, .. }
| ExprKind::Assign { lhs, rhs, .. }
| ExprKind::Comma { lhs, rhs } => {
substitute_idents(lhs, subs);
substitute_idents(rhs, subs);
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
substitute_idents(cond, subs);
substitute_idents(then_expr, subs);
substitute_idents(else_expr, subs);
}
ExprKind::Assert { condition, .. } => substitute_idents(condition, subs),
ExprKind::MacroCall { args, expanded, .. } => {
for arg in args {
substitute_idents(arg, subs);
}
substitute_idents(expanded, subs);
}
ExprKind::BuiltinCall { args, .. } => {
for arg in args {
if let crate::ast::BuiltinArg::Expr(e) = arg {
substitute_idents(e, subs);
}
}
}
ExprKind::CompoundLit { init, .. } => {
for item in init {
if let crate::ast::Initializer::Expr(e) = &mut item.init {
substitute_idents(e, subs);
}
}
}
ExprKind::StmtExpr(_) => {}
}
}
fn expr_yields_value_for_stmt_use(expr: &syn::Expr) -> bool {
match expr {
syn::Expr::Block(b) => {
matches!(b.block.stmts.last(), Some(syn::Stmt::Expr(_, None)))
}
syn::Expr::Binary(b) => matches!(
b.op,
syn::BinOp::And(_) | syn::BinOp::Or(_)
),
syn::Expr::Unary(_) => true,
syn::Expr::Paren(p) => expr_yields_value_for_stmt_use(&p.expr),
_ => false,
}
}
fn is_unsigned_cast_expr(expr_str: &str) -> bool {
if let Some(pos) = expr_str.rfind(" as ") {
let after = &expr_str[pos + 4..].trim_end_matches(')');
matches!(*after, "usize" | "u8" | "u16" | "u32" | "u64" | "u128" | "c_uint" | "c_ulong" | "c_ulonglong")
} else {
false
}
}
fn is_unsigned_integer_target(ty: &str) -> bool {
matches!(ty,
"u8" | "u16" | "u32" | "u64" | "u128" | "usize" |
"U8" | "U16" | "U32" | "U64" |
"UV" | "STRLEN" | "Size_t" | "size_t" | "PERL_UINTMAX_T" |
"c_uchar" | "c_ushort" | "c_uint" | "c_ulong" | "c_ulonglong"
)
}
fn strip_outer_parens(s: &str) -> &str {
let s = s.trim();
if s.len() < 2 || !s.starts_with('(') || !s.ends_with(')') {
return s;
}
let inner = &s[1..s.len() - 1];
if inner.trim_start().starts_with('{') {
return s;
}
let mut depth = 0i32;
for ch in inner.chars() {
match ch {
'(' | '{' | '[' => depth += 1,
')' | '}' | ']' => {
depth -= 1;
if depth < 0 {
return s;
}
}
_ => {}
}
}
if depth == 0 {
inner
} else {
s
}
}
fn extract_assert_message(expr: &Expr) -> Option<String> {
if let ExprKind::LogNot(inner) = &expr.kind {
if let ExprKind::StringLit(bytes) = &inner.kind {
return Some(String::from_utf8_lossy(bytes).into_owned());
}
}
None
}
fn decompose_assert_with_message(condition: &Expr) -> Option<(&Expr, String)> {
if let ExprKind::Binary { op: BinOp::LogOr, lhs, rhs } = &condition.kind {
if let Some(msg) = extract_assert_message(rhs) {
return Some((lhs, msg));
}
}
None
}
fn is_sv_subtype_cast(from: &UnifiedType, to: &UnifiedType) -> bool {
let inner_name = |ut: &UnifiedType| -> Option<String> {
match ut.inner_type()? {
UnifiedType::Named(name) => Some(name.clone()),
UnifiedType::Void => Some("c_void".to_string()),
_ => None,
}
};
let from_name = match inner_name(from) {
Some(n) => n,
None => return false,
};
let to_name = match inner_name(to) {
Some(n) => n,
None => return false,
};
const SV_SUBTYPES: &[&str] = &[
"GV", "HV", "AV", "CV", "IO", "p5rx", "REGEXP",
"gv", "hv", "av", "cv", "io", "regexp",
];
let sv_like = |n: &str| n == "SV" || n == "sv";
(SV_SUBTYPES.contains(&from_name.as_str()) && sv_like(&to_name))
|| (sv_like(&from_name) && SV_SUBTYPES.contains(&to_name.as_str()))
|| (SV_SUBTYPES.contains(&from_name.as_str())
&& SV_SUBTYPES.contains(&to_name.as_str()))
|| to_name == "c_void"
|| from_name == "c_void"
}
fn is_null_literal(expr: &Expr) -> bool {
match &expr.kind {
ExprKind::IntLit(0) => true,
ExprKind::Cast { expr: inner, .. } => is_null_literal(inner),
_ => false,
}
}
fn null_ptr_expr(return_type: &UnifiedType) -> String {
if return_type.is_const_pointer() {
"std::ptr::null()".to_string()
} else {
"std::ptr::null_mut()".to_string()
}
}
fn normalize_integer_type(ty: &str) -> Option<&'static str> {
match ty {
"u8" | "U8" | "c_uchar" => Some("u8"),
"u16" | "U16" | "c_ushort" => Some("u16"),
"u32" | "U32" | "c_uint" => Some("u32"),
"u64" | "U64" | "UV" | "c_ulong" | "c_ulonglong"
| "PERL_UINTMAX_T" => Some("u64"),
"i8" | "I8" | "c_schar" | "c_char" => Some("i8"),
"i16" | "I16" | "c_short" => Some("i16"),
"i32" | "I32" | "c_int" => Some("i32"),
"i64" | "I64" | "IV" | "c_long" | "c_longlong" => Some("i64"),
"usize" | "STRLEN" => Some("usize"),
"isize" | "SSize_t" | "ssize_t" | "PADOFFSET" => Some("isize"),
"Stack_off_t" => Some("i32"),
_ => None,
}
}
fn integer_types_compatible(a: &str, b: &str) -> bool {
if a == b { return true; }
matches!((a, b),
("i64", "isize") | ("isize", "i64") |
("u64", "usize") | ("usize", "u64")
)
}
fn integer_type_rank(ty: &str) -> Option<(bool, u8)> {
match normalize_integer_type(ty)? {
"u8" => Some((false, 1)), "i8" => Some((true, 1)),
"u16" => Some((false, 2)), "i16" => Some((true, 2)),
"u32" => Some((false, 4)), "i32" => Some((true, 4)),
"u64" => Some((false, 8)), "i64" => Some((true, 8)),
"usize" => Some((false, 8)), "isize" => Some((true, 8)),
_ => None,
}
}
fn wider_integer_type(a: &str, b: &str) -> Option<&'static str> {
let na = normalize_integer_type(a)?;
let nb = normalize_integer_type(b)?;
if na == nb { return None; }
let (a_signed, a_rank) = integer_type_rank(a)?;
let (_b_signed, b_rank) = integer_type_rank(b)?;
if a_rank == b_rank {
Some(if a_signed { nb } else { na })
} else if a_rank > b_rank {
Some(na)
} else {
Some(nb)
}
}
pub fn pointer_inner_compatible(a: &UnifiedType, b: &UnifiedType) -> bool {
let a_inner = match a { UnifiedType::Pointer { inner, .. } => inner.as_ref(), _ => return false };
let b_inner = match b { UnifiedType::Pointer { inner, .. } => inner.as_ref(), _ => return false };
type_inner_compatible(a_inner, b_inner)
}
fn type_inner_compatible(a: &UnifiedType, b: &UnifiedType) -> bool {
if a.is_pointer() && b.is_pointer() {
return pointer_inner_compatible(a, b);
}
let a_s = a.to_rust_string();
let b_s = b.to_rust_string();
if let (Some(na), Some(nb)) = (normalize_integer_type(&a_s), normalize_integer_type(&b_s)) {
return na == nb;
}
if a.is_void() && b.is_void() { return true; }
a_s == b_s
}
pub fn pointer_const_differs(a: &UnifiedType, b: &UnifiedType) -> bool {
if !(a.is_pointer() && b.is_pointer()) { return false; }
if a.is_const_pointer() == b.is_const_pointer() { return false; }
pointer_inner_compatible(a, b)
}
pub fn collect_must_mut_pointer_params(
parse_result: &ParseResult,
params: &[MacroParam],
callee_const_params: &HashMap<InternedStr, HashSet<usize>>,
) -> HashSet<InternedStr> {
let param_names: HashSet<InternedStr> = params.iter().map(|p| p.name).collect();
let mut result = HashSet::new();
match parse_result {
ParseResult::Expression(expr) => {
collect_must_mut_from_expr(expr, ¶m_names, callee_const_params, &mut result);
}
ParseResult::Statement(items) => {
for item in items {
if let BlockItem::Stmt(stmt) = item {
collect_must_mut_from_stmt(stmt, ¶m_names, callee_const_params, &mut result);
}
}
}
ParseResult::Unparseable(_) => {}
}
result
}
pub fn collect_must_mut_from_stmt(
stmt: &Stmt,
params: &HashSet<InternedStr>,
callee_const: &HashMap<InternedStr, HashSet<usize>>,
result: &mut HashSet<InternedStr>,
) {
match stmt {
Stmt::Expr(Some(expr), _) | Stmt::Return(Some(expr), _) => {
collect_must_mut_from_expr(expr, params, callee_const, result);
}
Stmt::Compound(compound) => {
for item in &compound.items {
match item {
BlockItem::Stmt(s) => collect_must_mut_from_stmt(s, params, callee_const, result),
BlockItem::Decl(decl) => {
for init_decl in &decl.declarators {
if let Some(Initializer::Expr(init)) = &init_decl.init {
collect_must_mut_from_expr(init, params, callee_const, result);
}
}
}
}
}
}
Stmt::If { cond, then_stmt, else_stmt, .. } => {
collect_must_mut_from_expr(cond, params, callee_const, result);
collect_must_mut_from_stmt(then_stmt, params, callee_const, result);
if let Some(e) = else_stmt {
collect_must_mut_from_stmt(e, params, callee_const, result);
}
}
Stmt::While { cond, body, .. } | Stmt::DoWhile { cond, body, .. } => {
collect_must_mut_from_expr(cond, params, callee_const, result);
collect_must_mut_from_stmt(body, params, callee_const, result);
}
Stmt::For { init, cond, step, body, .. } => {
if let Some(ForInit::Expr(e)) = init { collect_must_mut_from_expr(e, params, callee_const, result); }
if let Some(e) = cond { collect_must_mut_from_expr(e, params, callee_const, result); }
if let Some(e) = step { collect_must_mut_from_expr(e, params, callee_const, result); }
collect_must_mut_from_stmt(body, params, callee_const, result);
}
Stmt::Switch { expr, body, .. } => {
collect_must_mut_from_expr(expr, params, callee_const, result);
collect_must_mut_from_stmt(body, params, callee_const, result);
}
_ => {}
}
}
pub fn collect_must_mut_from_expr(
expr: &Expr,
params: &HashSet<InternedStr>,
callee_const: &HashMap<InternedStr, HashSet<usize>>,
result: &mut HashSet<InternedStr>,
) {
match &expr.kind {
ExprKind::Assign { lhs, rhs, .. } => {
mark_lvalue_mut(lhs, params, result);
collect_must_mut_from_expr(lhs, params, callee_const, result);
collect_must_mut_from_expr(rhs, params, callee_const, result);
}
ExprKind::PreInc(inner) | ExprKind::PreDec(inner) |
ExprKind::PostInc(inner) | ExprKind::PostDec(inner) => {
mark_lvalue_mut(inner, params, result);
collect_must_mut_from_expr(inner, params, callee_const, result);
}
ExprKind::Call { func, args } => {
if let ExprKind::Ident(func_name) = &func.kind {
let const_arg_positions = callee_const.get(func_name);
for (i, arg) in args.iter().enumerate() {
if let ExprKind::Ident(arg_name) = &arg.kind {
if params.contains(arg_name) {
let is_const_at_callee = const_arg_positions
.map_or(false, |positions| positions.contains(&i));
if !is_const_at_callee {
result.insert(*arg_name);
}
}
}
collect_must_mut_from_expr(arg, params, callee_const, result);
}
} else {
for arg in args {
collect_must_mut_from_expr(arg, params, callee_const, result);
}
}
collect_must_mut_from_expr(func, params, callee_const, result);
}
ExprKind::Binary { lhs, rhs, .. } | ExprKind::Comma { lhs, rhs } => {
collect_must_mut_from_expr(lhs, params, callee_const, result);
collect_must_mut_from_expr(rhs, params, callee_const, result);
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
collect_must_mut_from_expr(cond, params, callee_const, result);
collect_must_mut_from_expr(then_expr, params, callee_const, result);
collect_must_mut_from_expr(else_expr, params, callee_const, result);
}
ExprKind::MacroCall { name, args, expanded, .. } => {
let const_arg_positions = callee_const.get(name);
for (i, arg) in args.iter().enumerate() {
if let ExprKind::Ident(arg_name) = &arg.kind {
if params.contains(arg_name) {
let is_const_at_callee = const_arg_positions
.map_or(false, |positions| positions.contains(&i));
if !is_const_at_callee {
result.insert(*arg_name);
}
}
}
collect_must_mut_from_expr(arg, params, callee_const, result);
}
collect_must_mut_from_expr(expanded, params, callee_const, result);
}
ExprKind::Deref(inner) | ExprKind::UnaryMinus(inner) | ExprKind::BitNot(inner) |
ExprKind::LogNot(inner) | ExprKind::AddrOf(inner) |
ExprKind::Cast { expr: inner, .. } => {
collect_must_mut_from_expr(inner, params, callee_const, result);
}
ExprKind::Member { expr: inner, .. } | ExprKind::PtrMember { expr: inner, .. } => {
collect_must_mut_from_expr(inner, params, callee_const, result);
}
ExprKind::Sizeof(inner) => {
collect_must_mut_from_expr(inner, params, callee_const, result);
}
ExprKind::Assert { condition, .. } => {
collect_must_mut_from_expr(condition, params, callee_const, result);
}
ExprKind::StmtExpr(compound) => {
for item in &compound.items {
match item {
BlockItem::Stmt(s) => collect_must_mut_from_stmt(s, params, callee_const, result),
BlockItem::Decl(decl) => {
for init_decl in &decl.declarators {
if let Some(Initializer::Expr(init)) = &init_decl.init {
collect_must_mut_from_expr(init, params, callee_const, result);
}
}
}
}
}
}
_ => {}
}
}
pub fn mark_lvalue_mut(expr: &Expr, params: &HashSet<InternedStr>, result: &mut HashSet<InternedStr>) {
match &expr.kind {
ExprKind::Deref(inner) => {
if let ExprKind::Ident(name) = &inner.kind {
if params.contains(name) {
result.insert(*name);
}
}
mark_lvalue_mut(inner, params, result);
}
ExprKind::PtrMember { expr: inner, .. } => {
if let ExprKind::Ident(name) = &inner.kind {
if params.contains(name) {
result.insert(*name);
}
}
mark_lvalue_mut(inner, params, result);
}
ExprKind::Member { expr: inner, .. } => {
mark_lvalue_mut(inner, params, result);
}
ExprKind::Cast { expr: inner, type_name } => {
if let ExprKind::Ident(name) = &inner.kind {
if params.contains(name) {
let has_non_const_ptr = type_name.declarator.as_ref()
.map(|d| d.derived.iter().any(|dd| {
matches!(dd, crate::ast::DerivedDecl::Pointer(q) if !q.is_const)
}))
.unwrap_or(false);
if has_non_const_ptr {
result.insert(*name);
}
}
}
mark_lvalue_mut(inner, params, result);
}
ExprKind::MacroCall { expanded, args, .. } => {
mark_lvalue_mut(expanded, params, result);
for arg in args {
mark_lvalue_mut(arg, params, result);
}
}
ExprKind::Call { args, .. } => {
for arg in args {
if let ExprKind::Ident(name) = &arg.kind {
if params.contains(name) {
result.insert(*name);
}
}
mark_lvalue_mut(arg, params, result);
}
}
_ => {}
}
}
fn collect_mut_params(parse_result: &ParseResult, params: &[MacroParam]) -> HashSet<InternedStr> {
let param_names: HashSet<InternedStr> = params.iter().map(|p| p.name).collect();
let mut result = HashSet::new();
match parse_result {
ParseResult::Expression(expr) => collect_mut_params_from_expr(expr, ¶m_names, &mut result),
ParseResult::Statement(items) => {
for item in items {
if let BlockItem::Stmt(stmt) = item {
collect_mut_params_from_stmt(stmt, ¶m_names, &mut result);
}
}
}
ParseResult::Unparseable(_) => {}
}
result
}
fn collect_mut_params_from_expr(expr: &Expr, params: &HashSet<InternedStr>, result: &mut HashSet<InternedStr>) {
match &expr.kind {
ExprKind::AddrOf(inner) => {
if let ExprKind::Ident(name) = &inner.kind {
if params.contains(name) {
result.insert(*name);
}
}
collect_mut_params_from_expr(inner, params, result);
}
ExprKind::Assign { lhs, rhs, .. } => {
if let ExprKind::Ident(name) = &lhs.kind {
if params.contains(name) {
result.insert(*name);
}
}
collect_mut_params_from_expr(lhs, params, result);
collect_mut_params_from_expr(rhs, params, result);
}
ExprKind::PreInc(inner) | ExprKind::PreDec(inner) |
ExprKind::PostInc(inner) | ExprKind::PostDec(inner) => {
if let ExprKind::Ident(name) = &inner.kind {
if params.contains(name) {
result.insert(*name);
}
}
collect_mut_params_from_expr(inner, params, result);
}
ExprKind::Binary { lhs, rhs, .. } => {
collect_mut_params_from_expr(lhs, params, result);
collect_mut_params_from_expr(rhs, params, result);
}
ExprKind::Deref(inner) | ExprKind::UnaryMinus(inner) | ExprKind::BitNot(inner) |
ExprKind::LogNot(inner) | ExprKind::Cast { expr: inner, .. } => {
collect_mut_params_from_expr(inner, params, result);
}
ExprKind::Call { func, args } => {
collect_mut_params_from_expr(func, params, result);
for arg in args {
collect_mut_params_from_expr(arg, params, result);
}
}
ExprKind::MacroCall { expanded, args, .. } => {
collect_mut_params_from_expr(expanded, params, result);
for arg in args {
collect_mut_params_from_expr(arg, params, result);
}
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
collect_mut_params_from_expr(cond, params, result);
collect_mut_params_from_expr(then_expr, params, result);
collect_mut_params_from_expr(else_expr, params, result);
}
ExprKind::Comma { lhs, rhs } => {
collect_mut_params_from_expr(lhs, params, result);
collect_mut_params_from_expr(rhs, params, result);
}
ExprKind::Member { expr: inner, .. } | ExprKind::PtrMember { expr: inner, .. } => {
collect_mut_params_from_expr(inner, params, result);
}
ExprKind::StmtExpr(compound) => {
for item in &compound.items {
if let BlockItem::Stmt(stmt) = item {
collect_mut_params_from_stmt(stmt, params, result);
}
}
}
_ => {}
}
}
fn collect_mut_params_from_stmt(stmt: &Stmt, params: &HashSet<InternedStr>, result: &mut HashSet<InternedStr>) {
match stmt {
Stmt::Expr(Some(expr), _) => collect_mut_params_from_expr(expr, params, result),
Stmt::Return(Some(expr), _) => collect_mut_params_from_expr(expr, params, result),
Stmt::If { cond, then_stmt, else_stmt, .. } => {
collect_mut_params_from_expr(cond, params, result);
collect_mut_params_from_stmt(then_stmt, params, result);
if let Some(else_s) = else_stmt {
collect_mut_params_from_stmt(else_s, params, result);
}
}
Stmt::Compound(compound) => {
for item in &compound.items {
if let BlockItem::Stmt(s) = item {
collect_mut_params_from_stmt(s, params, result);
}
}
}
Stmt::While { cond, body, .. } | Stmt::DoWhile { body, cond, .. } => {
collect_mut_params_from_expr(cond, params, result);
collect_mut_params_from_stmt(body, params, result);
}
Stmt::For { init, cond, step, body, .. } => {
if let Some(ForInit::Expr(e)) = init {
collect_mut_params_from_expr(e, params, result);
}
if let Some(c) = cond {
collect_mut_params_from_expr(c, params, result);
}
if let Some(s) = step {
collect_mut_params_from_expr(s, params, result);
}
collect_mut_params_from_stmt(body, params, result);
}
_ => {}
}
}
fn type_str_is_fn_pointer(ty_str: &str) -> bool {
ty_str.contains("fn(") || ty_str.contains("fn (")
}
fn build_field_type_map(dict: Option<&RustDeclDict>) -> HashMap<String, UnifiedType> {
let mut map: HashMap<String, UnifiedType> = HashMap::new();
let mut conflicts: HashSet<String> = HashSet::new();
if let Some(dict) = dict {
for st in dict.structs.values() {
for field in &st.fields {
if conflicts.contains(&field.name) {
continue;
}
match map.entry(field.name.clone()) {
std::collections::hash_map::Entry::Vacant(e) => {
e.insert(field.uty.clone());
}
std::collections::hash_map::Entry::Occupied(e) => {
if e.get() != &field.uty {
conflicts.insert(field.name.clone());
e.remove();
}
}
}
}
}
for ((_struct, method), ret_ty) in &dict.bitfield_method_types {
if conflicts.contains(method) {
continue;
}
let uty = UnifiedType::from_rust_str(ret_ty);
match map.entry(method.clone()) {
std::collections::hash_map::Entry::Vacant(e) => {
e.insert(uty);
}
std::collections::hash_map::Entry::Occupied(e) => {
if e.get() != &uty {
conflicts.insert(method.clone());
e.remove();
}
}
}
}
}
map
}
#[derive(Debug, Clone)]
pub struct CodegenConfig {
pub emit_inline_fns: bool,
pub emit_macros: bool,
pub include_source_location: bool,
pub use_statements: Vec<String>,
pub dump_ast_for: Option<String>,
pub dump_types_for: Option<String>,
}
impl Default for CodegenConfig {
fn default() -> Self {
Self {
emit_inline_fns: true,
emit_macros: true,
include_source_location: true,
use_statements: Vec::new(),
dump_ast_for: None,
dump_types_for: None,
}
}
}
impl CodegenConfig {
pub fn default_use_statements() -> Vec<String> {
vec![
"#[allow(unused_imports)] use std::ffi::{c_void, c_char, c_uchar, c_int, c_uint, c_long, c_ulong, c_short, c_ushort}".to_string(),
"#[allow(non_camel_case_types, dead_code)] type size_t = usize".to_string(),
"#[allow(non_camel_case_types, dead_code)] type ssize_t = isize".to_string(),
"#[allow(non_camel_case_types, dead_code)] type SSize_t = isize".to_string(),
]
}
pub fn with_use_statements(mut self, statements: Vec<String>) -> Self {
self.use_statements = statements;
self
}
pub fn add_use_statement(mut self, statement: impl Into<String>) -> Self {
self.use_statements.push(statement.into());
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GenerateStatus {
Success,
ParseFailed,
TypeIncomplete,
CallsUnavailable,
ContainsGoto,
GenericUnsupported,
Skip,
}
fn stmt_contains_goto(stmt: &Stmt) -> bool {
match stmt {
Stmt::Goto(_, _) => true,
Stmt::Compound(cs) => block_items_contain_goto(&cs.items),
Stmt::If { then_stmt, else_stmt, .. } => {
stmt_contains_goto(then_stmt)
|| else_stmt.as_ref().is_some_and(|s| stmt_contains_goto(s))
}
Stmt::Switch { body, .. }
| Stmt::While { body, .. }
| Stmt::For { body, .. } => stmt_contains_goto(body),
Stmt::DoWhile { body, .. } => stmt_contains_goto(body),
Stmt::Label { stmt, .. } => stmt_contains_goto(stmt),
Stmt::Case { stmt, .. } | Stmt::Default { stmt, .. } => stmt_contains_goto(stmt),
_ => false,
}
}
fn block_items_contain_goto(items: &[BlockItem]) -> bool {
items.iter().any(|item| match item {
BlockItem::Stmt(stmt) => stmt_contains_goto(stmt),
BlockItem::Decl(_) => false,
})
}
fn stmt_contains_top_level_break(stmt: &Stmt) -> bool {
match stmt {
Stmt::Break(_) => true,
Stmt::Compound(cs) => cs.items.iter().any(|item| match item {
BlockItem::Stmt(s) => stmt_contains_top_level_break(s),
BlockItem::Decl(_) => false,
}),
Stmt::If { then_stmt, else_stmt, .. } => {
stmt_contains_top_level_break(then_stmt)
|| else_stmt.as_ref().is_some_and(|s| stmt_contains_top_level_break(s))
}
Stmt::Label { stmt, .. } => stmt_contains_top_level_break(stmt),
_ => false,
}
}
#[derive(Debug, Clone, Default)]
pub struct CodegenStats {
pub macros_success: usize,
pub macros_parse_failed: usize,
pub macros_type_incomplete: usize,
pub macros_calls_unavailable: usize,
pub macros_cascade_unavailable: usize,
pub macros_generic_unsupported: usize,
pub macros_unresolved_names: usize,
pub inline_fns_success: usize,
pub inline_fns_type_incomplete: usize,
pub inline_fns_unresolved_names: usize,
pub inline_fns_cascade_unavailable: usize,
pub inline_fns_contains_goto: usize,
}
#[derive(Debug, Clone)]
pub struct GeneratedCode {
pub code: String,
pub incomplete_count: usize,
pub unresolved_names: Vec<String>,
pub used_libc_fns: HashSet<String>,
pub codegen_errors: Vec<String>,
}
impl GeneratedCode {
pub fn is_complete(&self) -> bool {
self.incomplete_count == 0
}
pub fn has_unresolved_names(&self) -> bool {
!self.unresolved_names.is_empty()
}
}
pub struct RustCodegen<'a> {
interner: &'a StringInterner,
enum_dict: &'a EnumDict,
macro_ctx: &'a MacroInferContext,
bindings_info: BindingsInfo,
buffer: String,
incomplete_count: usize,
current_type_param_map: HashMap<InternedStr, String>,
current_literal_string_params: HashSet<InternedStr>,
current_return_type: Option<UnifiedType>,
param_substitutions: HashMap<InternedStr, String>,
current_param_types: HashMap<InternedStr, UnifiedType>,
known_symbols: &'a KnownSymbols,
current_local_names: HashSet<InternedStr>,
unresolved_names: Vec<String>,
used_libc_fns: HashSet<String>,
rust_decl_dict: Option<&'a RustDeclDict>,
inline_fn_dict: Option<&'a crate::inline_fn::InlineFnDict>,
fields_dict: Option<&'a crate::fields_dict::FieldsDict>,
field_type_map: HashMap<String, UnifiedType>,
dump_ast_for: Option<String>,
dump_types_for: Option<String>,
const_pointer_positions: HashSet<usize>,
mut_local_names: HashSet<InternedStr>,
codegen_errors: Vec<String>,
is_bool_return: bool,
bool_return_macros: HashSet<InternedStr>,
perl_threaded: bool,
}
pub struct CodegenDriver<'a, W: Write> {
writer: W,
interner: &'a StringInterner,
enum_dict: &'a EnumDict,
macro_ctx: &'a MacroInferContext,
bindings_info: BindingsInfo,
config: CodegenConfig,
stats: CodegenStats,
used_libc_fns: HashSet<String>,
successfully_generated_inlines: HashSet<InternedStr>,
generatable_macros: HashSet<InternedStr>,
const_pointer_params: HashMap<InternedStr, HashSet<usize>>,
bool_return_macros: HashSet<InternedStr>,
perl_threaded: bool,
}
impl<'a> RustCodegen<'a> {
pub fn new(
interner: &'a StringInterner,
enum_dict: &'a EnumDict,
macro_ctx: &'a MacroInferContext,
bindings_info: BindingsInfo,
known_symbols: &'a KnownSymbols,
rust_decl_dict: Option<&'a RustDeclDict>,
inline_fn_dict: Option<&'a crate::inline_fn::InlineFnDict>,
) -> Self {
Self {
interner,
enum_dict,
macro_ctx,
bindings_info,
buffer: String::new(),
incomplete_count: 0,
current_type_param_map: HashMap::new(),
current_literal_string_params: HashSet::new(),
current_return_type: None,
param_substitutions: HashMap::new(),
current_param_types: HashMap::new(),
known_symbols,
current_local_names: HashSet::new(),
unresolved_names: Vec::new(),
used_libc_fns: HashSet::new(),
rust_decl_dict,
inline_fn_dict,
fields_dict: None,
field_type_map: build_field_type_map(rust_decl_dict),
dump_ast_for: None,
dump_types_for: None,
const_pointer_positions: HashSet::new(),
is_bool_return: false,
bool_return_macros: HashSet::new(),
mut_local_names: HashSet::new(),
codegen_errors: Vec::new(),
perl_threaded: true,
}
}
pub fn with_perl_threaded(mut self, threaded: bool) -> Self {
self.perl_threaded = threaded;
self
}
pub fn with_dump_ast_for(mut self, name: Option<String>) -> Self {
self.dump_ast_for = name;
self
}
pub fn with_dump_types_for(mut self, name: Option<String>) -> Self {
self.dump_types_for = name;
self
}
pub fn with_fields_dict(mut self, dict: &'a crate::fields_dict::FieldsDict) -> Self {
self.fields_dict = Some(dict);
for (_name, def) in dict.iter_struct_defs() {
for m in &def.members {
let member_name = self.interner.get(m.name).to_string();
if self.field_type_map.contains_key(&member_name) {
continue;
}
let rust_ty = m.type_repr.to_rust_string(self.interner);
self.field_type_map
.insert(member_name, UnifiedType::from_rust_str(&rust_ty));
}
}
self
}
pub fn with_const_pointer_positions(mut self, positions: HashSet<usize>) -> Self {
self.const_pointer_positions = positions;
self
}
pub fn with_bool_return(mut self, is_bool: bool, bool_macros: HashSet<InternedStr>) -> Self {
self.is_bool_return = is_bool;
self.bool_return_macros = bool_macros;
self
}
fn dump_type_info(&self, name_str: &str, info: &MacroInferInfo, params_str: &str, return_type: &str) {
eprintln!("=== Type dump for {} ===", name_str);
for (i, p) in info.params.iter().enumerate() {
let pname = self.interner.get(p.name);
let is_const = info.const_pointer_positions.contains(&i);
let expr_ids: Vec<_> = info.type_env.param_to_exprs
.get(&p.name)
.map(|ids| ids.iter().cloned().collect())
.unwrap_or_default();
let mut all_ids = expr_ids;
all_ids.push(p.expr_id());
eprintln!(" param[{}] {} (const_position={})", i, pname, is_const);
for eid in &all_ids {
if let Some(constraints) = info.type_env.expr_constraints.get(eid) {
for c in constraints {
eprintln!(" constraint: tier={} rust={} context={} source={:?}",
c.ty.confidence_tier(),
c.ty.to_rust_string(self.interner),
c.context,
match &c.ty {
crate::type_repr::TypeRepr::CType { source, .. } => format!("{:?}", source),
crate::type_repr::TypeRepr::RustType { source, .. } => format!("{:?}", source),
crate::type_repr::TypeRepr::Inferred(i) => format!("Inferred({:?})", std::mem::discriminant(i)),
}
);
}
}
}
}
eprintln!(" params_str: {}", params_str);
eprintln!(" return_type: {}", return_type);
eprintln!(" is_bool_return: {}", info.is_bool_return);
if let Some(ty) = info.get_return_type() {
eprintln!(" return TypeRepr: tier={} rust={}", ty.confidence_tier(), ty.to_rust_string(self.interner));
}
if !info.type_env.return_constraints.is_empty() {
eprintln!(" return_constraints:");
for c in &info.type_env.return_constraints {
eprintln!(" tier={} rust={} context={}", c.ty.confidence_tier(), c.ty.to_rust_string(self.interner), c.context);
}
}
if let ParseResult::Expression(ref expr) = info.parse_result {
if let Some(constraints) = info.type_env.expr_constraints.get(&expr.id) {
eprintln!(" root expr constraints:");
for c in constraints {
eprintln!(" tier={} rust={} context={}",
c.ty.confidence_tier(),
c.ty.to_rust_string(self.interner),
c.context,
);
}
}
}
eprintln!("=== End type dump ===");
}
fn dump_ast_comment_for_expr(&mut self, name_str: &str, parse_result: &ParseResult) {
if self.dump_ast_for.as_deref() != Some(name_str) {
return;
}
let sexp = match parse_result {
ParseResult::Expression(expr) => {
let mut buf = Vec::new();
let mut printer = SexpPrinter::new(&mut buf, self.interner);
let _ = printer.print_expr(expr);
String::from_utf8_lossy(&buf).into_owned()
}
ParseResult::Statement(block_items) => {
let mut buf = Vec::new();
let mut printer = SexpPrinter::new(&mut buf, self.interner);
for item in block_items {
if let BlockItem::Stmt(stmt) = item {
let _ = printer.print_stmt(stmt);
} else if let BlockItem::Decl(decl) = item {
let _ = printer.print_declaration(decl);
}
}
String::from_utf8_lossy(&buf).into_owned()
}
ParseResult::Unparseable(msg) => {
format!("(unparseable: {})", msg.as_deref().unwrap_or("unknown"))
}
};
self.writeln(&format!("// [AST dump for {}]", name_str));
for line in sexp.lines() {
self.writeln(&format!("// {}", line));
}
}
fn dump_ast_comment_for_body(&mut self, name_str: &str, body: &CompoundStmt) {
if self.dump_ast_for.as_deref() != Some(name_str) {
return;
}
let mut buf = Vec::new();
let mut printer = SexpPrinter::new(&mut buf, self.interner);
for item in &body.items {
match item {
BlockItem::Stmt(stmt) => { let _ = printer.print_stmt(stmt); }
BlockItem::Decl(decl) => { let _ = printer.print_declaration(decl); }
}
}
let sexp = String::from_utf8_lossy(&buf).into_owned();
self.writeln(&format!("// [AST dump for {}]", name_str));
for line in sexp.lines() {
self.writeln(&format!("// {}", line));
}
}
fn try_expand_call_as_lvalue_syn(&mut self, func: &Expr, args: &[Expr],
info: Option<&MacroInferInfo>) -> Option<syn::Expr> {
if let ExprKind::Ident(name) = &func.kind {
if self.should_emit_as_macro_call(*name) {
if let Some(macro_info) = self.macro_ctx.macros.get(name) {
if let ParseResult::Expression(body) = ¯o_info.parse_result {
let body = body.clone();
let saved_params = std::mem::take(&mut self.param_substitutions);
for (i, param) in macro_info.params.iter().enumerate() {
if let Some(arg) = args.get(i) {
let arg_syn = self.build_syn_expr(arg, info);
let arg_str = crate::syn_codegen::expr_to_string(&arg_syn);
self.param_substitutions.insert(param.name, arg_str);
}
}
let body_syn = self.build_syn_expr(&body, info);
self.param_substitutions = saved_params;
return Some(body_syn);
}
}
}
}
None
}
fn wrap_as_bool_condition_inline(&self, expr: &Expr, expr_str: &str) -> String {
self.wrap_as_bool_condition(expr, expr_str, None)
}
fn infer_expr_type_inline(&self, expr: &Expr) -> Option<UnifiedType> {
self.infer_expr_type_unified(expr, None)
}
fn type_name_to_type_str_readonly(&self, type_name: &crate::ast::TypeName) -> String {
let pointer_count = type_name.declarator.as_ref()
.map(|d| d.derived.iter().filter(|dd| matches!(dd, crate::ast::DerivedDecl::Pointer(_))).count())
.unwrap_or(0);
let is_const_ptr = pointer_count == 1 && type_name.specs.qualifiers.is_const;
let base = self.base_type_str_readonly(&type_name.specs.type_specs);
let mut result = base;
for _ in 0..pointer_count {
let prefix = if is_const_ptr { "*const " } else { "*mut " };
result = format!("{}{}", prefix, result);
}
result
}
fn base_type_str_readonly(&self, type_specs: &[TypeSpec]) -> String {
for spec in type_specs {
if let TypeSpec::TypedefName(name) = spec {
return self.interner.get(*name).to_string();
}
}
for spec in type_specs {
match spec {
TypeSpec::Struct(s) | TypeSpec::Union(s) => {
if let Some(n) = &s.name {
return self.interner.get(*n).to_string();
}
}
TypeSpec::Enum(e) => {
if let Some(n) = &e.name {
return self.interner.get(*n).to_string();
}
}
_ => {}
}
}
let mut is_void = false;
let mut is_char = false;
let mut is_int = false;
let mut is_short = false;
let mut is_long = 0usize;
let mut is_unsigned = false;
for spec in type_specs {
match spec {
TypeSpec::Void => is_void = true,
TypeSpec::Char => is_char = true,
TypeSpec::Int => is_int = true,
TypeSpec::Short => is_short = true,
TypeSpec::Long => is_long += 1,
TypeSpec::Unsigned => is_unsigned = true,
TypeSpec::Signed => {}
TypeSpec::Bool => return "bool".to_string(),
_ => {}
}
}
if is_void { return "c_void".to_string(); }
if is_char { return if is_unsigned { "c_uchar".to_string() } else { "c_char".to_string() }; }
if is_short { return if is_unsigned { "c_ushort".to_string() } else { "c_short".to_string() }; }
if is_long >= 2 { return if is_unsigned { "c_ulonglong".to_string() } else { "c_longlong".to_string() }; }
if is_long == 1 { return if is_unsigned { "c_ulong".to_string() } else { "c_long".to_string() }; }
if is_int || is_unsigned { return if is_unsigned { "c_uint".to_string() } else { "c_int".to_string() }; }
"c_int".to_string()
}
fn infer_expr_type(&self, expr: &Expr, info: &MacroInferInfo) -> Option<UnifiedType> {
self.infer_expr_type_unified(expr, Some(info))
}
fn infer_expr_type_unified(&self, expr: &Expr, info: Option<&MacroInferInfo>) -> Option<UnifiedType> {
match &expr.kind {
ExprKind::Ident(name) => {
if let Some(ut) = self.current_param_types.get(name) {
return Some(ut.clone());
}
if let Some(info) = info {
if let Some(expr_ids) = info.type_env.param_to_exprs.get(name) {
let mut best: Option<(UnifiedType, u8)> = None;
for expr_id in expr_ids {
if let Some(constraints) = info.type_env.expr_constraints.get(expr_id) {
for c in constraints {
if c.ty.is_void() { continue; }
let tier = c.ty.confidence_tier();
if best.is_none() || tier < best.as_ref().unwrap().1 {
best = Some((UnifiedType::from_rust_str(&c.ty.to_rust_string(self.interner)), tier));
}
}
}
}
if let Some((ut, _)) = best {
return Some(ut);
}
}
if let Some(constraints) = info.type_env.param_constraints.get(name) {
let mut best: Option<(UnifiedType, u8)> = None;
for c in constraints {
if c.ty.is_void() { continue; }
let tier = c.ty.confidence_tier();
if best.is_none() || tier < best.as_ref().unwrap().1 {
best = Some((UnifiedType::from_rust_str(&c.ty.to_rust_string(self.interner)), tier));
}
}
if let Some((ut, _)) = best {
return Some(ut);
}
}
}
if let Some(dict) = self.rust_decl_dict {
let name_str = self.interner.get(*name);
if let Some(c) = dict.consts.get(name_str) {
return Some(c.uty.clone());
}
if let Some(ty_str) = dict.static_types.get(name_str) {
return Some(UnifiedType::from_rust_str(ty_str));
}
}
if let Some(enum_name) = self.enum_dict.get_enum_for_variant(*name) {
let enum_str = self.interner.get(enum_name).to_string();
return Some(UnifiedType::Named(enum_str));
}
None
}
ExprKind::Cast { type_name, .. } => {
Some(UnifiedType::from_rust_str(&self.type_name_to_type_str_readonly(type_name)))
}
ExprKind::Member { expr: base, member } | ExprKind::PtrMember { expr: base, member } => {
let resolve_struct_name = |this: &Self| -> Option<String> {
if let Some(info_ref) = info {
if let Some(constraints) = info_ref.type_env.expr_constraints.get(&base.id) {
if let Some(base_ty) = constraints.first().map(|c| &c.ty) {
let sn = if matches!(&expr.kind, ExprKind::PtrMember { .. }) {
base_ty.pointee_name()
} else {
base_ty.type_name()
};
if let Some(n) = sn {
return Some(this.interner.get(n).to_string());
}
}
}
}
let base_ut = this.infer_expr_type_unified(base, info)?;
let target = if matches!(&expr.kind, ExprKind::PtrMember { .. }) {
base_ut.inner_type()?
} else {
&base_ut
};
if let UnifiedType::Named(n) = target {
return Some(n.clone());
}
None
};
let member_str = self.interner.get(*member);
let fd_ty: Option<UnifiedType> = self.fields_dict.and_then(|fd| {
let struct_name_str = resolve_struct_name(self)?;
let struct_name = self.interner.lookup(&struct_name_str)?;
let member_ty = fd.member_type(struct_name, *member)?;
let rust_ty = member_ty.to_rust_string(self.interner);
if rust_ty.contains("/* fn */") {
return None;
}
Some(UnifiedType::from_rust_str(&rust_ty))
});
let ftm_ty = self.field_type_map.get(member_str).cloned();
match (&fd_ty, &ftm_ty) {
(Some(fd_ut), Some(ftm_ut))
if fd_ut.is_pointer() && ftm_ut.to_rust_string().starts_with('[') =>
{
return ftm_ty;
}
_ => {}
}
fd_ty.or(ftm_ty)
}
ExprKind::Deref(inner) => {
let inner_ut = self.infer_expr_type_unified(inner, info)?;
inner_ut.inner_type().cloned()
}
ExprKind::Index { expr: base, .. } => {
let base_ut = self.infer_expr_type_unified(base, info)?;
base_ut.inner_type().cloned()
}
ExprKind::Binary { op, lhs, rhs } => {
match op {
BinOp::Shl | BinOp::Shr => self.infer_expr_type_unified(lhs, info),
BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor => {
let lt = self.infer_expr_type_unified(lhs, info);
let rt = self.infer_expr_type_unified(rhs, info);
match (<, &rt) {
(Some(l), Some(r)) => {
let ls = l.to_rust_string();
let rs = r.to_rust_string();
wider_integer_type(&ls, &rs)
.map(|w| UnifiedType::from_rust_str(w))
.or(lt)
}
(Some(_), None) => lt,
(None, Some(_)) => rt,
_ => None,
}
}
BinOp::Eq | BinOp::Ne | BinOp::Lt | BinOp::Gt
| BinOp::Le | BinOp::Ge | BinOp::LogAnd | BinOp::LogOr => {
Some(UnifiedType::Bool)
}
_ => {
if *op == BinOp::Sub {
let lp = self.is_pointer_expr_unified(lhs, info)
|| self.infer_expr_type_unified(lhs, info).is_some_and(|ut| ut.is_pointer());
let rp = self.is_pointer_expr_unified(rhs, info)
|| self.infer_expr_type_unified(rhs, info).is_some_and(|ut| ut.is_pointer());
if lp && rp {
return Some(UnifiedType::Named("isize".to_string()));
}
}
let lt = self.infer_expr_type_unified(lhs, info);
if lt.is_some() { return lt; }
self.infer_expr_type_unified(rhs, info)
}
}
}
ExprKind::BitNot(inner) | ExprKind::UnaryMinus(inner) => self.infer_expr_type_unified(inner, info),
ExprKind::CharLit(_) => Some(UnifiedType::from_rust_str("i8")),
ExprKind::UIntLit(_) => Some(UnifiedType::Int { signed: false, size: crate::unified_type::IntSize::LongLong }),
ExprKind::Sizeof(_) | ExprKind::SizeofType(_) => Some(UnifiedType::Named("usize".to_string())),
ExprKind::Call { func, .. } => {
if let ExprKind::Member { expr: receiver, member, .. } = &func.kind {
let method_name = self.interner.get(*member);
if matches!(method_name, "offset" | "wrapping_add" | "wrapping_sub" | "wrapping_offset") {
return self.infer_expr_type_unified(receiver, info);
}
}
if let ExprKind::Ident(name) = &func.kind {
let func_name = self.interner.get(*name);
if let Some(ret_ut) = self.get_callee_return_type(func_name) {
return Some(ret_ut.clone());
}
if let Some(macro_info) = self.macro_ctx.macros.get(name) {
if let Some(ty) = macro_info.get_return_type() {
return Some(UnifiedType::from_rust_str(&ty.to_rust_string(self.interner)));
}
}
if let Some(dict) = self.inline_fn_dict {
if let Some(func_def) = dict.get(*name) {
let base = self.base_type_str_readonly(&func_def.specs.type_specs);
let mut result = base;
let pointer_count = func_def.declarator.derived.iter()
.take_while(|d| !matches!(d, crate::ast::DerivedDecl::Function(_)))
.filter(|d| matches!(d, crate::ast::DerivedDecl::Pointer(_)))
.count();
let is_const_ptr = pointer_count == 1
&& func_def.specs.qualifiers.is_const;
for _ in 0..pointer_count {
let prefix = if is_const_ptr { "*const " } else { "*mut " };
result = format!("{}{}", prefix, result);
}
return Some(UnifiedType::from_rust_str(&result));
}
}
}
None
}
ExprKind::MacroCall { name, expanded, .. } => {
if let Some(macro_info) = self.macro_ctx.macros.get(name) {
if let Some(ty) = macro_info.get_return_type() {
return Some(UnifiedType::from_rust_str(&ty.to_rust_string(self.interner)));
}
}
self.infer_expr_type_unified(expanded, info)
}
ExprKind::Conditional { then_expr, else_expr, .. } => {
if is_null_literal(then_expr) {
return self.infer_expr_type_unified(else_expr, info);
}
if is_null_literal(else_expr) {
return self.infer_expr_type_unified(then_expr, info);
}
let tt = self.infer_expr_type_unified(then_expr, info);
let et = self.infer_expr_type_unified(else_expr, info);
match (&tt, &et) {
(Some(t), Some(e)) if t.is_void_pointer() && e.is_concrete_pointer() => et,
(Some(t), Some(e)) if e.is_void_pointer() && t.is_concrete_pointer() => tt,
(Some(_), _) => tt,
(None, _) => et,
}
}
_ => None,
}
}
fn is_pointer_expr_unified(&self, expr: &Expr, info: Option<&MacroInferInfo>) -> bool {
match &expr.kind {
ExprKind::Ident(name) => {
if let Some(ut) = self.current_param_types.get(name) {
return ut.is_pointer();
}
if let Some(info) = info {
if let Some(constraints) = info.type_env.param_constraints.get(name) {
for c in constraints {
if is_type_repr_pointer(&c.ty) {
return true;
}
}
}
if let Some(expr_ids) = info.type_env.param_to_exprs.get(name) {
for expr_id in expr_ids {
if let Some(constraints) = info.type_env.expr_constraints.get(expr_id) {
for c in constraints {
if is_type_repr_pointer(&c.ty) {
return true;
}
}
}
}
}
}
false
}
ExprKind::Cast { type_name, .. } => {
type_name.declarator.as_ref()
.map(|d| d.derived.iter().any(|dd| matches!(dd, crate::ast::DerivedDecl::Pointer { .. })))
.unwrap_or(false)
}
ExprKind::AddrOf(_) => true,
ExprKind::Member { member, .. } | ExprKind::PtrMember { member, .. } => {
let member_str = self.interner.get(*member);
self.field_type_map.get(member_str).is_some_and(|ut| ut.is_pointer())
}
ExprKind::Deref(inner) => {
if let Some(ut) = self.infer_expr_type_unified(inner, info) {
if let Some(derefed) = ut.inner_type() {
return derefed.is_pointer();
}
}
false
}
ExprKind::Call { func, .. } | ExprKind::MacroCall { expanded: func, .. } => {
let check_func = match &expr.kind {
ExprKind::MacroCall { name, .. } => {
if let Some(callee) = self.macro_ctx.macros.get(name) {
for c in &callee.type_env.return_constraints {
if is_type_repr_pointer(&c.ty) { return true; }
}
}
func
}
_ => func,
};
if let ExprKind::Ident(name) = &check_func.kind {
if let Some(callee) = self.macro_ctx.macros.get(name) {
for c in &callee.type_env.return_constraints {
if is_type_repr_pointer(&c.ty) { return true; }
}
}
if let Some(ret_ut) = self.get_callee_return_type(self.interner.get(*name)) {
return ret_ut.is_pointer();
}
}
false
}
ExprKind::Binary { op, lhs, rhs } => {
match op {
BinOp::Add => self.is_pointer_expr_unified(lhs, info) || self.is_pointer_expr_unified(rhs, info),
BinOp::Sub => self.is_pointer_expr_unified(lhs, info) && !self.is_pointer_expr_unified(rhs, info),
_ => false,
}
}
_ => false,
}
}
fn is_option_fn_pointer_expr(&self, expr: &Expr, info: Option<&MacroInferInfo>) -> bool {
if let ExprKind::Member { member, .. } | ExprKind::PtrMember { member, .. } = &expr.kind {
let member_str = self.interner.get(*member);
if let Some(ut) = self.field_type_map.get(member_str) {
let ty_str = ut.to_rust_string();
if type_str_is_fn_pointer(&ty_str) {
return true;
}
if let Some(dict) = self.rust_decl_dict {
if let Some(alias) = dict.types.get(&ty_str) {
if type_str_is_fn_pointer(&alias.ty) {
return true;
}
}
}
}
}
let Some(ut) = self.infer_expr_type_unified(expr, info) else { return false };
let ty_str = ut.to_rust_string();
if type_str_is_fn_pointer(&ty_str) {
return true;
}
if let Some(dict) = self.rust_decl_dict {
if let Some(alias) = dict.types.get(&ty_str) {
if type_str_is_fn_pointer(&alias.ty) {
return true;
}
}
}
false
}
fn wrap_as_bool_condition(&self, expr: &Expr, expr_str: &str, info: Option<&MacroInferInfo>) -> String {
if self.is_bool_expr_with_dict(expr) {
return expr_str.to_string();
}
if let ExprKind::Ident(name) = &expr.kind {
if let Some(ut) = self.current_param_types.get(name) {
if ut.is_bool() {
return expr_str.to_string();
}
}
}
if let ExprKind::Member { member, .. } | ExprKind::PtrMember { member, .. } = &expr.kind {
let member_str = self.interner.get(*member);
if let Some(ut) = self.field_type_map.get(member_str) {
if ut.is_bool() {
return expr_str.to_string();
}
}
}
if let ExprKind::Call { func, args, .. } = &expr.kind {
if let ExprKind::Ident(name) = &func.kind {
if self.interner.get(*name) == "__builtin_expect" && !args.is_empty() {
return self.wrap_as_bool_condition(&args[0], expr_str, info);
}
}
}
if expr_str.ends_with(" as bool)") || expr_str.ends_with("!= 0)") || expr_str.ends_with(".is_null()") {
return expr_str.to_string();
}
if self.is_option_fn_pointer_expr(expr, info) {
return format!("{}.is_some()", expr_str);
}
if self.is_pointer_expr_unified(expr, info)
|| self.infer_expr_type_unified(expr, info).is_some_and(|ut| ut.is_pointer()) {
return format!("!({}).is_null()", expr_str);
}
format!("({} != 0)", strip_outer_parens(expr_str))
}
fn needs_my_perl_for_call(&self, func_name: crate::InternedStr, actual_arg_count: usize) -> bool {
if !self.perl_threaded {
return false;
}
if let Some(callee_info) = self.macro_ctx.macros.get(&func_name) {
if callee_info.is_thx_dependent {
let expected_count = callee_info.params.len() + 1;
return actual_arg_count + 1 == expected_count;
}
}
false
}
fn is_static_array_expr(&self, expr: &Expr) -> bool {
if let ExprKind::Ident(name) = &expr.kind {
let name_str = self.interner.get(*name);
self.bindings_info.static_arrays.contains(name_str)
} else {
false
}
}
fn is_array_like_expr(&self, expr: &Expr, info: Option<&MacroInferInfo>) -> bool {
if self.is_static_array_expr(expr) {
return true;
}
if let ExprKind::Member { expr: base, member }
| ExprKind::PtrMember { expr: base, member } = &expr.kind
{
if let (Some(fd), Some(info)) = (self.fields_dict, info) {
if let Some(constraints) = info.type_env.expr_constraints.get(&base.id) {
if let Some(base_type) = constraints.first().map(|c| &c.ty) {
let struct_name = match &expr.kind {
ExprKind::PtrMember { .. } => base_type.pointee_name(),
_ => base_type.type_name(),
};
if let Some(sn) = struct_name {
if fd.is_flexible_array_field(sn, *member) {
return false;
}
}
}
}
}
}
if let Some(ut) = self.infer_expr_type_unified(expr, info) {
let s = ut.to_rust_string();
if s.starts_with('[') && s.contains(';') {
return true;
}
}
false
}
fn is_bitfield_method(&self, member_name: &str) -> bool {
self.bindings_info.bitfield_methods.values()
.any(|methods| methods.contains(member_name))
}
fn get_callee_generic_params(&self, func_name: InternedStr) -> Option<&HashMap<i32, String>> {
let callee_info = self.macro_ctx.macros.get(&func_name)?;
if callee_info.generic_type_params.is_empty() {
return None;
}
if callee_info.generic_type_params.keys().any(|&k| k >= 0) {
Some(&callee_info.generic_type_params)
} else {
None
}
}
fn is_enum_cast_target(&self, type_name: &crate::ast::TypeName) -> bool {
for spec in &type_name.specs.type_specs {
match spec {
TypeSpec::TypedefName(name) => return self.enum_dict.is_target_enum(*name),
TypeSpec::Enum(_) => return true,
_ => {}
}
}
false
}
fn callee_expects_literal_string(&self, func_name: InternedStr, arg_index: usize) -> bool {
if let Some(callee_info) = self.macro_ctx.macros.get(&func_name) {
return callee_info.literal_string_params.contains(&arg_index);
}
false
}
fn get_callee_param_type(&self, func_name: &str, arg_index: usize) -> Option<&UnifiedType> {
self.rust_decl_dict?.fns.get(func_name).and_then(|f| {
f.params.get(arg_index).map(|p| &p.uty)
})
}
fn get_callee_param_type_extended(&mut self, func_name: &str, arg_index: usize) -> Option<UnifiedType> {
if let Some(ut) = libc_fn_param_type(func_name, arg_index) {
return Some(ut);
}
if let Some(ut) = self.get_callee_param_type(func_name, arg_index) {
return Some(ut.clone());
}
if let Some(interned) = self.interner.lookup(func_name) {
if let Some(dict) = self.inline_fn_dict {
if let Some(func_def) = dict.get(interned) {
for d in &func_def.declarator.derived {
if let DerivedDecl::Function(param_list) = d {
if let Some(param) = param_list.params.get(arg_index) {
let ty = self.param_type_only(param);
return Some(UnifiedType::from_rust_str(&ty));
}
break;
}
}
}
}
if let Some(macro_info) = self.macro_ctx.macros.get(&interned) {
let macro_param_idx = if self.perl_threaded && macro_info.is_thx_dependent {
if arg_index == 0 {
return Some(UnifiedType::from_rust_str("*mut PerlInterpreter"));
}
arg_index - 1
} else {
arg_index
};
if let Some(param) = macro_info.params.get(macro_param_idx) {
if let Some(mut ty) = best_constraint_for_macro_param(macro_info, param) {
let should_be_const = macro_info
.const_pointer_positions
.contains(¯o_param_idx);
if should_be_const {
ty.make_outer_pointer_const();
} else if ty.has_outer_pointer() {
ty.make_outer_pointer_mut();
}
let rust_ty = ty.to_rust_string(self.interner);
return Some(UnifiedType::from_rust_str(&rust_ty));
}
}
}
}
None
}
fn callee_param_is_bool(&self, func_name: &str, arg_index: usize) -> bool {
if let Some(param_ut) = self.get_callee_param_type(func_name, arg_index) {
return param_ut.is_bool();
}
if let Some(interned) = self.interner.lookup(func_name) {
if let Some(macro_info) = self.macro_ctx.macros.get(&interned) {
let macro_arg_index = if self.perl_threaded
&& macro_info.is_thx_dependent
&& arg_index > 0
{
arg_index - 1
} else {
arg_index
};
if let Some(param) = macro_info.params.get(macro_arg_index) {
if let Some(expr_ids) = macro_info.type_env.param_to_exprs.get(¶m.name) {
for expr_id in expr_ids {
if let Some(constraints) = macro_info.type_env.expr_constraints.get(expr_id) {
for c in constraints {
let rust_ty = c.ty.to_rust_string(self.interner);
if rust_ty == "bool" {
return true;
}
}
}
}
}
}
}
if let Some(dict) = self.inline_fn_dict {
if let Some(func_def) = dict.get(interned) {
for d in &func_def.declarator.derived {
if let DerivedDecl::Function(param_list) = d {
if let Some(param) = param_list.params.get(arg_index) {
let has_bool = param.specs.type_specs.iter().any(|ts| matches!(ts, TypeSpec::Bool));
let has_pointer = param.declarator.as_ref().map_or(false, |decl| {
decl.derived.iter().any(|d| matches!(d, DerivedDecl::Pointer(_)))
});
if has_bool && !has_pointer {
return true;
}
}
break;
}
}
}
}
}
false
}
fn get_callee_return_type(&self, func_name: &str) -> Option<&UnifiedType> {
self.rust_decl_dict?.fns.get(func_name).and_then(|f| {
f.uret_ty.as_ref()
})
}
fn is_rust_enum_type(&self, ut: &UnifiedType) -> bool {
if let UnifiedType::Named(name) = ut {
if let Some(dict) = self.rust_decl_dict {
return dict.enums.contains(name.as_str());
}
}
false
}
fn is_bool_expr_with_dict(&self, expr: &Expr) -> bool {
if is_boolean_expr_recursive(expr, self.interner) {
return true;
}
if let ExprKind::Index { expr: base, .. } = &expr.kind {
if let ExprKind::Ident(name) = &base.kind {
if let Some(dict) = self.rust_decl_dict {
let name_str = self.interner.get(*name);
if let Some(c) = dict.consts.get(name_str) {
if let Some(inner) = c.uty.inner_type() {
if inner.is_bool() {
return true;
}
}
}
if dict.statics.contains(name_str) && name_str.starts_with("PL_valid_types_") {
return true;
}
}
}
}
if let ExprKind::Ident(name) = &expr.kind {
if let Some(ut) = self.current_param_types.get(name) {
if ut.is_bool() {
return true;
}
}
}
if let ExprKind::Member { member, .. } | ExprKind::PtrMember { member, .. } = &expr.kind {
let member_str = self.interner.get(*member);
if let Some(ut) = self.field_type_map.get(member_str) {
if ut.is_bool() {
return true;
}
}
}
if let ExprKind::Call { func, .. } = &expr.kind {
if let ExprKind::Ident(name) = &func.kind {
if self.bool_return_macros.contains(name) {
return true;
}
let func_name = self.interner.get(*name);
if let Some(ret_ut) = self.get_callee_return_type(func_name) {
return ret_ut.is_bool();
}
if let Some(macro_info) = self.macro_ctx.macros.get(name) {
if let Some(ty) = macro_info.get_return_type() {
let rust_ty = ty.to_rust_string(self.interner);
return rust_ty == "bool";
}
}
if let Some(dict) = self.inline_fn_dict {
if let Some(func_def) = dict.get(*name) {
let has_bool_return = func_def.specs.type_specs.iter().any(|ts| matches!(ts, TypeSpec::Bool));
let has_return_pointer = func_def.declarator.derived.iter().any(|d| matches!(d, DerivedDecl::Pointer(_)));
if has_bool_return && !has_return_pointer {
return true;
}
}
}
}
}
if let ExprKind::MacroCall { name, .. } = &expr.kind {
if self.bool_return_macros.contains(name) {
return true;
}
}
false
}
fn find_literal_string_ident<'b>(&self, expr: &'b Expr) -> Option<&'b InternedStr> {
match &expr.kind {
ExprKind::Ident(name) if self.current_literal_string_params.contains(name) => {
Some(name)
}
ExprKind::Call { func, args } if args.len() == 1 => {
if let ExprKind::Ident(fname) = &func.kind {
let func_name = self.interner.get(*fname);
if func_name == "ASSERT_IS_LITERAL"
|| func_name == "ASSERT_IS_PTR"
|| func_name == "ASSERT_NOT_PTR"
{
return self.find_literal_string_ident(&args[0]);
}
}
None
}
_ => None,
}
}
fn should_emit_as_macro_call(&self, name: crate::InternedStr) -> bool {
if let Some(info) = self.macro_ctx.macros.get(&name) {
return info.is_parseable() && !info.calls_unavailable;
}
false
}
fn unknown_marker(&mut self) -> &'static str {
self.incomplete_count += 1;
"/* unknown */"
}
fn todo_marker(&mut self, msg: &str) -> String {
self.incomplete_count += 1;
format!("/* TODO: {} */", msg)
}
fn type_marker(&mut self) -> &'static str {
self.incomplete_count += 1;
"/* type */"
}
fn collect_decl_names(&mut self, decl: &Declaration) {
let base_type = self.decl_specs_to_rust(&decl.specs);
for init_decl in &decl.declarators {
if let Some(name) = init_decl.declarator.name {
self.current_local_names.insert(name);
let ty = self.apply_derived_to_type(&base_type, &init_decl.declarator.derived);
self.current_param_types.insert(name, UnifiedType::from_rust_str(&ty));
}
}
}
fn collect_local_names_recursive(&mut self, body: &CompoundStmt) {
for item in &body.items {
self.collect_local_names_from_block_item(item);
}
}
fn collect_local_names_from_block_item(&mut self, item: &BlockItem) {
match item {
BlockItem::Decl(d) => self.collect_decl_names(d),
BlockItem::Stmt(s) => self.collect_local_names_from_stmt(s),
}
}
fn collect_local_names_from_stmt(&mut self, stmt: &Stmt) {
match stmt {
Stmt::Compound(c) => self.collect_local_names_recursive(c),
Stmt::If { then_stmt, else_stmt, cond, .. } => {
self.collect_local_names_from_expr(cond);
self.collect_local_names_from_stmt(then_stmt);
if let Some(es) = else_stmt {
self.collect_local_names_from_stmt(es);
}
}
Stmt::Switch { body, expr, .. } => {
self.collect_local_names_from_expr(expr);
self.collect_local_names_from_stmt(body);
}
Stmt::While { body, cond, .. } => {
self.collect_local_names_from_expr(cond);
self.collect_local_names_from_stmt(body);
}
Stmt::DoWhile { body, cond, .. } => {
self.collect_local_names_from_stmt(body);
self.collect_local_names_from_expr(cond);
}
Stmt::For { init, cond, step, body, .. } => {
if let Some(i) = init {
match i {
ForInit::Decl(d) => self.collect_decl_names(d),
ForInit::Expr(e) => self.collect_local_names_from_expr(e),
}
}
if let Some(c) = cond { self.collect_local_names_from_expr(c); }
if let Some(s) = step { self.collect_local_names_from_expr(s); }
self.collect_local_names_from_stmt(body);
}
Stmt::Expr(Some(e), _) => self.collect_local_names_from_expr(e),
Stmt::Return(Some(e), _) => self.collect_local_names_from_expr(e),
Stmt::Label { stmt, .. } | Stmt::Case { stmt, .. } | Stmt::Default { stmt, .. } => {
self.collect_local_names_from_stmt(stmt);
}
_ => {}
}
}
fn collect_local_names_from_expr(&mut self, expr: &Expr) {
if let ExprKind::StmtExpr(c) = &expr.kind {
self.collect_local_names_recursive(c);
}
match &expr.kind {
ExprKind::Binary { lhs, rhs, .. }
| ExprKind::Assign { lhs, rhs, .. }
| ExprKind::Comma { lhs, rhs } => {
self.collect_local_names_from_expr(lhs);
self.collect_local_names_from_expr(rhs);
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
self.collect_local_names_from_expr(cond);
self.collect_local_names_from_expr(then_expr);
self.collect_local_names_from_expr(else_expr);
}
ExprKind::Cast { expr: e, .. }
| ExprKind::AddrOf(e) | ExprKind::Deref(e)
| ExprKind::UnaryPlus(e) | ExprKind::UnaryMinus(e)
| ExprKind::BitNot(e) | ExprKind::LogNot(e)
| ExprKind::PreInc(e) | ExprKind::PreDec(e)
| ExprKind::PostInc(e) | ExprKind::PostDec(e)
| ExprKind::Sizeof(e)
| ExprKind::Member { expr: e, .. } | ExprKind::PtrMember { expr: e, .. } => {
self.collect_local_names_from_expr(e);
}
ExprKind::Call { func, args } => {
self.collect_local_names_from_expr(func);
for a in args { self.collect_local_names_from_expr(a); }
}
ExprKind::Index { expr: e, index } => {
self.collect_local_names_from_expr(e);
self.collect_local_names_from_expr(index);
}
_ => {}
}
}
fn collect_decl_types(&mut self, decl: &Declaration) {
let base_type = self.decl_specs_to_rust(&decl.specs);
for init_decl in &decl.declarators {
if let Some(name) = init_decl.declarator.name {
let ty = self.apply_derived_to_type(&base_type, &init_decl.declarator.derived);
self.current_param_types.insert(name, UnifiedType::from_rust_str(&ty));
}
}
}
fn writeln(&mut self, s: &str) {
self.buffer.push_str(s);
self.buffer.push('\n');
}
fn into_generated_code(self) -> GeneratedCode {
GeneratedCode {
code: self.buffer,
incomplete_count: self.incomplete_count,
unresolved_names: self.unresolved_names,
used_libc_fns: self.used_libc_fns,
codegen_errors: self.codegen_errors,
}
}
pub fn generate_macro(mut self, info: &MacroInferInfo) -> GeneratedCode {
let name_str = self.interner.get(info.name);
for p in &info.params {
self.current_local_names.insert(p.name);
}
self.current_type_param_map = info.generic_type_params.iter()
.filter(|(idx, _)| **idx >= 0)
.filter_map(|(idx, generic_name)| {
info.params.get(*idx as usize).map(|p| (p.name, generic_name.clone()))
})
.collect();
for (name, _) in &self.current_type_param_map {
self.current_local_names.remove(name);
}
self.current_literal_string_params = info.literal_string_params.iter()
.filter_map(|&idx| info.params.get(idx).map(|p| p.name))
.collect();
let generic_clause = self.build_generic_clause(info);
let params_with_types = self.build_param_list(info);
let return_type = self.get_return_type(info);
self.current_return_type = Some(UnifiedType::from_rust_str(&return_type));
if self.dump_types_for.as_deref() == Some(name_str) {
self.dump_type_info(name_str, info, ¶ms_with_types, &return_type);
}
let thx_param = if self.perl_threaded && info.is_thx_dependent {
"my_perl: *mut PerlInterpreter"
} else {
""
};
let params_str = if thx_param.is_empty() {
params_with_types.clone()
} else if params_with_types.is_empty() {
thx_param.to_string()
} else {
format!("{}, {}", thx_param, params_with_types)
};
self.dump_ast_comment_for_expr(name_str, &info.parse_result);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
let generic_info = if !generic_clause.is_empty() { " [generic]" } else { "" };
self.writeln(&format!("/// {}{}{} - macro function", name_str, thx_info, generic_info));
self.writeln("#[inline]");
self.writeln("#[allow(unsafe_op_in_unsafe_fn)]");
self.writeln(&format!("pub unsafe fn {}{}({}) -> {} {{", name_str, generic_clause, params_str, return_type));
let needs_unsafe = info.has_unsafe_ops();
let body_indent = if needs_unsafe { " " } else { " " };
if needs_unsafe {
self.writeln(" unsafe {");
}
match &info.parse_result {
ParseResult::Expression(expr) => {
let type_hint = self.current_return_type.as_ref().map(|ut| ut.to_rust_string());
let mut syn_expr = self.build_syn_expr_with_type_hint(expr, Some(info), type_hint.as_deref());
if self.current_return_type.as_ref().is_some_and(|ut| ut.is_void()) {
let s = normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr));
self.writeln(&format!("{}{};", body_indent, s));
} else if self.current_return_type.as_ref().is_some_and(|ut| ut.is_bool())
&& !self.is_bool_expr_with_dict(expr)
&& !crate::syn_codegen::is_bool_syn_expr(&syn_expr) {
if self.is_pointer_expr_unified(expr, Some(info))
|| self.infer_expr_type_unified(expr, Some(info)).is_some_and(|ut| ut.is_pointer()) {
let s = normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr));
self.writeln(&format!("{}!({}).is_null()", body_indent, s));
} else {
syn_expr = crate::syn_codegen::wrap_as_bool(syn_expr);
let s = normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr));
self.writeln(&format!("{}{}", body_indent, s));
}
} else {
syn_expr = self.cast_return_syn_expr_if_needed(expr, Some(info), syn_expr);
let s = normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr));
self.writeln(&format!("{}{}", body_indent, s));
}
}
ParseResult::Statement(block_items) => {
for item in block_items {
if let BlockItem::Stmt(stmt) = item {
let rust_stmt = self.stmt_to_rust(stmt, info);
self.writeln(&format!("{}{}", body_indent, rust_stmt));
}
}
}
ParseResult::Unparseable(_) => {
self.writeln(&format!("{}unimplemented!()", body_indent));
}
}
if needs_unsafe {
self.writeln(" }");
}
self.writeln("}");
self.writeln("");
self.into_generated_code()
}
fn build_generic_clause(&self, info: &MacroInferInfo) -> String {
if info.generic_type_params.is_empty() {
return String::new();
}
let mut params: Vec<&String> = info.generic_type_params.values().collect();
params.sort();
params.dedup();
format!("<{}>", params.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(", "))
}
fn build_param_list(&mut self, info: &MacroInferInfo) -> String {
let mut_params = collect_mut_params(&info.parse_result, &info.params);
let mut parts = Vec::new();
for (i, p) in info.params.iter().enumerate() {
if info.generic_type_params.contains_key(&(i as i32)) {
continue;
}
let name = escape_rust_keyword(self.interner.get(p.name));
let ty = self.get_param_type(p, info, i);
self.current_param_types.insert(p.name, UnifiedType::from_rust_str(&ty));
let mut_prefix = if mut_params.contains(&p.name) { "mut " } else { "" };
parts.push(format!("{}{}: {}", mut_prefix, name, ty));
}
parts.join(", ")
}
fn get_param_type(&mut self, param: &MacroParam, info: &MacroInferInfo, param_index: usize) -> String {
if let Some(generic_name) = info.generic_type_params.get(&(param_index as i32)) {
return generic_name.clone();
}
if info.literal_string_params.contains(¶m_index) {
return "&str".to_string();
}
let should_be_const = self.const_pointer_positions.contains(¶m_index);
if let Some(mut ty) = best_constraint_for_macro_param(info, param) {
if should_be_const {
ty.make_outer_pointer_const();
} else if ty.has_outer_pointer() {
ty.make_outer_pointer_mut();
}
return self.type_repr_to_rust(&ty);
}
self.unknown_marker().to_string()
}
fn get_return_type(&mut self, info: &MacroInferInfo) -> String {
if let Some(generic_name) = info.generic_type_params.get(&-1) {
return generic_name.clone();
}
if self.is_bool_return {
return "bool".to_string();
}
match &info.parse_result {
ParseResult::Expression(expr) => {
if let Some(ty) = info.get_return_type() {
let mut ty_str = self.type_repr_to_rust(ty);
if ty_str != "()" {
if ty_str.contains("*mut") {
if let Some(expr_ut) = self.infer_expr_type(expr, info) {
if expr_ut.is_const_pointer() {
ty_str = ty_str.replace("*mut", "*const");
}
}
}
return ty_str;
}
if let Some(ut) = self.infer_expr_type(expr, info) {
let s = ut.to_rust_string();
if s != "()" {
return s;
}
}
return ty_str;
}
self.unknown_marker().to_string()
}
ParseResult::Statement(_) => "()".to_string(),
ParseResult::Unparseable(_) => "()".to_string(),
}
}
fn type_repr_to_rust(&mut self, ty: &crate::type_repr::TypeRepr) -> String {
let result = ty.to_rust_string(self.interner);
let result = self.substitute_type_params(&result);
if result.contains("/*") {
self.incomplete_count += 1;
}
result
}
fn substitute_type_params(&self, type_str: &str) -> String {
if self.current_type_param_map.is_empty() {
return type_str.to_string();
}
let mut result = type_str.to_string();
for (param_name, generic_name) in &self.current_type_param_map {
let name_str = self.interner.get(*param_name);
result = replace_word(&result, name_str, generic_name);
}
result
}
fn detect_mutable_ptr_pattern<'b>(&self, compound: &'b CompoundStmt) -> Option<&'b Expr> {
if compound.items.len() != 2 {
return None;
}
let decl = match &compound.items[0] {
BlockItem::Decl(d) => d,
_ => return None,
};
if decl.declarators.len() != 1 {
return None;
}
let init_decl = &decl.declarators[0];
let declared_name = init_decl.declarator.name?;
let init = init_decl.init.as_ref()?;
let init_expr = match init {
Initializer::Expr(e) => e.as_ref(),
_ => return None,
};
let last_expr = match &compound.items[1] {
BlockItem::Stmt(Stmt::Expr(Some(e), _)) => e,
_ => return None,
};
if let ExprKind::Ident(name) = &last_expr.kind {
if *name == declared_name {
return Some(init_expr);
}
}
None
}
fn maybe_decay_flex_array(
&self,
access: syn::Expr,
base: &Expr,
member: InternedStr,
info: Option<&MacroInferInfo>,
is_ptr_member: bool,
) -> syn::Expr {
let Some(fd) = self.fields_dict else { return access; };
let Some(info) = info else { return access; };
let Some(constraints) = info.type_env.expr_constraints.get(&base.id) else {
return access;
};
let Some(base_type) = constraints.first().map(|c| &c.ty) else {
return access;
};
let struct_name = if is_ptr_member {
base_type.pointee_name()
} else {
base_type.type_name()
};
let Some(struct_name) = struct_name else { return access; };
let Some(elem) = fd.flexible_array_element(struct_name, member) else {
return access;
};
let elem_str = elem.to_rust_string(self.interner);
let target_ty_str = format!("*mut {}", elem_str);
let raw_const = syn::Expr::RawAddr(syn::ExprRawAddr {
attrs: vec![],
and_token: Default::default(),
raw: Default::default(),
mutability: syn::PointerMutability::Const(Default::default()),
expr: Box::new(access),
});
crate::syn_codegen::insert_cast(raw_const, crate::syn_codegen::parse_type(&target_ty_str))
}
fn try_inline_call_for_addrof(&self, inner: &Expr) -> Option<Expr> {
let (callee_id, args) = match &inner.kind {
ExprKind::Call { func, args } => match &func.kind {
ExprKind::Ident(name) => (*name, args),
_ => return None,
},
_ => return None,
};
let callee_info = self.macro_ctx.macros.get(&callee_id)?;
let body = match &callee_info.parse_result {
ParseResult::Expression(e) => e,
_ => return None,
};
if callee_info.is_thx_dependent {
return None;
}
if callee_info.params.len() != args.len() {
return None;
}
let mut subs: HashMap<InternedStr, &Expr> = HashMap::new();
for (param, arg) in callee_info.params.iter().zip(args.iter()) {
subs.insert(param.name, arg);
}
let mut inlined = (**body).clone();
substitute_idents(&mut inlined, &subs);
Some(inlined)
}
fn build_syn_expr(&mut self, expr: &Expr, info: Option<&MacroInferInfo>) -> syn::Expr {
use crate::syn_codegen::*;
match &expr.kind {
ExprKind::Ident(name) => {
if let Some(subst) = self.param_substitutions.get(name) {
return syn::parse_str(subst).unwrap_or_else(|_| int_lit(0));
}
let name_str = self.interner.get(*name);
if LIBC_FUNCTIONS.contains(&name_str) {
self.used_libc_fns.insert(name_str.to_string());
}
if !self.current_local_names.contains(name)
&& !self.enum_dict.is_enum_variant(*name)
&& !self.known_symbols.contains(name_str)
{
let s = name_str.to_string();
if !self.unresolved_names.contains(&s) {
self.unresolved_names.push(s);
}
}
if name_str == "true" || name_str == "false" {
return syn::Expr::Lit(syn::ExprLit {
attrs: vec![],
lit: syn::Lit::Bool(syn::LitBool {
value: name_str == "true",
span: proc_macro2::Span::call_site(),
}),
});
}
let escaped = escape_rust_keyword(name_str);
if self.bindings_info.static_arrays.contains(name_str) {
let elem = self.bindings_info.static_array_element_type(name_str)
.unwrap_or_else(|| "u8".to_string());
return syn::parse_str(&format!(
"((&raw const {}) as *const {})",
escaped, elem
))
.unwrap_or_else(|_| int_lit(0));
}
syn::Expr::Path(syn::ExprPath {
attrs: vec![],
qself: None,
path: ident(&escaped).into(),
})
}
ExprKind::IntLit(n) => int_lit(*n),
ExprKind::UIntLit(n) => {
let lit = syn::LitInt::new(&format!("{}u64", n), proc_macro2::Span::call_site());
syn::Expr::Lit(syn::ExprLit { attrs: vec![], lit: syn::Lit::Int(lit) })
}
ExprKind::FloatLit(f) => {
let lit = syn::LitFloat::new(&format!("{}", f), proc_macro2::Span::call_site());
syn::Expr::Lit(syn::ExprLit { attrs: vec![], lit: syn::Lit::Float(lit) })
}
ExprKind::CharLit(c) => {
let s = if c.is_ascii() {
format!("b'{}' as i8", escape_char(*c))
} else {
format!("0x{:02x}u8 as i8", c)
};
syn::parse_str(&s).unwrap_or_else(|_| int_lit(0))
}
ExprKind::StringLit(s) => {
syn::parse_str(&format!("c\"{}\"", escape_string(s)))
.unwrap_or_else(|_| int_lit(0))
}
ExprKind::Deref(inner) => {
let e = self.build_syn_expr(inner, info);
deref(e)
}
ExprKind::AddrOf(inner) => {
if let Some(inlined) = self.try_inline_call_for_addrof(inner) {
let e = self.build_syn_expr(&inlined, info);
return addr_of_mut(e);
}
let e = self.build_syn_expr(inner, info);
addr_of_mut(e)
}
ExprKind::UnaryPlus(inner) => {
self.build_syn_expr(inner, info)
}
ExprKind::BitNot(inner) => {
let e = self.build_syn_expr(inner, info);
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![],
op: syn::UnOp::Not(Default::default()),
expr: Box::new(e),
})
}
ExprKind::Member { expr: base, member } => {
let e = self.build_syn_expr(base, info);
let m = self.interner.get(*member);
if self.is_bitfield_method(m) {
syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![],
receiver: Box::new(e),
dot_token: Default::default(),
method: ident(m),
turbofish: None,
paren_token: Default::default(),
args: syn::punctuated::Punctuated::new(),
})
} else {
let access = field_access(e, m);
self.maybe_decay_flex_array(access, base, *member, info, false)
}
}
ExprKind::PtrMember { expr: base, member } => {
let e = self.build_syn_expr(base, info);
let m = self.interner.get(*member);
let derefed = deref(e);
if self.is_bitfield_method(m) {
syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![],
receiver: Box::new(derefed),
dot_token: Default::default(),
method: ident(m),
turbofish: None,
paren_token: Default::default(),
args: syn::punctuated::Punctuated::new(),
})
} else {
let access = field_access(derefed, m);
self.maybe_decay_flex_array(access, base, *member, info, true)
}
}
ExprKind::Comma { lhs, rhs } => {
let l = self.build_syn_expr(lhs, info);
let r = self.build_syn_expr(rhs, info);
if expr_yields_value_for_stmt_use(&l) {
let l_str = expr_to_string(&l);
let r_str = expr_to_string(&r);
syn::parse_str(&format!("{{ let _ = {}; {} }}", l_str, r_str))
.unwrap_or_else(|_| int_lit(0))
} else {
syn::Expr::Block(syn::ExprBlock {
attrs: vec![],
label: None,
block: syn::Block {
brace_token: Default::default(),
stmts: vec![
syn::Stmt::Expr(l, Some(Default::default())),
syn::Stmt::Expr(r, None),
],
},
})
}
}
ExprKind::UnaryMinus(inner) => {
let e = self.build_syn_expr(inner, info);
let e_str = expr_to_string(&e);
if is_unsigned_cast_expr(&e_str) {
return syn::parse_str(&format!("({}).wrapping_neg()", e_str.trim_start_matches('-')))
.unwrap_or_else(|_| int_lit(0));
}
if let Some(ut) = self.infer_expr_type_unified(inner, info) {
let ts = ut.to_rust_string();
if matches!(normalize_integer_type(&ts), Some("usize" | "u8" | "u16" | "u32" | "u64")) {
self.codegen_errors.push(format!("cannot negate unsigned type: -({}: {})", e_str, ts));
}
}
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![],
op: syn::UnOp::Neg(Default::default()),
expr: Box::new(e),
})
}
ExprKind::LogNot(inner) => {
if matches!(&inner.kind, ExprKind::StringLit(_)) {
return syn::parse_str("false").unwrap_or_else(|_| int_lit(0));
}
let e = self.build_syn_expr(inner, info);
let bool_e = if self.is_bool_expr_with_dict(inner) {
e
} else if self.is_pointer_expr_unified(inner, info)
|| self.infer_expr_type_unified(inner, info).is_some_and(|ut| ut.is_pointer()) {
syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![],
receiver: Box::new(e),
dot_token: Default::default(),
method: ident("is_null"),
turbofish: None,
paren_token: Default::default(),
args: syn::punctuated::Punctuated::new(),
})
} else {
wrap_as_bool(e)
};
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![],
op: syn::UnOp::Not(Default::default()),
expr: Box::new(bool_e),
})
}
ExprKind::Cast { type_name, expr: inner } => {
let t = self.type_name_to_rust(type_name);
if is_unsigned_integer_target(&t) {
if let ExprKind::UnaryMinus(minus_inner) = &inner.kind {
if matches!(&minus_inner.kind,
ExprKind::IntLit(1) | ExprKind::UIntLit(1))
{
return syn::parse_str(&format!("{}::MAX", t))
.unwrap_or_else(|_| int_lit(0));
}
}
}
if (t.starts_with("*mut ") || t.starts_with("*const "))
&& matches!(&inner.kind, ExprKind::AddrOf(_))
{
if let ExprKind::AddrOf(addrof_inner) = &inner.kind {
let inlined_owned;
let inner_to_build: &Expr =
if let Some(inlined) = self.try_inline_call_for_addrof(addrof_inner) {
inlined_owned = inlined;
&inlined_owned
} else {
addrof_inner
};
let inner_e = self.build_syn_expr(inner_to_build, info);
let raw_const = syn::Expr::RawAddr(syn::ExprRawAddr {
attrs: vec![],
and_token: Default::default(),
raw: Default::default(),
mutability: syn::PointerMutability::Const(Default::default()),
expr: Box::new(inner_e),
});
return insert_cast(raw_const, parse_type(&t));
}
}
let e = self.build_syn_expr(inner, info);
if t == "()" {
let e_str = expr_to_string(&e);
return syn::parse_str(&format!("{{ let _ = {}; }}", e_str))
.unwrap_or_else(|_| int_lit(0));
}
if t == "bool" {
if self.is_bool_expr_with_dict(inner) {
return e;
}
if self.is_pointer_expr_unified(inner, info)
|| self.infer_expr_type_unified(inner, info).is_some_and(|ut| ut.is_pointer()) {
let is_null = syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![],
receiver: Box::new(e),
dot_token: Default::default(),
method: ident("is_null"),
turbofish: None,
paren_token: Default::default(),
args: syn::punctuated::Punctuated::new(),
});
return syn::Expr::Unary(syn::ExprUnary {
attrs: vec![],
op: syn::UnOp::Not(Default::default()),
expr: Box::new(is_null),
});
}
return wrap_as_bool(e);
}
if self.is_enum_cast_target(type_name) {
let e_str = expr_to_string(&e);
return syn::parse_str(&format!("std::mem::transmute::<_, {}>({})", t, e_str))
.unwrap_or_else(|_| int_lit(0));
}
insert_cast(e, parse_type(&t))
}
ExprKind::Sizeof(inner) => {
if let ExprKind::Ident(name) = &inner.kind {
if self.current_literal_string_params.contains(name) {
let param = escape_rust_keyword(self.interner.get(*name));
return syn::parse_str(&format!("({}.len() + 1)", param))
.unwrap_or_else(|_| int_lit(0));
}
}
let e = self.build_syn_expr(inner, info);
let e_str = expr_to_string(&e);
syn::parse_str(&format!("std::mem::size_of_val(&({}))", e_str))
.unwrap_or_else(|_| int_lit(0))
}
ExprKind::SizeofType(type_name) => {
let t = self.type_name_to_rust(type_name);
syn::parse_str(&format!("std::mem::size_of::<{}>()", t))
.unwrap_or_else(|_| int_lit(0))
}
ExprKind::Index { expr: base, index } => {
use crate::syn_codegen::*;
let i = self.build_syn_expr(index, info);
let i_isize = cast_syn_expr(i, "isize");
let base_expr: syn::Expr = if self.is_array_like_expr(base, info) {
let raw_ptr_expr: syn::Expr = if let ExprKind::Ident(n) = &base.kind {
let name_str = self.interner.get(*n);
let escaped = escape_rust_keyword(name_str);
if self.bindings_info.static_arrays.contains(name_str) {
let elem = self.bindings_info
.static_array_element_type(name_str)
.unwrap_or_else(|| "u8".to_string());
syn::parse_str(&format!(
"((&raw const {}) as *const {})",
escaped, elem
))
.unwrap_or_else(|_| int_lit(0))
} else {
let id = ident_expr(escaped.as_str());
method_call(id, "as_ptr", vec![])
}
} else {
let b = self.build_syn_expr(base, info);
method_call(b, "as_ptr", vec![])
};
let elem_str = self.infer_expr_type_unified(base, info)
.and_then(|ut| ut.inner_type().cloned())
.map(|inner| inner.to_rust_string());
if let Some(elem) = elem_str {
cast_syn_expr(raw_ptr_expr, &format!("*mut {}", elem))
} else {
raw_ptr_expr
}
} else {
self.build_syn_expr(base, info)
};
let offset_call = method_call(base_expr, "offset", vec![i_isize]);
deref(offset_call)
}
ExprKind::Conditional { cond, then_expr, else_expr } => {
let c = self.build_syn_expr(cond, info);
let c_str = expr_to_string(&c);
let cond_str = self.wrap_as_bool_condition(cond, &c_str, info);
let cond_syn: syn::Expr = syn::parse_str(&cond_str).unwrap_or(c);
let type_hint = self.current_return_type.as_ref().map(|ut| ut.to_rust_string());
let tt = self.infer_expr_type_unified(then_expr, info);
let et = self.infer_expr_type_unified(else_expr, info);
if let Some(ref hint) = type_hint {
let hint_ut = UnifiedType::from_rust_str(hint);
if hint_ut.is_pointer() {
if is_null_literal(else_expr) {
let t = self.build_syn_expr(then_expr, info);
let e: syn::Expr = syn::parse_str(&null_ptr_expr(&hint_ut))
.unwrap_or_else(|_| int_lit(0));
return if_else(cond_syn, t, e);
}
if is_null_literal(then_expr) {
let t: syn::Expr = syn::parse_str(&null_ptr_expr(&hint_ut))
.unwrap_or_else(|_| int_lit(0));
let e = self.build_syn_expr(else_expr, info);
return if_else(cond_syn, t, e);
}
}
if normalize_integer_type(hint).is_some() {
if is_null_literal(else_expr) {
let t = self.build_syn_expr(then_expr, info);
return if_else(cond_syn, t, int_lit(0));
}
if is_null_literal(then_expr) {
let e = self.build_syn_expr(else_expr, info);
return if_else(cond_syn, int_lit(0), e);
}
}
if hint_ut.is_bool() {
let then_syn = match &then_expr.kind {
ExprKind::IntLit(0) => syn::parse_str("false").unwrap(),
ExprKind::IntLit(1) => syn::parse_str("true").unwrap(),
_ => self.build_syn_expr(then_expr, info),
};
let else_syn = match &else_expr.kind {
ExprKind::IntLit(0) => syn::parse_str("false").unwrap(),
ExprKind::IntLit(1) => syn::parse_str("true").unwrap(),
_ => self.build_syn_expr(else_expr, info),
};
return if_else(cond_syn, then_syn, else_syn);
}
}
let then_syn = if is_null_literal(then_expr) {
if let Some(ref eut) = et {
if eut.is_pointer() {
syn::parse_str(&null_ptr_expr(eut)).unwrap_or_else(|_| int_lit(0))
} else { self.build_syn_expr(then_expr, info) }
} else { self.build_syn_expr(then_expr, info) }
} else { self.build_syn_expr(then_expr, info) };
let else_syn = if is_null_literal(else_expr) {
if let Some(ref tut) = tt {
if tut.is_pointer() {
syn::parse_str(&null_ptr_expr(tut)).unwrap_or_else(|_| int_lit(0))
} else { self.build_syn_expr(else_expr, info) }
} else { self.build_syn_expr(else_expr, info) }
} else { self.build_syn_expr(else_expr, info) };
if let (Some(tut), Some(eut)) = (&tt, &et) {
let ts = tut.to_rust_string();
let es = eut.to_rust_string();
if let (Some(tn), Some(en)) = (normalize_integer_type(&ts), normalize_integer_type(&es)) {
if tn != en {
if let Some(wider) = wider_integer_type(&ts, &es) {
let (then_final, else_final) = if normalize_integer_type(&ts) != Some(wider) {
(cast_syn_expr(then_syn, wider), else_syn)
} else {
(then_syn, cast_syn_expr(else_syn, wider))
};
return if_else(cond_syn, then_final, else_final);
}
}
}
if tut.is_pointer() && eut.is_pointer() && ts != es
&& is_sv_subtype_cast(tut, eut)
{
let target_str = type_hint.as_deref()
.filter(|t| *t != "()" && !UnifiedType::from_rust_str(t).is_void())
.unwrap_or(&ts)
.to_string();
return if_else(
cond_syn,
cast_syn_expr(then_syn, &target_str),
cast_syn_expr(else_syn, &target_str),
);
}
}
if_else(cond_syn, then_syn, else_syn)
}
ExprKind::Binary { op, lhs, rhs } => {
if *op == BinOp::Sub {
if let ExprKind::Sizeof(inner) = &lhs.kind {
if let ExprKind::Ident(name) = &inner.kind {
if self.current_literal_string_params.contains(name) {
if let ExprKind::IntLit(1) = &rhs.kind {
let param = escape_rust_keyword(self.interner.get(*name));
return syn::parse_str(&format!("{}.len()", param))
.unwrap_or_else(|_| int_lit(0));
}
}
}
}
}
if matches!(op, BinOp::Eq | BinOp::Ne) {
let s_eq = |s: &ExprKind| matches!(s, ExprKind::StringLit(_));
let lhs_is_str = s_eq(&lhs.kind);
let rhs_is_str = s_eq(&rhs.kind);
if (lhs_is_str && is_null_literal(rhs))
|| (rhs_is_str && is_null_literal(lhs))
{
return syn::parse_str(
if *op == BinOp::Eq { "false" } else { "true" }
).unwrap_or_else(|_| int_lit(0));
}
}
if matches!(op, BinOp::Eq | BinOp::Ne) {
let opt_lhs = (is_null_literal(rhs) || matches!(&rhs.kind, ExprKind::IntLit(0)))
&& self.is_option_fn_pointer_expr(lhs, info);
let opt_rhs = (is_null_literal(lhs) || matches!(&lhs.kind, ExprKind::IntLit(0)))
&& self.is_option_fn_pointer_expr(rhs, info);
if opt_lhs || opt_rhs {
let receiver_expr = if opt_lhs { lhs } else { rhs };
let r = self.build_syn_expr(receiver_expr, info);
let method = if *op == BinOp::Eq { "is_none" } else { "is_some" };
return syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![], receiver: Box::new(r), dot_token: Default::default(),
method: ident(method), turbofish: None,
paren_token: Default::default(), args: syn::punctuated::Punctuated::new(),
});
}
}
if matches!(op, BinOp::Eq | BinOp::Ne) {
if is_null_literal(rhs) {
if self.is_pointer_expr_unified(lhs, info)
|| self.infer_expr_type_unified(lhs, info).is_some_and(|ut| ut.is_pointer()) {
let l = self.build_syn_expr(lhs, info);
let is_null = syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![], receiver: Box::new(l), dot_token: Default::default(),
method: ident("is_null"), turbofish: None,
paren_token: Default::default(), args: syn::punctuated::Punctuated::new(),
});
return if *op == BinOp::Eq { is_null } else {
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![], op: syn::UnOp::Not(Default::default()),
expr: Box::new(is_null),
})
};
}
}
if is_null_literal(lhs) {
if self.is_pointer_expr_unified(rhs, info)
|| self.infer_expr_type_unified(rhs, info).is_some_and(|ut| ut.is_pointer()) {
let r = self.build_syn_expr(rhs, info);
let is_null = syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![], receiver: Box::new(r), dot_token: Default::default(),
method: ident("is_null"), turbofish: None,
paren_token: Default::default(), args: syn::punctuated::Punctuated::new(),
});
return if *op == BinOp::Eq { is_null } else {
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![], op: syn::UnOp::Not(Default::default()),
expr: Box::new(is_null),
})
};
}
}
if self.is_bool_expr_with_dict(lhs) {
match (&rhs.kind, op) {
(ExprKind::IntLit(0), BinOp::Ne) | (ExprKind::IntLit(1), BinOp::Eq) => {
return self.build_syn_expr(lhs, info);
}
(ExprKind::IntLit(0), BinOp::Eq) | (ExprKind::IntLit(1), BinOp::Ne) => {
let l = self.build_syn_expr(lhs, info);
return syn::Expr::Unary(syn::ExprUnary {
attrs: vec![], op: syn::UnOp::Not(Default::default()),
expr: Box::new(l),
});
}
_ => {}
}
}
if self.is_bool_expr_with_dict(rhs) {
match (&lhs.kind, op) {
(ExprKind::IntLit(0), BinOp::Ne) | (ExprKind::IntLit(1), BinOp::Eq) => {
return self.build_syn_expr(rhs, info);
}
(ExprKind::IntLit(0), BinOp::Eq) | (ExprKind::IntLit(1), BinOp::Ne) => {
let r = self.build_syn_expr(rhs, info);
return syn::Expr::Unary(syn::ExprUnary {
attrs: vec![], op: syn::UnOp::Not(Default::default()),
expr: Box::new(r),
});
}
_ => {}
}
}
}
if matches!(op, BinOp::Add | BinOp::Sub) {
let l_arr = !self.is_static_array_expr(lhs)
&& self.is_array_like_expr(lhs, info);
let r_arr = !self.is_static_array_expr(rhs)
&& self.is_array_like_expr(rhs, info);
let lp = l_arr
|| self.is_static_array_expr(lhs)
|| self.is_pointer_expr_unified(lhs, info)
|| self.infer_expr_type_unified(lhs, info).is_some_and(|ut| ut.is_pointer());
let rp = r_arr
|| self.is_static_array_expr(rhs)
|| self.is_pointer_expr_unified(rhs, info)
|| self.infer_expr_type_unified(rhs, info).is_some_and(|ut| ut.is_pointer());
let l_is_static_arr = self.is_static_array_expr(lhs);
let r_is_static_arr = self.is_static_array_expr(rhs);
let cast_to_mut = |this: &mut Self, expr: syn::Expr, arr_expr: &Expr,
needs_as_ptr: bool| -> syn::Expr {
let elem = this.infer_expr_type_unified(arr_expr, info)
.and_then(|ut| ut.inner_type().cloned())
.map(|u| u.to_rust_string());
let base = if needs_as_ptr {
crate::syn_codegen::method_call(expr, "as_ptr", vec![])
} else {
expr
};
if let Some(e) = elem {
crate::syn_codegen::cast_syn_expr(base, &format!("*mut {}", e))
} else {
base
}
};
if lp && !rp {
let l = self.build_syn_expr(lhs, info);
let l = if l_arr {
cast_to_mut(self, l, lhs, true)
} else if l_is_static_arr {
cast_to_mut(self, l, lhs, false)
} else {
l
};
let r = self.build_syn_expr(rhs, info);
let r_isize = crate::syn_codegen::cast_syn_expr(r, "isize");
let arg = if *op == BinOp::Add { r_isize } else {
syn::Expr::Unary(syn::ExprUnary {
attrs: vec![],
op: syn::UnOp::Neg(Default::default()),
expr: Box::new(r_isize),
})
};
return crate::syn_codegen::method_call(l, "offset", vec![arg]);
}
if rp && !lp && *op == BinOp::Add {
let l = self.build_syn_expr(lhs, info);
let r = self.build_syn_expr(rhs, info);
let r = if r_arr {
cast_to_mut(self, r, rhs, true)
} else if r_is_static_arr {
cast_to_mut(self, r, rhs, false)
} else {
r
};
let l_isize = crate::syn_codegen::cast_syn_expr(l, "isize");
return crate::syn_codegen::method_call(r, "offset", vec![l_isize]);
}
if lp && rp && *op == BinOp::Sub {
let l = self.build_syn_expr(lhs, info);
let r = self.build_syn_expr(rhs, info);
return crate::syn_codegen::method_call(l, "offset_from", vec![r]);
}
}
if matches!(&rhs.kind, ExprKind::IntLit(_)) {
if let Some(lut) = self.infer_expr_type_unified(lhs, info) {
if lut.is_float() {
if let ExprKind::IntLit(v) = &rhs.kind {
let l = self.build_syn_expr(lhs, info);
let l_str = expr_to_string(&l);
return syn::parse_str(&format!("{} {} {}.0", l_str, bin_op_to_rust(*op), v))
.unwrap_or_else(|_| int_lit(0));
}
}
}
}
if matches!(&lhs.kind, ExprKind::IntLit(_)) {
if let Some(rut) = self.infer_expr_type_unified(rhs, info) {
if rut.is_float() {
if let ExprKind::IntLit(v) = &lhs.kind {
let r = self.build_syn_expr(rhs, info);
let r_str = expr_to_string(&r);
return syn::parse_str(&format!("{}.0 {} {}", v, bin_op_to_rust(*op), r_str))
.unwrap_or_else(|_| int_lit(0));
}
}
}
}
if matches!(op, BinOp::Lt | BinOp::Le | BinOp::Gt | BinOp::Ge | BinOp::Eq | BinOp::Ne) {
if let (Some(lut), Some(rut)) =
(self.infer_expr_type_unified(lhs, info),
self.infer_expr_type_unified(rhs, info))
{
if lut.is_pointer() && rut.is_pointer()
&& pointer_inner_compatible(&lut, &rut)
&& (lut.is_const_pointer() != rut.is_const_pointer()
|| lut.to_rust_string() != rut.to_rust_string())
{
let l = self.build_syn_expr(lhs, info);
let r = self.build_syn_expr(rhs, info);
let target = lut.to_rust_string();
return syn::Expr::Binary(syn::ExprBinary {
attrs: vec![], left: Box::new(l),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(crate::syn_codegen::cast_syn_expr(r, &target)),
});
}
}
}
let l = self.build_syn_expr(lhs, info);
let r = self.build_syn_expr(rhs, info);
if matches!(op, BinOp::LogAnd | BinOp::LogOr) {
let l_str = expr_to_string(&l);
let r_str = expr_to_string(&r);
let l_bool = self.wrap_as_bool_condition(lhs, &l_str, info);
let r_bool = self.wrap_as_bool_condition(rhs, &r_str, info);
let l_syn: syn::Expr = syn::parse_str(&l_bool).unwrap_or(l);
let r_syn: syn::Expr = syn::parse_str(&r_bool).unwrap_or(r);
return syn::Expr::Binary(syn::ExprBinary {
attrs: vec![], left: Box::new(l_syn),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(r_syn),
});
}
let lt = self.infer_expr_type_unified(lhs, info);
let rt = self.infer_expr_type_unified(rhs, info);
let make_binary_op = |left: syn::Expr, right: syn::Expr| -> syn::Expr {
syn::Expr::Binary(syn::ExprBinary {
attrs: vec![], left: Box::new(left),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(right),
})
};
match (<, &rt) {
(Some(lut), None) if self.is_rust_enum_type(lut) => {
return make_binary_op(cast_syn_expr(l, "u32"), r);
}
(None, Some(rut)) if self.is_rust_enum_type(rut) => {
return make_binary_op(l, cast_syn_expr(r, "u32"));
}
_ => {}
}
if let (Some(lut), Some(rut)) = (<, &rt) {
let make_binary = |left: syn::Expr, right: syn::Expr| -> syn::Expr {
syn::Expr::Binary(syn::ExprBinary {
attrs: vec![], left: Box::new(left),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(right),
})
};
let l_is_enum = self.is_rust_enum_type(lut);
let r_is_enum = self.is_rust_enum_type(rut);
if l_is_enum && !r_is_enum {
let rs = rut.to_rust_string();
let target = normalize_integer_type(&rs).unwrap_or("u32");
return make_binary(cast_syn_expr(l, target), r);
}
if r_is_enum && !l_is_enum {
let ls = lut.to_rust_string();
let target = normalize_integer_type(&ls).unwrap_or("u32");
return make_binary(l, cast_syn_expr(r, target));
}
if rut.is_bool() {
let ls = lut.to_rust_string();
if let Some(nl) = normalize_integer_type(&ls) {
return make_binary(l, cast_syn_expr(r, nl));
}
}
if lut.is_bool() {
let rs = rut.to_rust_string();
if let Some(nr) = normalize_integer_type(&rs) {
return make_binary(cast_syn_expr(l, nr), r);
}
}
if lut.is_float() && !rut.is_float() {
let ls = lut.to_rust_string();
let float_ty = if ls == "c_float" || ls == "f32" { "f32" } else { "f64" };
return make_binary(l, cast_syn_expr(r, float_ty));
}
if rut.is_float() && !lut.is_float() {
let rs = rut.to_rust_string();
let float_ty = if rs == "c_float" || rs == "f32" { "f32" } else { "f64" };
return make_binary(cast_syn_expr(l, float_ty), r);
}
let ls = lut.to_rust_string();
let rs = rut.to_rust_string();
if let Some(wider) = wider_integer_type(&ls, &rs) {
if normalize_integer_type(&ls) != Some(wider) {
return make_binary(cast_syn_expr(l, wider), r);
} else {
return make_binary(l, cast_syn_expr(r, wider));
}
}
}
{
let make_binary = |left: syn::Expr, right: syn::Expr| -> syn::Expr {
syn::Expr::Binary(syn::ExprBinary {
attrs: vec![], left: Box::new(left),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(right),
})
};
match (<, &rt) {
(Some(lut), None) if lut.is_float() => {
let ls = lut.to_rust_string();
let float_ty = if ls == "c_float" || ls == "f32" { "f32" } else { "f64" };
return make_binary(l, cast_syn_expr(r, float_ty));
}
(None, Some(rut)) if rut.is_float() => {
let rs = rut.to_rust_string();
let float_ty = if rs == "c_float" || rs == "f32" { "f32" } else { "f64" };
return make_binary(cast_syn_expr(l, float_ty), r);
}
_ => {}
}
if matches!(op, BinOp::BitAnd | BinOp::BitOr | BinOp::BitXor) {
match (<, &rt) {
(Some(lut), None) => {
let ls = lut.to_rust_string();
if let Some(nl) = normalize_integer_type(&ls) {
return make_binary(l, cast_syn_expr(r, nl));
}
}
(None, Some(rut)) => {
let rs = rut.to_rust_string();
if let Some(nr) = normalize_integer_type(&rs) {
return make_binary(cast_syn_expr(l, nr), r);
}
}
_ => {}
}
}
}
syn::Expr::Binary(syn::ExprBinary {
attrs: vec![],
left: Box::new(l),
op: crate::syn_codegen::to_syn_binop(*op),
right: Box::new(r),
})
}
ExprKind::Call { func, args } => {
if let Some(syn_call) = self.try_build_common_macro_fn_call(func, args, info) {
return syn_call;
}
if let ExprKind::Ident(name) = &func.kind {
let func_name = self.interner.get(*name);
if func_name == "__builtin_expect" && !args.is_empty() {
return self.build_syn_expr(&args[0], info);
}
if func_name == "__builtin_unreachable" {
return syn::parse_str("std::hint::unreachable_unchecked()").unwrap();
}
if (func_name == "__builtin_ctz" || func_name == "__builtin_ctzl") && args.len() == 1 {
let arg = self.build_syn_expr(&args[0], info);
return syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![], receiver: Box::new(arg), dot_token: Default::default(),
method: ident("trailing_zeros"), turbofish: None,
paren_token: Default::default(), args: syn::punctuated::Punctuated::new(),
});
}
if (func_name == "__builtin_clz" || func_name == "__builtin_clzl") && args.len() == 1 {
let arg = self.build_syn_expr(&args[0], info);
return syn::Expr::MethodCall(syn::ExprMethodCall {
attrs: vec![], receiver: Box::new(arg), dot_token: Default::default(),
method: ident("leading_zeros"), turbofish: None,
paren_token: Default::default(), args: syn::punctuated::Punctuated::new(),
});
}
if matches!(func_name, "ASSERT_IS_LITERAL" | "ASSERT_IS_PTR" | "ASSERT_NOT_PTR")
&& args.len() == 1
{
return self.build_syn_expr(&args[0], info);
}
if matches!(func_name, "offsetof" | "__builtin_offsetof") && args.len() == 2 {
let type_name_str = if let ExprKind::Ident(name) = &args[0].kind {
escape_rust_keyword(self.interner.get(*name)).to_string()
} else {
let s = self.build_syn_expr(&args[0], info);
expr_to_string(&s)
};
if let Some(field_path) = self.expr_to_field_path(&args[1]) {
return syn::parse_str(&format!("std::mem::offset_of!({}, {})", type_name_str, field_path))
.unwrap_or_else(|_| int_lit(0));
}
}
}
let f_syn = self.build_syn_expr(func, info);
let f_str = expr_to_string(&f_syn);
let callee_name = if let ExprKind::Ident(name) = &func.kind { Some(*name) } else { None };
let needs_my_perl = callee_name
.map(|name| self.needs_my_perl_for_call(name, args.len()))
.unwrap_or(false);
let callee_generics = callee_name
.and_then(|name| self.get_callee_generic_params(name).cloned());
if let Some(ref generics) = callee_generics {
let mut type_args = Vec::new();
let mut value_args: Vec<String> = if needs_my_perl {
vec!["my_perl".to_string()]
} else { vec![] };
let mut value_idx = if needs_my_perl { 1usize } else { 0 };
for (i, arg) in args.iter().enumerate() {
if generics.contains_key(&(i as i32)) {
let syn_arg = self.build_syn_expr(arg, info);
type_args.push(normalize_parens(&expr_to_string(&syn_arg)));
} else {
value_args.push(self.build_arg_string_unified(arg, info, callee_name, value_idx));
value_idx += 1;
}
}
return syn::parse_str(&format!("{}::<{}>({})", f_str, type_args.join(", "), value_args.join(", ")))
.unwrap_or_else(|_| int_lit(0));
}
let mut arg_strs: Vec<String> = if needs_my_perl {
vec!["my_perl".to_string()]
} else { vec![] };
let arg_offset = if needs_my_perl { 1usize } else { 0 };
for (i, arg) in args.iter().enumerate() {
arg_strs.push(self.build_arg_string_unified(arg, info, callee_name, i + arg_offset));
}
syn::parse_str(&format!("{}({})", f_str, arg_strs.join(", ")))
.unwrap_or_else(|_| int_lit(0))
}
ExprKind::MacroCall { name, args, expanded, .. } => {
if self.should_emit_as_macro_call(*name) {
let name_str = escape_rust_keyword(self.interner.get(*name));
let needs_my_perl = self.needs_my_perl_for_call(*name, args.len());
let mut a: Vec<String> = if needs_my_perl {
vec!["my_perl".to_string()]
} else { vec![] };
for arg in args {
let arg_str = expr_to_string(&self.build_syn_expr(arg, info));
a.push(normalize_parens(&arg_str));
}
syn::parse_str(&format!("{}({})", name_str, a.join(", ")))
.unwrap_or_else(|_| int_lit(0))
} else {
self.build_syn_expr(expanded, info)
}
}
ExprKind::BuiltinCall { name, args } => {
let func_name = self.interner.get(*name);
if matches!(func_name, "offsetof" | "__builtin_offsetof" | "STRUCT_OFFSET")
&& args.len() == 2
{
let type_str = match &args[0] {
crate::ast::BuiltinArg::TypeName(tn) => self.type_name_to_rust(tn),
crate::ast::BuiltinArg::Expr(e) => {
let s = self.build_syn_expr(e, info);
expr_to_string(&s)
}
};
let field_expr = match &args[1] {
crate::ast::BuiltinArg::Expr(e) => self.expr_to_field_path(e),
_ => None,
};
if let Some(fp) = field_expr {
return syn::parse_str(&format!("std::mem::offset_of!({}, {})", type_str, fp))
.unwrap_or_else(|_| int_lit(0));
}
}
let a: Vec<String> = args.iter().map(|arg| match arg {
crate::ast::BuiltinArg::Expr(e) => {
let s = self.build_syn_expr(e, info);
expr_to_string(&s)
}
crate::ast::BuiltinArg::TypeName(tn) => self.type_name_to_rust(tn),
}).collect();
syn::parse_str(&format!("{}({})", func_name, a.join(", ")))
.unwrap_or_else(|_| int_lit(0))
}
ExprKind::Assign { op, lhs, rhs } => {
self.build_assign_syn_expr(*op, lhs, rhs, info)
}
ExprKind::PreInc(inner) => {
self.build_inc_dec_syn_expr(inner, info, true, false)
}
ExprKind::PreDec(inner) => {
self.build_inc_dec_syn_expr(inner, info, false, false)
}
ExprKind::PostInc(inner) => {
self.build_inc_dec_syn_expr(inner, info, true, true)
}
ExprKind::PostDec(inner) => {
self.build_inc_dec_syn_expr(inner, info, false, true)
}
ExprKind::Assert { kind, condition } => {
let assert_str = if let Some((real_cond, msg)) = decompose_assert_with_message(condition) {
let c = self.build_syn_expr(real_cond, info);
let c_str = expr_to_string(&c);
let cond_str = self.wrap_as_bool_condition(real_cond, &c_str, info);
format!("assert!({}, \"{}\")", normalize_parens(&cond_str), msg)
} else {
let c = self.build_syn_expr(condition, info);
let c_str = expr_to_string(&c);
if is_boolean_expr(condition) || self.is_bool_expr_with_dict(condition) {
format!("assert!({})", normalize_parens(&c_str))
} else if self.is_pointer_expr_unified(condition, info)
|| self.infer_expr_type_unified(condition, info).is_some_and(|ut| ut.is_pointer()) {
format!("assert!(!({}).is_null())", c_str)
} else {
format!("assert!({} != 0)", normalize_parens(&c_str))
}
};
let result = match kind {
AssertKind::Assert => assert_str,
AssertKind::AssertUnderscore => format!("{{ {}; }}", assert_str),
};
syn::parse_str(&result).unwrap_or_else(|_| int_lit(0))
}
ExprKind::StmtExpr(compound) => {
if let Some(init_expr) = self.detect_mutable_ptr_pattern(compound) {
return self.build_syn_expr(init_expr, info);
}
let mut parts: Vec<String> = Vec::new();
for item in &compound.items {
match item {
BlockItem::Stmt(Stmt::Expr(Some(e), _)) => {
parts.push(self.build_expr_string(e, info));
}
BlockItem::Stmt(stmt) => {
let s = match info {
Some(info) => self.stmt_to_rust(stmt, info),
None => self.stmt_to_rust_inline(stmt, ""),
};
parts.push(s);
}
BlockItem::Decl(decl) => {
self.collect_decl_types(decl);
let decl_str = self.decl_to_rust_let(decl, "");
for line in decl_str.lines() {
let trimmed = line.trim();
if !trimmed.is_empty() {
parts.push(trimmed.strip_suffix(';').unwrap_or(trimmed).to_string());
}
}
}
}
}
let block_str = if parts.is_empty() {
"{ }".to_string()
} else if parts.len() == 1 {
parts.pop().unwrap()
} else {
let last = parts.pop().unwrap();
let stmts = parts.join("; ");
format!("{{ {}; {} }}", stmts, last)
};
syn::parse_str(&block_str).unwrap_or_else(|_| int_lit(0))
}
ExprKind::Alignof(ty) => {
let ty_str = self.type_name_to_rust(ty);
syn::parse_str(&format!("std::mem::align_of::<{}>()", ty_str))
.unwrap_or_else(|_| int_lit(0))
}
_ => {
self.codegen_errors.push(format!(
"unhandled ExprKind in syn codegen: {:?}",
std::mem::discriminant(&expr.kind)
));
int_lit(0)
}
}
}
fn build_arg_string_unified(&mut self, arg: &Expr, info: Option<&MacroInferInfo>,
callee: Option<InternedStr>, arg_index: usize) -> String {
if info.is_some() {
if let Some(name) = self.find_literal_string_ident(arg) {
if let Some(callee_name) = callee {
if self.callee_expects_literal_string(callee_name, arg_index) {
return escape_rust_keyword(self.interner.get(*name));
}
}
let param = escape_rust_keyword(self.interner.get(*name));
return format!("{}.as_ptr() as *const c_char", param);
}
}
if is_null_literal(arg) {
if let Some(callee_name) = callee {
let func_name = self.interner.get(callee_name).to_string();
if let Some(expected_ut) = self.get_callee_param_type_extended(&func_name, arg_index) {
if expected_ut.is_pointer() {
return null_ptr_expr(&expected_ut);
}
}
}
}
if let Some(callee_name) = callee {
let func_name = self.interner.get(callee_name);
if self.callee_param_is_bool(func_name, arg_index) {
match &arg.kind {
ExprKind::IntLit(0) => return "false".to_string(),
ExprKind::IntLit(1) => return "true".to_string(),
_ => {}
}
}
}
let mut syn_expr = self.build_syn_expr(arg, info);
if let Some(callee_name) = callee {
let func_name = self.interner.get(callee_name).to_string();
if let Some(expected_ut) = self.get_callee_param_type_extended(&func_name, arg_index) {
let actual_ut = self.infer_expr_type_unified(arg, info);
let actual_ty = actual_ut.as_ref().map(|ut| ut.to_rust_string());
let expected_ty = expected_ut.to_rust_string();
if matches!(&arg.kind, ExprKind::StringLit(_))
&& expected_ut.is_pointer()
{
syn_expr = crate::syn_codegen::method_call(syn_expr, "as_ptr", vec![]);
}
syn_expr = self.cast_arg_syn_if_needed(syn_expr, actual_ty.as_deref(), &expected_ty);
}
}
normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr))
}
fn cast_arg_syn_if_needed(&self, arg_expr: syn::Expr,
actual_ty: Option<&str>, expected_ty: &str) -> syn::Expr {
use crate::syn_codegen::cast_syn_expr;
if let Some(actual) = actual_ty {
let actual_ut = UnifiedType::from_rust_str(actual);
if self.is_rust_enum_type(&actual_ut) {
if let Some(target) = normalize_integer_type(expected_ty) {
return cast_syn_expr(arg_expr, target);
}
}
let na = normalize_integer_type(actual);
let ne = normalize_integer_type(expected_ty);
if let (Some(a), Some(e)) = (na, ne) {
if !integer_types_compatible(a, e) {
return cast_syn_expr(arg_expr, e);
}
return arg_expr;
}
if actual != expected_ty {
let actual_ut = UnifiedType::from_rust_str(actual);
let expected_ut = UnifiedType::from_rust_str(expected_ty);
if actual_ut.is_pointer() && expected_ut.is_pointer()
&& is_sv_subtype_cast(&actual_ut, &expected_ut) {
let cast_ty = if actual.contains("*const") {
expected_ty.replace("*mut", "*const")
} else {
expected_ty.to_string()
};
return cast_syn_expr(arg_expr, &cast_ty);
}
if actual_ut.is_pointer() && expected_ut.is_pointer()
&& expected_ut.is_void_pointer()
{
return cast_syn_expr(arg_expr, expected_ty);
}
if pointer_inner_compatible(&actual_ut, &expected_ut) {
let top_mut_to_const = !actual_ut.is_const_pointer()
&& expected_ut.is_const_pointer();
let top_const_same = actual_ut.is_const_pointer()
== expected_ut.is_const_pointer();
let inner_exact_match = actual_ut.inner_type()
.zip(expected_ut.inner_type())
.is_some_and(|(a, b)| a.to_rust_string() == b.to_rust_string());
let auto_coerces = (top_mut_to_const || top_const_same)
&& inner_exact_match;
if !auto_coerces {
return cast_syn_expr(arg_expr, expected_ty);
}
}
}
return arg_expr;
}
let expected_ut = UnifiedType::from_rust_str(expected_ty);
if expected_ut.is_pointer() {
if let Some(inner) = expected_ut.inner_type() {
if let UnifiedType::Named(name) = inner {
let n = name.as_str();
if matches!(n, "SV" | "GV" | "HV" | "AV" | "CV" | "IO") {
if matches!(&arg_expr, syn::Expr::Call(_) | syn::Expr::MethodCall(_)) {
return cast_syn_expr(arg_expr, expected_ty);
}
}
}
}
}
arg_expr
}
fn try_build_common_macro_fn_call(
&mut self,
func: &Expr,
args: &[Expr],
info: Option<&MacroInferInfo>,
) -> Option<syn::Expr> {
use crate::syn_codegen::*;
let member_id = match &func.kind {
ExprKind::Member { member, .. } | ExprKind::PtrMember { member, .. } => *member,
_ => return None,
};
let mut is_fn_ptr = self
.fields_dict
.and_then(|d| d.canonical_field(member_id).map(|(_, f)| f.is_fn_pointer))
.unwrap_or(false);
if !is_fn_ptr {
let field_name = self.interner.get(member_id);
if let Some(ut) = self.field_type_map.get(field_name) {
let ty_str = ut.to_rust_string();
if type_str_is_fn_pointer(&ty_str) {
is_fn_ptr = true;
} else if let Some(dict) = self.rust_decl_dict {
if let Some(alias) = dict.types.get(&ty_str) {
if type_str_is_fn_pointer(&alias.ty) {
is_fn_ptr = true;
}
}
}
}
}
if !is_fn_ptr {
return None;
}
let field_access = self.build_syn_expr(func, info);
let callee = method_call(field_access, "unwrap_unchecked", vec![]);
let mut punctuated = syn::punctuated::Punctuated::new();
for (i, arg) in args.iter().enumerate() {
let s = self.build_arg_string_unified(arg, info, None, i);
let parsed = syn::parse_str(&s).unwrap_or_else(|_| int_lit(0));
punctuated.push(parsed);
}
Some(syn::Expr::Call(syn::ExprCall {
attrs: vec![],
func: Box::new(callee),
paren_token: Default::default(),
args: punctuated,
}))
}
fn build_lvalue_syn_expr(&mut self, expr: &Expr, info: Option<&MacroInferInfo>) -> syn::Expr {
if let ExprKind::MacroCall { expanded, .. } = &expr.kind {
return self.build_syn_expr(expanded, info);
}
if let ExprKind::Call { func, args } = &expr.kind {
if let Some(expanded) = self.try_expand_call_as_lvalue_syn(func, args, info) {
return expanded;
}
let syn_expr = self.build_syn_expr(expr, info);
let s = crate::syn_codegen::expr_to_string(&syn_expr);
self.codegen_errors.push(format!("invalid lvalue: {} cannot be assigned to", s));
return syn_expr;
}
self.build_syn_expr(expr, info)
}
fn build_inc_dec_syn_expr(&mut self, inner: &Expr, info: Option<&MacroInferInfo>,
is_inc: bool, is_post: bool) -> syn::Expr {
use crate::syn_codegen::*;
let lv = self.build_lvalue_syn_expr(inner, info);
let is_ptr = self.is_pointer_expr_unified(inner, info)
|| self.infer_expr_type_unified(inner, info).is_some_and(|ut| ut.is_pointer());
let step_stmt: syn::Stmt = if is_ptr {
let method = if is_inc { "wrapping_add" } else { "wrapping_sub" };
let call = method_call(lv.clone(), method, vec![int_lit(1)]);
semi_stmt(assign_expr(lv.clone(), call))
} else {
let op = if is_inc {
syn::BinOp::AddAssign(Default::default())
} else {
syn::BinOp::SubAssign(Default::default())
};
semi_stmt(assign_op_expr(lv.clone(), op, int_lit(1)))
};
if is_post {
let save = let_stmt("_t", lv.clone());
block_with_value(vec![save, step_stmt], ident_expr("_t"))
} else {
block_with_value(vec![step_stmt], lv)
}
}
fn build_assign_syn_expr(&mut self, op: AssignOp, lhs: &Expr, rhs: &Expr,
info: Option<&MacroInferInfo>) -> syn::Expr {
use crate::syn_codegen::*;
let l = self.build_lvalue_syn_expr(lhs, info);
let lhs_ut = self.infer_expr_type_unified(lhs, info);
let r: syn::Expr = if is_null_literal(rhs) && op == AssignOp::Assign {
match &lhs_ut {
Some(lut) if lut.is_pointer() => {
if lut.is_const_pointer() {
syn::parse_str("std::ptr::null()").unwrap_or_else(|_| int_lit(0))
} else {
syn::parse_str("std::ptr::null_mut()").unwrap_or_else(|_| int_lit(0))
}
}
Some(_) => int_lit(0),
None => syn::parse_str("std::ptr::null_mut()").unwrap_or_else(|_| int_lit(0)),
}
} else {
let mut r_expr = self.build_syn_expr(rhs, info);
if op == AssignOp::Assign {
if let Some(ref lut) = lhs_ut {
if lut.is_float() {
if let ExprKind::IntLit(n) = &rhs.kind {
r_expr = syn::parse_str(&format!("{}.0", n))
.unwrap_or_else(|_| int_lit(0));
}
}
}
if let Some(ref lut) = lhs_ut {
if let Some(rut) = self.infer_expr_type_unified(rhs, info) {
let ls = lut.to_rust_string();
let rs = rut.to_rust_string();
if let (Some(nl), Some(nr)) = (
normalize_integer_type(&ls),
normalize_integer_type(&rs),
) {
if !integer_types_compatible(nl, nr) {
r_expr = cast_syn_expr(r_expr, nl);
}
}
else if lut.is_pointer() && rut.is_pointer()
&& lut.is_const_pointer() != rut.is_const_pointer()
{
r_expr = cast_syn_expr(r_expr, &ls);
}
else if lut.is_pointer() && rut.is_pointer()
&& ls != rs
&& is_sv_subtype_cast(&rut, lut)
{
r_expr = cast_syn_expr(r_expr, &ls);
}
}
}
}
r_expr
};
if op == AssignOp::Assign {
if let syn::Expr::MethodCall(mc) = &l {
if mc.args.is_empty() {
let method_name = mc.method.to_string();
if self.is_bitfield_method(&method_name) {
let setter_name = format!("set_{}", method_name);
let arg_val = if let Some(dict) = self.rust_decl_dict {
let ret_ty = dict.bitfield_method_types.iter()
.find(|((_, m), _)| m == &method_name)
.map(|(_, ty)| ty.clone());
if let Some(ty) = ret_ty {
cast_syn_expr(r, &ty)
} else {
r
}
} else {
r
};
let setter_call = method_call(
(*mc.receiver).clone(),
&setter_name,
vec![arg_val],
);
let stmt = semi_stmt(setter_call);
return block_with_value(vec![stmt], l);
}
}
}
}
let stmt: syn::Stmt = match op {
AssignOp::Assign => semi_stmt(assign_expr(l.clone(), r)),
AssignOp::AddAssign | AssignOp::SubAssign => {
let is_ptr = self.is_pointer_expr_unified(lhs, info)
|| lhs_ut.as_ref().is_some_and(|ut| ut.is_pointer());
if is_ptr {
let method = if op == AssignOp::AddAssign { "wrapping_add" } else { "wrapping_sub" };
let r_usize = cast_syn_expr(r, "usize");
let call = method_call(l.clone(), method, vec![r_usize]);
semi_stmt(assign_expr(l.clone(), call))
} else {
let syn_op = c_assign_op_to_syn_compound(op).unwrap();
semi_stmt(assign_op_expr(l.clone(), syn_op, r))
}
}
AssignOp::AndAssign | AssignOp::OrAssign | AssignOp::XorAssign => {
let lt = &lhs_ut;
let rt = self.infer_expr_type_unified(rhs, info);
let r_final = {
let mut casted = false;
let mut ret = r;
if let (Some(lut), Some(rut)) = (lt, &rt) {
let ls = lut.to_rust_string();
let rs = rut.to_rust_string();
let nl = normalize_integer_type(&ls);
let nr = normalize_integer_type(&rs);
if nl.is_some() && nr.is_some() && nl != nr {
ret = cast_syn_expr(ret, nl.unwrap());
casted = true;
}
if !casted && self.is_rust_enum_type(rut) && nl.is_some() {
ret = cast_syn_expr(ret, nl.unwrap());
casted = true;
}
}
if !casted {
if let (Some(lut), None) = (lt, &rt) {
let ls = lut.to_rust_string();
if let Some(nl) = normalize_integer_type(&ls) {
ret = cast_syn_expr(ret, nl);
}
}
}
ret
};
let syn_op = c_assign_op_to_syn_compound(op).unwrap();
semi_stmt(assign_op_expr(l.clone(), syn_op, r_final))
}
_ => {
let syn_op = c_assign_op_to_syn_compound(op).unwrap();
semi_stmt(assign_op_expr(l.clone(), syn_op, r))
}
};
block_with_value(vec![stmt], l)
}
fn build_lvalue_string(&mut self, expr: &Expr, info: Option<&MacroInferInfo>) -> String {
let syn_expr = self.build_lvalue_syn_expr(expr, info);
normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr))
}
fn build_syn_expr_with_type_hint(&mut self, expr: &Expr, info: Option<&MacroInferInfo>,
type_hint: Option<&str>) -> syn::Expr {
use crate::syn_codegen::*;
if let Some(ty) = type_hint {
let ut = UnifiedType::from_rust_str(ty);
if ut.is_pointer() && is_null_literal(expr) {
return syn::parse_str(&null_ptr_expr(&ut)).unwrap_or_else(|_| int_lit(0));
}
if ut.is_bool() {
match &expr.kind {
ExprKind::IntLit(0) => return syn::parse_str("false").unwrap(),
ExprKind::IntLit(1) => return syn::parse_str("true").unwrap(),
_ => {}
}
}
}
self.build_syn_expr(expr, info)
}
fn build_return_stmt(&mut self, expr: &Expr, indent: &str, info: Option<&MacroInferInfo>) -> String {
use crate::syn_codegen::*;
if let Some(ref rt) = self.current_return_type {
if rt.is_pointer() && is_null_literal(expr) {
return format!("{}return {};", indent, null_ptr_expr(rt));
}
if rt.is_bool() {
match &expr.kind {
ExprKind::IntLit(0) => return format!("{}return false;", indent),
ExprKind::IntLit(1) => return format!("{}return true;", indent),
_ => {
let mut syn_expr = self.build_syn_expr(expr, info);
if !self.is_bool_expr_with_dict(expr) && !is_bool_syn_expr(&syn_expr) {
syn_expr = wrap_as_bool(syn_expr);
}
let s = normalize_parens(&expr_to_string(&syn_expr));
return format!("{}return {};", indent, s);
}
}
}
}
let mut syn_expr = self.build_syn_expr(expr, info);
syn_expr = self.cast_return_syn_expr_if_needed(expr, info, syn_expr);
let s = normalize_parens(&expr_to_string(&syn_expr));
format!("{}return {};", indent, s)
}
fn cast_return_syn_expr_if_needed(&self, expr: &Expr, info: Option<&MacroInferInfo>,
syn_expr: syn::Expr) -> syn::Expr {
let Some(ret_ut) = &self.current_return_type else { return syn_expr };
let Some(expr_ut) = self.infer_expr_type_unified(expr, info) else { return syn_expr };
let ret_s = ret_ut.to_rust_string();
let expr_s = expr_ut.to_rust_string();
if self.is_rust_enum_type(&expr_ut) {
if let Some(nr) = normalize_integer_type(&ret_s) {
return crate::syn_codegen::cast_syn_expr(syn_expr, nr);
}
}
if let (Some(nr), Some(ne)) = (normalize_integer_type(&ret_s), normalize_integer_type(&expr_s)) {
if !integer_types_compatible(nr, ne) {
return crate::syn_codegen::cast_syn_expr(syn_expr, nr);
}
}
if ret_ut.is_pointer() && expr_ut.is_pointer() && ret_s != expr_s
&& pointer_inner_compatible(ret_ut, &expr_ut)
{
return crate::syn_codegen::cast_syn_expr(syn_expr, &ret_s);
}
syn_expr
}
fn build_assign_stmt(&mut self, op: &AssignOp, lhs: &Expr, rhs: &Expr, indent: &str, info: Option<&MacroInferInfo>) -> String {
use crate::syn_codegen::*;
let l = self.build_lvalue_string(lhs, info);
let lhs_ut = self.infer_expr_type_unified(lhs, info);
let r_syn: syn::Expr = if is_null_literal(rhs) && *op == AssignOp::Assign {
match &lhs_ut {
Some(lut) if lut.is_pointer() => {
let s = if lut.is_const_pointer() { "std::ptr::null()" } else { "std::ptr::null_mut()" };
syn::parse_str(s).unwrap_or_else(|_| int_lit(0))
}
Some(_) => int_lit(0),
None => syn::parse_str("std::ptr::null_mut()").unwrap_or_else(|_| int_lit(0)),
}
} else {
let mut r_syn = self.build_syn_expr(rhs, info);
if *op == AssignOp::Assign {
if let Some(ref lut) = lhs_ut {
if lut.is_float() {
if let ExprKind::IntLit(n) = &rhs.kind {
r_syn = syn::parse_str(&format!("{}.0", n))
.unwrap_or_else(|_| int_lit(0));
}
}
}
if let Some(ref lut) = lhs_ut {
if let Some(rut) = self.infer_expr_type_unified(rhs, info) {
let ls = lut.to_rust_string();
let rs = rut.to_rust_string();
if self.is_rust_enum_type(&rut) {
if let Some(nl) = normalize_integer_type(&ls) {
r_syn = cast_syn_expr(r_syn, nl);
}
} else if let (Some(nl), Some(nr)) = (normalize_integer_type(&ls), normalize_integer_type(&rs)) {
if nl != nr {
r_syn = cast_syn_expr(r_syn, nl);
}
}
if pointer_const_differs(lut, &rut) {
r_syn = cast_syn_expr(r_syn, &ls);
} else if lut.is_pointer() && rut.is_pointer()
&& ls != rs
&& pointer_inner_compatible(lut, &rut)
{
r_syn = cast_syn_expr(r_syn, &ls);
}
else if lut.is_pointer() && rut.is_pointer()
&& ls != rs
&& is_sv_subtype_cast(&rut, lut)
{
r_syn = cast_syn_expr(r_syn, &ls);
}
}
}
} else if matches!(op, AssignOp::AndAssign | AssignOp::OrAssign | AssignOp::XorAssign) {
let rt = self.infer_expr_type_unified(rhs, info);
if let (Some(lut), Some(rut)) = (&lhs_ut, &rt) {
let ls = lut.to_rust_string();
let rs = rut.to_rust_string();
let nl = normalize_integer_type(&ls);
let nr = normalize_integer_type(&rs);
if nl.is_some() && nr.is_some() && nl != nr {
r_syn = cast_syn_expr(r_syn, nl.unwrap());
} else if self.is_rust_enum_type(rut) && nl.is_some() {
r_syn = cast_syn_expr(r_syn, nl.unwrap());
}
} else if let (Some(lut), None) = (&lhs_ut, &rt) {
let ls = lut.to_rust_string();
if let Some(nl) = normalize_integer_type(&ls) {
r_syn = cast_syn_expr(r_syn, nl);
}
}
}
r_syn
};
match op {
AssignOp::Assign => {
let r = normalize_parens(&expr_to_string(&r_syn));
format!("{}{} = {};", indent, l, r)
}
AssignOp::AddAssign | AssignOp::SubAssign => {
if self.is_pointer_expr_unified(lhs, info)
|| lhs_ut.as_ref().is_some_and(|ut| ut.is_pointer()) {
let method = if *op == AssignOp::AddAssign { "wrapping_add" } else { "wrapping_sub" };
let r_usize = cast_syn_expr(r_syn, "usize");
let r = expr_to_string(&r_usize);
format!("{}{} = {}.{}({});", indent, l, l, method, r)
} else {
let r = normalize_parens(&expr_to_string(&r_syn));
format!("{}{} {} {};", indent, l, assign_op_to_rust(*op), r)
}
}
_ => {
let r = normalize_parens(&expr_to_string(&r_syn));
format!("{}{} {} {};", indent, l, assign_op_to_rust(*op), r)
}
}
}
fn build_expr_string(&mut self, expr: &Expr, info: Option<&MacroInferInfo>) -> String {
let syn_expr = self.build_syn_expr(expr, info);
normalize_parens(&crate::syn_codegen::expr_to_string(&syn_expr))
}
fn stmt_to_rust(&mut self, stmt: &Stmt, info: &MacroInferInfo) -> String {
match stmt {
Stmt::Expr(Some(expr), _) => {
format!("{};", self.build_expr_string(expr, Some(info)))
}
Stmt::Expr(None, _) => ";".to_string(),
Stmt::Return(Some(expr), _) => self.build_return_stmt(expr, "", Some(info)),
Stmt::Return(None, _) => "return;".to_string(),
_ => self.todo_marker("stmt")
}
}
fn type_name_to_rust(&mut self, type_name: &crate::ast::TypeName) -> String {
let base_type = self.decl_specs_to_rust(&type_name.specs);
let mut result = if let Some(ref decl) = type_name.declarator {
self.apply_derived_to_type(&base_type, &decl.derived)
} else {
base_type
};
if type_name.specs.qualifiers.is_const {
if let Some(pos) = result.rfind("*mut ") {
result.replace_range(pos..pos + 5, "*const ");
}
}
result
}
fn decl_specs_to_rust(&mut self, specs: &DeclSpecs) -> String {
for spec in &specs.type_specs {
if let TypeSpec::TypedefName(name) = spec {
if let Some(generic_name) = self.current_type_param_map.get(name) {
return generic_name.clone();
}
let name_str = self.interner.get(*name).to_string();
if !self.known_symbols.contains(&name_str) {
self.codegen_errors.push(format!("undefined type: {}", name_str));
}
return name_str;
}
}
let mut is_void = false;
let mut is_char = false;
let mut is_int = false;
let mut is_short = false;
let mut is_long = 0usize;
let mut is_unsigned = false;
let mut is_float = false;
let mut is_double = false;
for spec in &specs.type_specs {
match spec {
TypeSpec::Void => is_void = true,
TypeSpec::Char => is_char = true,
TypeSpec::Int => is_int = true,
TypeSpec::Short => is_short = true,
TypeSpec::Long => is_long += 1,
TypeSpec::Unsigned => is_unsigned = true,
TypeSpec::Signed => {}
TypeSpec::Float => is_float = true,
TypeSpec::Double => is_double = true,
TypeSpec::Bool => return "bool".to_string(),
TypeSpec::Struct(spec) => {
if let Some(n) = spec.name {
return self.interner.get(n).to_string();
} else {
return self.type_marker().to_string();
}
}
TypeSpec::Union(spec) => {
if let Some(n) = spec.name {
return self.interner.get(n).to_string();
} else {
return self.type_marker().to_string();
}
}
TypeSpec::Enum(spec) => {
if let Some(n) = spec.name {
return self.interner.get(n).to_string();
} else {
return "c_int".to_string();
}
}
_ => {}
}
}
if is_void {
return "()".to_string();
}
if is_float {
return "c_float".to_string();
}
if is_double {
return if is_long > 0 { "c_longdouble".to_string() } else { "c_double".to_string() };
}
if is_char {
return if is_unsigned { "c_uchar".to_string() } else { "c_char".to_string() };
}
if is_short {
return if is_unsigned { "c_ushort".to_string() } else { "c_short".to_string() };
}
if is_long >= 2 {
return if is_unsigned { "c_ulonglong".to_string() } else { "c_longlong".to_string() };
}
if is_long == 1 {
return if is_unsigned { "c_ulong".to_string() } else { "c_long".to_string() };
}
if is_int || is_unsigned {
return if is_unsigned { "c_uint".to_string() } else { "c_int".to_string() };
}
self.type_marker().to_string()
}
fn apply_derived_to_type(&mut self, base: &str, derived: &[DerivedDecl]) -> String {
let fn_idx = derived
.iter()
.position(|d| matches!(d, DerivedDecl::Function(_)));
if let Some(idx) = fn_idx {
if let DerivedDecl::Function(param_list) = &derived[idx] {
let is_fn_pointer =
idx > 0 && matches!(derived[idx - 1], DerivedDecl::Pointer(_));
let return_end = if is_fn_pointer { idx - 1 } else { idx };
let return_derived = &derived[..return_end];
let return_type = self.apply_simple_derived(base, return_derived);
let params: Vec<_> = param_list
.params
.iter()
.map(|p| self.param_type_only(p))
.collect();
let params_str = params.join(", ");
let fn_type =
format!("unsafe extern \"C\" fn({}) -> {}", params_str, return_type);
if is_fn_pointer {
return format!("Option<{}>", fn_type);
}
return fn_type;
}
}
self.apply_simple_derived(base, derived)
}
fn apply_simple_derived(&self, base: &str, derived: &[DerivedDecl]) -> String {
self.apply_simple_derived_with_specs_const(base, derived, false)
}
fn apply_simple_derived_with_specs_const(&self, base: &str, derived: &[DerivedDecl], specs_is_const: bool) -> String {
let mut result = base.to_string();
let mut is_first_pointer = true;
for d in derived.iter().rev() {
match d {
DerivedDecl::Pointer(quals) => {
if result == "()" {
result = "c_void".to_string();
}
let pointee_const = if is_first_pointer {
specs_is_const
} else {
quals.is_const
};
if pointee_const {
result = format!("*const {}", result);
} else {
result = format!("*mut {}", result);
}
is_first_pointer = false;
}
DerivedDecl::Array(arr) => {
if result == "()" {
result = "c_void".to_string();
}
if let Some(ref size_expr) = arr.size {
if let ExprKind::IntLit(n) = &size_expr.kind {
result = format!("[{}; {}]", result, n);
} else {
result = format!("*mut {}", result);
}
} else {
result = format!("*mut {}", result);
}
}
DerivedDecl::Function(_) => {
}
}
}
result
}
fn param_type_only(&mut self, param: &ParamDecl) -> String {
let ty = self.decl_specs_to_rust(¶m.specs);
if let Some(ref declarator) = param.declarator {
self.apply_simple_derived_with_specs_const(&ty, &declarator.derived, param.specs.qualifiers.is_const)
} else {
ty
}
}
pub fn generate_inline_fn(mut self, name: crate::InternedStr, func_def: &FunctionDef) -> GeneratedCode {
let name_str = self.interner.get(name);
let mut_params = {
let mut all_names = HashSet::new();
for d in &func_def.declarator.derived {
if let DerivedDecl::Function(param_list) = d {
for p in ¶m_list.params {
if let Some(ref declarator) = p.declarator {
if let Some(param_name) = declarator.name {
all_names.insert(param_name);
}
}
}
}
}
for item in &func_def.body.items {
if let BlockItem::Decl(decl) = item {
for init_decl in &decl.declarators {
if let Some(var_name) = init_decl.declarator.name {
all_names.insert(var_name);
}
}
}
}
let mut result = HashSet::new();
for item in &func_def.body.items {
if let BlockItem::Stmt(stmt) = item {
collect_mut_params_from_stmt(stmt, &all_names, &mut result);
}
}
result
};
self.mut_local_names = mut_params.clone();
let params_str = self.build_fn_param_list(&func_def.declarator.derived, &mut_params);
let return_type = self.decl_specs_to_rust(&func_def.specs);
let return_derived: Vec<_> = func_def.declarator.derived.iter()
.filter(|d| !matches!(d, DerivedDecl::Function(_)))
.cloned()
.collect();
let return_type = self.apply_simple_derived_with_specs_const(&return_type, &return_derived, func_def.specs.qualifiers.is_const);
self.current_return_type = Some(UnifiedType::from_rust_str(&return_type));
for d in &func_def.declarator.derived {
if let DerivedDecl::Function(param_list) = d {
for p in ¶m_list.params {
if let Some(ref declarator) = p.declarator {
if let Some(param_name) = declarator.name {
let ty = self.param_type_only(p);
self.current_param_types.insert(param_name, UnifiedType::from_rust_str(&ty));
self.current_local_names.insert(param_name);
}
}
}
}
}
self.collect_local_names_recursive(&func_def.body);
let is_thx_dependent = self.perl_threaded
&& self.is_inline_fn_thx_dependent(&func_def.declarator.derived);
let thx_info = if is_thx_dependent { " [THX]" } else { "" };
self.dump_ast_comment_for_body(name_str, &func_def.body);
self.writeln(&format!("/// {}{} - inline function", name_str, thx_info));
self.writeln("#[inline]");
self.writeln("#[allow(unsafe_op_in_unsafe_fn)]");
self.writeln(&format!("pub unsafe fn {}({}) -> {} {{", name_str, params_str, return_type));
let needs_unsafe = func_def.function_call_count > 0 || func_def.deref_count > 0;
if needs_unsafe {
self.writeln(" unsafe {");
let body_str = self.compound_stmt_to_string(&func_def.body, " ");
self.buffer.push_str(&body_str);
self.writeln(" }");
} else {
let body_str = self.compound_stmt_to_string(&func_def.body, " ");
self.buffer.push_str(&body_str);
}
self.writeln("}");
self.writeln("");
self.into_generated_code()
}
fn build_fn_param_list(&mut self, derived: &[DerivedDecl], mut_params: &HashSet<InternedStr>) -> String {
for d in derived {
if let DerivedDecl::Function(param_list) = d {
if is_void_only_param_list(¶m_list.params) {
return String::new();
}
let params: Vec<_> = param_list.params.iter()
.map(|p| self.param_decl_to_rust(p, mut_params))
.collect();
let mut result = params.join(", ");
if param_list.is_variadic {
if !result.is_empty() {
result.push_str(", ");
}
result.push_str("...");
}
return result;
}
}
String::new()
}
fn is_inline_fn_thx_dependent(&self, derived: &[DerivedDecl]) -> bool {
for d in derived {
if let DerivedDecl::Function(param_list) = d {
if let Some(first_param) = param_list.params.first() {
if let Some(ref declarator) = first_param.declarator {
if let Some(name) = declarator.name {
let name_str = self.interner.get(name);
return name_str == "my_perl";
}
}
}
return false;
}
}
false
}
fn param_decl_to_rust(&mut self, param: &ParamDecl, mut_params: &HashSet<InternedStr>) -> String {
let param_name_interned = param.declarator
.as_ref()
.and_then(|d| d.name);
let name = param_name_interned
.map(|n| escape_rust_keyword(self.interner.get(n)))
.unwrap_or_else(|| "_".to_string());
let ty = self.decl_specs_to_rust(¶m.specs);
let ty = if let Some(ref declarator) = param.declarator {
self.apply_simple_derived_with_specs_const(&ty, &declarator.derived, param.specs.qualifiers.is_const)
} else {
ty
};
if let Some(n) = param_name_interned {
self.current_param_types.insert(n, UnifiedType::from_rust_str(&ty));
}
let mut_prefix = if param_name_interned.is_some_and(|n| mut_params.contains(&n)) {
"mut "
} else {
""
};
format!("{}{}: {}", mut_prefix, name, ty)
}
fn decl_to_rust_let(&mut self, decl: &Declaration, indent: &str) -> String {
let mut result = String::new();
let base_type = self.decl_specs_to_rust(&decl.specs);
for init_decl in &decl.declarators {
let name = init_decl.declarator.name
.map(|n| escape_rust_keyword(self.interner.get(n)))
.unwrap_or_else(|| "_".to_string());
let ty = self.apply_derived_to_type(&base_type, &init_decl.declarator.derived);
if let Some(ref init) = init_decl.init {
match init {
Initializer::Expr(expr) => {
let mut init_syn = self.build_syn_expr(expr, None);
if let Some(expr_ut) = self.infer_expr_type_inline(expr) {
let decl_s = ty.clone();
let expr_s = expr_ut.to_rust_string();
let nd = normalize_integer_type(&decl_s);
let ne = normalize_integer_type(&expr_s);
if let (Some(d), Some(e)) = (nd, ne) {
if !integer_types_compatible(d, e) {
init_syn = crate::syn_codegen::cast_syn_expr(init_syn, d);
}
}
}
let decl_is_mut_ptr = ty.contains("*mut ") && !ty.contains("*const ");
let decl_is_const_ptr = ty.contains("*const ") && !ty.contains("*mut ");
if let Some(expr_ut) = self.infer_expr_type_inline(expr) {
if expr_ut.is_pointer() {
if decl_is_mut_ptr && expr_ut.is_const_pointer() {
init_syn = crate::syn_codegen::cast_syn_expr(init_syn, &ty);
} else if decl_is_const_ptr && !expr_ut.is_const_pointer() {
init_syn = crate::syn_codegen::cast_syn_expr(init_syn, &ty);
}
}
}
let init_expr = normalize_parens(&crate::syn_codegen::expr_to_string(&init_syn));
let init_expr = if is_null_literal(expr) && ty.contains("*mut") {
"std::ptr::null_mut()".to_string()
} else if is_null_literal(expr) && ty.contains("*const") {
"std::ptr::null()".to_string()
} else {
init_expr
};
let mut_kw = if init_decl.declarator.name.is_some_and(|n| self.mut_local_names.contains(&n)) { "mut " } else { "" };
result.push_str(&format!("{}let {}{}: {} = {};\n", indent, mut_kw, name, ty, strip_outer_parens(&init_expr)));
}
Initializer::List(_) => {
result.push_str(&format!("{}let {}: {} = /* init list */;\n", indent, name, ty));
}
}
} else {
let mut_kw = if init_decl.declarator.name.is_some_and(|n| self.mut_local_names.contains(&n)) { "mut " } else { "" };
result.push_str(&format!("{}let {}{}: {}; // uninitialized\n", indent, mut_kw, name, ty));
}
}
result
}
fn compound_stmt_to_string(&mut self, stmt: &CompoundStmt, indent: &str) -> String {
let mut result = String::new();
for item in &stmt.items {
match item {
BlockItem::Decl(decl) => {
self.collect_decl_types(decl);
result.push_str(&self.decl_to_rust_let(decl, indent));
}
BlockItem::Stmt(s) => {
let rust_stmt = self.stmt_to_rust_inline(s, indent);
result.push_str(&rust_stmt);
result.push('\n');
}
}
}
result
}
fn stmt_to_rust_inline(&mut self, stmt: &Stmt, indent: &str) -> String {
match stmt {
Stmt::Expr(Some(expr), _) => {
if let ExprKind::Assign { op, lhs, rhs } = &expr.kind {
self.build_assign_stmt(op, lhs, rhs, indent, None)
} else {
format!("{}{};", indent, self.build_expr_string(expr, None))
}
}
Stmt::Expr(None, _) => String::new(),
Stmt::Return(Some(expr), _) => self.build_return_stmt(expr, indent, None),
Stmt::Return(None, _) => format!("{}return;", indent),
Stmt::If { cond, then_stmt, else_stmt, .. } => {
let cond_str = self.build_expr_string(cond, None);
let cond_bool = self.wrap_as_bool_condition_inline(cond, &cond_str);
let mut result = format!("{}if {} {{\n", indent, normalize_parens(&cond_bool));
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(then_stmt, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
if let Some(else_stmt) = else_stmt {
result.push_str(" else {\n");
result.push_str(&self.stmt_to_rust_inline(else_stmt, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
}
result
}
Stmt::Compound(compound) => {
let mut result = format!("{}{{\n", indent);
for item in &compound.items {
match item {
BlockItem::Stmt(s) => {
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(s, &nested_indent));
result.push_str("\n");
}
BlockItem::Decl(decl) => {
self.collect_decl_types(decl);
let nested_indent = format!("{} ", indent);
result.push_str(&self.decl_to_rust_let(decl, &nested_indent));
}
}
}
result.push_str(&format!("{}}}", indent));
result
}
Stmt::While { cond, body, .. } => {
let cond_str = self.build_expr_string(cond, None);
let cond_bool = self.wrap_as_bool_condition_inline(cond, &cond_str);
let mut result = format!("{}while {} {{\n", indent, cond_bool);
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(body, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
result
}
Stmt::For { init, cond, step, body, .. } => {
let mut result = format!("{}{{\n", indent);
let nested_indent = format!("{} ", indent);
if let Some(for_init) = init {
match for_init {
ForInit::Expr(expr) => {
result.push_str(&format!("{}{};\n", nested_indent, self.build_expr_string(expr, None)));
}
ForInit::Decl(decl) => {
self.collect_decl_types(decl);
result.push_str(&self.decl_to_rust_let(decl, &nested_indent));
}
}
}
if let Some(cond_expr) = cond {
let cond_str = self.build_expr_string(cond_expr, None);
let cond_bool = self.wrap_as_bool_condition_inline(cond_expr, &cond_str);
result.push_str(&format!("{}while {} {{\n", nested_indent, cond_bool));
} else {
result.push_str(&format!("{}loop {{\n", nested_indent));
}
let body_indent = format!("{} ", nested_indent);
result.push_str(&self.stmt_to_rust_inline(body, &body_indent));
result.push_str("\n");
if let Some(step_expr) = step {
result.push_str(&format!("{}{};\n", body_indent, self.build_expr_string(step_expr, None)));
}
result.push_str(&format!("{}}}\n", nested_indent));
result.push_str(&format!("{}}}", indent));
result
}
Stmt::DoWhile { body, cond, .. } => {
if is_zero_constant(cond) {
if !stmt_contains_top_level_break(body) {
let mut result = format!("{}{{\n", indent);
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(body, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
return result;
}
let mut result = format!("{}loop {{\n", indent);
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(body, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{} break;\n", indent));
result.push_str(&format!("{}}}", indent));
return result;
}
let mut result = format!("{}loop {{\n", indent);
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(body, &nested_indent));
result.push_str("\n");
let cond_str = self.build_expr_string(cond, None);
let break_cond = if is_boolean_expr(cond) {
normalize_parens(&format!("!({})", cond_str))
} else {
format!("{} == 0", cond_str)
};
result.push_str(&format!("{} if {} {{ break; }}\n", indent, break_cond));
result.push_str(&format!("{}}}", indent));
result
}
Stmt::Switch { expr, body, .. } => {
let expr_str = self.build_expr_string(expr, None);
let mut result = format!("{}match {} {{\n", indent, expr_str);
let nested_indent = format!("{} ", indent);
self.collect_switch_cases(body, &nested_indent, &mut result);
result.push_str(&format!("{}}}", indent));
result
}
Stmt::Case { expr: case_expr, stmt: case_stmt, .. } => {
let case_val = self.build_expr_string(case_expr, None);
let mut result = format!("{}{} => {{\n", indent, case_val);
let body_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(case_stmt, &body_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
result
}
Stmt::Default { stmt: default_stmt, .. } => {
let mut result = format!("{}_ => {{\n", indent);
let body_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(default_stmt, &body_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
result
}
Stmt::Goto(label, _) => {
let label_str = self.interner.get(*label);
format!("{}break '{}; // goto", indent, label_str)
}
Stmt::Label { name, stmt: label_stmt, .. } => {
let label_str = self.interner.get(*name);
let mut result = format!("{}'{}: {{\n", indent, label_str);
let nested_indent = format!("{} ", indent);
result.push_str(&self.stmt_to_rust_inline(label_stmt, &nested_indent));
result.push_str("\n");
result.push_str(&format!("{}}}", indent));
result
}
Stmt::Break(_) => format!("{}break;", indent),
Stmt::Continue(_) => format!("{}continue;", indent),
_ => self.todo_marker(&format!("{:?}", std::mem::discriminant(stmt)))
}
}
fn collect_switch_cases(&mut self, stmt: &Stmt, indent: &str, result: &mut String) {
struct SwitchCase {
patterns: Vec<String>, body_stmts: Vec<String>,
is_default: bool,
}
let mut cases: Vec<SwitchCase> = Vec::new();
let body_indent = format!("{} ", indent);
fn collect_items<'a>(stmt: &'a Stmt, items: &mut Vec<&'a BlockItem>) {
if let Stmt::Compound(compound) = stmt {
for item in &compound.items {
items.push(item);
}
}
}
fn flatten_case_chain<'a>(stmt: &'a Stmt, patterns: &mut Vec<&'a Expr>) -> (&'a Stmt, bool) {
match stmt {
Stmt::Case { expr, stmt: inner_stmt, .. } => {
patterns.push(expr);
flatten_case_chain(inner_stmt, patterns)
}
Stmt::Default { stmt: inner_stmt, .. } => {
(inner_stmt, true)
}
other => (other, false)
}
}
let mut items: Vec<&BlockItem> = Vec::new();
collect_items(stmt, &mut items);
for item in items {
match item {
BlockItem::Stmt(s) => {
match s {
Stmt::Case { expr: case_expr, stmt: case_stmt, .. } => {
let mut patterns: Vec<&Expr> = vec![case_expr];
let (final_stmt, has_default) = flatten_case_chain(case_stmt, &mut patterns);
let pattern_strs: Vec<String> = patterns.iter()
.map(|e| self.expr_to_rust_pattern(e))
.collect();
let body_stmts = if matches!(final_stmt, Stmt::Break(_)) {
vec![]
} else {
vec![self.stmt_to_rust_inline(final_stmt, &body_indent)]
};
cases.push(SwitchCase {
patterns: pattern_strs,
body_stmts,
is_default: has_default,
});
}
Stmt::Default { stmt: default_stmt, .. } => {
let mut patterns: Vec<&Expr> = Vec::new();
let (final_stmt, _) = flatten_case_chain(default_stmt, &mut patterns);
let pattern_strs: Vec<String> = patterns.iter()
.map(|e| self.expr_to_rust_pattern(e))
.collect();
let body_stmts = if matches!(final_stmt, Stmt::Break(_)) {
vec![]
} else {
vec![self.stmt_to_rust_inline(final_stmt, &body_indent)]
};
cases.push(SwitchCase {
patterns: pattern_strs,
body_stmts,
is_default: true,
});
}
Stmt::Break(_) => {
}
other => {
if let Some(last) = cases.last_mut() {
last.body_stmts.push(self.stmt_to_rust_inline(other, &body_indent));
}
}
}
}
BlockItem::Decl(decl) => {
self.collect_decl_types(decl);
if let Some(last) = cases.last_mut() {
last.body_stmts.push(self.decl_to_rust_let(decl, &body_indent));
}
}
}
}
let has_default = cases.iter().any(|c| c.is_default);
for case in &cases {
let pattern = if case.is_default {
if case.patterns.is_empty() {
"_".to_string()
} else {
format!("{} | _", case.patterns.join(" | "))
}
} else {
case.patterns.join(" | ")
};
result.push_str(&format!("{}{} => {{\n", indent, pattern));
for stmt in &case.body_stmts {
result.push_str(stmt);
result.push_str("\n");
}
result.push_str(&format!("{}}}\n", indent));
}
if !has_default {
result.push_str(&format!("{}_ => {{}}\n", indent));
}
}
fn expr_to_field_path(&self, expr: &Expr) -> Option<String> {
match &expr.kind {
ExprKind::Ident(name) => {
Some(self.interner.get(*name).to_string())
}
ExprKind::Member { expr: base, member } => {
let base_path = self.expr_to_field_path(base)?;
let member_name = self.interner.get(*member);
Some(format!("{}.{}", base_path, member_name))
}
_ => None,
}
}
fn expr_to_rust_pattern(&mut self, expr: &Expr) -> String {
match &expr.kind {
ExprKind::Ident(name) => {
if let Some(enum_name) = self.enum_dict.get_enum_for_variant(*name) {
let enum_str = self.interner.get(enum_name);
let variant_str = self.interner.get(*name);
format!("crate::{}::{}", enum_str, variant_str)
} else {
escape_rust_keyword(self.interner.get(*name))
}
}
_ => self.build_expr_string(expr, None)
}
}
}
impl<'a, W: Write> CodegenDriver<'a, W> {
pub fn new(
writer: W,
interner: &'a StringInterner,
enum_dict: &'a EnumDict,
macro_ctx: &'a MacroInferContext,
bindings_info: BindingsInfo,
config: CodegenConfig,
) -> Self {
Self {
writer,
interner,
enum_dict,
macro_ctx,
bindings_info,
config,
stats: CodegenStats::default(),
used_libc_fns: HashSet::new(),
successfully_generated_inlines: HashSet::new(),
generatable_macros: HashSet::new(),
const_pointer_params: HashMap::new(),
bool_return_macros: HashSet::new(),
perl_threaded: true,
}
}
pub fn with_perl_threaded(mut self, threaded: bool) -> Self {
self.perl_threaded = threaded;
self
}
pub fn stats(&self) -> &CodegenStats {
&self.stats
}
pub fn generate(&mut self, result: &InferResult) -> io::Result<()> {
self.perl_threaded = result.perl_build_mode.is_threaded();
let missing_structs = crate::struct_emitter::emit_missing_structs(
&result.fields_dict,
result.rust_decl_dict.as_ref(),
self.interner,
);
let static_arrays = crate::static_array_emitter::emit_static_arrays(
&result.global_const_dict,
&result.fields_dict,
result.rust_decl_dict.as_ref(),
self.interner,
);
for n in &static_arrays.emitted_names {
self.bindings_info.static_arrays.insert(n.clone());
}
for (n, t) in &static_arrays.emitted_types {
self.bindings_info.static_types.insert(n.clone(), t.clone());
}
let mut known_symbols = KnownSymbols::new(result, self.interner);
for n in &missing_structs.emitted_struct_names {
known_symbols.insert(n.clone());
}
for n in &missing_structs.emitted_typedef_names {
known_symbols.insert(n.clone());
}
for (struct_name, methods) in &missing_structs.bitfield_methods {
self.bindings_info
.bitfield_methods
.entry(struct_name.clone())
.or_default()
.extend(methods.iter().cloned());
}
writeln!(self.writer, "// Auto-generated Rust bindings")?;
writeln!(self.writer, "// Generated by libperl-macrogen")?;
writeln!(self.writer)?;
self.generate_use_statements()?;
self.generate_enum_imports(result)?;
if !missing_structs.source.is_empty() {
self.writer.write_all(missing_structs.source.as_bytes())?;
}
if !static_arrays.source.is_empty() {
self.writer.write_all(static_arrays.source.as_bytes())?;
}
self.precompute_macro_generability(result, &known_symbols);
for (&name, info) in &result.infer_ctx.macros {
if info.is_bool_return {
self.bool_return_macros.insert(name);
}
}
if self.config.emit_inline_fns {
self.generate_inline_fns(result, &known_symbols)?;
}
if self.config.emit_macros {
self.generate_macros(result, &known_symbols)?;
}
if !self.used_libc_fns.is_empty() {
let mut fns: Vec<_> = self.used_libc_fns.iter().cloned().collect();
fns.sort();
writeln!(self.writer, "use libc::{{{}}};", fns.join(", "))?;
}
Ok(())
}
fn generate_use_statements(&mut self) -> io::Result<()> {
let statements = if self.config.use_statements.is_empty() {
CodegenConfig::default_use_statements()
} else {
self.config.use_statements.clone()
};
if !statements.is_empty() {
for stmt in &statements {
writeln!(self.writer, "{};", stmt)?;
}
writeln!(self.writer)?;
}
Ok(())
}
fn generate_enum_imports(&mut self, result: &InferResult) -> io::Result<()> {
let enum_names = result.enum_dict.target_enum_names(self.interner);
let bindings_enums = result.rust_decl_dict.as_ref().map(|d| &d.enums);
let filtered_names: Vec<_> = enum_names
.into_iter()
.filter(|name| {
bindings_enums.map_or(true, |enums| enums.contains(*name))
})
.collect();
if !filtered_names.is_empty() {
writeln!(self.writer, "// Enum variant imports")?;
for name in filtered_names {
writeln!(self.writer, "#[allow(unused_imports)]")?;
writeln!(self.writer, "use crate::{}::*;", name)?;
}
writeln!(self.writer)?;
}
Ok(())
}
fn precompute_macro_generability(&mut self, result: &InferResult, known_symbols: &KnownSymbols) {
let macros: Vec<_> = result.infer_ctx.macros.iter()
.filter(|(_, info)| self.should_include_macro(info))
.collect();
let included_set: HashSet<InternedStr> = macros.iter().map(|(n, _)| **n).collect();
let sorted_names = self.topological_sort_macros(¯os);
for name in sorted_names {
let info = result.infer_ctx.macros.get(&name).unwrap();
if info.apidoc_suppressed {
continue;
}
let has_cascade_failure = info.called_functions.iter().any(|called| {
if included_set.contains(called) {
return result.infer_ctx.macros.get(called)
.map(|u| u.is_parseable() && !u.is_unavailable_for_codegen())
.unwrap_or(false)
&& !self.generatable_macros.contains(called);
}
false
});
if has_cascade_failure {
continue;
}
let status = self.get_macro_status(info);
if status == GenerateStatus::Success {
let codegen = RustCodegen::new(
self.interner, self.enum_dict, self.macro_ctx,
self.bindings_info.clone(), &known_symbols,
result.rust_decl_dict.as_ref(), Some(&result.inline_fn_dict),
).with_perl_threaded(self.perl_threaded);
let generated = codegen.generate_macro(info);
if generated.is_complete() && !generated.has_unresolved_names() {
self.generatable_macros.insert(name);
}
}
}
}
pub fn generate_inline_fns(&mut self, result: &InferResult, known_symbols: &KnownSymbols) -> io::Result<()> {
writeln!(self.writer, "// =============================================================================")?;
writeln!(self.writer, "// Inline Functions")?;
writeln!(self.writer, "// =============================================================================")?;
writeln!(self.writer)?;
let mut fns: Vec<_> = result.inline_fn_dict.iter()
.filter(|(_, func_def)| func_def.is_target)
.collect();
fns.sort_by_key(|(name, _)| self.interner.get(**name));
let inline_set: HashSet<InternedStr> = fns.iter().map(|(n, _)| **n).collect();
enum InlineGenResult {
CallsUnavailable,
ContainsGoto,
UnresolvedNames { code: String, unresolved: Vec<String> },
CodegenError { code: String, errors: Vec<String> },
Incomplete { code: String },
Success { code: String, used_libc: HashSet<String> },
Suppressed { reason: String },
}
let mut gen_results: Vec<(InternedStr, InlineGenResult)> = Vec::new();
for (name, func_def) in &fns {
if result.inline_fn_dict.is_apidoc_suppressed(**name) {
let n_str = self.interner.get(**name);
let reason = result.apidoc_patches.skip_reason(n_str)
.unwrap_or("apidoc skip_codegen")
.to_string();
gen_results.push((**name,
InlineGenResult::Suppressed { reason }));
continue;
}
if result.inline_fn_dict.is_calls_unavailable(**name) {
gen_results.push((**name, InlineGenResult::CallsUnavailable));
continue;
}
if block_items_contain_goto(&func_def.body.items) {
gen_results.push((**name, InlineGenResult::ContainsGoto));
continue;
}
let codegen = RustCodegen::new(self.interner, self.enum_dict, self.macro_ctx, self.bindings_info.clone(), known_symbols, result.rust_decl_dict.as_ref(), Some(&result.inline_fn_dict))
.with_perl_threaded(self.perl_threaded)
.with_dump_ast_for(self.config.dump_ast_for.clone())
.with_dump_types_for(self.config.dump_types_for.clone())
.with_fields_dict(&result.fields_dict)
.with_bool_return(false, self.bool_return_macros.clone());
let generated = codegen.generate_inline_fn(**name, func_def);
if generated.has_unresolved_names() {
gen_results.push((**name, InlineGenResult::UnresolvedNames {
code: generated.code,
unresolved: generated.unresolved_names,
}));
} else if !generated.codegen_errors.is_empty() {
gen_results.push((**name, InlineGenResult::CodegenError {
code: generated.code,
errors: generated.codegen_errors,
}));
} else if generated.is_complete() {
gen_results.push((**name, InlineGenResult::Success {
code: generated.code,
used_libc: generated.used_libc_fns,
}));
} else {
gen_results.push((**name, InlineGenResult::Incomplete {
code: generated.code,
}));
}
}
for (name, gen_result) in &gen_results {
if matches!(gen_result, InlineGenResult::Success { .. }) {
self.successfully_generated_inlines.insert(*name);
}
}
let mut changed = true;
while changed {
changed = false;
let current_success = self.successfully_generated_inlines.clone();
for (name, _) in &gen_results {
if !current_success.contains(name) {
continue;
}
if let Some(calls) = result.inline_fn_dict.get_called_functions(*name) {
let has_unavailable = calls.iter().any(|called| {
if inline_set.contains(called) && !current_success.contains(called) {
return true;
}
if let Some(macro_info) = result.infer_ctx.macros.get(called) {
if macro_info.is_target && self.should_include_macro(macro_info) {
if !self.generatable_macros.contains(called) {
return true;
}
}
}
false
});
if has_unavailable {
self.successfully_generated_inlines.remove(name);
changed = true;
}
}
}
}
for (name, gen_result) in gen_results {
match gen_result {
InlineGenResult::CallsUnavailable => {
let name_str = self.interner.get(name);
let calls = result.inline_fn_dict.get_called_functions(name);
let absent: Vec<String> = calls
.map(|cs| cs.iter()
.filter(|c| {
let fn_name = self.interner.get(**c);
!self.is_function_available(**c, fn_name, result)
})
.map(|c| self.interner.get(*c).to_string())
.collect())
.unwrap_or_default();
let cascade_deps: Vec<String> = calls
.map(|cs| cs.iter()
.filter(|c| {
let is_unavailable_inline = result.inline_fn_dict.get(**c).is_some()
&& result.inline_fn_dict.is_unavailable_for_codegen(**c);
let is_unavailable_macro = result.infer_ctx.macros.get(c)
.map(|info| info.is_unavailable_for_codegen())
.unwrap_or(false);
is_unavailable_inline || is_unavailable_macro
})
.map(|c| self.interner.get(*c).to_string())
.collect())
.unwrap_or_default();
if absent.is_empty() {
writeln!(self.writer,
"// [CASCADE_UNAVAILABLE] {} - dependency not generated: {}",
name_str, cascade_deps.join(", "))?;
} else {
writeln!(self.writer,
"// [CALLS_UNAVAILABLE] {} - calls unavailable function(s): {}",
name_str, absent.join(", "))?;
}
writeln!(self.writer)?;
self.stats.inline_fns_cascade_unavailable += 1;
}
InlineGenResult::ContainsGoto => {
let name_str = self.interner.get(name);
writeln!(self.writer, "// [CONTAINS_GOTO] {} - excluded (contains goto)", name_str)?;
writeln!(self.writer)?;
self.stats.inline_fns_contains_goto += 1;
}
InlineGenResult::Suppressed { reason } => {
let name_str = self.interner.get(name);
writeln!(self.writer,
"// [CODEGEN_SUPPRESSED] {} - inline function (apidoc patch)",
name_str)?;
writeln!(self.writer, "// Reason: {}", reason)?;
writeln!(self.writer)?;
}
InlineGenResult::UnresolvedNames { code, unresolved } => {
let name_str = self.interner.get(name);
writeln!(self.writer, "// [UNRESOLVED_NAMES] {} - inline function", name_str)?;
writeln!(self.writer, "// Unresolved: {}", unresolved.join(", "))?;
for line in code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.inline_fns_unresolved_names += 1;
}
InlineGenResult::CodegenError { code, errors } => {
let name_str = self.interner.get(name);
writeln!(self.writer, "// [CODEGEN_ERROR] {} - inline function", name_str)?;
for err in &errors {
writeln!(self.writer, "// {}", err)?;
}
for line in code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
}
InlineGenResult::Incomplete { code } => {
let name_str = self.interner.get(name);
writeln!(self.writer, "// [CODEGEN_INCOMPLETE] {} - inline function", name_str)?;
for line in code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.inline_fns_type_incomplete += 1;
}
InlineGenResult::Success { code, used_libc } => {
if self.successfully_generated_inlines.contains(&name) {
write!(self.writer, "{}", code)?;
self.used_libc_fns.extend(used_libc.iter().cloned());
self.stats.inline_fns_success += 1;
} else {
let name_str = self.interner.get(name);
let unavailable: Vec<String> = result.inline_fn_dict.get_called_functions(name)
.map(|calls| calls.iter()
.filter(|c| inline_set.contains(c) && !self.successfully_generated_inlines.contains(c))
.map(|c| self.interner.get(*c).to_string())
.collect())
.unwrap_or_default();
writeln!(self.writer, "// [CASCADE_UNAVAILABLE] {} - dependency not generated: {}",
name_str, unavailable.join(", "))?;
for line in code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.inline_fns_cascade_unavailable += 1;
}
}
}
}
writeln!(self.writer)?;
Ok(())
}
pub fn generate_macros(&mut self, result: &InferResult, known_symbols: &KnownSymbols) -> io::Result<()> {
writeln!(self.writer, "// =============================================================================")?;
writeln!(self.writer, "// Macro Functions")?;
writeln!(self.writer, "// =============================================================================")?;
writeln!(self.writer)?;
let macros: Vec<_> = result.infer_ctx.macros.iter()
.filter(|(_, info)| self.should_include_macro(info))
.collect();
let included_set: HashSet<InternedStr> = macros.iter().map(|(n, _)| **n).collect();
let sorted_names = self.topological_sort_macros(¯os);
let mut callee_const_params: HashMap<InternedStr, HashSet<usize>> = HashMap::new();
let mut bool_return_macros: HashSet<InternedStr> = HashSet::new();
for (&name, info) in &result.infer_ctx.macros {
if !info.const_pointer_positions.is_empty() {
callee_const_params.insert(name, info.const_pointer_positions.clone());
}
if info.is_bool_return {
bool_return_macros.insert(name);
}
}
self.const_pointer_params = callee_const_params;
self.bool_return_macros = bool_return_macros;
let mut successfully_generated: HashSet<InternedStr> = HashSet::new();
for name in sorted_names {
let info = result.infer_ctx.macros.get(&name).unwrap();
if info.apidoc_suppressed {
let name_str_for_patch = self.interner.get(name);
let reason = result.apidoc_patches.skip_reason(name_str_for_patch)
.unwrap_or("apidoc skip_codegen");
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer,
"// [CODEGEN_SUPPRESSED] {}{} - macro function (apidoc patch)",
name_str_for_patch, thx_info)?;
writeln!(self.writer, "// Reason: {}", reason)?;
writeln!(self.writer)?;
continue;
}
let unavailable_deps: Vec<String> = info.called_functions.iter()
.filter(|called| {
if included_set.contains(called) {
return result.infer_ctx.macros.get(called)
.map(|u| u.is_parseable() && !u.is_unavailable_for_codegen())
.unwrap_or(false)
&& !successfully_generated.contains(called);
}
if result.inline_fn_dict.get(**called)
.map(|f| f.is_target)
.unwrap_or(false)
{
return !self.successfully_generated_inlines.contains(called);
}
false
})
.map(|called| self.interner.get(*called).to_string())
.collect();
if !unavailable_deps.is_empty() {
self.generate_macro_cascade_unavailable(info, &unavailable_deps)?;
self.stats.macros_cascade_unavailable += 1;
continue;
}
let status = self.get_macro_status(info);
match status {
GenerateStatus::Success => {
let const_positions = self.const_pointer_params.get(&name)
.cloned().unwrap_or_default();
let is_bool = self.bool_return_macros.contains(&name);
let codegen = RustCodegen::new(self.interner, self.enum_dict, self.macro_ctx, self.bindings_info.clone(), known_symbols, result.rust_decl_dict.as_ref(), Some(&result.inline_fn_dict))
.with_perl_threaded(self.perl_threaded)
.with_dump_ast_for(self.config.dump_ast_for.clone())
.with_dump_types_for(self.config.dump_types_for.clone())
.with_fields_dict(&result.fields_dict)
.with_const_pointer_positions(const_positions)
.with_bool_return(is_bool, self.bool_return_macros.clone());
let generated = codegen.generate_macro(info);
if generated.has_unresolved_names() {
let name_str = self.interner.get(info.name);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer, "// [UNRESOLVED_NAMES] {}{} - macro function", name_str, thx_info)?;
writeln!(self.writer, "// Unresolved: {}", generated.unresolved_names.join(", "))?;
for line in generated.code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.macros_unresolved_names += 1;
} else if !generated.codegen_errors.is_empty() {
let name_str = self.interner.get(info.name);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer, "// [CODEGEN_ERROR] {}{} - macro function", name_str, thx_info)?;
for err in &generated.codegen_errors {
writeln!(self.writer, "// {}", err)?;
}
for line in generated.code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
} else if generated.is_complete() {
write!(self.writer, "{}", generated.code)?;
self.used_libc_fns.extend(generated.used_libc_fns.iter().cloned());
self.stats.macros_success += 1;
successfully_generated.insert(name);
} else {
let name_str = self.interner.get(info.name);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer, "// [CODEGEN_INCOMPLETE] {}{} - macro function", name_str, thx_info)?;
for line in generated.code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.macros_type_incomplete += 1;
}
}
GenerateStatus::ParseFailed => {
self.generate_macro_parse_failed(info)?;
self.stats.macros_parse_failed += 1;
}
GenerateStatus::TypeIncomplete => {
self.generate_macro_type_incomplete(info, result)?;
self.stats.macros_type_incomplete += 1;
}
GenerateStatus::CallsUnavailable => {
let absent: Vec<&str> = info.called_functions.iter()
.filter(|fn_id| {
let fn_name = self.interner.get(**fn_id);
!self.is_function_available(**fn_id, fn_name, result)
})
.map(|fn_id| self.interner.get(*fn_id))
.collect();
if absent.is_empty() {
let cascade_deps: Vec<String> = info.called_functions.iter()
.filter(|fn_id| {
if let Some(m) = result.infer_ctx.macros.get(*fn_id) {
return m.is_unavailable_for_codegen();
}
if result.inline_fn_dict.get(**fn_id).is_some() {
return result.inline_fn_dict
.is_unavailable_for_codegen(**fn_id);
}
false
})
.map(|fn_id| self.interner.get(*fn_id).to_string())
.collect();
self.generate_macro_cascade_unavailable(info, &cascade_deps)?;
self.stats.macros_cascade_unavailable += 1;
} else {
self.generate_macro_calls_unavailable(info, result)?;
self.stats.macros_calls_unavailable += 1;
}
}
GenerateStatus::ContainsGoto => {
let name_str = self.interner.get(info.name);
writeln!(self.writer, "// [CONTAINS_GOTO] {} - excluded (contains goto)", name_str)?;
writeln!(self.writer)?;
}
GenerateStatus::GenericUnsupported => {
let name_str = self.interner.get(info.name);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer, "// [GENERIC_UNSUPPORTED] {}{} - Rust cannot cast to generic type T", name_str, thx_info)?;
let const_positions = self.const_pointer_params.get(&name)
.cloned().unwrap_or_default();
let is_bool = self.bool_return_macros.contains(&name);
let codegen = RustCodegen::new(self.interner, self.enum_dict, self.macro_ctx, self.bindings_info.clone(), known_symbols, result.rust_decl_dict.as_ref(), Some(&result.inline_fn_dict))
.with_perl_threaded(self.perl_threaded)
.with_fields_dict(&result.fields_dict)
.with_const_pointer_positions(const_positions)
.with_bool_return(is_bool, self.bool_return_macros.clone());
let generated = codegen.generate_macro(info);
for line in generated.code.lines() {
writeln!(self.writer, "// {}", line)?;
}
writeln!(self.writer)?;
self.stats.macros_generic_unsupported += 1;
}
GenerateStatus::Skip => {
}
}
}
Ok(())
}
fn topological_sort_macros(
&self,
macros: &[(&InternedStr, &MacroInferInfo)],
) -> Vec<InternedStr> {
use std::collections::VecDeque;
let macro_set: HashSet<InternedStr> = macros.iter().map(|(n, _)| **n).collect();
let mut in_degree: HashMap<InternedStr, usize> = HashMap::new();
let mut dependents: HashMap<InternedStr, Vec<InternedStr>> = HashMap::new();
for (name, _) in macros {
in_degree.insert(**name, 0);
}
for (name, info) in macros {
for used in &info.uses {
if macro_set.contains(used) {
*in_degree.entry(**name).or_insert(0) += 1;
dependents.entry(*used).or_default().push(**name);
}
}
}
let mut queue: VecDeque<InternedStr> = {
let mut zeros: Vec<_> = in_degree.iter()
.filter(|(_, deg)| **deg == 0)
.map(|(name, _)| *name)
.collect();
zeros.sort_by_key(|n| self.interner.get(*n));
zeros.into_iter().collect()
};
let mut result = Vec::with_capacity(macros.len());
while let Some(name) = queue.pop_front() {
result.push(name);
if let Some(deps) = dependents.get(&name) {
let mut newly_ready: Vec<InternedStr> = Vec::new();
for dep in deps {
if let Some(deg) = in_degree.get_mut(dep) {
*deg -= 1;
if *deg == 0 {
newly_ready.push(*dep);
}
}
}
newly_ready.sort_by_key(|n| self.interner.get(*n));
for n in newly_ready {
queue.push_back(n);
}
}
}
if result.len() < macros.len() {
let result_set: HashSet<_> = result.iter().copied().collect();
let mut remaining: Vec<_> = macro_set.iter()
.filter(|n| !result_set.contains(n))
.copied()
.collect();
remaining.sort_by_key(|n| self.interner.get(*n));
result.extend(remaining);
}
result
}
fn generate_macro_cascade_unavailable(
&mut self,
info: &MacroInferInfo,
unavailable_deps: &[String],
) -> io::Result<()> {
let name_str = self.interner.get(info.name);
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer,
"// [CASCADE_UNAVAILABLE] {}{} - dependency not generated: {}",
name_str, thx_info, unavailable_deps.join(", "))?;
writeln!(self.writer)?;
Ok(())
}
fn should_include_macro(&self, info: &MacroInferInfo) -> bool {
if !info.is_target {
return false;
}
if !info.has_body {
return false;
}
info.is_function
}
fn get_macro_status(&self, info: &MacroInferInfo) -> GenerateStatus {
if info.calls_unavailable {
return GenerateStatus::CallsUnavailable;
}
if !info.generic_type_params.is_empty() {
return GenerateStatus::GenericUnsupported;
}
match &info.parse_result {
ParseResult::Unparseable(_) => GenerateStatus::ParseFailed,
ParseResult::Statement(items) => {
if block_items_contain_goto(items) {
return GenerateStatus::ContainsGoto;
}
if info.is_fully_confirmed() {
GenerateStatus::Success
} else {
GenerateStatus::TypeIncomplete
}
}
ParseResult::Expression(_) => {
if info.is_fully_confirmed() {
GenerateStatus::Success
} else {
GenerateStatus::TypeIncomplete
}
}
}
}
fn generate_macro_parse_failed(&mut self, info: &MacroInferInfo) -> io::Result<()> {
let name_str = self.interner.get(info.name);
let params_str = if info.is_function {
let params: Vec<_> = info.params.iter()
.map(|p| self.interner.get(p.name).to_string())
.collect();
format!("({})", params.join(", "))
} else {
String::new()
};
writeln!(self.writer, "// [PARSE_FAILED] {}{}", name_str, params_str)?;
if let ParseResult::Unparseable(Some(err_msg)) = &info.parse_result {
writeln!(self.writer, "// Error: {}", err_msg)?;
}
writeln!(self.writer, "// (tokens not available in parsed form)")?;
writeln!(self.writer)?;
Ok(())
}
fn generate_macro_calls_unavailable(&mut self, info: &MacroInferInfo, result: &InferResult) -> io::Result<()> {
let name_str = self.interner.get(info.name);
let params_str = if info.is_function {
let params: Vec<_> = info.params.iter()
.map(|p| self.interner.get(p.name).to_string())
.collect();
format!("({})", params.join(", "))
} else {
String::new()
};
let thx_info = if info.is_thx_dependent { " [THX]" } else { "" };
writeln!(self.writer, "// [CALLS_UNAVAILABLE] {}{}{} - calls unavailable function(s)", name_str, params_str, thx_info)?;
let unavailable_fns: Vec<_> = info.called_functions.iter()
.filter(|&fn_id| {
let fn_name = self.interner.get(*fn_id);
!self.is_function_available(*fn_id, fn_name, result)
})
.map(|fn_id| self.interner.get(*fn_id))
.collect();
if !unavailable_fns.is_empty() {
writeln!(self.writer, "// Unavailable: {}", unavailable_fns.join(", "))?;
}
writeln!(self.writer)?;
Ok(())
}
fn is_function_available(&self, fn_id: crate::InternedStr, fn_name: &str, result: &InferResult) -> bool {
if result.infer_ctx.macros.contains_key(&fn_id) {
return true;
}
if let Some(rust_decl_dict) = &result.rust_decl_dict {
if rust_decl_dict.fns.contains_key(fn_name) {
return true;
}
}
if result.inline_fn_dict.get(fn_id).is_some() {
return true;
}
let builtin_fns = [
"__builtin_expect",
"__builtin_offsetof",
"offsetof",
"__builtin_types_compatible_p",
"__builtin_constant_p",
"__builtin_choose_expr",
"__builtin_unreachable",
"__builtin_trap",
"__builtin_assume",
"__builtin_bswap16",
"__builtin_bswap32",
"__builtin_bswap64",
"__builtin_popcount",
"__builtin_clz",
"__builtin_ctz",
"pthread_mutex_lock",
"pthread_mutex_unlock",
"pthread_rwlock_rdlock",
"pthread_rwlock_wrlock",
"pthread_rwlock_unlock",
"memchr",
"memcpy",
"memmove",
"memset",
"strlen",
"strcmp",
"strncmp",
"strcpy",
"strncpy",
"ASSERT_IS_LITERAL",
"ASSERT_IS_PTR",
"ASSERT_NOT_PTR",
];
if builtin_fns.contains(&fn_name) {
return true;
}
false
}
fn generate_macro_type_incomplete(&mut self, info: &MacroInferInfo, result: &InferResult) -> io::Result<()> {
let name_str = self.interner.get(info.name);
let params_str = if info.is_function {
let params: Vec<_> = info.params.iter()
.map(|p| self.interner.get(p.name).to_string())
.collect();
format!("({})", params.join(", "))
} else {
String::new()
};
writeln!(self.writer, "// [TYPE_INCOMPLETE] {}{}", name_str, params_str)?;
writeln!(self.writer, "// Args status: {:?}, Return status: {:?}",
info.args_infer_status, info.return_infer_status)?;
writeln!(self.writer, "// Typed S-expression:")?;
self.write_typed_sexp_comment(info, result)?;
writeln!(self.writer)?;
Ok(())
}
fn write_typed_sexp_comment(&mut self, info: &MacroInferInfo, _result: &InferResult) -> io::Result<()> {
match &info.parse_result {
ParseResult::Expression(expr) => {
self.write_expr_sexp_comment(expr, info, "// ")?;
}
ParseResult::Statement(block_items) => {
for item in block_items {
if let BlockItem::Stmt(stmt) = item {
self.write_stmt_sexp_comment(stmt, info, "// ")?;
}
}
}
ParseResult::Unparseable(_) => {
writeln!(self.writer, "// (unparseable)")?;
}
}
Ok(())
}
fn write_expr_sexp_comment(&mut self, expr: &Expr, info: &MacroInferInfo, prefix: &str) -> io::Result<()> {
let mut buf = Vec::new();
{
let mut printer = SexpPrinter::new(&mut buf, self.interner);
let _ = printer.print_expr(expr);
}
let sexp_str = String::from_utf8_lossy(&buf);
let type_info = self.get_expr_type_info(expr, info);
for line in sexp_str.lines() {
writeln!(self.writer, "{}{}", prefix, line)?;
}
if !type_info.is_empty() {
writeln!(self.writer, "{} :type {}", prefix, type_info)?;
}
Ok(())
}
fn write_stmt_sexp_comment(&mut self, stmt: &Stmt, _info: &MacroInferInfo, prefix: &str) -> io::Result<()> {
let mut buf = Vec::new();
{
let mut printer = SexpPrinter::new(&mut buf, self.interner);
let _ = printer.print_stmt(stmt);
}
let sexp_str = String::from_utf8_lossy(&buf);
for line in sexp_str.lines() {
writeln!(self.writer, "{}{}", prefix, line)?;
}
Ok(())
}
fn get_expr_type_info(&self, expr: &Expr, info: &MacroInferInfo) -> String {
if let Some(constraints) = info.type_env.expr_constraints.get(&expr.id) {
if let Some(first) = constraints.first() {
return first.ty.to_display_string(self.interner);
}
}
"<unknown>".to_string()
}
}