Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 77 additions & 27 deletions c2rust-transpile/src/c_ast/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ mod node_types {

type ClangId = u64;
type ImporterId = u64;
type MacroInvocationId = u32;

/// Correspondence between old/new IDs.
///
Expand Down Expand Up @@ -244,6 +245,12 @@ pub struct ConversionContext {
/// Typed context we are building up during the conversion
typed_context: TypedAstContext,

/// Maps expressions to the stack of macro invocations that expanded to them.
macro_invocations: IndexMap<CExprId, Vec<MacroInvocationId>>,

/// Maps macro invocation IDs to information about the invocation (macro ID, arguments, etc).
macro_infos: HashMap<MacroInvocationId, MacroInvocationInfo>,

pub invalid_clang_ast: bool,
}

Expand Down Expand Up @@ -329,6 +336,8 @@ impl ConversionContext {
processed_nodes: HashMap::new(),
visit_as,
typed_context: TypedAstContext::new(input_path, &untyped_context.files),
macro_invocations: IndexMap::new(),
macro_infos: HashMap::new(),
invalid_clang_ast,
};

Expand Down Expand Up @@ -554,31 +563,43 @@ impl ConversionContext {
move |e| case_exprs.contains(&e)
};

self.typed_context.macro_invocations = mem::take(&mut self.typed_context.macro_invocations)
let macro_invocations = mem::take(&mut self.macro_invocations);
let mut macro_infos = mem::take(&mut self.macro_infos);

self.typed_context.macro_invocations = macro_invocations
.into_iter()
.map(|(expr_id, macro_ids)| {
(
if is_case_expr(expr_id) {
expr_id
} else {
self.typed_context.unwrap_implicit_cast_expr(expr_id)
},
macro_ids,
)
.map(|(expr_id, invocation_ids)| {
let expr_id = if is_case_expr(expr_id) {
expr_id
} else {
self.typed_context.unwrap_implicit_cast_expr(expr_id)
};

// For each invocation ID, retrieve its corresponding `MacroInvocationInfo`
// and wrap it in an `Rc`.
let infos = invocation_ids
.into_iter()
.map(|key| {
let info = macro_infos
.remove(&key)
Comment thread
ahomescu marked this conversation as resolved.
.unwrap_or_else(|| panic!("No macro_infos entry for key {key}"));
let info = Rc::new(MacroInvocationInfo { expr_id, ..info });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to also normalize info.arguments here (through unwrap_implicit_cast_expr or something else)?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly, but I'm not sure. It kinda works "in reverse" when it comes to macro arguments, because they are placed within the macro definition. So if casts are left as-is, then they are considered to form part of the argument passed at the call site. If they are unwrapped, then they are considered part of the context surrounding the argument within the macro definition.


// Invert the macro invocations to get a list of macro expansion expressions
self.typed_context
.macro_expansions
.entry(info.macro_id)
.or_default()
.push(info.clone()); // Clone the `Rc`

info
})
.collect();

(expr_id, infos)
})
.collect();

// Invert the macro invocations to get a list of macro expansion expressions
for (expr_id, macro_ids) in &self.typed_context.macro_invocations {
for mac_id in macro_ids {
self.typed_context
.macro_expansions
.entry(*mac_id)
.or_default()
.push(*expr_id);
}
}

self.typed_context.va_list_kind = untyped_context.va_list_kind;
self.typed_context.target = untyped_context.target.clone();
}
Expand Down Expand Up @@ -1047,13 +1068,42 @@ impl ConversionContext {
};

if expected_ty & EXPR != 0 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the callers of expr_possibly_as_stmt? I.e. expressions used as statements?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those get wrapped in a CStmtKind::Expr, in which the statement part and the expression part are handled separately.

let expr_id = CExprId(new_id);

for info in &node.macro_invocations {
let mac = CDeclId(self.visit_node_type(info.macro_id, MACRO_DECL));
self.typed_context
.macro_invocations
.entry(CExprId(new_id))
.or_default()
.push(mac);
let &MacroInvocationInfoRaw {
key,
macro_id,
ref parameter,
} = info;

let macro_id = CDeclId(self.visit_node_type(macro_id, MACRO_DECL));

// Is this a macro invocation ID we've seen previously?
let info = self
.macro_infos
.entry(key)
// If not, create a dummy info entry.
.or_insert_with(|| MacroInvocationInfo {
expr_id: CExprId(u64::MAX),
macro_id,
arguments: HashMap::new(),
});
assert_eq!(macro_id, info.macro_id);

// Fill in the values that were provided in the `MacroInvocationInfoRaw`.
if let Some(parameter) = parameter {
// This expression is an argument for a functional macro.
info.arguments.insert(parameter.clone(), expr_id);
} else {
// This expression is the result of a macro expansion.
assert!(
info.expr_id == CExprId(u64::MAX),
"macro invocation key is not unique!"
);
info.expr_id = expr_id;
Comment thread
ahomescu marked this conversation as resolved.
self.macro_invocations.entry(expr_id).or_default().push(key);
}
}

if let Some(text) = &node.macro_invocation_text {
Expand Down
35 changes: 25 additions & 10 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,11 @@ pub struct TypedAstContext {
/// Names of the labels defined in the C source code.
pub label_names: IndexMap<CLabelId, Rc<str>>,

/// map expressions to the stack of macros they were expanded from
pub macro_invocations: IndexMap<CExprId, Vec<CDeclId>>,
/// Maps expressions to the stack of macro invocations that expanded to them.
pub(crate) macro_invocations: IndexMap<CExprId, Vec<Rc<MacroInvocationInfo>>>,

/// map macro decls to the expressions they expand to
pub macro_expansions: IndexMap<CDeclId, Vec<CExprId>>,
/// Maps macro decls to the invocation sites that use them.
pub(crate) macro_expansions: IndexMap<CDeclId, Vec<Rc<MacroInvocationInfo>>>,

/// map expressions to the text of the macro invocation they expanded from,
/// if any
Expand All @@ -123,6 +123,21 @@ pub struct TypedAstContext {
pub target: String,
}

/// Information about a single macro invocation.
#[derive(Debug)]
pub(crate) struct MacroInvocationInfo {
/// The expression that the invocation expanded into.
pub(crate) expr_id: CExprId,

/// The macro that was called.
pub(crate) macro_id: CDeclId,

/// If `macro_id` is a functional macro, the value of the known named arguments that were
/// passed to the macro. This is not guaranteed to hold every argument the macro needs, only
/// those that were found and exported by the C++ exporter.
pub(crate) arguments: HashMap<String, CExprId>,
}

/// Comments associated with a typed AST context
#[derive(Debug, Clone)]
pub struct CommentContext {
Expand Down Expand Up @@ -1408,9 +1423,9 @@ impl TypedAstContext {

let expr = self.index_unwrap_parens(expr_id);
if let Some(macs) = self.macro_invocations.get(&expr_id) {
for mac_id in macs {
if wanted_decls.insert(*mac_id) {
to_walk.push(*mac_id);
for info in macs {
if wanted_decls.insert(info.macro_id) {
to_walk.push(info.macro_id);
}
}
}
Expand Down Expand Up @@ -1469,9 +1484,9 @@ impl TypedAstContext {
.retain(|x, _| self.c_decls.contains_key(x));
self.macro_invocations
.retain(|&expr_id, _| self.c_exprs.contains_key(&expr_id));
self.macro_expansions.retain(|_, expr_ids| {
expr_ids.retain(|&expr_id| self.c_exprs.contains_key(&expr_id));
!expr_ids.is_empty()
self.macro_expansions.retain(|_, infos| {
infos.retain(|info| self.c_exprs.contains_key(&info.expr_id));
!infos.is_empty()
});

if let Some(main_id) = self.c_main {
Expand Down
75 changes: 37 additions & 38 deletions c2rust-transpile/src/translator/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use proc_macro2::{Span, TokenStream};
use std::rc::Rc;
use syn::{Expr, MacroDelimiter};

use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind};
use crate::c_ast::{CDeclId, CExprId, CQualTypeId, CTypeId, CTypeKind, MacroInvocationInfo};
use crate::diagnostics::{TranslationError, TranslationResult};
use crate::translator::{ConvertedDecl, ConvertedMacro, ExprContext, Translation};
use crate::with_stmts::WithStmts;
Expand All @@ -25,29 +25,26 @@ impl<'c> Translation<'c> {
self.ast_context[decl_id]
);

self.recreate_const_macro_from_expansions(
ctx.const_().set_expanding_macro(decl_id),
&self.ast_context.macro_expansions[&decl_id],
)
.and_then(|(replacement, converted)| {
trace!(" to {:?}", replacement);
self.recreate_const_macro_from_expansions(ctx, decl_id)
.and_then(|(replacement, converted)| {
trace!(" to {:?}", replacement);

let ty = self.convert_type(converted.ty)?;
self.converted_macros
.borrow_mut()
.insert(decl_id, Some(Rc::new(converted)));
let ty = self.convert_type(converted.ty)?;
self.converted_macros
.borrow_mut()
.insert(decl_id, Some(Rc::new(converted)));

Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item(
name,
ty,
replacement,
)))
})
.unwrap_or_else(|e| {
self.converted_macros.borrow_mut().insert(decl_id, None);
info!("Could not expand macro {}: {}", name, e);
ConvertedDecl::NoItem
})
Ok(ConvertedDecl::Item(mk().span(span).pub_().const_item(
name,
ty,
replacement,
)))
})
.unwrap_or_else(|e| {
self.converted_macros.borrow_mut().insert(decl_id, None);
info!("Could not expand macro {}: {}", name, e);
ConvertedDecl::NoItem
})
}

/// Given all of the expansions of a const macro,
Expand All @@ -64,25 +61,27 @@ impl<'c> Translation<'c> {
fn recreate_const_macro_from_expansions(
&self,
ctx: ExprContext,
expansions: &[CExprId],
macro_id: CDeclId,
) -> TranslationResult<(Box<Expr>, ConvertedMacro)> {
struct ConvertedMacroExpr {
val: WithStmts<Box<Expr>>,
ty: CTypeId,
}

let canonical = expansions
let ctx = ctx.const_().set_expanding_macro(macro_id);
let canonical = self.ast_context.macro_expansions[&macro_id]
.iter()
.try_fold::<Option<ConvertedMacroExpr>, _, _>(None, |canonical, &id| {
self.can_convert_const_macro_expansion(id)?;
.try_fold::<Option<ConvertedMacroExpr>, _, _>(None, |canonical, info| {
let &MacroInvocationInfo { expr_id, .. } = info.as_ref();
Comment thread
ahomescu marked this conversation as resolved.
self.can_convert_const_macro_expansion(expr_id)?;

let ty = self
.ast_context
.index_unwrap_parens(id)
.index_unwrap_parens(expr_id)
.kind
.get_type()
.ok_or_else(|| format_err!("Invalid expression type"))?;
let val = self.convert_expr(ctx, id, None)?;
let val = self.convert_expr(ctx, expr_id, None)?;
let new = ConvertedMacroExpr { val, ty };

// Join ty and cur_ty to the smaller of the two types. If the
Expand Down Expand Up @@ -172,18 +171,18 @@ 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))
.splitn(2, |info| ctx.expanding_macro(&info.macro_id))
.last()
.unwrap()
.first();
let macro_id = match first_macro {
Some(macro_id) => macro_id,
let info = match first_macro {
Some(info) => info,
None => return Ok(None),
};

trace!(" found macro expansion: {macro_id:?}");
trace!(" found macro expansion: {:?}", info.macro_id);
// Ensure that we've converted this macro and that it has a valid definition.
let maybe_converted = self.converted_macros.borrow().get(macro_id).cloned();
let maybe_converted = self.converted_macros.borrow().get(&info.macro_id).cloned();
let converted = match maybe_converted {
// Macro was converted previously.
Some(Some(converted)) => converted,
Expand All @@ -193,8 +192,8 @@ impl<'c> Translation<'c> {

// We haven't tried to convert it yet.
None => {
self.convert_decl(ctx.not_pattern(), *macro_id)?;
let maybe_converted = self.converted_macros.borrow().get(macro_id).cloned();
self.convert_decl(ctx.not_pattern(), info.macro_id)?;
let maybe_converted = self.converted_macros.borrow().get(&info.macro_id).cloned();
if let Some(Some(converted)) = maybe_converted {
converted
} else {
Expand All @@ -205,10 +204,10 @@ impl<'c> Translation<'c> {
let rust_name = self
.renamer
.borrow_mut()
.get(macro_id)
.get(&info.macro_id)
.ok_or_else(|| format_err!("Macro name not declared"))?;

self.add_import(*macro_id, &rust_name);
self.add_import(info.macro_id, &rust_name);

let mut val = WithStmts::new_val(mk().path_expr(vec![rust_name]));

Expand All @@ -225,7 +224,7 @@ impl<'c> Translation<'c> {
Err(err) => {
info!(
"Could not convert cast of macro {} for {:?}: {}",
self.renamer.borrow_mut().get(macro_id).unwrap(),
self.renamer.borrow_mut().get(&info.macro_id).unwrap(),
expr_id,
err
);
Expand Down
Loading