From 9174b2038941d90f9b8a800834bc9a53d07fb142 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 29 May 2026 18:29:32 +0200 Subject: [PATCH 1/4] transpile: Return `MacroExpansion` from `recreate_const_macro_from_expansions` --- c2rust-transpile/src/translator/macros.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 148e60a199..5c7dfe47e3 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -30,14 +30,13 @@ impl<'c> Translation<'c> { ); match maybe_replacement { - Ok((replacement, ty)) => { + Ok((replacement, expansion)) => { trace!(" to {:?}", replacement); - let expansion = MacroExpansion { ty }; + let ty = self.convert_type(expansion.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, @@ -68,7 +67,7 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, expansions: &[CExprId], - ) -> TranslationResult<(Box, CTypeId)> { + ) -> TranslationResult<(Box, MacroExpansion)> { let (val, ty) = expansions .iter() .try_fold::>, CTypeId)>, _, _>(None, |canonical, &id| { @@ -102,9 +101,10 @@ impl<'c> Translation<'c> { })? .ok_or_else(|| format_err!("Could not find a valid type for macro"))?; + let expansion = MacroExpansion { ty }; val.wrap_unsafe() .to_pure_expr() - .map(|val| (val, ty)) + .map(|val| (val, expansion)) .ok_or_else(|| TranslationError::generic("Macro expansion is not a pure expression")) // TODO: Validate that all replacements are equivalent and pick the most From c2aaec252833a5faba34cf3cd9c381df66ecf690 Mon Sep 17 00:00:00 2001 From: Rua Date: Thu, 14 May 2026 22:28:13 +0200 Subject: [PATCH 2/4] transpile: Refactor `recreate_const_macro_from_expansions` --- c2rust-transpile/src/translator/macros.rs | 76 +++++++++++++++-------- 1 file changed, 49 insertions(+), 27 deletions(-) diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 5c7dfe47e3..a90a67bdf2 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -68,40 +68,62 @@ impl<'c> Translation<'c> { ctx: ExprContext, expansions: &[CExprId], ) -> TranslationResult<(Box, MacroExpansion)> { - let (val, ty) = expansions + struct ConvertedMacroExpr { + val: WithStmts>, + type_id: CTypeId, + } + + let canonical = expansions .iter() - .try_fold::>, CTypeId)>, _, _>(None, |canonical, &id| { - self.can_convert_const_macro_expansion(id)?; + .try_fold::, _, _>( + None, + |mut canonical, &expr_id| -> TranslationResult<_> { + self.can_convert_const_macro_expansion(expr_id)?; + + let type_id = self.ast_context[expr_id] + .kind + .get_type() + .ok_or_else(|| format_err!("Invalid expression type"))?; + let val = self.convert_expr(ctx, expr_id, None)?; + let new = ConvertedMacroExpr { val, type_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. + match &mut canonical { + Some(canonical) => { + let canon_type_kind = self + .ast_context + .resolve_type(canonical.type_id) + .kind + .clone(); + let new_type_kind = + self.ast_context.resolve_type(new.type_id).kind.clone(); + let smaller_type_kind = CTypeKind::smaller_compatible_type( + canon_type_kind.clone(), + new_type_kind, + ); + let Some(smaller_type_kind) = smaller_type_kind else { + return Err( + format_err!("Not all macro expansions are compatible types") + .into() + ) + }; - // 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))) + if smaller_type_kind != canon_type_kind { + *canonical = new; + } } - } else { - Err(format_err!("Not all macro expansions are compatible types")) + + None => canonical = Some(new), } - } else { - Ok(Some((expr, ty))) - } - })? + + Ok(canonical) + }, + )? .ok_or_else(|| format_err!("Could not find a valid type for macro"))?; - let expansion = MacroExpansion { ty }; + let ConvertedMacroExpr { val, type_id } = canonical; + let expansion = MacroExpansion { ty: type_id }; val.wrap_unsafe() .to_pure_expr() .map(|val| (val, expansion)) From 5718463e8036b1239c6d6fc1c83ea9570dc5f73e Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 15 May 2026 11:23:04 +0200 Subject: [PATCH 3/4] transpile: Wrap `MacroExpansion` in `Rc` to avoid cloning --- c2rust-transpile/src/translator/macros.rs | 15 ++++++++++----- c2rust-transpile/src/translator/mod.rs | 3 +-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index a90a67bdf2..79ac56ec59 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -2,6 +2,7 @@ use c2rust_ast_builder::mk; use failure::format_err; use log::{info, trace}; use proc_macro2::{Span, TokenStream}; +use std::rc::Rc; use syn::{Expr, MacroDelimiter}; use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind}; @@ -36,7 +37,7 @@ impl<'c> Translation<'c> { let ty = self.convert_type(expansion.ty)?; self.macro_expansions .borrow_mut() - .insert(decl_id, Some(expansion)); + .insert(decl_id, Some(Rc::new(expansion))); Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item( name, @@ -191,9 +192,9 @@ impl<'c> Translation<'c> { 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 { + let expansion = match expansion { // Expansion exists. - Some(Some(expansion)) => expansion.ty, + Some(Some(expansion)) => expansion, // Expansion wasn't possible. Some(None) => return Ok(None), @@ -201,13 +202,17 @@ impl<'c> Translation<'c> { // 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 + let expansion = self.macro_expansions.borrow().get(macro_id).cloned(); + + if let Some(Some(expansion)) = expansion { + expansion } else { return Ok(None); } } }; + + let macro_ty = expansion.ty; let rust_name = self .renamer .borrow_mut() diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 888a4d5683..44550934c7 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -260,7 +260,6 @@ impl FuncContext { } } -#[derive(Clone)] struct MacroExpansion { ty: CTypeId, } @@ -283,7 +282,7 @@ pub struct Translation<'c> { zero_inits: RefCell, function_context: RefCell, potential_flexible_array_members: RefCell>, - macro_expansions: RefCell>>, + macro_expansions: RefCell>>>, /// Sets of imports deferred while translating nested expressions for caching. Imports are /// deferred when caching translations to make them pure and thus cache the translation /// alongside its required imports. Each additional nested level of caching translation From d7d679d463f6569c541e0fe3c9e6f8a68c646a73 Mon Sep 17 00:00:00 2001 From: Rua Date: Fri, 15 May 2026 13:45:09 +0200 Subject: [PATCH 4/4] transpile: Allow macro conversion to succeed only for some expansions --- c2rust-transpile/src/translator/macros.rs | 120 ++++++++++++++-------- c2rust-transpile/src/translator/mod.rs | 1 + 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 79ac56ec59..e4d6fa5937 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -1,5 +1,6 @@ use c2rust_ast_builder::mk; use failure::format_err; +use indexmap::IndexMap; use log::{info, trace}; use proc_macro2::{Span, TokenStream}; use std::rc::Rc; @@ -34,6 +35,15 @@ impl<'c> Translation<'c> { Ok((replacement, expansion)) => { trace!(" to {:?}", replacement); + for (expr_id, result) in &expansion.expr_results { + if let Err(err) = result { + info!( + "Could not convert macro {} for {:?}: {}", + name, expr_id, err + ); + } + } + let ty = self.convert_type(expansion.ty)?; self.macro_expansions .borrow_mut() @@ -74,57 +84,74 @@ impl<'c> Translation<'c> { type_id: CTypeId, } - let canonical = expansions + let mut canonical: Option = None; + let mut expr_results: IndexMap<_, _> = expansions .iter() - .try_fold::, _, _>( - None, - |mut canonical, &expr_id| -> TranslationResult<_> { - self.can_convert_const_macro_expansion(expr_id)?; - - let type_id = self.ast_context[expr_id] - .kind - .get_type() - .ok_or_else(|| format_err!("Invalid expression type"))?; - let val = self.convert_expr(ctx, expr_id, None)?; - let new = ConvertedMacroExpr { val, type_id }; - - // Join ty and cur_ty to the smaller of the two types. If the - // types are not cast-compatible, abort the fold. - match &mut canonical { - Some(canonical) => { - let canon_type_kind = self - .ast_context - .resolve_type(canonical.type_id) - .kind - .clone(); - let new_type_kind = - self.ast_context.resolve_type(new.type_id).kind.clone(); - let smaller_type_kind = CTypeKind::smaller_compatible_type( - canon_type_kind.clone(), - new_type_kind, - ); - let Some(smaller_type_kind) = smaller_type_kind else { - return Err( - format_err!("Not all macro expansions are compatible types") - .into() - ) - }; - - if smaller_type_kind != canon_type_kind { - *canonical = new; + .map(|&expr_id| { + let result = self + .can_convert_const_macro_expansion(expr_id) + .and_then(|_| { + let type_id = self.ast_context[expr_id] + .kind + .get_type() + .ok_or_else(|| format_err!("Invalid expression type"))?; + let val = self.convert_expr(ctx, expr_id, None)?; + Ok(ConvertedMacroExpr { val, type_id }) + }) + .and_then(|new| { + // Join ty and cur_ty to the smaller of the two types. If the + // types are not cast-compatible, skip this expansion. + match &mut canonical { + Some(canonical) => { + let canon_type_kind = self + .ast_context + .resolve_type(canonical.type_id) + .kind + .clone(); + let new_type_kind = + self.ast_context.resolve_type(new.type_id).kind.clone(); + let smaller_type_kind = CTypeKind::smaller_compatible_type( + canon_type_kind.clone(), + new_type_kind, + ); + let Some(smaller_type_kind) = smaller_type_kind else { + return Err( + format_err!( + "Not all macro expansions are compatible types" + ) + .into() + ) + }; + + if smaller_type_kind != canon_type_kind { + *canonical = new; + } } + + None => canonical = Some(new), } - None => canonical = Some(new), - } + Ok(()) + }); - Ok(canonical) - }, - )? - .ok_or_else(|| format_err!("Could not find a valid type for macro"))?; + (expr_id, result) + }) + .collect(); + + let Some(canonical) = canonical else { + // If none of the expansions could be converted, try returning the first error we got. + let err = match expr_results.swap_remove_index(0) { + Some((_, Err(err))) => err, + _ => format_err!("Could not find a valid type for macro").into(), + }; + return Err(err); + }; let ConvertedMacroExpr { val, type_id } = canonical; - let expansion = MacroExpansion { ty: type_id }; + let expansion = MacroExpansion { + ty: type_id, + expr_results, + }; val.wrap_unsafe() .to_pure_expr() .map(|val| (val, expansion)) @@ -212,6 +239,11 @@ impl<'c> Translation<'c> { } }; + if !matches!(expansion.expr_results.get(&expr_id), Some(Ok(_))) { + // Expansion succeeded in general, but not for this particular expression. + return Ok(None); + } + let macro_ty = expansion.ty; let rust_name = self .renamer diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index 44550934c7..66c9915898 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -262,6 +262,7 @@ impl FuncContext { struct MacroExpansion { ty: CTypeId, + expr_results: IndexMap>, } type ZeroInits = IndexMap>, IndexSet)>;