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
7 changes: 6 additions & 1 deletion c2rust-ast-exporter/src/AstExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2625,11 +2625,16 @@ class TranslateASTVisitor final

void TypeEncoder::VisitEnumType(const EnumType *T) {
auto ed = T->getDecl()->getDefinition();

if (!ed) {
ed = T->getDecl()->getCanonicalDecl();
}

encodeType(T, TagEnumType, [T, ed](CborEncoder *local) {
cbor_encode_uint(local, uintptr_t(ed));
});

if (ed != nullptr) astEncoder->TraverseDecl(ed);
astEncoder->TraverseDecl(ed);
}

void TypeEncoder::VisitRecordType(const RecordType *T) {
Expand Down
67 changes: 58 additions & 9 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,54 @@ impl TypedAstContext {
}
}

/// Identifies typedefs that name unnamed types.
/// Later, the two declarations can be collapsed into a single name and declaration,
/// eliminating the typedef altogether.
pub fn set_prenamed_decls(&mut self) {
let mut prenamed_decls: IndexMap<CDeclId, CDeclId> = IndexMap::new();

for (&decl_id, decl) in self.iter_decls() {
if let CDeclKind::Typedef { ref name, typ, .. } = decl.kind {
if let Some(subdecl_id) = self.resolve_type(typ.ctype).kind.as_underlying_decl() {
use CDeclKind::*;
let is_unnamed = match self[subdecl_id].kind {
Struct { name: None, .. }
| Union { name: None, .. }
| Enum { name: None, .. } => true,

// Detect case where typedef and struct share the same name.
// In this case the purpose of the typedef was simply to eliminate
// the need for the 'struct' tag when referring to the type name.
Struct {
name: Some(ref target_name),
..
}
| Union {
name: Some(ref target_name),
..
}
| Enum {
name: Some(ref target_name),
..
} => name == target_name,

_ => false,
};

if is_unnamed
&& !prenamed_decls
.values()
.any(|decl_id| *decl_id == subdecl_id)
{
prenamed_decls.insert(decl_id, subdecl_id);
}
}
}
}

self.prenamed_decls = prenamed_decls;
}

pub fn prune_unwanted_decls(&mut self, want_unused_functions: bool) {
// Starting from a set of root declarations, walk each one to find declarations it
// depends on. Then walk each of those, recursively.
Expand Down Expand Up @@ -1105,19 +1153,20 @@ impl TypedAstContext {
}
}

// Unset c_main if we are not retaining its declaration
if let Some(main_id) = self.c_main {
if !wanted.contains(&main_id) {
self.c_main = None;
}
}

// Prune any declaration that isn't considered live
self.c_decls
.retain(|&decl_id, _decl| wanted.contains(&decl_id));

// Prune top declarations that are not considered live
self.c_decls_top.retain(|x| wanted.contains(x));
// Remove references to removed decls that are held elsewhere.
self.c_decls_top.retain(|x| self.c_decls.contains_key(x));
self.prenamed_decls
.retain(|x, _| self.c_decls.contains_key(x));

if let Some(main_id) = self.c_main {
if !self.c_decls.contains_key(&main_id) {
self.c_main = None;
}
}
}

/// Bubble types of unary and binary operators up from their args into the expression type.
Expand Down
65 changes: 12 additions & 53 deletions c2rust-transpile/src/translator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,10 @@ pub fn translate(
None
};

// Identify typedefs that name unnamed types and collapse the two declarations
// into a single name and declaration, eliminating the typedef altogether.
t.ast_context.set_prenamed_decls();

// Headers often pull in declarations that are unused;
// we simplify the translator output by omitting those.
t.ast_context
Expand Down Expand Up @@ -737,62 +741,17 @@ pub fn translate(
prefix_names(&mut t, prefix);
}

// Identify typedefs that name unnamed types and collapse the two declarations
// into a single name and declaration, eliminating the typedef altogether.
let mut prenamed_decls: IndexMap<CDeclId, CDeclId> = IndexMap::new();
for (&decl_id, decl) in t.ast_context.iter_decls() {
if let CDeclKind::Typedef { ref name, typ, .. } = decl.kind {
if let Some(subdecl_id) = t
.ast_context
.resolve_type(typ.ctype)
.kind
.as_underlying_decl()
{
use CDeclKind::*;
let is_unnamed = match t.ast_context[subdecl_id].kind {
Struct { name: None, .. }
| Union { name: None, .. }
| Enum { name: None, .. } => true,

// Detect case where typedef and struct share the same name.
// In this case the purpose of the typedef was simply to eliminate
// the need for the 'struct' tag when referring to the type name.
Struct {
name: Some(ref target_name),
..
}
| Union {
name: Some(ref target_name),
..
}
| Enum {
name: Some(ref target_name),
..
} => name == target_name,

_ => false,
};

if is_unnamed
&& !prenamed_decls
.values()
.any(|decl_id| *decl_id == subdecl_id)
{
prenamed_decls.insert(decl_id, subdecl_id);

t.type_converter
.borrow_mut()
.declare_decl_name(decl_id, name);
t.type_converter
.borrow_mut()
.alias_decl_name(subdecl_id, decl_id);
}
}
for (&decl_id, &subdecl_id) in &t.ast_context.prenamed_decls {
if let CDeclKind::Typedef { ref name, .. } = t.ast_context[decl_id].kind {
t.type_converter
.borrow_mut()
.declare_decl_name(decl_id, name);
t.type_converter
.borrow_mut()
.alias_decl_name(subdecl_id, decl_id);
}
}

t.ast_context.prenamed_decls = prenamed_decls;

// Helper function that returns true if there is either a matching typedef or its
// corresponding struct/union/enum
fn contains(prenamed_decls: &IndexMap<CDeclId, CDeclId>, decl_id: &CDeclId) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/enums/src/enum_fwd_decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ struct _Evas_Func
};

// dummy item imported by `test_enums.rs`
int foo(int i) { return i;}
struct _Evas_Func foo(struct _Evas_Func i) { return i;}
Loading