Commit 450b718b9e

Veikka Tuominen <git@vexu.eu>
2021-02-10 22:38:45
translate-c: convert field/array access, call, pre/postcrement
1 parent cadd448
Changed files (2)
src
src/translate_c/ast.zig
@@ -17,6 +17,7 @@ pub const Node = extern union {
         empty_block,
         return_void,
         zero_literal,
+        one_literal,
         void_type,
         noreturn_type,
         /// pub usingnamespace @import("std").c.builtins;
@@ -44,7 +45,6 @@ pub const Node = extern union {
         break_val,
         @"return",
         field_access,
-        field_access_arrow,
         array_access,
         call,
         std_mem_zeroes,
@@ -153,8 +153,10 @@ pub const Node = extern union {
         bit_not,
         not,
         address_of,
-        // operand.?.*
+        /// operand.?.*
         unwrap_deref,
+        /// .*
+        deref,
 
         block,
         @"break",
@@ -202,11 +204,11 @@ pub const Node = extern union {
                 .usingnamespace_builtins,
                 .return_void,
                 .zero_literal,
+                .one_literal,
                 .void_type,
                 .noreturn_type,
                 => @compileError("Type Tag " ++ @tagName(t) ++ " has no payload"),
 
-                .array_access,
                 .std_mem_zeroes,
                 .@"return",
                 .discard,
@@ -218,6 +220,7 @@ pub const Node = extern union {
                 .optional_type,
                 .address_of,
                 .unwrap_deref,
+                .deref,
                 .ptr_to_int,
                 .enum_to_int,
                 .empty_array,
@@ -296,8 +299,6 @@ pub const Node = extern union {
                 .string,
                 .char,
                 .identifier,
-                .field_access,
-                .field_access_arrow,
                 .warning,
                 .failed_decl,
                 .sizeof,
@@ -323,9 +324,10 @@ pub const Node = extern union {
                 .array_type => Payload.Array,
                 .arg_redecl => Payload.ArgRedecl,
                 .log2_int_type => Payload.Log2IntType,
-                .typedef, .pub_typedef, .pub_var_simple => Payload.SimpleVarDecl,
+                .typedef, .pub_typedef, .var_simple, .pub_var_simple => Payload.SimpleVarDecl,
                 .enum_redecl => Payload.EnumRedecl,
                 .array_filler => Payload.ArrayFiller,
+                .field_access => Payload.FieldAccess,
             };
         }
 
@@ -588,6 +590,14 @@ pub const Payload = struct {
             count: usize,
         },
     };
+
+    pub const FieldAccess = struct {
+        base: Node,
+        data: struct {
+            container: Node,
+            name: []const u8,
+        },
+    };
 };
 
 /// Converts the nodes into a Zig ast.
src/translate_c.zig
@@ -2438,11 +2438,11 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used:
     return block_scope.complete(c);
 }
 
-fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!*ast.Node {
-    var container_node = try transExpr(rp, scope, stmt.getBase(), .used, .r_value);
+fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {
+    var container_node = try transExpr(c, scope, stmt.getBase(), .used, .r_value);
 
     if (stmt.isArrow()) {
-        container_node = try transCreateNodePtrDeref(rp.c, container_node);
+        container_node = try Node.deref.create(c.arena, container_node);
     }
 
     const member_decl = stmt.getMemberDecl();
@@ -2453,19 +2453,19 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.MemberExp
         if (decl_kind == .Field) {
             const field_decl = @ptrCast(*const clang.FieldDecl, member_decl);
             if (field_decl.isAnonymousStructOrUnion()) {
-                const name = rp.c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
-                break :blk try mem.dupe(rp.c.arena, u8, name);
+                const name = c.decl_table.get(@ptrToInt(field_decl.getCanonicalDecl())).?;
+                break :blk try mem.dupe(c.arena, u8, name);
             }
         }
         const decl = @ptrCast(*const clang.NamedDecl, member_decl);
-        break :blk try rp.c.str(decl.getName_bytes_begin());
+        break :blk try c.str(decl.getName_bytes_begin());
     };
 
-    const node = try transCreateNodeFieldAccess(rp.c, container_node, name);
-    return maybeSuppressResult(rp, scope, result_used, node);
+    const node = try Node.field_access.create(c.arena, .{ .container = container_node, .name = name});
+    return maybeSuppressResult(c, scope, result_used, node);
 }
 
-fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!*ast.Node {
+fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
     var base_stmt = stmt.getBase();
 
     // Unwrap the base statement if it's an array decayed to a bare pointer type
@@ -2478,30 +2478,23 @@ fn transArrayAccess(rp: RestorePoint, scope: *Scope, stmt: *const clang.ArraySub
         }
     }
 
-    const container_node = try transExpr(rp, scope, base_stmt, .used, .r_value);
-    const node = try transCreateNodeArrayAccess(rp.c, container_node);
+    const container_node = try transExpr(c, scope, base_stmt, .used, .r_value);
 
     // cast if the index is long long or signed
     const subscr_expr = stmt.getIdx();
-    const qt = getExprQualType(rp.c, subscr_expr);
+    const qt = getExprQualType(c, subscr_expr);
     const is_longlong = cIsLongLongInteger(qt);
     const is_signed = cIsSignedInteger(qt);
 
-    if (is_longlong or is_signed) {
-        const cast_node = try rp.c.createBuiltinCall("@intCast", 2);
+    
+    const node = try Node.array_access.create(c.arena, .{ .lhs = container_node, .rhs = if (is_longlong or is_signed) blk: {
+        const cast_node = try c.createBuiltinCall("@intCast", 2);
         // check if long long first so that signed long long doesn't just become unsigned long long
-        var typeid_node = if (is_longlong) try transCreateNodeIdentifier(rp.c, "usize") else try transQualTypeIntWidthOf(rp.c, qt, false);
-        cast_node.params()[0] = typeid_node;
-        _ = try appendToken(rp.c, .Comma, ",");
-        cast_node.params()[1] = try transExpr(rp, scope, subscr_expr, .used, .r_value);
-        cast_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-        node.rtoken = try appendToken(rp.c, .RBrace, "]");
-        node.index_expr = &cast_node.base;
-    } else {
-        node.index_expr = try transExpr(rp, scope, subscr_expr, .used, .r_value);
-        node.rtoken = try appendToken(rp.c, .RBrace, "]");
-    }
-    return maybeSuppressResult(rp, scope, result_used, &node.base);
+        var typeid_node = if (is_longlong) try transCreateNodeIdentifier(c, "usize") else try transQualTypeIntWidthOf(c, qt, false);
+        break :blk try Node.int_cast.create(c.arena, .{ .lhs = typeid_node, .rhs = try transExpr(c, scope, subscr_expr, .used, .r_value)});
+    } else
+        try transExpr(c, scope, subscr_expr, .used, .r_value)});
+    return maybeSuppressResult(c, scope, result_used, node);
 }
 
 /// Check if an expression is ultimately a reference to a function declaration
@@ -2536,9 +2529,9 @@ fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
     }
 }
 
-fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!*ast.Node {
+fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node {
     const callee = stmt.getCallee();
-    var raw_fn_expr = try transExpr(rp, scope, callee, .used, .r_value);
+    var raw_fn_expr = try transExpr(c, scope, callee, .used, .r_value);
 
     var is_ptr = false;
     const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);
@@ -2549,16 +2542,12 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
         raw_fn_expr;
 
     const num_args = stmt.getNumArgs();
-    const node = try rp.c.createCall(fn_expr, num_args);
-    const call_params = node.params();
+    const call_params = try c.arena.alloc(Node, num_args);
 
     const args = stmt.getArgs();
     var i: usize = 0;
     while (i < num_args) : (i += 1) {
-        if (i != 0) {
-            _ = try appendToken(rp.c, .Comma, ",");
-        }
-        var call_param = try transExpr(rp, scope, args[i], .used, .r_value);
+        var call_param = try transExpr(c, scope, args[i], .used, .r_value);
 
         // In C the result type of a boolean expression is int. If this result is passed as
         // an argument to a function whose parameter is also int, there is no cast. Therefore
@@ -2570,10 +2559,7 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
                     if (i < param_count) {
                         const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
                         if (isBoolRes(call_param) and cIsNativeInt(param_qt)) {
-                            const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
-                            builtin_node.params()[0] = call_param;
-                            builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
-                            call_param = &builtin_node.base;
+                            call_param = try Node.bool_to_int.create(c.arena, call_param);
                         }
                     }
                 },
@@ -2582,18 +2568,16 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
         }
         call_params[i] = call_param;
     }
-    node.rtoken = try appendToken(rp.c, .RParen, ")");
-
+    const node = try Node.call.create(c.arena, .{ .lhs = fn_expr, .args = call_params });
     if (fn_ty) |ty| {
         const canon = ty.getReturnType().getCanonicalType();
         const ret_ty = canon.getTypePtr();
         if (ret_ty.isVoidType()) {
-            _ = try appendToken(rp.c, .Semicolon, ";");
-            return &node.base;
+            return node;
         }
     }
 
-    return maybeSuppressResult(rp, scope, result_used, &node.base);
+    return maybeSuppressResult(c, scope, result_used, node);
 }
 
 const ClangFunctionType = union(enum) {
@@ -2667,21 +2651,21 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat
     const op_expr = stmt.getSubExpr();
     switch (stmt.getOpcode()) {
         .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
-            return transCreatePostCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
+            return transCreatePostCrement(c, scope, stmt, .assign_add_wrap, used)
         else
-            return transCreatePostCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
+            return transCreatePostCrement(c, scope, stmt, .assign_add, used),
         .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
-            return transCreatePostCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
+            return transCreatePostCrement(c, scope, stmt, .assign_sub_wrap, used)
         else
-            return transCreatePostCrement(c, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
+            return transCreatePostCrement(c, scope, stmt, .assign_sub, used),
         .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
-            return transCreatePreCrement(c, scope, stmt, .AssignAddWrap, .PlusPercentEqual, "+%=", used)
+            return transCreatePreCrement(c, scope, stmt, .assign_add_wrap, used)
         else
-            return transCreatePreCrement(c, scope, stmt, .AssignAdd, .PlusEqual, "+=", used),
+            return transCreatePreCrement(c, scope, stmt, .assign_add, used),
         .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
-            return transCreatePreCrement(c, scope, stmt, .AssignSubWrap, .MinusPercentEqual, "-%=", used)
+            return transCreatePreCrement(c, scope, stmt, .assign_sub_wrap, used)
         else
-            return transCreatePreCrement(c, scope, stmt, .AssignSub, .MinusEqual, "-=", used),
+            return transCreatePreCrement(c, scope, stmt, .assign_sub, used),
         .AddrOf => {
             if (cIsFunctionDeclRef(op_expr)) {
                 return transExpr(rp, scope, op_expr, used, .r_value);
@@ -2704,7 +2688,7 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat
                 // use -% x for unsigned integers
                 return Node.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value));
             } else
-                return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{});
+                return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{});
         },
         .Not => {
             return Node.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used, .r_value));
@@ -2715,31 +2699,32 @@ fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperat
         .Extension => {
             return transExpr(c, scope, stmt.getSubExpr(), used, .l_value);
         },
-        else => return revertAndWarn(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}),
+        else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}),
     }
 }
 
 fn transCreatePreCrement(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.UnaryOperator,
-    op: ast.Node.Tag,
-    op_tok_id: std.zig.Token.Id,
-    bytes: []const u8,
+    op: Node.Tag,
     used: ResultUsed,
-) TransError!*ast.Node {
+) TransError!Node {
     const op_expr = stmt.getSubExpr();
 
     if (used == .unused) {
         // common case
         // c: ++expr
         // zig: expr += 1
-        const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
-        const token = try appendToken(rp.c, op_tok_id, bytes);
-        const one = try transCreateNodeInt(rp.c, 1);
-        if (scope.id != .Condition)
-            _ = try appendToken(rp.c, .Semicolon, ";");
-        return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+        const payload = try c.arena.create(ast.Payload.BinOp);
+        payload.* = .{
+            .base = .{ .tag = op },
+            .data = .{
+                .lhs = try transExpr(c, scope, op_expr, .used, .r_value),
+                .rhs = Node.one_literal.init(),
+            }
+        };
+        return Node.initPayload(&payload.base);
     }
     // worst case
     // c: ++expr
@@ -2748,71 +2733,55 @@ fn transCreatePreCrement(
     // zig:     _ref.* += 1;
     // zig:     break :blk _ref.*
     // zig: })
-    var block_scope = try Scope.Block.init(rp.c, scope, true);
+    var block_scope = try Scope.Block.init(c, scope, true);
     defer block_scope.deinit();
-    const ref = try block_scope.makeMangledName(rp.c, "ref");
+    const ref = try block_scope.makeMangledName(c, "ref");
 
-    const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(rp.c, ref);
-    const eq_token = try appendToken(rp.c, .Equal, "=");
-    const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
-    rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
-    const init_node = &rhs_node.base;
-    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,
-    }, .{
-        .eq_token = eq_token,
-        .init_node = init_node,
-    });
-    try block_scope.statements.append(&node.base);
+    const expr = try transExpr(c, scope, op_expr, .used, .r_value);
+    const addr_of = try Node.address_of.create(c.arena, expr);
+    const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of});
+    try block_scope.statements.append(ref_decl);
 
-    const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
-    const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    const token = try appendToken(rp.c, op_tok_id, bytes);
-    const one = try transCreateNodeInt(rp.c, 1);
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
-    try block_scope.statements.append(assign);
-
-    const break_node = try transCreateNodeBreak(rp.c, block_scope.label, ref_node);
-    try block_scope.statements.append(&break_node.base);
-    const block_node = try block_scope.complete(rp.c);
-    // semicolon must immediately follow rbrace because it is the last token in a block
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
-    grouped_expr.* = .{
-        .lparen = try appendToken(rp.c, .LParen, "("),
-        .expr = block_node,
-        .rparen = try appendToken(rp.c, .RParen, ")"),
+    const lhs_node = try Node.identifier.create(c.arena, ref);
+    const ref_node = try Node.deref.create(c.arena, lhs_node);
+    const payload = try c.arena.create(ast.Payload.BinOp);
+    payload.* = .{
+        .base = .{ .tag = op },
+        .data = .{
+            .lhs = ref_node,
+            .rhs = Node.one_literal.init(),
+        }
     };
-    return &grouped_expr.base;
+    try block_scope.statements.append(Node.initPayload(&payload.base));
+
+    return Node.break_val.create(c.arena, .{
+        .label = block_scope.label,
+        .val = ref_node,
+    });
 }
 
 fn transCreatePostCrement(
-    rp: RestorePoint,
+    c: *Context,
     scope: *Scope,
     stmt: *const clang.UnaryOperator,
-    op: ast.Node.Tag,
-    op_tok_id: std.zig.Token.Id,
-    bytes: []const u8,
+    op: Node.Tag,
     used: ResultUsed,
-) TransError!*ast.Node {
+) TransError!Node {
     const op_expr = stmt.getSubExpr();
 
     if (used == .unused) {
         // common case
         // c: ++expr
         // zig: expr += 1
-        const expr = try transExpr(rp, scope, op_expr, .used, .r_value);
-        const token = try appendToken(rp.c, op_tok_id, bytes);
-        const one = try transCreateNodeInt(rp.c, 1);
-        if (scope.id != .Condition)
-            _ = try appendToken(rp.c, .Semicolon, ";");
-        return transCreateNodeInfixOp(rp, scope, expr, op, token, one, .used, false);
+        const payload = try c.arena.create(ast.Payload.BinOp);
+        payload.* = .{
+            .base = .{ .tag = op },
+            .data = .{
+                .lhs = try transExpr(c, scope, op_expr, .used, .r_value),
+                .rhs = Node.one_literal.init(),
+            }
+        };
+        return Node.initPayload(&payload.base);
     }
     // worst case
     // c: expr++
@@ -2822,68 +2791,36 @@ fn transCreatePostCrement(
     // zig:     _ref.* += 1;
     // zig:     break :blk _tmp
     // zig: })
-    var block_scope = try Scope.Block.init(rp.c, scope, true);
+    var block_scope = try Scope.Block.init(c, scope, true);
     defer block_scope.deinit();
-    const ref = try block_scope.makeMangledName(rp.c, "ref");
+    const ref = try block_scope.makeMangledName(c, "ref");
 
-    const mut_tok = try appendToken(rp.c, .Keyword_const, "const");
-    const name_tok = try appendIdentifier(rp.c, ref);
-    const eq_token = try appendToken(rp.c, .Equal, "=");
-    const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&");
-    rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value);
-    const init_node = &rhs_node.base;
-    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,
-    }, .{
-        .eq_token = eq_token,
-        .init_node = init_node,
-    });
-    try block_scope.statements.append(&node.base);
+    const expr = try transExpr(c, scope, op_expr, .used, .r_value);
+    const addr_of = try Node.address_of.create(c.arena, expr);
+    const ref_decl = try Node.var_simple.create(c.arena, .{ .name = ref, .init = addr_of});
+    try block_scope.statements.append(ref_decl);
 
-    const lhs_node = try transCreateNodeIdentifier(rp.c, ref);
-    const ref_node = try transCreateNodePtrDeref(rp.c, lhs_node);
-    _ = try appendToken(rp.c, .Semicolon, ";");
+    const lhs_node = try Node.identifier.create(c.arena, ref);
+    const ref_node = try Node.deref.create(c.arena, lhs_node);
 
-    const tmp = try block_scope.makeMangledName(rp.c, "tmp");
-    const tmp_mut_tok = try appendToken(rp.c, .Keyword_const, "const");
-    const tmp_name_tok = try appendIdentifier(rp.c, tmp);
-    const tmp_eq_token = try appendToken(rp.c, .Equal, "=");
-    const tmp_init_node = ref_node;
-    const tmp_semicolon_token = try appendToken(rp.c, .Semicolon, ";");
-    const tmp_node = try ast.Node.VarDecl.create(rp.c.arena, .{
-        .name_token = tmp_name_tok,
-        .mut_token = tmp_mut_tok,
-        .semicolon_token = semicolon_token,
-    }, .{
-        .eq_token = tmp_eq_token,
-        .init_node = tmp_init_node,
-    });
-    try block_scope.statements.append(&tmp_node.base);
+    const tmp = try block_scope.makeMangledName(c, "tmp");
+    const tmp_decl = try Node.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node});
+    try block_scope.statements.append(tmp_decl);
 
-    const token = try appendToken(rp.c, op_tok_id, bytes);
-    const one = try transCreateNodeInt(rp.c, 1);
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    const assign = try transCreateNodeInfixOp(rp, scope, ref_node, op, token, one, .used, false);
-    try block_scope.statements.append(assign);
-
-    const break_node = blk: {
-        var tmp_ctrl_flow = try CtrlFlow.initToken(rp.c, .Break, block_scope.label);
-        const rhs = try transCreateNodeIdentifier(rp.c, tmp);
-        break :blk try tmp_ctrl_flow.finish(rhs);
-    };
-    try block_scope.statements.append(&break_node.base);
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    const block_node = try block_scope.complete(rp.c);
-    const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression);
-    grouped_expr.* = .{
-        .lparen = try appendToken(rp.c, .LParen, "("),
-        .expr = block_node,
-        .rparen = try appendToken(rp.c, .RParen, ")"),
+    const payload = try c.arena.create(ast.Payload.BinOp);
+    payload.* = .{
+        .base = .{ .tag = op },
+        .data = .{
+            .lhs = ref_node,
+            .rhs = Node.one_literal.init(),
+        }
     };
-    return &grouped_expr.base;
+    try block_scope.statements.append(Node.initPayload(&payload.base));
+
+    return Node.break_val.create(c.arena, .{
+        .label = block_scope.label,
+        .val = try Node.identifier.create(c.arena, tmp),
+    });
 }
 
 fn transCompoundAssignOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!*ast.Node {
@@ -3139,31 +3076,24 @@ fn transCPtrCast(
     }
 }
 
-fn transBreak(rp: RestorePoint, scope: *Scope) TransError!*ast.Node {
+fn transBreak(c: *Context, scope: *Scope) TransError!Node {
     const break_scope = scope.getBreakableScope();
     const label_text: ?[]const u8 = if (break_scope.id == .Switch) blk: {
         const swtch = @fieldParentPtr(Scope.Switch, "base", break_scope);
-        const block_scope = try scope.findBlockScope(rp.c);
-        swtch.switch_label = try block_scope.makeMangledName(rp.c, "switch");
+        const block_scope = try scope.findBlockScope(c);
+        swtch.switch_label = try block_scope.makeMangledName(c, "switch");
         break :blk swtch.switch_label;
     } else
         null;
 
-    var cf = try CtrlFlow.init(rp.c, .Break, label_text);
-    const br = try cf.finish(null);
-    _ = try appendToken(rp.c, .Semicolon, ";");
-    return &br.base;
+    return Node.@"break".create(c.arena, label_text);
 }
 
-fn transFloatingLiteral(rp: RestorePoint, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!*ast.Node {
+fn transFloatingLiteral(c: *Context, scope: *Scope, stmt: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
     // TODO use something more accurate
     const dbl = stmt.getValueAsApproximateDouble();
-    const node = try rp.c.arena.create(ast.Node.OneToken);
-    node.* = .{
-        .base = .{ .tag = .FloatLiteral },
-        .token = try appendTokenFmt(rp.c, .FloatLiteral, "{d}", .{dbl}),
-    };
-    return maybeSuppressResult(rp, scope, used, &node.base);
+    const node = try Node.float_literal.create(c.arena, try std.fmt.allocPrint(c.arena, "{d}", .{dbl}));
+    return maybeSuppressResult(c, scope, used, &node.base);
 }
 
 fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!*ast.Node {
@@ -3943,169 +3873,7 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a
     return &fn_proto.base;
 }
 
-fn transCreateNodeUnwrapNull(c: *Context, wrapped: *ast.Node) !*ast.Node {
-    _ = try appendToken(c, .Period, ".");
-    const qm = try appendToken(c, .QuestionMark, "?");
-    const node = try c.arena.create(ast.Node.SimpleSuffixOp);
-    node.* = .{
-        .base = .{ .tag = .UnwrapOptional },
-        .lhs = wrapped,
-        .rtoken = qm,
-    };
-    return &node.base;
-}
-
-fn transCreateNodeEnumLiteral(c: *Context, name: []const u8) !*ast.Node {
-    const node = try c.arena.create(ast.Node.EnumLiteral);
-    node.* = .{
-        .dot = try appendToken(c, .Period, "."),
-        .name = try appendIdentifier(c, name),
-    };
-    return &node.base;
-}
-
-fn transCreateNodeStringLiteral(c: *Context, str: []const u8) !*ast.Node {
-    const node = try c.arena.create(ast.Node.OneToken);
-    node.* = .{
-        .base = .{ .tag = .StringLiteral },
-        .token = try appendToken(c, .StringLiteral, str),
-    };
-    return &node.base;
-}
-
-fn transCreateNodeIf(c: *Context) !*ast.Node.If {
-    const if_tok = try appendToken(c, .Keyword_if, "if");
-    _ = try appendToken(c, .LParen, "(");
-    const node = try c.arena.create(ast.Node.If);
-    node.* = .{
-        .if_token = if_tok,
-        .condition = undefined,
-        .payload = null,
-        .body = undefined,
-        .@"else" = null,
-    };
-    return node;
-}
-
-fn transCreateNodeElse(c: *Context) !*ast.Node.Else {
-    const node = try c.arena.create(ast.Node.Else);
-    node.* = .{
-        .else_token = try appendToken(c, .Keyword_else, "else"),
-        .payload = null,
-        .body = undefined,
-    };
-    return node;
-}
-
-fn transCreateNodeBreak(
-    c: *Context,
-    label: ?ast.TokenIndex,
-    rhs: ?*ast.Node,
-) !*ast.Node.ControlFlowExpression {
-    var ctrl_flow = try CtrlFlow.init(c, .Break, if (label) |l| tokenSlice(c, l) else null);
-    return ctrl_flow.finish(rhs);
-}
-
-const CtrlFlow = struct {
-    c: *Context,
-    ltoken: ast.TokenIndex,
-    label_token: ?ast.TokenIndex,
-    tag: ast.Node.Tag,
-
-    /// Does everything except the RHS.
-    fn init(c: *Context, tag: ast.Node.Tag, label: ?[]const u8) !CtrlFlow {
-        const kw: Token.Id = switch (tag) {
-            .Break => .Keyword_break,
-            .Continue => .Keyword_continue,
-            .Return => .Keyword_return,
-            else => unreachable,
-        };
-        const kw_text = switch (tag) {
-            .Break => "break",
-            .Continue => "continue",
-            .Return => "return",
-            else => unreachable,
-        };
-        const ltoken = try appendToken(c, kw, kw_text);
-        const label_token = if (label) |l| blk: {
-            _ = try appendToken(c, .Colon, ":");
-            break :blk try appendIdentifier(c, l);
-        } else null;
-        return CtrlFlow{
-            .c = c,
-            .ltoken = ltoken,
-            .label_token = label_token,
-            .tag = tag,
-        };
-    }
-
-    fn initToken(c: *Context, tag: ast.Node.Tag, label: ?ast.TokenIndex) !CtrlFlow {
-        const other_token = label orelse return init(c, tag, null);
-        const loc = c.token_locs.items[other_token];
-        const label_name = c.source_buffer.items[loc.start..loc.end];
-        return init(c, tag, label_name);
-    }
-
-    fn finish(self: *CtrlFlow, rhs: ?*ast.Node) !*ast.Node.ControlFlowExpression {
-        return ast.Node.ControlFlowExpression.create(self.c.arena, .{
-            .ltoken = self.ltoken,
-            .tag = self.tag,
-        }, .{
-            .label = self.label_token,
-            .rhs = rhs,
-        });
-    }
-};
-
-fn transCreateNodeWhile(c: *Context) !*ast.Node.While {
-    const while_tok = try appendToken(c, .Keyword_while, "while");
-    _ = try appendToken(c, .LParen, "(");
-
-    const node = try c.arena.create(ast.Node.While);
-    node.* = .{
-        .label = null,
-        .inline_token = null,
-        .while_token = while_tok,
-        .condition = undefined,
-        .payload = null,
-        .continue_expr = null,
-        .body = undefined,
-        .@"else" = null,
-    };
-    return node;
-}
-
-fn transCreateNodeContinue(c: *Context) !*ast.Node {
-    const ltoken = try appendToken(c, .Keyword_continue, "continue");
-    const node = try ast.Node.ControlFlowExpression.create(c.arena, .{
-        .ltoken = ltoken,
-        .tag = .Continue,
-    }, .{});
-    _ = try appendToken(c, .Semicolon, ";");
-    return &node.base;
-}
-
-fn transCreateNodeSwitchCase(c: *Context, lhs: *ast.Node) !*ast.Node.SwitchCase {
-    const arrow_tok = try appendToken(c, .EqualAngleBracketRight, "=>");
 
-    const node = try ast.Node.SwitchCase.alloc(c.arena, 1);
-    node.* = .{
-        .items_len = 1,
-        .arrow_token = arrow_tok,
-        .payload = null,
-        .expr = undefined,
-    };
-    node.items()[0] = lhs;
-    return node;
-}
-
-fn transCreateNodeSwitchElse(c: *Context) !*ast.Node {
-    const node = try c.arena.create(ast.Node.SwitchElse);
-    node.* = .{
-        .token = try appendToken(c, .Keyword_else, "else"),
-    };
-    return &node.base;
-}
 
 fn transCreateNodeShiftOp(
     c: *Context,
@@ -4137,27 +3905,6 @@ fn transCreateNodeShiftOp(
     return Node.initPayload(&payload.base);
 }
 
-fn transCreateNodePtrDeref(c: *Context, lhs: *ast.Node) !*ast.Node {
-    const node = try c.arena.create(ast.Node.SimpleSuffixOp);
-    node.* = .{
-        .base = .{ .tag = .Deref },
-        .lhs = lhs,
-        .rtoken = try appendToken(c, .PeriodAsterisk, ".*"),
-    };
-    return &node.base;
-}
-
-fn transCreateNodeArrayAccess(c: *Context, lhs: *ast.Node) !*ast.Node.ArrayAccess {
-    _ = try appendToken(c, .LBrace, "[");
-    const node = try c.arena.create(ast.Node.ArrayAccess);
-    node.* = .{
-        .lhs = lhs,
-        .index_expr = undefined,
-        .rtoken = undefined,
-    };
-    return node;
-}
-
 fn transType(c: *Context, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
     switch (ty.getTypeClass()) {
         .Builtin => {