Skip to content
Merged
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
15 changes: 11 additions & 4 deletions c2rust-ast-exporter/src/AstExporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2226,7 +2226,7 @@ class TranslateASTVisitor final

encode_entry(
VD, TagVarDecl, loc, childIds, T,
[VD, is_defn, def, is_externally_visible](CborEncoder *array) {
[this, VD, is_defn, def, is_externally_visible](CborEncoder *array) {
auto name = VD->getNameAsString();
cbor_encode_string(array, name);

Expand All @@ -2246,11 +2246,9 @@ class TranslateASTVisitor final
cbor_encoder_create_array(array, &attr_info,
CborIndefiniteLength);

bool has_attrs = def ? def->hasAttrs() : VD->hasAttrs();
bool has_attrs = def->hasAttrs();

if (has_attrs) {
auto attrs = def ? def->getAttrs() : VD->getAttrs();

for (auto attr : def->attrs()) {
cbor_encode_text_stringz(&attr_info,
attr->getSpelling());
Expand All @@ -2261,6 +2259,15 @@ class TranslateASTVisitor final
} else if (auto *aa = dyn_cast<AliasAttr>(attr)) {
cbor_encode_text_stringz(
&attr_info, aa->getAliasee().str().c_str());
} else if (auto *ca = dyn_cast<CleanupAttr>(attr)) {
cbor_encode_uint(
&attr_info,
uintptr_t(ca->getFunctionDecl()->getCanonicalDecl()));
} else {
printDiag(Context, DiagnosticsEngine::Warning,
std::string("ignoring unsupported variable attribute: ") +
attr->getSpelling(),
def);
}
}
}
Expand Down
109 changes: 66 additions & 43 deletions c2rust-transpile/src/c_ast/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,54 +168,77 @@ fn parse_cast_kind(kind: &str) -> CastKind {
}
}

fn parse_attributes(attributes: Vec<Value>) -> IndexSet<Attribute> {
let mut attrs = IndexSet::new();
let mut expect_section_value = false;
let mut expect_alias_value = false;
let mut expect_visibility_value = false;

for attr in attributes.into_iter() {
let attr_str = from_value::<String>(attr).expect("Decl attributes should be strings");

match attr_str.as_str() {
"alias" => expect_alias_value = true,
"always_inline" => {
attrs.insert(Attribute::AlwaysInline);
}
"cold" => {
attrs.insert(Attribute::Cold);
}
"gnu_inline" => {
attrs.insert(Attribute::GnuInline);
}
"noinline" => {
attrs.insert(Attribute::NoInline);
}
"used" => {
attrs.insert(Attribute::Used);
impl ConversionContext {
fn parse_attributes(&mut self, attributes: Vec<Value>) -> IndexSet<Attribute> {
let mut attrs = IndexSet::new();
let mut expect_section_value = false;
let mut expect_alias_value = false;
let mut expect_visibility_value = false;
let mut expect_cleanup_id = false;

for attr in attributes.into_iter() {
if expect_cleanup_id {
expect_cleanup_id = false;
match from_value::<ClangId>(attr) {
Ok(func_id) => {
attrs.insert(Attribute::Cleanup(self.visit_decl(func_id)));
}
Err(_) => {
diag!(
Diagnostic::ClangAst,
"cleanup attribute is missing its function decl id",
);
self.invalid_clang_ast = true;
}
}
continue;
}
"visibility" => expect_visibility_value = true,
"section" => expect_section_value = true,
s if expect_section_value => {
attrs.insert(Attribute::Section(s.into()));

expect_section_value = false;
}
s if expect_alias_value => {
attrs.insert(Attribute::Alias(s.into()));
let attr_str = from_value::<String>(attr).expect("Decl attributes should be strings");

expect_alias_value = false;
}
s if expect_visibility_value => {
attrs.insert(Attribute::Visibility(s.into()));
match attr_str.as_str() {
"alias" => expect_alias_value = true,
"always_inline" => {
attrs.insert(Attribute::AlwaysInline);
}
"cleanup" => expect_cleanup_id = true,
"cold" => {
attrs.insert(Attribute::Cold);
}
"gnu_inline" => {
attrs.insert(Attribute::GnuInline);
}
"noinline" => {
attrs.insert(Attribute::NoInline);
}
"used" => {
attrs.insert(Attribute::Used);
}
"visibility" => expect_visibility_value = true,
"section" => expect_section_value = true,
s if expect_section_value => {
attrs.insert(Attribute::Section(s.into()));

expect_section_value = false;
}
s if expect_alias_value => {
attrs.insert(Attribute::Alias(s.into()));

expect_alias_value = false;
}
s if expect_visibility_value => {
attrs.insert(Attribute::Visibility(s.into()));

expect_visibility_value = false;
expect_visibility_value = false;
}
s => {
diag!(Diagnostic::ClangAst, "unrecognized attribute: {}", s);
}
}
_ => {}
}
}

attrs
attrs
}
}

/// This stores the information needed to convert an `AstContext` into a `TypedAstContext`.
Expand Down Expand Up @@ -2082,7 +2105,7 @@ impl ConversionContext {
.expect("Expected to find inline visibliity");
let attributes = from_value::<Vec<Value>>(node.extras[7].clone())
.expect("Expected to find attributes");
let attrs = parse_attributes(attributes);
let attrs = self.parse_attributes(attributes);

// The always_inline attribute implies inline even if the
// inline keyword is not present.
Expand Down Expand Up @@ -2335,7 +2358,7 @@ impl ConversionContext {
.expect("Expected to find type on variable declaration");
let typ = self.visit_qualified_type(typ_id);

let attrs = parse_attributes(attributes);
let attrs = self.parse_attributes(attributes);

let variable_decl = CDeclKind::Variable {
has_static_duration,
Expand Down
16 changes: 16 additions & 0 deletions c2rust-transpile/src/c_ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,20 @@ impl TypedAstContext {
to_walk.push(parent_id);
}
}

// `__attribute__((cleanup(func)))` references its cleanup
// function through the attribute payload, not via a
// DeclRef the traversal would otherwise see, so mark it
// here.
if let CDeclKind::Variable { ref attrs, .. } = self.c_decls[&decl_id].kind {
for attr in attrs {
if let Attribute::Cleanup(fn_id) = attr {
if wanted.insert(*fn_id) {
to_walk.push(*fn_id);
}
}
}
}
}

// Stmts can include decls, but we'll see the DeclId itself in a later
Expand Down Expand Up @@ -2859,6 +2873,8 @@ pub enum Attribute {
Alias(String),
/// __attribute__((always_inline, __always_inline__))
AlwaysInline,
/// __attribute__((cleanup(func), __cleanup__(func)))
Cleanup(CDeclId),
/// __attribute__((cold, __cold__))
Cold,
/// Clang `__counted_by` / `__sized_by` (`_or_null`) bounds attribute on a
Expand Down
63 changes: 62 additions & 1 deletion c2rust-transpile/src/cfg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1152,6 +1152,42 @@ impl WipBlock {

/// This impl block deals with creating control flow graphs
impl CfgBuilder {
fn decl_has_cleanup_attr(translator: &Translation, decl_id: CDeclId) -> bool {
matches!(
translator.ast_context[decl_id].kind,
CDeclKind::Variable { ref attrs, .. }
if attrs.iter().any(|attr| matches!(attr, Attribute::Cleanup(_)))
)
}

fn stmt_declares_cleanup(translator: &Translation, stmt_id: CStmtId) -> bool {
match translator.ast_context[stmt_id].kind {
CStmtKind::Decls(ref decls) => decls
.iter()
.any(|&decl_id| Self::decl_has_cleanup_attr(translator, decl_id)),
CStmtKind::Label(sub_stmt)
| CStmtKind::Case(_, sub_stmt, _)
| CStmtKind::Default(sub_stmt)
| CStmtKind::Attributed {
substatement: sub_stmt,
..
} => Self::stmt_declares_cleanup(translator, sub_stmt),
_ => false,
}
}

fn compound_declares_cleanup(translator: &Translation, stmt_ids: &[CStmtId]) -> bool {
stmt_ids
.iter()
.any(|&stmt_id| Self::stmt_declares_cleanup(translator, stmt_id))
}

fn for_init_declares_cleanup(translator: &Translation, init: Option<CStmtId>) -> bool {
init.map_or(false, |stmt_id| {
Self::stmt_declares_cleanup(translator, stmt_id)
})
}

fn last_per_stmt_mut(&mut self) -> &mut PerStmt {
self.per_stmt_stack
.last_mut()
Expand Down Expand Up @@ -1398,6 +1434,14 @@ impl CfgBuilder {
.get_span(SomeId::Stmt(stmt_id))
.unwrap_or_else(Span::call_site);

let wrap_in_cleanup_scope = match translator.ast_context.index(stmt_id).kind {
CStmtKind::Compound(ref comp_stmts) => {
Self::compound_declares_cleanup(translator, comp_stmts)
}
CStmtKind::ForLoop { init, .. } => Self::for_init_declares_cleanup(translator, init),
_ => false,
};

let out_wip: TranslationResult<Option<WipBlock>> = match translator
.ast_context
.index(stmt_id)
Expand Down Expand Up @@ -2028,7 +2072,13 @@ impl CfgBuilder {
.unwrap()
.is_contained(&self.c_label_to_goto, self.currently_live.last().unwrap())
{
self.incrementally_reloop_subgraph(translator, in_tail, entry, out_wip)
self.incrementally_reloop_subgraph(
translator,
in_tail,
entry,
out_wip,
wrap_in_cleanup_scope,
)
} else {
let last_per_stmt = self.per_stmt_stack.pop().unwrap();
self.per_stmt_stack
Expand Down Expand Up @@ -2067,6 +2117,10 @@ impl CfgBuilder {

// Exit WIP
out_wip: Option<WipBlock>,

// Whether this C statement owns a cleanup variable whose lifetime ends
// at the statement boundary.
wrap_in_cleanup_scope: bool,
) -> TranslationResult<Option<Label>> {
// Close off the `wip` using a `break` terminator
let brk_lbl: Label = self.fresh_label();
Expand Down Expand Up @@ -2126,6 +2180,13 @@ impl CfgBuilder {
stmts = vec![mk().expr_stmt(block)]
}

if wrap_in_cleanup_scope {
let block_span = inner_span.unwrap_or_else(Span::call_site);
let block = mk().span(block_span).block(stmts);
let block_expr = mk().span(block_span).block_expr(block);
stmts = vec![mk().span(block_span).expr_stmt(block_expr)];
}

let mut flattened_wip = self.new_wip_block(entry);
// Copy span from removed statement if there was only one.
if stmts.is_empty() {
Expand Down
Loading
Loading