Commit f36849fed2

Veikka Tuominen <git@vexu.eu>
2021-02-07 22:13:40
translate-c: convert function translation
1 parent 7514c0a
Changed files (2)
src
src/translate_c/ast.zig
@@ -14,6 +14,10 @@ pub const Node = extern union {
         true_literal,
         false_literal,
         empty_block,
+        return_void,
+        zero_literal,
+        void_type,
+        noreturn_type,
         /// pub usingnamespace @import("std").c.builtins;
         usingnamespace_builtins,
         // After this, the tag requires a payload.
@@ -99,6 +103,7 @@ pub const Node = extern union {
         bit_or,
         bit_xor,
 
+        log2_int_type,
         /// @import("std").math.Log2Int(operand)
         std_math_Log2Int,
         /// @intCast(lhs, rhs)
@@ -132,7 +137,13 @@ pub const Node = extern union {
         single_pointer,
         array_type,
 
-        pub const last_no_payload_tag = Tag.false_literal;
+
+        // pub const name = @compileError(msg);
+        fail_decl,
+        // var actual = mangled;
+        arg_redecl,
+
+        pub const last_no_payload_tag = Tag.usingnamespace_builtins;
         pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1;
 
         pub fn Type(tag: Tag) ?type {
@@ -144,6 +155,10 @@ pub const Node = extern union {
                 .false_litral,
                 .empty_block,
                 .usingnamespace_builtins,
+                .return_void,
+                .zero_literal,
+                .void_type,
+                .noreturn_type,
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
                 .array_access,
@@ -227,6 +242,7 @@ pub const Node = extern union {
                 .sizeof,
                 .alignof,
                 .type,
+                .fail_decl,
                 => Payload.Value,
                 .@"if" => Payload.If,
                 .@"while" => Payload.While,
@@ -244,6 +260,8 @@ pub const Node = extern union {
                 .c_pointer => Payload.Pointer,
                 .single_pointer => Payload.Pointer,
                 .array_type => Payload.Array,
+                .arg_redecl => Payload.ArgRedecl,
+                .log2_int_type => Payload.Log2IntType,
             };
         }
 
@@ -265,6 +283,24 @@ pub const Node = extern union {
             return std.meta.fieldInfo(t.Type(), .data).field_type;
         }
     };
+
+    pub fn tag(self: Node) Tag {
+        if (self.tag_if_small_enough < Tag.no_payload_count) {
+            return @intToEnum(Tag, @intCast(@TagType(Tag), self.tag_if_small_enough));
+        } else {
+            return self.ptr_otherwise.tag;
+        }
+    }
+
+    pub fn castTag(self: Node, comptime t: Tag) ?*t.Type() {
+        if (self.tag_if_small_enough < Tag.no_payload_count)
+            return null;
+
+        if (self.ptr_otherwise.tag == t)
+            return @fieldParentPtr(t.Type(), "base", self.ptr_otherwise);
+
+        return null;
+    }
 };
 
 pub const Payload = struct {
@@ -360,19 +396,22 @@ pub const Payload = struct {
     pub const Func = struct {
         base: Node = .{.func},
         data: struct {
-            @"pub": bool,
-            @"extern": bool,
-            @"export": bool,
+            is_pub: bool,
+            is_extern: bool,
+            is_export: bool,
+            is_var_args: bool,
             name: []const u8,
-            cc: std.builtin.CallingConvention,
+            link_section_string: ?[]const u8,
+            explicit_callconv: ?std.builtin.CallingConvention,
             params: []Param,
-            return_type: Type,
+            return_type: Node,
             body: ?Node,
+            alignment: c_uint,
 
             pub const Param = struct {
-                @"noalias": bool,
+                is_noalias: bool,
                 name: ?[]const u8,
-                type: Type,
+                type: Node,
             };
         },
     };
@@ -449,6 +488,19 @@ pub const Payload = struct {
             is_volatile: bool,
         },
     };
+
+    pub const ArgRedecl = struct {
+        base: Node,
+        data: struct {
+            actual: []const u8,
+            mangled: []const u8,
+        },
+    };
+
+    pub const Log2IntType = struct {
+        base: Node,
+        data: std.math.Log2Int(u64),
+    };
 };
 
 /// Converts the nodes into a Zig ast.
src/translate_c.zig
@@ -469,7 +469,6 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
             return visitFnDecl(c, def);
     }
 
-    const rp = makeRestorePoint(c);
     const fn_decl_loc = fn_decl.getLocation();
     const has_body = fn_decl.hasBody();
     const storage_class = fn_decl.getStorageClass();
@@ -513,9 +512,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
                 decl_ctx.has_body = false;
                 decl_ctx.storage_class = .Extern;
                 decl_ctx.is_export = false;
-                try emitWarning(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{});
+                try warn(c, fn_decl_loc, "TODO unable to translate variadic function, demoted to declaration", .{});
             }
-            break :blk transFnProto(rp, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
+            break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
                 error.UnsupportedType => {
                     return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
                 },
@@ -524,7 +523,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
         },
         .FunctionNoProto => blk: {
             const fn_no_proto_type = @ptrCast(*const clang.FunctionType, fn_type);
-            break :blk transFnNoProto(rp, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
+            break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
                 error.UnsupportedType => {
                     return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
                 },
@@ -535,13 +534,12 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
     };
 
     if (!decl_ctx.has_body) {
-        const semi_tok = try appendToken(c, .Semicolon, ";");
         return addTopLevelDecl(c, fn_name, &proto_node.base);
     }
 
     // actual function definition with body
     const body_stmt = fn_decl.getBody();
-    var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false);
+    var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
     block_scope.return_type = return_qt;
     defer block_scope.deinit();
 
@@ -559,34 +557,22 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
         const is_const = qual_type.isConstQualified();
 
         const mangled_param_name = try block_scope.makeMangledName(c, param_name);
+        param.name = mangled_param_name;
 
         if (!is_const) {
             const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
             const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
+            param.name = arg_name;
 
-            const mut_tok = try appendToken(c, .Keyword_var, "var");
-            const name_tok = try appendIdentifier(c, mangled_param_name);
-            const eq_token = try appendToken(c, .Equal, "=");
-            const init_node = try transCreateNodeIdentifier(c, arg_name);
-            const semicolon_token = try appendToken(c, .Semicolon, ";");
-            const node = try ast.Node.VarDecl.create(c.arena, .{
-                .mut_token = mut_tok,
-                .name_token = name_tok,
-                .semicolon_token = semicolon_token,
-            }, .{
-                .eq_token = eq_token,
-                .init_node = init_node,
-            });
-            try block_scope.statements.append(&node.base);
-            param.name_token = try appendIdentifier(c, arg_name);
-            _ = try appendToken(c, .Colon, ":");
+            const redecl_node = try Node.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
+            try block_scope.statements.append(redecl_node);
         }
 
         param_id += 1;
     }
 
     const casted_body = @ptrCast(*const clang.CompoundStmt, body_stmt);
-    transCompoundStmtInline(rp, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
+    transCompoundStmtInline(c, &block_scope.base, casted_body, &block_scope) catch |err| switch (err) {
         error.OutOfMemory => |e| return e,
         error.UnsupportedTranslation,
         error.UnsupportedType,
@@ -600,37 +586,31 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
         if (block_scope.statements.items.len > 0) {
             var last = block_scope.statements.items[block_scope.statements.items.len - 1];
             while (true) {
-                switch (last.tag) {
-                    .Block, .LabeledBlock => {
-                        const stmts = last.blockStatements();
-                        if (stmts.len == 0) break;
+                switch (last.tag()) {
+                    .block => {
+                        const block = last.castTag(.block).?;
+                        if (block.data.stmts.len == 0) break;
 
-                        last = stmts[stmts.len - 1];
+                        last = block.data.stmts[block.data.stmts.len - 1];
                     },
                     // no extra return needed
-                    .Return => break :blk,
+                    .@"return", .return_void => break :blk,
                     else => break,
                 }
             }
         }
 
-        const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{
-            .ltoken = try appendToken(rp.c, .Keyword_return, "return"),
-            .tag = .Return,
-        }, .{
-            .rhs = transZeroInitExpr(rp, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
-                error.OutOfMemory => |e| return e,
-                error.UnsupportedTranslation,
-                error.UnsupportedType,
-                => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
-            },
-        });
-        _ = try appendToken(rp.c, .Semicolon, ";");
-        try block_scope.statements.append(&return_expr.base);
+        const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
+            error.OutOfMemory => |e| return e,
+            error.UnsupportedTranslation,
+            error.UnsupportedType,
+            => return failDecl(c, fn_decl_loc, fn_name, "unable to create a return value for function", .{}),
+        };
+        const ret = try Node.@"return".create(c.arena, rhs);
+        try block_scope.statements.append(ret);
     }
 
-    const body_node = try block_scope.complete(rp.c);
-    proto_node.setBodyNode(body_node);
+    proto_node.body = try block_scope.complete(c);
     return addTopLevelDecl(c, fn_name, &proto_node.base);
 }
 
@@ -2440,16 +2420,16 @@ fn transInitListExpr(
 }
 
 fn transZeroInitExpr(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     source_loc: clang.SourceLocation,
     ty: *const clang.Type,
-) TransError!*ast.Node {
+) TransError!Node {
     switch (ty.getTypeClass()) {
         .Builtin => {
             const builtin_ty = @ptrCast(*const clang.BuiltinType, ty);
             switch (builtin_ty.getKind()) {
-                .Bool => return try transCreateNodeBoolLiteral(rp.c, false),
+                .Bool => return Node.false_literal.init(),
                 .Char_U,
                 .UChar,
                 .Char_S,
@@ -2470,16 +2450,16 @@ fn transZeroInitExpr(
                 .Float128,
                 .Float16,
                 .LongDouble,
-                => return transCreateNodeInt(rp.c, 0),
-                else => return revertAndWarn(rp, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
+                => return Node.zero_literal.init(),
+                else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
             }
         },
-        .Pointer => return transCreateNodeNullLiteral(rp.c),
+        .Pointer => return Node.null_literal.init(),
         .Typedef => {
             const typedef_ty = @ptrCast(*const clang.TypedefType, ty);
             const typedef_decl = typedef_ty.getDecl();
             return transZeroInitExpr(
-                rp,
+                c,
                 scope,
                 source_loc,
                 typedef_decl.getUnderlyingType().getTypePtr(),
@@ -2488,7 +2468,7 @@ fn transZeroInitExpr(
         else => {},
     }
 
-    return revertAndWarn(rp, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
+    return fail(c, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{});
 }
 
 fn transImplicitValueInitExpr(
@@ -3985,7 +3965,7 @@ fn qualTypeToLog2IntRef(c: *Context, qt: clang.QualType, source_loc: clang.Sourc
     if (int_bit_width != 0) {
         // we can perform the log2 now.
         const cast_bit_width = math.log2_int(u64, int_bit_width);
-        return Node.uint_type.create(c.arena, cast_bit_width);
+        return Node.log2_int_type.create(c.arena, cast_bit_width);
     }
 
     const zig_type = try transQualType(c, qt, source_loc);
@@ -4886,7 +4866,7 @@ const FnDeclContext = struct {
 };
 
 fn transCC(
-    rp: RestorePoint,
+    c: *Context,
     fn_ty: *const clang.FunctionType,
     source_loc: clang.SourceLocation,
 ) !CallingConvention {
@@ -4899,7 +4879,7 @@ fn transCC(
         .X86ThisCall => return CallingConvention.Thiscall,
         .AAPCS => return CallingConvention.AAPCS,
         .AAPCS_VFP => return CallingConvention.AAPCSVFP,
-        else => return revertAndWarn(
+        else => return fail(
             rp,
             error.UnsupportedType,
             source_loc,
@@ -4910,33 +4890,33 @@ fn transCC(
 }
 
 fn transFnProto(
-    rp: RestorePoint,
+    c: *Context,
     fn_decl: ?*const clang.FunctionDecl,
     fn_proto_ty: *const clang.FunctionProtoType,
     source_loc: clang.SourceLocation,
     fn_decl_context: ?FnDeclContext,
     is_pub: bool,
-) !*ast.Node.FnProto {
+) !Node.FnProto {
     const fn_ty = @ptrCast(*const clang.FunctionType, fn_proto_ty);
-    const cc = try transCC(rp, fn_ty, source_loc);
+    const cc = try transCC(c, fn_ty, source_loc);
     const is_var_args = fn_proto_ty.isVariadic();
-    return finishTransFnProto(rp, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
+    return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
 }
 
 fn transFnNoProto(
-    rp: RestorePoint,
+    c: *Context,
     fn_ty: *const clang.FunctionType,
     source_loc: clang.SourceLocation,
     fn_decl_context: ?FnDeclContext,
     is_pub: bool,
-) !*ast.Node.FnProto {
-    const cc = try transCC(rp, fn_ty, source_loc);
+) !Node.FnProto {
+    const cc = try transCC(c, fn_ty, source_loc);
     const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static) else true;
-    return finishTransFnProto(rp, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
+    return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
 }
 
 fn finishTransFnProto(
-    rp: RestorePoint,
+    c: *Context,
     fn_decl: ?*const clang.FunctionDecl,
     fn_proto_ty: ?*const clang.FunctionProtoType,
     fn_ty: *const clang.FunctionType,
@@ -4945,128 +4925,77 @@ fn finishTransFnProto(
     is_var_args: bool,
     cc: CallingConvention,
     is_pub: bool,
-) !*ast.Node.FnProto {
+) !*ast.Payload.Func {
     const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
     const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
 
     // TODO check for always_inline attribute
     // TODO check for align attribute
 
-    // pub extern fn name(...) T
-    const pub_tok = if (is_pub) try appendToken(rp.c, .Keyword_pub, "pub") else null;
-    const extern_export_inline_tok = if (is_export)
-        try appendToken(rp.c, .Keyword_export, "export")
-    else if (is_extern)
-        try appendToken(rp.c, .Keyword_extern, "extern")
-    else
-        null;
-    const fn_tok = try appendToken(rp.c, .Keyword_fn, "fn");
-    const name_tok = if (fn_decl_context) |ctx| try appendIdentifier(rp.c, ctx.fn_name) else null;
-    const lparen_tok = try appendToken(rp.c, .LParen, "(");
-
-    var fn_params = std.ArrayList(ast.Node.FnProto.ParamDecl).init(rp.c.gpa);
+    var fn_params = std.ArrayList(ast.Payload.Func.Param).init(c.gpa);
     defer fn_params.deinit();
     const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;
-    try fn_params.ensureCapacity(param_count + 1); // +1 for possible var args node
+    try fn_params.ensureCapacity(param_count);
 
     var i: usize = 0;
     while (i < param_count) : (i += 1) {
         const param_qt = fn_proto_ty.?.getParamType(@intCast(c_uint, i));
+        const is_noalias = param_qt.isRestrictQualified();
 
-        const noalias_tok = if (param_qt.isRestrictQualified()) try appendToken(rp.c, .Keyword_noalias, "noalias") else null;
-
-        const param_name_tok: ?ast.TokenIndex = blk: {
-            if (fn_decl) |decl| {
-                const param = decl.getParamDecl(@intCast(c_uint, i));
-                const param_name: []const u8 = try rp.c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
-                if (param_name.len < 1)
-                    break :blk null;
-
-                const result = try appendIdentifier(rp.c, param_name);
-                _ = try appendToken(rp.c, .Colon, ":");
-                break :blk result;
-            }
-            break :blk null;
-        };
+        const param_name: ?[]const u8 =
+            if (fn_decl) |decl|
+        blk: {
+            const param = decl.getParamDecl(@intCast(c_uint, i));
+            const param_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, param).getName_bytes_begin());
+            if (param_name.len < 1)
+                break :blk null;
 
-        const type_node = try transQualType(rp, param_qt, source_loc);
+            break :blk param_name;
+        } else null;
+        const type_node = try transQualType(c, param_qt, source_loc);
 
         fn_params.addOneAssumeCapacity().* = .{
-            .doc_comments = null,
-            .comptime_token = null,
-            .noalias_token = noalias_tok,
-            .name_token = param_name_tok,
-            .param_type = .{ .type_expr = type_node },
+            .is_noalias = is_noalias,
+            .name = param_name,
+            .type = type_node,
         };
-
-        if (i + 1 < param_count) {
-            _ = try appendToken(rp.c, .Comma, ",");
-        }
     }
 
-    const var_args_token: ?ast.TokenIndex = if (is_var_args) blk: {
-        if (param_count > 0) {
-            _ = try appendToken(rp.c, .Comma, ",");
-        }
-        break :blk try appendToken(rp.c, .Ellipsis3, "...");
-    } else null;
-
-    const rparen_tok = try appendToken(rp.c, .RParen, ")");
-
-    const linksection_expr = blk: {
+    const link_section_string: ?[]const u8 = blk: {
         if (fn_decl) |decl| {
             var str_len: usize = undefined;
             if (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: c_uint = blk: {
         if (fn_decl) |decl| {
-            const alignment = decl.getAlignedAttribute(rp.c.clang_context);
+            const alignment = 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 callconv_expr = if ((is_export or is_extern) and cc == .C) null else blk: {
-        _ = try appendToken(rp.c, .Keyword_callconv, "callconv");
-        _ = try appendToken(rp.c, .LParen, "(");
-        const expr = try transCreateNodeEnumLiteral(rp.c, @tagName(cc));
-        _ = try appendToken(rp.c, .RParen, ")");
-        break :blk expr;
-    };
+    const explicit_callconv = if ((is_export or is_extern) and cc == .C) null else cc;
 
     const return_type_node = blk: {
         if (fn_ty.getNoReturnAttr()) {
-            break :blk try transCreateNodeIdentifier(rp.c, "noreturn");
+            break :blk Node.noreturn_type.init();
         } else {
             const return_qt = fn_ty.getReturnType();
             if (isCVoid(return_qt)) {
                 // convert primitive c_void to actual void (only for return type)
-                break :blk try transCreateNodeIdentifier(rp.c, "void");
+                break :blk Node.void_type.init();
             } else {
-                break :blk transQualType(rp, return_qt, source_loc) catch |err| switch (err) {
+                break :blk transQualType(c, return_qt, source_loc) catch |err| switch (err) {
                     error.UnsupportedType => {
-                        try emitWarning(rp.c, source_loc, "unsupported function proto return type", .{});
+                        try warn(c, source_loc, "unsupported function proto return type", .{});
                         return err;
                     },
                     error.OutOfMemory => |e| return e,
@@ -5075,32 +5004,23 @@ fn finishTransFnProto(
         }
     };
 
-    // We need to reserve an undefined (but non-null) body node to set later.
-    var body_node: ?*ast.Node = null;
-    if (fn_decl_context) |ctx| {
-        if (ctx.has_body) {
-            // TODO: we should be able to use undefined here but
-            // it causes a bug. This is undefined without zig language
-            // being aware of it.
-            body_node = @intToPtr(*ast.Node, 0x08);
-        }
-    }
-
-    const fn_proto = try ast.Node.FnProto.create(rp.c.arena, .{
-        .params_len = fn_params.items.len,
-        .return_type = .{ .Explicit = return_type_node },
-        .fn_token = fn_tok,
-    }, .{
-        .visib_token = pub_tok,
-        .name_token = name_tok,
-        .extern_export_inline_token = extern_export_inline_tok,
-        .align_expr = align_expr,
-        .section_expr = linksection_expr,
-        .callconv_expr = callconv_expr,
-        .body_node = body_node,
-        .var_args_token = var_args_token,
-    });
-    mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items);
+    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;
 }
 
@@ -5122,124 +5042,12 @@ fn fail(
 }
 
 pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) !void {
+    // location
     // pub const name = @compileError(msg);
-    const pub_tok = try appendToken(c, .Keyword_pub, "pub");
-    const const_tok = try appendToken(c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(c, name);
-    const eq_tok = try appendToken(c, .Equal, "=");
-    const builtin_tok = try appendToken(c, .Builtin, "@compileError");
-    const lparen_tok = try appendToken(c, .LParen, "(");
-    const msg_tok = try appendTokenFmt(c, .StringLiteral, "\"" ++ format ++ "\"", args);
-    const rparen_tok = try appendToken(c, .RParen, ")");
-    const semi_tok = try appendToken(c, .Semicolon, ";");
-    _ = try appendTokenFmt(c, .LineComment, "// {s}", .{c.locStr(loc)});
-
-    const msg_node = try c.arena.create(ast.Node.OneToken);
-    msg_node.* = .{
-        .base = .{ .tag = .StringLiteral },
-        .token = msg_tok,
-    };
-
-    const call_node = try ast.Node.BuiltinCall.alloc(c.arena, 1);
-    call_node.* = .{
-        .builtin_token = builtin_tok,
-        .params_len = 1,
-        .rparen_token = rparen_tok,
-    };
-    call_node.params()[0] = &msg_node.base;
-
-    const var_decl_node = try ast.Node.VarDecl.create(c.arena, .{
-        .name_token = name_tok,
-        .mut_token = const_tok,
-        .semicolon_token = semi_tok,
-    }, .{
-        .visib_token = pub_tok,
-        .eq_token = eq_tok,
-        .init_node = &call_node.base,
-    });
-    try addTopLevelDecl(c, name, &var_decl_node.base);
-}
-
-fn appendToken(c: *Context, token_id: Token.Id, bytes: []const u8) !ast.TokenIndex {
-    std.debug.assert(token_id != .Identifier); // use appendIdentifier
-    return appendTokenFmt(c, token_id, "{s}", .{bytes});
-}
-
-fn appendTokenFmt(c: *Context, token_id: Token.Id, comptime format: []const u8, args: anytype) !ast.TokenIndex {
-    assert(token_id != .Invalid);
-
-    try c.token_ids.ensureCapacity(c.gpa, c.token_ids.items.len + 1);
-    try c.token_locs.ensureCapacity(c.gpa, c.token_locs.items.len + 1);
-
-    const start_index = c.source_buffer.items.len;
-    try c.source_buffer.writer().print(format ++ " ", args);
-
-    c.token_ids.appendAssumeCapacity(token_id);
-    c.token_locs.appendAssumeCapacity(.{
-        .start = start_index,
-        .end = c.source_buffer.items.len - 1, // back up before the space
-    });
-
-    return c.token_ids.items.len - 1;
-}
-
-// TODO hook up with codegen
-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;
-    }
-    // void is invalid in c so it doesn't need to be checked.
-    return mem.eql(u8, name, "comptime_float") or
-        mem.eql(u8, name, "comptime_int") or
-        mem.eql(u8, name, "bool") or
-        mem.eql(u8, name, "isize") or
-        mem.eql(u8, name, "usize") or
-        mem.eql(u8, name, "f16") or
-        mem.eql(u8, name, "f32") or
-        mem.eql(u8, name, "f64") or
-        mem.eql(u8, name, "f128") or
-        mem.eql(u8, name, "c_longdouble") or
-        mem.eql(u8, name, "noreturn") or
-        mem.eql(u8, name, "type") or
-        mem.eql(u8, name, "anyerror") or
-        mem.eql(u8, name, "c_short") or
-        mem.eql(u8, name, "c_ushort") or
-        mem.eql(u8, name, "c_int") or
-        mem.eql(u8, name, "c_uint") or
-        mem.eql(u8, name, "c_long") or
-        mem.eql(u8, name, "c_ulong") or
-        mem.eql(u8, name, "c_longlong") or
-        mem.eql(u8, name, "c_ulonglong");
-}
-
-fn appendIdentifier(c: *Context, name: []const u8) !ast.TokenIndex {
-    return appendTokenFmt(c, .Identifier, "{}", .{std.zig.fmtId(name)});
-}
-
-fn transCreateNodeIdentifier(c: *Context, name: []const u8) !*ast.Node {
-    const token_index = try appendIdentifier(c, name);
-    const identifier = try c.arena.create(ast.Node.OneToken);
-    identifier.* = .{
-        .base = .{ .tag = .Identifier },
-        .token = token_index,
-    };
-    return &identifier.base;
-}
-
-fn transCreateNodeIdentifierUnchecked(c: *Context, name: []const u8) !*ast.Node {
-    const token_index = try appendTokenFmt(c, .Identifier, "{s}", .{name});
-    const identifier = try c.arena.create(ast.Node.OneToken);
-    identifier.* = .{
-        .base = .{ .tag = .Identifier },
-        .token = token_index,
-    };
-    return &identifier.base;
+    const location_comment = std.fmt.allocPrint(c.arena, "// {s}", .{c.locStr(loc)});
+    try c.global_scope.nodes.append(try Node.warning.create(c.arena, location_comment));
+    const fail_msg = std.fmt.allocPrint(c.arena, format, args);
+    try c.global_scope.nodes.append(try Node.fail_decl.create(c.arena, fail_msg));
 }
 
 pub fn freeErrors(errors: []ClangErrMsg) void {