Commit 22cb693889

TwoClocks <5883156+TwoClocks@users.noreply.github.com>
2022-01-15 19:30:18
reserve correct space for bitfields
1 parent 0e6285c
src/clang.zig
@@ -476,6 +476,12 @@ pub const FieldDecl = opaque {
     pub const isBitField = ZigClangFieldDecl_isBitField;
     extern fn ZigClangFieldDecl_isBitField(*const FieldDecl) bool;
 
+    pub const getBitWidthValue = ZigClangFieldDecl_getBitWidthValue;
+    extern fn ZigClangFieldDecl_getBitWidthValue(*const FieldDecl, *const ASTContext) c_uint;
+
+    pub const isZeroLengthBitField = ZigClangFieldDecl_isZeroLengthBitField;
+    extern fn ZigClangFieldDecl_isZeroLengthBitField(*const FieldDecl, *const ASTContext) bool;
+
     pub const getType = ZigClangFieldDecl_getType;
     extern fn ZigClangFieldDecl_getType(*const FieldDecl) QualType;
 
src/translate_c.zig
@@ -1085,17 +1085,15 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
         const layout = record_def.getASTRecordLayout(c.clang_context);
         const record_alignment = layout.getAlignment();
 
+        var record_bitfield_count: u32 = 0;
+        var bits_unused: i32 = 0;
+        var bits_type: clang.BuiltinTypeKind = clang.BuiltinTypeKind.Void;
+
         while (it.neq(end_it)) : (it = it.next()) {
             const field_decl = it.deref();
             const field_loc = field_decl.getLocation();
             const field_qt = field_decl.getType();
 
-            if (field_decl.isBitField()) {
-                try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
-                try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
-                break :blk Tag.opaque_literal.init();
-            }
-
             var is_anon = false;
             var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
             if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {
@@ -1125,6 +1123,53 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
                 else => |e| return e,
             };
 
+            if (field_decl.isBitField()) {
+                var this_field_width = @intCast(i32, field_decl.getBitWidthValue(c.clang_context));
+
+                // are we starting new bitfield?
+                if (bits_unused <= 0) {
+                    const size_map = std.ComptimeStringMap(u16, .{ .{ "u8", 8 }, .{ "c_ushort", 16 }, .{ "u16", 16 }, .{ "u32", 32 }, .{ "c_uint", 32 }, .{ "c_ulong", 32 }, .{ "c_ulonglong", 64 }, .{ "u64", 64 } });
+
+                    bits_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind();
+
+                    var field_width = field_type.castTag(.type).?.*.data; // we just set it 10 lines back. this should not fail.
+
+                    if (size_map.get(field_width)) |sz| {
+                        bits_unused = @intCast(i32, sz) - this_field_width;
+                        field_name = try std.fmt.allocPrint(c.arena, "bitfield{d}", .{record_bitfield_count});
+                        record_bitfield_count += 1;
+                    } else {
+                        try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type not unsigned or unknown. type:{s} ", .{ container_kind_name, field_width });
+                        break :blk Tag.opaque_literal.init();
+                    }
+                } else {
+                    var this_type = @ptrCast(*const clang.BuiltinType, field_qt.getTypePtr()).getKind();
+                    if (field_decl.isZeroLengthBitField(c.clang_context)) {
+                        try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield with zero size not supported", .{container_kind_name});
+                        break :blk Tag.opaque_literal.init();
+                    } else if (bits_type != this_type) {
+                        try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield type changed in the middle of bitfield was;{s} is:{s}", .{ container_kind_name, @tagName(bits_type), @tagName(this_type) });
+                        break :blk Tag.opaque_literal.init();
+                    } else {
+                        // next
+                        bits_unused -= this_field_width;
+                        if (bits_unused < 0) {
+                            try warn(c, scope, field_loc, "{s} demoted to opaque type - bitfield overrun type size not supported", .{container_kind_name});
+                            break :blk Tag.opaque_literal.init();
+                        }
+                    }
+                    continue; // free the field_type? tag is alloc'd
+                }
+            } else {
+                if (bits_unused >= 8) {
+                    // if they didn't add a "reserved:n" at the end, and the # of bits used is more than 1 byte of their requested size,
+                    // the layout is to compiler specific.
+                    try warn(c, scope, field_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused });
+                    break :blk Tag.opaque_literal.init();
+                }
+                bits_unused = 0;
+            }
+
             const alignment = if (has_flexible_array and field_decl.getFieldIndex() == 0)
                 @intCast(c_uint, record_alignment)
             else
@@ -1140,6 +1185,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
                 .alignment = alignment,
             });
         }
+        if (bits_unused >= 8) { // one last check if the last field was a bitfield
+            try warn(c, scope, record_loc, "{s} demoted to opaque type - less bits used than field size. unused bit count:{d}", .{ container_kind_name, bits_unused });
+            break :blk Tag.opaque_literal.init();
+        }
 
         const record_payload = try c.arena.create(ast.Payload.Record);
         record_payload.* = .{
@@ -2576,7 +2625,9 @@ fn transInitListExprRecord(
             continue;
         }
 
-        assert(init_i < init_count);
+        if (init_i >= init_count) {
+            return fail(c, error.UnsupportedTranslation, loc, "init list longer fields in record. Record has bitfield?", .{});
+        }
         const elem_expr = expr.getInit(init_i);
         init_i += 1;
 
src/zig_clang.cpp
@@ -3335,6 +3335,18 @@ bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *self) {
     return casted->isBitField();
 }
 
+unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *self, const ZigClangASTContext *ctx) {
+    auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
+    auto casted_ctx = const_cast<clang::ASTContext *>(reinterpret_cast<const clang::ASTContext *>(ctx));
+    return casted->getBitWidthValue(*casted_ctx);
+}
+
+bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *self, const ZigClangASTContext  *ctx) {
+    auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
+    auto casted_ctx = const_cast<clang::ASTContext *>(reinterpret_cast<const clang::ASTContext *>(ctx));
+    return casted->isZeroLengthBitField(*casted_ctx);
+}
+
 bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *field_decl) {
     return reinterpret_cast<const clang::FieldDecl*>(field_decl)->isAnonymousStructOrUnion();
 }
src/zig_clang.h
@@ -1407,6 +1407,8 @@ ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSour
 ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangMacroDefinitionRecord_getSourceRange_getEnd(const struct ZigClangMacroDefinitionRecord *);
 
 ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *);
+ZIG_EXTERN_C unsigned ZigClangFieldDecl_getBitWidthValue( const struct ZigClangFieldDecl *, const ZigClangASTContext *);
+ZIG_EXTERN_C bool ZigClangFieldDecl_isZeroLengthBitField( ZigClangFieldDecl *, const ZigClangASTContext  *);
 ZIG_EXTERN_C bool ZigClangFieldDecl_isAnonymousStructOrUnion(const ZigClangFieldDecl *);
 ZIG_EXTERN_C struct ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *);
 ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldDecl *);
test/translate_c.zig
@@ -915,21 +915,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\};
     });
 
-    cases.add("pointer to struct demoted to opaque due to bit fields",
-        \\struct Foo {
-        \\    unsigned int: 1;
-        \\};
-        \\struct Bar {
-        \\    struct Foo *foo;
-        \\};
-    , &[_][]const u8{
-        \\pub const struct_Foo = opaque {};
-        ,
-        \\pub const struct_Bar = extern struct {
-        \\    foo: ?*struct_Foo,
-        \\};
-    });
-
     cases.add("macro with left shift",
         \\#define REDISMODULE_READ (1<<0)
     , &[_][]const u8{
@@ -3577,34 +3562,33 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\}
     });
 
-    cases.add("Demote function that initializes opaque struct",
+    cases.add("function that dereferences bitfield works",
         \\struct my_struct {
-        \\    unsigned a: 15;
-        \\    unsigned: 2;
-        \\    unsigned b: 15;
+        \\    unsigned a: 1;
+        \\    unsigned b: 28;
         \\};
-        \\void initialize(void) {
-        \\    struct my_struct S = {.a = 1, .b = 2};
+        \\void deref(struct my_struct *s) {
+        \\    *s;
         \\}
     , &[_][]const u8{
-        \\warning: cannot initialize opaque type
+        \\pub const struct_my_struct = extern struct {
+        \\    bitfield0: c_uint,
         ,
-        \\warning: unable to translate function, demoted to extern
-        \\pub extern fn initialize() void;
+        \\pub export fn deref(arg_s: ?*struct_my_struct) void {
+        \\    var s = arg_s;
+        \\    _ = s.*;
+        \\}
     });
 
-    cases.add("Demote function that dereferences opaque type",
-        \\struct my_struct {
+    cases.add("bitfield don't cover requedted space",
+        \\struct inner {
         \\    unsigned a: 1;
+        \\    char after;            
         \\};
-        \\void deref(struct my_struct *s) {
-        \\    *s;
-        \\}
     , &[_][]const u8{
-        \\warning: cannot dereference opaque type
+        \\less bits used than field size.
         ,
-        \\warning: unable to translate function, demoted to extern
-        \\pub extern fn deref(arg_s: ?*struct_my_struct) void;
+        \\pub const struct_inner = opaque {};
     });
 
     cases.add("Function prototype declared within function",