Commit b0fa2ff853

LemonBoy <thatlemon@gmail.com>
2020-01-02 17:47:53
Translate struct/union initializer expressions
1 parent 197509e
Changed files (5)
src/zig_clang.cpp
@@ -1825,6 +1825,18 @@ const ZigClangArrayType *ZigClangType_getAsArrayTypeUnsafe(const ZigClangType *s
     return reinterpret_cast<const ZigClangArrayType *>(result);
 }
 
+const ZigClangRecordType *ZigClangType_getAsRecordType(const ZigClangType *self) {
+    auto casted = reinterpret_cast<const clang::Type *>(self);
+    const clang::RecordType *result = casted->getAsStructureType();
+    return reinterpret_cast<const ZigClangRecordType *>(result);
+}
+
+const ZigClangRecordType *ZigClangType_getAsUnionType(const ZigClangType *self) {
+    auto casted = reinterpret_cast<const clang::Type *>(self);
+    const clang::RecordType *result = casted->getAsUnionType();
+    return reinterpret_cast<const ZigClangRecordType *>(result);
+}
+
 ZigClangSourceLocation ZigClangStmt_getBeginLoc(const ZigClangStmt *self) {
     auto casted = reinterpret_cast<const clang::Stmt *>(self);
     return bitcast(casted->getBeginLoc());
@@ -1898,6 +1910,12 @@ const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListEx
     return reinterpret_cast<const ZigClangExpr *>(result);
 }
 
+const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self) {
+    auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
+    const clang::FieldDecl *result = casted->getInitializedFieldInUnion();
+    return reinterpret_cast<const ZigClangFieldDecl *>(result);
+}
+
 unsigned ZigClangInitListExpr_getNumInits(const ZigClangInitListExpr *self) {
     auto casted = reinterpret_cast<const clang::InitListExpr *>(self);
     return casted->getNumInits();
src/zig_clang.h
@@ -934,6 +934,8 @@ ZIG_EXTERN_C struct ZigClangQualType ZigClangType_getPointeeType(const struct Zi
 ZIG_EXTERN_C bool ZigClangType_isVoidType(const struct ZigClangType *self);
 ZIG_EXTERN_C const char *ZigClangType_getTypeClassName(const struct ZigClangType *self);
 ZIG_EXTERN_C const struct ZigClangArrayType *ZigClangType_getAsArrayTypeUnsafe(const struct ZigClangType *self);
+ZIG_EXTERN_C const ZigClangRecordType *ZigClangType_getAsRecordType(const ZigClangType *self);
+ZIG_EXTERN_C const ZigClangRecordType *ZigClangType_getAsUnionType(const ZigClangType *self);
 
 ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangStmt_getBeginLoc(const struct ZigClangStmt *self);
 ZIG_EXTERN_C enum ZigClangStmtClass ZigClangStmt_getStmtClass(const struct ZigClangStmt *self);
@@ -952,6 +954,7 @@ ZIG_EXTERN_C bool ZigClangExpr_EvaluateAsConstantExpr(const struct ZigClangExpr
 ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getInit(const ZigClangInitListExpr *, unsigned);
 ZIG_EXTERN_C const ZigClangExpr *ZigClangInitListExpr_getArrayFiller(const ZigClangInitListExpr *);
 ZIG_EXTERN_C unsigned ZigClangInitListExpr_getNumInits(const ZigClangInitListExpr *);
+ZIG_EXTERN_C const ZigClangFieldDecl *ZigClangInitListExpr_getInitializedFieldInUnion(const ZigClangInitListExpr *self);
 
 ZIG_EXTERN_C enum ZigClangAPValueKind ZigClangAPValue_getKind(const struct ZigClangAPValue *self);
 ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangAPValue_getInt(const struct ZigClangAPValue *self);
src-self-hosted/clang.zig
@@ -804,6 +804,8 @@ pub extern fn ZigClangType_getPointeeType(self: ?*const struct_ZigClangType) str
 pub extern fn ZigClangType_isVoidType(self: ?*const struct_ZigClangType) bool;
 pub extern fn ZigClangType_getTypeClassName(self: *const struct_ZigClangType) [*:0]const u8;
 pub extern fn ZigClangType_getAsArrayTypeUnsafe(self: *const ZigClangType) *const ZigClangArrayType;
+pub extern fn ZigClangType_getAsRecordType(self: *const ZigClangType) ?*const ZigClangRecordType;
+pub extern fn ZigClangType_getAsUnionType(self: *const ZigClangType) ?*const ZigClangRecordType;
 pub extern fn ZigClangStmt_getBeginLoc(self: *const struct_ZigClangStmt) struct_ZigClangSourceLocation;
 pub extern fn ZigClangStmt_getStmtClass(self: ?*const struct_ZigClangStmt) ZigClangStmtClass;
 pub extern fn ZigClangStmt_classof_Expr(self: ?*const struct_ZigClangStmt) bool;
@@ -813,6 +815,7 @@ pub extern fn ZigClangExpr_getBeginLoc(self: *const struct_ZigClangExpr) struct_
 pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitListExpr, i: c_uint) *const ZigClangExpr;
 pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr;
 pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint;
+pub extern fn ZigClangInitListExpr_getInitializedFieldInUnion(self: ?*const struct_ZigClangInitListExpr) ?*ZigClangFieldDecl;
 pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind;
 pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt;
 pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint;
src-self-hosted/translate_c.zig
@@ -1781,6 +1781,70 @@ fn transExprCoercing(
     return transExpr(rp, scope, expr, .used, .r_value);
 }
 
+fn transInitListExprRecord(
+    rp: RestorePoint,
+    scope: *Scope,
+    loc: ZigClangSourceLocation,
+    expr: *const ZigClangInitListExpr,
+    ty: *const ZigClangType,
+    used: ResultUsed,
+) TransError!*ast.Node {
+    var is_union_type = false;
+    // Unions and Structs are both represented as RecordDecl
+    const record_ty = ZigClangType_getAsRecordType(ty) orelse
+        blk: {
+        is_union_type = true;
+        break :blk ZigClangType_getAsUnionType(ty);
+    } orelse unreachable;
+    const record_decl = ZigClangRecordType_getDecl(record_ty);
+    const record_def = ZigClangRecordDecl_getDefinition(record_decl) orelse
+        unreachable;
+
+    const ty_node = try transType(rp, ty, loc);
+    const init_count = ZigClangInitListExpr_getNumInits(expr);
+    var init_node = try transCreateNodeStructInitializer(rp.c, ty_node);
+
+    var init_i: c_uint = 0;
+    var it = ZigClangRecordDecl_field_begin(record_def);
+    const end_it = ZigClangRecordDecl_field_end(record_def);
+    while (ZigClangRecordDecl_field_iterator_neq(it, end_it)) : (it = ZigClangRecordDecl_field_iterator_next(it)) {
+        const field_decl = ZigClangRecordDecl_field_iterator_deref(it);
+
+        // The initializer for a union type has a single entry only
+        if (is_union_type and field_decl != ZigClangInitListExpr_getInitializedFieldInUnion(expr)) {
+            continue;
+        }
+
+        assert(init_i < init_count);
+        const elem_expr = ZigClangInitListExpr_getInit(expr, init_i);
+        init_i += 1;
+
+        // Generate the field assignment expression:
+        //     .field_name = expr
+        const period_tok = try appendToken(rp.c, .Period, ".");
+
+        const raw_name = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, field_decl)));
+        if (raw_name.len < 1) continue;
+        const field_name_tok = try appendIdentifier(rp.c, raw_name);
+
+        _ = try appendToken(rp.c, .Equal, "=");
+
+        const field_init_node = try rp.c.a().create(ast.Node.FieldInitializer);
+        field_init_node.* = .{
+            .period_token = period_tok,
+            .name_token = field_name_tok,
+            .expr = try transExpr(rp, scope, elem_expr, .used, .r_value),
+        };
+
+        try init_node.op.StructInitializer.push(&field_init_node.base);
+        _ = try appendToken(rp.c, .Comma, ",");
+    }
+
+    init_node.rtoken = try appendToken(rp.c, .RBrace, "}");
+
+    return &init_node.base;
+}
+
 fn transInitListExpr(
     rp: RestorePoint,
     scope: *Scope,
@@ -1793,7 +1857,7 @@ fn transInitListExpr(
     switch (ZigClangType_getTypeClass(qual_type)) {
         .ConstantArray => {},
         .Record, .Elaborated => {
-            return revertAndWarn(rp, error.UnsupportedType, source_loc, "TODO initListExpr for structs", .{});
+            return transInitListExprRecord(rp, scope, source_loc, expr, qual_type, used);
         },
         else => {
             const type_name = rp.c.str(ZigClangType_getTypeClassName(qual_type));
@@ -1861,16 +1925,13 @@ fn transInitListExpr(
     return &cat_node.base;
 }
 
-fn transImplicitValueInitExpr(
+fn transZeroInitExpr(
     rp: RestorePoint,
     scope: *Scope,
-    expr: *const ZigClangExpr,
-    used: ResultUsed,
+    source_loc: ZigClangSourceLocation,
+    ty: *const ZigClangType,
 ) TransError!*ast.Node {
-    const source_loc = ZigClangExpr_getBeginLoc(expr);
-    const qt = getExprQualType(rp.c, expr);
-    const ty = ZigClangQualType_getTypePtr(qt);
-    const node = switch (ZigClangType_getTypeClass(ty)) {
+    switch (ZigClangType_getTypeClass(ty)) {
         .Builtin => blk: {
             const builtin_ty = @ptrCast(*const ZigClangBuiltinType, ty);
             switch (ZigClangBuiltinType_getKind(builtin_ty)) {
@@ -1900,8 +1961,34 @@ fn transImplicitValueInitExpr(
             }
         },
         .Pointer => return transCreateNodeNullLiteral(rp.c),
-        else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}),
-    };
+        .Typedef => {
+            const typedef_ty = @ptrCast(*const ZigClangTypedefType, ty);
+            const typedef_decl = ZigClangTypedefType_getDecl(typedef_ty);
+            return transZeroInitExpr(
+                rp,
+                scope,
+                source_loc,
+                ZigClangQualType_getTypePtr(
+                    ZigClangTypedefNameDecl_getUnderlyingType(typedef_decl),
+                ),
+            );
+        },
+        else => {},
+    }
+
+    return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
+}
+
+fn transImplicitValueInitExpr(
+    rp: RestorePoint,
+    scope: *Scope,
+    expr: *const ZigClangExpr,
+    used: ResultUsed,
+) TransError!*ast.Node {
+    const source_loc = ZigClangExpr_getBeginLoc(expr);
+    const qt = getExprQualType(rp.c, expr);
+    const ty = ZigClangQualType_getTypePtr(qt);
+    return transZeroInitExpr(rp, scope, source_loc, ty);
 }
 
 fn transIfStmt(
@@ -3484,6 +3571,19 @@ fn transCreateNodeContainerInitializer(c: *Context, dot_tok: ast.TokenIndex) !*a
     return node;
 }
 
+fn transCreateNodeStructInitializer(c: *Context, ty: *ast.Node) !*ast.Node.SuffixOp {
+    _ = try appendToken(c, .LBrace, "{");
+    const node = try c.a().create(ast.Node.SuffixOp);
+    node.* = ast.Node.SuffixOp{
+        .lhs = .{ .node = ty },
+        .op = .{
+            .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(c.a()),
+        },
+        .rtoken = undefined, // set after appending values
+    };
+    return node;
+}
+
 fn transCreateNodeInt(c: *Context, int: var) !*ast.Node {
     const token = try appendTokenFmt(c, .IntegerLiteral, "{}", .{int});
     const node = try c.a().create(ast.Node.IntegerLiteral);
test/translate_c.zig
@@ -2,6 +2,69 @@ const tests = @import("tests.zig");
 const builtin = @import("builtin");
 
 pub fn addCases(cases: *tests.TranslateCContext) void {
+    cases.add("union initializer",
+        \\union { int x; char c[4]; }
+        \\  ua = {1},
+        \\  ub = {.c={'a','b','b','a'}};
+    , &[_][]const u8{
+        \\const union_unnamed_1 = extern union {
+        \\    x: c_int,
+        \\    c: [4]u8,
+        \\};
+        \\pub export var ua: union_unnamed_1 = union_unnamed_1{
+        \\    .x = @as(c_int, 1),
+        \\};
+        \\pub export var ub: union_unnamed_1 = union_unnamed_1{
+        \\    .c = .{
+        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
+        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
+        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'b'))),
+        \\        @bitCast(u8, @truncate(i8, @as(c_int, 'a'))),
+        \\    },
+        \\};
+    });
+
+    cases.add("struct initializer - simple",
+        \\struct {double x,y,z;} s0 = {1.2, 1.3};
+        \\struct {int sec,min,hour,day,mon,year;} s1 = {.day=31,12,2014,.sec=30,15,17};
+        \\struct {int x,y;} s2 = {.y = 2, .x=1};
+    , &[_][]const u8{
+        \\const struct_unnamed_1 = extern struct {
+        \\    x: f64,
+        \\    y: f64,
+        \\    z: f64,
+        \\};
+        \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
+        \\    .x = 1.2,
+        \\    .y = 1.3,
+        \\    .z = 0,
+        \\};
+        \\const struct_unnamed_2 = extern struct {
+        \\    sec: c_int,
+        \\    min: c_int,
+        \\    hour: c_int,
+        \\    day: c_int,
+        \\    mon: c_int,
+        \\    year: c_int,
+        \\};
+        \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
+        \\    .sec = @as(c_int, 30),
+        \\    .min = @as(c_int, 15),
+        \\    .hour = @as(c_int, 17),
+        \\    .day = @as(c_int, 31),
+        \\    .mon = @as(c_int, 12),
+        \\    .year = @as(c_int, 2014),
+        \\};
+        \\const struct_unnamed_3 = extern struct {
+        \\    x: c_int,
+        \\    y: c_int,
+        \\};
+        \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
+        \\    .x = @as(c_int, 1),
+        \\    .y = @as(c_int, 2),
+        \\};
+    });
+
     cases.add("simple ptrCast for casts between opaque types",
         \\struct opaque;
         \\struct opaque_2;