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.
master
Andrew Kelley 2020-08-14 15:27:48 -07:00
parent c12a262503
commit 9a5a1013a8
6 changed files with 220 additions and 109 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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())

View File

@ -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,
}
}
}

View File

@ -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;