From 9a5a1013a833229e1d12588615c1a05644f76cc5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 14 Aug 2020 15:27:48 -0700 Subject: [PATCH] std.zig.ast: extract out Node.LabeledBlock from Node.Block This is part of an ongoing effort to reduce size of in-memory AST. This enum flattening pattern is widespread throughout the self-hosted compiler. This is a API breaking change for consumers of the self-hosted parser. --- lib/std/zig/ast.zig | 92 ++++++++++++++++++++++++++++----- lib/std/zig/parse.zig | 66 ++++++++++++----------- lib/std/zig/render.zig | 39 +++++++++++--- src-self-hosted/Module.zig | 2 +- src-self-hosted/astgen.zig | 40 ++++++++------ src-self-hosted/translate_c.zig | 90 ++++++++++++++++---------------- 6 files changed, 220 insertions(+), 109 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 6b2b8b4cf2..9258fc58d0 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -526,6 +526,7 @@ pub const Node = struct { Comptime, Nosuspend, Block, + LabeledBlock, // Misc DocComment, @@ -654,6 +655,7 @@ pub const Node = struct { .Comptime => Comptime, .Nosuspend => Nosuspend, .Block => Block, + .LabeledBlock => LabeledBlock, .DocComment => DocComment, .SwitchCase => SwitchCase, .SwitchElse => SwitchElse, @@ -666,6 +668,13 @@ pub const Node = struct { .FieldInitializer => FieldInitializer, }; } + + pub fn isBlock(tag: Tag) bool { + return switch (tag) { + .Block, .LabeledBlock => true, + else => false, + }; + } }; /// Prefer `castTag` to this. @@ -729,6 +738,7 @@ pub const Node = struct { .Root, .ContainerField, .Block, + .LabeledBlock, .Payload, .PointerPayload, .PointerIndexPayload, @@ -739,6 +749,7 @@ pub const Node = struct { .DocComment, .TestDecl, => return false, + .While => { const while_node = @fieldParentPtr(While, "base", n); if (while_node.@"else") |@"else"| { @@ -746,7 +757,7 @@ pub const Node = struct { continue; } - return while_node.body.tag != .Block; + return !while_node.body.tag.isBlock(); }, .For => { const for_node = @fieldParentPtr(For, "base", n); @@ -755,7 +766,7 @@ pub const Node = struct { continue; } - return for_node.body.tag != .Block; + return !for_node.body.tag.isBlock(); }, .If => { const if_node = @fieldParentPtr(If, "base", n); @@ -764,7 +775,7 @@ pub const Node = struct { continue; } - return if_node.body.tag != .Block; + return !if_node.body.tag.isBlock(); }, .Else => { const else_node = @fieldParentPtr(Else, "base", n); @@ -773,29 +784,40 @@ pub const Node = struct { }, .Defer => { const defer_node = @fieldParentPtr(Defer, "base", n); - return defer_node.expr.tag != .Block; + return !defer_node.expr.tag.isBlock(); }, .Comptime => { const comptime_node = @fieldParentPtr(Comptime, "base", n); - return comptime_node.expr.tag != .Block; + return !comptime_node.expr.tag.isBlock(); }, .Suspend => { const suspend_node = @fieldParentPtr(Suspend, "base", n); if (suspend_node.body) |body| { - return body.tag != .Block; + return !body.tag.isBlock(); } return true; }, .Nosuspend => { const nosuspend_node = @fieldParentPtr(Nosuspend, "base", n); - return nosuspend_node.expr.tag != .Block; + return !nosuspend_node.expr.tag.isBlock(); }, else => return true, } } } + /// Asserts the node is a Block or LabeledBlock and returns the statements slice. + pub fn blockStatements(base: *Node) []*Node { + if (base.castTag(.Block)) |block| { + return block.statements(); + } else if (base.castTag(.LabeledBlock)) |labeled_block| { + return labeled_block.statements(); + } else { + unreachable; + } + } + pub fn dump(self: *Node, indent: usize) void { { var i: usize = 0; @@ -1460,7 +1482,6 @@ pub const Node = struct { statements_len: NodeIndex, lbrace: TokenIndex, rbrace: TokenIndex, - label: ?TokenIndex, /// After this the caller must initialize the statements list. pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*Block { @@ -1483,10 +1504,6 @@ pub const Node = struct { } pub fn firstToken(self: *const Block) TokenIndex { - if (self.label) |label| { - return label; - } - return self.lbrace; } @@ -1509,6 +1526,57 @@ pub const Node = struct { } }; + /// The statements of the block follow LabeledBlock directly in memory. + pub const LabeledBlock = struct { + base: Node = Node{ .tag = .LabeledBlock }, + statements_len: NodeIndex, + lbrace: TokenIndex, + rbrace: TokenIndex, + label: TokenIndex, + + /// After this the caller must initialize the statements list. + pub fn alloc(allocator: *mem.Allocator, statements_len: NodeIndex) !*LabeledBlock { + const bytes = try allocator.alignedAlloc(u8, @alignOf(LabeledBlock), sizeInBytes(statements_len)); + return @ptrCast(*LabeledBlock, bytes.ptr); + } + + pub fn free(self: *LabeledBlock, allocator: *mem.Allocator) void { + const bytes = @ptrCast([*]u8, self)[0..sizeInBytes(self.statements_len)]; + allocator.free(bytes); + } + + pub fn iterate(self: *const LabeledBlock, index: usize) ?*Node { + var i = index; + + if (i < self.statements_len) return self.statementsConst()[i]; + i -= self.statements_len; + + return null; + } + + pub fn firstToken(self: *const LabeledBlock) TokenIndex { + return self.label; + } + + pub fn lastToken(self: *const LabeledBlock) TokenIndex { + return self.rbrace; + } + + pub fn statements(self: *LabeledBlock) []*Node { + const decls_start = @ptrCast([*]u8, self) + @sizeOf(LabeledBlock); + return @ptrCast([*]*Node, decls_start)[0..self.statements_len]; + } + + pub fn statementsConst(self: *const LabeledBlock) []const *Node { + const decls_start = @ptrCast([*]const u8, self) + @sizeOf(LabeledBlock); + return @ptrCast([*]const *Node, decls_start)[0..self.statements_len]; + } + + fn sizeInBytes(statements_len: NodeIndex) usize { + return @sizeOf(LabeledBlock) + @sizeOf(*Node) * @as(usize, statements_len); + } + }; + pub const Defer = struct { base: Node = Node{ .tag = .Defer }, defer_token: TokenIndex, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 70a305007b..e0eb2dd895 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -364,9 +364,10 @@ const Parser = struct { const name_node = try p.expectNode(parseStringLiteralSingle, .{ .ExpectedStringLiteral = .{ .token = p.tok_i }, }); - const block_node = try p.expectNode(parseBlock, .{ - .ExpectedLBrace = .{ .token = p.tok_i }, - }); + const block_node = (try p.parseBlock(null)) orelse { + try p.errors.append(p.gpa, .{ .ExpectedLBrace = .{ .token = p.tok_i } }); + return error.ParseError; + }; const test_node = try p.arena.allocator.create(Node.TestDecl); test_node.* = .{ @@ -540,12 +541,14 @@ const Parser = struct { if (p.eatToken(.Semicolon)) |_| { break :blk null; } - break :blk try p.expectNodeRecoverable(parseBlock, .{ + const body_block = (try p.parseBlock(null)) orelse { // Since parseBlock only return error.ParseError on // a missing '}' we can assume this function was // supposed to end here. - .ExpectedSemiOrLBrace = .{ .token = p.tok_i }, - }); + try p.errors.append(p.gpa, .{ .ExpectedSemiOrLBrace = .{ .token = p.tok_i } }); + break :blk null; + }; + break :blk body_block; }, .as_type => null, }; @@ -823,10 +826,7 @@ const Parser = struct { var colon: TokenIndex = undefined; const label_token = p.parseBlockLabel(&colon); - if (try p.parseBlock()) |node| { - node.cast(Node.Block).?.label = label_token; - return node; - } + if (try p.parseBlock(label_token)) |node| return node; if (try p.parseLoopStatement()) |node| { if (node.cast(Node.For)) |for_node| { @@ -1003,14 +1003,13 @@ const Parser = struct { fn parseBlockExpr(p: *Parser) Error!?*Node { var colon: TokenIndex = undefined; const label_token = p.parseBlockLabel(&colon); - const block_node = (try p.parseBlock()) orelse { + const block_node = (try p.parseBlock(label_token)) orelse { if (label_token) |label| { p.putBackToken(label + 1); // ":" p.putBackToken(label); // IDENTIFIER } return null; }; - block_node.cast(Node.Block).?.label = label_token; return block_node; } @@ -1177,7 +1176,7 @@ const Parser = struct { p.putBackToken(token); // IDENTIFIER } - if (try p.parseBlock()) |node| return node; + if (try p.parseBlock(null)) |node| return node; if (try p.parseCurlySuffixExpr()) |node| return node; return null; @@ -1189,7 +1188,7 @@ const Parser = struct { } /// Block <- LBRACE Statement* RBRACE - fn parseBlock(p: *Parser) !?*Node { + fn parseBlock(p: *Parser, label_token: ?TokenIndex) !?*Node { const lbrace = p.eatToken(.LBrace) orelse return null; var statements = std.ArrayList(*Node).init(p.gpa); @@ -1211,16 +1210,26 @@ const Parser = struct { const statements_len = @intCast(NodeIndex, statements.items.len); - const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); - block_node.* = .{ - .label = null, - .lbrace = lbrace, - .statements_len = statements_len, - .rbrace = rbrace, - }; - std.mem.copy(*Node, block_node.statements(), statements.items); - - return &block_node.base; + if (label_token) |label| { + const block_node = try Node.LabeledBlock.alloc(&p.arena.allocator, statements_len); + block_node.* = .{ + .label = label, + .lbrace = lbrace, + .statements_len = statements_len, + .rbrace = rbrace, + }; + std.mem.copy(*Node, block_node.statements(), statements.items); + return &block_node.base; + } else { + const block_node = try Node.Block.alloc(&p.arena.allocator, statements_len); + block_node.* = .{ + .lbrace = lbrace, + .statements_len = statements_len, + .rbrace = rbrace, + }; + std.mem.copy(*Node, block_node.statements(), statements.items); + return &block_node.base; + } } /// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) @@ -1658,11 +1667,8 @@ const Parser = struct { var colon: TokenIndex = undefined; const label = p.parseBlockLabel(&colon); - if (label) |token| { - if (try p.parseBlock()) |node| { - node.cast(Node.Block).?.label = token; - return node; - } + if (label) |label_token| { + if (try p.parseBlock(label_token)) |node| return node; } if (try p.parseLoopTypeExpr()) |node| { @@ -3440,6 +3446,7 @@ const Parser = struct { } } + /// TODO Delete this function. I don't like the inversion of control. fn expectNode( p: *Parser, parseFn: NodeParseFn, @@ -3449,6 +3456,7 @@ const Parser = struct { return (try p.expectNodeRecoverable(parseFn, err)) orelse return error.ParseError; } + /// TODO Delete this function. I don't like the inversion of control. fn expectNodeRecoverable( p: *Parser, parseFn: NodeParseFn, diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index c516250a17..7b9036f259 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -392,28 +392,50 @@ fn renderExpression( return renderToken(tree, stream, any_type.token, indent, start_col, space); }, - .Block => { - const block = @fieldParentPtr(ast.Node.Block, "base", base); + .Block, .LabeledBlock => { + const block: struct { + label: ?ast.TokenIndex, + statements: []*ast.Node, + lbrace: ast.TokenIndex, + rbrace: ast.TokenIndex, + } = b: { + if (base.castTag(.Block)) |block| { + break :b .{ + .label = null, + .statements = block.statements(), + .lbrace = block.lbrace, + .rbrace = block.rbrace, + }; + } else if (base.castTag(.LabeledBlock)) |block| { + break :b .{ + .label = block.label, + .statements = block.statements(), + .lbrace = block.lbrace, + .rbrace = block.rbrace, + }; + } else { + unreachable; + } + }; if (block.label) |label| { try renderToken(tree, stream, label, indent, start_col, Space.None); try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); } - if (block.statements_len == 0) { + if (block.statements.len == 0) { try renderToken(tree, stream, block.lbrace, indent + indent_delta, start_col, Space.None); return renderToken(tree, stream, block.rbrace, indent, start_col, space); } else { const block_indent = indent + indent_delta; try renderToken(tree, stream, block.lbrace, block_indent, start_col, Space.Newline); - const block_statements = block.statements(); - for (block_statements) |statement, i| { + for (block.statements) |statement, i| { try stream.writeByteNTimes(' ', block_indent); try renderStatement(allocator, stream, tree, block_indent, start_col, statement); - if (i + 1 < block_statements.len) { - try renderExtraNewline(tree, stream, start_col, block_statements[i + 1]); + if (i + 1 < block.statements.len) { + try renderExtraNewline(tree, stream, start_col, block.statements[i + 1]); } } @@ -1841,7 +1863,7 @@ fn renderExpression( const rparen = tree.nextToken(for_node.array_expr.lastToken()); - const body_is_block = for_node.body.tag == .Block; + const body_is_block = for_node.body.tag.isBlock(); const src_one_line_to_body = !body_is_block and tree.tokensOnSameLine(rparen, for_node.body.firstToken()); const body_on_same_line = body_is_block or src_one_line_to_body; @@ -2578,6 +2600,7 @@ fn renderDocCommentsToken( fn nodeIsBlock(base: *const ast.Node) bool { return switch (base.tag) { .Block, + .LabeledBlock, .If, .For, .While, diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index a172a4b679..6c0e942cc6 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -1343,7 +1343,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { const body_block = body_node.cast(ast.Node.Block).?; - _ = try astgen.blockExpr(self, params_scope, .none, body_block); + try astgen.blockExpr(self, params_scope, body_block); if (gen_scope.instructions.items.len == 0 or !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn()) diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 3c149cd3dd..41d48c46fc 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -107,31 +107,46 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), - .Block => return blockExpr(mod, scope, rl, node.castTag(.Block).?), + .Block => return rlWrapVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)), + .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?), else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), } } -pub fn blockExpr( +pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void { + const tracy = trace(@src()); + defer tracy.end(); + + try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements()); +} + +fn labeledBlockExpr( mod: *Module, parent_scope: *Scope, rl: ResultLoc, - block_node: *ast.Node.Block, + block_node: *ast.Node.LabeledBlock, ) InnerError!*zir.Inst { const tracy = trace(@src()); defer tracy.end(); - if (block_node.label) |label| { - return mod.failTok(parent_scope, label, "TODO implement labeled blocks", .{}); + const statements = block_node.statements(); + + if (statements.len == 0) { + // Hot path for `{}`. + return rlWrapVoid(mod, parent_scope, rl, &block_node.base, {}); } + return mod.failNode(parent_scope, &block_node.base, "TODO implement labeled blocks", .{}); +} + +fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void { + const tree = parent_scope.tree(); + var block_arena = std.heap.ArenaAllocator.init(mod.gpa); defer block_arena.deinit(); - const tree = parent_scope.tree(); - var scope = parent_scope; - for (block_node.statements()) |statement| { + for (statements) |statement| { const src = tree.token_locs[statement.firstToken()].start; _ = try addZIRNoOp(mod, scope, src, .dbg_stmt); switch (statement.tag) { @@ -162,12 +177,6 @@ pub fn blockExpr( }, } } - - const src = tree.token_locs[block_node.firstToken()].start; - return addZIRInstConst(mod, parent_scope, src, .{ - .ty = Type.initTag(.void), - .val = Value.initTag(.void_value), - }); } fn varDecl( @@ -1184,6 +1193,7 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { .Slice, .Deref, .ArrayAccess, + .Block, => return false, // Forward the question to a sub-expression. @@ -1210,11 +1220,11 @@ fn nodeMayNeedMemoryLocation(start_node: *ast.Node) bool { .Switch, .Call, .BuiltinCall, // TODO some of these can return false + .LabeledBlock, => return true, // Depending on AST properties, they may need memory locations. .If => return node.castTag(.If).?.@"else" != null, - .Block => return node.castTag(.Block).?.label != null, } } } diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 2382375fc5..98cb68f059 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -118,19 +118,31 @@ const Scope = struct { self.* = undefined; } - fn complete(self: *Block, c: *Context) !*ast.Node.Block { + fn complete(self: *Block, c: *Context) !*ast.Node { // We reserve 1 extra statement if the parent is a Loop. This is in case of // do while, we want to put `if (cond) break;` at the end. const alloc_len = self.statements.items.len + @boolToInt(self.base.parent.?.id == .Loop); - const node = try ast.Node.Block.alloc(c.arena, alloc_len); - node.* = .{ - .statements_len = self.statements.items.len, - .lbrace = self.lbrace, - .rbrace = try appendToken(c, .RBrace, "}"), - .label = self.label, - }; - mem.copy(*ast.Node, node.statements(), self.statements.items); - return node; + const rbrace = try appendToken(c, .RBrace, "}"); + if (self.label) |label| { + const node = try ast.Node.LabeledBlock.alloc(c.arena, alloc_len); + node.* = .{ + .statements_len = self.statements.items.len, + .lbrace = self.lbrace, + .rbrace = rbrace, + .label = label, + }; + mem.copy(*ast.Node, node.statements(), self.statements.items); + return &node.base; + } else { + const node = try ast.Node.Block.alloc(c.arena, alloc_len); + node.* = .{ + .statements_len = self.statements.items.len, + .lbrace = self.lbrace, + .rbrace = rbrace, + }; + mem.copy(*ast.Node, node.statements(), self.statements.items); + return &node.base; + } } /// Given the desired name, return a name that does not shadow anything from outer scopes. @@ -320,15 +332,9 @@ pub const Context = struct { return node; } - fn createBlock(c: *Context, label: ?[]const u8, statements_len: ast.NodeIndex) !*ast.Node.Block { - const label_node = if (label) |l| blk: { - const ll = try appendIdentifier(c, l); - _ = try appendToken(c, .Colon, ":"); - break :blk ll; - } else null; + fn createBlock(c: *Context, statements_len: ast.NodeIndex) !*ast.Node.Block { const block_node = try ast.Node.Block.alloc(c.arena, statements_len); block_node.* = .{ - .label = label_node, .lbrace = try appendToken(c, .LBrace, "{"), .statements_len = statements_len, .rbrace = undefined, @@ -640,8 +646,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { var last = block_scope.statements.items[block_scope.statements.items.len - 1]; while (true) { switch (last.tag) { - .Block => { - const stmts = last.castTag(.Block).?.statements(); + .Block, .LabeledBlock => { + const stmts = last.blockStatements(); if (stmts.len == 0) break; last = stmts[stmts.len - 1]; @@ -669,7 +675,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { } const body_node = try block_scope.complete(rp.c); - proto_node.setTrailer("body_node", &body_node.base); + proto_node.setTrailer("body_node", body_node); return addTopLevelDecl(c, fn_name, &proto_node.base); } @@ -1275,7 +1281,7 @@ fn transStmt( .WhileStmtClass => return transWhileLoop(rp, scope, @ptrCast(*const ZigClangWhileStmt, stmt)), .DoStmtClass => return transDoWhileLoop(rp, scope, @ptrCast(*const ZigClangDoStmt, stmt)), .NullStmtClass => { - const block = try rp.c.createBlock(null, 0); + const block = try rp.c.createBlock(0); block.rbrace = try appendToken(rp.c, .RBrace, "}"); return &block.base; }, @@ -1356,7 +1362,7 @@ fn transBinaryOperator( const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = lparen, - .expr = &block_node.base, + .expr = block_node, .rparen = rparen, }; return maybeSuppressResult(rp, scope, result_used, &grouped_expr.base); @@ -1521,8 +1527,7 @@ fn transCompoundStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangCompo var block_scope = try Scope.Block.init(rp.c, scope, false); defer block_scope.deinit(); try transCompoundStmtInline(rp, &block_scope.base, stmt, &block_scope); - const node = try block_scope.complete(rp.c); - return &node.base; + return try block_scope.complete(rp.c); } fn transCStyleCastExprClass( @@ -2589,7 +2594,7 @@ fn transDoWhileLoop( // zig: if (!cond) break; // zig: } const node = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value); - break :blk node.cast(ast.Node.Block).?; + break :blk node.castTag(.Block).?; } else blk: { // the C statement is without a block, so we need to create a block to contain it. // c: do @@ -2600,7 +2605,7 @@ fn transDoWhileLoop( // zig: if (!cond) break; // zig: } new = true; - const block = try rp.c.createBlock(null, 2); + const block = try rp.c.createBlock(2); block.statements_len = 1; // over-allocated so we can add another below block.statements()[0] = try transStmt(rp, &loop_scope, ZigClangDoStmt_getBody(stmt), .unused, .r_value); break :blk block; @@ -2659,8 +2664,7 @@ fn transForLoop( while_node.body = try transStmt(rp, &loop_scope, ZigClangForStmt_getBody(stmt), .unused, .r_value); if (block_scope) |*bs| { try bs.statements.append(&while_node.base); - const node = try bs.complete(rp.c); - return &node.base; + return try bs.complete(rp.c); } else { _ = try appendToken(rp.c, .Semicolon, ";"); return &while_node.base; @@ -2768,7 +2772,7 @@ fn transSwitch( const result_node = try switch_scope.pending_block.complete(rp.c); switch_scope.pending_block.deinit(); - return &result_node.base; + return result_node; } fn transCase( @@ -2820,7 +2824,7 @@ fn transCase( switch_scope.pending_block.deinit(); switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); - try switch_scope.pending_block.statements.append(&pending_node.base); + try switch_scope.pending_block.statements.append(pending_node); return transStmt(rp, scope, ZigClangCaseStmt_getSubStmt(stmt), .unused, .r_value); } @@ -2857,7 +2861,7 @@ fn transDefault( const pending_node = try switch_scope.pending_block.complete(rp.c); switch_scope.pending_block.deinit(); switch_scope.pending_block = try Scope.Block.init(rp.c, scope, false); - try switch_scope.pending_block.statements.append(&pending_node.base); + try switch_scope.pending_block.statements.append(pending_node); return transStmt(rp, scope, ZigClangDefaultStmt_getSubStmt(stmt), .unused, .r_value); } @@ -2972,7 +2976,7 @@ fn transStmtExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangStmtExpr, const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = lparen, - .expr = &block_node.base, + .expr = block_node, .rparen = rparen, }; return maybeSuppressResult(rp, scope, used, &grouped_expr.base); @@ -3304,7 +3308,7 @@ fn transCreatePreCrement( const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = try appendToken(rp.c, .LParen, "("), - .expr = &block_node.base, + .expr = block_node, .rparen = try appendToken(rp.c, .RParen, ")"), }; return &grouped_expr.base; @@ -3398,7 +3402,7 @@ fn transCreatePostCrement( const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = try appendToken(rp.c, .LParen, "("), - .expr = &block_node.base, + .expr = block_node, .rparen = try appendToken(rp.c, .RParen, ")"), }; return &grouped_expr.base; @@ -3589,7 +3593,7 @@ fn transCreateCompoundAssign( const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = try appendToken(rp.c, .LParen, "("), - .expr = &block_node.base, + .expr = block_node, .rparen = try appendToken(rp.c, .RParen, ")"), }; return &grouped_expr.base; @@ -3748,7 +3752,7 @@ fn transBinaryConditionalOperator(rp: RestorePoint, scope: *Scope, stmt: *const const grouped_expr = try rp.c.arena.create(ast.Node.GroupedExpression); grouped_expr.* = .{ .lparen = lparen, - .expr = &block_node.base, + .expr = block_node, .rparen = try appendToken(rp.c, .RParen, ")"), }; return maybeSuppressResult(rp, scope, used, &grouped_expr.base); @@ -4191,7 +4195,7 @@ fn transCreateNodeAssign( 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, ";"); - return &block_node.base; + return block_node; } fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []const u8) !*ast.Node { @@ -4484,7 +4488,6 @@ fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: *ast.Node, proto_a const block = try ast.Node.Block.alloc(c.arena, 1); block.* = .{ - .label = null, .lbrace = block_lbrace, .statements_len = 1, .rbrace = try appendToken(c, .RBrace, "}"), @@ -5475,9 +5478,9 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { if (last != .Eof and last != .Nl) return m.fail(c, "unable to translate C expr: unexpected token .{}", .{@tagName(last)}); _ = try appendToken(c, .Semicolon, ";"); - const type_of_arg = if (expr.tag != .Block) expr else blk: { - const blk = @fieldParentPtr(ast.Node.Block, "base", expr); - const blk_last = blk.statements()[blk.statements_len - 1]; + const type_of_arg = if (!expr.tag.isBlock()) expr else blk: { + const stmts = expr.blockStatements(); + const blk_last = stmts[stmts.len - 1]; const br = blk_last.cast(ast.Node.ControlFlowExpression).?; break :blk br.getRHS().?; }; @@ -5500,7 +5503,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void { .visib_token = pub_tok, .extern_export_inline_token = inline_tok, .name_token = name_tok, - .body_node = &block_node.base, + .body_node = block_node, }); mem.copy(ast.Node.FnProto.ParamDecl, fn_proto.params(), fn_params.items); @@ -5555,8 +5558,7 @@ fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!*ast.Node { const break_node = try transCreateNodeBreak(c, block_scope.label, last); try block_scope.statements.append(&break_node.base); - const block_node = try block_scope.complete(c); - return &block_node.base; + return try block_scope.complete(c); }, else => { m.i -= 1;