From 459f575d8d432b88b2f9f7dc291b6ffba2adf48f Mon Sep 17 00:00:00 2001 From: Rua Date: Tue, 16 Dec 2025 16:21:45 +0100 Subject: [PATCH 1/4] transpile: Split off new `macros` module --- c2rust-transpile/src/translator/macros.rs | 251 ++++++++++++++++++++++ c2rust-transpile/src/translator/mod.rs | 235 +------------------- 2 files changed, 255 insertions(+), 231 deletions(-) create mode 100644 c2rust-transpile/src/translator/macros.rs diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs new file mode 100644 index 0000000000..d6354a9106 --- /dev/null +++ b/c2rust-transpile/src/translator/macros.rs @@ -0,0 +1,251 @@ +use c2rust_ast_builder::mk; +use failure::format_err; +use log::{info, trace}; +use proc_macro2::{Span, TokenStream}; +use syn::{Expr, MacroDelimiter}; + +use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind}; +use crate::diagnostics::{TranslationError, TranslationResult}; +use crate::translator::{ConvertedDecl, ExprContext, MacroExpansion, Translation}; +use crate::with_stmts::WithStmts; +use crate::TranslateMacros; + +impl<'c> Translation<'c> { + pub fn convert_macro( + &self, + ctx: ExprContext, + decl_id: CDeclId, + span: Span, + name: &str, + ) -> TranslationResult { + trace!( + "Expanding macro {:?}: {:?}", + decl_id, + self.ast_context[decl_id] + ); + + let maybe_replacement = self.recreate_const_macro_from_expansions( + ctx.const_().set_expanding_macro(decl_id), + &self.ast_context.macro_expansions[&decl_id], + ); + + match maybe_replacement { + Ok((replacement, ty)) => { + trace!(" to {:?}", replacement); + + let expansion = MacroExpansion { ty }; + self.macro_expansions + .borrow_mut() + .insert(decl_id, Some(expansion)); + let ty = self.convert_type(ty)?; + + Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item( + name, + ty, + replacement, + ))) + } + Err(e) => { + self.macro_expansions.borrow_mut().insert(decl_id, None); + info!("Could not expand macro {}: {}", name, e); + Ok(ConvertedDecl::NoItem) + } + } + } + + /// Given all of the expansions of a const macro, + /// try to recreate a Rust `const` translation + /// that is equivalent to every expansion. + /// + /// This may fail, in which case we simply don't emit a `const` + /// and leave all of the expansions as fully inlined + /// instead of referencing this `const`. + /// + /// For example, if the types of the macro expansion have no common type, + /// which is required for a Rust `const` but not a C const macro, + /// this can fail. Or there could just be a feature we don't yet support. + fn recreate_const_macro_from_expansions( + &self, + ctx: ExprContext, + expansions: &[CExprId], + ) -> TranslationResult<(Box, CTypeId)> { + let (val, ty) = expansions + .iter() + .try_fold::>, CTypeId)>, _, _>(None, |canonical, &id| { + self.can_convert_const_macro_expansion(id)?; + + let ty = self.ast_context[id] + .kind + .get_type() + .ok_or_else(|| format_err!("Invalid expression type"))?; + let expr = self.convert_expr(ctx, id, None)?; + + // Join ty and cur_ty to the smaller of the two types. If the + // types are not cast-compatible, abort the fold. + let ty_kind = self.ast_context.resolve_type(ty).kind.clone(); + if let Some((canon_val, canon_ty)) = canonical { + let canon_ty_kind = self.ast_context.resolve_type(canon_ty).kind.clone(); + if let Some(smaller_ty) = + CTypeKind::smaller_compatible_type(canon_ty_kind.clone(), ty_kind) + { + if smaller_ty == canon_ty_kind { + Ok(Some((canon_val, canon_ty))) + } else { + Ok(Some((expr, ty))) + } + } else { + Err(format_err!("Not all macro expansions are compatible types")) + } + } else { + Ok(Some((expr, ty))) + } + })? + .ok_or_else(|| format_err!("Could not find a valid type for macro"))?; + + val.to_unsafe_pure_expr() + .map(|val| (val, ty)) + .ok_or_else(|| TranslationError::generic("Macro expansion is not a pure expression")) + + // TODO: Validate that all replacements are equivalent and pick the most + // common type to minimize casts. + } + + /// Determine if we're able to convert this const macro expansion. + fn can_convert_const_macro_expansion(&self, expr_id: CExprId) -> TranslationResult<()> { + match self.tcfg.translate_const_macros { + TranslateMacros::None => Err(format_err!("translate_const_macros is None"))?, + TranslateMacros::Conservative => { + // TODO We still allow `CExprKind::ExplicitCast`s + // even though they're broken (see #853). + + // This is a top-down, pessimistic/conservative analysis. + // This is somewhat duplicative of `fn convert_expr` simply checking + // `ExprContext::is_const` and returning errors where the expr is not `const`, + // which is an non-conservative analysis scattered across all of the `fn convert_*`s. + // That's what's done for `TranslateMacros::Experimental`, + // as opposed to the conservative analysis done here for `TranslateMacros::Conservative`. + // When the conservative analysis is incomplete, + // it won't translate the macro, but the result will compile. + // But when the non-conservative analysis is incomplete, + // the resulting code may not transpile, + // which is why the conservative analysis is used for `TranslateMacros::Conservative`. + if !self.ast_context.is_const_expr(expr_id) { + Err(format_err!("non-const expr {expr_id:?}"))?; + } + Ok(()) + } + TranslateMacros::Experimental => Ok(()), + } + } + + /// Convert the expansion of a const-like macro. + /// + /// See [`TranspilerConfig::translate_const_macros`]. + /// + /// [`TranspilerConfig::translate_const_macros`]: crate::TranspilerConfig::translate_const_macros + pub fn convert_const_macro_expansion( + &self, + ctx: ExprContext, + expr_id: CExprId, + override_ty: Option, + ) -> TranslationResult>>> { + let macros = match self.ast_context.macro_invocations.get(&expr_id) { + Some(macros) => macros.as_slice(), + None => return Ok(None), + }; + + // Find the first macro after the macro we're currently expanding, if any. + let first_macro = macros + .splitn(2, |macro_id| ctx.expanding_macro(macro_id)) + .last() + .unwrap() + .first(); + let macro_id = match first_macro { + Some(macro_id) => macro_id, + None => return Ok(None), + }; + + trace!(" found macro expansion: {macro_id:?}"); + // Ensure that we've converted this macro and that it has a valid definition. + let expansion = self.macro_expansions.borrow().get(macro_id).cloned(); + let macro_ty = match expansion { + // Expansion exists. + Some(Some(expansion)) => expansion.ty, + + // Expansion wasn't possible. + Some(None) => return Ok(None), + + // We haven't tried to expand it yet. + None => { + self.convert_decl(ctx, *macro_id)?; + if let Some(Some(expansion)) = self.macro_expansions.borrow().get(macro_id) { + expansion.ty + } else { + return Ok(None); + } + } + }; + let rust_name = self + .renamer + .borrow_mut() + .get(macro_id) + .ok_or_else(|| format_err!("Macro name not declared"))?; + + self.add_import(*macro_id, &rust_name); + + let val = WithStmts::new_val(mk().path_expr(vec![rust_name])); + + let expr_kind = &self.ast_context[expr_id].kind; + // TODO We'd like to get rid of this cast eventually (see #1321). + // Currently, const macros do not get the correct `override_ty` themselves, + // so they aren't declared with the correct portable type, + // but its uses are expecting the correct portable type, + // so we need to cast it to the `override_ty` here. + let expr_ty = override_ty.or_else(|| expr_kind.get_qual_type()); + if let Some(expr_ty) = expr_ty { + self.convert_cast( + ctx, + CQualTypeId::new(macro_ty), + expr_ty, + val, + None, + None, + None, + ) + .map(Some) + } else { + Ok(Some(val)) + } + + // TODO: May need to handle volatile reads here. + // See `DeclRef` below. + } + + /// Convert the expansion of a function-like macro. + /// + /// See [`TranspilerConfig::translate_fn_macros`]. + /// + /// [`TranspilerConfig::translate_fn_macros`]: crate::TranspilerConfig::translate_fn_macros + pub fn convert_fn_macro_invocation( + &self, + _ctx: ExprContext, + text: &str, + ) -> Option>> { + match self.tcfg.translate_fn_macros { + TranslateMacros::None => return None, + TranslateMacros::Conservative => return None, // Nothing is supported for `Conservative` yet. + TranslateMacros::Experimental => {} + } + + let mut split = text.splitn(2, '('); + let ident = split.next()?.trim(); + let args = split.next()?.trim_end_matches(')'); + + let ts: TokenStream = syn::parse_str(args).ok()?; + Some(WithStmts::new_val(mk().mac_expr(mk().mac( + mk().path(ident), + ts, + MacroDelimiter::Paren(Default::default()), + )))) + } +} diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 13190694a3..958dcd2996 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -13,7 +13,7 @@ use failure::{err_msg, format_err, Fail}; use indexmap::indexmap; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; -use log::{error, info, trace, warn}; +use log::{error, trace, warn}; use proc_macro2::{Punct, Spacing::*, Span, TokenStream, TokenTree}; use syn::spanned::Spanned as _; use syn::{ @@ -37,12 +37,12 @@ use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_printer::pprust; use crate::c_ast::iterators::{DFExpr, SomeId}; +use crate::c_ast::*; use crate::cfg; use crate::convert_type::TypeConverter; use crate::renamer::Renamer; use crate::with_stmts::WithStmts; use crate::{c_ast, format_translation_err}; -use crate::{c_ast::*, TranslateMacros}; use crate::{ExternCrate, TranspilerConfig}; use c2rust_ast_exporter::clang_ast::LRValue; @@ -52,6 +52,7 @@ mod builtins; mod comments; mod enums; mod literals; +mod macros; mod main_function; mod named_references; mod operators; @@ -2248,39 +2249,7 @@ impl<'c> Translation<'c> { .get(&decl_id) .expect("Macro object not named"); - trace!( - "Expanding macro {:?}: {:?}", - decl_id, - self.ast_context[decl_id] - ); - - let maybe_replacement = self.recreate_const_macro_from_expansions( - ctx.const_().set_expanding_macro(decl_id), - &self.ast_context.macro_expansions[&decl_id], - ); - - match maybe_replacement { - Ok((replacement, ty)) => { - trace!(" to {:?}", replacement); - - let expansion = MacroExpansion { ty }; - self.macro_expansions - .borrow_mut() - .insert(decl_id, Some(expansion)); - let ty = self.convert_type(ty)?; - - Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item( - name, - ty, - replacement, - ))) - } - Err(e) => { - self.macro_expansions.borrow_mut().insert(decl_id, None); - info!("Could not expand macro {}: {}", name, e); - Ok(ConvertedDecl::NoItem) - } - } + self.convert_macro(ctx, decl_id, span, &name) } // We aren't doing anything with the definitions of function-like @@ -2298,91 +2267,6 @@ impl<'c> Translation<'c> { } } - /// Determine if we're able to convert this const macro expansion. - fn can_convert_const_macro_expansion(&self, expr_id: CExprId) -> TranslationResult<()> { - match self.tcfg.translate_const_macros { - TranslateMacros::None => Err(format_err!("translate_const_macros is None"))?, - TranslateMacros::Conservative => { - // TODO We still allow `CExprKind::ExplicitCast`s - // even though they're broken (see #853). - - // This is a top-down, pessimistic/conservative analysis. - // This is somewhat duplicative of `fn convert_expr` simply checking - // `ExprContext::is_const` and returning errors where the expr is not `const`, - // which is an non-conservative analysis scattered across all of the `fn convert_*`s. - // That's what's done for `TranslateMacros::Experimental`, - // as opposed to the conservative analysis done here for `TranslateMacros::Conservative`. - // When the conservative analysis is incomplete, - // it won't translate the macro, but the result will compile. - // But when the non-conservative analysis is incomplete, - // the resulting code may not transpile, - // which is why the conservative analysis is used for `TranslateMacros::Conservative`. - if !self.ast_context.is_const_expr(expr_id) { - Err(format_err!("non-const expr {expr_id:?}"))?; - } - Ok(()) - } - TranslateMacros::Experimental => Ok(()), - } - } - - /// Given all of the expansions of a const macro, - /// try to recreate a Rust `const` translation - /// that is equivalent to every expansion. - /// - /// This may fail, in which case we simply don't emit a `const` - /// and leave all of the expansions as fully inlined - /// instead of referencing this `const`. - /// - /// For example, if the types of the macro expansion have no common type, - /// which is required for a Rust `const` but not a C const macro, - /// this can fail. Or there could just be a feature we don't yet support. - fn recreate_const_macro_from_expansions( - &self, - ctx: ExprContext, - expansions: &[CExprId], - ) -> TranslationResult<(Box, CTypeId)> { - let (val, ty) = expansions - .iter() - .try_fold::>, CTypeId)>, _, _>(None, |canonical, &id| { - self.can_convert_const_macro_expansion(id)?; - - let ty = self.ast_context[id] - .kind - .get_type() - .ok_or_else(|| format_err!("Invalid expression type"))?; - let expr = self.convert_expr(ctx, id, None)?; - - // Join ty and cur_ty to the smaller of the two types. If the - // types are not cast-compatible, abort the fold. - let ty_kind = self.ast_context.resolve_type(ty).kind.clone(); - if let Some((canon_val, canon_ty)) = canonical { - let canon_ty_kind = self.ast_context.resolve_type(canon_ty).kind.clone(); - if let Some(smaller_ty) = - CTypeKind::smaller_compatible_type(canon_ty_kind.clone(), ty_kind) - { - if smaller_ty == canon_ty_kind { - Ok(Some((canon_val, canon_ty))) - } else { - Ok(Some((expr, ty))) - } - } else { - Err(format_err!("Not all macro expansions are compatible types")) - } - } else { - Ok(Some((expr, ty))) - } - })? - .ok_or_else(|| format_err!("Could not find a valid type for macro"))?; - - val.to_unsafe_pure_expr() - .map(|val| (val, ty)) - .ok_or_else(|| TranslationError::generic("Macro expansion is not a pure expression")) - - // TODO: Validate that all replacements are equivalent and pick the most - // common type to minimize casts. - } - fn convert_function( &self, ctx: ExprContext, @@ -4137,117 +4021,6 @@ impl<'c> Translation<'c> { Ok(val) } - /// Convert the expansion of a const-like macro. - /// - /// See [`TranspilerConfig::translate_const_macros`]. - /// - /// [`TranspilerConfig::translate_const_macros`]: crate::TranspilerConfig::translate_const_macros - fn convert_const_macro_expansion( - &self, - ctx: ExprContext, - expr_id: CExprId, - override_ty: Option, - ) -> TranslationResult>>> { - let macros = match self.ast_context.macro_invocations.get(&expr_id) { - Some(macros) => macros.as_slice(), - None => return Ok(None), - }; - - // Find the first macro after the macro we're currently expanding, if any. - let first_macro = macros - .splitn(2, |macro_id| ctx.expanding_macro(macro_id)) - .last() - .unwrap() - .first(); - let macro_id = match first_macro { - Some(macro_id) => macro_id, - None => return Ok(None), - }; - - trace!(" found macro expansion: {macro_id:?}"); - // Ensure that we've converted this macro and that it has a valid definition. - let expansion = self.macro_expansions.borrow().get(macro_id).cloned(); - let macro_ty = match expansion { - // Expansion exists. - Some(Some(expansion)) => expansion.ty, - - // Expansion wasn't possible. - Some(None) => return Ok(None), - - // We haven't tried to expand it yet. - None => { - self.convert_decl(ctx, *macro_id)?; - if let Some(Some(expansion)) = self.macro_expansions.borrow().get(macro_id) { - expansion.ty - } else { - return Ok(None); - } - } - }; - let rust_name = self - .renamer - .borrow_mut() - .get(macro_id) - .ok_or_else(|| format_err!("Macro name not declared"))?; - - self.add_import(*macro_id, &rust_name); - - let val = WithStmts::new_val(mk().path_expr(vec![rust_name])); - - let expr_kind = &self.ast_context[expr_id].kind; - // TODO We'd like to get rid of this cast eventually (see #1321). - // Currently, const macros do not get the correct `override_ty` themselves, - // so they aren't declared with the correct portable type, - // but its uses are expecting the correct portable type, - // so we need to cast it to the `override_ty` here. - let expr_ty = override_ty.or_else(|| expr_kind.get_qual_type()); - if let Some(expr_ty) = expr_ty { - self.convert_cast( - ctx, - CQualTypeId::new(macro_ty), - expr_ty, - val, - None, - None, - None, - ) - .map(Some) - } else { - Ok(Some(val)) - } - - // TODO: May need to handle volatile reads here. - // See `DeclRef` below. - } - - /// Convert the expansion of a function-like macro. - /// - /// See [`TranspilerConfig::translate_fn_macros`]. - /// - /// [`TranspilerConfig::translate_fn_macros`]: crate::TranspilerConfig::translate_fn_macros - fn convert_fn_macro_invocation( - &self, - _ctx: ExprContext, - text: &str, - ) -> Option>> { - match self.tcfg.translate_fn_macros { - TranslateMacros::None => return None, - TranslateMacros::Conservative => return None, // Nothing is supported for `Conservative` yet. - TranslateMacros::Experimental => {} - } - - let mut split = text.splitn(2, '('); - let ident = split.next()?.trim(); - let args = split.next()?.trim_end_matches(')'); - - let ts: TokenStream = syn::parse_str(args).ok()?; - Some(WithStmts::new_val(mk().mac_expr(mk().mac( - mk().path(ident), - ts, - MacroDelimiter::Paren(Default::default()), - )))) - } - /// If `ctx` is unused, convert `expr` to a semi statement, otherwise return /// `expr`. fn convert_side_effects_expr( From 46ba60f992f6bb33d22faaaa0f22273431846193 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 7 Mar 2026 13:11:00 +0100 Subject: [PATCH 2/4] transpile: Split off `expr_is_expanded_macro` helper function --- c2rust-transpile/src/translator/enums.rs | 5 +---- c2rust-transpile/src/translator/macros.rs | 12 ++++++++++++ c2rust-transpile/src/translator/pointers.rs | 7 +------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index afaa10164c..91f1c4ef8e 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -97,10 +97,7 @@ impl<'c> Translation<'c> { if self.is_variant_of_enum(enum_id, enum_constant_id) => { // `enum`s shouldn't need portable `override_ty`s. - let expr_is_macro = matches!( - self.convert_const_macro_expansion(ctx, expr, None), - Ok(Some(_)) - ); + let expr_is_macro = self.expr_is_expanded_macro(ctx, expr, None); // If this DeclRef expanded to a const macro, we actually need to insert a cast, // because the translation of a const macro skips implicit casts in its context. diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index d6354a9106..67023b247f 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -248,4 +248,16 @@ impl<'c> Translation<'c> { MacroDelimiter::Paren(Default::default()), )))) } + + pub fn expr_is_expanded_macro( + &self, + ctx: ExprContext, + expr_id: CExprId, + override_ty: Option, + ) -> bool { + matches!( + self.convert_const_macro_expansion(ctx, expr_id, override_ty), + Ok(Some(_)) + ) + } } diff --git a/c2rust-transpile/src/translator/pointers.rs b/c2rust-transpile/src/translator/pointers.rs index d7f42fb347..3a81cd3ef5 100644 --- a/c2rust-transpile/src/translator/pointers.rs +++ b/c2rust-transpile/src/translator/pointers.rs @@ -103,12 +103,7 @@ impl<'c> Translation<'c> { .ast_context .get_pointee_qual_type(pointer_cty.ctype) .ok_or_else(|| TranslationError::generic("Address-of should return a pointer"))?; - let arg_is_macro = arg.map_or(false, |arg| { - matches!( - self.convert_const_macro_expansion(ctx, arg, None), - Ok(Some(_)) - ) - }); + let arg_is_macro = arg.map_or(false, |arg| self.expr_is_expanded_macro(ctx, arg, None)); let mut needs_cast = false; let mut ref_cast_pointee_ty = None; From 0d85e862f9796e7659eb2e24f820ced9334fa464 Mon Sep 17 00:00:00 2001 From: Rua Date: Sat, 7 Mar 2026 18:18:59 +0100 Subject: [PATCH 3/4] transpile: Move `CastKind` determination out of `convert_cast` --- c2rust-transpile/src/c_ast/mod.rs | 57 +++++++++++++++++ c2rust-transpile/src/translator/macros.rs | 29 +++++---- c2rust-transpile/src/translator/mod.rs | 76 +---------------------- 3 files changed, 77 insertions(+), 85 deletions(-) diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index c01937bf6c..cf413fce92 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1792,6 +1792,63 @@ pub enum CastKind { NonAtomicToAtomic, } +impl CastKind { + pub fn from_types(source_ty_kind: &CTypeKind, target_ty_kind: &CTypeKind) -> Option { + Some(match (source_ty_kind, target_ty_kind) { + (CTypeKind::VariableArray(..), CTypeKind::Pointer(..)) + | (CTypeKind::ConstantArray(..), CTypeKind::Pointer(..)) + | (CTypeKind::IncompleteArray(..), CTypeKind::Pointer(..)) => { + CastKind::ArrayToPointerDecay + } + + (CTypeKind::Function(..), CTypeKind::Pointer(..)) => CastKind::FunctionToPointerDecay, + + (_, CTypeKind::Pointer(..)) if source_ty_kind.is_integral_type() => { + CastKind::IntegralToPointer + } + + (CTypeKind::Pointer(..), CTypeKind::Bool) => CastKind::PointerToBoolean, + + (CTypeKind::Pointer(..), _) if target_ty_kind.is_integral_type() => { + CastKind::PointerToIntegral + } + + (_, CTypeKind::Bool) if source_ty_kind.is_integral_type() => { + CastKind::IntegralToBoolean + } + + (CTypeKind::Bool, _) if target_ty_kind.is_signed_integral_type() => { + CastKind::BooleanToSignedIntegral + } + + (_, _) if source_ty_kind.is_integral_type() && target_ty_kind.is_integral_type() => { + CastKind::IntegralCast + } + + (_, _) if source_ty_kind.is_integral_type() && target_ty_kind.is_floating_type() => { + CastKind::IntegralToFloating + } + + (_, CTypeKind::Bool) if source_ty_kind.is_floating_type() => { + CastKind::FloatingToBoolean + } + + (_, _) if source_ty_kind.is_floating_type() && target_ty_kind.is_integral_type() => { + CastKind::FloatingToIntegral + } + + (_, _) if source_ty_kind.is_floating_type() && target_ty_kind.is_floating_type() => { + CastKind::FloatingCast + } + + (CTypeKind::Pointer(..), CTypeKind::Pointer(..)) => CastKind::BitCast, + + // Ignoring Complex casts for now + _ => return None, + }) + } +} + /// Represents a unary operator in C (6.5.3 Unary operators) and GNU C extensions #[derive(Debug, Clone, Copy)] pub enum UnOp { diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 67023b247f..0828e85630 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -1,10 +1,11 @@ use c2rust_ast_builder::mk; use failure::format_err; +use log::warn; use log::{info, trace}; use proc_macro2::{Span, TokenStream}; use syn::{Expr, MacroDelimiter}; -use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind}; +use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind, CastKind}; use crate::diagnostics::{TranslationError, TranslationResult}; use crate::translator::{ConvertedDecl, ExprContext, MacroExpansion, Translation}; use crate::with_stmts::WithStmts; @@ -203,16 +204,22 @@ impl<'c> Translation<'c> { // so we need to cast it to the `override_ty` here. let expr_ty = override_ty.or_else(|| expr_kind.get_qual_type()); if let Some(expr_ty) = expr_ty { - self.convert_cast( - ctx, - CQualTypeId::new(macro_ty), - expr_ty, - val, - None, - None, - None, - ) - .map(Some) + let source_cty = CQualTypeId::new(macro_ty); + let target_cty = expr_ty; + + let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; + let target_ty_kind = &self.ast_context.resolve_type(target_cty.ctype).kind; + let kind = CastKind::from_types(source_ty_kind, target_ty_kind).unwrap_or_else(|| { + warn!( + "Unknown CastKind for {:?} to {:?} cast. Defaulting to BitCast", + source_ty_kind, target_ty_kind, + ); + + CastKind::BitCast + }); + + self.convert_cast(ctx, source_cty, target_cty, val, None, kind, None) + .map(Some) } else { Ok(Some(val)) } diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 958dcd2996..c6790ded4f 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -3682,7 +3682,7 @@ impl<'c> Translation<'c> { target_ty, val, Some(expr), - Some(kind), + kind, opt_field_id, ) } @@ -4146,7 +4146,7 @@ impl<'c> Translation<'c> { target_cty: CQualTypeId, val: WithStmts>, expr: Option, - kind: Option, + kind: CastKind, opt_field_id: Option, ) -> TranslationResult>> { let source_ty_kind = &self.ast_context.resolve_type(source_cty.ctype).kind; @@ -4156,78 +4156,6 @@ impl<'c> Translation<'c> { return Ok(val); } - let kind = kind.unwrap_or_else(|| { - match (source_ty_kind, target_ty_kind) { - (CTypeKind::VariableArray(..), CTypeKind::Pointer(..)) - | (CTypeKind::ConstantArray(..), CTypeKind::Pointer(..)) - | (CTypeKind::IncompleteArray(..), CTypeKind::Pointer(..)) => { - CastKind::ArrayToPointerDecay - } - - (CTypeKind::Function(..), CTypeKind::Pointer(..)) => { - CastKind::FunctionToPointerDecay - } - - (_, CTypeKind::Pointer(..)) if source_ty_kind.is_integral_type() => { - CastKind::IntegralToPointer - } - - (CTypeKind::Pointer(..), CTypeKind::Bool) => CastKind::PointerToBoolean, - - (CTypeKind::Pointer(..), _) if target_ty_kind.is_integral_type() => { - CastKind::PointerToIntegral - } - - (_, CTypeKind::Bool) if source_ty_kind.is_integral_type() => { - CastKind::IntegralToBoolean - } - - (CTypeKind::Bool, _) if target_ty_kind.is_signed_integral_type() => { - CastKind::BooleanToSignedIntegral - } - - (_, _) - if source_ty_kind.is_integral_type() && target_ty_kind.is_integral_type() => - { - CastKind::IntegralCast - } - - (_, _) - if source_ty_kind.is_integral_type() && target_ty_kind.is_floating_type() => - { - CastKind::IntegralToFloating - } - - (_, CTypeKind::Bool) if source_ty_kind.is_floating_type() => { - CastKind::FloatingToBoolean - } - - (_, _) - if source_ty_kind.is_floating_type() && target_ty_kind.is_integral_type() => - { - CastKind::FloatingToIntegral - } - - (_, _) - if source_ty_kind.is_floating_type() && target_ty_kind.is_floating_type() => - { - CastKind::FloatingCast - } - - (CTypeKind::Pointer(..), CTypeKind::Pointer(..)) => CastKind::BitCast, - - // Ignoring Complex casts for now - _ => { - warn!( - "Unknown CastKind for {:?} to {:?} cast. Defaulting to BitCast", - source_ty_kind, target_ty_kind, - ); - - CastKind::BitCast - } - } - }); - match kind { CastKind::BitCast | CastKind::NoOp => { self.convert_pointer_to_pointer_cast(source_cty.ctype, target_cty.ctype, val) From 17aa6c7ba85d22c9de823c9a934fa94de60a41de Mon Sep 17 00:00:00 2001 From: Rua Date: Wed, 25 Mar 2026 16:18:39 +0100 Subject: [PATCH 4/4] transpile: Translate some literals in conditionals as `bool` directly --- c2rust-transpile/src/translator/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index c6790ded4f..52407c5c65 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -2634,6 +2634,16 @@ impl<'c> Translation<'c> { null_pointer_case(ptr, !target) } + CExprKind::Literal(_, ref literal @ CLiteral::Integer(0 | 1, _)) + if !self.expr_is_expanded_macro(ctx, cond_id, None) => + { + // If there is a literal `0` or `1` here, translate them directly rather than + // with a comparison. But not if they're inside a macro, we want to keep that. + // TODO: What about the `false` and `true` macros in stdbool.h? + let val = mk().lit_expr(mk().bool_lit(target == literal.get_bool())); + Ok(WithStmts::new_val(val)) + } + CExprKind::Unary(_, c_ast::UnOp::Not, subexpr_id, _) => { self.convert_condition(ctx, !target, subexpr_id) }