Commit 294ee1bbc9

Evan Haas <evan@lagerdata.com>
2021-02-26 01:43:39
translate-c: add limited OffsetOfExpr support
Add support for OffsetOfExpr that contain exactly 1 component, when that component is a field. For example, given: ```c struct S { float f; double d; }; struct T { long l; int i; struct S s[10]; }; ``` Then: ```c offsetof(struct T, i) // supported offsetof(struct T, s[2].d) // not supported currently ```
1 parent 0423f0f
src/translate_c/ast.zig
@@ -148,6 +148,8 @@ pub const Node = extern union {
         ptr_cast,
         /// @divExact(lhs, rhs)
         div_exact,
+        /// @byteOffsetOf(lhs, rhs)
+        byte_offset_of,
 
         negate,
         negate_wrap,
@@ -303,6 +305,7 @@ pub const Node = extern union {
                 .std_mem_zeroinit,
                 .ptr_cast,
                 .div_exact,
+                .byte_offset_of,
                 => Payload.BinOp,
 
                 .integer_literal,
@@ -1135,6 +1138,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
             const payload = node.castTag(.div_exact).?.data;
             return renderBuiltinCall(c, "@divExact", &.{ payload.lhs, payload.rhs });
         },
+        .byte_offset_of => {
+            const payload = node.castTag(.byte_offset_of).?.data;
+            return renderBuiltinCall(c, "@byteOffsetOf", &.{ payload.lhs, payload.rhs });
+        },
         .sizeof => {
             const payload = node.castTag(.sizeof).?.data;
             return renderBuiltinCall(c, "@sizeOf", &.{payload});
@@ -2001,6 +2008,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .array_type,
         .bool_to_int,
         .div_exact,
+        .byte_offset_of,
         => {
             // no grouping needed
             return renderNode(c, node);
src/clang.zig
@@ -432,6 +432,9 @@ pub const FieldDecl = opaque {
 
     pub const getLocation = ZigClangFieldDecl_getLocation;
     extern fn ZigClangFieldDecl_getLocation(*const FieldDecl) SourceLocation;
+
+    pub const getParent = ZigClangFieldDecl_getParent;
+    extern fn ZigClangFieldDecl_getParent(*const FieldDecl) ?*const RecordDecl;
 };
 
 pub const FileID = opaque {};
@@ -593,6 +596,34 @@ pub const TypeOfExprType = opaque {
     extern fn ZigClangTypeOfExprType_getUnderlyingExpr(*const TypeOfExprType) *const Expr;
 };
 
+pub const OffsetOfNode = opaque {
+    pub const getKind = ZigClangOffsetOfNode_getKind;
+    extern fn ZigClangOffsetOfNode_getKind(*const OffsetOfNode) OffsetOfNode_Kind;
+
+    pub const getArrayExprIndex = ZigClangOffsetOfNode_getArrayExprIndex;
+    extern fn ZigClangOffsetOfNode_getArrayExprIndex(*const OffsetOfNode) c_uint;
+
+    pub const getField = ZigClangOffsetOfNode_getField;
+    extern fn ZigClangOffsetOfNode_getField(*const OffsetOfNode) *FieldDecl;
+};
+
+pub const OffsetOfExpr = opaque {
+    pub const getNumComponents = ZigClangOffsetOfExpr_getNumComponents;
+    extern fn ZigClangOffsetOfExpr_getNumComponents(*const OffsetOfExpr) c_uint;
+
+    pub const getNumExpressions = ZigClangOffsetOfExpr_getNumExpressions;
+    extern fn ZigClangOffsetOfExpr_getNumExpressions(*const OffsetOfExpr) c_uint;
+
+    pub const getIndexExpr = ZigClangOffsetOfExpr_getIndexExpr;
+    extern fn ZigClangOffsetOfExpr_getIndexExpr(*const OffsetOfExpr, idx: c_uint) *const Expr;
+
+    pub const getComponent = ZigClangOffsetOfExpr_getComponent;
+    extern fn ZigClangOffsetOfExpr_getComponent(*const OffsetOfExpr, idx: c_uint) *const OffsetOfNode;
+
+    pub const getBeginLoc = ZigClangOffsetOfExpr_getBeginLoc;
+    extern fn ZigClangOffsetOfExpr_getBeginLoc(*const OffsetOfExpr) SourceLocation;
+};
+
 pub const MemberExpr = opaque {
     pub const getBase = ZigClangMemberExpr_getBase;
     extern fn ZigClangMemberExpr_getBase(*const MemberExpr) *const Expr;
@@ -1655,6 +1686,13 @@ pub const UnaryExprOrTypeTrait_Kind = extern enum {
     PreferredAlignOf,
 };
 
+pub const OffsetOfNode_Kind = extern enum {
+    Array,
+    Field,
+    Identifier,
+    Base,
+};
+
 pub const Stage2ErrorMsg = extern struct {
     filename_ptr: ?[*]const u8,
     filename_len: usize,
src/translate_c.zig
@@ -1069,12 +1069,64 @@ fn transStmt(
             const expr = try transExpr(c, scope, source_expr, .used);
             return maybeSuppressResult(c, scope, result_used, expr);
         },
+        .OffsetOfExprClass => return transOffsetOfExpr(c, scope, @ptrCast(*const clang.OffsetOfExpr, stmt), result_used),
         else => {
             return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
         },
     }
 }
 
+/// Translate a "simple" offsetof expression containing exactly one component,
+/// when that component is of kind .Field - e.g. offsetof(mytype, myfield)
+fn transSimpleOffsetOfExpr(
+    c: *Context,
+    scope: *Scope,
+    expr: *const clang.OffsetOfExpr,
+) TransError!Node {
+    assert(expr.getNumComponents() == 1);
+    const component = expr.getComponent(0);
+    if (component.getKind() == .Field) {
+        const field_decl = component.getField();
+        if (field_decl.getParent()) |record_decl| {
+            if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |type_name| {
+                const type_node = try Tag.type.create(c.arena, type_name);
+
+                var raw_field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
+                const quoted_field_name = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{raw_field_name});
+                const field_name_node = try Tag.string_literal.create(c.arena, quoted_field_name);
+
+                return Tag.byte_offset_of.create(c.arena, .{
+                    .lhs = type_node,
+                    .rhs = field_name_node,
+                });
+            }
+        }
+    }
+    return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "Failed to translate simple OffsetOfExpr", .{});
+}
+
+fn transOffsetOfExpr(
+    c: *Context,
+    scope: *Scope,
+    expr: *const clang.OffsetOfExpr,
+    result_used: ResultUsed,
+) TransError!Node {
+    if (expr.getNumComponents() == 1) {
+        const offsetof_expr = try transSimpleOffsetOfExpr(c, scope, expr);
+        return maybeSuppressResult(c, scope, result_used, offsetof_expr);
+    }
+
+    // TODO implement OffsetOfExpr with more than 1 component
+    // OffsetOfExpr API:
+    //     call expr.getComponent(idx) while idx < expr.getNumComponents()
+    //     component.getKind() will be either .Array or .Field (other kinds are C++-only)
+    //     if .Field, use component.getField() to retrieve *clang.FieldDecl
+    //     if .Array, use component.getArrayExprIndex() to get a c_uint which
+    //         can be passed to expr.getIndexExpr(expr_index) to get the *clang.Expr for the array index
+
+    return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{});
+}
+
 fn transBinaryOperator(
     c: *Context,
     scope: *Scope,
src/zig_clang.cpp
@@ -2609,6 +2609,46 @@ const struct ZigClangExpr *ZigClangTypeOfExprType_getUnderlyingExpr(const struct
     return reinterpret_cast<const struct ZigClangExpr *>(casted->getUnderlyingExpr());
 }
 
+enum ZigClangOffsetOfNode_Kind ZigClangOffsetOfNode_getKind(const struct ZigClangOffsetOfNode *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfNode *>(self);
+    return (ZigClangOffsetOfNode_Kind)casted->getKind();
+}
+
+unsigned ZigClangOffsetOfNode_getArrayExprIndex(const struct ZigClangOffsetOfNode *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfNode *>(self);
+    return casted->getArrayExprIndex();
+}
+
+struct ZigClangFieldDecl *ZigClangOffsetOfNode_getField(const struct ZigClangOffsetOfNode *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfNode *>(self);
+    return reinterpret_cast<ZigClangFieldDecl *>(casted->getField());
+}
+
+unsigned ZigClangOffsetOfExpr_getNumComponents(const struct ZigClangOffsetOfExpr *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfExpr *>(self);
+    return casted->getNumComponents();
+}
+
+unsigned ZigClangOffsetOfExpr_getNumExpressions(const struct ZigClangOffsetOfExpr *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfExpr *>(self);
+    return casted->getNumExpressions();
+}
+
+const struct ZigClangExpr *ZigClangOffsetOfExpr_getIndexExpr(const struct ZigClangOffsetOfExpr *self, unsigned idx) {
+    auto casted = reinterpret_cast<const clang::OffsetOfExpr *>(self);
+    return reinterpret_cast<const struct ZigClangExpr *>(casted->getIndexExpr(idx));
+}
+
+const struct ZigClangOffsetOfNode *ZigClangOffsetOfExpr_getComponent(const struct ZigClangOffsetOfExpr *self, unsigned idx) {
+    auto casted = reinterpret_cast<const clang::OffsetOfExpr *>(self);
+    return reinterpret_cast<const struct ZigClangOffsetOfNode *>(&casted->getComponent(idx));
+}
+
+ZigClangSourceLocation ZigClangOffsetOfExpr_getBeginLoc(const ZigClangOffsetOfExpr *self) {
+    auto casted = reinterpret_cast<const clang::OffsetOfExpr *>(self);
+    return bitcast(casted->getBeginLoc());
+}
+
 struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *self) {
     auto casted = reinterpret_cast<const clang::ElaboratedType *>(self);
     return bitcast(casted->getNamedType());
@@ -3008,6 +3048,11 @@ ZigClangSourceLocation ZigClangFieldDecl_getLocation(const struct ZigClangFieldD
     return bitcast(casted->getLocation());
 }
 
+const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigClangFieldDecl *self) {
+    auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
+    return reinterpret_cast<const ZigClangRecordDecl *>(casted->getParent());
+}
+
 ZigClangQualType ZigClangFieldDecl_getType(const struct ZigClangFieldDecl *self) {
     auto casted = reinterpret_cast<const clang::FieldDecl *>(self);
     return bitcast(casted->getType());
src/zig_clang.h
@@ -928,6 +928,13 @@ enum ZigClangUnaryExprOrTypeTrait_Kind {
     ZigClangUnaryExprOrTypeTrait_KindPreferredAlignOf,
 };
 
+enum ZigClangOffsetOfNode_Kind {
+    ZigClangOffsetOfNode_KindArray,
+    ZigClangOffsetOfNode_KindField,
+    ZigClangOffsetOfNode_KindIdentifier,
+    ZigClangOffsetOfNode_KindBase,
+};
+
 ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,
         struct ZigClangSourceLocation Loc);
 ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,
@@ -1161,6 +1168,16 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangTypeOfType_getUnderlyingType(const
 
 ZIG_EXTERN_C const struct ZigClangExpr *ZigClangTypeOfExprType_getUnderlyingExpr(const struct ZigClangTypeOfExprType *);
 
+ZIG_EXTERN_C enum ZigClangOffsetOfNode_Kind ZigClangOffsetOfNode_getKind(const struct ZigClangOffsetOfNode *);
+ZIG_EXTERN_C unsigned ZigClangOffsetOfNode_getArrayExprIndex(const struct ZigClangOffsetOfNode *);
+ZIG_EXTERN_C struct ZigClangFieldDecl * ZigClangOffsetOfNode_getField(const struct ZigClangOffsetOfNode *);
+
+ZIG_EXTERN_C unsigned ZigClangOffsetOfExpr_getNumComponents(const struct ZigClangOffsetOfExpr *);
+ZIG_EXTERN_C unsigned ZigClangOffsetOfExpr_getNumExpressions(const struct ZigClangOffsetOfExpr *);
+ZIG_EXTERN_C const struct ZigClangExpr *ZigClangOffsetOfExpr_getIndexExpr(const struct ZigClangOffsetOfExpr *, unsigned idx);
+ZIG_EXTERN_C const struct ZigClangOffsetOfNode *ZigClangOffsetOfExpr_getComponent(const struct ZigClangOffsetOfExpr *, unsigned idx);
+ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangOffsetOfExpr_getBeginLoc(const struct ZigClangOffsetOfExpr *);
+
 ZIG_EXTERN_C struct ZigClangQualType ZigClangElaboratedType_getNamedType(const struct ZigClangElaboratedType *);
 ZIG_EXTERN_C enum ZigClangElaboratedTypeKeyword ZigClangElaboratedType_getKeyword(const struct ZigClangElaboratedType *);
 
@@ -1261,6 +1278,7 @@ ZIG_EXTERN_C bool ZigClangFieldDecl_isBitField(const struct ZigClangFieldDecl *)
 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 *);
+ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const struct ZigClangFieldDecl *);
 
 ZIG_EXTERN_C const struct ZigClangExpr *ZigClangEnumConstantDecl_getInitExpr(const struct ZigClangEnumConstantDecl *);
 ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
test/run_translated_c.zig
@@ -1073,4 +1073,37 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\    return 0;
         \\}
     , "");
+
+    cases.add("offsetof",
+        \\#include <stddef.h>
+        \\#include <stdlib.h>
+        \\#define container_of(ptr, type, member) ({                      \
+        \\        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        \\        (type *)( (char *)__mptr - offsetof(type,member) );})
+        \\typedef struct {
+        \\    int i;
+        \\    struct { int x; char y; int z; } s;
+        \\    float f;
+        \\} container;
+        \\int main(void) {
+        \\    if (offsetof(container, i) != 0) abort();
+        \\    if (offsetof(container, s) <= offsetof(container, i)) abort();
+        \\    if (offsetof(container, f) <= offsetof(container, s)) abort();
+        \\
+        \\    container my_container;
+        \\    typeof(my_container.s) *inner_member_pointer = &my_container.s;
+        \\    float *float_member_pointer = &my_container.f;
+        \\    int *anon_member_pointer = &my_container.s.z;
+        \\    container *my_container_p;
+        \\
+        \\    my_container_p = container_of(inner_member_pointer, container, s);
+        \\    if (my_container_p != &my_container) abort();
+        \\
+        \\    my_container_p = container_of(float_member_pointer, container, f);
+        \\    if (my_container_p != &my_container) abort();
+        \\
+        \\    if (container_of(anon_member_pointer, typeof(my_container.s), z) != inner_member_pointer) abort();
+        \\    return 0;
+        \\}
+    , "");
 }