Commit 5dac3683c9

Veikka Tuominen <git@vexu.eu>
2021-02-08 10:52:21
translate-c: convert record and enum decls
1 parent bb867b0
Changed files (2)
src
src/translate_c/ast.zig
@@ -10,6 +10,7 @@ pub const Node = extern union {
     pub const Tag = enum {
         null_literal,
         undefined_literal,
+        /// opaque {}
         opaque_literal,
         true_literal,
         false_literal,
@@ -42,6 +43,7 @@ pub const Node = extern union {
         func,
         warning,
         failed_decl,
+        /// All enums are non-exhaustive
         @"enum",
         @"struct",
         @"union",
@@ -145,8 +147,12 @@ pub const Node = extern union {
         arg_redecl,
         /// const name = init;
         typedef,
+        var_simple,
         /// pub const name = init;
         pub_typedef,
+        pub_var_simple,
+        /// pub const enum_field_name = @enumToInt(enum_name.field_name);
+        enum_redecl,
 
         pub const last_no_payload_tag = Tag.usingnamespace_builtins;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -266,7 +272,8 @@ pub const Node = extern union {
                 .array_type => Payload.Array,
                 .arg_redecl => Payload.ArgRedecl,
                 .log2_int_type => Payload.Log2IntType,
-                .typedef, .pub_typedef => Payload.Typedef,
+                .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl,
+                .enum_redecl => Payload.EnumRedecl,
             };
         }
 
@@ -419,41 +426,37 @@ pub const Payload = struct {
             return_type: Node,
             body: ?Node,
             alignment: ?c_uint,
-
-            pub const Param = struct {
-                is_noalias: bool,
-                name: ?[]const u8,
-                type: Node,
-            };
         },
+
+        pub const Param = struct {
+            is_noalias: bool,
+            name: ?[]const u8,
+            type: Node,
+        };
     };
 
     pub const Enum = struct {
         base: Node = .{ .tag = .@"enum" },
-        data: struct {
-            name: ?[]const u8,
-            fields: []Field,
+        data: []Field,
 
-            pub const Field = struct {
-                name: []const u8,
-                value: ?[]const u8,
-            };
-        },
+        pub const Field = struct {
+            name: []const u8,
+            value: ?Node,
+        };
     };
 
     pub const Record = struct {
         base: Node,
         data: struct {
-            name: ?[]const u8,
             @"packed": bool,
             fields: []Field,
-
-            pub const Field = struct {
-                name: []const u8,
-                type: Type,
-                alignment: c_uint,
-            };
         },
+
+        pub const Field = struct {
+            name: []const u8,
+            type: Node,
+            alignment: ?c_uint,
+        };
     };
 
     pub const ArrayInit = struct {
@@ -514,13 +517,22 @@ pub const Payload = struct {
         data: std.math.Log2Int(u64),
     };
 
-    pub const Typedef = struct {
+    pub const SimpleVarDecl = struct {
         base: Node,
         data: struct {
             name: []const u8,
             init: Node,
         },
     };
+
+    pub const EnumRedecl = struct {
+        base: Node,
+        data: struct {
+            enum_val_name: []const u8,
+            field_name: []const u8,
+            enum_name: []const u8,
+        },
+    };
 };
 
 /// Converts the nodes into a Zig ast.
src/translate_c.zig
@@ -783,7 +783,7 @@ fn transCreateNodeTypedef(
 
     const payload = try c.arena.create(ast.Payload.Typedef);
     payload.* = .{
-        .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[toplevel] },
+        .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[@boolToInt(toplevel)] },
         .data = .{
             .name = checked_name,
             .init = init_node,
@@ -792,7 +792,7 @@ fn transCreateNodeTypedef(
     return Node.initPayload(&payload.base);
 }
 
-fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node {
+fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?Node {
     if (c.decl_table.get(@ptrToInt(record_decl.getCanonicalDecl()))) |name|
         return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
     const record_loc = record_decl.getLocation();
@@ -807,46 +807,30 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
     }
 
     var container_kind_name: []const u8 = undefined;
-    var container_kind: std.zig.Token.Id = undefined;
+    var is_union = false;
     if (record_decl.isUnion()) {
         container_kind_name = "union";
-        container_kind = .Keyword_union;
+        is_union = true;
     } else if (record_decl.isStruct()) {
         container_kind_name = "struct";
-        container_kind = .Keyword_struct;
     } else {
-        try emitWarning(c, record_loc, "record {s} is not a struct or union", .{bare_name});
+        try warn(c, record_loc, "record {s} is not a struct or union", .{bare_name});
         return null;
     }
 
     const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
     _ = try c.decl_table.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
 
-    const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
-    const mut_tok = try appendToken(c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(c, name);
-
-    const eq_token = try appendToken(c, .Equal, "=");
-
-    var semicolon: ast.TokenIndex = undefined;
+    const is_pub = !is_unnamed;
     const init_node = blk: {
-        const rp = makeRestorePoint(c);
         const record_def = record_decl.getDefinition() orelse {
             _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
-            const opaque_type = try transCreateNodeOpaqueType(c);
-            semicolon = try appendToken(c, .Semicolon, ";");
-            break :blk opaque_type;
+            break :blk Node.opaque_literal.init();
         };
 
-        const layout_tok = try if (record_decl.getPackedAttribute())
-            appendToken(c, .Keyword_packed, "packed")
-        else
-            appendToken(c, .Keyword_extern, "extern");
-        const container_tok = try appendToken(c, container_kind, container_kind_name);
-        const lbrace_token = try appendToken(c, .LBrace, "{");
-
-        var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
-        defer fields_and_decls.deinit();
+        const is_packed = record_decl.getPackedAttribute();
+        var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);
+        defer fields.deinit();
 
         var unnamed_field_count: u32 = 0;
         var it = record_def.field_begin();
@@ -858,110 +842,82 @@ fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*as
 
             if (field_decl.isBitField()) {
                 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
-                const opaque_type = try transCreateNodeOpaqueType(c);
-                semicolon = try appendToken(c, .Semicolon, ";");
-                try emitWarning(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
-                break :blk opaque_type;
+                try warn(c, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
+                break :blk Node.opaque_literal.init();
             }
 
             if (qualTypeCanon(field_qt).isIncompleteOrZeroLengthArrayType(c.clang_context)) {
                 _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
-                const opaque_type = try transCreateNodeOpaqueType(c);
-                semicolon = try appendToken(c, .Semicolon, ";");
-                try emitWarning(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
-                break :blk opaque_type;
+                try warn(c, field_loc, "{s} demoted to opaque type - has variable length array", .{container_kind_name});
+                break :blk Node.opaque_literal.init();
             }
 
             var is_anon = false;
-            var raw_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
-            if (field_decl.isAnonymousStructOrUnion() or raw_name.len == 0) {
+            var field_name = try c.str(@ptrCast(*const clang.NamedDecl, field_decl).getName_bytes_begin());
+            if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {
                 // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.
-                raw_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
+                field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
                 unnamed_field_count += 1;
                 is_anon = true;
             }
-            const field_name = try appendIdentifier(c, raw_name);
-            _ = try appendToken(c, .Colon, ":");
-            const field_type = transQualType(rp, field_qt, field_loc) catch |err| switch (err) {
+            const field_type = transQualType(c, field_qt, field_loc) catch |err| switch (err) {
                 error.UnsupportedType => {
                     _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), {});
-                    const opaque_type = try transCreateNodeOpaqueType(c);
-                    semicolon = try appendToken(c, .Semicolon, ";");
-                    try emitWarning(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name });
-                    break :blk opaque_type;
+                    try warn(c, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, raw_name });
+                    break :blk Node.opaque_literal.init();
                 },
                 else => |e| return e,
             };
 
-            const align_expr = blk_2: {
+            const alignment = blk_2: {
                 const alignment = field_decl.getAlignedAttribute(c.clang_context);
                 if (alignment != 0) {
-                    _ = try appendToken(c, .Keyword_align, "align");
-                    _ = try appendToken(c, .LParen, "(");
                     // Clang reports the alignment in bits
-                    const expr = try transCreateNodeInt(c, alignment / 8);
-                    _ = try appendToken(c, .RParen, ")");
-
-                    break :blk_2 expr;
+                    break :blk_2 alignment / 8;
                 }
                 break :blk_2 null;
             };
 
-            const field_node = try c.arena.create(ast.Node.ContainerField);
-            field_node.* = .{
-                .doc_comments = null,
-                .comptime_token = null,
-                .name_token = field_name,
-                .type_expr = field_type,
-                .value_expr = null,
-                .align_expr = align_expr,
-            };
-
             if (is_anon) {
-                _ = try c.decl_table.put(
-                    c.gpa,
-                    @ptrToInt(field_decl.getCanonicalDecl()),
-                    raw_name,
-                );
+                _ = try c.decl_table.put(c.gpa, @ptrToInt(field_decl.getCanonicalDecl()), field_name);
             }
 
-            try fields_and_decls.append(&field_node.base);
-            _ = try appendToken(c, .Comma, ",");
+            try fields.append(.{
+                .name = field_name,
+                .type = field_type,
+                .alignment = alignment,
+            });
         }
-        const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
+
+        const payload = try c.arena.create(ast.Payload.Record);
         container_node.* = .{
-            .layout_token = layout_tok,
-            .kind_token = container_tok,
-            .init_arg_expr = .None,
-            .fields_and_decls_len = fields_and_decls.items.len,
-            .lbrace_token = lbrace_token,
-            .rbrace_token = try appendToken(c, .RBrace, "}"),
+            .base = .{ .tag = ([2]ast.Node.Tag{ .@"struct", .@"union" })[@boolToInt(is_union)] },
+            .data = .{
+                .is_packed = is_packed,
+                .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
+            },
         };
-        mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
-        semicolon = try appendToken(c, .Semicolon, ";");
-        break :blk &container_node.base;
+        break :blk Node.initPayload(&container_node.base);
     };
 
-    const node = try ast.Node.VarDecl.create(c.arena, .{
-        .name_token = name_tok,
-        .mut_token = mut_tok,
-        .semicolon_token = semicolon,
-    }, .{
-        .visib_token = visib_tok,
-        .eq_token = eq_token,
-        .init_node = init_node,
-    });
+    const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
+    payload.* = .{
+        .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
+        .data = .{
+            .name = name,
+            .init = init_node,
+        },
+    };
 
-    try addTopLevelDecl(c, name, &node.base);
+    try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
     if (!is_unnamed)
         try c.alias_list.append(.{ .alias = bare_name, .name = name });
-    return transCreateNodeIdentifier(c, name);
+    return Node.identifier.create(c.arena, name);
 }
 
-fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node {
+fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?Node {
     if (c.decl_table.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |name|
         return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
-    const rp = makeRestorePoint(c);
     const enum_loc = enum_decl.getLocation();
 
     var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
@@ -974,10 +930,7 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
     const name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
     _ = try c.decl_table.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
 
-    const visib_tok = if (!is_unnamed) try appendToken(c, .Keyword_pub, "pub") else null;
-    const mut_tok = try appendToken(c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(c, name);
-    const eq_token = try appendToken(c, .Equal, "=");
+    const is_pub = !is_unnamed;
 
     const init_node = if (enum_decl.getDefinition()) |enum_def| blk: {
         var pure_enum = true;
@@ -991,11 +944,8 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
             }
         }
 
-        const extern_tok = try appendToken(c, .Keyword_extern, "extern");
-        const container_tok = try appendToken(c, .Keyword_enum, "enum");
-
-        var fields_and_decls = std.ArrayList(*ast.Node).init(c.gpa);
-        defer fields_and_decls.deinit();
+        var fields = std.ArrayList(ast.Payload.Enum.Field).init(c.gpa);
+        defer fields.deinit();
 
         const int_type = enum_decl.getIntegerType();
         // The underlying type may be null in case of forward-declared enum
@@ -1003,30 +953,23 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
         // default to the usual integer type used for all the enums.
 
         // default to c_int since msvc and gcc default to different types
-        _ = try appendToken(c, .LParen, "(");
-        const init_arg_expr = ast.Node.ContainerDecl.InitArg{
-            .Type = if (int_type.ptr != null and
-                !isCBuiltinType(int_type, .UInt) and
-                !isCBuiltinType(int_type, .Int))
-                transQualType(rp, int_type, enum_loc) catch |err| switch (err) {
-                    error.UnsupportedType => {
-                        try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
-                        return null;
-                    },
-                    else => |e| return e,
-                }
-            else
-                try transCreateNodeIdentifier(c, "c_int"),
-        };
-        _ = try appendToken(c, .RParen, ")");
-
-        const lbrace_token = try appendToken(c, .LBrace, "{");
+        const init_arg_expr = if (int_type.ptr != null and
+            !isCBuiltinType(int_type, .UInt) and
+            !isCBuiltinType(int_type, .Int))
+            transQualType(c, int_type, enum_loc) catch |err| switch (err) {
+                error.UnsupportedType => {
+                    try failDecl(c, enum_loc, name, "unable to translate enum tag type", .{});
+                    return null;
+                },
+                else => |e| return e,
+            }
+        else
+            try Node.type.create(c.arena, "c_int");
 
         it = enum_def.enumerator_begin();
         end_it = enum_def.enumerator_end();
         while (it.neq(end_it)) : (it = it.next()) {
             const enum_const = it.deref();
-
             const enum_val_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_const).getName_bytes_begin());
 
             const field_name = if (!is_unnamed and mem.startsWith(u8, enum_val_name, bare_name))
@@ -1034,100 +977,41 @@ fn transEnumDecl(c: *Context, enum_decl: *const clang.EnumDecl) Error!?*ast.Node
             else
                 enum_val_name;
 
-            const field_name_tok = try appendIdentifier(c, field_name);
-
-            const int_node = if (!pure_enum) blk_2: {
-                _ = try appendToken(c, .Colon, "=");
-                break :blk_2 try transCreateNodeAPInt(c, enum_const.getInitVal());
-            } else
+            const int_node = if (!pure_enum)
+                try transCreateNodeAPInt(c, enum_const.getInitVal())
+            else
                 null;
 
-            const field_node = try c.arena.create(ast.Node.ContainerField);
-            field_node.* = .{
-                .doc_comments = null,
-                .comptime_token = null,
-                .name_token = field_name_tok,
-                .type_expr = null,
-                .value_expr = int_node,
-                .align_expr = null,
-            };
-
-            try fields_and_decls.append(&field_node.base);
-            _ = try appendToken(c, .Comma, ",");
+            try fields_and_decls.append(.{
+                .name = field_name,
+                .value = int_node,
+            });
 
             // In C each enum value is in the global namespace. So we put them there too.
             // At this point we can rely on the enum emitting successfully.
-            const tld_visib_tok = try appendToken(c, .Keyword_pub, "pub");
-            const tld_mut_tok = try appendToken(c, .Keyword_const, "const");
-            const tld_name_tok = try appendIdentifier(c, enum_val_name);
-            const tld_eq_token = try appendToken(c, .Equal, "=");
-            const cast_node = try rp.c.createBuiltinCall("@enumToInt", 1);
-            const enum_ident = try transCreateNodeIdentifier(c, name);
-            const period_tok = try appendToken(c, .Period, ".");
-            const field_ident = try transCreateNodeIdentifier(c, field_name);
-            const field_access_node = try c.arena.create(ast.Node.SimpleInfixOp);
-            field_access_node.* = .{
-                .base = .{ .tag = .Period },
-                .op_token = period_tok,
-                .lhs = enum_ident,
-                .rhs = field_ident,
-            };
-            cast_node.params()[0] = &field_access_node.base;
-            cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-            const tld_init_node = &cast_node.base;
-            const tld_semicolon_token = try appendToken(c, .Semicolon, ";");
-            const tld_node = try ast.Node.VarDecl.create(c.arena, .{
-                .name_token = tld_name_tok,
-                .mut_token = tld_mut_tok,
-                .semicolon_token = tld_semicolon_token,
-            }, .{
-                .visib_token = tld_visib_tok,
-                .eq_token = tld_eq_token,
-                .init_node = tld_init_node,
-            });
-            try addTopLevelDecl(c, field_name, &tld_node.base);
+            try addTopLevelDecl(c, field_name, try Node.enum_redecl.create(c.arena, .{
+                .enum_val_name = enum_val_name,
+                .field_name = field_name,
+                .enum_name = name,
+            }));
         }
-        // make non exhaustive
-        const field_node = try c.arena.create(ast.Node.ContainerField);
-        field_node.* = .{
-            .doc_comments = null,
-            .comptime_token = null,
-            .name_token = try appendIdentifier(c, "_"),
-            .type_expr = null,
-            .value_expr = null,
-            .align_expr = null,
-        };
 
-        try fields_and_decls.append(&field_node.base);
-        _ = try appendToken(c, .Comma, ",");
-        const container_node = try ast.Node.ContainerDecl.alloc(c.arena, fields_and_decls.items.len);
-        container_node.* = .{
-            .layout_token = extern_tok,
-            .kind_token = container_tok,
-            .init_arg_expr = init_arg_expr,
-            .fields_and_decls_len = fields_and_decls.items.len,
-            .lbrace_token = lbrace_token,
-            .rbrace_token = try appendToken(c, .RBrace, "}"),
-        };
-        mem.copy(*ast.Node, container_node.fieldsAndDecls(), fields_and_decls.items);
-        break :blk &container_node.base;
+        break :blk try Node.@"enum".create(c.arena, try c.arena.dupe(ast.Payload.Enum.Field, fields.items));
     } else blk: {
         _ = try c.opaque_demotes.put(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), {});
-        break :blk try transCreateNodeOpaqueType(c);
+        break :blk Node.opaque_literal.init();
     };
 
-    const semicolon_token = try appendToken(c, .Semicolon, ";");
-    const node = try ast.Node.VarDecl.create(c.arena, .{
-        .name_token = name_tok,
-        .mut_token = mut_tok,
-        .semicolon_token = semicolon_token,
-    }, .{
-        .visib_token = visib_tok,
-        .eq_token = eq_token,
-        .init_node = init_node,
-    });
+    const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
+    payload.* = .{
+        .base = .{ .tag = ([2]ast.Node.Tag{ .var_simple, .pub_var_simple })[@boolToInt(is_pub)] },
+        .data = .{
+            .name = name,
+            .init = init_node,
+        },
+    };
 
-    try addTopLevelDecl(c, name, &node.base);
+    try addTopLevelDecl(c, name, Node.initPayload(&payload.base));
     if (!is_unnamed)
         try c.alias_list.append(.{ .alias = bare_name, .name = name });
     return transCreateNodeIdentifier(c, name);
@@ -1382,22 +1266,22 @@ fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt
 }
 
 fn transCStyleCastExprClass(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.CStyleCastExpr,
     result_used: ResultUsed,
     lrvalue: LRValue,
-) TransError!*ast.Node {
+) TransError!Node {
     const sub_expr = stmt.getSubExpr();
     const cast_node = (try transCCast(
-        rp,
+        c,
         scope,
         stmt.getBeginLoc(),
         stmt.getType(),
         sub_expr.getType(),
-        try transExpr(rp, scope, sub_expr, .used, lrvalue),
+        try transExpr(c, scope, sub_expr, .used, lrvalue),
     ));
-    return maybeSuppressResult(rp, scope, result_used, cast_node);
+    return maybeSuppressResult(c, scope, result_used, cast_node);
 }
 
 fn transDeclStmtOne(