diff --git a/c2rust-transpile/src/translator/literals.rs b/c2rust-transpile/src/translator/literals.rs index e1bfeca9f3..f17f461a71 100644 --- a/c2rust-transpile/src/translator/literals.rs +++ b/c2rust-transpile/src/translator/literals.rs @@ -166,7 +166,7 @@ impl<'c> Translation<'c> { ) -> TranslationResult>> { // C compound literals are lvalues, but equivalent Rust expressions generally are not. // So if an address is needed, store it in an intermediate variable first. - if !ctx.needs_address() || ctx.expanding_macro.is_some() { + if !ctx.needs_address() || ctx.converting_macro.is_some() { return self.convert_expr(ctx, val, override_ty); } diff --git a/c2rust-transpile/src/translator/macros.rs b/c2rust-transpile/src/translator/macros.rs index 148e60a199..7345623f28 100644 --- a/c2rust-transpile/src/translator/macros.rs +++ b/c2rust-transpile/src/translator/macros.rs @@ -1,12 +1,13 @@ use c2rust_ast_builder::mk; use failure::format_err; +use indexmap::IndexMap; 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, CTypeKind}; use crate::diagnostics::{TranslationError, TranslationResult}; -use crate::translator::{ConvertedDecl, ExprContext, MacroExpansion, Translation}; +use crate::translator::{ConvertedDecl, ConvertedMacro, ExprContext, Translation}; use crate::with_stmts::WithStmts; use crate::TranslateMacros; @@ -25,29 +26,37 @@ impl<'c> Translation<'c> { ); let maybe_replacement = self.recreate_const_macro_from_expansions( - ctx.const_().set_expanding_macro(decl_id), + ctx.const_().set_converting_macro(decl_id), &self.ast_context.macro_expansions[&decl_id], ); match maybe_replacement { - Ok((replacement, ty)) => { + Ok((replacement, converted)) => { trace!(" to {:?}", replacement); - let expansion = MacroExpansion { ty }; - self.macro_expansions + for (expr_id, result) in &converted.expr_results { + if let Err(err) = result { + info!( + "Could not convert macro {} for {:?}: {}", + name, expr_id, err + ); + } + } + + let result_type_rs = self.convert_type(converted.result_type_id)?; + self.converted_macros .borrow_mut() - .insert(decl_id, Some(expansion)); - let ty = self.convert_type(ty)?; + .insert(decl_id, Some(converted)); Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item( name, - ty, + result_type_rs, replacement, ))) } Err(e) => { - self.macro_expansions.borrow_mut().insert(decl_id, None); - info!("Could not expand macro {}: {}", name, e); + self.converted_macros.borrow_mut().insert(decl_id, None); + info!("Could not convert macro {}: {}", name, e); Ok(ConvertedDecl::NoItem) } } @@ -68,43 +77,77 @@ impl<'c> Translation<'c> { &self, ctx: ExprContext, expansions: &[CExprId], - ) -> TranslationResult<(Box, CTypeId)> { - let (val, ty) = expansions + ) -> TranslationResult<(Box, ConvertedMacro)> { + let mut canonical = None; + let mut expr_results: IndexMap<_, _> = 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))) + .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((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 &mut (_, canon_type_id) = canonical; + let (_, new_type_id) = new; + + let canon_type_kind = + self.ast_context.resolve_type(canon_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), } - } 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"))?; + Ok(()) + }); + + (expr_id, result) + }) + .collect(); + + let Some((val, result_type_id)) = 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 converted = ConvertedMacro { + result_type_id, + expr_results, + }; val.wrap_unsafe() .to_pure_expr() - .map(|val| (val, ty)) + .map(|val| (val, converted)) .ok_or_else(|| TranslationError::generic("Macro expansion is not a pure expression")) // TODO: Validate that all replacements are equivalent and pick the most @@ -157,9 +200,9 @@ impl<'c> Translation<'c> { // 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() + .iter() + .position(|macro_id| ctx.is_converting_macro(macro_id)) + .map_or(macros, |index| ¯os[index + 1..]) .first(); let macro_id = match first_macro { Some(macro_id) => macro_id, @@ -167,25 +210,34 @@ 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 { - // Expansion exists. - Some(Some(expansion)) => expansion.ty, - // Expansion wasn't possible. - Some(None) => return Ok(None), + use std::cell::Ref; + let get_converted = || -> Option>> { + Ref::filter_map(self.converted_macros.borrow(), |converted_macros| { + converted_macros.get(macro_id) + }) + .ok() + .map(|val| Ref::filter_map(val, Option::as_ref).ok()) + }; + // Ensure that we've converted this macro and that it has a valid definition. + let mut converted = get_converted(); + if converted.is_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); - } - } + self.convert_decl(ctx, *macro_id)?; + converted = get_converted(); + } + let Some(Some(converted)) = converted else { + // Expansion wasn't possible. + return Ok(None); }; + + if !matches!(converted.expr_results.get(&expr_id), Some(Ok(_))) { + // Expansion succeeded in general, but not for this particular expression. + return Ok(None); + } + + let result_type_id = converted.result_type_id; let rust_name = self .renamer .borrow_mut() @@ -206,7 +258,7 @@ impl<'c> Translation<'c> { if let Some(expr_ty) = expr_ty { self.convert_cast( ctx, - CQualTypeId::new(macro_ty), + CQualTypeId::new(result_type_id), expr_ty, val, None, diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index d4bfb88c37..1da486bda1 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -143,7 +143,7 @@ pub struct ExprContext { needs_address: bool, ternary_needs_parens: bool, - expanding_macro: Option, + converting_macro: Option, } impl ExprContext { @@ -212,15 +212,15 @@ impl ExprContext { } /// Are we expanding the given macro in the current context? - pub fn expanding_macro(&self, mac: &CDeclId) -> bool { - match self.expanding_macro { + pub fn is_converting_macro(&self, mac: &CDeclId) -> bool { + match self.converting_macro { Some(expanding) => expanding == *mac, None => false, } } - pub fn set_expanding_macro(self, mac: CDeclId) -> Self { + pub fn set_converting_macro(self, mac: CDeclId) -> Self { ExprContext { - expanding_macro: Some(mac), + converting_macro: Some(mac), ..self } } @@ -260,9 +260,9 @@ impl FuncContext { } } -#[derive(Clone)] -struct MacroExpansion { - ty: CTypeId, +struct ConvertedMacro { + result_type_id: CTypeId, + expr_results: IndexMap>, } type ZeroInits = IndexMap>, IndexSet)>; @@ -283,7 +283,7 @@ pub struct Translation<'c> { zero_inits: RefCell, function_context: RefCell, potential_flexible_array_members: RefCell>, - macro_expansions: RefCell>>, + converted_macros: 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 @@ -694,7 +694,7 @@ pub fn translate( is_bitfield_write: false, needs_address: false, ternary_needs_parens: false, - expanding_macro: None, + converting_macro: None, }; { @@ -1508,7 +1508,7 @@ impl<'c> Translation<'c> { zero_inits: RefCell::new(IndexMap::new()), function_context: RefCell::new(FuncContext::new()), potential_flexible_array_members: RefCell::new(IndexSet::new()), - macro_expansions: RefCell::new(IndexMap::new()), + converted_macros: RefCell::new(IndexMap::new()), deferred_imports: RefCell::new(Vec::new()), comment_context, comment_store: RefCell::new(CommentStore::new()), @@ -3008,7 +3008,7 @@ impl<'c> Translation<'c> { .get_decl(&decl_id) .ok_or_else(|| format_err!("Missing declref {:?}", decl_id))? .kind; - if ctx.expanding_macro.is_some() { + if ctx.converting_macro.is_some() { // TODO Determining which declarations have been declared within the scope of the const macro expr // vs. which are out-of-scope of the const macro is non-trivial, // so for now, we don't allow const macros referencing any declarations. @@ -4324,8 +4324,8 @@ impl<'c> Translation<'c> { } => add_use_items_for_type(typ), CDeclKind::MacroObject { .. } => { - if let Some(Some(expansion)) = self.macro_expansions.borrow().get(&decl_id) { - add_use_items_for_type(expansion.ty) + if let Some(Some(converted)) = self.converted_macros.borrow().get(&decl_id) { + add_use_items_for_type(converted.result_type_id) } }