diff --git a/c2rust-transpile/src/c_ast/conversion.rs b/c2rust-transpile/src/c_ast/conversion.rs index 59cc7bcfa8..5600f4f333 100644 --- a/c2rust-transpile/src/c_ast/conversion.rs +++ b/c2rust-transpile/src/c_ast/conversion.rs @@ -388,17 +388,23 @@ impl ConversionContext { /// Add a `CStmt` node into the `TypedAstContext` fn add_stmt(&mut self, id: ImporterId, stmt: CStmt) { - self.typed_context.c_stmts.insert(CStmtId(id), stmt); + let id = CStmtId(id); + self.typed_context.add_stmt_parents(id, &stmt.kind); + self.typed_context.c_stmts.insert(id, stmt); } /// Add a `CExpr` node into the `TypedAstContext` fn add_expr(&mut self, id: ImporterId, expr: CExpr) { - self.typed_context.c_exprs.insert(CExprId(id), expr); + let id = CExprId(id); + self.typed_context.add_expr_parents(id, &expr.kind); + self.typed_context.c_exprs.insert(id, expr); } /// Add a `CDecl` node into the `TypedAstContext` fn add_decl(&mut self, id: ImporterId, decl: CDecl) { - self.typed_context.c_decls.insert(CDeclId(id), decl); + let id = CDeclId(id); + self.typed_context.add_decl_parents(id, &decl.kind); + self.typed_context.c_decls.insert(id, decl); } /// Clang has `Expression <: Statement`, but we want to make that explicit via the @@ -561,7 +567,6 @@ impl ConversionContext { &'a mut self, untyped_context: &'a AstContext, node: &'a AstNode, - new_id: ImporterId, ) -> impl Iterator + 'a { use self::node_types::*; @@ -573,7 +578,6 @@ impl ConversionContext { .expect("child node not found"); let id = CDeclId(self.visit_node_type(decl, FIELD_DECL | ENUM_DECL | RECORD_DECL)); - self.typed_context.parents.insert(id, CDeclId(new_id)); if decl_node.tag == ASTEntryTag::TagFieldDecl { Some(id) @@ -2164,9 +2168,7 @@ impl ConversionContext { .iter() .map(|id| { let con = id.expect("Enum constant not found"); - let id = CDeclId(self.visit_node_type(con, ENUM_CON)); - self.typed_context.parents.insert(id, CDeclId(new_id)); - id + CDeclId(self.visit_node_type(con, ENUM_CON)) }) .collect(); @@ -2269,10 +2271,7 @@ impl ConversionContext { from_value(node.extras[6].clone()).expect("Expected struct alignment"); let fields: Option> = if has_def { - Some( - self.visit_record_children(untyped_context, node, new_id) - .collect(), - ) + Some(self.visit_record_children(untyped_context, node).collect()) } else { None }; @@ -2300,10 +2299,7 @@ impl ConversionContext { let attrs = from_value::>(node.extras[2].clone()) .expect("Expected attribute array on record"); let fields: Option> = if has_def { - Some( - self.visit_record_children(untyped_context, node, new_id) - .collect(), - ) + Some(self.visit_record_children(untyped_context, node).collect()) } else { None }; diff --git a/c2rust-transpile/src/c_ast/iterators.rs b/c2rust-transpile/src/c_ast/iterators.rs index 38bcffe6a8..6e0c3ab075 100644 --- a/c2rust-transpile/src/c_ast/iterators.rs +++ b/c2rust-transpile/src/c_ast/iterators.rs @@ -1,36 +1,5 @@ use crate::c_ast::*; -#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] -pub enum SomeId { - Stmt(CStmtId), - Expr(CExprId), - Decl(CDeclId), - Type(CTypeId), -} - -macro_rules! from_some_id { - ( $field_type:ty, $con_name:ident, $proj_name:ident ) => { - impl From<$field_type> for SomeId { - fn from(a: $field_type) -> Self { - SomeId::$con_name(a) - } - } - impl SomeId { - pub fn $proj_name(self) -> Option<$field_type> { - match self { - SomeId::$con_name(x) => Some(x), - _ => None, - } - } - } - }; -} - -from_some_id!(CExprId, Expr, expr); -from_some_id!(CStmtId, Stmt, stmt); -from_some_id!(CDeclId, Decl, decl); -from_some_id!(CTypeId, Type, type_); - /// Like the vec macro except that it calls the into method on all list elements macro_rules! intos { ( $( $x:expr ),* ) => { vec![ $( $x.into(), )* ] }; diff --git a/c2rust-transpile/src/c_ast/mod.rs b/c2rust-transpile/src/c_ast/mod.rs index cf413fce92..6accc20df1 100644 --- a/c2rust-transpile/src/c_ast/mod.rs +++ b/c2rust-transpile/src/c_ast/mod.rs @@ -1,5 +1,5 @@ use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; -use crate::iterators::{DFNodes, SomeId}; +use crate::iterators::DFNodes; use c2rust_ast_exporter::clang_ast::LRValue; use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; @@ -55,12 +55,16 @@ pub struct TypedAstContext { /// iterated over export all defined types during translation. c_decls: IndexMap, + /// The parent nodes of each CExprId, CStmtId or CDeclId node. + /// Most nodes have exactly one parent, or zero if they are a top-level node. + /// + /// Expressions appearing inside an `InitListExpr` can have two parents, + /// as they are shared between the semantic form and the syntactic form. + parents: HashMap>, + pub c_decls_top: Vec, pub c_main: Option, - /// record fields and enum constants - pub parents: HashMap, - /// Mapping from [`FileId`] to [`SrcFile`]. Deduplicated by file path. files: Vec, @@ -240,6 +244,366 @@ impl TypedAstContext { } } + fn add_parent(&mut self, child: impl Into, parent: impl Into) { + self.parents + .entry(child.into()) + .or_default() + .push(parent.into()); + } + + fn add_stmt_parents(&mut self, id: CStmtId, kind: &CStmtKind) { + use CStmtKind::*; + let parent = SomeId::Stmt(id); + + match *kind { + Label(stmt) => { + self.add_parent(stmt, parent); + } + + Case(expr, stmt, _) => { + self.add_parent(expr, parent); + self.add_parent(stmt, parent); + } + + Default(stmt) => { + self.add_parent(stmt, parent); + } + + Compound(ref stmts) => { + for &stmt in stmts { + self.add_parent(stmt, parent); + } + } + + Expr(expr) => { + self.add_parent(expr, parent); + } + + Empty => {} + + If { + scrutinee, + true_variant, + false_variant, + } => { + self.add_parent(scrutinee, parent); + self.add_parent(true_variant, parent); + + if let Some(false_variant) = false_variant { + self.add_parent(false_variant, parent); + } + } + Switch { scrutinee, body } => { + self.add_parent(scrutinee, parent); + self.add_parent(body, parent); + } + + While { condition, body } => { + self.add_parent(condition, parent); + self.add_parent(body, parent); + } + + DoWhile { body, condition } => { + self.add_parent(body, parent); + self.add_parent(condition, parent); + } + + ForLoop { + init, + condition, + increment, + body, + } => { + if let Some(init) = init { + self.add_parent(init, parent); + } + + if let Some(condition) = condition { + self.add_parent(condition, parent); + } + + if let Some(increment) = increment { + self.add_parent(increment, parent); + } + + self.add_parent(body, parent); + } + + Goto(..) => {} + Break => {} + Continue => {} + + Return(expr) => { + if let Some(expr) = expr { + self.add_parent(expr, parent); + } + } + + Decls(ref decls) => { + for &decl in decls { + self.add_parent(decl, parent); + } + } + + Asm { .. } => {} + + Attributed { + attributes: _, + substatement, + } => { + self.add_parent(substatement, parent); + } + } + } + + fn add_expr_parents(&mut self, id: CExprId, kind: &CExprKind) { + use CExprKind::*; + let parent = SomeId::Expr(id); + + match *kind { + Literal(..) => {} + + Unary(_, _, expr, _) => { + self.add_parent(expr, parent); + } + + UnaryType(_, _, expr, _) => { + if let Some(expr) = expr { + self.add_parent(expr, parent); + } + } + + OffsetOf(_, ref kind) => match *kind { + OffsetOfKind::Constant(..) => {} + OffsetOfKind::Variable(_, _, expr) => { + self.add_parent(expr, parent); + } + }, + + Binary(_, _, lhs, rhs, _, _) => { + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + ImplicitCast(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ExplicitCast(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ConstantExpr(_, expr, _) => { + self.add_parent(expr, parent); + } + + DeclRef(..) => {} + + Call(_, func, ref args) => { + self.add_parent(func, parent); + + for &arg in args { + self.add_parent(arg, parent); + } + } + + Member(_, expr, _, _, _) => { + self.add_parent(expr, parent); + } + + ArraySubscript(_, lhs, rhs, _) => { + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + Conditional(_, cond, lhs, rhs) => { + self.add_parent(cond, parent); + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + BinaryConditional(_, cond, rhs) => { + self.add_parent(cond, parent); + self.add_parent(rhs, parent); + } + + InitList(_, ref exprs, _, syntactic_form) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + + if let Some(syntactic_form) = syntactic_form { + self.add_parent(syntactic_form, parent); + } + } + + ImplicitValueInit(..) => {} + + Paren(_, expr) => { + self.add_parent(expr, parent); + } + + CompoundLiteral(_, expr) => { + self.add_parent(expr, parent); + } + + Predefined(_, expr) => { + self.add_parent(expr, parent); + } + + Statements(_, stmt) => { + self.add_parent(stmt, parent); + } + + VAArg(_, expr) => { + self.add_parent(expr, parent); + } + + ShuffleVector(_, ref exprs) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + } + + ConvertVector(_, ref exprs) => { + for &expr in exprs { + self.add_parent(expr, parent); + } + } + + DesignatedInitExpr(_, _, expr) => { + self.add_parent(expr, parent); + } + + Choose(_, cond, lhs, rhs, _) => { + self.add_parent(cond, parent); + self.add_parent(lhs, parent); + self.add_parent(rhs, parent); + } + + Atomic { + ptr, + order, + val1, + order_fail, + val2, + weak, + .. + } => { + self.add_parent(ptr, parent); + self.add_parent(order, parent); + + if let Some(val1) = val1 { + self.add_parent(val1, parent); + } + + if let Some(order_fail) = order_fail { + self.add_parent(order_fail, parent); + } + + if let Some(val2) = val2 { + self.add_parent(val2, parent); + } + + if let Some(weak) = weak { + self.add_parent(weak, parent); + } + } + + BadExpr => {} + } + } + + fn add_decl_parents(&mut self, id: CDeclId, kind: &CDeclKind) { + use CDeclKind::*; + let parent = SomeId::Decl(id); + + match *kind { + Function { + ref parameters, + body, + .. + } => { + for ¶meter in parameters { + self.add_parent(parameter, parent); + } + + if let Some(body) = body { + self.add_parent(body, parent); + } + } + + Variable { initializer, .. } => { + if let Some(initializer) = initializer { + self.add_parent(initializer, parent); + } + } + + Enum { + ref variants, + integral_type, + .. + } => { + if integral_type.is_some() { + for &variant in variants { + self.add_parent(variant, parent); + } + } + } + + EnumConstant { .. } => {} + + Typedef { .. } => {} + + Struct { ref fields, .. } => { + if let Some(fields) = fields { + for &field in fields { + self.add_parent(field, parent); + } + } + } + + Union { ref fields, .. } => { + if let Some(fields) = fields { + for &field in fields { + self.add_parent(field, parent); + } + } + } + + Field { .. } => {} + + MacroObject { .. } => {} + + MacroFunction { .. } => {} + + NonCanonicalDecl { .. } => {} + + StaticAssert { assert_expr, .. } => { + self.add_parent(assert_expr, parent); + } + } + } + + /// Returns the parent nodes of `child`. + pub fn parents(&self, child: impl Into) -> &[SomeId] { + self.parents + .get(&child.into()) + .map(AsRef::as_ref) + .unwrap_or_default() + } + + /// If `child` has a parent node, returns the first one. + pub fn parent(&self, child: impl Into) -> Option { + self.parents(child).get(0).copied() + } + + /// If `child` has a parent node, and the first one is of the given type `T`, returns it. + pub fn parent_with_type>(&self, child: impl Into) -> Option { + self.parent(child) + .and_then(|parent| T::try_from(parent).ok()) + } + pub fn display_loc(&self, loc: &Option) -> Option { loc.as_ref().map(|loc| DisplaySrcSpan { file: self.files[self.file_map[loc.fileid as usize]].path.clone(), @@ -1091,7 +1455,9 @@ impl TypedAstContext { if let CDeclKind::EnumConstant { .. } = self.c_decls[&decl_id].kind { // Special case for enums. The enum constant is used, so the whole // enum is also used. - let parent_id = self.parents[&decl_id]; + let parent_id = self + .parent_with_type(decl_id) + .expect("Enum constant does not have a parent Enum"); if wanted.insert(parent_id) { to_walk.push(parent_id); } @@ -3096,3 +3462,44 @@ c = {c} locs.sort_unstable_by_key(|&loc| ctx.cmp_loc_include(loc)); } } + +#[derive(Copy, Clone, Eq, PartialEq, Debug, Hash)] +pub enum SomeId { + Stmt(CStmtId), + Expr(CExprId), + Decl(CDeclId), + Type(CTypeId), +} + +macro_rules! from_some_id { + ( $field_type:ty, $con_name:ident, $proj_name:ident ) => { + impl From<$field_type> for SomeId { + fn from(a: $field_type) -> Self { + SomeId::$con_name(a) + } + } + impl TryFrom for $field_type { + type Error = (); + + fn try_from(id: SomeId) -> Result { + match id { + SomeId::$con_name(x) => Ok(x), + _ => Err(()), + } + } + } + impl SomeId { + pub fn $proj_name(self) -> Option<$field_type> { + match self { + SomeId::$con_name(x) => Some(x), + _ => None, + } + } + } + }; +} + +from_some_id!(CExprId, Expr, expr); +from_some_id!(CStmtId, Stmt, stmt); +from_some_id!(CDeclId, Decl, decl); +from_some_id!(CTypeId, Type, type_); diff --git a/c2rust-transpile/src/cfg/mod.rs b/c2rust-transpile/src/cfg/mod.rs index 76549ee735..55e916bde3 100644 --- a/c2rust-transpile/src/cfg/mod.rs +++ b/c2rust-transpile/src/cfg/mod.rs @@ -15,8 +15,8 @@ //! - convert the `Vec>` back into a `Vec` //! -use crate::c_ast::iterators::{DFExpr, SomeId}; -use crate::c_ast::CLabelId; +use crate::c_ast::iterators::DFExpr; +use crate::c_ast::{CLabelId, SomeId}; use crate::diagnostics::TranslationResult; use crate::rust_ast::SpanExt; use c2rust_ast_printer::pprust; diff --git a/c2rust-transpile/src/translator/comments.rs b/c2rust-transpile/src/translator/comments.rs index ed9e8ed4bb..806d18c56a 100644 --- a/c2rust-transpile/src/translator/comments.rs +++ b/c2rust-transpile/src/translator/comments.rs @@ -1,6 +1,6 @@ use super::Translation; -use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor, SomeId}; -use crate::c_ast::{CDeclId, CDeclKind, CommentContext, SrcLoc, TypedAstContext}; +use crate::c_ast::iterators::{immediate_children_all_types, NodeVisitor}; +use crate::c_ast::{CDeclId, CDeclKind, CommentContext, SomeId, SrcLoc, TypedAstContext}; use crate::rust_ast::comment_store::CommentStore; use crate::rust_ast::{pos_to_span, SpanExt}; use log::debug; diff --git a/c2rust-transpile/src/translator/enums.rs b/c2rust-transpile/src/translator/enums.rs index 91f1c4ef8e..23dfc119ee 100644 --- a/c2rust-transpile/src/translator/enums.rs +++ b/c2rust-transpile/src/translator/enums.rs @@ -40,7 +40,10 @@ impl<'c> Translation<'c> { .borrow_mut() .get(&enum_constant_id) .expect("Enum constant not named"); - let enum_id = self.ast_context.parents[&enum_constant_id]; + let enum_id = self + .ast_context + .parent_with_type(enum_constant_id) + .expect("Enum constant does not have a parent Enum"); let enum_name = self .type_converter .borrow() diff --git a/c2rust-transpile/src/translator/mod.rs b/c2rust-transpile/src/translator/mod.rs index a0c6bf74cc..c89ef43fce 100644 --- a/c2rust-transpile/src/translator/mod.rs +++ b/c2rust-transpile/src/translator/mod.rs @@ -36,7 +36,7 @@ use crate::translator::variadic::{mk_va_list_copy, mk_va_list_ty}; use c2rust_ast_builder::{mk, properties::*, Builder}; use c2rust_ast_printer::pprust; -use crate::c_ast::iterators::{DFExpr, SomeId}; +use crate::c_ast::iterators::DFExpr; use crate::c_ast::*; use crate::cfg; use crate::convert_type::TypeConverter; diff --git a/c2rust-transpile/src/translator/structs_unions.rs b/c2rust-transpile/src/translator/structs_unions.rs index b754159e9b..13b949ad00 100644 --- a/c2rust-transpile/src/translator/structs_unions.rs +++ b/c2rust-transpile/src/translator/structs_unions.rs @@ -1046,7 +1046,11 @@ impl<'a> Translation<'a> { } }; - let record_id = self.ast_context.parents[&decl]; + let record_id = self + .ast_context + .parent_with_type(decl) + .expect("Field does not have a parent Struct or Union"); + if self.ast_context.has_inner_struct_decl(record_id) { // The structure is split into an outer and an inner, // so we need to go through the outer structure to the inner one @@ -1099,7 +1103,10 @@ impl<'a> Translation<'a> { opt_field_id: Option, ) -> TranslationResult>> { let field_id = opt_field_id.expect("Missing field ID in union cast"); - let union_id = self.ast_context.parents[&field_id]; + let union_id = self + .ast_context + .parent_with_type(field_id) + .expect("Union field does not have a parent Union"); let union_name = self .type_converter