Commit 1273bc277f

Evan Haas <evan@lagerdata.com>
2021-05-19 06:59:45
translate-c: add support for __cleanup__ attribute
Use a `defer` statement to implement the C __cleanup__ attribute. See https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
1 parent 28a89b9
src/translate_c/ast.zig
@@ -67,6 +67,7 @@ pub const Node = extern union {
         @"struct",
         @"union",
         @"comptime",
+        @"defer",
         array_init,
         tuple,
         container_init,
@@ -247,6 +248,7 @@ pub const Node = extern union {
                 .std_mem_zeroes,
                 .@"return",
                 .@"comptime",
+                .@"defer",
                 .asm_simple,
                 .discard,
                 .std_math_Log2Int,
@@ -1020,6 +1022,17 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
                 },
             });
         },
+        .@"defer" => {
+            const payload = node.castTag(.@"defer").?.data;
+            return c.addNode(.{
+                .tag = .@"defer",
+                .main_token = try c.addToken(.keyword_defer, "defer"),
+                .data = .{
+                    .lhs = undefined,
+                    .rhs = try renderNode(c, payload),
+                },
+            });
+        },
         .asm_simple => {
             const payload = node.castTag(.asm_simple).?.data;
             const asm_token = try c.addToken(.keyword_asm, "asm");
@@ -2273,6 +2286,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .@"continue",
         .@"return",
         .@"comptime",
+        .@"defer",
         .asm_simple,
         .usingnamespace_builtins,
         .while_true,
src/clang.zig
@@ -976,6 +976,9 @@ pub const VarDecl = opaque {
     pub const getAlignedAttribute = ZigClangVarDecl_getAlignedAttribute;
     extern fn ZigClangVarDecl_getAlignedAttribute(*const VarDecl, *const ASTContext) c_uint;
 
+    pub const getCleanupAttribute = ZigClangVarDecl_getCleanupAttribute;
+    extern fn ZigClangVarDecl_getCleanupAttribute(*const VarDecl) ?*const FunctionDecl;
+
     pub const getTypeSourceInfo_getType = ZigClangVarDecl_getTypeSourceInfo_getType;
     extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType;
 };
src/translate_c.zig
@@ -1656,6 +1656,22 @@ fn transDeclStmtOne(
                 .init = init_node,
             });
             try block_scope.statements.append(node);
+
+            const cleanup_attr = var_decl.getCleanupAttribute();
+            if (cleanup_attr) |fn_decl| {
+                const cleanup_fn_name = try c.str(@ptrCast(*const clang.NamedDecl, fn_decl).getName_bytes_begin());
+                const fn_id = try Tag.identifier.create(c.arena, cleanup_fn_name);
+
+                const varname = try Tag.identifier.create(c.arena, mangled_name);
+                const args = try c.arena.alloc(Node, 1);
+                args[0] = try Tag.address_of.create(c.arena, varname);
+
+                const cleanup_call = try Tag.call.create(c.arena, .{ .lhs = fn_id, .args = args });
+                const discard = try Tag.discard.create(c.arena, cleanup_call);
+                const deferred_cleanup = try Tag.@"defer".create(c.arena, discard);
+
+                try block_scope.statements.append(deferred_cleanup);
+            }
         },
         .Typedef => {
             try transTypeDef(c, scope, @ptrCast(*const clang.TypedefNameDecl, decl));
src/zig_clang.cpp
@@ -1784,6 +1784,14 @@ unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self,
     return 0;
 }
 
+const struct ZigClangFunctionDecl *ZigClangVarDecl_getCleanupAttribute(const struct ZigClangVarDecl *self) {
+    auto casted_self = reinterpret_cast<const clang::VarDecl *>(self);
+    if (const clang::CleanupAttr *CA = casted_self->getAttr<clang::CleanupAttr>()) {
+        return reinterpret_cast<const ZigClangFunctionDecl *>(CA->getFunctionDecl());
+    }
+    return nullptr;
+}
+
 unsigned ZigClangFieldDecl_getAlignedAttribute(const struct ZigClangFieldDecl *self, const ZigClangASTContext* ctx) {
     auto casted_self = reinterpret_cast<const clang::FieldDecl *>(self);
     auto casted_ctx = const_cast<clang::ASTContext *>(reinterpret_cast<const clang::ASTContext *>(ctx));
src/zig_clang.h
@@ -997,6 +997,7 @@ ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCa
 ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self);
 ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self);
 ZIG_EXTERN_C const char* ZigClangVarDecl_getSectionAttribute(const struct ZigClangVarDecl *self, size_t *len);
+ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangVarDecl_getCleanupAttribute(const struct ZigClangVarDecl *self);
 ZIG_EXTERN_C unsigned ZigClangVarDecl_getAlignedAttribute(const struct ZigClangVarDecl *self, const ZigClangASTContext* ctx);
 ZIG_EXTERN_C unsigned ZigClangFunctionDecl_getAlignedAttribute(const struct ZigClangFunctionDecl *self, const ZigClangASTContext* ctx);
 ZIG_EXTERN_C unsigned ZigClangFieldDecl_getAlignedAttribute(const struct ZigClangFieldDecl *self, const ZigClangASTContext* ctx);
test/run_translated_c.zig
@@ -1490,4 +1490,22 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\    return 0;
         \\}
     , "");
+
+    cases.add("__cleanup__ attribute",
+        \\#include <stdlib.h>
+        \\static int cleanup_count = 0;
+        \\void clean_up(int *final_value) {
+        \\    if (*final_value != cleanup_count++) abort();
+        \\}
+        \\void doit(void) {
+        \\    int a __attribute__ ((__cleanup__(clean_up))) __attribute__ ((unused)) = 2;
+        \\    int b __attribute__ ((__cleanup__(clean_up))) __attribute__ ((unused)) = 1;
+        \\    int c __attribute__ ((__cleanup__(clean_up))) __attribute__ ((unused)) = 0;
+        \\}
+        \\int main(void) {
+        \\    doit();
+        \\    if (cleanup_count != 3) abort();
+        \\    return 0;
+        \\}
+    , "");
 }