Commit bb867b071a

Veikka Tuominen <git@vexu.eu>
2021-02-08 10:07:44
translate-c: convert vardecl and typedef
1 parent f36849f
Changed files (3)
src/translate_c/ast.zig
@@ -137,11 +137,16 @@ pub const Node = extern union {
         single_pointer,
         array_type,
 
-
+        /// @import("std").mem.zeroes(T)
+        std_mem_zeroes,
         // pub const name = @compileError(msg);
         fail_decl,
         // var actual = mangled;
         arg_redecl,
+        /// const name = init;
+        typedef,
+        /// pub const name = init;
+        pub_typedef,
 
         pub const last_no_payload_tag = Tag.usingnamespace_builtins;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
@@ -257,11 +262,11 @@ pub const Node = extern union {
                 .container_init => Payload.ContainerInit,
                 .std_meta_cast => Payload.Infix,
                 .block => Payload.Block,
-                .c_pointer => Payload.Pointer,
-                .single_pointer => Payload.Pointer,
+                .c_pointer, .single_pointer => Payload.Pointer,
                 .array_type => Payload.Array,
                 .arg_redecl => Payload.ArgRedecl,
                 .log2_int_type => Payload.Log2IntType,
+                .typedef, .pub_typedef => Payload.Typedef,
             };
         }
 
@@ -301,6 +306,11 @@ pub const Node = extern union {
 
         return null;
     }
+
+    pub fn initPayload(payload: *Payload) Node {
+        assert(@enumToInt(payload.tag) >= Tag.no_payload_count);
+        return .{ .ptr_otherwise = payload };
+    }
 };
 
 pub const Payload = struct {
@@ -383,13 +393,15 @@ pub const Payload = struct {
     pub const VarDecl = struct {
         base: Node = .{ .tag = .var_decl },
         data: struct {
-            @"pub": bool,
-            @"const": bool,
-            @"extern": bool,
-            @"export": bool,
+            is_pub: bool,
+            is_const: bool,
+            is_extern: bool,
+            is_export: bool,
+            alignment: ?c_uint,
+            linksection_string: ?[]const u8,
             name: []const u8,
-            type: Type,
-            init: Node,
+            type: Node,
+            init: ?Node,
         },
     };
 
@@ -401,12 +413,12 @@ pub const Payload = struct {
             is_export: bool,
             is_var_args: bool,
             name: []const u8,
-            link_section_string: ?[]const u8,
+            linksection_string: ?[]const u8,
             explicit_callconv: ?std.builtin.CallingConvention,
             params: []Param,
             return_type: Node,
             body: ?Node,
-            alignment: c_uint,
+            alignment: ?c_uint,
 
             pub const Param = struct {
                 is_noalias: bool,
@@ -501,6 +513,14 @@ pub const Payload = struct {
         base: Node,
         data: std.math.Log2Int(u64),
     };
+
+    pub const Typedef = struct {
+        base: Node,
+        data: struct {
+            name: []const u8,
+            init: Node,
+        },
+    };
 };
 
 /// Converts the nodes into a Zig ast.
src/astgen.zig
@@ -2664,8 +2664,11 @@ fn identifier(
         return mod.failNode(scope, ident, "TODO implement '_' identifier", .{});
     }
 
-    if (getSimplePrimitiveValue(ident_name)) |typed_value| {
-        const result = try addZIRInstConst(mod, scope, src, typed_value);
+    if (simple_types.get(ident_name)) |val_tag| {
+        const result = try addZIRInstConst(mod, scope, src, TypedValue{
+            .ty = Type.initTag(.type),
+            .val = Value.initTag(val_tag),
+        });
         return rvalue(mod, scope, rl, result);
     }
 
@@ -3325,42 +3328,33 @@ fn callExpr(
     return rvalue(mod, scope, rl, result);
 }
 
-fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
-    const simple_types = std.ComptimeStringMap(Value.Tag, .{
-        .{ "u8", .u8_type },
-        .{ "i8", .i8_type },
-        .{ "isize", .isize_type },
-        .{ "usize", .usize_type },
-        .{ "c_short", .c_short_type },
-        .{ "c_ushort", .c_ushort_type },
-        .{ "c_int", .c_int_type },
-        .{ "c_uint", .c_uint_type },
-        .{ "c_long", .c_long_type },
-        .{ "c_ulong", .c_ulong_type },
-        .{ "c_longlong", .c_longlong_type },
-        .{ "c_ulonglong", .c_ulonglong_type },
-        .{ "c_longdouble", .c_longdouble_type },
-        .{ "f16", .f16_type },
-        .{ "f32", .f32_type },
-        .{ "f64", .f64_type },
-        .{ "f128", .f128_type },
-        .{ "c_void", .c_void_type },
-        .{ "bool", .bool_type },
-        .{ "void", .void_type },
-        .{ "type", .type_type },
-        .{ "anyerror", .anyerror_type },
-        .{ "comptime_int", .comptime_int_type },
-        .{ "comptime_float", .comptime_float_type },
-        .{ "noreturn", .noreturn_type },
-    });
-    if (simple_types.get(name)) |tag| {
-        return TypedValue{
-            .ty = Type.initTag(.type),
-            .val = Value.initTag(tag),
-        };
-    }
-    return null;
-}
+pub const simple_types = std.ComptimeStringMap(Value.Tag, .{
+    .{ "u8", .u8_type },
+    .{ "i8", .i8_type },
+    .{ "isize", .isize_type },
+    .{ "usize", .usize_type },
+    .{ "c_short", .c_short_type },
+    .{ "c_ushort", .c_ushort_type },
+    .{ "c_int", .c_int_type },
+    .{ "c_uint", .c_uint_type },
+    .{ "c_long", .c_long_type },
+    .{ "c_ulong", .c_ulong_type },
+    .{ "c_longlong", .c_longlong_type },
+    .{ "c_ulonglong", .c_ulonglong_type },
+    .{ "c_longdouble", .c_longdouble_type },
+    .{ "f16", .f16_type },
+    .{ "f32", .f32_type },
+    .{ "f64", .f64_type },
+    .{ "f128", .f128_type },
+    .{ "c_void", .c_void_type },
+    .{ "bool", .bool_type },
+    .{ "void", .void_type },
+    .{ "type", .type_type },
+    .{ "anyerror", .anyerror_type },
+    .{ "comptime_int", .comptime_int_type },
+    .{ "comptime_float", .comptime_float_type },
+    .{ "noreturn", .noreturn_type },
+});
 
 fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
     var node = start_node;
src/translate_c.zig
@@ -614,25 +614,21 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
     return addTopLevelDecl(c, fn_name, &proto_node.base);
 }
 
-fn transQualTypeMaybeInitialized(rp: RestorePoint, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!*ast.Node {
+fn transQualTypeMaybeInitialized(c: *Context, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node {
     return if (decl_init) |init_expr|
-        transQualTypeInitialized(rp, qt, init_expr, loc)
+        transQualTypeInitialized(c, qt, init_expr, loc)
     else
-        transQualType(rp, qt, loc);
+        transQualType(c, qt, loc);
 }
+
 /// if mangled_name is not null, this var decl was declared in a block scope.
 fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {
     const var_name = mangled_name orelse try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());
     if (c.global_scope.sym_table.contains(var_name))
         return; // Avoid processing this decl twice
-    const rp = makeRestorePoint(c);
-    const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub");
-
-    const thread_local_token = if (var_decl.getTLSKind() == .None)
-        null
-    else
-        try appendToken(c, .Keyword_threadlocal, "threadlocal");
 
+    const is_pub = mangled_name == null;
+    const is_thread_local = var_decl.getTLSKind() != .None;
     const scope = &c.global_scope.base;
 
     // TODO https://github.com/ziglang/zig/issues/3756
@@ -651,42 +647,27 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
     // does the same as:
     // extern int foo;
     // int foo = 2;
-    const extern_tok = if (storage_class == .Extern and !has_init)
-        try appendToken(c, .Keyword_extern, "extern")
-    else if (storage_class != .Static)
-        try appendToken(c, .Keyword_export, "export")
-    else
-        null;
-
-    const mut_tok = if (is_const)
-        try appendToken(c, .Keyword_const, "const")
-    else
-        try appendToken(c, .Keyword_var, "var");
+    const is_extern = storage_class == .Extern and !has_init;
+    const is_export = !is_extern and storage_class != .Static;
 
-    const name_tok = try appendIdentifier(c, checked_name);
-
-    _ = try appendToken(c, .Colon, ":");
-
-    const type_node = transQualTypeMaybeInitialized(rp, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
+    const type_node = transQualTypeMaybeInitialized(c, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
         error.UnsupportedTranslation, error.UnsupportedType => {
             return failDecl(c, var_decl_loc, checked_name, "unable to resolve variable type", .{});
         },
         error.OutOfMemory => |e| return e,
     };
 
-    var eq_tok: ast.TokenIndex = undefined;
-    var init_node: ?*ast.Node = null;
+    var init_node: ?Node = null;
 
     // If the initialization expression is not present, initialize with undefined.
     // If it is an integer literal, we can skip the @as since it will be redundant
     // with the variable type.
     if (has_init) {
-        eq_tok = try appendToken(c, .Equal, "=");
         if (decl_init) |expr| {
             const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
-                transStringLiteralAsArray(rp, &c.global_scope.base, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(rp.c, type_node) catch 0)
+                transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), zigArraySize(c, type_node) catch 0)
             else
-                transExprCoercing(rp, scope, expr, .used, .r_value);
+                transExprCoercing(c, scope, expr, .used, .r_value);
             init_node = node_or_error catch |err| switch (err) {
                 error.UnsupportedTranslation,
                 error.UnsupportedType,
@@ -695,118 +676,83 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
                 },
                 error.OutOfMemory => |e| return e,
             };
+            if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
+                init_node = try Node.bool_to_int.create(c.arena, init_node);
+            }
         } else {
-            init_node = try transCreateNodeUndefinedLiteral(c);
+            init_node = Node.undefined_literal.init();
         }
     } else if (storage_class != .Extern) {
-        eq_tok = try appendToken(c, .Equal, "=");
         // The C language specification states that variables with static or threadlocal
         // storage without an initializer are initialized to a zero value.
 
         // @import("std").mem.zeroes(T)
-        const import_fn_call = try c.createBuiltinCall("@import", 1);
-        const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
-        import_fn_call.params()[0] = std_node;
-        import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
-        const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
-        const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes");
-
-        const zero_init_call = try c.createCall(outer_field_access, 1);
-        zero_init_call.params()[0] = type_node;
-        zero_init_call.rtoken = try appendToken(c, .RParen, ")");
-
-        init_node = &zero_init_call.base;
+        init_node = try Node.std_mem_zeroes.create(c.arena, type_node);
     }
 
-    const linksection_expr = blk: {
+    const linksection_string = blk: {
         var str_len: usize = undefined;
         if (var_decl.getSectionAttribute(&str_len)) |str_ptr| {
-            _ = try appendToken(rp.c, .Keyword_linksection, "linksection");
-            _ = try appendToken(rp.c, .LParen, "(");
-            const expr = try transCreateNodeStringLiteral(
-                rp.c,
-                try std.fmt.allocPrint(rp.c.arena, "\"{s}\"", .{str_ptr[0..str_len]}),
-            );
-            _ = try appendToken(rp.c, .RParen, ")");
-
-            break :blk expr;
+            break :blk str_ptr[0..str_len];
         }
         break :blk null;
     };
 
-    const align_expr = blk: {
-        const alignment = var_decl.getAlignedAttribute(rp.c.clang_context);
+    const alignment = blk: {
+        const alignment = var_decl.getAlignedAttribute(c.clang_context);
         if (alignment != 0) {
-            _ = try appendToken(rp.c, .Keyword_align, "align");
-            _ = try appendToken(rp.c, .LParen, "(");
             // Clang reports the alignment in bits
-            const expr = try transCreateNodeInt(rp.c, alignment / 8);
-            _ = try appendToken(rp.c, .RParen, ")");
-
-            break :blk expr;
+            break :blk alignment / 8;
         }
         break :blk null;
     };
 
-    const node = try ast.Node.VarDecl.create(c.arena, .{
-        .name_token = name_tok,
-        .mut_token = mut_tok,
-        .semicolon_token = try appendToken(c, .Semicolon, ";"),
-    }, .{
-        .visib_token = visib_tok,
-        .thread_local_token = thread_local_token,
-        .eq_token = eq_tok,
-        .extern_export_token = extern_tok,
-        .type_node = type_node,
-        .align_node = align_expr,
-        .section_node = linksection_expr,
-        .init_node = init_node,
+    const node = try Node.var_decl.create(c.arena, .{
+        .is_pub = is_pub,
+        .is_const = is_const,
+        .is_extern = is_extern,
+        .is_export = is_export,
+        .linksection_string = linksection_string,
+        .alignment = alignment,
+        .name = checked_name,
+        .type = type_node,
+        .init = init_node,
     });
     return addTopLevelDecl(c, checked_name, &node.base);
 }
 
-fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !*ast.Node {
+fn transTypeDefAsBuiltin(c: *Context, typedef_decl: *const clang.TypedefNameDecl, builtin_name: []const u8) !Node {
     _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin_name);
-    return transCreateNodeIdentifier(c, builtin_name);
-}
-
-fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 {
-    const table = [_][2][]const u8{
-        .{ "uint8_t", "u8" },
-        .{ "int8_t", "i8" },
-        .{ "uint16_t", "u16" },
-        .{ "int16_t", "i16" },
-        .{ "uint32_t", "u32" },
-        .{ "int32_t", "i32" },
-        .{ "uint64_t", "u64" },
-        .{ "int64_t", "i64" },
-        .{ "intptr_t", "isize" },
-        .{ "uintptr_t", "usize" },
-        .{ "ssize_t", "isize" },
-        .{ "size_t", "usize" },
-    };
-
-    for (table) |entry| {
-        if (mem.eql(u8, checked_name, entry[0])) {
-            return entry[1];
-        }
-    }
-
-    return null;
-}
+    return Node.identifier.create(c.arena, builtin_name);
+}
+
+const builtin_typedef_map = std.ComptimeStringMap([]const u8, .{
+    .{ "uint8_t", "u8" },
+    .{ "int8_t", "i8" },
+    .{ "uint16_t", "u16" },
+    .{ "int16_t", "i16" },
+    .{ "uint32_t", "u32" },
+    .{ "int32_t", "i32" },
+    .{ "uint64_t", "u64" },
+    .{ "int64_t", "i64" },
+    .{ "intptr_t", "isize" },
+    .{ "uintptr_t", "usize" },
+    .{ "ssize_t", "isize" },
+    .{ "size_t", "usize" },
+});
 
-fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?*ast.Node {
+fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_level_visit: bool) Error!?Node {
     if (c.decl_table.get(@ptrToInt(typedef_decl.getCanonicalDecl()))) |name|
         return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice
-    const rp = makeRestorePoint(c);
 
     const typedef_name = try c.str(@ptrCast(*const clang.NamedDecl, typedef_decl).getName_bytes_begin());
 
     // TODO https://github.com/ziglang/zig/issues/3756
     // TODO https://github.com/ziglang/zig/issues/1802
     const checked_name = if (isZigPrimitiveType(typedef_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ typedef_name, c.getMangle() }) else typedef_name;
-    if (checkForBuiltinTypedef(checked_name)) |builtin| {
-        return transTypeDefAsBuiltin(c, typedef_decl, builtin);
+    if (builtin_typedef_map.get(checked_name)) |builtin| {
+        _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), builtin);
+        return Node.identifier.create(c.arena, builtin);
     }
 
     if (!top_level_visit) {
@@ -814,42 +760,36 @@ fn transTypeDef(c: *Context, typedef_decl: *const clang.TypedefNameDecl, top_lev
     }
 
     _ = try c.decl_table.put(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), checked_name);
-    const node = (try transCreateNodeTypedef(rp, typedef_decl, true, checked_name)) orelse return null;
+    const node = (try transCreateNodeTypedef(c, typedef_decl, true, checked_name)) orelse return null;
     try addTopLevelDecl(c, checked_name, node);
     return transCreateNodeIdentifier(c, checked_name);
 }
 
 fn transCreateNodeTypedef(
-    rp: RestorePoint,
+    c: *Context,
     typedef_decl: *const clang.TypedefNameDecl,
     toplevel: bool,
     checked_name: []const u8,
-) Error!?*ast.Node {
-    const visib_tok = if (toplevel) try appendToken(rp.c, .Keyword_pub, "pub") else null;
-    const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(rp.c, checked_name);
-    const eq_token = try appendToken(rp.c, .Equal, "=");
+) Error!?Node {
     const child_qt = typedef_decl.getUnderlyingType();
     const typedef_loc = typedef_decl.getLocation();
-    const init_node = transQualType(rp, child_qt, typedef_loc) catch |err| switch (err) {
+    const init_node = transQualType(c, child_qt, typedef_loc) catch |err| switch (err) {
         error.UnsupportedType => {
-            try failDecl(rp.c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
+            try failDecl(c, typedef_loc, checked_name, "unable to resolve typedef child type", .{});
             return null;
         },
         error.OutOfMemory => |e| return e,
     };
-    const semicolon_token = try appendToken(rp.c, .Semicolon, ";");
 
-    const node = try ast.Node.VarDecl.create(rp.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,
-    });
-    return &node.base;
+    const payload = try c.arena.create(ast.Payload.Typedef);
+    payload.* = .{
+        .base = .{ .tag = ([2]ast.Node.Tag{ .typedef, .pub_typedef })[toplevel] },
+        .data = .{
+            .name = checked_name,
+            .init = init_node,
+        },
+    };
+    return Node.initPayload(&payload.base);
 }
 
 fn transRecordDecl(c: *Context, record_decl: *const clang.RecordDecl) Error!?*ast.Node {
@@ -1399,13 +1339,15 @@ fn transBinaryOperator(
     const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used, .l_value);
     const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used, .r_value);
 
-    const lhs = if (isBoolRes(lhs_uncasted)) 
+    const lhs = if (isBoolRes(lhs_uncasted))
         try Node.bool_to_int.create(c.arena, lhs_uncasted)
-    else lhs_uncasted;
+    else
+        lhs_uncasted;
 
-    const rhs = if (isBoolRes(rhs_uncasted)) 
+    const rhs = if (isBoolRes(rhs_uncasted))
         try Node.bool_to_int.create(c.arena, rhs_uncasted)
-    else rhs_uncasted;
+    else
+        rhs_uncasted;
 
     const payload = try c.arena.create(ast.Payload.BinOp);
     payload.* = .{
@@ -1415,7 +1357,7 @@ fn transBinaryOperator(
             .rhs = rhs,
         },
     };
-    return maybeSuppressResult(c, scope, used, &payload.base);
+    return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base));
 }
 
 fn transCompoundStmtInline(
@@ -1459,13 +1401,11 @@ fn transCStyleCastExprClass(
 }
 
 fn transDeclStmtOne(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     decl: *const clang.Decl,
     block_scope: *Scope.Block,
-) TransError!*ast.Node {
-    const c = rp.c;
-
+) TransError!Node {
     switch (decl.getKind()) {
         .Var => {
             const var_decl = @ptrCast(*const clang.VarDecl, decl);
@@ -1479,47 +1419,38 @@ fn transDeclStmtOne(
                 .Extern, .Static => {
                     // This is actually a global variable, put it in the global scope and reference it.
                     // `_ = mangled_name;`
-                    try visitVarDecl(rp.c, var_decl, mangled_name);
-                    return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name));
+                    try visitVarDecl(c, var_decl, mangled_name);
+                    return try maybeSuppressResult(c, scope, .unused, try Node.identifier.create(c.arena, mangled_name));
                 },
                 else => {},
             }
 
-            const mut_tok = if (qual_type.isConstQualified())
-                try appendToken(c, .Keyword_const, "const")
-            else
-                try appendToken(c, .Keyword_var, "var");
-            const name_tok = try appendIdentifier(c, mangled_name);
+            const is_const = qual_type.isConstQualified();
 
-            _ = try appendToken(c, .Colon, ":");
             const loc = decl.getLocation();
-            const type_node = try transQualTypeMaybeInitialized(rp, qual_type, decl_init, loc);
+            const type_node = try transQualTypeMaybeInitialized(c, qual_type, decl_init, loc);
 
-            const eq_token = try appendToken(c, .Equal, "=");
             var init_node = if (decl_init) |expr|
                 if (expr.getStmtClass() == .StringLiteralClass)
-                    try transStringLiteralAsArray(rp, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(rp.c, type_node))
+                    try transStringLiteralAsArray(c, scope, @ptrCast(*const clang.StringLiteral, expr), try zigArraySize(c, type_node))
                 else
-                    try transExprCoercing(rp, scope, expr, .used, .r_value)
+                    try transExprCoercing(c, scope, expr, .used, .r_value)
             else
                 try transCreateNodeUndefinedLiteral(c);
             if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
-                const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
-                builtin_node.params()[0] = init_node;
-                builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-                init_node = &builtin_node.base;
+                init_node = try Node.bool_to_int.create(c.arena, init_node);
             }
-            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,
-            }, .{
-                .eq_token = eq_token,
-                .type_node = type_node,
-                .init_node = init_node,
+            return Node.var_decl.create(c.arena, .{
+                .is_pub = false,
+                .is_const = is_const,
+                .is_extern = false,
+                .is_export = false,
+                .linksection_string = null,
+                .alignment = null,
+                .name = mangled_name,
+                .type = type_node,
+                .init = init_node,
             });
-            return &node.base;
         },
         .Typedef => {
             const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);
@@ -1529,7 +1460,7 @@ fn transDeclStmtOne(
             const underlying_type = underlying_qual.getTypePtr();
 
             const mangled_name = try block_scope.makeMangledName(c, name);
-            const node = (try transCreateNodeTypedef(rp, typedef_decl, false, mangled_name)) orelse
+            const node = (try transCreateNodeTypedef(c, typedef_decl, false, mangled_name)) orelse
                 return error.UnsupportedTranslation;
             return node;
         },
@@ -1543,14 +1474,14 @@ fn transDeclStmtOne(
     }
 }
 
-fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) TransError!*ast.Node {
-    const block_scope = scope.findBlockScope(rp.c) catch unreachable;
+fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node {
+    const block_scope = scope.findBlockScope(c) catch unreachable;
 
     var it = stmt.decl_begin();
     const end_it = stmt.decl_end();
     assert(it != end_it);
     while (true) : (it += 1) {
-        const node = try transDeclStmtOne(rp, scope, it[0], block_scope);
+        const node = try transDeclStmtOne(c, scope, it[0], block_scope);
 
         if (it + 1 == end_it) {
             return node;
@@ -1562,15 +1493,15 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const clang.DeclStmt) T
 }
 
 fn transDeclRefExpr(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     expr: *const clang.DeclRefExpr,
     lrvalue: LRValue,
-) TransError!*ast.Node {
+) TransError!Node {
     const value_decl = expr.getDecl();
-    const name = try rp.c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
+    const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
     const mangled_name = scope.getAlias(name);
-    return transCreateNodeIdentifier(rp.c, mangled_name);
+    return Node.identifier.create(c.arena, mangled_name);
 }
 
 fn transImplicitCastExpr(
@@ -1642,52 +1573,29 @@ fn transImplicitCastExpr(
 }
 
 fn transBoolExpr(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     expr: *const clang.Expr,
     used: ResultUsed,
     lrvalue: LRValue,
-    grouped: bool,
-) TransError!*ast.Node {
+) TransError!Node {
     if (@ptrCast(*const clang.Stmt, expr).getStmtClass() == .IntegerLiteralClass) {
         var is_zero: bool = undefined;
-        if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, rp.c.clang_context))) {
-            return revertAndWarn(rp, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
+        if (!(@ptrCast(*const clang.IntegerLiteral, expr).isZero(&is_zero, c.clang_context))) {
+            return revertAndWarn(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
         }
-        return try transCreateNodeBoolLiteral(rp.c, !is_zero);
+        return Node{ .tag = ([2]ast.Node.Tag{ .true_literal, .false_literal })[is_zero] };
     }
 
-    const lparen = if (grouped)
-        try appendToken(rp.c, .LParen, "(")
-    else
-        undefined;
-    var res = try transExpr(rp, scope, expr, used, lrvalue);
-
+    var res = try transExpr(c, scope, expr, used, lrvalue);
     if (isBoolRes(res)) {
-        if (!grouped and res.tag == .GroupedExpression) {
-            const group = @fieldParentPtr(ast.Node.GroupedExpression, "base", res);
-            res = group.expr;
-            // get zig fmt to work properly
-            tokenSlice(rp.c, group.lparen)[0] = ')';
-        }
         return res;
     }
 
-    const ty = getExprQualType(rp.c, expr).getTypePtr();
-    const node = try finishBoolExpr(rp, scope, expr.getBeginLoc(), ty, res, used);
+    const ty = getExprQualType(c, expr).getTypePtr();
+    const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used);
 
-    if (grouped) {
-        const rparen = try appendToken(rp.c, .RParen, ")");
-        const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
-        grouped_expr.* = .{
-            .lparen = lparen,
-            .expr = node,
-            .rparen = rparen,
-        };
-        return maybeSuppressResult(rp, scope, used, &grouped_expr.base);
-    } else {
-        return maybeSuppressResult(rp, scope, used, node);
-    }
+    return maybeSuppressResult(c, scope, used, node);
 }
 
 fn exprIsBooleanType(expr: *const clang.Expr) bool {
@@ -1713,34 +1621,32 @@ fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
     }
 }
 
-fn isBoolRes(res: *ast.Node) bool {
-    switch (res.tag) {
-        .BoolOr,
-        .BoolAnd,
-        .EqualEqual,
-        .BangEqual,
-        .LessThan,
-        .GreaterThan,
-        .LessOrEqual,
-        .GreaterOrEqual,
-        .BoolNot,
-        .BoolLiteral,
+fn isBoolRes(res: Node) bool {
+    switch (res.tag()) {
+        .@"or",
+        .@"and",
+        .equal,
+        .note_equal,
+        .less_than,
+        .less_than_equal,
+        .greater_than,
+        .greater_than_equal,
+        .not,
+        .false_literal,
+        .true_literal,
         => return true,
-
-        .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr),
-
         else => return false,
     }
 }
 
 fn finishBoolExpr(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     loc: clang.SourceLocation,
     ty: *const clang.Type,
-    node: *ast.Node,
+    node: Node,
     used: ResultUsed,
-) TransError!*ast.Node {
+) TransError!Node {
     switch (ty.getTypeClass()) {
         .Builtin => {
             const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
@@ -1772,42 +1678,39 @@ fn finishBoolExpr(
                 .WChar_S,
                 .Float16,
                 => {
-                    const op_token = try appendToken(rp.c, .BangEqual, "!=");
-                    const rhs_node = try transCreateNodeInt(rp.c, 0);
-                    return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+                    // node != 0
+                    return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()});
                 },
                 .NullPtr => {
-                    const op_token = try appendToken(rp.c, .EqualEqual, "==");
-                    const rhs_node = try transCreateNodeNullLiteral(rp.c);
-                    return transCreateNodeInfixOp(rp, scope, node, .EqualEqual, op_token, rhs_node, used, false);
+                    // node == null
+                    return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()});
                 },
                 else => {},
             }
         },
         .Pointer => {
-            const op_token = try appendToken(rp.c, .BangEqual, "!=");
-            const rhs_node = try transCreateNodeNullLiteral(rp.c);
-            return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+            // node == null
+            return Node.equal.create(c.arena, .{ .lhs = node, .rhs = Node.null_literal.init()});
         },
         .Typedef => {
             const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
             const typedef_decl = typedef_ty.getDecl();
             const underlying_type = typedef_decl.getUnderlyingType();
-            return finishBoolExpr(rp, scope, loc, underlying_type.getTypePtr(), node, used);
+            return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used);
         },
         .Enum => {
-            const op_token = try appendToken(rp.c, .BangEqual, "!=");
-            const rhs_node = try transCreateNodeInt(rp.c, 0);
-            return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false);
+            // node != 0
+            return Node.not_equal.create(c.arena, .{ .lhs = node, .rhs = Node.zero_literal.init()});
+            const op_token = try appendToken(c, .BangEqual, "!=");
         },
         .Elaborated => {
             const elaborated_ty = @ptrCast(*const clang.ElaboratedType, ty);
             const named_type = elaborated_ty.getNamedType();
-            return finishBoolExpr(rp, scope, loc, named_type.getTypePtr(), node, used);
+            return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used);
         },
         else => {},
     }
-    return revertAndWarn(rp, error.UnsupportedType, loc, "unsupported bool expression type", .{});
+    return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{});
 }
 
 const SuppressCast = enum {
@@ -4242,7 +4145,7 @@ fn transCreateNodeBoolInfixOp(
             .rhs = rhs,
         },
     };
-    return maybeSuppressResult(c, scope, used, &payload.base);
+    return maybeSuppressResult(c, scope, used, Node.initPayload(&payload.base));
 }
 
 fn transCreateNodePtrType(
@@ -4654,7 +4557,7 @@ fn transCreateNodeShiftOp(
     const lhs = try transExpr(c, scope, lhs_expr, .used, .l_value);
 
     const rhs_type = try qualTypeToLog2IntRef(c, stmt.getType(), rhs_location);
-    const rhs = try transExpr(c, scope, rhs_expr, .used, .r_value);
+    const rhs = try transExprCoercing(c, scope, rhs_expr, .used, .r_value);
     const rhs_casted = try Node.int_cast.create(c.arena, .{ .lhs = rhs_type, .rhs = rhs_type });
 
     const payload = try c.arena.create(ast.Payload.BinOp);
@@ -4663,9 +4566,9 @@ fn transCreateNodeShiftOp(
         .data = .{
             .lhs = lhs,
             .rhs = rhs_casted,
-        }
+        },
     };
-    return &payload.base;
+    return Node.initPayload(&payload.base);
 }
 
 fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
@@ -4961,7 +4864,7 @@ fn finishTransFnProto(
         };
     }
 
-    const link_section_string: ?[]const u8 = blk: {
+    const linksection_string = blk: {
         if (fn_decl) |decl| {
             var str_len: usize = undefined;
             if (decl.getSectionAttribute(&str_len)) |str_ptr| {
@@ -5004,24 +4907,19 @@ fn finishTransFnProto(
         }
     };
 
-    const fn_proto = try c.arena.create(ast.Payload.Func);
-    fn_proto.* = .{
-        .base = .{ .tag = .func },
-        .data = .{
-            .is_pub = is_pub,
-            .is_extern = is_extern,
-            .is_export = is_export,
-            .is_var_args = is_var_args,
-            .name = name,
-            .link_section_string = link_section_string,
-            .explicit_callconv = explicit_callconv,
-            .params = c.arena.dupe(ast.Payload.Func.Param, fn_params.items),
-            .return_type = return_node,
-            .body = null,
-            .alignment = alignment,
-        },
-    };
-    return fn_proto;
+    return Node.func.create(c.arena, .{
+        .is_pub = is_pub,
+        .is_extern = is_extern,
+        .is_export = is_export,
+        .is_var_args = is_var_args,
+        .name = name,
+        .linksection_string = linksection_string,
+        .explicit_callconv = explicit_callconv,
+        .params = try c.arena.dupe(ast.Payload.Func.Param, fn_params.items),
+        .return_type = return_node,
+        .body = null,
+        .alignment = alignment,
+    });
 }
 
 fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void {
@@ -5054,6 +4952,19 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
     errors.ptr.delete(errors.len);
 }
 
+fn isZigPrimitiveType(name: []const u8) bool {
+    if (name.len > 1 and (name[0] == 'u' or name[0] == 'i')) {
+        for (name[1..]) |c| {
+            switch (c) {
+                '0'...'9' => {},
+                else => return false,
+            }
+        }
+        return true;
+    }
+    return @import("astgen.zig").simple_types.has(name);
+}
+
 const MacroCtx = struct {
     source: []const u8,
     list: []const CToken,